Coverage Report

Created: 2026-05-23 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pidgin/libpurple/protocols/jabber/si.c
Line
Count
Source
1
/*
2
 * purple - Jabber Protocol Plugin
3
 *
4
 * Purple is the legal property of its developers, whose names are too numerous
5
 * to list here.  Please refer to the COPYRIGHT file distributed with this
6
 * source distribution.
7
 *
8
 * This program is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 2 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
22
 *
23
 */
24
25
#include "internal.h"
26
27
#include "blist.h"
28
#include "debug.h"
29
#include "ft.h"
30
#include "request.h"
31
#include "network.h"
32
#include "notify.h"
33
34
#include "buddy.h"
35
#include "data.h"
36
#include "disco.h"
37
#include "jabber.h"
38
#include "ibb.h"
39
#include "iq.h"
40
#include "si.h"
41
42
0
#define STREAMHOST_CONNECT_TIMEOUT 5
43
#define ENABLE_FT_THUMBNAILS 0
44
45
typedef struct _JabberSIXfer {
46
  JabberStream *js;
47
48
  PurpleProxyConnectData *connect_data;
49
  PurpleNetworkListenData *listen_data;
50
  guint connect_timeout;
51
52
  gboolean accepted;
53
54
  char *stream_id;
55
  char *iq_id;
56
57
  enum {
58
    STREAM_METHOD_UNKNOWN     = 0,
59
    STREAM_METHOD_BYTESTREAMS = 2 << 1,
60
    STREAM_METHOD_IBB         = 2 << 2,
61
    STREAM_METHOD_UNSUPPORTED = 2 << 30
62
  } stream_method;
63
64
  GList *streamhosts;
65
  PurpleProxyInfo *gpi;
66
67
  char *rxqueue;
68
  size_t rxlen;
69
  gsize rxmaxlen;
70
  int local_streamhost_fd;
71
72
  JabberIBBSession *ibb_session;
73
  guint ibb_timeout_handle;
74
  PurpleCircBuffer *ibb_buffer;
75
} JabberSIXfer;
76
77
/* some forward declarations */
78
static void jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer);
79
80
static PurpleXfer*
81
jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from)
82
0
{
83
0
  GList *xfers;
84
85
0
  if(!sid || !from)
86
0
    return NULL;
87
88
0
  for(xfers = js->file_transfers; xfers; xfers = xfers->next) {
89
0
    PurpleXfer *xfer = xfers->data;
90
0
    JabberSIXfer *jsx = xfer->data;
91
0
    if(jsx->stream_id && xfer->who &&
92
0
        purple_strequal(jsx->stream_id, sid) && purple_strequal(xfer->who, from))
93
0
      return xfer;
94
0
  }
95
96
0
  return NULL;
97
0
}
98
99
static void
100
jabber_si_free_streamhost(gpointer data, gpointer user_data)
101
0
{
102
0
  JabberBytestreamsStreamhost *sh = data;
103
104
0
  if(!data)
105
0
    return;
106
107
0
  g_free(sh->jid);
108
0
  g_free(sh->host);
109
0
  g_free(sh->zeroconf);
110
0
  g_free(sh);
111
0
}
112
113
114
115
static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer);
116
117
static void
118
jabber_si_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
119
0
{
120
0
  PurpleXfer *xfer = data;
121
0
  JabberSIXfer *jsx = xfer->data;
122
0
  JabberIq *iq;
123
0
  xmlnode *query, *su;
124
0
  JabberBytestreamsStreamhost *streamhost = jsx->streamhosts->data;
125
126
0
  purple_proxy_info_destroy(jsx->gpi);
127
0
  jsx->gpi = NULL;
128
0
  jsx->connect_data = NULL;
129
130
0
  if (jsx->connect_timeout > 0)
131
0
    purple_timeout_remove(jsx->connect_timeout);
132
0
  jsx->connect_timeout = 0;
133
134
0
  if(source < 0) {
135
0
    purple_debug_warning("jabber",
136
0
        "si connection failed, jid was %s, host was %s, error was %s\n",
137
0
        streamhost->jid, streamhost->host,
138
0
        error_message ? error_message : "(null)");
139
0
    jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
140
0
    jabber_si_free_streamhost(streamhost, NULL);
141
0
    jabber_si_bytestreams_attempt_connect(xfer);
142
0
    return;
143
0
  }
144
145
  /* unknown file transfer type is assumed to be RECEIVE */
146
0
  if(xfer->type == PURPLE_XFER_SEND)
147
0
  {
148
0
    xmlnode *activate;
149
0
    iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, NS_BYTESTREAMS);
150
0
    xmlnode_set_attrib(iq->node, "to", streamhost->jid);
151
0
    query = xmlnode_get_child(iq->node, "query");
152
0
    xmlnode_set_attrib(query, "sid", jsx->stream_id);
153
0
    activate = xmlnode_new_child(query, "activate");
154
0
    xmlnode_insert_data(activate, xfer->who, -1);
155
156
    /* TODO: We need to wait for an activation result before starting */
157
0
  }
158
0
  else
159
0
  {
160
0
    iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, NS_BYTESTREAMS);
161
0
    xmlnode_set_attrib(iq->node, "to", xfer->who);
162
0
    jabber_iq_set_id(iq, jsx->iq_id);
163
0
    query = xmlnode_get_child(iq->node, "query");
164
0
    su = xmlnode_new_child(query, "streamhost-used");
165
0
    xmlnode_set_attrib(su, "jid", streamhost->jid);
166
0
  }
167
168
0
  jabber_iq_send(iq);
169
170
0
  purple_xfer_start(xfer, source, NULL, -1);
171
0
}
172
173
static gboolean
174
connect_timeout_cb(gpointer data)
175
0
{
176
0
  PurpleXfer *xfer = data;
177
0
  JabberSIXfer *jsx = xfer->data;
178
179
0
  purple_debug_info("jabber", "Streamhost connection timeout of %d seconds exceeded.\n", STREAMHOST_CONNECT_TIMEOUT);
180
181
0
  jsx->connect_timeout = 0;
182
183
0
  if (jsx->connect_data != NULL)
184
0
    purple_proxy_connect_cancel(jsx->connect_data);
185
0
  jsx->connect_data = NULL;
186
187
  /* Trigger the connect error manually */
188
0
  jabber_si_bytestreams_connect_cb(xfer, -1, "Timeout Exceeded.");
189
190
0
  return FALSE;
191
0
}
192
193
static void
194
jabber_si_bytestreams_ibb_timeout_remove(JabberSIXfer *jsx)
195
0
{
196
0
  if (jsx->ibb_timeout_handle) {
197
0
    purple_timeout_remove(jsx->ibb_timeout_handle);
198
0
    jsx->ibb_timeout_handle = 0;
199
0
  }
200
0
}
201
202
static gboolean
203
jabber_si_bytestreams_ibb_timeout_cb(gpointer data)
204
0
{
205
0
  PurpleXfer *xfer = (PurpleXfer *) data;
206
0
  JabberSIXfer *jsx = xfer->data;
207
208
0
  if (jsx && !jsx->ibb_session) {
209
0
    purple_debug_info("jabber",
210
0
      "jabber_si_bytestreams_ibb_timeout called and IBB session not set "
211
0
      " up yet, cancel transfer");
212
0
    jabber_si_bytestreams_ibb_timeout_remove(jsx);
213
0
    purple_xfer_cancel_local(xfer);
214
0
  }
215
216
0
  return FALSE;
217
0
}
218
219
static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
220
0
{
221
0
  JabberSIXfer *jsx = xfer->data;
222
0
  JabberBytestreamsStreamhost *streamhost;
223
0
  JabberID *dstjid;
224
225
0
  if(!jsx->streamhosts) {
226
0
    JabberIq *iq = jabber_iq_new(jsx->js, JABBER_IQ_ERROR);
227
0
    xmlnode *error, *inf;
228
229
0
    if(jsx->iq_id)
230
0
      jabber_iq_set_id(iq, jsx->iq_id);
231
232
0
    xmlnode_set_attrib(iq->node, "to", xfer->who);
233
0
    error = xmlnode_new_child(iq->node, "error");
234
0
    xmlnode_set_attrib(error, "code", "404");
235
0
    xmlnode_set_attrib(error, "type", "cancel");
236
0
    inf = xmlnode_new_child(error, "item-not-found");
237
0
    xmlnode_set_namespace(inf, NS_XMPP_STANZAS);
238
239
0
    jabber_iq_send(iq);
240
241
    /* if IBB is available, revert to that before giving up... */
242
0
    if (jsx->stream_method & STREAM_METHOD_IBB) {
243
      /* if we are the initializer, init IBB */
244
0
      purple_debug_info("jabber",
245
0
        "jabber_si_bytestreams_attempt_connect: "
246
0
        "no streamhosts found, trying IBB\n");
247
      /* if we are the sender, open an IBB session, but not if we already
248
        did it, since we could have received the error <iq/> from the
249
        receiver already... */
250
0
      if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
251
0
        && !jsx->ibb_session) {
252
0
        jabber_si_xfer_ibb_send_init(jsx->js, xfer);
253
0
      } else {
254
        /* setup a timeout to cancel waiting for IBB open */
255
0
        jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
256
0
          jabber_si_bytestreams_ibb_timeout_cb, xfer);
257
0
      }
258
      /* if we are the receiver, just wait for IBB open, callback is
259
        already set up... */
260
0
    } else {
261
0
      purple_xfer_cancel_local(xfer);
262
0
    }
263
264
0
    return;
265
0
  }
266
267
0
  streamhost = jsx->streamhosts->data;
268
269
0
  jsx->connect_data = NULL;
