Logo Search packages:      
Sourcecode: gaim version File versions

si.c

/*
 * gaim - Jabber Protocol Plugin
 *
 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "blist.h"

#include "internal.h"
#include "debug.h"
#include "ft.h"
#include "network.h"
#include "notify.h"
#include "sha.h"
#include "util.h"

#include "buddy.h"
#include "disco.h"
#include "jabber.h"
#include "iq.h"
#include "si.h"

#include "si.h"

struct bytestreams_streamhost {
      char *jid;
      char *host;
      int port;
};

typedef struct _JabberSIXfer {
      JabberStream *js;

      char *stream_id;
      char *iq_id;

      enum {
            STREAM_METHOD_UNKNOWN = 0,
            STREAM_METHOD_BYTESTREAMS = 2 << 1,
            STREAM_METHOD_IBB = 2 << 2,
            STREAM_METHOD_UNSUPPORTED = 2 << 31
      } stream_method;

      GList *streamhosts;
      GaimProxyInfo *gpi;

      char *rxqueue;
      size_t rxlen;
} JabberSIXfer;

static GaimXfer*
jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from)
{
      GList *xfers;

      if(!sid || !from)
            return NULL;

      for(xfers = js->file_transfers; xfers; xfers = xfers->next) {
            GaimXfer *xfer = xfers->data;
            JabberSIXfer *jsx = xfer->data;
            if(jsx && jsx->stream_id && xfer->who &&
                        !strcmp(jsx->stream_id, sid) && !strcmp(xfer->who, from))
                  return xfer;
      }

      return NULL;
}


static void jabber_si_bytestreams_attempt_connect(GaimXfer *xfer);

static void jabber_si_bytestreams_connect_cb(gpointer data, gint source, GaimInputCondition cond)
{
      GaimXfer *xfer = data;
      JabberSIXfer *jsx = xfer->data;
      JabberIq *iq;
      xmlnode *query, *su;
      struct bytestreams_streamhost *streamhost = jsx->streamhosts->data;

      gaim_proxy_info_destroy(jsx->gpi);

      if(source < 0) {
            jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
            g_free(streamhost->jid);
            g_free(streamhost->host);
            g_free(streamhost);
            jabber_si_bytestreams_attempt_connect(xfer);
            return;
      }

      iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, "http://jabber.org/protocol/bytestreams");
      xmlnode_set_attrib(iq->node, "to", xfer->who);
      jabber_iq_set_id(iq, jsx->iq_id);
      query = xmlnode_get_child(iq->node, "query");
      su = xmlnode_new_child(query, "streamhost-used");
      xmlnode_set_attrib(su, "jid", streamhost->jid);

      jabber_iq_send(iq);

      gaim_xfer_start(xfer, source, NULL, -1);
}

static void jabber_si_bytestreams_attempt_connect(GaimXfer *xfer)
{
      JabberSIXfer *jsx = xfer->data;
      struct bytestreams_streamhost *streamhost;
      char *dstaddr, *p;
      int i;
      unsigned char hashval[20];

      if(!jsx->streamhosts) {
            JabberIq *iq = jabber_iq_new(jsx->js, JABBER_IQ_ERROR);
            xmlnode *error, *condition;

            if(jsx->iq_id)
                  jabber_iq_set_id(iq, jsx->iq_id);

            xmlnode_set_attrib(iq->node, "to", xfer->who);
            error = xmlnode_new_child(iq->node, "error");
            xmlnode_set_attrib(error, "code", "404");
            xmlnode_set_attrib(error, "type", "cancel");
            condition = xmlnode_new_child(error, "condition");
            xmlnode_set_attrib(condition, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
            xmlnode_new_child(condition, "item-not-found");

            jabber_iq_send(iq);

            gaim_xfer_cancel_local(xfer);

            return;
      }

      streamhost = jsx->streamhosts->data;

      jsx->gpi = gaim_proxy_info_new();
      gaim_proxy_info_set_type(jsx->gpi, GAIM_PROXY_SOCKS5);
      gaim_proxy_info_set_host(jsx->gpi, streamhost->host);
      gaim_proxy_info_set_port(jsx->gpi, streamhost->port);

      dstaddr = g_strdup_printf("%s%s%s@%s/%s", jsx->stream_id, xfer->who, jsx->js->user->node,
                  jsx->js->user->domain, jsx->js->user->resource);
      shaBlock((unsigned char *)dstaddr, strlen(dstaddr), hashval);
      g_free(dstaddr);
      dstaddr = g_malloc(41);
      p = dstaddr;
      for(i=0; i<20; i++, p+=2)
            snprintf(p, 3, "%02x", hashval[i]);

      gaim_proxy_connect_socks5(jsx->gpi, dstaddr, 0, jabber_si_bytestreams_connect_cb, xfer);
      g_free(dstaddr);
}

void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet)
{
      GaimXfer *xfer;
      JabberSIXfer *jsx;
      xmlnode *query, *streamhost;
      const char *sid, *from;

      if(!(from = xmlnode_get_attrib(packet, "from")))
            return;

      if(!(query = xmlnode_get_child(packet, "query")))
            return;

      if(!(sid = xmlnode_get_attrib(query, "sid")))
            return;

      if(!(xfer = jabber_si_xfer_find(js, sid, from)))
            return;

      jsx = xfer->data;
      if(jsx->iq_id)
            g_free(jsx->iq_id);
      jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id"));

      for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost;
                  streamhost = xmlnode_get_next_twin(streamhost)) {
            const char *jid, *host, *port;
            int portnum;

            if((jid = xmlnode_get_attrib(streamhost, "jid")) &&
                        (host = xmlnode_get_attrib(streamhost, "host")) &&
                        (port = xmlnode_get_attrib(streamhost, "port")) &&
                        (portnum = atoi(port))) {
                  struct bytestreams_streamhost *sh = g_new0(struct bytestreams_streamhost, 1);
                  sh->jid = g_strdup(jid);
                  sh->host = g_strdup(host);
                  sh->port = portnum;
                  jsx->streamhosts = g_list_append(jsx->streamhosts, sh);
            }
      }

      jabber_si_bytestreams_attempt_connect(xfer);
}

static void
jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source,
            GaimInputCondition cond)
{
      GaimXfer *xfer = data;
      JabberSIXfer *jsx = xfer->data;
      int i;
      char buffer[256];
      int len;
      char *dstaddr, *p;
      unsigned char hashval[20];
      const char *host;

      gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_again_cb\n");

      if(jsx->rxlen < 5) {
            gaim_debug_info("jabber", "reading the first 5 bytes\n");
            if((len = read(source, buffer, 5 - jsx->rxlen)) <= 0) {
                  gaim_input_remove(xfer->watcher);
                  xfer->watcher = 0;
                  close(source);
                  gaim_xfer_cancel_remote(xfer);
                  return;
            }
            jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
            memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
            jsx->rxlen += len;
            return;
      } else if(jsx->rxqueue[0] != 0x05 || jsx->rxqueue[1] != 0x01 ||
                  jsx->rxqueue[3] != 0x03) {
            gaim_debug_info("jabber", "invalid socks5 stuff\n");
            gaim_input_remove(xfer->watcher);
            xfer->watcher = 0;
            close(source);
            gaim_xfer_cancel_remote(xfer);
            return;
      } else if(jsx->rxlen - 5 <  jsx->rxqueue[4] + 2) {
            gaim_debug_info("jabber", "reading umpteen more bytes\n");
            if((len = read(source, buffer, jsx->rxqueue[4] + 5 + 2 - jsx->rxlen)) <= 0) {
                  gaim_input_remove(xfer->watcher);
                  xfer->watcher = 0;
                  close(source);
                  gaim_xfer_cancel_remote(xfer);
                  return;
            }
            jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
            memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
            jsx->rxlen += len;
      }

      if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2)
            return;

      gaim_input_remove(xfer->watcher);
      xfer->watcher = 0;

      dstaddr = g_strdup_printf("%s%s@%s/%s%s", jsx->stream_id,
                  jsx->js->user->node, jsx->js->user->domain,
                  jsx->js->user->resource, xfer->who);
      shaBlock((unsigned char *)dstaddr, strlen(dstaddr), hashval);
      g_free(dstaddr);
      dstaddr = g_malloc(41);
      p = dstaddr;
      for(i=0; i<20; i++, p+=2)
            snprintf(p, 3, "%02x", hashval[i]);

      if(jsx->rxqueue[4] != 40 || strncmp(dstaddr, jsx->rxqueue+5, 40) ||
                  jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00) {
            gaim_debug_error("jabber", "someone connected with the wrong info!\n");
            close(source);
            gaim_xfer_cancel_remote(xfer);
            return;
      }

      host = gaim_network_get_my_ip(jsx->js->fd);

      buffer[0] = 0x05;
      buffer[1] = 0x00;
      buffer[2] = 0x00;
      buffer[3] = 0x03;
      buffer[4] = strlen(host);
      memcpy(buffer + 5, host, strlen(host));
      buffer[5+strlen(host)] = 0x00;
      buffer[6+strlen(host)] = 0x00;

      write(source, buffer, strlen(host)+7);

      gaim_xfer_start(xfer, source, NULL, -1);
}

static void
jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source,
            GaimInputCondition cond)
{
      GaimXfer *xfer = data;
      JabberSIXfer *jsx = xfer->data;
      int i;
      int len;
      char buffer[256];

      gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_cb\n");

      xfer->fd = source;

      if(jsx->rxlen < 2) {
            gaim_debug_info("jabber", "reading those first two bytes\n");
            if((len = read(source, buffer, 2 - jsx->rxlen)) <= 0) {
                  gaim_input_remove(xfer->watcher);
                  xfer->watcher = 0;
                  close(source);
                  gaim_xfer_cancel_remote(xfer);
                  return;
            }
            jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
            memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
            jsx->rxlen += len;
            return;
      } else if(jsx->rxlen - 2 <  jsx->rxqueue[1]) {
            gaim_debug_info("jabber", "reading the next umpteen bytes\n");
            if((len = read(source, buffer, jsx->rxqueue[1] + 2 - jsx->rxlen)) <= 0) {
                  gaim_input_remove(xfer->watcher);
                  xfer->watcher = 0;
                  close(source);
                  gaim_xfer_cancel_remote(xfer);
                  return;
            }
            jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
            memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
            jsx->rxlen += len;
      }

      if(jsx->rxlen -2 < jsx->rxqueue[1])
            return;

      gaim_input_remove(xfer->watcher);
      xfer->watcher = 0;


      gaim_debug_info("jabber", "checking to make sure we're socks FIVE\n");

      if(jsx->rxqueue[0] != 0x05) {
            close(source);
            gaim_xfer_cancel_remote(xfer);
            return;
      }

      gaim_debug_info("jabber", "going to test %hhu different methods\n", jsx->rxqueue[1]);

      for(i=0; i<jsx->rxqueue[1]; i++) {

            gaim_debug_info("jabber", "testing %hhu\n", jsx->rxqueue[i+2]);
            if(jsx->rxqueue[i+2] == 0x00) {
                  buffer[0] = 0x05;
                  buffer[1] = 0x00;
                  write(source, buffer, 2);
                  xfer->watcher = gaim_input_add(source, GAIM_INPUT_READ,
                              jabber_si_xfer_bytestreams_send_read_again_cb, xfer);
                  g_free(jsx->rxqueue);
                  jsx->rxqueue = NULL;
                  jsx->rxlen = 0;
                  return;
            }
      }

      buffer[0] = 0x05;
      buffer[1] = 0xFF;
      write(source, buffer, 2);
      close(source);
      g_free(jsx->rxqueue);
      jsx->rxqueue = NULL;
      jsx->rxlen = 0;
      gaim_xfer_cancel_remote(xfer);
}

static void
jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source,
            GaimInputCondition cond)
{
      GaimXfer *xfer = data;
      int acceptfd;

      gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n");

      if((acceptfd = accept(source, NULL, 0)) == -1) {
            gaim_debug_warning("jabber", "accept: %s\n", strerror(errno));
            return;
      }

      gaim_input_remove(xfer->watcher);
      close(source);

      xfer->watcher = gaim_input_add(acceptfd, GAIM_INPUT_READ,
                  jabber_si_xfer_bytestreams_send_read_cb, xfer);
}


static void
jabber_si_xfer_bytestreams_send_init(GaimXfer *xfer)
{
      JabberSIXfer *jsx = xfer->data;
      JabberIq *iq;
      xmlnode *query, *streamhost;
      char *jid, *port;
      int fd;

      iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET,
                  "http://jabber.org/protocol/bytestreams");
      xmlnode_set_attrib(iq->node, "to", xfer->who);
      query = xmlnode_get_child(iq->node, "query");

      xmlnode_set_attrib(query, "sid", jsx->stream_id);

      streamhost = xmlnode_new_child(query, "streamhost");
      jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource);
      xmlnode_set_attrib(streamhost, "jid", jid);
      g_free(jid);

      if((fd = gaim_network_listen_range(0, 0)) < 0) {
            /* XXX: couldn't open a port, we're fscked */
            return;
      }

      xmlnode_set_attrib(streamhost, "host",  gaim_network_get_my_ip(jsx->js->fd));
      xfer->local_port = gaim_network_get_port_from_fd(fd);
      port = g_strdup_printf("%hu", xfer->local_port);
      xmlnode_set_attrib(streamhost, "port", port);
      g_free(port);

      xfer->watcher = gaim_input_add(fd, GAIM_INPUT_READ,
                  jabber_si_xfer_bytestreams_send_connected_cb, xfer);

      /* XXX: insert proxies here */

      /* XXX: callback to find out which streamhost they used, or see if they
       * screwed it up */
      jabber_iq_send(iq);
}

