Mercurial > pidgin
annotate src/protocols/gg/lib/events.c @ 13967:99b9b58b19dd
[gaim-migrate @ 16523]
Fix a crazy MSN crash. Basically it's possible to have more than one
slplink associated with a given switchboard, but our code did not
allow for that. I think it happens when you're in a multi-user
chat and you do stuff with multiple users that involves slplinks.
Like maybe file transfer and buddy icon related stuff.
Tracking this down took an ungodly amount of time, but thanks to
Meebo for letting me do it :-)
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Thu, 20 Jul 2006 07:31:15 +0000 |
parents | 9cbc5967fbfd |
children |
rev | line source |
---|---|
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
1 /* $Id: events.c 14520 2005-11-25 00:32:45Z rlaager $ */ |
11360 | 2 |
3 /* | |
4 * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> | |
5 * Robert J. Woźny <speedy@ziew.org> | |
6 * Arkadiusz Miśkiewicz <arekm@pld-linux.org> | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU Lesser General Public License Version | |
10 * 2.1 as published by the Free Software Foundation. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, | |
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 * GNU Lesser General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Lesser General Public | |
18 * License along with this program; if not, write to the Free Software | |
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, | |
20 * USA. | |
21 */ | |
22 | |
23 #include <sys/types.h> | |
11546
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
24 #ifndef _WIN32 |
11360 | 25 #include <sys/wait.h> |
26 #include <sys/ioctl.h> | |
27 #include <sys/socket.h> | |
28 #include <netinet/in.h> | |
29 #include <arpa/inet.h> | |
11546
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
30 #endif |
11360 | 31 |
32 #include "libgadu-config.h" | |
33 | |
34 #include <errno.h> | |
35 #ifdef __GG_LIBGADU_HAVE_PTHREAD | |
36 # include <pthread.h> | |
37 #endif | |
38 #include <stdio.h> | |
39 #include <string.h> | |
40 #include <stdlib.h> | |
41 #include <time.h> | |
42 #include <unistd.h> | |
43 #ifdef __GG_LIBGADU_HAVE_OPENSSL | |
44 # include <openssl/err.h> | |
45 # include <openssl/x509.h> | |
46 #endif | |
47 | |
48 #include "compat.h" | |
49 #include "libgadu.h" | |
50 | |
51 /* | |
52 * gg_event_free() | |
53 * | |
54 * zwalnia pamięć zajmowaną przez informację o zdarzeniu. | |
55 * | |
56 * - e - wskaźnik do informacji o zdarzeniu | |
57 */ | |
58 void gg_event_free(struct gg_event *e) | |
59 { | |
60 gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e); | |
61 | |
62 if (!e) | |
63 return; | |
64 | |
65 switch (e->type) { | |
66 case GG_EVENT_MSG: | |
67 free(e->event.msg.message); | |
68 free(e->event.msg.formats); | |
69 free(e->event.msg.recipients); | |
70 break; | |
71 | |
72 case GG_EVENT_NOTIFY: | |
73 free(e->event.notify); | |
74 break; | |
75 | |
76 case GG_EVENT_NOTIFY60: | |
77 { | |
78 int i; | |
79 | |
80 for (i = 0; e->event.notify60[i].uin; i++) | |
81 free(e->event.notify60[i].descr); | |
82 | |
83 free(e->event.notify60); | |
84 | |
85 break; | |
86 } | |
87 | |
88 case GG_EVENT_STATUS60: | |
89 free(e->event.status60.descr); | |
90 break; | |
91 | |
92 case GG_EVENT_STATUS: | |
93 free(e->event.status.descr); | |
94 break; | |
95 | |
96 case GG_EVENT_NOTIFY_DESCR: | |
97 free(e->event.notify_descr.notify); | |
98 free(e->event.notify_descr.descr); | |
99 break; | |
100 | |
101 case GG_EVENT_DCC_VOICE_DATA: | |
102 free(e->event.dcc_voice_data.data); | |
103 break; | |
104 | |
105 case GG_EVENT_PUBDIR50_SEARCH_REPLY: | |
106 case GG_EVENT_PUBDIR50_READ: | |
107 case GG_EVENT_PUBDIR50_WRITE: | |
108 gg_pubdir50_free(e->event.pubdir50); | |
109 break; | |
110 | |
111 case GG_EVENT_USERLIST: | |
112 free(e->event.userlist.reply); | |
113 break; | |
114 | |
115 case GG_EVENT_IMAGE_REPLY: | |
116 free(e->event.image_reply.filename); | |
117 free(e->event.image_reply.image); | |
118 break; | |
119 } | |
120 | |
121 free(e); | |
122 } | |
123 | |
124 /* | |
125 * gg_image_queue_remove() | |
126 * | |
127 * usuwa z kolejki dany wpis. | |
128 * | |
129 * - s - sesja | |
130 * - q - kolejka | |
131 * - freeq - czy zwolnić kolejkę | |
132 * | |
133 * 0/-1 | |
134 */ | |
135 int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) | |
136 { | |
137 if (!s || !q) { | |
138 errno = EFAULT; | |
139 return -1; | |
140 } | |
141 | |
142 if (s->images == q) | |
143 s->images = q->next; | |
144 else { | |
145 struct gg_image_queue *qq; | |
146 | |
147 for (qq = s->images; qq; qq = qq->next) { | |
148 if (qq->next == q) { | |
149 qq->next = q->next; | |
150 break; | |
151 } | |
152 } | |
153 } | |
154 | |
155 if (freeq) { | |
156 free(q->image); | |
157 free(q->filename); | |
158 free(q); | |
159 } | |
160 | |
161 return 0; | |
162 } | |
163 | |
164 /* | |
165 * gg_image_queue_parse() // funkcja wewnętrzna | |
166 * | |
167 * parsuje przychodzący pakiet z obrazkiem. | |
168 * | |
169 * - e - opis zdarzenia | |
170 * - | |
171 */ | |
172 static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender) | |
173 { | |
174 struct gg_msg_image_reply *i = (void*) p; | |
175 struct gg_image_queue *q, *qq; | |
176 | |
177 if (!p || !sess || !e) { | |
178 errno = EFAULT; | |
179 return; | |
180 } | |
181 | |
182 /* znajdź dany obrazek w kolejce danej sesji */ | |
183 | |
184 for (qq = sess->images, q = NULL; qq; qq = qq->next) { | |
185 if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) { | |
186 q = qq; | |
187 break; | |
188 } | |
189 } | |
190 | |
191 if (!q) { | |
192 gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32); | |
193 return; | |
194 } | |
195 | |
196 if (p[0] == 0x05) { | |
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
197 unsigned int i, ok = 0; |
11360 | 198 |
199 q->done = 0; | |
200 | |
201 len -= sizeof(struct gg_msg_image_reply); | |
202 p += sizeof(struct gg_msg_image_reply); | |
203 | |
204 /* sprawdź, czy mamy tekst zakończony \0 */ | |
205 | |
206 for (i = 0; i < len; i++) { | |
207 if (!p[i]) { | |
208 ok = 1; | |
209 break; | |
210 } | |
211 } | |
212 | |
213 if (!ok) { | |
214 gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender); | |
215 return; | |
216 } | |
217 | |
218 if (!(q->filename = strdup(p))) { | |
219 gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n"); | |
220 return; | |
221 } | |
222 | |
223 len -= strlen(p) + 1; | |
224 p += strlen(p) + 1; | |
225 } else { | |
226 len -= sizeof(struct gg_msg_image_reply); | |
227 p += sizeof(struct gg_msg_image_reply); | |
228 } | |
229 | |
230 if (q->done + len > q->size) | |
231 len = q->size - q->done; | |
232 | |
233 memcpy(q->image + q->done, p, len); | |
234 q->done += len; | |
235 | |
236 /* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */ | |
237 | |
238 if (q->done >= q->size) { | |
239 e->type = GG_EVENT_IMAGE_REPLY; | |
240 e->event.image_reply.sender = sender; | |
241 e->event.image_reply.size = q->size; | |
242 e->event.image_reply.crc32 = q->crc32; | |
243 e->event.image_reply.filename = q->filename; | |
244 e->event.image_reply.image = q->image; | |
245 | |
246 gg_image_queue_remove(sess, q, 0); | |
247 | |
248 free(q); | |
249 } | |
250 } | |
251 | |
252 /* | |
253 * gg_handle_recv_msg() // funkcja wewnętrzna | |
254 * | |
255 * obsługuje pakiet z przychodzącą wiadomością, rozbijając go na dodatkowe | |
256 * struktury (konferencje, kolorki) w razie potrzeby. | |
257 * | |
258 * - h - nagłówek pakietu | |
259 * - e - opis zdarzenia | |
260 * | |
261 * 0, -1. | |
262 */ | |
263 static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess) | |
264 { | |
265 struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header)); | |
266 char *p, *packet_end = (char*) r + h->length; | |
267 | |
268 gg_debug(GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e); | |
269 | |
270 if (!r->seq && !r->msgclass) { | |
271 gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n"); | |
272 e->type = GG_EVENT_NONE; | |
273 return 0; | |
274 } | |
275 | |
276 for (p = (char*) r + sizeof(*r); *p; p++) { | |
277 if (*p == 0x02 && p == packet_end - 1) { | |
278 gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n"); | |
279 break; | |
280 } | |
281 if (p >= packet_end) { | |
282 gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n"); | |
283 goto malformed; | |
284 } | |
285 } | |
286 | |
287 p++; | |
288 | |
289 /* przeanalizuj dodatkowe opcje */ | |
290 while (p < packet_end) { | |
291 switch (*p) { | |
292 case 0x01: /* konferencja */ | |
293 { | |
294 struct gg_msg_recipients *m = (void*) p; | |
295 uint32_t i, count; | |
296 | |
297 p += sizeof(*m); | |
298 | |
299 if (p > packet_end) { | |
300 gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n"); | |
301 goto malformed; | |
302 } | |
303 | |
304 count = gg_fix32(m->count); | |
305 | |
306 if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) { | |
307 gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n"); | |
308 goto malformed; | |
309 } | |
310 | |
311 if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) { | |
312 gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n"); | |
313 goto fail; | |
314 } | |
315 | |
316 for (i = 0; i < count; i++, p += sizeof(uint32_t)) { | |
317 uint32_t u; | |
318 memcpy(&u, p, sizeof(uint32_t)); | |
319 e->event.msg.recipients[i] = gg_fix32(u); | |
320 } | |
321 | |
322 e->event.msg.recipients_count = count; | |
323 | |
324 break; | |
325 } | |
326 | |
327 case 0x02: /* richtext */ | |
328 { | |
329 uint16_t len; | |
330 char *buf; | |
331 | |
332 if (p + 3 > packet_end) { | |
333 gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n"); | |
334 goto malformed; | |
335 } | |
336 | |
337 memcpy(&len, p + 1, sizeof(uint16_t)); | |
338 len = gg_fix16(len); | |
339 | |
340 if (!(buf = malloc(len))) { | |
341 gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n"); | |
342 goto fail; | |
343 } | |
344 | |
345 p += 3; | |
346 | |
347 if (p + len > packet_end) { | |
348 gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); | |
349 free(buf); | |
350 goto malformed; | |
351 } | |
352 | |
353 memcpy(buf, p, len); | |
354 | |
355 e->event.msg.formats = buf; | |
356 e->event.msg.formats_length = len; | |
357 | |
358 p += len; | |
359 | |
360 break; | |
361 } | |
362 | |
363 case 0x04: /* image_request */ | |
364 { | |
365 struct gg_msg_image_request *i = (void*) p; | |
366 | |
367 if (p + sizeof(*i) > packet_end) { | |
368 gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); | |
369 goto malformed; | |
370 } | |
371 | |
372 e->event.image_request.sender = gg_fix32(r->sender); | |
373 e->event.image_request.size = gg_fix32(i->size); | |
374 e->event.image_request.crc32 = gg_fix32(i->crc32); | |
375 | |
376 e->type = GG_EVENT_IMAGE_REQUEST; | |
377 | |
378 return 0; | |
379 } | |
380 | |
381 case 0x05: /* image_reply */ | |
382 case 0x06: | |
383 { | |
384 struct gg_msg_image_reply *rep = (void*) p; | |
385 | |
386 if (p + sizeof(struct gg_msg_image_reply) == packet_end) { | |
387 | |
388 /* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */ | |
389 | |
390 e->type = GG_EVENT_IMAGE_REPLY; | |
391 e->event.image_reply.sender = gg_fix32(r->sender); | |
392 e->event.image_reply.size = 0; | |
393 e->event.image_reply.crc32 = gg_fix32(rep->crc32); | |
394 e->event.image_reply.filename = NULL; | |
395 e->event.image_reply.image = NULL; | |
396 return 0; | |
397 | |
398 } else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) { | |
399 | |
400 gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n"); | |
401 goto malformed; | |
402 } | |
403 | |
404 rep->size = gg_fix32(rep->size); | |
405 rep->crc32 = gg_fix32(rep->crc32); | |
406 gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, gg_fix32(r->sender)); | |
407 | |
408 return 0; | |
409 } | |
410 | |
411 default: | |
412 { | |
413 gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p); | |
414 p = packet_end; | |
415 } | |
416 } | |
417 } | |
418 | |
419 e->type = GG_EVENT_MSG; | |
420 e->event.msg.msgclass = gg_fix32(r->msgclass); | |
421 e->event.msg.sender = gg_fix32(r->sender); | |
422 e->event.msg.time = gg_fix32(r->time); | |
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
423 e->event.msg.message = (unsigned char *)strdup((char*) r + sizeof(*r)); |
11360 | 424 |
425 return 0; | |
426 | |
427 malformed: | |
428 e->type = GG_EVENT_NONE; | |
429 | |
430 free(e->event.msg.recipients); | |
431 free(e->event.msg.formats); | |
432 | |
433 return 0; | |
434 | |
435 fail: | |
436 free(e->event.msg.recipients); | |
437 free(e->event.msg.formats); | |
438 return -1; | |
439 } | |
440 | |
441 /* | |
442 * gg_watch_fd_connected() // funkcja wewnętrzna | |
443 * | |
444 * patrzy na gniazdo, odbiera pakiet i wypełnia strukturę zdarzenia. | |
445 * | |
446 * - sess - struktura opisująca sesję | |
447 * - e - opis zdarzenia | |
448 * | |
449 * 0, -1. | |
450 */ | |
451 static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e) | |
452 { | |
453 struct gg_header *h = NULL; | |
454 char *p; | |
455 | |
456 gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e); | |
457 | |
458 if (!sess) { | |
459 errno = EFAULT; | |
460 return -1; | |
461 } | |
462 | |
463 if (!(h = gg_recv_packet(sess))) { | |
464 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno)); | |
465 goto fail; | |
466 } | |
467 | |
468 p = (char*) h + sizeof(struct gg_header); | |
469 | |
470 switch (h->type) { | |
471 case GG_RECV_MSG: | |
472 { | |
473 if (h->length >= sizeof(struct gg_recv_msg)) | |
474 if (gg_handle_recv_msg(h, e, sess)) | |
475 goto fail; | |
476 | |
477 break; | |
478 } | |
479 | |
480 case GG_NOTIFY_REPLY: | |
481 { | |
482 struct gg_notify_reply *n = (void*) p; | |
483 unsigned int count, i; | |
484 char *tmp; | |
485 | |
486 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); | |
487 | |
488 if (h->length < sizeof(*n)) { | |
489 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n"); | |
490 errno = EINVAL; | |
491 goto fail; | |
492 } | |
493 | |
494 if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) { | |
495 e->type = GG_EVENT_NOTIFY_DESCR; | |
496 | |
497 if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) { | |
498 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); | |
499 goto fail; | |
500 } | |
501 e->event.notify_descr.notify[1].uin = 0; | |
502 memcpy(e->event.notify_descr.notify, p, sizeof(*n)); | |
503 e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin); | |
504 e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status); | |
505 e->event.notify_descr.notify[0].remote_ip = e->event.notify_descr.notify[0].remote_ip; | |
506 e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port); | |
507 | |
508 count = h->length - sizeof(*n); | |
509 if (!(tmp = malloc(count + 1))) { | |
510 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); | |
511 goto fail; | |
512 } | |
513 memcpy(tmp, p + sizeof(*n), count); | |
514 tmp[count] = 0; | |
515 e->event.notify_descr.descr = tmp; | |
516 | |
517 } else { | |
518 e->type = GG_EVENT_NOTIFY; | |
519 | |
520 if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) { | |
521 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); | |
522 goto fail; | |
523 } | |
524 | |
525 memcpy(e->event.notify, p, h->length); | |
526 count = h->length / sizeof(*n); | |
527 e->event.notify[count].uin = 0; | |
528 | |
529 for (i = 0; i < count; i++) { | |
530 e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin); | |
531 e->event.notify[i].status = gg_fix32(e->event.notify[i].status); | |
532 e->event.notify[i].remote_ip = e->event.notify[i].remote_ip; | |
533 e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port); | |
534 } | |
535 } | |
536 | |
537 break; | |
538 } | |
539 | |
540 case GG_STATUS: | |
541 { | |
542 struct gg_status *s = (void*) p; | |
543 | |
544 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); | |
545 | |
546 if (h->length >= sizeof(*s)) { | |
547 e->type = GG_EVENT_STATUS; | |
548 memcpy(&e->event.status, p, sizeof(*s)); | |
549 e->event.status.uin = gg_fix32(e->event.status.uin); | |
550 e->event.status.status = gg_fix32(e->event.status.status); | |
551 if (h->length > sizeof(*s)) { | |
552 int len = h->length - sizeof(*s); | |
553 char *buf = malloc(len + 1); | |
554 if (buf) { | |
555 memcpy(buf, p + sizeof(*s), len); | |
556 buf[len] = 0; | |
557 } | |
558 e->event.status.descr = buf; | |
559 } else | |
560 e->event.status.descr = NULL; | |
561 } | |
562 | |
563 break; | |
564 } | |
565 | |
566 case GG_NOTIFY_REPLY60: | |
567 { | |
568 struct gg_notify_reply60 *n = (void*) p; | |
569 unsigned int length = h->length, i = 0; | |
570 | |
571 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); | |
572 | |
573 e->type = GG_EVENT_NOTIFY60; | |
574 e->event.notify60 = malloc(sizeof(*e->event.notify60)); | |
575 | |
576 if (!e->event.notify60) { | |
577 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); | |
578 goto fail; | |
579 } | |
580 | |
581 e->event.notify60[0].uin = 0; | |
582 | |
583 while (length >= sizeof(struct gg_notify_reply60)) { | |
584 uin_t uin = gg_fix32(n->uin); | |
585 char *tmp; | |
586 | |
587 e->event.notify60[i].uin = uin & 0x00ffffff; | |
588 e->event.notify60[i].status = n->status; | |
589 e->event.notify60[i].remote_ip = n->remote_ip; | |
590 e->event.notify60[i].remote_port = gg_fix16(n->remote_port); | |
591 e->event.notify60[i].version = n->version; | |
592 e->event.notify60[i].image_size = n->image_size; | |
593 e->event.notify60[i].descr = NULL; | |
594 e->event.notify60[i].time = 0; | |
595 | |
596 if (uin & 0x40000000) | |
597 e->event.notify60[i].version |= GG_HAS_AUDIO_MASK; | |
598 if (uin & 0x08000000) | |
599 e->event.notify60[i].version |= GG_ERA_OMNIX_MASK; | |
600 | |
601 if (GG_S_D(n->status)) { | |
602 unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60)); | |
603 | |
604 if (descr_len < length) { | |
605 if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) { | |
606 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); | |
607 goto fail; | |
608 } | |
609 | |
610 memcpy(e->event.notify60[i].descr, (char*) n + sizeof(struct gg_notify_reply60) + 1, descr_len); | |
611 e->event.notify60[i].descr[descr_len] = 0; | |
612 | |
613 /* XXX czas */ | |
614 } | |
615 | |
616 length -= sizeof(struct gg_notify_reply60) + descr_len + 1; | |
617 n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1); | |
618 } else { | |
619 length -= sizeof(struct gg_notify_reply60); | |
620 n = (void*) ((char*) n + sizeof(struct gg_notify_reply60)); | |
621 } | |
622 | |
623 if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { | |
624 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); | |
625 free(e->event.notify60); | |
626 goto fail; | |
627 } | |
628 | |
629 e->event.notify60 = (void*) tmp; | |
630 e->event.notify60[++i].uin = 0; | |
631 } | |
632 | |
633 break; | |
634 } | |
635 | |
636 case GG_STATUS60: | |
637 { | |
638 struct gg_status60 *s = (void*) p; | |
639 uint32_t uin; | |
640 | |
641 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); | |
642 | |
643 if (h->length < sizeof(*s)) | |
644 break; | |
645 | |
646 uin = gg_fix32(s->uin); | |
647 | |
648 e->type = GG_EVENT_STATUS60; | |
649 e->event.status60.uin = uin & 0x00ffffff; | |
650 e->event.status60.status = s->status; | |
651 e->event.status60.remote_ip = s->remote_ip; | |
652 e->event.status60.remote_port = gg_fix16(s->remote_port); | |
653 e->event.status60.version = s->version; | |
654 e->event.status60.image_size = s->image_size; | |
655 e->event.status60.descr = NULL; | |
656 e->event.status60.time = 0; | |
657 | |
658 if (uin & 0x40000000) | |
659 e->event.status60.version |= GG_HAS_AUDIO_MASK; | |
660 if (uin & 0x08000000) | |
661 e->event.status60.version |= GG_ERA_OMNIX_MASK; | |
662 | |
663 if (h->length > sizeof(*s)) { | |
664 int len = h->length - sizeof(*s); | |
665 char *buf = malloc(len + 1); | |
666 | |
667 if (buf) { | |
668 memcpy(buf, (char*) p + sizeof(*s), len); | |
669 buf[len] = 0; | |
670 } | |
671 | |
672 e->event.status60.descr = buf; | |
673 | |
674 if (len > 4 && p[h->length - 5] == 0) { | |
675 uint32_t t; | |
676 memcpy(&t, p + h->length - 4, sizeof(uint32_t)); | |
677 e->event.status60.time = gg_fix32(t); | |
678 } | |
679 } | |
680 | |
681 break; | |
682 } | |
683 | |
684 case GG_SEND_MSG_ACK: | |
685 { | |
686 struct gg_send_msg_ack *s = (void*) p; | |
687 | |
688 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n"); | |
689 | |
690 if (h->length < sizeof(*s)) | |
691 break; | |
692 | |
693 e->type = GG_EVENT_ACK; | |
694 e->event.ack.status = gg_fix32(s->status); | |
695 e->event.ack.recipient = gg_fix32(s->recipient); | |
696 e->event.ack.seq = gg_fix32(s->seq); | |
697 | |
698 break; | |
699 } | |
700 | |
701 case GG_PONG: | |
702 { | |
703 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n"); | |
704 | |
705 e->type = GG_EVENT_PONG; | |
706 sess->last_pong = time(NULL); | |
707 | |
708 break; | |
709 } | |
710 | |
711 case GG_DISCONNECTING: | |
712 { | |
713 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n"); | |
714 e->type = GG_EVENT_DISCONNECT; | |
715 break; | |
716 } | |
717 | |
718 case GG_PUBDIR50_REPLY: | |
719 { | |
720 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n"); | |
721 if (gg_pubdir50_handle_reply(e, p, h->length) == -1) | |
722 goto fail; | |
723 break; | |
724 } | |
725 | |
726 case GG_USERLIST_REPLY: | |
727 { | |
728 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n"); | |
729 | |
730 if (h->length < 1) | |
731 break; | |
732 | |
733 /* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko | |
734 * gdy otrzymano wszystkie odpowiedzi */ | |
735 if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) { | |
736 if (--sess->userlist_blocks) | |
737 break; | |
738 | |
739 p[0] = GG_USERLIST_PUT_REPLY; | |
740 } | |
741 | |
742 if (h->length > 1) { | |
743 char *tmp; | |
744 unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0; | |
745 | |
746 gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len); | |
747 | |
748 if (!(tmp = realloc(sess->userlist_reply, len + h->length))) { | |
749 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n"); | |
750 free(sess->userlist_reply); | |
751 sess->userlist_reply = NULL; | |
752 goto fail; | |
753 } | |
754 | |
755 sess->userlist_reply = tmp; | |
756 sess->userlist_reply[len + h->length - 1] = 0; | |
757 memcpy(sess->userlist_reply + len, p + 1, h->length - 1); | |
758 } | |
759 | |
760 if (p[0] == GG_USERLIST_GET_MORE_REPLY) | |
761 break; | |
762 | |
763 e->type = GG_EVENT_USERLIST; | |
764 e->event.userlist.type = p[0]; | |
765 e->event.userlist.reply = sess->userlist_reply; | |
766 sess->userlist_reply = NULL; | |
767 | |
768 break; | |
769 } | |
770 | |
771 default: | |
772 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type); | |
773 } | |
774 | |
775 free(h); | |
776 return 0; | |
777 | |
778 fail: | |
779 free(h); | |
780 return -1; | |
781 } | |
782 | |
783 /* | |
784 * gg_watch_fd() | |
785 * | |
786 * funkcja, którą należy wywołać, gdy coś się stanie z obserwowanym | |
787 * deskryptorem. zwraca klientowi informację o tym, co się dzieje. | |
788 * | |
789 * - sess - opis sesji | |
790 * | |
791 * wskaźnik do struktury gg_event, którą trzeba zwolnić później | |
792 * za pomocą gg_event_free(). jesli rodzaj zdarzenia jest równy | |
793 * GG_EVENT_NONE, należy je zignorować. jeśli zwróciło NULL, | |
794 * stało się coś niedobrego -- albo zabrakło pamięci albo zerwało | |
795 * połączenie. | |
796 */ | |
797 struct gg_event *gg_watch_fd(struct gg_session *sess) | |
798 { | |
799 struct gg_event *e; | |
800 int res = 0; | |
801 int port = 0; | |
802 int errno2 = 0; | |
803 | |
804 gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); | |
805 | |
806 if (!sess) { | |
807 errno = EFAULT; | |
808 return NULL; | |
809 } | |
810 | |
811 if (!(e = (void*) calloc(1, sizeof(*e)))) { | |
812 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); | |
813 return NULL; | |
814 } | |
815 | |
816 e->type = GG_EVENT_NONE; | |
817 | |
818 switch (sess->state) { | |
819 case GG_STATE_RESOLVING: | |
820 { | |
821 struct in_addr addr; | |
822 int failed = 0; | |
823 | |
824 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n"); | |
825 | |
826 if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) { | |
827 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); | |
828 failed = 1; | |
829 errno2 = errno; | |
830 } | |
831 | |
832 close(sess->fd); | |
833 sess->fd = -1; | |
834 | |
11546
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
835 #ifdef __GG_LIBGADU_HAVE_PTHREAD |
11360 | 836 if (sess->resolver) { |
837 pthread_cancel(*((pthread_t*) sess->resolver)); | |
838 free(sess->resolver); | |
839 sess->resolver = NULL; | |
840 } | |
11546
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
841 #elif defined _WIN32 |
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
842 if (sess->resolver) { |
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
843 HANDLE h = sess->resolver; |
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
844 TerminateThread(h, 0); |
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
845 CloseHandle(h); |
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
846 sess->resolver = NULL; |
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
847 } |
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
848 #else |
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
849 waitpid(sess->pid, NULL, 0); |
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
850 sess->pid = -1; |
11360 | 851 #endif |
852 | |
853 if (failed) { | |
854 errno = errno2; | |
855 goto fail_resolving; | |
856 } | |
857 | |
858 /* jeśli jesteśmy w resolverze i mamy ustawiony port | |
859 * proxy, znaczy, że resolvowaliśmy proxy. zatem | |
860 * wpiszmy jego adres. */ | |
861 if (sess->proxy_port) | |
862 sess->proxy_addr = addr.s_addr; | |
863 | |
864 /* zapiszmy sobie adres huba i adres serwera (do | |
865 * bezpośredniego połączenia, jeśli hub leży) | |
866 * z resolvera. */ | |
867 if (sess->proxy_addr && sess->proxy_port) | |
868 port = sess->proxy_port; | |
869 else { | |
870 sess->server_addr = sess->hub_addr = addr.s_addr; | |
871 port = GG_APPMSG_PORT; | |
872 } | |
873 | |
874 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port); | |
875 | |
876 /* łączymy się albo z hubem, albo z proxy, zależnie | |
877 * od tego, co resolvowaliśmy. */ | |
878 if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) { | |
879 /* jeśli w trybie asynchronicznym gg_connect() | |
880 * zwróci błąd, nie ma sensu próbować dalej. */ | |
881 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); | |
882 goto fail_connecting; | |
883 } | |
884 | |
885 /* jeśli podano serwer i łączmy się przez proxy, | |
886 * jest to bezpośrednie połączenie, inaczej jest | |
887 * do huba. */ | |
888 sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB; | |
889 sess->check = GG_CHECK_WRITE; | |
890 sess->timeout = GG_DEFAULT_TIMEOUT; | |
891 | |
892 break; | |
893 } | |
894 | |
895 case GG_STATE_CONNECTING_HUB: | |
896 { | |
897 char buf[1024], *client, *auth; | |
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
898 int res = 0; |
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
899 socklen_t res_size = sizeof(res); |
11360 | 900 const char *host, *appmsg; |
901 | |
902 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); | |
903 | |
904 /* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił | |
905 * przypadkiem jakiś błąd. */ | |
906 if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { | |
907 /* no tak, nie udało się połączyć z proxy. nawet | |
908 * nie próbujemy dalej. */ | |
909 if (sess->proxy_addr && sess->proxy_port) { | |
910 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); | |
911 goto fail_connecting; | |
912 } | |
913 | |
914 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res)); | |
915 close(sess->fd); | |
916 | |
917 if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) { | |
918 /* przy asynchronicznych, gg_connect() | |
919 * zwraca -1 przy błędach socket(), | |
920 * ioctl(), braku routingu itd. dlatego | |
921 * nawet nie próbujemy dalej. */ | |
922 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); | |
923 goto fail_connecting; | |
924 } | |
925 | |
926 sess->state = GG_STATE_CONNECTING_GG; | |
927 sess->check = GG_CHECK_WRITE; | |
928 sess->timeout = GG_DEFAULT_TIMEOUT; | |
929 break; | |
930 } | |
931 | |
932 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); | |
933 | |
934 if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) { | |
935 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); | |
936 goto fail_connecting; | |
937 } | |
938 | |
939 if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) | |
940 host = "http://" GG_APPMSG_HOST; | |
941 else | |
942 host = ""; | |
943 | |
944 #ifdef __GG_LIBGADU_HAVE_OPENSSL | |
945 if (sess->ssl) | |
946 appmsg = "appmsg3.asp"; | |
947 else | |
948 #endif | |
949 appmsg = "appmsg2.asp"; | |
950 | |
951 auth = gg_proxy_auth(); | |
952 | |
953 snprintf(buf, sizeof(buf) - 1, | |
954 "GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n" | |
955 "Host: " GG_APPMSG_HOST "\r\n" | |
956 "User-Agent: " GG_HTTP_USERAGENT "\r\n" | |
957 "Pragma: no-cache\r\n" | |
958 "%s" | |
959 "\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : ""); | |
960 | |
961 if (auth) | |
962 free(auth); | |
963 | |
964 free(client); | |
965 | |
966 /* zwolnij pamięć po wersji klienta. */ | |
967 if (sess->client_version) { | |
968 free(sess->client_version); | |
969 sess->client_version = NULL; | |
970 } | |
971 | |
972 gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); | |
973 | |
974 /* zapytanie jest krótkie, więc zawsze zmieści się | |
975 * do bufora gniazda. jeśli write() zwróci mniej, | |
976 * stało się coś złego. */ | |
977 if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { | |
978 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); | |
979 | |
980 e->type = GG_EVENT_CONN_FAILED; | |
981 e->event.failure = GG_FAILURE_WRITING; | |
982 sess->state = GG_STATE_IDLE; | |
983 close(sess->fd); | |
984 sess->fd = -1; | |
985 break; | |
986 } | |
987 | |
988 sess->state = GG_STATE_READING_DATA; | |
989 sess->check = GG_CHECK_READ; | |
990 sess->timeout = GG_DEFAULT_TIMEOUT; | |
991 | |
992 break; | |
993 } | |
994 | |
995 case GG_STATE_READING_DATA: | |
996 { | |
997 char buf[1024], *tmp, *host; | |
998 int port = GG_DEFAULT_PORT; | |
999 struct in_addr addr; | |
1000 | |
1001 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); | |
1002 | |
1003 /* czytamy linię z gniazda i obcinamy \r\n. */ | |
1004 gg_read_line(sess->fd, buf, sizeof(buf) - 1); | |
1005 gg_chomp(buf); | |
1006 gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); | |
1007 | |
1008 /* sprawdzamy, czy wszystko w porządku. */ | |
1009 if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) { | |
1010 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n"); | |
1011 | |
1012 close(sess->fd); | |
1013 | |
1014 /* jeśli otrzymaliśmy jakieś dziwne informacje, | |
1015 * próbujemy się łączyć z pominięciem huba. */ | |
1016 if (sess->proxy_addr && sess->proxy_port) { | |
1017 if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { | |
1018 /* trudno. nie wyszło. */ | |
1019 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); | |
1020 goto fail_connecting; | |
1021 } | |
1022 | |
1023 sess->state = GG_STATE_CONNECTING_GG; | |
1024 sess->check = GG_CHECK_WRITE; | |
1025 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1026 break; | |
1027 } | |
1028 | |
1029 sess->port = GG_DEFAULT_PORT; | |
1030 | |
1031 /* łączymy się na port 8074 huba. */ | |
1032 if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { | |
1033 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); | |
1034 | |
1035 sess->port = GG_HTTPS_PORT; | |
1036 | |
1037 /* łączymy się na port 443. */ | |
1038 if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { | |
1039 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); | |
1040 goto fail_connecting; | |
1041 } | |
1042 } | |
1043 | |
1044 sess->state = GG_STATE_CONNECTING_GG; | |
1045 sess->check = GG_CHECK_WRITE; | |
1046 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1047 break; | |
1048 } | |
1049 | |
1050 /* ignorujemy resztę nagłówka. */ | |
1051 while (strcmp(buf, "\r\n") && strcmp(buf, "")) | |
1052 gg_read_line(sess->fd, buf, sizeof(buf) - 1); | |
1053 | |
1054 /* czytamy pierwszą linię danych. */ | |
1055 gg_read_line(sess->fd, buf, sizeof(buf) - 1); | |
1056 gg_chomp(buf); | |
1057 | |
1058 /* jeśli pierwsza liczba w linii nie jest równa zeru, | |
1059 * oznacza to, że mamy wiadomość systemową. */ | |
1060 if (atoi(buf)) { | |
1061 char tmp[1024], *foo, *sysmsg_buf = NULL; | |
1062 int len = 0; | |
1063 | |
1064 while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) { | |
1065 if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) { | |
1066 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); | |
1067 break; | |
1068 } | |
1069 | |
1070 sysmsg_buf = foo; | |
1071 | |
1072 if (!len) | |
1073 strcpy(sysmsg_buf, tmp); | |
1074 else | |
1075 strcat(sysmsg_buf, tmp); | |
1076 | |
1077 len += strlen(tmp); | |
1078 } | |
1079 | |
1080 e->type = GG_EVENT_MSG; | |
1081 e->event.msg.msgclass = atoi(buf); | |
1082 e->event.msg.sender = 0; | |
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
1083 e->event.msg.message = (unsigned char *)sysmsg_buf; |
11360 | 1084 } |
1085 | |
1086 close(sess->fd); | |
1087 | |
1088 gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); | |
1089 | |
1090 /* analizujemy otrzymane dane. */ | |
1091 tmp = buf; | |
1092 | |
1093 while (*tmp && *tmp != ' ') | |
1094 tmp++; | |
1095 while (*tmp && *tmp == ' ') | |
1096 tmp++; | |
1097 host = tmp; | |
1098 while (*tmp && *tmp != ' ') | |
1099 tmp++; | |
1100 *tmp = 0; | |
1101 | |
1102 if ((tmp = strchr(host, ':'))) { | |
1103 *tmp = 0; | |
1104 port = atoi(tmp + 1); | |
1105 } | |
1106 | |
1107 addr.s_addr = inet_addr(host); | |
1108 sess->server_addr = addr.s_addr; | |
1109 | |
1110 if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) { | |
1111 /* jeśli mamy proxy, łączymy się z nim. */ | |
1112 if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { | |
1113 /* nie wyszło? trudno. */ | |
1114 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); | |
1115 goto fail_connecting; | |
1116 } | |
1117 | |
1118 sess->state = GG_STATE_CONNECTING_GG; | |
1119 sess->check = GG_CHECK_WRITE; | |
1120 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1121 break; | |
1122 } | |
1123 | |
1124 sess->port = port; | |
1125 | |
1126 /* łączymy się z właściwym serwerem. */ | |
1127 if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { | |
1128 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); | |
1129 | |
1130 sess->port = GG_HTTPS_PORT; | |
1131 | |
1132 /* nie wyszło? próbujemy portu 443. */ | |
1133 if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) { | |
1134 /* ostatnia deska ratunku zawiodła? | |
1135 * w takim razie zwijamy manatki. */ | |
1136 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); | |
1137 goto fail_connecting; | |
1138 } | |
1139 } | |
1140 | |
1141 sess->state = GG_STATE_CONNECTING_GG; | |
1142 sess->check = GG_CHECK_WRITE; | |
1143 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1144 | |
1145 break; | |
1146 } | |
1147 | |
1148 case GG_STATE_CONNECTING_GG: | |
1149 { | |
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
1150 int res = 0; |
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
1151 socklen_t res_size = sizeof(res); |
11360 | 1152 |
1153 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); | |
1154 | |
1155 /* jeśli wystąpił błąd podczas łączenia się... */ | |
1156 if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { | |
1157 /* jeśli nie udało się połączenie z proxy, | |
1158 * nie mamy czego próbować więcej. */ | |
1159 if (sess->proxy_addr && sess->proxy_port) { | |
1160 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); | |
1161 goto fail_connecting; | |
1162 } | |
1163 | |
1164 close(sess->fd); | |
1165 sess->fd = -1; | |
1166 | |
1167 #ifdef ETIMEDOUT | |
1168 if (sess->timeout == 0) | |
1169 errno = ETIMEDOUT; | |
1170 #endif | |
1171 | |
1172 #ifdef __GG_LIBGADU_HAVE_OPENSSL | |
1173 /* jeśli logujemy się po TLS, nie próbujemy | |
1174 * się łączyć już z niczym innym w przypadku | |
1175 * błędu. nie dość, że nie ma sensu, to i | |
1176 * trzeba by się bawić w tworzenie na nowo | |
1177 * SSL i SSL_CTX. */ | |
1178 | |
1179 if (sess->ssl) { | |
1180 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); | |
1181 goto fail_connecting; | |
1182 } | |
1183 #endif | |
1184 | |
1185 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); | |
1186 | |
1187 sess->port = GG_HTTPS_PORT; | |
1188 | |
1189 /* próbujemy na port 443. */ | |
1190 if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { | |
1191 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); | |
1192 goto fail_connecting; | |
1193 } | |
1194 } | |
1195 | |
1196 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); | |
1197 | |
1198 if (gg_proxy_http_only) | |
1199 sess->proxy_port = 0; | |
1200 | |
1201 /* jeśli mamy proxy, wyślijmy zapytanie. */ | |
1202 if (sess->proxy_addr && sess->proxy_port) { | |
1203 char buf[100], *auth = gg_proxy_auth(); | |
1204 struct in_addr addr; | |
1205 | |
1206 if (sess->server_addr) | |
1207 addr.s_addr = sess->server_addr; | |
1208 else | |
1209 addr.s_addr = sess->hub_addr; | |
1210 | |
1211 snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port); | |
1212 | |
1213 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf); | |
1214 | |
1215 /* wysyłamy zapytanie. jest ono na tyle krótkie, | |
1216 * że musi się zmieścić w buforze gniazda. jeśli | |
1217 * write() zawiedzie, stało się coś złego. */ | |
1218 if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { | |
1219 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); | |
1220 goto fail_connecting; | |
1221 } | |
1222 | |
1223 if (auth) { | |
1224 gg_debug(GG_DEBUG_MISC, "// %s", auth); | |
1225 if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) { | |
1226 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); | |
1227 goto fail_connecting; | |
1228 } | |
1229 | |
1230 free(auth); | |
1231 } | |
1232 | |
1233 if (write(sess->fd, "\r\n", 2) < 2) { | |
1234 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); | |
1235 goto fail_connecting; | |
1236 } | |
1237 } | |
1238 | |
1239 #ifdef __GG_LIBGADU_HAVE_OPENSSL | |
1240 if (sess->ssl) { | |
1241 SSL_set_fd(sess->ssl, sess->fd); | |
1242 | |
1243 sess->state = GG_STATE_TLS_NEGOTIATION; | |
1244 sess->check = GG_CHECK_WRITE; | |
1245 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1246 | |
1247 break; | |
1248 } | |
1249 #endif | |
1250 | |
1251 sess->state = GG_STATE_READING_KEY; | |
1252 sess->check = GG_CHECK_READ; | |
1253 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1254 | |
1255 break; | |
1256 } | |
1257 | |
1258 #ifdef __GG_LIBGADU_HAVE_OPENSSL | |
1259 case GG_STATE_TLS_NEGOTIATION: | |
1260 { | |
1261 int res; | |
1262 X509 *peer; | |
1263 | |
1264 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); | |
1265 | |
1266 if ((res = SSL_connect(sess->ssl)) <= 0) { | |
1267 int err = SSL_get_error(sess->ssl, res); | |
1268 | |
1269 if (res == 0) { | |
1270 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); | |
1271 | |
1272 e->type = GG_EVENT_CONN_FAILED; | |
1273 e->event.failure = GG_FAILURE_TLS; | |
1274 sess->state = GG_STATE_IDLE; | |
1275 close(sess->fd); | |
1276 sess->fd = -1; | |
1277 break; | |
1278 } | |
1279 | |
1280 if (err == SSL_ERROR_WANT_READ) { | |
1281 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); | |
1282 | |
1283 sess->state = GG_STATE_TLS_NEGOTIATION; | |
1284 sess->check = GG_CHECK_READ; | |
1285 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1286 | |
1287 break; | |
1288 } else if (err == SSL_ERROR_WANT_WRITE) { | |
1289 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); | |
1290 | |
1291 sess->state = GG_STATE_TLS_NEGOTIATION; | |
1292 sess->check = GG_CHECK_WRITE; | |
1293 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1294 | |
1295 break; | |
1296 } else { | |
1297 char buf[1024]; | |
1298 | |
1299 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); | |
1300 | |
1301 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); | |
1302 | |
1303 e->type = GG_EVENT_CONN_FAILED; | |
1304 e->event.failure = GG_FAILURE_TLS; | |
1305 sess->state = GG_STATE_IDLE; | |
1306 close(sess->fd); | |
1307 sess->fd = -1; | |
1308 break; | |
1309 } | |
1310 } | |
1311 | |
1312 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl)); | |
1313 | |
1314 peer = SSL_get_peer_certificate(sess->ssl); | |
1315 | |
1316 if (!peer) | |
1317 gg_debug(GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); | |
1318 else { | |
1319 char buf[1024]; | |
1320 | |
1321 X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf)); | |
1322 gg_debug(GG_DEBUG_MISC, "// cert subject: %s\n", buf); | |
1323 | |
1324 X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf)); | |
1325 gg_debug(GG_DEBUG_MISC, "// cert issuer: %s\n", buf); | |
1326 } | |
1327 | |
1328 sess->state = GG_STATE_READING_KEY; | |
1329 sess->check = GG_CHECK_READ; | |
1330 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1331 | |
1332 break; | |
1333 } | |
1334 #endif | |
1335 | |
1336 case GG_STATE_READING_KEY: | |
1337 { | |
1338 struct gg_header *h; | |
1339 struct gg_welcome *w; | |
1340 struct gg_login60 l; | |
1341 unsigned int hash; | |
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
1342 char *password = sess->password; |
11360 | 1343 int ret; |
1344 | |
1345 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); | |
1346 | |
1347 memset(&l, 0, sizeof(l)); | |
1348 l.dunno2 = 0xbe; | |
1349 | |
1350 /* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie | |
1351 * się tekstu wrzucanego przez proxy. */ | |
1352 if (sess->proxy_addr && sess->proxy_port) { | |
1353 char buf[100]; | |
1354 | |
1355 strcpy(buf, ""); | |
1356 gg_read_line(sess->fd, buf, sizeof(buf) - 1); | |
1357 gg_chomp(buf); | |
1358 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf); | |
1359 | |
1360 while (strcmp(buf, "")) { | |
1361 gg_read_line(sess->fd, buf, sizeof(buf) - 1); | |
1362 gg_chomp(buf); | |
1363 if (strcmp(buf, "")) | |
1364 gg_debug(GG_DEBUG_MISC, "// %s\n", buf); | |
1365 } | |
1366 | |
1367 /* XXX niech czeka jeszcze raz w tej samej | |
1368 * fazie. głupio, ale działa. */ | |
1369 sess->proxy_port = 0; | |
1370 | |
1371 break; | |
1372 } | |
1373 | |
1374 /* czytaj pierwszy pakiet. */ | |
1375 if (!(h = gg_recv_packet(sess))) { | |
1376 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); | |
1377 | |
1378 e->type = GG_EVENT_CONN_FAILED; | |
1379 e->event.failure = GG_FAILURE_READING; | |
1380 sess->state = GG_STATE_IDLE; | |
1381 errno2 = errno; | |
1382 close(sess->fd); | |
1383 errno = errno2; | |
1384 sess->fd = -1; | |
1385 break; | |
1386 } | |
1387 | |
1388 if (h->type != GG_WELCOME) { | |
1389 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); | |
1390 free(h); | |
1391 close(sess->fd); | |
1392 sess->fd = -1; | |
1393 errno = EINVAL; | |
1394 e->type = GG_EVENT_CONN_FAILED; | |
1395 e->event.failure = GG_FAILURE_INVALID; | |
1396 sess->state = GG_STATE_IDLE; | |
1397 break; | |
1398 } | |
1399 | |
1400 w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header)); | |
1401 w->key = gg_fix32(w->key); | |
1402 | |
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
1403 hash = gg_login_hash((unsigned char *)password, w->key); |
11360 | 1404 |
1405 gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash); | |
1406 | |
1407 free(h); | |
1408 | |
1409 free(sess->password); | |
1410 sess->password = NULL; | |
1411 | |
1412 { | |
1413 struct in_addr dcc_ip; | |
1414 dcc_ip.s_addr = gg_dcc_ip; | |
1415 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip)); | |
1416 } | |
1417 | |
1418 if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) { | |
1419 struct sockaddr_in sin; | |
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
1420 socklen_t sin_len = sizeof(sin); |
11360 | 1421 |
1422 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); | |
1423 | |
1424 if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) { | |
1425 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); | |
1426 l.local_ip = sin.sin_addr.s_addr; | |
1427 } else { | |
1428 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); | |
1429 l.local_ip = 0; | |
1430 } | |
1431 } else | |
1432 l.local_ip = gg_dcc_ip; | |
1433 | |
1434 l.uin = gg_fix32(sess->uin); | |
1435 l.hash = gg_fix32(hash); | |
1436 l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); | |
1437 l.version = gg_fix32(sess->protocol_version); | |
1438 l.local_port = gg_fix16(gg_dcc_port); | |
1439 l.image_size = sess->image_size; | |
1440 | |
1441 if (sess->external_addr && sess->external_port > 1023) { | |
1442 l.external_ip = sess->external_addr; | |
1443 l.external_port = gg_fix16(sess->external_port); | |
1444 } | |
1445 | |
1446 gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n"); | |
1447 ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL); | |
1448 | |
1449 free(sess->initial_descr); | |
1450 sess->initial_descr = NULL; | |
1451 | |
1452 if (ret == -1) { | |
1453 gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); | |
1454 errno2 = errno; | |
1455 close(sess->fd); | |
1456 errno = errno2; | |
1457 sess->fd = -1; | |
1458 e->type = GG_EVENT_CONN_FAILED; | |
1459 e->event.failure = GG_FAILURE_WRITING; | |
1460 sess->state = GG_STATE_IDLE; | |
1461 break; | |
1462 } | |
1463 | |
1464 sess->state = GG_STATE_READING_REPLY; | |
1465 | |
1466 break; | |
1467 } | |
1468 | |
1469 case GG_STATE_READING_REPLY: | |
1470 { | |
1471 struct gg_header *h; | |
1472 | |
1473 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); | |
1474 | |
1475 if (!(h = gg_recv_packet(sess))) { | |
1476 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); | |
1477 e->type = GG_EVENT_CONN_FAILED; | |
1478 e->event.failure = GG_FAILURE_READING; | |
1479 sess->state = GG_STATE_IDLE; | |
1480 errno2 = errno; | |
1481 close(sess->fd); | |
1482 errno = errno2; | |
1483 sess->fd = -1; | |
1484 break; | |
1485 } | |
1486 | |
1487 if (h->type == GG_LOGIN_OK) { | |
1488 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); | |
1489 e->type = GG_EVENT_CONN_SUCCESS; | |
1490 sess->state = GG_STATE_CONNECTED; | |
1491 sess->timeout = -1; | |
1492 sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL; | |
1493 free(h); | |
1494 break; | |
1495 } | |
1496 | |
1497 if (h->type == GG_LOGIN_FAILED) { | |
1498 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); | |
1499 e->event.failure = GG_FAILURE_PASSWORD; | |
1500 errno = EACCES; | |
1501 } else if (h->type == GG_NEED_EMAIL) { | |
1502 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() email change needed\n"); | |
1503 e->event.failure = GG_FAILURE_NEED_EMAIL; | |
1504 errno = EACCES; | |
1505 } else { | |
1506 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); | |
1507 e->event.failure = GG_FAILURE_INVALID; | |
1508 errno = EINVAL; | |
1509 } | |
1510 | |
1511 e->type = GG_EVENT_CONN_FAILED; | |
1512 sess->state = GG_STATE_IDLE; | |
1513 errno2 = errno; | |
1514 close(sess->fd); | |
1515 errno = errno2; | |
1516 sess->fd = -1; | |
1517 free(h); | |
1518 | |
1519 break; | |
1520 } | |
1521 | |
1522 case GG_STATE_CONNECTED: | |
1523 { | |
1524 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); | |
1525 | |
1526 sess->last_event = time(NULL); | |
1527 | |
1528 if ((res = gg_watch_fd_connected(sess, e)) == -1) { | |
1529 | |
1530 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); | |
1531 | |
1532 if (errno == EAGAIN) { | |
1533 e->type = GG_EVENT_NONE; | |
1534 res = 0; | |
1535 } else | |
1536 res = -1; | |
1537 } | |
1538 break; | |
1539 } | |
1540 } | |
1541 | |
1542 done: | |
1543 if (res == -1) { | |
1544 free(e); | |
1545 e = NULL; | |
1546 } | |
1547 | |
1548 return e; | |
1549 | |
1550 fail_connecting: | |
1551 if (sess->fd != -1) { | |
1552 errno2 = errno; | |
1553 close(sess->fd); | |
1554 errno = errno2; | |
1555 sess->fd = -1; | |
1556 } | |
1557 e->type = GG_EVENT_CONN_FAILED; | |
1558 e->event.failure = GG_FAILURE_CONNECTING; | |
1559 sess->state = GG_STATE_IDLE; | |
1560 goto done; | |
1561 | |
1562 fail_resolving: | |
1563 e->type = GG_EVENT_CONN_FAILED; | |
1564 e->event.failure = GG_FAILURE_RESOLVING; | |
1565 sess->state = GG_STATE_IDLE; | |
1566 goto done; | |
1567 } | |
1568 | |
1569 /* | |
1570 * Local variables: | |
1571 * c-indentation-style: k&r | |
1572 * c-basic-offset: 8 | |
1573 * indent-tabs-mode: notnil | |
1574 * End: | |
1575 * | |
1576 * vim: shiftwidth=8: | |
1577 */ |