270
0
  if (jsx->gpi != NULL)
271
0
    purple_proxy_info_destroy(jsx->gpi);
272
0
  jsx->gpi = NULL;
273
274
0
  dstjid = jabber_id_new(xfer->who);
275
276
  /* TODO: Deal with zeroconf */
277
278
0
  if(dstjid != NULL && streamhost->host && streamhost->port > 0) {
279
0
    char *dstaddr, *hash;
280
0
    PurpleAccount *account;
281
0
    jsx->gpi = purple_proxy_info_new();
282
0
    purple_proxy_info_set_type(jsx->gpi, PURPLE_PROXY_SOCKS5);
283
0
    purple_proxy_info_set_host(jsx->gpi, streamhost->host);
284
0
    purple_proxy_info_set_port(jsx->gpi, streamhost->port);
285
286
    /* unknown file transfer type is assumed to be RECEIVE */
287
0
    if(xfer->type == PURPLE_XFER_SEND)
288
0
      dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, jsx->js->user->node, jsx->js->user->domain,
289
0
        jsx->js->user->resource, dstjid->node, dstjid->domain, dstjid->resource);
290
0
    else
291
0
      dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, dstjid->node, dstjid->domain, dstjid->resource,
292
0
        jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource);
293
294
    /* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
295
0
    hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1");
296
297
0
    account = purple_connection_get_account(jsx->js->gc);
298
0
    jsx->connect_data = purple_proxy_connect_socks5_account(NULL, account,
299
0
        jsx->gpi, hash, 0,
300
0
        jabber_si_bytestreams_connect_cb, xfer);
301
0
    g_free(hash);
302
0
    g_free(dstaddr);
303
304
    /* When selecting a streamhost, timeout after STREAMHOST_CONNECT_TIMEOUT seconds, otherwise it takes forever */
305
0
    if (xfer->type != PURPLE_XFER_SEND && jsx->connect_data != NULL)
306
0
      jsx->connect_timeout = purple_timeout_add_seconds(
307
0
        STREAMHOST_CONNECT_TIMEOUT, connect_timeout_cb, xfer);
308
309
0
    jabber_id_free(dstjid);
310
0
  }
311
312
0
  if (jsx->connect_data == NULL)
313
0
  {
314
0
    jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
315
0
    jabber_si_free_streamhost(streamhost, NULL);
316
0
    jabber_si_bytestreams_attempt_connect(xfer);
317
0
  }
318
0
}
319
320
void jabber_bytestreams_parse(JabberStream *js, const char *from,
321
                              JabberIqType type, const char *id, xmlnode *query)
322
0
{
323
0
  PurpleXfer *xfer;
324
0
  JabberSIXfer *jsx;
325
0
  xmlnode *streamhost;
326
0
  const char *sid;
327
328
0
  if(type != JABBER_IQ_SET)
329
0
    return;
330
331
0
  if(!from)
332
0
    return;
333
334
0
  if(!(sid = xmlnode_get_attrib(query, "sid")))
335
0
    return;
336
337
0
  if(!(xfer = jabber_si_xfer_find(js, sid, from)))
338
0
    return;
339
340
0
  jsx = xfer->data;
341
342
0
  if(!jsx->accepted)
343
0
    return;
344
345
0
  if(jsx->iq_id)
346
0
    g_free(jsx->iq_id);
347
0
  jsx->iq_id = g_strdup(id);
348
349
0
  for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost;
350
0
      streamhost = xmlnode_get_next_twin(streamhost)) {
351
0
    const char *jid, *host = NULL, *port, *zeroconf;
352
0
    int portnum = 0;
353
354
0
    if((jid = xmlnode_get_attrib(streamhost, "jid")) &&
355
0
        ((zeroconf = xmlnode_get_attrib(streamhost, "zeroconf")) ||
356
0
        ((host = xmlnode_get_attrib(streamhost, "host")) &&
357
0
        (port = xmlnode_get_attrib(streamhost, "port")) &&
358
0
        (portnum = atoi(port))))) {
359
      /* ignore 0.0.0.0 */
360
0
      if(purple_strequal(host, "0.0.0.0") == FALSE) {
361
0
        JabberBytestreamsStreamhost *sh = g_new0(JabberBytestreamsStreamhost, 1);
362
0
        sh->jid = g_strdup(jid);
363
0
        sh->host = g_strdup(host);
364
0
        sh->port = portnum;
365
0
        sh->zeroconf = g_strdup(zeroconf);
366
367
        /* If there were a lot of these, it'd be worthwhile to prepend and reverse. */
368
0
        jsx->streamhosts = g_list_append(jsx->streamhosts, sh);
369
0
      }
370
0
    }
371
0
  }
372
373
0
  jabber_si_bytestreams_attempt_connect(xfer);
374
0
}
375
376
377
static void
378
jabber_si_xfer_bytestreams_send_read_again_resp_cb(gpointer data, gint source,
379
    PurpleInputCondition cond)
380
0
{
381
0
  PurpleXfer *xfer = data;
382
0
  JabberSIXfer *jsx = xfer->data;
383
0
  int len;
384
385
0
  len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen);
386
0
  if (len < 0 && errno == EAGAIN)
387
0
    return;
388
0
  else if (len < 0) {
389
0
    purple_input_remove(xfer->watcher);
390
0
    xfer->watcher = 0;
391
0
    g_free(jsx->rxqueue);
392
0
    jsx->rxqueue = NULL;
393
0
    close(source);
394
0
    purple_xfer_cancel_remote(xfer);
395
0
    return;
396
0
  }
397
0
  jsx->rxlen += len;
398
399
0
  if (jsx->rxlen < jsx->rxmaxlen)
400
0
    return;
401
402
0
  purple_input_remove(xfer->watcher);
403
0
  xfer->watcher = 0;
404
0
  g_free(jsx->rxqueue);
405
0
  jsx->rxqueue = NULL;
406
407
  /* Before actually starting sending the file, we need to wait until the
408
   * recipient sends the IQ result with <streamhost-used/>
409
   */
410
0
  purple_debug_info("jabber", "SOCKS5 connection negotiation completed. "
411
0
            "Waiting for IQ result to start file transfer.\n");
412
0
}
413
414
static void
415
jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source,
416
    PurpleInputCondition cond)