static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet,
            gpointer data)
{
      GaimXfer *xfer = data;
      xmlnode *si, *feature, *x, *field, *value;

      if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) {
            gaim_xfer_cancel_remote(xfer);
            return;
      }

      if(!(feature = xmlnode_get_child_with_namespace(si, "feature", "http://jabber.org/protocol/feature-neg"))) {
            gaim_xfer_cancel_remote(xfer);
            return;
      }

      if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) {
            gaim_xfer_cancel_remote(xfer);
            return;
      }

      for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
            const char *var = xmlnode_get_attrib(field, "var");

            if(var && !strcmp(var, "stream-method")) {
                  if((value = xmlnode_get_child(field, "value"))) {
                        char *val = xmlnode_get_data(value);
                        if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) {
                              jabber_si_xfer_bytestreams_send_init(xfer);
                              g_free(val);
                              return;
                        }
                        g_free(val);
                  }
            }
      }
      gaim_xfer_cancel_remote(xfer);
}

static void jabber_si_xfer_send_request(GaimXfer *xfer)
{
      JabberSIXfer *jsx = xfer->data;
      JabberIq *iq;
      xmlnode *si, *file, *feature, *x, *field, *option, *value;
      char buf[32];

      xfer->filename = g_path_get_basename(xfer->local_filename);

      iq = jabber_iq_new(jsx->js, JABBER_IQ_SET);
      xmlnode_set_attrib(iq->node, "to", xfer->who);
      si = xmlnode_new_child(iq->node, "si");
      xmlnode_set_attrib(si, "xmlns", "http://jabber.org/protocol/si");
      jsx->stream_id = jabber_get_next_id(jsx->js);
      xmlnode_set_attrib(si, "id", jsx->stream_id);
      xmlnode_set_attrib(si, "profile",
                  "http://jabber.org/protocol/si/profile/file-transfer");

      file = xmlnode_new_child(si, "file");
      xmlnode_set_attrib(file, "xmlns",
                  "http://jabber.org/protocol/si/profile/file-transfer");
      xmlnode_set_attrib(file, "name", xfer->filename);
      g_snprintf(buf, sizeof(buf), "%zu", xfer->size);
      xmlnode_set_attrib(file, "size", buf);
      /* maybe later we'll do hash and date attribs */

      feature = xmlnode_new_child(si, "feature");
      xmlnode_set_attrib(feature, "xmlns",
                  "http://jabber.org/protocol/feature-neg");
      x = xmlnode_new_child(feature, "x");
      xmlnode_set_attrib(x, "xmlns", "jabber:x:data");
      xmlnode_set_attrib(x, "type", "form");
      field = xmlnode_new_child(x, "field");
      xmlnode_set_attrib(field, "var", "stream-method");
      xmlnode_set_attrib(field, "type", "list-single");
      option = xmlnode_new_child(field, "option");
      value = xmlnode_new_child(option, "value");
      xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams",
                  -1);
      /*
      option = xmlnode_new_child(field, "option");
      value = xmlnode_new_child(option, "value");
      xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
      */

      jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer);

      jabber_iq_send(iq);
}

