Mercurial > pidgin.yaz
annotate src/protocols/gg/lib/events.c @ 11911:fe12abd6b879
[gaim-migrate @ 14202]
SF Patch #1339005 from Sadrul
"This patch is a fix for this small bug:
(1) open a new conversation with some buddy in a contact.
(2) open another conversation with another buddy in the
same contact.
(3) now press space/enter on the first buddy in the
buddy-list. you will notice that the active-buddy in
the conversation doesn't change to the buddy you just
activated."
committer: Tailor Script <tailor@pidgin.im>
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Sun, 30 Oct 2005 23:36:51 +0000 |
parents | 3c536224f0d0 |
children | 9cbc5967fbfd |
rev | line source |
---|---|
11546
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
1 /* $Id: events.c 13801 2005-09-14 19:10:39Z datallah $ */ |
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) { | |
197 int i, ok = 0; | |
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); | |
423 e->event.msg.message = strdup((char*) r + sizeof(*r)); | |
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; | |
898 int res = 0, res_size = sizeof(res); | |
899 const char *host, *appmsg; | |
900 | |
901 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); | |
902 | |
903 /* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił | |
904 * przypadkiem jakiś błąd. */ | |
905 if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { | |
906 /* no tak, nie udało się połączyć z proxy. nawet | |
907 * nie próbujemy dalej. */ | |
908 if (sess->proxy_addr && sess->proxy_port) { | |
909 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); | |
910 goto fail_connecting; | |
911 } | |
912 | |
913 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res)); | |
914 close(sess->fd); | |
915 | |
916 if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) { | |
917 /* przy asynchronicznych, gg_connect() | |
918 * zwraca -1 przy błędach socket(), | |
919 * ioctl(), braku routingu itd. dlatego | |
920 * nawet nie próbujemy dalej. */ | |
921 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); | |
922 goto fail_connecting; | |
923 } | |
924 | |
925 sess->state = GG_STATE_CONNECTING_GG; | |
926 sess->check = GG_CHECK_WRITE; | |
927 sess->timeout = GG_DEFAULT_TIMEOUT; | |
928 break; | |
929 } | |
930 | |
931 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); | |
932 | |
933 if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) { | |
934 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); | |
935 goto fail_connecting; | |
936 } | |
937 | |
938 if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) | |
939 host = "http://" GG_APPMSG_HOST; | |
940 else | |
941 host = ""; | |
942 | |
943 #ifdef __GG_LIBGADU_HAVE_OPENSSL | |
944 if (sess->ssl) | |
945 appmsg = "appmsg3.asp"; | |
946 else | |
947 #endif | |
948 appmsg = "appmsg2.asp"; | |
949 | |
950 auth = gg_proxy_auth(); | |
951 | |
952 snprintf(buf, sizeof(buf) - 1, | |
953 "GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n" | |
954 "Host: " GG_APPMSG_HOST "\r\n" | |
955 "User-Agent: " GG_HTTP_USERAGENT "\r\n" | |
956 "Pragma: no-cache\r\n" | |
957 "%s" | |
958 "\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : ""); | |
959 | |
960 if (auth) | |
961 free(auth); | |
962 | |
963 free(client); | |
964 | |
965 /* zwolnij pamięć po wersji klienta. */ | |
966 if (sess->client_version) { | |
967 free(sess->client_version); | |
968 sess->client_version = NULL; | |
969 } | |
970 | |
971 gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); | |
972 | |
973 /* zapytanie jest krótkie, więc zawsze zmieści się | |
974 * do bufora gniazda. jeśli write() zwróci mniej, | |
975 * stało się coś złego. */ | |
976 if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { | |
977 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); | |
978 | |
979 e->type = GG_EVENT_CONN_FAILED; | |
980 e->event.failure = GG_FAILURE_WRITING; | |
981 sess->state = GG_STATE_IDLE; | |
982 close(sess->fd); | |
983 sess->fd = -1; | |
984 break; | |
985 } | |
986 | |
987 sess->state = GG_STATE_READING_DATA; | |
988 sess->check = GG_CHECK_READ; | |
989 sess->timeout = GG_DEFAULT_TIMEOUT; | |
990 | |
991 break; | |
992 } | |
993 | |
994 case GG_STATE_READING_DATA: | |
995 { | |
996 char buf[1024], *tmp, *host; | |
997 int port = GG_DEFAULT_PORT; | |
998 struct in_addr addr; | |
999 | |
1000 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); | |
1001 | |
1002 /* czytamy linię z gniazda i obcinamy \r\n. */ | |
1003 gg_read_line(sess->fd, buf, sizeof(buf) - 1); | |
1004 gg_chomp(buf); | |
1005 gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); | |
1006 | |
1007 /* sprawdzamy, czy wszystko w porządku. */ | |
1008 if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) { | |
1009 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n"); | |
1010 | |
1011 close(sess->fd); | |
1012 | |
1013 /* jeśli otrzymaliśmy jakieś dziwne informacje, | |
1014 * próbujemy się łączyć z pominięciem huba. */ | |
1015 if (sess->proxy_addr && sess->proxy_port) { | |
1016 if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { | |
1017 /* trudno. nie wyszło. */ | |
1018 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); | |
1019 goto fail_connecting; | |
1020 } | |
1021 | |
1022 sess->state = GG_STATE_CONNECTING_GG; | |
1023 sess->check = GG_CHECK_WRITE; | |
1024 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1025 break; | |
1026 } | |
1027 | |
1028 sess->port = GG_DEFAULT_PORT; | |
1029 | |
1030 /* łączymy się na port 8074 huba. */ | |
1031 if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { | |
1032 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); | |
1033 | |
1034 sess->port = GG_HTTPS_PORT; | |
1035 | |
1036 /* łączymy się na port 443. */ | |
1037 if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { | |
1038 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); | |
1039 goto fail_connecting; | |
1040 } | |
1041 } | |
1042 | |
1043 sess->state = GG_STATE_CONNECTING_GG; | |
1044 sess->check = GG_CHECK_WRITE; | |
1045 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1046 break; | |
1047 } | |
1048 | |
1049 /* ignorujemy resztę nagłówka. */ | |
1050 while (strcmp(buf, "\r\n") && strcmp(buf, "")) | |
1051 gg_read_line(sess->fd, buf, sizeof(buf) - 1); | |
1052 | |
1053 /* czytamy pierwszą linię danych. */ | |
1054 gg_read_line(sess->fd, buf, sizeof(buf) - 1); | |
1055 gg_chomp(buf); | |
1056 | |
1057 /* jeśli pierwsza liczba w linii nie jest równa zeru, | |
1058 * oznacza to, że mamy wiadomość systemową. */ | |
1059 if (atoi(buf)) { | |
1060 char tmp[1024], *foo, *sysmsg_buf = NULL; | |
1061 int len = 0; | |
1062 | |
1063 while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) { | |
1064 if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) { | |
1065 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); | |
1066 break; | |
1067 } | |
1068 | |
1069 sysmsg_buf = foo; | |
1070 | |
1071 if (!len) | |
1072 strcpy(sysmsg_buf, tmp); | |
1073 else | |
1074 strcat(sysmsg_buf, tmp); | |
1075 | |
1076 len += strlen(tmp); | |
1077 } | |
1078 | |
1079 e->type = GG_EVENT_MSG; | |
1080 e->event.msg.msgclass = atoi(buf); | |
1081 e->event.msg.sender = 0; | |
1082 e->event.msg.message = sysmsg_buf; | |
1083 } | |
1084 | |
1085 close(sess->fd); | |
1086 | |
1087 gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); | |
1088 | |
1089 /* analizujemy otrzymane dane. */ | |
1090 tmp = buf; | |
1091 | |
1092 while (*tmp && *tmp != ' ') | |
1093 tmp++; | |
1094 while (*tmp && *tmp == ' ') | |
1095 tmp++; | |
1096 host = tmp; | |
1097 while (*tmp && *tmp != ' ') | |
1098 tmp++; | |
1099 *tmp = 0; | |
1100 | |
1101 if ((tmp = strchr(host, ':'))) { | |
1102 *tmp = 0; | |
1103 port = atoi(tmp + 1); | |
1104 } | |
1105 | |
1106 addr.s_addr = inet_addr(host); | |
1107 sess->server_addr = addr.s_addr; | |
1108 | |
1109 if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) { | |
1110 /* jeśli mamy proxy, łączymy się z nim. */ | |
1111 if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { | |
1112 /* nie wyszło? trudno. */ | |
1113 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); | |
1114 goto fail_connecting; | |
1115 } | |
1116 | |
1117 sess->state = GG_STATE_CONNECTING_GG; | |
1118 sess->check = GG_CHECK_WRITE; | |
1119 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1120 break; | |
1121 } | |
1122 | |
1123 sess->port = port; | |
1124 | |
1125 /* łączymy się z właściwym serwerem. */ | |
1126 if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { | |
1127 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); | |
1128 | |
1129 sess->port = GG_HTTPS_PORT; | |
1130 | |
1131 /* nie wyszło? próbujemy portu 443. */ | |
1132 if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) { | |
1133 /* ostatnia deska ratunku zawiodła? | |
1134 * w takim razie zwijamy manatki. */ | |
1135 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); | |
1136 goto fail_connecting; | |
1137 } | |
1138 } | |
1139 | |
1140 sess->state = GG_STATE_CONNECTING_GG; | |
1141 sess->check = GG_CHECK_WRITE; | |
1142 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1143 | |
1144 break; | |
1145 } | |
1146 | |
1147 case GG_STATE_CONNECTING_GG: | |
1148 { | |
1149 int res = 0, res_size = sizeof(res); | |
1150 | |
1151 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); | |
1152 | |
1153 /* jeśli wystąpił błąd podczas łączenia się... */ | |
1154 if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { | |
1155 /* jeśli nie udało się połączenie z proxy, | |
1156 * nie mamy czego próbować więcej. */ | |
1157 if (sess->proxy_addr && sess->proxy_port) { | |
1158 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); | |
1159 goto fail_connecting; | |
1160 } | |
1161 | |
1162 close(sess->fd); | |
1163 sess->fd = -1; | |
1164 | |
1165 #ifdef ETIMEDOUT | |
1166 if (sess->timeout == 0) | |
1167 errno = ETIMEDOUT; | |
1168 #endif | |
1169 | |
1170 #ifdef __GG_LIBGADU_HAVE_OPENSSL | |
1171 /* jeśli logujemy się po TLS, nie próbujemy | |
1172 * się łączyć już z niczym innym w przypadku | |
1173 * błędu. nie dość, że nie ma sensu, to i | |
1174 * trzeba by się bawić w tworzenie na nowo | |
1175 * SSL i SSL_CTX. */ | |
1176 | |
1177 if (sess->ssl) { | |
1178 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); | |
1179 goto fail_connecting; | |
1180 } | |
1181 #endif | |
1182 | |
1183 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); | |
1184 | |
1185 sess->port = GG_HTTPS_PORT; | |
1186 | |
1187 /* próbujemy na port 443. */ | |
1188 if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { | |
1189 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); | |
1190 goto fail_connecting; | |
1191 } | |
1192 } | |
1193 | |
1194 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); | |
1195 | |
1196 if (gg_proxy_http_only) | |
1197 sess->proxy_port = 0; | |
1198 | |
1199 /* jeśli mamy proxy, wyślijmy zapytanie. */ | |
1200 if (sess->proxy_addr && sess->proxy_port) { | |
1201 char buf[100], *auth = gg_proxy_auth(); | |
1202 struct in_addr addr; | |
1203 | |
1204 if (sess->server_addr) | |
1205 addr.s_addr = sess->server_addr; | |
1206 else | |
1207 addr.s_addr = sess->hub_addr; | |
1208 | |
1209 snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port); | |
1210 | |
1211 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf); | |
1212 | |
1213 /* wysyłamy zapytanie. jest ono na tyle krótkie, | |
1214 * że musi się zmieścić w buforze gniazda. jeśli | |
1215 * write() zawiedzie, stało się coś złego. */ | |
1216 if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { | |
1217 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); | |
1218 goto fail_connecting; | |
1219 } | |
1220 | |
1221 if (auth) { | |
1222 gg_debug(GG_DEBUG_MISC, "// %s", auth); | |
1223 if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) { | |
1224 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); | |
1225 goto fail_connecting; | |
1226 } | |
1227 | |
1228 free(auth); | |
1229 } | |
1230 | |
1231 if (write(sess->fd, "\r\n", 2) < 2) { | |
1232 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); | |
1233 goto fail_connecting; | |
1234 } | |
1235 } | |
1236 | |
1237 #ifdef __GG_LIBGADU_HAVE_OPENSSL | |
1238 if (sess->ssl) { | |
1239 SSL_set_fd(sess->ssl, sess->fd); | |
1240 | |
1241 sess->state = GG_STATE_TLS_NEGOTIATION; | |
1242 sess->check = GG_CHECK_WRITE; | |
1243 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1244 | |
1245 break; | |
1246 } | |
1247 #endif | |
1248 | |
1249 sess->state = GG_STATE_READING_KEY; | |
1250 sess->check = GG_CHECK_READ; | |
1251 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1252 | |
1253 break; | |
1254 } | |
1255 | |
1256 #ifdef __GG_LIBGADU_HAVE_OPENSSL | |
1257 case GG_STATE_TLS_NEGOTIATION: | |
1258 { | |
1259 int res; | |
1260 X509 *peer; | |
1261 | |
1262 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); | |
1263 | |
1264 if ((res = SSL_connect(sess->ssl)) <= 0) { | |
1265 int err = SSL_get_error(sess->ssl, res); | |
1266 | |
1267 if (res == 0) { | |
1268 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); | |
1269 | |
1270 e->type = GG_EVENT_CONN_FAILED; | |
1271 e->event.failure = GG_FAILURE_TLS; | |
1272 sess->state = GG_STATE_IDLE; | |
1273 close(sess->fd); | |
1274 sess->fd = -1; | |
1275 break; | |
1276 } | |
1277 | |
1278 if (err == SSL_ERROR_WANT_READ) { | |
1279 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); | |
1280 | |
1281 sess->state = GG_STATE_TLS_NEGOTIATION; | |
1282 sess->check = GG_CHECK_READ; | |
1283 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1284 | |
1285 break; | |
1286 } else if (err == SSL_ERROR_WANT_WRITE) { | |
1287 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); | |
1288 | |
1289 sess->state = GG_STATE_TLS_NEGOTIATION; | |
1290 sess->check = GG_CHECK_WRITE; | |
1291 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1292 | |
1293 break; | |
1294 } else { | |
1295 char buf[1024]; | |
1296 | |
1297 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); | |
1298 | |
1299 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); | |
1300 | |
1301 e->type = GG_EVENT_CONN_FAILED; | |
1302 e->event.failure = GG_FAILURE_TLS; | |
1303 sess->state = GG_STATE_IDLE; | |
1304 close(sess->fd); | |
1305 sess->fd = -1; | |
1306 break; | |
1307 } | |
1308 } | |
1309 | |
1310 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl)); | |
1311 | |
1312 peer = SSL_get_peer_certificate(sess->ssl); | |
1313 | |
1314 if (!peer) | |
1315 gg_debug(GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); | |
1316 else { | |
1317 char buf[1024]; | |
1318 | |
1319 X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf)); | |
1320 gg_debug(GG_DEBUG_MISC, "// cert subject: %s\n", buf); | |
1321 | |
1322 X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf)); | |
1323 gg_debug(GG_DEBUG_MISC, "// cert issuer: %s\n", buf); | |
1324 } | |
1325 | |
1326 sess->state = GG_STATE_READING_KEY; | |
1327 sess->check = GG_CHECK_READ; | |
1328 sess->timeout = GG_DEFAULT_TIMEOUT; | |
1329 | |
1330 break; | |
1331 } | |
1332 #endif | |
1333 | |
1334 case GG_STATE_READING_KEY: | |
1335 { | |
1336 struct gg_header *h; | |
1337 struct gg_welcome *w; | |
1338 struct gg_login60 l; | |
1339 unsigned int hash; | |
1340 unsigned char *password = sess->password; | |
1341 int ret; | |
1342 | |
1343 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); | |
1344 | |
1345 memset(&l, 0, sizeof(l)); | |
1346 l.dunno2 = 0xbe; | |
1347 | |
1348 /* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie | |
1349 * się tekstu wrzucanego przez proxy. */ | |
1350 if (sess->proxy_addr && sess->proxy_port) { | |
1351 char buf[100]; | |
1352 | |
1353 strcpy(buf, ""); | |
1354 gg_read_line(sess->fd, buf, sizeof(buf) - 1); | |
1355 gg_chomp(buf); | |
1356 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf); | |
1357 | |
1358 while (strcmp(buf, "")) { | |
1359 gg_read_line(sess->fd, buf, sizeof(buf) - 1); | |
1360 gg_chomp(buf); | |
1361 if (strcmp(buf, "")) | |
1362 gg_debug(GG_DEBUG_MISC, "// %s\n", buf); | |
1363 } | |
1364 | |
1365 /* XXX niech czeka jeszcze raz w tej samej | |
1366 * fazie. głupio, ale działa. */ | |
1367 sess->proxy_port = 0; | |
1368 | |
1369 break; | |
1370 } | |
1371 | |
1372 /* czytaj pierwszy pakiet. */ | |
1373 if (!(h = gg_recv_packet(sess))) { | |
1374 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); | |
1375 | |
1376 e->type = GG_EVENT_CONN_FAILED; | |
1377 e->event.failure = GG_FAILURE_READING; | |
1378 sess->state = GG_STATE_IDLE; | |
1379 errno2 = errno; | |
1380 close(sess->fd); | |
1381 errno = errno2; | |
1382 sess->fd = -1; | |
1383 break; | |
1384 } | |
1385 | |
1386 if (h->type != GG_WELCOME) { | |
1387 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); | |
1388 free(h); | |
1389 close(sess->fd); | |
1390 sess->fd = -1; | |
1391 errno = EINVAL; | |
1392 e->type = GG_EVENT_CONN_FAILED; | |
1393 e->event.failure = GG_FAILURE_INVALID; | |
1394 sess->state = GG_STATE_IDLE; | |
1395 break; | |
1396 } | |
1397 | |
1398 w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header)); | |
1399 w->key = gg_fix32(w->key); | |
1400 | |
1401 hash = gg_login_hash(password, w->key); | |
1402 | |
1403 gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash); | |
1404 | |
1405 free(h); | |
1406 | |
1407 free(sess->password); | |
1408 sess->password = NULL; | |
1409 | |
1410 { | |
1411 struct in_addr dcc_ip; | |
1412 dcc_ip.s_addr = gg_dcc_ip; | |
1413 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip)); | |
1414 } | |
1415 | |
1416 if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) { | |
1417 struct sockaddr_in sin; | |
1418 int sin_len = sizeof(sin); | |
1419 | |
1420 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); | |
1421 | |
1422 if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) { | |
1423 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); | |
1424 l.local_ip = sin.sin_addr.s_addr; | |
1425 } else { | |
1426 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); | |
1427 l.local_ip = 0; | |
1428 } | |
1429 } else | |
1430 l.local_ip = gg_dcc_ip; | |
1431 | |
1432 l.uin = gg_fix32(sess->uin); | |
1433 l.hash = gg_fix32(hash); | |
1434 l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); | |
1435 l.version = gg_fix32(sess->protocol_version); | |
1436 l.local_port = gg_fix16(gg_dcc_port); | |
1437 l.image_size = sess->image_size; | |
1438 | |
1439 if (sess->external_addr && sess->external_port > 1023) { | |
1440 l.external_ip = sess->external_addr; | |
1441 l.external_port = gg_fix16(sess->external_port); | |
1442 } | |
1443 | |
1444 gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n"); | |
1445 ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL); | |
1446 | |
1447 free(sess->initial_descr); | |
1448 sess->initial_descr = NULL; | |
1449 | |
1450 if (ret == -1) { | |
1451 gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); | |
1452 errno2 = errno; | |
1453 close(sess->fd); | |
1454 errno = errno2; | |
1455 sess->fd = -1; | |
1456 e->type = GG_EVENT_CONN_FAILED; | |
1457 e->event.failure = GG_FAILURE_WRITING; | |
1458 sess->state = GG_STATE_IDLE; | |
1459 break; | |
1460 } | |
1461 | |
1462 sess->state = GG_STATE_READING_REPLY; | |
1463 | |
1464 break; | |
1465 } | |
1466 | |
1467 case GG_STATE_READING_REPLY: | |
1468 { | |
1469 struct gg_header *h; | |
1470 | |
1471 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); | |
1472 | |
1473 if (!(h = gg_recv_packet(sess))) { | |
1474 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); | |
1475 e->type = GG_EVENT_CONN_FAILED; | |
1476 e->event.failure = GG_FAILURE_READING; | |
1477 sess->state = GG_STATE_IDLE; | |
1478 errno2 = errno; | |
1479 close(sess->fd); | |
1480 errno = errno2; | |
1481 sess->fd = -1; | |
1482 break; | |
1483 } | |
1484 | |
1485 if (h->type == GG_LOGIN_OK) { | |
1486 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); | |
1487 e->type = GG_EVENT_CONN_SUCCESS; | |
1488 sess->state = GG_STATE_CONNECTED; | |
1489 sess->timeout = -1; | |
1490 sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL; | |
1491 free(h); | |
1492 break; | |
1493 } | |
1494 | |
1495 if (h->type == GG_LOGIN_FAILED) { | |
1496 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); | |
1497 e->event.failure = GG_FAILURE_PASSWORD; | |
1498 errno = EACCES; | |
1499 } else if (h->type == GG_NEED_EMAIL) { | |
1500 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() email change needed\n"); | |
1501 e->event.failure = GG_FAILURE_NEED_EMAIL; | |
1502 errno = EACCES; | |
1503 } else { | |
1504 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); | |
1505 e->event.failure = GG_FAILURE_INVALID; | |
1506 errno = EINVAL; | |
1507 } | |
1508 | |
1509 e->type = GG_EVENT_CONN_FAILED; | |
1510 sess->state = GG_STATE_IDLE; | |
1511 errno2 = errno; | |
1512 close(sess->fd); | |
1513 errno = errno2; | |
1514 sess->fd = -1; | |
1515 free(h); | |
1516 | |
1517 break; | |
1518 } | |
1519 | |
1520 case GG_STATE_CONNECTED: | |
1521 { | |
1522 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); | |
1523 | |
1524 sess->last_event = time(NULL); | |
1525 | |
1526 if ((res = gg_watch_fd_connected(sess, e)) == -1) { | |
1527 | |
1528 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); | |
1529 | |
1530 if (errno == EAGAIN) { | |
1531 e->type = GG_EVENT_NONE; | |
1532 res = 0; | |
1533 } else | |
1534 res = -1; | |
1535 } | |
1536 break; | |
1537 } | |
1538 } | |
1539 | |
1540 done: | |
1541 if (res == -1) { | |
1542 free(e); | |
1543 e = NULL; | |
1544 } | |
1545 | |
1546 return e; | |
1547 | |
1548 fail_connecting: | |
1549 if (sess->fd != -1) { | |
1550 errno2 = errno; | |
1551 close(sess->fd); | |
1552 errno = errno2; | |
1553 sess->fd = -1; | |
1554 } | |
1555 e->type = GG_EVENT_CONN_FAILED; | |
1556 e->event.failure = GG_FAILURE_CONNECTING; | |
1557 sess->state = GG_STATE_IDLE; | |
1558 goto done; | |
1559 | |
1560 fail_resolving: | |
1561 e->type = GG_EVENT_CONN_FAILED; | |
1562 e->event.failure = GG_FAILURE_RESOLVING; | |
1563 sess->state = GG_STATE_IDLE; | |
1564 goto done; | |
1565 } | |
1566 | |
1567 /* | |
1568 * Local variables: | |
1569 * c-indentation-style: k&r | |
1570 * c-basic-offset: 8 | |
1571 * indent-tabs-mode: notnil | |
1572 * End: | |
1573 * | |
1574 * vim: shiftwidth=8: | |
1575 */ |