417
0
{
418
0
  PurpleXfer *xfer = data;
419
0
  JabberSIXfer *jsx = xfer->data;
420
0
  char buffer[42]; /* 40 for DST.ADDR + 2 bytes for port number*/
421
0
  int len;
422
0
  char *dstaddr, *hash;
423
0
  const char *host;
424
425
0
  purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_again_cb\n");
426
427
0
  if(jsx->rxlen < 5) {
428
0
    purple_debug_info("jabber", "reading the first 5 bytes\n");
429
0
    len = read(source, buffer, 5 - jsx->rxlen);
430
0
    if(len < 0 && errno == EAGAIN)
431
0
      return;
432
0
    else if(len <= 0) {
433
0
      purple_input_remove(xfer->watcher);
434
0
      xfer->watcher = 0;
435
0
      close(source);
436
0
      purple_xfer_cancel_remote(xfer);
437
0
      return;
438
0
    }
439
0
    jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
440
0
    memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
441
0
    jsx->rxlen += len;
442
0
    return;
443
0
  } else if(jsx->rxqueue[0] != 0x05 || jsx->rxqueue[1] != 0x01 ||
444
0
      jsx->rxqueue[3] != 0x03 || jsx->rxqueue[4] != 40) {
445
0
    purple_debug_info("jabber", "Invalid socks5 conn req. header[0x%x,0x%x,0x%x,0x%x,0x%x]\n",
446
0
          jsx->rxqueue[0], jsx->rxqueue[1], jsx->rxqueue[2],
447
0
          jsx->rxqueue[3], jsx->rxqueue[4]);
448
0
    purple_input_remove(xfer->watcher);
449
0
    xfer->watcher = 0;
450
0
    close(source);
451
0
    purple_xfer_cancel_remote(xfer);
452
0
    return;
453
0
  } else if(jsx->rxlen - 5 < (size_t)jsx->rxqueue[4] + 2) {
454
    /* Upper-bound of 257 (jsx->rxlen = 5, jsx->rxqueue[4] = 0xFF) */
455
0
    unsigned short to_read = jsx->rxqueue[4] + 2 - (jsx->rxlen - 5);
456
0
    purple_debug_info("jabber", "reading %u bytes for DST.ADDR + port num (trying to read %hu now)\n",
457
0
          jsx->rxqueue[4] + 2, to_read);
458
0
    len = read(source, buffer, to_read);
459
0
    if(len < 0 && errno == EAGAIN)
460
0
      return;
461
0
    else if(len <= 0) {
462
0
      purple_input_remove(xfer->watcher);
463
0
      xfer->watcher = 0;
464
0
      close(source);
465
0
      purple_xfer_cancel_remote(xfer);
466
0
      return;
467
0
    }
468
0
    jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
469
0
    memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
470
0
    jsx->rxlen += len;
471
0
  }
472
473
  /* Have we not read all of DST.ADDR and the following 2-byte port number? */
474
0
  if(jsx->rxlen - 5 < (size_t)jsx->rxqueue[4] + 2)
475
0
    return;
476
477
0
  purple_input_remove(xfer->watcher);
478
0
  xfer->watcher = 0;
479
480
0
  dstaddr = g_strdup_printf("%s%s@%s/%s%s", jsx->stream_id,
481
0
      jsx->js->user->node, jsx->js->user->domain,
482
0
      jsx->js->user->resource, xfer->who);
483
484
  /* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
485
0
  hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1");
486
487
0
  if(strncmp(hash, jsx->rxqueue + 5, 40) ||
488
0
      jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00) {
489
0
    if (jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00)
490
0
      purple_debug_error("jabber", "Got SOCKS5 BS conn with the wrong DST.PORT"
491
0
                 " (must be 0 - got[0x%x,0x%x]).\n",
492
0
                 jsx->rxqueue[45], jsx->rxqueue[46]);
493
0
    else
494
0
      purple_debug_error("jabber", "Got SOCKS5 BS conn with the wrong DST.ADDR"
495
0
                 " (expected '%s' - got '%.40s').\n",
496
0
                 hash, jsx->rxqueue + 5);
497
0
    close(source);
498
0
    purple_xfer_cancel_remote(xfer);
499
0
    g_free(hash);
500
0
    g_free(dstaddr);
501
0
    return;
502
0
  }
503
504
0
  g_free(hash);
505
0
  g_free(dstaddr);
506
507
0
  g_free(jsx->rxqueue);
508
0
  host = purple_network_get_my_ip(jsx->js->fd);
509
510
0
  jsx->rxmaxlen = 5 + strlen(host) + 2;
511
0
  jsx->rxqueue = g_malloc(jsx->rxmaxlen);
512
0
  jsx->rxlen = 0;
513
514
0
  jsx->rxqueue[0] = 0x05;
515
0
  jsx->rxqueue[1] = 0x00;
516
0
  jsx->rxqueue[2] = 0x00;
517
0
  jsx->rxqueue[3] = 0x03;
518
0
  jsx->rxqueue[4] = strlen(host);
519
0
  memcpy(jsx->rxqueue + 5, host, strlen(host));
520
0
  jsx->rxqueue[5+strlen(host)] = 0x00;
521
0
  jsx->rxqueue[6+strlen(host)] = 0x00;
522
523
0
  xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
524
0
    jabber_si_xfer_bytestreams_send_read_again_resp_cb, xfer);
525
0
  jabber_si_xfer_bytestreams_send_read_again_resp_cb(xfer, source,
526
0
    PURPLE_INPUT_WRITE);
527
0
}
528
529
static void
530
jabber_si_xfer_bytestreams_send_read_response_cb(gpointer data, gint source,
531
    PurpleInputCondition cond)
532
0
{
533
0
  PurpleXfer *xfer = data;
534
0
  JabberSIXfer *jsx = xfer->data;
535
0
  int len;
536
537
0
  len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen);
538
0
  if (len < 0 && errno == EAGAIN)
539
0
    return;
540
0
  else if (len < 0) {
541
0
    purple_input_remove(xfer->watcher);
542
0
    xfer->watcher = 0;
543
0
    g_free(jsx->rxqueue);
544
0
    jsx->rxqueue = NULL;
545
0
    close(source);
546
0
    purple_xfer_cancel_remote(xfer);
547
0
    return;
548
0
  }
549
0
  jsx->rxlen += len;
550
551
0
  if (jsx->rxlen < jsx->rxmaxlen)
552
0
    return;
553
554
0
  purple_input_remove(xfer->watcher);
555
0
  xfer->watcher = 0;
556
557
  /* If we sent a "Success", wait for a response, otherwise give up and cancel */
558
0
  if (jsx->rxqueue[1] == 0x00) {
559
0
    xfer->watcher = purple_input_add(source, PURPLE_INPUT_READ,
560
0
      jabber_si_xfer_bytestreams_send_read_again_cb, xfer);
561
0
    g_free(jsx->rxqueue);
562
0
    jsx->rxqueue = NULL;
563
0
    jsx->rxlen = 0;
564
0
  } else {
565
0
    close(source);
566
0
    purple_xfer_cancel_remote(xfer);
567
0
  }
568
0
}
569
570
static void
571
jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source,
572
    PurpleInputCondition cond)
573
0
{
574
0
  PurpleXfer *xfer = data;
575
0
  JabberSIXfer *jsx = xfer->data;
576
0
  int i;
577
0
  int len;
578
0
  char buffer[256];
579
580
0
  purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_cb\n");
581
582
0
  xfer->fd = source;
583
584
  /** Try to read the SOCKS5 header */
585
0
  if(jsx->rxlen < 2) {
586
0
    purple_debug_info("jabber", "reading those first two bytes\n");
587
0
    len = read(source, buffer, 2 - jsx->rxlen);
588
0
    if(len < 0 && errno == EAGAIN)
589
0
      return;
590
0
    else if(len <= 0) {
591
0
      purple_input_remove(xfer->watcher);
592
0
      xfer->watcher = 0;
593
0
      close(source);
594
0
      purple_xfer_cancel_remote(xfer);
595
0
      return;
596
0
    }
597
0
    jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
598
0
    memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
599
0
    jsx->rxlen += len;
600
0
    return;
601
0
  } else if(jsx->rxlen - 2 < (size_t)jsx->rxqueue[1]) {
602
    /* Has a maximum value of 255 (jsx->rxlen = 2, jsx->rxqueue[1] = 0xFF) */
603
0
    unsigned short to_read = jsx->rxqueue[1] - (jsx->rxlen - 2);
604
0
    purple_debug_info("jabber", "reading %u bytes for auth methods (trying to read %hu now)\n",
605
0
          jsx->rxqueue[1], to_read);
606
0
    len = read(source, buffer, to_read);
607
0
    if(len < 0 && errno == EAGAIN)
608
0
      return;
609
0
    else if(len <= 0) {
610
0
      purple_input_remove(xfer->watcher);
611
0
      xfer->watcher = 0;
612
0
      close(source);
613
0
      purple_xfer_cancel_remote(xfer);
614
0
      return;
615
0
    }
616
0
    jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
617
0
    memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
618
0
    jsx->rxlen += len;
619
0
  }
620
621
  /* Have we not read all the auth. method bytes? */
622
0
  if(jsx->rxlen -2 < (size_t)jsx->rxqueue[1])
623
0
    return;
624
625
0
  purple_input_remove(xfer->watcher);
626
0
  xfer->watcher = 0;
627
628
0
  purple_debug_info("jabber", "checking to make sure we're socks FIVE\n");
629
630
0
  if(jsx->rxqueue[0] != 0x05) {
631
0
    close(source);
632
0
    purple_xfer_cancel_remote(xfer);
633
0
    return;
634
0
  }
635
636
0
  purple_debug_info("jabber", "going to test %hhu different methods\n", jsx->rxqueue[1]);
637
638
0
  for(i=0; i<jsx->rxqueue[1]; i++) {
639
640
0
    purple_debug_info("jabber", "testing %hhu\n", jsx->rxqueue[i+2]);
641
0
    if(jsx->rxqueue[i+2] == 0x00) {
642
0
      g_free(jsx->rxqueue);
643
0
      jsx->rxlen = 0;
644
0
      jsx->rxmaxlen = 2;
645
0
      jsx->rxqueue = g_malloc(jsx->rxmaxlen);
646
0
      jsx->rxqueue[0] = 0x05;
647
0
      jsx->rxqueue[1] = 0x00;
648
0
      xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
649
0
        jabber_si_xfer_bytestreams_send_read_response_cb,
650
0
        xfer);
651
0
      jabber_si_xfer_bytestreams_send_read_response_cb(xfer,
652
0
        source, PURPLE_INPUT_WRITE);
653
0
      jsx->rxqueue = NULL;
654
0
      jsx->rxlen = 0;
655
0
      return;
656
0
    }
657
0
  }
658
659
0
  g_free(jsx->rxqueue);
660
0
  jsx->rxlen = 0;
661
0
  jsx->rxmaxlen = 2;
662
0
  jsx->rxqueue = g_malloc(jsx->rxmaxlen);
663
0
  jsx->rxqueue[0] = 0x05;
664
0
  jsx->rxqueue[1] = 0xFF;
665
0
  xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
666
0
    jabber_si_xfer_bytestreams_send_read_response_cb, xfer);
667
0
  jabber_si_xfer_bytestreams_send_read_response_cb(xfer,
668
0
    source, PURPLE_INPUT_WRITE);
669
0
}
670
671
static gint
672
jabber_si_compare_jid(gconstpointer a, gconstpointer b)
673
0
{
674
0
  const JabberBytestreamsStreamhost *sh = a;
675
676
0
  if(!a)
677
0
    return -1;
678
679
0
  return strcmp(sh->jid, (char *)b);
680
0
}
681
682
static void
683
jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source,
684
    PurpleInputCondition cond)
685
0
{
686
0
  PurpleXfer *xfer = data;
687
0
  JabberSIXfer *jsx = xfer->data;
688
0
  int acceptfd;
689
690
0
  purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n");
691
692
0
  acceptfd = accept(source, NULL, 0);
693
0
  if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
694
0
    return;
695
0
  else if(acceptfd == -1) {
696
0
    purple_debug_warning("jabber", "accept: %s\n", g_strerror(errno));
697
    /* Don't cancel the ft - allow it to fall to the next streamhost.*/
698
0
    return;
699
0
  }
700
701
0
  purple_input_remove(xfer->watcher);
702
0
  close(source);
703
0
  jsx->local_streamhost_fd = -1;
704
705
0
  _purple_network_set_common_socket_flags(acceptfd);
706
707
0
  xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
708
0
           jabber_si_xfer_bytestreams_send_read_cb, xfer);
709
0
}
710
711
static void
712
jabber_si_connect_proxy_cb(JabberStream *js, const char *from,
713
                           JabberIqType type, const char *id,
714
                           xmlnode *packet, gpointer data)