static void jabber_si_xfer_free(GaimXfer *xfer)
{
      JabberSIXfer *jsx = xfer->data;
      JabberStream *js = jsx->js;

      js->file_transfers = g_list_remove(js->file_transfers, xfer);

      g_free(jsx->stream_id);
      g_free(jsx->iq_id);
      /* XXX: free other stuff */
      g_free(jsx);
      xfer->data = NULL;
}

static void jabber_si_xfer_cancel_send(GaimXfer *xfer)
{
      jabber_si_xfer_free(xfer);
      gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_send\n");
}


static void jabber_si_xfer_cancel_recv(GaimXfer *xfer)
{
      jabber_si_xfer_free(xfer);
      gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_recv\n");
}

static void jabber_si_xfer_request_denied(GaimXfer *xfer)
{
      /* XXX: let the other side know */
      jabber_si_xfer_free(xfer);
}


static void jabber_si_xfer_end(GaimXfer *xfer)
{
      jabber_si_xfer_free(xfer);
}


static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who,
            JabberCapabilities capabilities, gpointer data)
{
      GaimXfer *xfer = data;

      if(capabilities & JABBER_CAP_SI_FILE_XFER) {
            jabber_si_xfer_send_request(xfer);
      } else {
            char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who);
            gaim_notify_error(js->gc, _("File Send Failed"),
                        _("File Send Failed"), msg);
            g_free(msg);
      }
}

