Mercurial > pidgin.yaz
annotate src/protocols/jabber/si.c @ 12508:5cfc53ead482
[gaim-migrate @ 14820]
patch from Simon Wilkinson to add Cyrus SASL support for jabber
Give him credit if it works flawlessly. Blame me if it doesn't, as the
patch was against 1.3.1 (yeah, I've been sitting on it for that long), and
I had to merge it to HEAD, and clean up a bunch of warnings
committer: Tailor Script <tailor@pidgin.im>
author | Nathan Walp <nwalp@pidgin.im> |
---|---|
date | Sat, 17 Dec 2005 02:24:05 +0000 |
parents | cbebda5f019c |
children | d5b8f4dc1622 |
rev | line source |
---|---|
7395 | 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 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 * | |
20 */ | |
9030 | 21 |
22 #include "blist.h" | |
23 | |
7395 | 24 #include "internal.h" |
10684
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10112
diff
changeset
|
25 #include "cipher.h" |
7395 | 26 #include "debug.h" |
27 #include "ft.h" | |
8231
f50c059b6384
[gaim-migrate @ 8954]
Christian Hammond <chipx86@chipx86.com>
parents:
8135
diff
changeset
|
28 #include "network.h" |
7395 | 29 #include "notify.h" |
30 | |
31 #include "buddy.h" | |
8312 | 32 #include "disco.h" |
7395 | 33 #include "jabber.h" |
34 #include "iq.h" | |
35 #include "si.h" | |
36 | |
37 #include "si.h" | |
38 | |
8262 | 39 struct bytestreams_streamhost { |
40 char *jid; | |
41 char *host; | |
42 int port; | |
43 }; | |
44 | |
45 typedef struct _JabberSIXfer { | |
46 JabberStream *js; | |
47 | |
10940 | 48 gboolean accepted; |
49 | |
8262 | 50 char *stream_id; |
51 char *iq_id; | |
52 | |
53 enum { | |
54 STREAM_METHOD_UNKNOWN = 0, | |
55 STREAM_METHOD_BYTESTREAMS = 2 << 1, | |
56 STREAM_METHOD_IBB = 2 << 2, | |
57 STREAM_METHOD_UNSUPPORTED = 2 << 31 | |
58 } stream_method; | |
59 | |
60 GList *streamhosts; | |
61 GaimProxyInfo *gpi; | |
8316 | 62 |
63 char *rxqueue; | |
64 size_t rxlen; | |
8262 | 65 } JabberSIXfer; |
66 | |
67 static GaimXfer* | |
68 jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from) | |
7395 | 69 { |
70 GList *xfers; | |
71 | |
8262 | 72 if(!sid || !from) |
7395 | 73 return NULL; |
74 | |
75 for(xfers = js->file_transfers; xfers; xfers = xfers->next) { | |
76 GaimXfer *xfer = xfers->data; | |
77 JabberSIXfer *jsx = xfer->data; | |
8316 | 78 if(jsx->stream_id && xfer->who && |
79 !strcmp(jsx->stream_id, sid) && !strcmp(xfer->who, from)) | |
7395 | 80 return xfer; |
81 } | |
82 | |
83 return NULL; | |
84 } | |
85 | |
8262 | 86 |
87 static void jabber_si_bytestreams_attempt_connect(GaimXfer *xfer); | |
88 | |
89 static void jabber_si_bytestreams_connect_cb(gpointer data, gint source, GaimInputCondition cond) | |
90 { | |
7395 | 91 GaimXfer *xfer = data; |
92 JabberSIXfer *jsx = xfer->data; | |
8262 | 93 JabberIq *iq; |
94 xmlnode *query, *su; | |
95 struct bytestreams_streamhost *streamhost = jsx->streamhosts->data; | |
7395 | 96 |
8262 | 97 gaim_proxy_info_destroy(jsx->gpi); |
98 | |
99 if(source < 0) { | |
100 jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost); | |
101 g_free(streamhost->jid); | |
102 g_free(streamhost->host); | |
103 g_free(streamhost); | |
104 jabber_si_bytestreams_attempt_connect(xfer); | |
105 return; | |
106 } | |
107 | |
108 iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, "http://jabber.org/protocol/bytestreams"); | |
109 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
110 jabber_iq_set_id(iq, jsx->iq_id); | |
111 query = xmlnode_get_child(iq->node, "query"); | |
112 su = xmlnode_new_child(query, "streamhost-used"); | |
113 xmlnode_set_attrib(su, "jid", streamhost->jid); | |
114 | |
115 jabber_iq_send(iq); | |
116 | |
117 gaim_xfer_start(xfer, source, NULL, -1); | |
118 } | |
119 | |
120 static void jabber_si_bytestreams_attempt_connect(GaimXfer *xfer) | |
121 { | |
122 JabberSIXfer *jsx = xfer->data; | |
123 struct bytestreams_streamhost *streamhost; | |
124 char *dstaddr, *p; | |
125 int i; | |
126 unsigned char hashval[20]; | |
127 | |
128 if(!jsx->streamhosts) { | |
129 JabberIq *iq = jabber_iq_new(jsx->js, JABBER_IQ_ERROR); | |
130 xmlnode *error, *condition; | |
131 | |
132 if(jsx->iq_id) | |
133 jabber_iq_set_id(iq, jsx->iq_id); | |
134 | |
135 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
136 error = xmlnode_new_child(iq->node, "error"); | |
137 xmlnode_set_attrib(error, "code", "404"); | |
138 xmlnode_set_attrib(error, "type", "cancel"); | |
139 condition = xmlnode_new_child(error, "condition"); | |
140 xmlnode_set_attrib(condition, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas"); | |
141 xmlnode_new_child(condition, "item-not-found"); | |
142 | |
143 jabber_iq_send(iq); | |
144 | |
145 gaim_xfer_cancel_local(xfer); | |
146 | |
147 return; | |
148 } | |
149 | |
150 streamhost = jsx->streamhosts->data; | |
7395 | 151 |
8262 | 152 jsx->gpi = gaim_proxy_info_new(); |
153 gaim_proxy_info_set_type(jsx->gpi, GAIM_PROXY_SOCKS5); | |
154 gaim_proxy_info_set_host(jsx->gpi, streamhost->host); | |
155 gaim_proxy_info_set_port(jsx->gpi, streamhost->port); | |
156 | |
157 dstaddr = g_strdup_printf("%s%s%s@%s/%s", jsx->stream_id, xfer->who, jsx->js->user->node, | |
158 jsx->js->user->domain, jsx->js->user->resource); | |
10684
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10112
diff
changeset
|
159 |
11183 | 160 gaim_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr), |
10687
b256ce6b85b8
[gaim-migrate @ 12235]
Etan Reisner <pidgin@unreliablesource.net>
parents:
10684
diff
changeset
|
161 sizeof(hashval), hashval, NULL); |
8262 | 162 g_free(dstaddr); |
163 dstaddr = g_malloc(41); | |
164 p = dstaddr; | |
165 for(i=0; i<20; i++, p+=2) | |
166 snprintf(p, 3, "%02x", hashval[i]); | |
167 | |
168 gaim_proxy_connect_socks5(jsx->gpi, dstaddr, 0, jabber_si_bytestreams_connect_cb, xfer); | |
169 g_free(dstaddr); | |
170 } | |
171 | |
172 void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet) | |
173 { | |
174 GaimXfer *xfer; | |
175 JabberSIXfer *jsx; | |
176 xmlnode *query, *streamhost; | |
177 const char *sid, *from; | |
178 | |
179 if(!(from = xmlnode_get_attrib(packet, "from"))) | |
180 return; | |
181 | |
182 if(!(query = xmlnode_get_child(packet, "query"))) | |
183 return; | |
184 | |
185 if(!(sid = xmlnode_get_attrib(query, "sid"))) | |
186 return; | |
187 | |
188 if(!(xfer = jabber_si_xfer_find(js, sid, from))) | |
189 return; | |
190 | |
191 jsx = xfer->data; | |
10940 | 192 |
193 if(!jsx->accepted) | |
194 return; | |
195 | |
8262 | 196 if(jsx->iq_id) |
197 g_free(jsx->iq_id); | |
198 jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id")); | |
199 | |
200 for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost; | |
201 streamhost = xmlnode_get_next_twin(streamhost)) { | |
202 const char *jid, *host, *port; | |
203 int portnum; | |
204 | |
205 if((jid = xmlnode_get_attrib(streamhost, "jid")) && | |
206 (host = xmlnode_get_attrib(streamhost, "host")) && | |
207 (port = xmlnode_get_attrib(streamhost, "port")) && | |
208 (portnum = atoi(port))) { | |
209 struct bytestreams_streamhost *sh = g_new0(struct bytestreams_streamhost, 1); | |
210 sh->jid = g_strdup(jid); | |
211 sh->host = g_strdup(host); | |
212 sh->port = portnum; | |
213 jsx->streamhosts = g_list_append(jsx->streamhosts, sh); | |
214 } | |
215 } | |
216 | |
217 jabber_si_bytestreams_attempt_connect(xfer); | |
7395 | 218 } |
219 | |
8312 | 220 static void |
8316 | 221 jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source, |
8312 | 222 GaimInputCondition cond) |
223 { | |
224 GaimXfer *xfer = data; | |
8316 | 225 JabberSIXfer *jsx = xfer->data; |
8312 | 226 int i; |
8316 | 227 char buffer[256]; |
228 int len; | |
229 char *dstaddr, *p; | |
230 unsigned char hashval[20]; | |
231 const char *host; | |
232 | |
233 gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_again_cb\n"); | |
8312 | 234 |
8316 | 235 if(jsx->rxlen < 5) { |
236 gaim_debug_info("jabber", "reading the first 5 bytes\n"); | |
237 if((len = read(source, buffer, 5 - jsx->rxlen)) <= 0) { | |
238 gaim_input_remove(xfer->watcher); | |
239 xfer->watcher = 0; | |
240 close(source); | |
241 gaim_xfer_cancel_remote(xfer); | |
242 return; | |
243 } | |
244 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen); | |
245 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len); | |
246 jsx->rxlen += len; | |
247 return; | |
248 } else if(jsx->rxqueue[0] != 0x05 || jsx->rxqueue[1] != 0x01 || | |
249 jsx->rxqueue[3] != 0x03) { | |
250 gaim_debug_info("jabber", "invalid socks5 stuff\n"); | |
251 gaim_input_remove(xfer->watcher); | |
252 xfer->watcher = 0; | |
253 close(source); | |
254 gaim_xfer_cancel_remote(xfer); | |
255 return; | |
256 } else if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2) { | |
257 gaim_debug_info("jabber", "reading umpteen more bytes\n"); | |
258 if((len = read(source, buffer, jsx->rxqueue[4] + 5 + 2 - jsx->rxlen)) <= 0) { | |
259 gaim_input_remove(xfer->watcher); | |
260 xfer->watcher = 0; | |
261 close(source); | |
262 gaim_xfer_cancel_remote(xfer); | |
263 return; | |
264 } | |
265 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen); | |
266 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len); | |
267 jsx->rxlen += len; | |
268 } | |
8312 | 269 |
8316 | 270 if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2) |
271 return; | |
272 | |
273 gaim_input_remove(xfer->watcher); | |
274 xfer->watcher = 0; | |
8312 | 275 |
8316 | 276 dstaddr = g_strdup_printf("%s%s@%s/%s%s", jsx->stream_id, |
277 jsx->js->user->node, jsx->js->user->domain, | |
278 jsx->js->user->resource, xfer->who); | |
10684
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10112
diff
changeset
|
279 |
11183 | 280 gaim_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr), |
10687
b256ce6b85b8
[gaim-migrate @ 12235]
Etan Reisner <pidgin@unreliablesource.net>
parents:
10684
diff
changeset
|
281 sizeof(hashval), hashval, NULL); |
8316 | 282 g_free(dstaddr); |
283 dstaddr = g_malloc(41); | |
284 p = dstaddr; | |
285 for(i=0; i<20; i++, p+=2) | |
286 snprintf(p, 3, "%02x", hashval[i]); | |
287 | |
288 if(jsx->rxqueue[4] != 40 || strncmp(dstaddr, jsx->rxqueue+5, 40) || | |
289 jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00) { | |
290 gaim_debug_error("jabber", "someone connected with the wrong info!\n"); | |
8312 | 291 close(source); |
292 gaim_xfer_cancel_remote(xfer); | |
293 return; | |
294 } | |
295 | |
8838 | 296 host = gaim_network_get_my_ip(jsx->js->fd); |
8316 | 297 |
298 buffer[0] = 0x05; | |
299 buffer[1] = 0x00; | |
300 buffer[2] = 0x00; | |
301 buffer[3] = 0x03; | |
302 buffer[4] = strlen(host); | |
303 memcpy(buffer + 5, host, strlen(host)); | |
304 buffer[5+strlen(host)] = 0x00; | |
305 buffer[6+strlen(host)] = 0x00; | |
306 | |
307 write(source, buffer, strlen(host)+7); | |
308 | |
309 gaim_xfer_start(xfer, source, NULL, -1); | |
310 } | |
311 | |
312 static void | |
313 jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source, | |
314 GaimInputCondition cond) | |
315 { | |
316 GaimXfer *xfer = data; | |
317 JabberSIXfer *jsx = xfer->data; | |
318 int i; | |
319 int len; | |
320 char buffer[256]; | |
321 | |
322 gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_cb\n"); | |
323 | |
324 xfer->fd = source; | |
325 | |
326 if(jsx->rxlen < 2) { | |
327 gaim_debug_info("jabber", "reading those first two bytes\n"); | |
328 if((len = read(source, buffer, 2 - jsx->rxlen)) <= 0) { | |
329 gaim_input_remove(xfer->watcher); | |
330 xfer->watcher = 0; | |
331 close(source); | |
332 gaim_xfer_cancel_remote(xfer); | |
333 return; | |
334 } | |
335 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen); | |
336 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len); | |
337 jsx->rxlen += len; | |
338 return; | |
339 } else if(jsx->rxlen - 2 < jsx->rxqueue[1]) { | |
340 gaim_debug_info("jabber", "reading the next umpteen bytes\n"); | |
341 if((len = read(source, buffer, jsx->rxqueue[1] + 2 - jsx->rxlen)) <= 0) { | |
342 gaim_input_remove(xfer->watcher); | |
343 xfer->watcher = 0; | |
344 close(source); | |
345 gaim_xfer_cancel_remote(xfer); | |
346 return; | |
347 } | |
348 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen); | |
349 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len); | |
350 jsx->rxlen += len; | |
351 } | |
352 | |
353 if(jsx->rxlen -2 < jsx->rxqueue[1]) | |
354 return; | |
355 | |
356 gaim_input_remove(xfer->watcher); | |
357 xfer->watcher = 0; | |
358 | |
359 | |
360 gaim_debug_info("jabber", "checking to make sure we're socks FIVE\n"); | |
361 | |
362 if(jsx->rxqueue[0] != 0x05) { | |
363 close(source); | |
364 gaim_xfer_cancel_remote(xfer); | |
365 return; | |
366 } | |
367 | |
368 gaim_debug_info("jabber", "going to test %hhu different methods\n", jsx->rxqueue[1]); | |
369 | |
370 for(i=0; i<jsx->rxqueue[1]; i++) { | |
371 | |
372 gaim_debug_info("jabber", "testing %hhu\n", jsx->rxqueue[i+2]); | |
373 if(jsx->rxqueue[i+2] == 0x00) { | |
374 buffer[0] = 0x05; | |
375 buffer[1] = 0x00; | |
376 write(source, buffer, 2); | |
377 xfer->watcher = gaim_input_add(source, GAIM_INPUT_READ, | |
378 jabber_si_xfer_bytestreams_send_read_again_cb, xfer); | |
379 g_free(jsx->rxqueue); | |
380 jsx->rxqueue = NULL; | |
381 jsx->rxlen = 0; | |
8312 | 382 return; |
383 } | |
384 } | |
385 | |
8316 | 386 buffer[0] = 0x05; |
387 buffer[1] = 0xFF; | |
388 write(source, buffer, 2); | |
389 close(source); | |
390 g_free(jsx->rxqueue); | |
391 jsx->rxqueue = NULL; | |
392 jsx->rxlen = 0; | |
8312 | 393 gaim_xfer_cancel_remote(xfer); |
394 } | |
395 | |
396 static void | |
8316 | 397 jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source, |
398 GaimInputCondition cond) | |
399 { | |
400 GaimXfer *xfer = data; | |
401 int acceptfd; | |
402 | |
403 gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n"); | |
404 | |
405 if((acceptfd = accept(source, NULL, 0)) == -1) { | |
406 gaim_debug_warning("jabber", "accept: %s\n", strerror(errno)); | |
407 return; | |
408 } | |
409 | |
410 gaim_input_remove(xfer->watcher); | |
411 close(source); | |
412 | |
413 xfer->watcher = gaim_input_add(acceptfd, GAIM_INPUT_READ, | |
414 jabber_si_xfer_bytestreams_send_read_cb, xfer); | |
415 } | |
416 | |
417 | |
418 static void | |
8312 | 419 jabber_si_xfer_bytestreams_send_init(GaimXfer *xfer) |
420 { | |
421 JabberSIXfer *jsx = xfer->data; | |
422 JabberIq *iq; | |
423 xmlnode *query, *streamhost; | |
424 char *jid, *port; | |
425 int fd; | |
426 | |
427 iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, | |
428 "http://jabber.org/protocol/bytestreams"); | |
429 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
430 query = xmlnode_get_child(iq->node, "query"); | |
431 | |
432 xmlnode_set_attrib(query, "sid", jsx->stream_id); | |
433 | |
434 streamhost = xmlnode_new_child(query, "streamhost"); | |
435 jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource); | |
436 xmlnode_set_attrib(streamhost, "jid", jid); | |
437 g_free(jid); | |
438 | |
439 if((fd = gaim_network_listen_range(0, 0)) < 0) { | |
440 /* XXX: couldn't open a port, we're fscked */ | |
441 return; | |
442 } | |
443 | |
8838 | 444 xmlnode_set_attrib(streamhost, "host", gaim_network_get_my_ip(jsx->js->fd)); |
8312 | 445 xfer->local_port = gaim_network_get_port_from_fd(fd); |
8316 | 446 port = g_strdup_printf("%hu", xfer->local_port); |
8312 | 447 xmlnode_set_attrib(streamhost, "port", port); |
448 g_free(port); | |
449 | |
450 xfer->watcher = gaim_input_add(fd, GAIM_INPUT_READ, | |
451 jabber_si_xfer_bytestreams_send_connected_cb, xfer); | |
452 | |
453 /* XXX: insert proxies here */ | |
454 | |
455 /* XXX: callback to find out which streamhost they used, or see if they | |
456 * screwed it up */ | |
457 jabber_iq_send(iq); | |
458 } | |
459 | |
460 static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet, | |
461 gpointer data) | |
462 { | |
463 GaimXfer *xfer = data; | |
464 xmlnode *si, *feature, *x, *field, *value; | |
465 | |
466 if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) { | |
467 gaim_xfer_cancel_remote(xfer); | |
468 return; | |
469 } | |
470 | |
471 if(!(feature = xmlnode_get_child_with_namespace(si, "feature", "http://jabber.org/protocol/feature-neg"))) { | |
472 gaim_xfer_cancel_remote(xfer); | |
473 return; | |
474 } | |
475 | |
476 if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) { | |
477 gaim_xfer_cancel_remote(xfer); | |
478 return; | |
479 } | |
480 | |
481 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { | |
482 const char *var = xmlnode_get_attrib(field, "var"); | |
483 | |
484 if(var && !strcmp(var, "stream-method")) { | |
485 if((value = xmlnode_get_child(field, "value"))) { | |
486 char *val = xmlnode_get_data(value); | |
487 if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) { | |
488 jabber_si_xfer_bytestreams_send_init(xfer); | |
489 g_free(val); | |
490 return; | |
491 } | |
492 g_free(val); | |
493 } | |
494 } | |
495 } | |
496 gaim_xfer_cancel_remote(xfer); | |
497 } | |
498 | |
499 static void jabber_si_xfer_send_request(GaimXfer *xfer) | |
500 { | |
501 JabberSIXfer *jsx = xfer->data; | |
502 JabberIq *iq; | |
503 xmlnode *si, *file, *feature, *x, *field, *option, *value; | |
504 char buf[32]; | |
505 | |
506 xfer->filename = g_path_get_basename(xfer->local_filename); | |
507 | |
508 iq = jabber_iq_new(jsx->js, JABBER_IQ_SET); | |
509 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
510 si = xmlnode_new_child(iq->node, "si"); | |
511 xmlnode_set_attrib(si, "xmlns", "http://jabber.org/protocol/si"); | |
512 jsx->stream_id = jabber_get_next_id(jsx->js); | |
513 xmlnode_set_attrib(si, "id", jsx->stream_id); | |
514 xmlnode_set_attrib(si, "profile", | |
515 "http://jabber.org/protocol/si/profile/file-transfer"); | |
516 | |
517 file = xmlnode_new_child(si, "file"); | |
518 xmlnode_set_attrib(file, "xmlns", | |
519 "http://jabber.org/protocol/si/profile/file-transfer"); | |
520 xmlnode_set_attrib(file, "name", xfer->filename); | |
11656
f9c5480ad0ce
[gaim-migrate @ 13940]
Richard Laager <rlaager@wiktel.com>
parents:
11183
diff
changeset
|
521 g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size); |
8312 | 522 xmlnode_set_attrib(file, "size", buf); |
523 /* maybe later we'll do hash and date attribs */ | |
524 | |
525 feature = xmlnode_new_child(si, "feature"); | |
526 xmlnode_set_attrib(feature, "xmlns", | |
527 "http://jabber.org/protocol/feature-neg"); | |
528 x = xmlnode_new_child(feature, "x"); | |
529 xmlnode_set_attrib(x, "xmlns", "jabber:x:data"); | |
530 xmlnode_set_attrib(x, "type", "form"); | |
531 field = xmlnode_new_child(x, "field"); | |
532 xmlnode_set_attrib(field, "var", "stream-method"); | |
533 xmlnode_set_attrib(field, "type", "list-single"); | |
534 option = xmlnode_new_child(field, "option"); | |
535 value = xmlnode_new_child(option, "value"); | |
536 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", | |
537 -1); | |
538 /* | |
539 option = xmlnode_new_child(field, "option"); | |
540 value = xmlnode_new_child(option, "value"); | |
541 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); | |
542 */ | |
543 | |
544 jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer); | |
545 | |
546 jabber_iq_send(iq); | |
547 } | |
548 | |
8316 | 549 static void jabber_si_xfer_free(GaimXfer *xfer) |
8312 | 550 { |
8316 | 551 JabberSIXfer *jsx = xfer->data; |
552 JabberStream *js = jsx->js; | |
553 | |
554 js->file_transfers = g_list_remove(js->file_transfers, xfer); | |
555 | |
556 g_free(jsx->stream_id); | |
557 g_free(jsx->iq_id); | |
558 /* XXX: free other stuff */ | |
559 g_free(jsx); | |
560 xfer->data = NULL; | |
561 } | |
562 | |
563 static void jabber_si_xfer_cancel_send(GaimXfer *xfer) | |
564 { | |
565 jabber_si_xfer_free(xfer); | |
8312 | 566 gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_send\n"); |
567 } | |
568 | |
569 | |
8316 | 570 static void jabber_si_xfer_cancel_recv(GaimXfer *xfer) |
8312 | 571 { |
8316 | 572 jabber_si_xfer_free(xfer); |
8312 | 573 gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_recv\n"); |
574 } | |
575 | |
576 | |
8316 | 577 static void jabber_si_xfer_end(GaimXfer *xfer) |
578 { | |
579 jabber_si_xfer_free(xfer); | |
580 } | |
581 | |
582 | |
8312 | 583 static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who, |
584 JabberCapabilities capabilities, gpointer data) | |
585 { | |
586 GaimXfer *xfer = data; | |
587 | |
588 if(capabilities & JABBER_CAP_SI_FILE_XFER) { | |
589 jabber_si_xfer_send_request(xfer); | |
590 } else { | |
591 char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who); | |
592 gaim_notify_error(js->gc, _("File Send Failed"), | |
593 _("File Send Failed"), msg); | |
594 g_free(msg); | |
595 } | |
596 } | |
597 | |
8262 | 598 static void jabber_si_xfer_init(GaimXfer *xfer) |
599 { | |
600 JabberSIXfer *jsx = xfer->data; | |
601 JabberIq *iq; | |
8312 | 602 if(gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) { |
603 JabberBuddy *jb; | |
604 JabberBuddyResource *jbr = NULL; | |
605 | |
606 jb = jabber_buddy_find(jsx->js, xfer->who, TRUE); | |
607 /* XXX */ | |
608 if(!jb) | |
609 return; | |
8262 | 610 |
8312 | 611 /* XXX: for now, send to the first resource available */ |
612 if(g_list_length(jb->resources) >= 1) { | |
613 char *who; | |
8316 | 614 jbr = jabber_buddy_find_resource(jb, NULL); |
8312 | 615 who = g_strdup_printf("%s/%s", xfer->who, jbr->name); |
616 g_free(xfer->who); | |
617 xfer->who = who; | |
618 jabber_disco_info_do(jsx->js, who, | |
619 jabber_si_xfer_send_disco_cb, xfer); | |
620 } else { | |
621 return; /* XXX: ick */ | |
622 } | |
623 } else { | |
624 xmlnode *si, *feature, *x, *field, *value; | |
8262 | 625 |
8312 | 626 iq = jabber_iq_new(jsx->js, JABBER_IQ_RESULT); |
627 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
628 if(jsx->iq_id) | |
629 jabber_iq_set_id(iq, jsx->iq_id); | |
630 | |
10940 | 631 jsx->accepted = TRUE; |
632 | |
8312 | 633 si = xmlnode_new_child(iq->node, "si"); |
634 xmlnode_set_attrib(si, "xmlns", "http://jabber.org/protocol/si"); | |
635 | |
636 feature = xmlnode_new_child(si, "feature"); | |
637 xmlnode_set_attrib(feature, "xmlns", "http://jabber.org/protocol/feature-neg"); | |
8262 | 638 |
8312 | 639 x = xmlnode_new_child(feature, "x"); |
640 xmlnode_set_attrib(x, "xmlns", "jabber:x:data"); | |
8343 | 641 xmlnode_set_attrib(x, "type", "submit"); |
8262 | 642 |
8312 | 643 field = xmlnode_new_child(x, "field"); |
644 xmlnode_set_attrib(field, "var", "stream-method"); | |
645 | |
646 value = xmlnode_new_child(field, "value"); | |
647 if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) | |
648 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); | |
649 /* | |
650 else if(jsx->stream_method & STREAM_METHOD_IBB) | |
8262 | 651 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); |
652 */ | |
653 | |
8312 | 654 jabber_iq_send(iq); |
655 } | |
8262 | 656 } |
657 | |
12143
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
658 GaimXfer *jabber_si_new_xfer(GaimConnection *gc, const char *who) |
8312 | 659 { |
9030 | 660 JabberStream *js; |
661 | |
8312 | 662 GaimXfer *xfer; |
663 JabberSIXfer *jsx; | |
664 | |
9030 | 665 js = gc->proto_data; |
666 | |
9466 | 667 xfer = gaim_xfer_new(gc->account, GAIM_XFER_SEND, who); |
8262 | 668 |
8312 | 669 xfer->data = jsx = g_new0(JabberSIXfer, 1); |
670 jsx->js = js; | |
8262 | 671 |
8312 | 672 gaim_xfer_set_init_fnc(xfer, jabber_si_xfer_init); |
673 gaim_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send); | |
8316 | 674 gaim_xfer_set_end_fnc(xfer, jabber_si_xfer_end); |
8312 | 675 |
12143
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
676 js->file_transfers = g_list_append(js->file_transfers, xfer); |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
677 |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
678 return xfer; |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
679 } |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
680 |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
681 void jabber_si_xfer_send(GaimConnection *gc, const char *who, const char *file) |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
682 { |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
683 JabberStream *js; |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
684 |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
685 GaimXfer *xfer; |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
686 |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
687 js = gc->proto_data; |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
688 |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
689 if(!gaim_find_buddy(gc->account, who) || !jabber_buddy_find(js, who, FALSE)) |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
690 return; |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
691 |
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
692 xfer = jabber_si_new_xfer(gc, who); |
8312 | 693 |
9466 | 694 if (file) |
695 gaim_xfer_request_accepted(xfer, file); | |
696 else | |
697 gaim_xfer_request(xfer); | |
8262 | 698 } |
699 | |
700 void jabber_si_parse(JabberStream *js, xmlnode *packet) | |
701 { | |
702 JabberSIXfer *jsx; | |
703 GaimXfer *xfer; | |
704 xmlnode *si, *file, *feature, *x, *field, *option, *value; | |
10939 | 705 const char *stream_id, *filename, *filesize_c, *profile, *from; |
8262 | 706 size_t filesize = 0; |
707 | |
708 if(!(si = xmlnode_get_child(packet, "si"))) | |
709 return; | |
710 | |
711 if(!(profile = xmlnode_get_attrib(si, "profile")) || | |
712 strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) | |
713 return; | |
714 | |
715 if(!(stream_id = xmlnode_get_attrib(si, "id"))) | |
716 return; | |
717 | |
718 if(!(file = xmlnode_get_child(si, "file"))) | |
719 return; | |
720 | |
721 if(!(filename = xmlnode_get_attrib(file, "name"))) | |
722 return; | |
723 | |
724 if((filesize_c = xmlnode_get_attrib(file, "size"))) | |
725 filesize = atoi(filesize_c); | |
726 | |
727 if(!(feature = xmlnode_get_child(si, "feature"))) | |
728 return; | |
729 | |
730 if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) | |
731 return; | |
732 | |
10939 | 733 if(!(from = xmlnode_get_attrib(packet, "from"))) |
734 return; | |
735 | |
736 /* if they've already sent us this file transfer with the same damn id | |
737 * then we're gonna ignore it, until I think of something better to do | |
738 * with it */ | |
739 if((xfer = jabber_si_xfer_find(js, stream_id, from))) | |
740 return; | |
741 | |
8262 | 742 jsx = g_new0(JabberSIXfer, 1); |
743 | |
744 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { | |
745 const char *var = xmlnode_get_attrib(field, "var"); | |
746 if(var && !strcmp(var, "stream-method")) { | |
747 for(option = xmlnode_get_child(field, "option"); option; | |
748 option = xmlnode_get_next_twin(option)) { | |
749 if((value = xmlnode_get_child(option, "value"))) { | |
750 char *val; | |
751 if((val = xmlnode_get_data(value))) { | |
752 if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) { | |
753 jsx->stream_method |= STREAM_METHOD_BYTESTREAMS; | |
754 /* | |
755 } else if(!strcmp(val, "http://jabber.org/protocol/ibb")) { | |
756 jsx->stream_method |= STREAM_METHOD_IBB; | |
757 */ | |
758 } | |
759 g_free(val); | |
760 } | |
761 } | |
762 } | |
763 } | |
764 } | |
765 | |
766 if(jsx->stream_method == STREAM_METHOD_UNKNOWN) { | |
767 g_free(jsx); | |
768 return; | |
769 } | |
770 | |
771 jsx->js = js; | |
772 jsx->stream_id = g_strdup(stream_id); | |
773 jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id")); | |
774 | |
10939 | 775 xfer = gaim_xfer_new(js->gc->account, GAIM_XFER_RECEIVE, from); |
8262 | 776 xfer->data = jsx; |
777 | |
778 gaim_xfer_set_filename(xfer, filename); | |
779 if(filesize > 0) | |
780 gaim_xfer_set_size(xfer, filesize); | |
781 | |
782 gaim_xfer_set_init_fnc(xfer, jabber_si_xfer_init); | |
8316 | 783 gaim_xfer_set_cancel_recv_fnc(xfer, jabber_si_xfer_cancel_recv); |
784 gaim_xfer_set_end_fnc(xfer, jabber_si_xfer_end); | |
8262 | 785 |
786 js->file_transfers = g_list_append(js->file_transfers, xfer); | |
787 | |
788 gaim_xfer_request(xfer); | |
789 } | |
790 | |
7395 | 791 |