715
0
{
716
0
  PurpleXfer *xfer = data;
717
0
  JabberSIXfer *jsx;
718
0
  xmlnode *query, *streamhost_used;
719
0
  const char *jid;
720
0
  GList *matched;
721
722
  /* TODO: This need to send errors if we don't see what we're looking for */
723
724
  /* Make sure that the xfer is actually still valid and we're not just receiving an old iq response */
725
0
  if (!g_list_find(js->file_transfers, xfer)) {
726
0
    purple_debug_error("jabber", "Got bytestreams response for no longer existing xfer (%p)\n", xfer);
727
0
    return;
728
0
  }
729
730
  /* In the case of a direct file transfer, this is expected to return */
731
0
  if(!xfer->data)
732
0
    return;
733
734
0
  jsx = xfer->data;
735
736
0
  if(type != JABBER_IQ_RESULT) {
737
0
    purple_debug_info("jabber",
738
0
          "jabber_si_xfer_connect_proxy_cb: type = error\n");
739
    /* if IBB is available, open IBB session */
740
0
    purple_debug_info("jabber",
741
0
      "jabber_si_xfer_connect_proxy_cb: got error, method: %d\n",
742
0
      jsx->stream_method);
743
0
    if (jsx->stream_method & STREAM_METHOD_IBB) {
744
0
      purple_debug_info("jabber", "IBB is possible, try it\n");
745
      /* if we are the sender and haven't already opened an IBB
746
        session, do so now (we might already have failed to open
747
        the bytestream proxy ourselves when receiving this <iq/> */
748
0
      if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
749
0
        && !jsx->ibb_session) {
750
0
        jabber_si_xfer_ibb_send_init(js, xfer);
751
0
      } else {
752
0
        jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
753
0
          jabber_si_bytestreams_ibb_timeout_cb, xfer);
754
0
      }
755
      /* if we are receiver, just wait for IBB open stanza, callback
756
        is already set up */
757
0
    } else {
758
0
      purple_xfer_cancel_remote(xfer);
759
0
    }
760
0
    return;
761
0
  }
762
763
0
  if (!from)
764
0
    return;
765
766
0
  if(!(query = xmlnode_get_child(packet, "query")))
767
0
    return;
768
769
0
  if(!(streamhost_used = xmlnode_get_child(query, "streamhost-used")))
770
0
    return;
771
772
0
  if(!(jid = xmlnode_get_attrib(streamhost_used, "jid")))
773
0
    return;
774
775
0
  purple_debug_info("jabber", "jabber_si_connect_proxy_cb() will be looking at jsx %p: jsx->streamhosts is %p and jid is %s\n",
776
0
            jsx, jsx->streamhosts, jid);
777
778
0
  if(!(matched = g_list_find_custom(jsx->streamhosts, jid, jabber_si_compare_jid)))
779
0
  {
780
0
    gchar *my_jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
781
0
      jsx->js->user->domain, jsx->js->user->resource);
782
0
    if (purple_strequal(jid, my_jid)) {
783
0
      purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n");
784
0
      purple_xfer_start(xfer, xfer->fd, NULL, -1);
785
0
    } else {
786
      /* if available, try to revert to IBB... */
787
0
      if (jsx->stream_method & STREAM_METHOD_IBB) {
788
0
        purple_debug_info("jabber",
789
0
          "jabber_si_connect_proxy_cb: trying to revert to IBB\n");
790
0
        if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
791
0
          jabber_si_xfer_ibb_send_init(jsx->js, xfer);
792
0
        } else {
793
0
          jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
794
0
            jabber_si_bytestreams_ibb_timeout_cb, xfer);
795
0
        }
796
        /* if we are the receiver, we are already set up...*/
797
0
      } else {
798
0
        purple_debug_info("jabber",
799
0
          "streamhost-used does not match any proxy that was offered to target\n");
800
0
        purple_xfer_cancel_local(xfer);
801
0
      }
802
0
    }
803
0
    g_free(my_jid);
804
0
    return;
805
0
  }
806
807
  /* Clean up the local streamhost - it isn't going to be used.*/
808
0
  if (xfer->watcher > 0) {
809
0
    purple_input_remove(xfer->watcher);
810
0
    xfer->watcher = 0;
811
0
  }
812
0
  if (jsx->local_streamhost_fd >= 0) {
813
0
    close(jsx->local_streamhost_fd);
814
0
    jsx->local_streamhost_fd = -1;
815
0
  }
816
817
0
  jsx->streamhosts = g_list_remove_link(jsx->streamhosts, matched);
818
0
  g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
819
0
  g_list_free(jsx->streamhosts);
820
821
0
  jsx->streamhosts = matched;
822
823
0
  jabber_si_bytestreams_attempt_connect(xfer);
824
0
}
825
826
static void
827
jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
828
0
{
829
0
  PurpleXfer *xfer = data;
830
0
  JabberSIXfer *jsx;
831
0
  JabberIq *iq;
832
0
  xmlnode *query, *streamhost;
833
0
  char port[6];
834
0
  GList *tmp;
835
0
  JabberBytestreamsStreamhost *sh, *sh2;
836
0
  int streamhost_count = 0;
837
838
0
  jsx = xfer->data;
839
0
  jsx->listen_data = NULL;
840
841
  /* I'm not sure under which conditions this can happen
842
   * (it seems like it shouldn't be possible */
843
0
  if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) {
844
0
    purple_xfer_unref(xfer);
845
0
    return;
846
0
  }
847
848
0
  purple_xfer_unref(xfer);
849
850
0
  iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, NS_BYTESTREAMS);
851
0
  xmlnode_set_attrib(iq->node, "to", xfer->who);
852
0
  query = xmlnode_get_child(iq->node, "query");
853
854
0
  xmlnode_set_attrib(query, "sid", jsx->stream_id);
855
856
  /* If we successfully started listening locally */
857
0
  if (sock >= 0) {
858
0
    gchar *jid;
859
0
    GList *local_ips =
860
0
      purple_network_get_all_local_system_ips();
861
0
    const char *public_ip;
862
0
    gboolean has_public_ip = FALSE;
863
864
0
    jsx->local_streamhost_fd = sock;
865
866
0
    jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
867
0
      jsx->js->user->domain, jsx->js->user->resource);
868
0
    xfer->local_port = purple_network_get_port_from_fd(sock);
869
0
    g_snprintf(port, sizeof(port), "%hu", (guint16)xfer->local_port);
870
871
0
    public_ip = purple_network_get_my_ip(jsx->js->fd);
872
873
    /* Include the localhost's IPs (for in-network transfers) */
874
0
    while (local_ips) {
875
0
      gchar *local_ip = local_ips->data;
876
0
      streamhost_count++;
877
0
      streamhost = xmlnode_new_child(query, "streamhost");
878
0
      xmlnode_set_attrib(streamhost, "jid", jid);
879
0
      xmlnode_set_attrib(streamhost, "host", local_ip);
880
0
      xmlnode_set_attrib(streamhost, "port", port);
881
0
      if (purple_strequal(local_ip, public_ip))
882
0
        has_public_ip = TRUE;
883
0
      g_free(local_ip);
884
0
      local_ips = g_list_delete_link(local_ips, local_ips);
885
0
    }
886
887
    /* Include the public IP (assuming that there is a port mapped somehow) */
888
0
    if (!has_public_ip && !purple_strequal(public_ip, "0.0.0.0")) {
889
0
      streamhost_count++;
890
0
      streamhost = xmlnode_new_child(query, "streamhost");
891
0
      xmlnode_set_attrib(streamhost, "jid", jid);
892
0
      xmlnode_set_attrib(streamhost, "host", public_ip);
893
0
      xmlnode_set_attrib(streamhost, "port", port);
894
0
    }
895
896
0
    g_free(jid);
897
898
    /* The listener for the local proxy */
899
0
    xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
900
0
        jabber_si_xfer_bytestreams_send_connected_cb, xfer);
901
0
  }
902
903
0
  for (tmp = jsx->js->bs_proxies; tmp; tmp = tmp->next) {
904
0
    sh = tmp->data;
905
906
    /* TODO: deal with zeroconf proxies */
907
908
0
    if (!(sh->jid && sh->host && sh->port > 0))
909
0
      continue;
910
911
0
    purple_debug_info("jabber", "jabber_si_xfer_bytestreams_listen_cb() will be looking at jsx %p: jsx->streamhosts %p and sh->jid %p\n",
912
0
              jsx, jsx->streamhosts, sh->jid);
913
0
    if(g_list_find_custom(jsx->streamhosts, sh->jid, jabber_si_compare_jid) != NULL)
914
0
      continue;
915
916
0
    streamhost_count++;
917
0
    streamhost = xmlnode_new_child(query, "streamhost");
918
0
    xmlnode_set_attrib(streamhost, "jid", sh->jid);
919
0
    xmlnode_set_attrib(streamhost, "host", sh->host);
920
0
    g_snprintf(port, sizeof(port), "%hu", (guint16)sh->port);
921
0
    xmlnode_set_attrib(streamhost, "port", port);
922
923
0
    sh2 = g_new0(JabberBytestreamsStreamhost, 1);
924
0
    sh2->jid = g_strdup(sh->jid);
925
0
    sh2->host = g_strdup(sh->host);
926
    /*sh2->zeroconf = g_strdup(sh->zeroconf);*/
927
0
    sh2->port = sh->port;
928
929
0
    jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh2);
930
0
  }
931
932
  /* We have no way of transferring, cancel the transfer */
