Mercurial > pidgin.yaz
comparison libgaim/protocols/jabber/si.c @ 14192:60b1bc8dbf37
[gaim-migrate @ 16863]
Renamed 'core' to 'libgaim'
committer: Tailor Script <tailor@pidgin.im>
author | Evan Schoenberg <evan.s@dreskin.net> |
---|---|
date | Sat, 19 Aug 2006 01:50:10 +0000 |
parents | |
children | baff095b146c |
comparison
equal
deleted
inserted
replaced
14191:009db0b357b5 | 14192:60b1bc8dbf37 |
---|---|
1 /* | |
2 * gaim - Jabber Protocol Plugin | |
3 * | |
4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> | |
5 * | |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * | |
15 * GNU General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU General Public License | |
18 * along with this program; if not, write to the Free Software | |
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 * | |
21 */ | |
22 | |
23 #include "blist.h" | |
24 | |
25 #include "internal.h" | |
26 #include "cipher.h" | |
27 #include "debug.h" | |
28 #include "ft.h" | |
29 #include "network.h" | |
30 #include "notify.h" | |
31 | |
32 #include "buddy.h" | |
33 #include "disco.h" | |
34 #include "jabber.h" | |
35 #include "iq.h" | |
36 #include "si.h" | |
37 | |
38 #include "si.h" | |
39 | |
40 struct bytestreams_streamhost { | |
41 char *jid; | |
42 char *host; | |
43 int port; | |
44 }; | |
45 | |
46 typedef struct _JabberSIXfer { | |
47 JabberStream *js; | |
48 | |
49 GaimProxyConnectInfo *connect_info; | |
50 | |
51 gboolean accepted; | |
52 | |
53 char *stream_id; | |
54 char *iq_id; | |
55 | |
56 enum { | |
57 STREAM_METHOD_UNKNOWN = 0, | |
58 STREAM_METHOD_BYTESTREAMS = 2 << 1, | |
59 STREAM_METHOD_IBB = 2 << 2, | |
60 STREAM_METHOD_UNSUPPORTED = 2 << 31 | |
61 } stream_method; | |
62 | |
63 GList *streamhosts; | |
64 GaimProxyInfo *gpi; | |
65 | |
66 char *rxqueue; | |
67 size_t rxlen; | |
68 gsize rxmaxlen; | |
69 } JabberSIXfer; | |
70 | |
71 static GaimXfer* | |
72 jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from) | |
73 { | |
74 GList *xfers; | |
75 | |
76 if(!sid || !from) | |
77 return NULL; | |
78 | |
79 for(xfers = js->file_transfers; xfers; xfers = xfers->next) { | |
80 GaimXfer *xfer = xfers->data; | |
81 JabberSIXfer *jsx = xfer->data; | |
82 if(jsx->stream_id && xfer->who && | |
83 !strcmp(jsx->stream_id, sid) && !strcmp(xfer->who, from)) | |
84 return xfer; | |
85 } | |
86 | |
87 return NULL; | |
88 } | |
89 | |
90 | |
91 static void jabber_si_bytestreams_attempt_connect(GaimXfer *xfer); | |
92 | |
93 static void | |
94 jabber_si_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message) | |
95 { | |
96 GaimXfer *xfer = data; | |
97 JabberSIXfer *jsx = xfer->data; | |
98 JabberIq *iq; | |
99 xmlnode *query, *su; | |
100 struct bytestreams_streamhost *streamhost = jsx->streamhosts->data; | |
101 | |
102 gaim_proxy_info_destroy(jsx->gpi); | |
103 jsx->connect_info = NULL; | |
104 | |
105 if(source < 0) { | |
106 jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost); | |
107 g_free(streamhost->jid); | |
108 g_free(streamhost->host); | |
109 g_free(streamhost); | |
110 jabber_si_bytestreams_attempt_connect(xfer); | |
111 return; | |
112 } | |
113 | |
114 iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, "http://jabber.org/protocol/bytestreams"); | |
115 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
116 jabber_iq_set_id(iq, jsx->iq_id); | |
117 query = xmlnode_get_child(iq->node, "query"); | |
118 su = xmlnode_new_child(query, "streamhost-used"); | |
119 xmlnode_set_attrib(su, "jid", streamhost->jid); | |
120 | |
121 jabber_iq_send(iq); | |
122 | |
123 gaim_xfer_start(xfer, source, NULL, -1); | |
124 } | |
125 | |
126 static void jabber_si_bytestreams_attempt_connect(GaimXfer *xfer) | |
127 { | |
128 JabberSIXfer *jsx = xfer->data; | |
129 struct bytestreams_streamhost *streamhost; | |
130 char *dstaddr, *p; | |
131 int i; | |
132 unsigned char hashval[20]; | |
133 | |
134 if(!jsx->streamhosts) { | |
135 JabberIq *iq = jabber_iq_new(jsx->js, JABBER_IQ_ERROR); | |
136 xmlnode *error, *condition; | |
137 | |
138 if(jsx->iq_id) | |
139 jabber_iq_set_id(iq, jsx->iq_id); | |
140 | |
141 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
142 error = xmlnode_new_child(iq->node, "error"); | |
143 xmlnode_set_attrib(error, "code", "404"); | |
144 xmlnode_set_attrib(error, "type", "cancel"); | |
145 condition = xmlnode_new_child(error, "condition"); | |
146 xmlnode_set_namespace(condition, "urn:ietf:params:xml:ns:xmpp-stanzas"); | |
147 xmlnode_new_child(condition, "item-not-found"); | |
148 | |
149 jabber_iq_send(iq); | |
150 | |
151 gaim_xfer_cancel_local(xfer); | |
152 | |
153 return; | |
154 } | |
155 | |
156 streamhost = jsx->streamhosts->data; | |
157 | |
158 jsx->gpi = gaim_proxy_info_new(); | |
159 gaim_proxy_info_set_type(jsx->gpi, GAIM_PROXY_SOCKS5); | |
160 gaim_proxy_info_set_host(jsx->gpi, streamhost->host); | |
161 gaim_proxy_info_set_port(jsx->gpi, streamhost->port); | |
162 | |
163 dstaddr = g_strdup_printf("%s%s%s@%s/%s", jsx->stream_id, xfer->who, jsx->js->user->node, | |
164 jsx->js->user->domain, jsx->js->user->resource); | |
165 | |
166 gaim_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr), | |
167 sizeof(hashval), hashval, NULL); | |
168 g_free(dstaddr); | |
169 dstaddr = g_malloc(41); | |
170 p = dstaddr; | |
171 for(i=0; i<20; i++, p+=2) | |
172 snprintf(p, 3, "%02x", hashval[i]); | |
173 | |
174 jsx->connect_info = gaim_proxy_connect_socks5(jsx->gpi, dstaddr, 0, | |
175 jabber_si_bytestreams_connect_cb, xfer); | |
176 g_free(dstaddr); | |
177 | |
178 if (jsx->connect_info == NULL) | |
179 { | |
180 jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost); | |
181 g_free(streamhost->jid); | |
182 g_free(streamhost->host); | |
183 g_free(streamhost); | |
184 jabber_si_bytestreams_attempt_connect(xfer); | |
185 } | |
186 } | |
187 | |
188 void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet) | |
189 { | |
190 GaimXfer *xfer; | |
191 JabberSIXfer *jsx; | |
192 xmlnode *query, *streamhost; | |
193 const char *sid, *from; | |
194 | |
195 if(!(from = xmlnode_get_attrib(packet, "from"))) | |
196 return; | |
197 | |
198 if(!(query = xmlnode_get_child(packet, "query"))) | |
199 return; | |
200 | |
201 if(!(sid = xmlnode_get_attrib(query, "sid"))) | |
202 return; | |
203 | |
204 if(!(xfer = jabber_si_xfer_find(js, sid, from))) | |
205 return; | |
206 | |
207 jsx = xfer->data; | |
208 | |
209 if(!jsx->accepted) | |
210 return; | |
211 | |
212 if(jsx->iq_id) | |
213 g_free(jsx->iq_id); | |
214 jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id")); | |
215 | |
216 for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost; | |
217 streamhost = xmlnode_get_next_twin(streamhost)) { | |
218 const char *jid, *host, *port; | |
219 int portnum; | |
220 | |
221 if((jid = xmlnode_get_attrib(streamhost, "jid")) && | |
222 (host = xmlnode_get_attrib(streamhost, "host")) && | |
223 (port = xmlnode_get_attrib(streamhost, "port")) && | |
224 (portnum = atoi(port))) { | |
225 struct bytestreams_streamhost *sh = g_new0(struct bytestreams_streamhost, 1); | |
226 sh->jid = g_strdup(jid); | |
227 sh->host = g_strdup(host); | |
228 sh->port = portnum; | |
229 jsx->streamhosts = g_list_append(jsx->streamhosts, sh); | |
230 } | |
231 } | |
232 | |
233 jabber_si_bytestreams_attempt_connect(xfer); | |
234 } | |
235 | |
236 | |
237 static void | |
238 jabber_si_xfer_bytestreams_send_read_again_resp_cb(gpointer data, gint source, | |
239 GaimInputCondition cond) | |
240 { | |
241 GaimXfer *xfer = data; | |
242 JabberSIXfer *jsx = xfer->data; | |
243 int len; | |
244 | |
245 len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen); | |
246 if (len < 0 && errno == EAGAIN) | |
247 return; | |
248 else if (len < 0) { | |
249 gaim_input_remove(xfer->watcher); | |
250 xfer->watcher = 0; | |
251 g_free(jsx->rxqueue); | |
252 jsx->rxqueue = NULL; | |
253 close(source); | |
254 gaim_xfer_cancel_remote(xfer); | |
255 return; | |
256 } | |
257 jsx->rxlen += len; | |
258 | |
259 if (jsx->rxlen < jsx->rxmaxlen) | |
260 return; | |
261 | |
262 gaim_input_remove(xfer->watcher); | |
263 xfer->watcher = 0; | |
264 g_free(jsx->rxqueue); | |
265 jsx->rxqueue = NULL; | |
266 | |
267 gaim_xfer_start(xfer, source, NULL, -1); | |
268 } | |
269 | |
270 static void | |
271 jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source, | |
272 GaimInputCondition cond) | |
273 { | |
274 GaimXfer *xfer = data; | |
275 JabberSIXfer *jsx = xfer->data; | |
276 int i; | |
277 char buffer[256]; | |
278 int len; | |
279 char *dstaddr, *p; | |
280 unsigned char hashval[20]; | |
281 const char *host; | |
282 | |
283 gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_again_cb\n"); | |
284 | |
285 if(jsx->rxlen < 5) { | |
286 gaim_debug_info("jabber", "reading the first 5 bytes\n"); | |
287 len = read(source, buffer, 5 - jsx->rxlen); | |
288 if(len < 0 && errno == EAGAIN) | |
289 return; | |
290 else if(len <= 0) { | |
291 gaim_input_remove(xfer->watcher); | |
292 xfer->watcher = 0; | |
293 close(source); | |
294 gaim_xfer_cancel_remote(xfer); | |
295 return; | |
296 } | |
297 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen); | |
298 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len); | |
299 jsx->rxlen += len; | |
300 return; | |
301 } else if(jsx->rxqueue[0] != 0x05 || jsx->rxqueue[1] != 0x01 || | |
302 jsx->rxqueue[3] != 0x03) { | |
303 gaim_debug_info("jabber", "invalid socks5 stuff\n"); | |
304 gaim_input_remove(xfer->watcher); | |
305 xfer->watcher = 0; | |
306 close(source); | |
307 gaim_xfer_cancel_remote(xfer); | |
308 return; | |
309 } else if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2) { | |
310 gaim_debug_info("jabber", "reading umpteen more bytes\n"); | |
311 len = read(source, buffer, jsx->rxqueue[4] + 5 + 2 - jsx->rxlen); | |
312 if(len < 0 && errno == EAGAIN) | |
313 return; | |
314 else if(len <= 0) { | |
315 gaim_input_remove(xfer->watcher); | |
316 xfer->watcher = 0; | |
317 close(source); | |
318 gaim_xfer_cancel_remote(xfer); | |
319 return; | |
320 } | |
321 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen); | |
322 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len); | |
323 jsx->rxlen += len; | |
324 } | |
325 | |
326 if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2) | |
327 return; | |
328 | |
329 gaim_input_remove(xfer->watcher); | |
330 xfer->watcher = 0; | |
331 | |
332 dstaddr = g_strdup_printf("%s%s@%s/%s%s", jsx->stream_id, | |
333 jsx->js->user->node, jsx->js->user->domain, | |
334 jsx->js->user->resource, xfer->who); | |
335 | |
336 gaim_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr), | |
337 sizeof(hashval), hashval, NULL); | |
338 g_free(dstaddr); | |
339 dstaddr = g_malloc(41); | |
340 p = dstaddr; | |
341 for(i=0; i<20; i++, p+=2) | |
342 snprintf(p, 3, "%02x", hashval[i]); | |
343 | |
344 if(jsx->rxqueue[4] != 40 || strncmp(dstaddr, jsx->rxqueue+5, 40) || | |
345 jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00) { | |
346 gaim_debug_error("jabber", "someone connected with the wrong info!\n"); | |
347 close(source); | |
348 gaim_xfer_cancel_remote(xfer); | |
349 return; | |
350 } | |
351 | |
352 g_free(jsx->rxqueue); | |
353 host = gaim_network_get_my_ip(jsx->js->fd); | |
354 | |
355 jsx->rxmaxlen = 5 + strlen(host) + 2; | |
356 jsx->rxqueue = g_malloc(jsx->rxmaxlen); | |
357 jsx->rxlen = 0; | |
358 | |
359 jsx->rxqueue[0] = 0x05; | |
360 jsx->rxqueue[1] = 0x00; | |
361 jsx->rxqueue[2] = 0x00; | |
362 jsx->rxqueue[3] = 0x03; | |
363 jsx->rxqueue[4] = strlen(host); | |
364 memcpy(jsx->rxqueue + 5, host, strlen(host)); | |
365 jsx->rxqueue[5+strlen(host)] = 0x00; | |
366 jsx->rxqueue[6+strlen(host)] = 0x00; | |
367 | |
368 xfer->watcher = gaim_input_add(source, GAIM_INPUT_WRITE, | |
369 jabber_si_xfer_bytestreams_send_read_again_resp_cb, xfer); | |
370 jabber_si_xfer_bytestreams_send_read_again_resp_cb(xfer, source, | |
371 GAIM_INPUT_WRITE); | |
372 } | |
373 | |
374 static void | |
375 jabber_si_xfer_bytestreams_send_read_response_cb(gpointer data, gint source, | |
376 GaimInputCondition cond) | |
377 { | |
378 GaimXfer *xfer = data; | |
379 JabberSIXfer *jsx = xfer->data; | |
380 int len; | |
381 | |
382 len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen); | |
383 if (len < 0 && errno == EAGAIN) | |
384 return; | |
385 else if (len < 0) { | |
386 gaim_input_remove(xfer->watcher); | |
387 xfer->watcher = 0; | |
388 g_free(jsx->rxqueue); | |
389 jsx->rxqueue = NULL; | |
390 close(source); | |
391 gaim_xfer_cancel_remote(xfer); | |
392 return; | |
393 } | |
394 jsx->rxlen += len; | |
395 | |
396 if (jsx->rxlen < jsx->rxmaxlen) | |
397 return; | |
398 | |
399 gaim_input_remove(xfer->watcher); | |
400 xfer->watcher = 0; | |
401 | |
402 if (jsx->rxqueue[1] == 0x00) { | |
403 xfer->watcher = gaim_input_add(source, GAIM_INPUT_READ, | |
404 jabber_si_xfer_bytestreams_send_read_again_cb, xfer); | |
405 g_free(jsx->rxqueue); | |
406 jsx->rxqueue = NULL; | |
407 } else { | |
408 close(source); | |
409 gaim_xfer_cancel_remote(xfer); | |
410 } | |
411 } | |
412 | |
413 static void | |
414 jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source, | |
415 GaimInputCondition cond) | |
416 { | |
417 GaimXfer *xfer = data; | |
418 JabberSIXfer *jsx = xfer->data; | |
419 int i; | |
420 int len; | |
421 char buffer[256]; | |
422 | |
423 gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_cb\n"); | |
424 | |
425 xfer->fd = source; | |
426 | |
427 if(jsx->rxlen < 2) { | |
428 gaim_debug_info("jabber", "reading those first two bytes\n"); | |
429 len = read(source, buffer, 2 - jsx->rxlen); | |
430 if(len < 0 && errno == EAGAIN) | |
431 return; | |
432 else if(len <= 0) { | |
433 gaim_input_remove(xfer->watcher); | |
434 xfer->watcher = 0; | |
435 close(source); | |
436 gaim_xfer_cancel_remote(xfer); | |
437 return; | |
438 } | |
439 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen); | |
440 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len); | |
441 jsx->rxlen += len; | |
442 return; | |
443 } else if(jsx->rxlen - 2 < jsx->rxqueue[1]) { | |
444 gaim_debug_info("jabber", "reading the next umpteen bytes\n"); | |
445 len = read(source, buffer, jsx->rxqueue[1] + 2 - jsx->rxlen); | |
446 if(len < 0 && errno == EAGAIN) | |
447 return; | |
448 else if(len <= 0) { | |
449 gaim_input_remove(xfer->watcher); | |
450 xfer->watcher = 0; | |
451 close(source); | |
452 gaim_xfer_cancel_remote(xfer); | |
453 return; | |
454 } | |
455 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen); | |
456 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len); | |
457 jsx->rxlen += len; | |
458 } | |
459 | |
460 if(jsx->rxlen -2 < jsx->rxqueue[1]) | |
461 return; | |
462 | |
463 gaim_input_remove(xfer->watcher); | |
464 xfer->watcher = 0; | |
465 | |
466 gaim_debug_info("jabber", "checking to make sure we're socks FIVE\n"); | |
467 | |
468 if(jsx->rxqueue[0] != 0x05) { | |
469 close(source); | |
470 gaim_xfer_cancel_remote(xfer); | |
471 return; | |
472 } | |
473 | |
474 gaim_debug_info("jabber", "going to test %hhu different methods\n", jsx->rxqueue[1]); | |
475 | |
476 for(i=0; i<jsx->rxqueue[1]; i++) { | |
477 | |
478 gaim_debug_info("jabber", "testing %hhu\n", jsx->rxqueue[i+2]); | |
479 if(jsx->rxqueue[i+2] == 0x00) { | |
480 g_free(jsx->rxqueue); | |
481 jsx->rxlen = 0; | |
482 jsx->rxmaxlen = 2; | |
483 jsx->rxqueue = g_malloc(jsx->rxmaxlen); | |
484 jsx->rxqueue[0] = 0x05; | |
485 jsx->rxqueue[1] = 0x00; | |
486 xfer->watcher = gaim_input_add(source, GAIM_INPUT_WRITE, | |
487 jabber_si_xfer_bytestreams_send_read_response_cb, | |
488 xfer); | |
489 jabber_si_xfer_bytestreams_send_read_response_cb(xfer, | |
490 source, GAIM_INPUT_WRITE); | |
491 jsx->rxqueue = NULL; | |
492 jsx->rxlen = 0; | |
493 return; | |
494 } | |
495 } | |
496 | |
497 g_free(jsx->rxqueue); | |
498 jsx->rxlen = 0; | |
499 jsx->rxmaxlen = 2; | |
500 jsx->rxqueue = g_malloc(jsx->rxmaxlen); | |
501 jsx->rxqueue[0] = 0x05; | |
502 jsx->rxqueue[1] = 0xFF; | |
503 xfer->watcher = gaim_input_add(source, GAIM_INPUT_WRITE, | |
504 jabber_si_xfer_bytestreams_send_read_response_cb, xfer); | |
505 jabber_si_xfer_bytestreams_send_read_response_cb(xfer, | |
506 source, GAIM_INPUT_WRITE); | |
507 } | |
508 | |
509 static void | |
510 jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source, | |
511 GaimInputCondition cond) | |
512 { | |
513 GaimXfer *xfer = data; | |
514 int acceptfd; | |
515 | |
516 gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n"); | |
517 | |
518 acceptfd = accept(source, NULL, 0); | |
519 if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) | |
520 return; | |
521 else if(acceptfd == -1) { | |
522 gaim_debug_warning("jabber", "accept: %s\n", strerror(errno)); | |
523 return; | |
524 } | |
525 | |
526 gaim_input_remove(xfer->watcher); | |
527 close(source); | |
528 | |
529 xfer->watcher = gaim_input_add(acceptfd, GAIM_INPUT_READ, | |
530 jabber_si_xfer_bytestreams_send_read_cb, xfer); | |
531 } | |
532 | |
533 static void | |
534 jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data) | |
535 { | |
536 GaimXfer *xfer = data; | |
537 JabberSIXfer *jsx; | |
538 JabberIq *iq; | |
539 xmlnode *query, *streamhost; | |
540 char *jid, *port; | |
541 | |
542 if (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) { | |
543 gaim_xfer_unref(xfer); | |
544 return; | |
545 } | |
546 | |
547 jsx = xfer->data; | |
548 | |
549 gaim_xfer_unref(xfer); | |
550 | |
551 if (sock < 0) { | |
552 gaim_xfer_cancel_local(xfer); | |
553 return; | |
554 } | |
555 | |
556 iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, | |
557 "http://jabber.org/protocol/bytestreams"); | |
558 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
559 query = xmlnode_get_child(iq->node, "query"); | |
560 | |
561 xmlnode_set_attrib(query, "sid", jsx->stream_id); | |
562 | |
563 streamhost = xmlnode_new_child(query, "streamhost"); | |
564 jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node, | |
565 jsx->js->user->domain, jsx->js->user->resource); | |
566 xmlnode_set_attrib(streamhost, "jid", jid); | |
567 g_free(jid); | |
568 | |
569 /* XXX: shouldn't we use the public IP or something? here */ | |
570 xmlnode_set_attrib(streamhost, "host", | |
571 gaim_network_get_my_ip(jsx->js->fd)); | |
572 xfer->local_port = gaim_network_get_port_from_fd(sock); | |
573 port = g_strdup_printf("%hu", xfer->local_port); | |
574 xmlnode_set_attrib(streamhost, "port", port); | |
575 g_free(port); | |
576 | |
577 xfer->watcher = gaim_input_add(sock, GAIM_INPUT_READ, | |
578 jabber_si_xfer_bytestreams_send_connected_cb, xfer); | |
579 | |
580 /* XXX: insert proxies here */ | |
581 | |
582 /* XXX: callback to find out which streamhost they used, or see if they | |
583 * screwed it up */ | |
584 jabber_iq_send(iq); | |
585 | |
586 } | |
587 | |
588 static void | |
589 jabber_si_xfer_bytestreams_send_init(GaimXfer *xfer) | |
590 { | |
591 gaim_xfer_ref(xfer); | |
592 | |
593 if(!gaim_network_listen_range(0, 0, SOCK_STREAM, | |
594 jabber_si_xfer_bytestreams_listen_cb, xfer)) { | |
595 gaim_xfer_unref(xfer); | |
596 /* XXX: couldn't open a port, we're fscked */ | |
597 gaim_xfer_cancel_local(xfer); | |
598 return; | |
599 } | |
600 | |
601 } | |
602 | |
603 static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet, | |
604 gpointer data) | |
605 { | |
606 GaimXfer *xfer = data; | |
607 xmlnode *si, *feature, *x, *field, *value; | |
608 | |
609 if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) { | |
610 gaim_xfer_cancel_remote(xfer); | |
611 return; | |
612 } | |
613 | |
614 if(!(feature = xmlnode_get_child_with_namespace(si, "feature", "http://jabber.org/protocol/feature-neg"))) { | |
615 gaim_xfer_cancel_remote(xfer); | |
616 return; | |
617 } | |
618 | |
619 if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) { | |
620 gaim_xfer_cancel_remote(xfer); | |
621 return; | |
622 } | |
623 | |
624 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { | |
625 const char *var = xmlnode_get_attrib(field, "var"); | |
626 | |
627 if(var && !strcmp(var, "stream-method")) { | |
628 if((value = xmlnode_get_child(field, "value"))) { | |
629 char *val = xmlnode_get_data(value); | |
630 if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) { | |
631 jabber_si_xfer_bytestreams_send_init(xfer); | |
632 g_free(val); | |
633 return; | |
634 } | |
635 g_free(val); | |
636 } | |
637 } | |
638 } | |
639 gaim_xfer_cancel_remote(xfer); | |
640 } | |
641 | |
642 static void jabber_si_xfer_send_request(GaimXfer *xfer) | |
643 { | |
644 JabberSIXfer *jsx = xfer->data; | |
645 JabberIq *iq; | |
646 xmlnode *si, *file, *feature, *x, *field, *option, *value; | |
647 char buf[32]; | |
648 | |
649 xfer->filename = g_path_get_basename(xfer->local_filename); | |
650 | |
651 iq = jabber_iq_new(jsx->js, JABBER_IQ_SET); | |
652 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
653 si = xmlnode_new_child(iq->node, "si"); | |
654 xmlnode_set_namespace(si, "http://jabber.org/protocol/si"); | |
655 jsx->stream_id = jabber_get_next_id(jsx->js); | |
656 xmlnode_set_attrib(si, "id", jsx->stream_id); | |
657 xmlnode_set_attrib(si, "profile", | |
658 "http://jabber.org/protocol/si/profile/file-transfer"); | |
659 | |
660 file = xmlnode_new_child(si, "file"); | |
661 xmlnode_set_namespace(file, | |
662 "http://jabber.org/protocol/si/profile/file-transfer"); | |
663 xmlnode_set_attrib(file, "name", xfer->filename); | |
664 g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size); | |
665 xmlnode_set_attrib(file, "size", buf); | |
666 /* maybe later we'll do hash and date attribs */ | |
667 | |
668 feature = xmlnode_new_child(si, "feature"); | |
669 xmlnode_set_namespace(feature, | |
670 "http://jabber.org/protocol/feature-neg"); | |
671 x = xmlnode_new_child(feature, "x"); | |
672 xmlnode_set_namespace(x, "jabber:x:data"); | |
673 xmlnode_set_attrib(x, "type", "form"); | |
674 field = xmlnode_new_child(x, "field"); | |
675 xmlnode_set_attrib(field, "var", "stream-method"); | |
676 xmlnode_set_attrib(field, "type", "list-single"); | |
677 option = xmlnode_new_child(field, "option"); | |
678 value = xmlnode_new_child(option, "value"); | |
679 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", | |
680 -1); | |
681 /* | |
682 option = xmlnode_new_child(field, "option"); | |
683 value = xmlnode_new_child(option, "value"); | |
684 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); | |
685 */ | |
686 | |
687 jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer); | |
688 | |
689 jabber_iq_send(iq); | |
690 } | |
691 | |
692 static void jabber_si_xfer_free(GaimXfer *xfer) | |
693 { | |
694 JabberSIXfer *jsx = xfer->data; | |
695 JabberStream *js = jsx->js; | |
696 | |
697 js->file_transfers = g_list_remove(js->file_transfers, xfer); | |
698 | |
699 if (jsx->connect_info != NULL) | |
700 gaim_proxy_connect_cancel(jsx->connect_info); | |
701 | |
702 g_free(jsx->stream_id); | |
703 g_free(jsx->iq_id); | |
704 /* XXX: free other stuff */ | |
705 g_free(jsx->rxqueue); | |
706 g_free(jsx); | |
707 xfer->data = NULL; | |
708 } | |
709 | |
710 static void jabber_si_xfer_cancel_send(GaimXfer *xfer) | |
711 { | |
712 jabber_si_xfer_free(xfer); | |
713 gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_send\n"); | |
714 } | |
715 | |
716 | |
717 static void jabber_si_xfer_request_denied(GaimXfer *xfer) | |
718 { | |
719 jabber_si_xfer_free(xfer); | |
720 gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_request_denied\n"); | |
721 } | |
722 | |
723 | |
724 static void jabber_si_xfer_cancel_recv(GaimXfer *xfer) | |
725 { | |
726 jabber_si_xfer_free(xfer); | |
727 gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_recv\n"); | |
728 } | |
729 | |
730 | |
731 static void jabber_si_xfer_end(GaimXfer *xfer) | |
732 { | |
733 jabber_si_xfer_free(xfer); | |
734 } | |
735 | |
736 | |
737 static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who, | |
738 JabberCapabilities capabilities, gpointer data) | |
739 { | |
740 GaimXfer *xfer = data; | |
741 | |
742 if(capabilities & JABBER_CAP_SI_FILE_XFER) { | |
743 jabber_si_xfer_send_request(xfer); | |
744 } else { | |
745 char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who); | |
746 gaim_notify_error(js->gc, _("File Send Failed"), | |
747 _("File Send Failed"), msg); | |
748 g_free(msg); | |
749 } | |
750 } | |
751 | |
752 static void jabber_si_xfer_init(GaimXfer *xfer) | |
753 { | |
754 JabberSIXfer *jsx = xfer->data; | |
755 JabberIq *iq; | |
756 if(gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) { | |
757 JabberBuddy *jb; | |
758 JabberBuddyResource *jbr = NULL; | |
759 | |
760 jb = jabber_buddy_find(jsx->js, xfer->who, TRUE); | |
761 /* XXX */ | |
762 if(!jb) | |
763 return; | |
764 | |
765 /* XXX: for now, send to the first resource available */ | |
766 if(g_list_length(jb->resources) >= 1) { | |
767 char **who_v = g_strsplit(xfer->who, "/", 2); | |
768 char *who; | |
769 | |
770 jbr = jabber_buddy_find_resource(jb, NULL); | |
771 who = g_strdup_printf("%s/%s", who_v[0], jbr->name); | |
772 g_strfreev(who_v); | |
773 g_free(xfer->who); | |
774 xfer->who = who; | |
775 jabber_disco_info_do(jsx->js, who, | |
776 jabber_si_xfer_send_disco_cb, xfer); | |
777 } else { | |
778 return; /* XXX: ick */ | |
779 } | |
780 } else { | |
781 xmlnode *si, *feature, *x, *field, *value; | |
782 | |
783 iq = jabber_iq_new(jsx->js, JABBER_IQ_RESULT); | |
784 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
785 if(jsx->iq_id) | |
786 jabber_iq_set_id(iq, jsx->iq_id); | |
787 | |
788 jsx->accepted = TRUE; | |
789 | |
790 si = xmlnode_new_child(iq->node, "si"); | |
791 xmlnode_set_namespace(si, "http://jabber.org/protocol/si"); | |
792 | |
793 feature = xmlnode_new_child(si, "feature"); | |
794 xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); | |
795 | |
796 x = xmlnode_new_child(feature, "x"); | |
797 xmlnode_set_namespace(x, "jabber:x:data"); | |
798 xmlnode_set_attrib(x, "type", "submit"); | |
799 | |
800 field = xmlnode_new_child(x, "field"); | |
801 xmlnode_set_attrib(field, "var", "stream-method"); | |
802 | |
803 value = xmlnode_new_child(field, "value"); | |
804 if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) | |
805 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); | |
806 /* | |
807 else if(jsx->stream_method & STREAM_METHOD_IBB) | |
808 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); | |
809 */ | |
810 | |
811 jabber_iq_send(iq); | |
812 } | |
813 } | |
814 | |
815 GaimXfer *jabber_si_new_xfer(GaimConnection *gc, const char *who) | |
816 { | |
817 JabberStream *js; | |
818 | |
819 GaimXfer *xfer; | |
820 JabberSIXfer *jsx; | |
821 | |
822 js = gc->proto_data; | |
823 | |
824 xfer = gaim_xfer_new(gc->account, GAIM_XFER_SEND, who); | |
825 | |
826 xfer->data = jsx = g_new0(JabberSIXfer, 1); | |
827 jsx->js = js; | |
828 | |
829 gaim_xfer_set_init_fnc(xfer, jabber_si_xfer_init); | |
830 gaim_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send); | |
831 gaim_xfer_set_end_fnc(xfer, jabber_si_xfer_end); | |
832 | |
833 js->file_transfers = g_list_append(js->file_transfers, xfer); | |
834 | |
835 return xfer; | |
836 } | |
837 | |
838 void jabber_si_xfer_send(GaimConnection *gc, const char *who, const char *file) | |
839 { | |
840 JabberStream *js; | |
841 | |
842 GaimXfer *xfer; | |
843 | |
844 js = gc->proto_data; | |
845 | |
846 if(!gaim_find_buddy(gc->account, who) || !jabber_buddy_find(js, who, FALSE)) | |
847 return; | |
848 | |
849 xfer = jabber_si_new_xfer(gc, who); | |
850 | |
851 if (file) | |
852 gaim_xfer_request_accepted(xfer, file); | |
853 else | |
854 gaim_xfer_request(xfer); | |
855 } | |
856 | |
857 void jabber_si_parse(JabberStream *js, xmlnode *packet) | |
858 { | |
859 JabberSIXfer *jsx; | |
860 GaimXfer *xfer; | |
861 xmlnode *si, *file, *feature, *x, *field, *option, *value; | |
862 const char *stream_id, *filename, *filesize_c, *profile, *from; | |
863 size_t filesize = 0; | |
864 | |
865 if(!(si = xmlnode_get_child(packet, "si"))) | |
866 return; | |
867 | |
868 if(!(profile = xmlnode_get_attrib(si, "profile")) || | |
869 strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) | |
870 return; | |
871 | |
872 if(!(stream_id = xmlnode_get_attrib(si, "id"))) | |
873 return; | |
874 | |
875 if(!(file = xmlnode_get_child(si, "file"))) | |
876 return; | |
877 | |
878 if(!(filename = xmlnode_get_attrib(file, "name"))) | |
879 return; | |
880 | |
881 if((filesize_c = xmlnode_get_attrib(file, "size"))) | |
882 filesize = atoi(filesize_c); | |
883 | |
884 if(!(feature = xmlnode_get_child(si, "feature"))) | |
885 return; | |
886 | |
887 if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) | |
888 return; | |
889 | |
890 if(!(from = xmlnode_get_attrib(packet, "from"))) | |
891 return; | |
892 | |
893 /* if they've already sent us this file transfer with the same damn id | |
894 * then we're gonna ignore it, until I think of something better to do | |
895 * with it */ | |
896 if((xfer = jabber_si_xfer_find(js, stream_id, from))) | |
897 return; | |
898 | |
899 jsx = g_new0(JabberSIXfer, 1); | |
900 | |
901 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { | |
902 const char *var = xmlnode_get_attrib(field, "var"); | |
903 if(var && !strcmp(var, "stream-method")) { | |
904 for(option = xmlnode_get_child(field, "option"); option; | |
905 option = xmlnode_get_next_twin(option)) { | |
906 if((value = xmlnode_get_child(option, "value"))) { | |
907 char *val; | |
908 if((val = xmlnode_get_data(value))) { | |
909 if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) { | |
910 jsx->stream_method |= STREAM_METHOD_BYTESTREAMS; | |
911 /* | |
912 } else if(!strcmp(val, "http://jabber.org/protocol/ibb")) { | |
913 jsx->stream_method |= STREAM_METHOD_IBB; | |
914 */ | |
915 } | |
916 g_free(val); | |
917 } | |
918 } | |
919 } | |
920 } | |
921 } | |
922 | |
923 if(jsx->stream_method == STREAM_METHOD_UNKNOWN) { | |
924 g_free(jsx); | |
925 return; | |
926 } | |
927 | |
928 jsx->js = js; | |
929 jsx->stream_id = g_strdup(stream_id); | |
930 jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id")); | |
931 | |
932 xfer = gaim_xfer_new(js->gc->account, GAIM_XFER_RECEIVE, from); | |
933 xfer->data = jsx; | |
934 | |
935 gaim_xfer_set_filename(xfer, filename); | |
936 if(filesize > 0) | |
937 gaim_xfer_set_size(xfer, filesize); | |
938 | |
939 gaim_xfer_set_init_fnc(xfer, jabber_si_xfer_init); | |
940 gaim_xfer_set_request_denied_fnc(xfer, jabber_si_xfer_request_denied); | |
941 gaim_xfer_set_cancel_recv_fnc(xfer, jabber_si_xfer_cancel_recv); | |
942 gaim_xfer_set_end_fnc(xfer, jabber_si_xfer_end); | |
943 | |
944 js->file_transfers = g_list_append(js->file_transfers, xfer); | |
945 | |
946 gaim_xfer_request(xfer); | |
947 } | |
948 | |
949 |