static void jabber_si_xfer_init(GaimXfer *xfer)
{
      JabberSIXfer *jsx = xfer->data;
      JabberIq *iq;
      if(gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) {
            JabberBuddy *jb;
            JabberBuddyResource *jbr = NULL;

            jb = jabber_buddy_find(jsx->js, xfer->who, TRUE);
            /* XXX */
            if(!jb)
                  return;

            /* XXX: for now, send to the first resource available */
            if(g_list_length(jb->resources) >= 1) {
                  char *who;
                  jbr = jabber_buddy_find_resource(jb, NULL);
                  who = g_strdup_printf("%s/%s", xfer->who, jbr->name);
                  g_free(xfer->who);
                  xfer->who = who;
                  jabber_disco_info_do(jsx->js, who,
                              jabber_si_xfer_send_disco_cb, xfer);
            } else {
                  return; /* XXX: ick */
            }
      } else {
            xmlnode *si, *feature, *x, *field, *value;

            iq = jabber_iq_new(jsx->js, JABBER_IQ_RESULT);
            xmlnode_set_attrib(iq->node, "to", xfer->who);
            if(jsx->iq_id)
                  jabber_iq_set_id(iq, jsx->iq_id);

            si = xmlnode_new_child(iq->node, "si");
            xmlnode_set_attrib(si, "xmlns", "http://jabber.org/protocol/si");

            feature = xmlnode_new_child(si, "feature");
            xmlnode_set_attrib(feature, "xmlns", "http://jabber.org/protocol/feature-neg");

            x = xmlnode_new_child(feature, "x");
            xmlnode_set_attrib(x, "xmlns", "jabber:x:data");
            xmlnode_set_attrib(x, "type", "submit");

            field = xmlnode_new_child(x, "field");
            xmlnode_set_attrib(field, "var", "stream-method");

            value = xmlnode_new_child(field, "value");
            if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS)
                  xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
            /*
            else if(jsx->stream_method & STREAM_METHOD_IBB)
            xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
            */

            jabber_iq_send(iq);
      }
}