933
0
  if (streamhost_count == 0) {
934
0
    jabber_iq_free(iq);
935
936
    /* if available, revert to IBB */
937
0
    if (jsx->stream_method & STREAM_METHOD_IBB) {
938
0
      purple_debug_info("jabber",
939
0
        "jabber_si_xfer_bytestreams_listen_cb: trying to revert to IBB\n");
940
0
      if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
941
        /* if we are the sender, init the IBB session... */
942
0
        jabber_si_xfer_ibb_send_init(jsx->js, xfer);
943
0
      } else {
944
0
        jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
945
0
          jabber_si_bytestreams_ibb_timeout_cb, xfer);
946
0
      }
947
      /* if we are the receiver, we should just wait... the IBB open
948
        handler has already been set up... */
949
0
    } else {
950
      /* We should probably notify the target,
951
        but this really shouldn't ever happen */
952
0
      purple_xfer_cancel_local(xfer);
953
0
    }
954
955
0
    return;
956
0
  }
957
958
0
  jabber_iq_set_callback(iq, jabber_si_connect_proxy_cb, xfer);
959
960
0
  jabber_iq_send(iq);
961
962
0
}
963
964
static void
965
jabber_si_xfer_bytestreams_send_init(PurpleXfer *xfer)
966
0
{
967
0
  JabberSIXfer *jsx;
968
0
  PurpleProxyType proxy_type;
969
970
0
  purple_xfer_ref(xfer);
971
972
0
  jsx = xfer->data;
973
974
  /* TODO: This should probably be done with an account option instead of
975
   *       piggy-backing on the TOR proxy type. */
976
0
  proxy_type = purple_proxy_info_get_type(
977
0
    purple_proxy_get_setup(purple_connection_get_account(jsx->js->gc)));
978
0
  if (proxy_type == PURPLE_PROXY_TOR) {
979
0
    purple_debug_info("jabber", "Skipping attempting local streamhost.\n");
980
0
    jsx->listen_data = NULL;
981
0
  } else
982
0
    jsx->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
983
0
        jabber_si_xfer_bytestreams_listen_cb, xfer);
984
985
0
  if (jsx->listen_data == NULL) {
986
    /* We couldn't open a local port.  Perhaps we can use a proxy. */
987
0
    jabber_si_xfer_bytestreams_listen_cb(-1, xfer);
988
0
  }
989
990
0
}
991
992
static void
993
jabber_si_xfer_ibb_error_cb(JabberIBBSession *sess)
994
0
{
995
0
  PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
996
997
0
  purple_debug_error("jabber", "an error occurred during IBB file transfer\n");
998
0
  purple_xfer_cancel_remote(xfer);
999
0
}
1000
1001
static void
1002
jabber_si_xfer_ibb_closed_cb(JabberIBBSession *sess)
1003
0
{
1004
0
  PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
1005
1006
0
  purple_debug_info("jabber", "the remote user closed the transfer\n");
1007
0
  if (purple_xfer_get_bytes_remaining(xfer) > 0) {
1008
0
    purple_xfer_cancel_remote(xfer);
1009
0
  } else {
1010
0
    purple_xfer_set_completed(xfer, TRUE);
1011
0
    purple_xfer_end(xfer);
1012
0
  }
1013
0
}
1014
1015
static void
1016
jabber_si_xfer_ibb_recv_data_cb(JabberIBBSession *sess, gpointer data,
1017
  gsize size)
1018
0
{
1019
0
  PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
1020
0
  JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1021
1022
0
  if (size <= purple_xfer_get_bytes_remaining(xfer)) {
1023
0
    purple_debug_info("jabber", "about to write %" G_GSIZE_FORMAT " bytes from IBB stream\n",
1024
0
      size);
1025
0
    purple_circ_buffer_append(jsx->ibb_buffer, data, size);
1026
0
    purple_xfer_prpl_ready(xfer);
1027
0
  } else {
1028
    /* trying to write past size of file transfers negotiated size,
1029
      reject transfer to protect against malicious behaviour */
1030
0
    purple_debug_error("jabber",
1031
0
      "IBB file transfer send more data than expected\n");
1032
0
    purple_xfer_cancel_remote(xfer);
1033
0
  }
1034
1035
0
}
1036
1037
static gssize
1038
jabber_si_xfer_ibb_read(guchar **out_buffer, PurpleXfer *xfer)
1039
0
{
1040
0
  JabberSIXfer *jsx = xfer->data;
1041
0
  guchar *buffer;
1042
0
  gsize size;
1043
0
  gsize tmp;
1044
1045
0
  size = jsx->ibb_buffer->bufused;
1046
0
  *out_buffer = buffer = g_malloc(size);
1047
0
  while ((tmp = purple_circ_buffer_get_max_read(jsx->ibb_buffer))) {
1048
0
    memcpy(buffer, jsx->ibb_buffer->outptr, tmp);
1049
0
    buffer += tmp;
1050
0
    purple_circ_buffer_mark_read(jsx->ibb_buffer, tmp);
1051
0
  }
1052
1053
0
  return size;
1054
0
}
1055
1056
static gboolean
1057
jabber_si_xfer_ibb_open_cb(JabberStream *js, const char *who, const char *id,
1058
                           xmlnode *open)
1059
0
{
1060
0
  const gchar *sid = xmlnode_get_attrib(open, "sid");
1061
0
  PurpleXfer *xfer = jabber_si_xfer_find(js, sid, who);
1062
0
  if (xfer) {
1063
0
    JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1064
0
    JabberIBBSession *sess =
1065
0
      jabber_ibb_session_create_from_xmlnode(js, who, id, open, xfer);
1066
1067
0
    jabber_si_bytestreams_ibb_timeout_remove(jsx);
1068
1069
0
    if (sess) {
1070
      /* setup callbacks here...*/
1071
0
      jabber_ibb_session_set_data_received_callback(sess,
1072
0
        jabber_si_xfer_ibb_recv_data_cb);
1073
0
      jabber_ibb_session_set_closed_callback(sess,
1074
0
        jabber_si_xfer_ibb_closed_cb);
1075
0
      jabber_ibb_session_set_error_callback(sess,
1076
0
        jabber_si_xfer_ibb_error_cb);
1077
1078
0
      jsx->ibb_session = sess;
1079
      /* we handle up to block-size bytes of decoded data, to handle
1080
       clients interpreting the block-size attribute as that
1081
       (see also remark in ibb.c) */
1082
0
      jsx->ibb_buffer =
1083
0
        purple_circ_buffer_new(jabber_ibb_session_get_block_size(sess));
1084
1085
      /* set up read function */
1086
0
      purple_xfer_set_read_fnc(xfer, jabber_si_xfer_ibb_read);
1087
1088
      /* start the transfer */
1089
0
      purple_xfer_start(xfer, -1, NULL, 0);
1090
0
      return TRUE;
1091
0
    } else {
1092
      /* failed to create IBB session */
1093
0
      purple_debug_error("jabber", "failed to create IBB session\n");
1094
0
      purple_xfer_cancel_remote(xfer);
1095
0
      return FALSE;
1096
0
    }
1097
0
  } else {
1098
    /* we got an IBB <open/> for an unknown file transfer, pass along... */
1099
0
    purple_debug_info("jabber",
1100
0
      "IBB open did not match any SI file transfer\n");
1101
0
    return FALSE;
1102
0
  }
1103
0
}
1104
1105
static gssize
1106
jabber_si_xfer_ibb_write(const guchar *buffer, size_t len, PurpleXfer *xfer)
1107
0
{
1108
0
  JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1109
0
  JabberIBBSession *sess = jsx->ibb_session;
1110
0
  gsize packet_size = len < jabber_ibb_session_get_max_data_size(sess) ?
1111
0
    len : jabber_ibb_session_get_max_data_size(sess);
1112
1113
0
  jabber_ibb_session_send_data(sess, buffer, packet_size);
1114
1115
0
  return packet_size;
1116
0
}
1117
1118
static void
1119
jabber_si_xfer_ibb_sent_cb(JabberIBBSession *sess)
1120
0
{
1121
0
  PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
1122
0
  gsize remaining = purple_xfer_get_bytes_remaining(xfer);
1123
1124
0
  if (remaining == 0) {
1125
    /* close the session */
1126
0
    jabber_ibb_session_close(sess);
1127
0
    purple_xfer_set_completed(xfer, TRUE);
1128
0
    purple_xfer_end(xfer);
1129
0
  } else {
1130
    /* send more... */
1131
0
    purple_xfer_prpl_ready(xfer);
1132
0
  }
1133
0
}
1134
1135
static void
1136
jabber_si_xfer_ibb_opened_cb(JabberIBBSession *sess)
1137
0
{
1138
0
  PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
1139
1140
0
  if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_OPENED) {
1141
0
    purple_xfer_start(xfer, -1, NULL, 0);
1142
0
    purple_xfer_prpl_ready(xfer);
1143
0
  } else {
1144
    /* error */
1145
0
    purple_xfer_end(xfer);
1146
0
  }
1147
0
}
1148
1149
static void
1150
jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer)
1151
0
{
1152
0
  JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1153
1154
0
  jsx->ibb_session = jabber_ibb_session_create(js, jsx->stream_id,
1155
0
    purple_xfer_get_remote_user(xfer), xfer);
1156
1157
0
  if (jsx->ibb_session) {
1158
    /* should set callbacks here... */
1159
0
    jabber_ibb_session_set_opened_callback(jsx->ibb_session,
1160
0
      jabber_si_xfer_ibb_opened_cb);
1161
0
    jabber_ibb_session_set_data_sent_callback(jsx->ibb_session,
1162
0
      jabber_si_xfer_ibb_sent_cb);
1163
0
    jabber_ibb_session_set_closed_callback(jsx->ibb_session,
1164
0
      jabber_si_xfer_ibb_closed_cb);
1165
0
    jabber_ibb_session_set_error_callback(jsx->ibb_session,
1166
0
      jabber_si_xfer_ibb_error_cb);
1167
1168
0
    purple_xfer_set_write_fnc(xfer, jabber_si_xfer_ibb_write);
1169
1170
0
    jsx->ibb_buffer =
1171
0
      purple_circ_buffer_new(jabber_ibb_session_get_max_data_size(jsx->ibb_session));
1172
1173
    /* open the IBB session */
1174
0
    jabber_ibb_session_open(jsx->ibb_session);
1175
1176
0
  } else {
1177
    /* failed to create IBB session */
1178
0
    purple_debug_error("jabber",
1179
0
      "failed to initiate IBB session for file transfer\n");
1180
0
    purple_xfer_cancel_local(xfer);
1181
0
  }
1182
0
}
1183
1184
static void jabber_si_xfer_send_method_cb(JabberStream *js, const char *from,
1185
                                          JabberIqType type, const char *id,
1186
                                          xmlnode *packet, gpointer data)
