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