void jabber_si_xfer_send(GaimConnection *gc, const char *who, const char *file)
{
      JabberStream *js;

      GaimXfer *xfer;
      JabberSIXfer *jsx;

      js = gc->proto_data;

      if(!gaim_find_buddy(gc->account, who) || !jabber_buddy_find(js, who, FALSE))
            return;

      xfer = gaim_xfer_new(gc->account, GAIM_XFER_SEND, who);

      xfer->data = jsx = g_new0(JabberSIXfer, 1);
      jsx->js = js;

      gaim_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
      gaim_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send);
      gaim_xfer_set_request_denied_fnc(xfer, jabber_si_xfer_request_denied);
      gaim_xfer_set_end_fnc(xfer, jabber_si_xfer_end);

      js->file_transfers = g_list_append(js->file_transfers, xfer);

      if (file)
            gaim_xfer_request_accepted(xfer, file);
      else
            gaim_xfer_request(xfer);
}

void jabber_si_parse(JabberStream *js, xmlnode *packet)
{
      JabberSIXfer *jsx;
      GaimXfer *xfer;
      xmlnode *si, *file, *feature, *x, *field, *option, *value;
      const char *stream_id, *filename, *filesize_c, *profile, *from;
      size_t filesize = 0;

      if(!(si = xmlnode_get_child(packet, "si")))
            return;

      if(!(profile = xmlnode_get_attrib(si, "profile")) ||
                  strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer"))
            return;

      if(!(stream_id = xmlnode_get_attrib(si, "id")))
            return;

      if(!(file = xmlnode_get_child(si, "file")))
            return;

      if(!(filename = xmlnode_get_attrib(file, "name")))
            return;

      if((filesize_c = xmlnode_get_attrib(file, "size")))
            filesize = atoi(filesize_c);

      if(!(feature = xmlnode_get_child(si, "feature")))
            return;

      if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data")))
            return;

      if(!(from = xmlnode_get_attrib(packet, "from")))
            return;

      /* if they've already sent us this file transfer with the same damn id
       * then we're gonna ignore it, until I think of something better to do
       * with it */
      if((xfer = jabber_si_xfer_find(js, stream_id, from)))
            return;

      jsx = g_new0(JabberSIXfer, 1);

      for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
            const char *var = xmlnode_get_attrib(field, "var");
            if(var && !strcmp(var, "stream-method")) {
                  for(option = xmlnode_get_child(field, "option"); option;
                              option = xmlnode_get_next_twin(option)) {
                        if((value = xmlnode_get_child(option, "value"))) {
                              char *val;
                              if((val = xmlnode_get_data(value))) {
                                    if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) {
                                          jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
                                          /*
                                    } else if(!strcmp(val, "http://jabber.org/protocol/ibb")) {
                                          jsx->stream_method |= STREAM_METHOD_IBB;
                                          */
                                    }
                                    g_free(val);
                              }
                        }
                  }
            }
      }

      if(jsx->stream_method == STREAM_METHOD_UNKNOWN) {
            g_free(jsx);
            return;
      }

      jsx->js = js;
      jsx->stream_id = g_strdup(stream_id);
      jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id"));

      xfer = gaim_xfer_new(js->gc->account, GAIM_XFER_RECEIVE, from);
      xfer->data = jsx;

      gaim_xfer_set_filename(xfer, filename);
      if(filesize > 0)
            gaim_xfer_set_size(xfer, filesize);

      gaim_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
      gaim_xfer_set_cancel_recv_fnc(xfer, jabber_si_xfer_cancel_recv);
      gaim_xfer_set_request_denied_fnc(xfer, jabber_si_xfer_request_denied);
      gaim_xfer_set_end_fnc(xfer, jabber_si_xfer_end);

      js->file_transfers = g_list_append(js->file_transfers, xfer);

      gaim_xfer_request(xfer);
}



Generated by  Doxygen 1.6.0   Back to index