1187
0
{
1188
0
  PurpleXfer *xfer = data;
1189
0
  xmlnode *si, *feature, *x, *field, *value;
1190
0
  gboolean found_method = FALSE;
1191
1192
0
  if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) {
1193
0
    purple_xfer_cancel_remote(xfer);
1194
0
    return;
1195
0
  }
1196
1197
0
  if(!(feature = xmlnode_get_child_with_namespace(si, "feature", "http://jabber.org/protocol/feature-neg"))) {
1198
0
    purple_xfer_cancel_remote(xfer);
1199
0
    return;
1200
0
  }
1201
1202
0
  if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) {
1203
0
    purple_xfer_cancel_remote(xfer);
1204
0
    return;
1205
0
  }
1206
1207
0
  for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
1208
0
    const char *var = xmlnode_get_attrib(field, "var");
1209
0
    JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1210
1211
0
    if(purple_strequal(var, "stream-method")) {
1212
0
      if((value = xmlnode_get_child(field, "value"))) {
1213
0
        char *val = xmlnode_get_data(value);
1214
0
        if(purple_strequal(val, NS_BYTESTREAMS)) {
1215
0
          jabber_si_xfer_bytestreams_send_init(xfer);
1216
0
          jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
1217
0
          found_method = TRUE;
1218
0
        } else if (purple_strequal(val, NS_IBB)) {
1219
0
          jsx->stream_method |= STREAM_METHOD_IBB;
1220
0
          if (!found_method) {
1221
            /* we haven't tried to init a bytestream session, yet
1222
              start IBB right away... */
1223
0
            jabber_si_xfer_ibb_send_init(js, xfer);
1224
0
            found_method = TRUE;
1225
0
          }
1226
0
        }
1227
0
        g_free(val);
1228
0
      }
1229
0
    }
1230
0
  }
1231
1232
0
  if (!found_method) {
1233
0
    purple_xfer_cancel_remote(xfer);
1234
0
  }
1235
1236
0
}
1237
1238
static void jabber_si_xfer_send_request(PurpleXfer *xfer)
1239
0
{
1240
0
  JabberSIXfer *jsx = xfer->data;
1241
0
  JabberIq *iq;
1242
0
  xmlnode *si, *file, *feature, *x, *field, *option, *value;
1243
0
  char buf[32];
1244
#if ENABLE_FT_THUMBNAILS
1245
  gconstpointer thumb;
1246
  gsize thumb_size;
1247
1248
  purple_xfer_prepare_thumbnail(xfer, "jpeg,png");
1249
#endif
1250
0
  xfer->filename = g_path_get_basename(xfer->local_filename);
1251
1252
0
  iq = jabber_iq_new(jsx->js, JABBER_IQ_SET);
1253
0
  xmlnode_set_attrib(iq->node, "to", xfer->who);
1254
0
  si = xmlnode_new_child(iq->node, "si");
1255
0
  xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
1256
0
  jsx->stream_id = jabber_get_next_id(jsx->js);
1257
0
  xmlnode_set_attrib(si, "id", jsx->stream_id);
1258
0
  xmlnode_set_attrib(si, "profile", NS_SI_FILE_TRANSFER);
1259
1260
0
  file = xmlnode_new_child(si, "file");
1261
0
  xmlnode_set_namespace(file, NS_SI_FILE_TRANSFER);
1262
0
  xmlnode_set_attrib(file, "name", xfer->filename);
1263
0
  g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size);
1264
0
  xmlnode_set_attrib(file, "size", buf);
1265
  /* maybe later we'll do hash and date attribs */
1266
1267
#if ENABLE_FT_THUMBNAILS
1268
  /* add thumbnail, if appropriate */
1269
  if ((thumb = purple_xfer_get_thumbnail(xfer, &thumb_size))) {
1270
    const gchar *mimetype = purple_xfer_get_thumbnail_mimetype(xfer);
1271
    JabberData *thumbnail_data =
1272
      jabber_data_create_from_data(thumb, thumb_size,
1273
        mimetype, TRUE, jsx->js);
1274
    xmlnode *thumbnail = xmlnode_new_child(file, "thumbnail");
1275
    xmlnode_set_namespace(thumbnail, NS_THUMBS);
1276
    xmlnode_set_attrib(thumbnail, "cid",
1277
      jabber_data_get_cid(thumbnail_data));
1278
    xmlnode_set_attrib(thumbnail, "mime-type", mimetype);
1279
    /* cache data */
1280
    jabber_data_associate_local(thumbnail_data, NULL);
1281
  }
1282
#endif
1283
1284
0
  feature = xmlnode_new_child(si, "feature");
1285
0
  xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
1286
0
  x = xmlnode_new_child(feature, "x");
1287
0
  xmlnode_set_namespace(x, "jabber:x:data");
1288
0
  xmlnode_set_attrib(x, "type", "form");
1289
0
  field = xmlnode_new_child(x, "field");
1290
0
  xmlnode_set_attrib(field, "var", "stream-method");
1291
0
  xmlnode_set_attrib(field, "type", "list-single");
1292
  /* maybe we should add an option to always skip bytestreams for people
1293
    behind troublesome firewalls */
1294
0
  option = xmlnode_new_child(field, "option");
1295
0
  value = xmlnode_new_child(option, "value");
1296
0
  xmlnode_insert_data(value, NS_BYTESTREAMS, -1);
1297
0
  option = xmlnode_new_child(field, "option");
1298
0
  value = xmlnode_new_child(option, "value");
1299
0
  xmlnode_insert_data(value, NS_IBB, -1);
1300
1301
0
  jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer);
1302
1303
  /* Store the IQ id so that we can cancel the callback */
1304
0
  g_free(jsx->iq_id);
1305
0
  jsx->iq_id = g_strdup(iq->id);
1306
1307
0
  jabber_iq_send(iq);
1308
0
}
1309
1310
static void jabber_si_xfer_free(PurpleXfer *xfer)
1311
0
{
1312
0
  JabberSIXfer *jsx = xfer->data;
1313
1314
0
  if (jsx) {
1315
0
    JabberStream *js = jsx->js;
1316
1317
0
    js->file_transfers = g_list_remove(js->file_transfers, xfer);
1318
1319
0
    if (jsx->connect_data != NULL)
1320
0
      purple_proxy_connect_cancel(jsx->connect_data);
1321
0
    if (jsx->listen_data != NULL)
1322
0
      purple_network_listen_cancel(jsx->listen_data);
1323
0
    if (jsx->iq_id != NULL)
1324
0
      jabber_iq_remove_callback_by_id(js, jsx->iq_id);
1325
0
    if (jsx->local_streamhost_fd >= 0)
1326
0
      close(jsx->local_streamhost_fd);
1327
0
    if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND &&
1328
0
      xfer->fd >= 0) {
1329
0
      purple_debug_info("jabber", "remove port mapping\n");
1330
0
      purple_network_remove_port_mapping(xfer->fd);
1331
0
    }
1332
0
    if (jsx->connect_timeout > 0)
1333
0
      purple_timeout_remove(jsx->connect_timeout);
1334
0
    if (jsx->ibb_timeout_handle > 0)
1335
0
      purple_timeout_remove(jsx->ibb_timeout_handle);
1336
1337
0
    if (jsx->streamhosts) {
1338
0
      g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
1339
0
      g_list_free(jsx->streamhosts);
1340
0
    }
1341
1342
0
    if (jsx->ibb_session) {
1343
0
      purple_debug_info("jabber",
1344
0
        "jabber_si_xfer_free: destroying IBB session\n");
1345
0
      jabber_ibb_session_destroy(jsx->ibb_session);
1346
0
    }
1347
1348
0
    if (jsx->ibb_buffer) {
1349
0
      purple_circ_buffer_destroy(jsx->ibb_buffer);
1350
0
    }
1351
1352
0
    purple_debug_info("jabber", "jabber_si_xfer_free(): freeing jsx %p\n", jsx);
1353
1354
0
    g_free(jsx->stream_id);
1355
0
    g_free(jsx->iq_id);
1356
    /* XXX: free other stuff */
1357
0
    g_free(jsx->rxqueue);
1358
0
    g_free(jsx);
1359
0
    xfer->data = NULL;
1360
0
  }
1361
0
}
1362
1363
/*
1364
 * These four functions should only be called from the PurpleXfer functions
1365
 * (typically purple_xfer_cancel_(remote|local), purple_xfer_end, or
1366
 * purple_xfer_request_denied.
1367
 */
1368
static void jabber_si_xfer_cancel_send(PurpleXfer *xfer)
1369
0
{
1370
0
  JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1371
1372
  /* if there is an IBB session active, send close on that */
1373
0
  if (jsx->ibb_session) {
1374
0
    jabber_ibb_session_close(jsx->ibb_session);
1375
0
  }
1376
0
  jabber_si_xfer_free(xfer);
1377
0
  purple_debug_info("jabber", "in jabber_si_xfer_cancel_send\n");
1378
0
}
1379
1380
1381
static void jabber_si_xfer_request_denied(PurpleXfer *xfer)
1382
0
{
1383
0
  JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1384
0
  JabberStream *js = jsx->js;
1385
1386
  /*
1387
   * TODO: It's probably an error if jsx->iq_id == NULL. g_return_if_fail
1388
   * might be warranted.
1389
   */
1390
0
  if (jsx->iq_id && !jsx->accepted) {
1391
0
    JabberIq *iq;
1392
0
    xmlnode *error, *child;
1393
0
    iq = jabber_iq_new(js, JABBER_IQ_ERROR);
1394
0
    xmlnode_set_attrib(iq->node, "to", xfer->who);
1395
0
    jabber_iq_set_id(iq, jsx->iq_id);
1396
1397
0
    error = xmlnode_new_child(iq->node, "error");
1398
0
    xmlnode_set_attrib(error, "type", "cancel");
1399
0
    child = xmlnode_new_child(error, "forbidden");
1400
0
    xmlnode_set_namespace(child, NS_XMPP_STANZAS);
1401
0
    child = xmlnode_new_child(error, "text");
1402
0
    xmlnode_set_namespace(child, NS_XMPP_STANZAS);
1403
0
    xmlnode_insert_data(child, "Offer Declined", -1);
1404
1405
0
    jabber_iq_send(iq);
1406
0
  }
1407
1408
0
  jabber_si_xfer_free(xfer);
1409
0
  purple_debug_info("jabber", "in jabber_si_xfer_request_denied\n");
1410
0
}
1411
1412
1413
static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer)
1414
0
{
1415
0
  JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1416
  /* if there is an IBB session active, send close */
1417
0
  if (jsx->ibb_session) {
1418
0
    jabber_ibb_session_close(jsx->ibb_session);
1419
0
  }
1420
0
  jabber_si_xfer_free(xfer);
1421
0
  purple_debug_info("jabber", "in jabber_si_xfer_cancel_recv\n");
1422
0
}
1423
1424
1425
static void jabber_si_xfer_end(PurpleXfer *xfer)
1426
0
{
1427
0
  jabber_si_xfer_free(xfer);
1428
0
}
1429
1430
1431
static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who,
1432
    JabberCapabilities capabilities, gpointer data)
1433
0
{
1434
0
  PurpleXfer *xfer = (PurpleXfer *) data;
1435
0
  JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1436
1437
0
  if (capabilities & JABBER_CAP_IBB) {
1438
0
    purple_debug_info("jabber",
1439
0
      "jabber_si_xfer_send_disco_cb: remote JID supports IBB\n");
1440
0
    jsx->stream_method |= STREAM_METHOD_IBB;
1441
0
  }
1442
1443
0
  if (capabilities & JABBER_CAP_SI_FILE_XFER) {
1444
0
    jabber_si_xfer_send_request(xfer);
1445
0
  } else {
1446
0
    char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who);
1447
0
    purple_notify_error(js->gc, _("File Send Failed"),
1448
0
        _("File Send Failed"), msg);
1449
0
    g_free(msg);
1450
0
    purple_xfer_cancel_local(xfer);
1451
0
  }
1452
0
}
1453
1454
static void resource_select_cancel_cb(PurpleXfer *xfer, PurpleRequestFields *fields)
1455
0
{
1456
0
  purple_xfer_cancel_local(xfer);
1457
0
}
1458
1459
static void do_transfer_send(PurpleXfer *xfer, const char *resource)
1460
0
{
1461
0
  JabberSIXfer *jsx = xfer->data;
1462
0
  char **who_v = g_strsplit(xfer->who, "/", 2);
1463
0
  char *who;
1464
0
  JabberBuddy *jb;
1465
0
  JabberBuddyResource *jbr = NULL;
1466
1467
0
  jb = jabber_buddy_find(jsx->js, who_v[0], FALSE);
1468
0
  if (jb) {
1469
0
    jbr = jabber_buddy_find_resource(jb, resource);
1470
0
  }
1471
1472
0
  who = g_strdup_printf("%s/%s", who_v[0], resource);
1473
0
  g_strfreev(who_v);
1474
0
  g_free(xfer->who);
1475
0
  xfer->who = who;
1476
1477
0
  if (jbr && jabber_resource_know_capabilities(jbr)) {
1478
0
    char *msg;
1479
1480
0
    if (jabber_resource_has_capability(jbr, NS_IBB))
1481
0
      jsx->stream_method |= STREAM_METHOD_IBB;
1482
0
    if (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER)) {
1483
0
      jabber_si_xfer_send_request(xfer);
1484
0
      return;
1485
0
    }
1486
1487
0
    msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who);
1488
0
    purple_notify_error(jsx->js->gc, _("File Send Failed"),
1489
0
        _("File Send Failed"), msg);
1490
0
    g_free(msg);
1491
0
    purple_xfer_cancel_local(xfer);
1492
0
  } else {
1493
0
    jabber_disco_info_do(jsx->js, who,
1494
0
        jabber_si_xfer_send_disco_cb, xfer);
1495
0
  }
1496
0
}
1497
1498
static void resource_select_ok_cb(PurpleXfer *xfer, PurpleRequestFields *fields)
1499
0
{
1500
0
  PurpleRequestField *field = purple_request_fields_get_field(fields, "resource");
1501
0
  int selected_id = purple_request_field_choice_get_value(field);
1502
0
  GList *labels = purple_request_field_choice_get_labels(field);
1503
1504
0
  const char *selected_label = g_list_nth_data(labels, selected_id);
1505
1506
0
  do_transfer_send(xfer, selected_label);
1507
0
}
1508
1509
static void jabber_si_xfer_init(PurpleXfer *xfer)
1510
0
{
1511
0
  JabberSIXfer *jsx = xfer->data;
1512
0
  JabberIq *iq;
1513
0
  if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
1514
0
    JabberBuddy *jb;
1515
0
    JabberBuddyResource *jbr = NULL;
1516
0
    char *resource;
1517
0
    GList *resources = NULL;
1518
1519
0
    if(NULL != (resource = jabber_get_resource(xfer->who))) {
1520
      /* they've specified a resource, no need to ask or
1521
       * default or anything, just do it */
1522
1523
0
      do_transfer_send(xfer, resource);
1524
0
      g_free(resource);
1525
0
      return;
1526
0
    }
1527
1528
0
    jb = jabber_buddy_find(jsx->js, xfer->who, TRUE);
1529
1530
0
    if (jb) {
1531
0
      GList *l;
1532
1533
0
      for (l = jb->resources ; l ; l = g_list_next(l)) {
1534
0
        jbr = l->data;
1535
1536
0
        if (!jabber_resource_know_capabilities(jbr) ||
1537
0
            (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER)
1538
0
             && (jabber_resource_has_capability(jbr, NS_BYTESTREAMS)
1539
0
                 || jabber_resource_has_capability(jbr, NS_IBB)))) {
1540
0
          resources = g_list_append(resources, jbr);
1541
0
        }
1542
0
      }
1543
0
    }
1544
1545
0
    if (!resources) {
1546
      /* no resources online, we're trying to send to someone
1547
       * whose presence we're not subscribed to, or
1548
       * someone who is offline.  Let's inform the user */
1549
0
      char *msg;
1550
1551
0
      if(!jb) {
1552
0
        msg = g_strdup_printf(_("Unable to send file to %s, invalid JID"), xfer->who);
1553
0
      } else if(jb->subscription & JABBER_SUB_TO) {
1554
0
        msg = g_strdup_printf(_("Unable to send file to %s, user is not online"), xfer->who);
1555
0
      } else {
1556
0
        msg = g_strdup_printf(_("Unable to send file to %s, not subscribed to user presence"), xfer->who);
1557
0
      }
1558
1559
0
      purple_notify_error(jsx->js->gc, _("File Send Failed"), _("File Send Failed"), msg);
1560
0
      g_free(msg);
1561
0
    } else if (g_list_length(resources) == 1) {
1562
      /* only 1 resource online (probably our most common case)
1563
       * so no need to ask who to send to */
1564
0
      jbr = resources->data;
1565
0
      do_transfer_send(xfer, jbr->name);
1566
0
    } else {
1567
      /* we've got multiple resources, we need to pick one to send to */
1568
0
      GList *l;
1569
0
      char *msg = g_strdup_printf(_("Please select the resource of %s to which you would like to send a file"), xfer->who);
1570
0
      PurpleRequestFields *fields = purple_request_fields_new();
1571
0
      PurpleRequestField *field = purple_request_field_choice_new("resource", _("Resource"), 0);
1572
0
      PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
1573
1574
0
      for(l = resources; l; l = l->next) {
1575
0
        jbr = l->data;
1576
0
        purple_request_field_choice_add(field, jbr->name);
1577
0
      }
1578
1579
0
      purple_request_field_group_add_field(group, field);
1580
1581
0
      purple_request_fields_add_group(fields, group);
1582
1583
0
      purple_request_fields(jsx->js->gc, _("Select a Resource"), msg, NULL, fields,
1584
0
          _("Send File"), G_CALLBACK(resource_select_ok_cb), _("Cancel"), G_CALLBACK(resource_select_cancel_cb),
1585
0
          jsx->js->gc->account, xfer->who, NULL, xfer);
1586
1587
0
      g_free(msg);
1588
0
    }
1589
1590
0
    g_list_free(resources);
1591
0
  } else {
1592
0
    xmlnode *si, *feature, *x, *field, *value;
1593
1594
0
    iq = jabber_iq_new(jsx->js, JABBER_IQ_RESULT);
1595
0
    xmlnode_set_attrib(iq->node, "to", xfer->who);
1596
0
    if(jsx->iq_id)
1597
0
      jabber_iq_set_id(iq, jsx->iq_id);
1598
0
    else
1599
0
      purple_debug_error("jabber", "Sending SI result with new IQ id.\n");
1600
1601
0
    jsx->accepted = TRUE;
1602
1603
0
    si = xmlnode_new_child(iq->node, "si");
1604
0
    xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
1605
1606
0
    feature = xmlnode_new_child(si, "feature");
1607
0
    xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
1608
1609
0
    x = xmlnode_new_child(feature, "x");
1610
0
    xmlnode_set_namespace(x, "jabber:x:data");
1611
0
    xmlnode_set_attrib(x, "type", "submit");
1612
0
    field = xmlnode_new_child(x, "field");
1613
0
    xmlnode_set_attrib(field, "var", "stream-method");
1614
1615
    /* we should maybe "remember" if bytestreams has failed before (in the
1616
      same session) with this JID, and only present IBB as an option to
1617
      avoid unnessesary timeout */
1618
    /* maybe we should have an account option to always just try IBB
1619
      for people who know their firewalls are very restrictive */
1620
0
    if (jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
1621
0
      value = xmlnode_new_child(field, "value");
1622
0
      xmlnode_insert_data(value, NS_BYTESTREAMS, -1);
1623
0
    } else if(jsx->stream_method & STREAM_METHOD_IBB) {
1624
0
      value = xmlnode_new_child(field, "value");
1625
0
      xmlnode_insert_data(value, NS_IBB, -1);
1626
0
    }
1627
1628
0
    jabber_iq_send(iq);
1629
0
  }
1630
0
}
1631
1632
PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who)
1633
0
{
1634
0
  JabberStream *js;
1635
1636
0
  PurpleXfer *xfer;
1637
0
  JabberSIXfer *jsx;
1638
1639
0
  js = gc->proto_data;
1640
1641
0
  xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
1642
0
  if (xfer)
1643
0
  {
1644
0
    xfer->data = jsx = g_new0(JabberSIXfer, 1);
1645
0
    jsx->js = js;
1646
0
    jsx->local_streamhost_fd = -1;
1647
1648
0
    jsx->ibb_session = NULL;
1649
1650
0
    purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
1651
0
    purple_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send);
1652
0
    purple_xfer_set_end_fnc(xfer, jabber_si_xfer_end);
1653
1654
0
    js->file_transfers = g_list_append(js->file_transfers, xfer);
1655
0
  }
1656
1657
0
  return xfer;
1658
0
}
1659
1660
void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file)
1661
0
{
1662
0
  PurpleXfer *xfer;
1663
1664
0
  xfer = jabber_si_new_xfer(gc, who);
1665
1666
0
  if (file)
1667
0
    purple_xfer_request_accepted(xfer, file);
1668
0
  else
1669
0
    purple_xfer_request(xfer);
1670
0
}
1671
1672
#if ENABLE_FT_THUMBNAILS
1673
static void
1674
jabber_si_thumbnail_cb(JabberData *data, gchar *alt, gpointer userdata)
1675
{
1676
  PurpleXfer *xfer = (PurpleXfer *) userdata;
1677
1678
  if (data) {
1679
    purple_xfer_set_thumbnail(xfer, jabber_data_get_data(data),
1680
      jabber_data_get_size(data), jabber_data_get_type(data));
1681
    /* data is ephemeral, get rid of now (the xfer re-owned the thumbnail */
1682
    jabber_data_destroy(data);
1683
  }
1684
1685
  purple_xfer_request(xfer);
1686
}
1687
#endif
1688
1689
void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
1690
                     const char *id, xmlnode *si)
1691
0
{
1692
0
  JabberSIXfer *jsx;
1693
0
  PurpleXfer *xfer;
1694
0
  xmlnode *file, *feature, *x, *field, *option, *value;
1695
#if ENABLE_FT_THUMBNAILS
1696
  xmlnode *thumbnail;
1697
#endif
1698
0
  const char *stream_id, *filename, *filesize_c, *profile;
1699
0
  guint64 filesize_64 = 0;
1700
0
  size_t filesize = 0;
1701
1702
0
  if(!(profile = xmlnode_get_attrib(si, "profile")) ||
1703
0
      !purple_strequal(profile, NS_SI_FILE_TRANSFER))
1704
0
    return;
1705
1706
0
  if(!(stream_id = xmlnode_get_attrib(si, "id")))
1707
0
    return;
1708
1709
0
  if(!(file = xmlnode_get_child(si, "file")))
1710
0
    return;
1711
1712
0
  if(!(filename = xmlnode_get_attrib(file, "name")))
1713
0
    return;
1714
1715
0
  if((filesize_c = xmlnode_get_attrib(file, "size")))
1716
0
    filesize_64 = g_ascii_strtoull(filesize_c, NULL, 10);
1717
1718
0
#ifndef __COVERITY__
1719
  /* TODO 3.0.0: When the core uses a guint64, this is redundant.
1720
   * See #8477.
1721
   *
1722
   * It may not be necessary on 64-bit machine.
1723
   * It raises result_independent_of_operands coverity false positive.
1724
   */
1725
0
  if (filesize_64 > G_MAXSIZE) {
1726
    /* Should this pop up a warning? */
1727
0
    purple_debug_warning("jabber", "Unable to transfer file (too large)"
1728
0
                         " -- see #8477 for more details.");
1729
0
    return;
1730
0
  }
1731
0
#endif
1732
0
  filesize = filesize_64;
1733
1734
0
  if(!(feature = xmlnode_get_child(si, "feature")))
1735
0
    return;
1736
1737
0
  if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data")))
1738
0
    return;
1739
1740
0
  if(!from)
1741
0
    return;
1742
1743
  /* if they've already sent us this file transfer with the same damn id
1744
   * then we're gonna ignore it, until I think of something better to do
1745
   * with it */
1746
0
  if(jabber_si_xfer_find(js, stream_id, from) != NULL)
1747
0
    return;
1748
1749
0
  jsx = g_new0(JabberSIXfer, 1);
1750
0
  jsx->local_streamhost_fd = -1;
1751
1752
0
  jsx->ibb_session = NULL;
1753
1754
0
  for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
1755
0
    const char *var = xmlnode_get_attrib(field, "var");
1756
0
    if(purple_strequal(var, "stream-method")) {
1757
0
      for(option = xmlnode_get_child(field, "option"); option;
1758
0
          option = xmlnode_get_next_twin(option)) {
1759
0
        if((value = xmlnode_get_child(option, "value"))) {
1760
0
          char *val;
1761
0
          if((val = xmlnode_get_data(value))) {
1762
0
            if(purple_strequal(val, NS_BYTESTREAMS)) {
1763
0
              jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
1764
0
            } else if(purple_strequal(val, NS_IBB)) {
1765
0
              jsx->stream_method |= STREAM_METHOD_IBB;
1766
0
            }
1767
0
            g_free(val);
1768
0
          }
1769
0
        }
1770
0
      }
1771
0
    }
1772
0
  }
1773
1774
0
  if(jsx->stream_method == STREAM_METHOD_UNKNOWN) {
1775
0
    g_free(jsx);
1776
0
    return;
1777
0
  }
1778
1779
0
  jsx->js = js;
1780
0
  jsx->stream_id = g_strdup(stream_id);
1781
0
  jsx->iq_id = g_strdup(id);
1782
1783
0
  xfer = purple_xfer_new(js->gc->account, PURPLE_XFER_RECEIVE, from);
1784
0
  g_return_if_fail(xfer != NULL);
1785
1786
0
  xfer->data = jsx;
1787
1788
0
  purple_xfer_set_filename(xfer, filename);
1789
0
  if(filesize > 0)
1790
0
    purple_xfer_set_size(xfer, filesize);
1791
1792
0
  purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
1793
0
  purple_xfer_set_request_denied_fnc(xfer, jabber_si_xfer_request_denied);
1794
0
  purple_xfer_set_cancel_recv_fnc(xfer, jabber_si_xfer_cancel_recv);
1795
0
  purple_xfer_set_end_fnc(xfer, jabber_si_xfer_end);
1796
1797
0
  js->file_transfers = g_list_append(js->file_transfers, xfer);
1798
1799
#if ENABLE_FT_THUMBNAILS
1800
  /* if there is a thumbnail, we should request it... */
1801
  if ((thumbnail = xmlnode_get_child_with_namespace(file, "thumbnail",
1802
    NS_THUMBS))) {
1803
    const char *cid = xmlnode_get_attrib(thumbnail, "cid");
1804
    if (cid) {
1805
      jabber_data_request(js, cid, purple_xfer_get_remote_user(xfer),
1806
          NULL, TRUE, jabber_si_thumbnail_cb, xfer);
1807
      return;
1808
    }
1809
  }
1810
#endif
1811
1812
0
  purple_xfer_request(xfer);
1813
0
}
1814
1815
void
1816
jabber_si_init(void)
1817
0
{
1818
0
  jabber_iq_register_handler("si", "http://jabber.org/protocol/si", jabber_si_parse);
1819
1820
0
  jabber_ibb_register_open_handler(jabber_si_xfer_ibb_open_cb);
1821
0
}
1822
1823
void
1824
jabber_si_uninit(void)
1825
0
{
1826
0
  jabber_ibb_unregister_open_handler(jabber_si_xfer_ibb_open_cb);
1827
0
}
1828