Mercurial > pidgin.yaz
diff libpurple/protocols/gg/lib/events.c @ 29938:6359fde67f4c
Update our internal libgadu to 1.9.0-rc2. This does not yet build on Windows.
Refs #10542. The Windows build errors are the only reason this isn't on
`im.pidgin.pidgin` already.
author | John Bailey <rekkanoryo@rekkanoryo.org> |
---|---|
date | Sun, 21 Feb 2010 16:52:42 +0000 |
parents | 259bbfb423d4 |
children | db6735e579f8 |
line wrap: on
line diff
--- a/libpurple/protocols/gg/lib/events.c Sun Feb 21 00:11:56 2010 +0000 +++ b/libpurple/protocols/gg/lib/events.c Sun Feb 21 16:52:42 2010 +0000 @@ -1,9 +1,10 @@ -/* $Id: events.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: events.c 855 2009-10-12 21:42:51Z wojtekka $ */ /* - * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. WoĽny <speedy@ziew.org> - * Arkadiusz Mi¶kiewicz <arekm@pld-linux.org> + * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. WoĹşny <speedy@ziew.org> + * Arkadiusz MiĹ›kiewicz <arekm@pld-linux.org> + * Adam Wysocki <gophi@ekg.chmurka.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -16,71 +17,74 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ -#include "libgadu.h" +/** + * \file events.c + * + * \brief ObsĹ‚uga zdarzeĹ„ + */ #include <sys/types.h> -#ifndef _WIN32 -#include <sys/wait.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> -#endif - -#include "libgadu-config.h" + +#include "compat.h" +#include "libgadu.h" +#include "protocol.h" +#include "libgadu-internal.h" #include <errno.h> -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include <pthread.h> -#endif #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <unistd.h> -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#include <ctype.h> +#ifdef GG_CONFIG_HAVE_OPENSSL # include <openssl/err.h> # include <openssl/x509.h> #endif -#include "compat.h" - -/* - * gg_event_free() +/** + * Zwalnia pamięć zajmowanÄ… przez informacjÄ™ o zdarzeniu. * - * zwalnia pamięć zajmowan± przez informację o zdarzeniu. + * FunkcjÄ™ naleĹĽy wywoĹ‚ywać za kaĹĽdym razem gdy funkcja biblioteki zwrĂłci + * strukturÄ™ \c gg_event. * - * - e - wskaĽnik do informacji o zdarzeniu + * \param e Struktura zdarzenia + * + * \ingroup events */ void gg_event_free(struct gg_event *e) { gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e); - + if (!e) return; - + switch (e->type) { case GG_EVENT_MSG: free(e->event.msg.message); free(e->event.msg.formats); free(e->event.msg.recipients); break; - + case GG_EVENT_NOTIFY: free(e->event.notify); break; - + case GG_EVENT_NOTIFY60: { int i; for (i = 0; e->event.notify60[i].uin; i++) free(e->event.notify60[i].descr); - + free(e->event.notify60); break; @@ -89,7 +93,7 @@ case GG_EVENT_STATUS60: free(e->event.status60.descr); break; - + case GG_EVENT_STATUS: free(e->event.status.descr); break; @@ -112,26 +116,30 @@ case GG_EVENT_USERLIST: free(e->event.userlist.reply); break; - + case GG_EVENT_IMAGE_REPLY: free(e->event.image_reply.filename); free(e->event.image_reply.image); break; + + case GG_EVENT_XML_EVENT: + free(e->event.xml_event.data); + break; } free(e); } -/* - * gg_image_queue_remove() - * - * usuwa z kolejki dany wpis. +/** \cond internal */ + +/** + * \internal Usuwa obrazek z kolejki do wysĹ‚ania. * - * - s - sesja - * - q - kolejka - * - freeq - czy zwolnić kolejkę + * \param s Struktura sesji + * \param q Struktura obrazka + * \param freeq Flaga zwolnienia elementu kolejki * - * 0/-1 + * \return 0 jeĹ›li siÄ™ powiodĹ‚o, -1 jeĹ›li wystÄ…piĹ‚ bĹ‚Ä…d */ int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) { @@ -162,13 +170,14 @@ return 0; } -/* - * gg_image_queue_parse() // funkcja wewnętrzna +/** + * \internal Analizuje przychodzÄ…cy pakiet z obrazkiem. * - * parsuje przychodz±cy pakiet z obrazkiem. - * - * - e - opis zdarzenia - * - + * \param e Struktura zdarzenia + * \param p Bufor z danymi + * \param len DĹ‚ugość bufora + * \param sess Struktura sesji + * \param sender Numer nadawcy */ static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender) { @@ -180,8 +189,8 @@ return; } - /* znajdĽ dany obrazek w kolejce danej sesji */ - + /* znajdĹş dany obrazek w kolejce danej sesji */ + for (qq = sess->images, q = NULL; qq; qq = qq->next) { if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) { q = qq; @@ -190,34 +199,23 @@ } if (!q) { - gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32); return; } if (p[0] == 0x05) { - unsigned int i, ok = 0; - q->done = 0; len -= sizeof(struct gg_msg_image_reply); p += sizeof(struct gg_msg_image_reply); - /* sprawdĽ, czy mamy tekst zakończony \0 */ - - for (i = 0; i < len; i++) { - if (!p[i]) { - ok = 1; - break; - } - } - - if (!ok) { - gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender); + if (memchr(p, 0, len) == NULL) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender); return; } if (!(q->filename = strdup(p))) { - gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n"); return; } @@ -230,11 +228,11 @@ if (q->done + len > q->size) len = q->size - q->done; - + memcpy(q->image + q->done, p, len); q->done += len; - /* je¶li skończono odbierać obrazek, wygeneruj zdarzenie */ + /* jeĹ›li skoĹ„czono odbierać obrazek, wygeneruj zdarzenie */ if (q->done >= q->size) { e->type = GG_EVENT_IMAGE_REPLY; @@ -250,78 +248,55 @@ } } -/* - * gg_handle_recv_msg() // funkcja wewnętrzna - * - * obsługuje pakiet z przychodz±c± wiadomo¶ci±, rozbijaj±c go na dodatkowe - * struktury (konferencje, kolorki) w razie potrzeby. +/** + * \internal Analizuje informacje rozszerzone wiadomoĹ›ci. + * + * \param sess Struktura sesji. + * \param e Struktura zdarzenia. + * \param sender Numer nadawcy. + * \param p WskaĹşnik na dane rozszerzone. + * \param packet_end WskaĹşnik na koniec pakietu. * - * - h - nagłówek pakietu - * - e - opis zdarzenia - * - * 0, -1. + * \return 0 jeĹ›li siÄ™ powiodĹ‚o, -1 jeĹ›li wiadomość obsĹ‚uĹĽono i wynik ma + * zostać przekazany aplikacji, -2 jeĹ›li wystÄ…piĹ‚ bĹ‚Ä…d ogĂłlny, -3 jeĹ›li + * wiadomość jest niepoprawna. */ -static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess) +static int gg_handle_recv_msg_options(struct gg_session *sess, struct gg_event *e, uin_t sender, char *p, char *packet_end) { - struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header)); - char *p, *packet_end = (char*) r + h->length; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e); - - if (!r->seq && !r->msgclass) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n"); - e->type = GG_EVENT_NONE; - return 0; - } - - for (p = (char*) r + sizeof(*r); *p; p++) { - if (*p == 0x02 && p == packet_end - 1) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n"); - break; - } - if (p >= packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n"); - goto malformed; - } - } - - p++; - - /* przeanalizuj dodatkowe opcje */ while (p < packet_end) { switch (*p) { case 0x01: /* konferencja */ { struct gg_msg_recipients *m = (void*) p; uint32_t i, count; - + p += sizeof(*m); - + if (p > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n"); goto malformed; } count = gg_fix32(m->count); if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n"); goto malformed; } - + if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n"); goto fail; } - + for (i = 0; i < count; i++, p += sizeof(uint32_t)) { uint32_t u; memcpy(&u, p, sizeof(uint32_t)); e->event.msg.recipients[i] = gg_fix32(u); } - + e->event.msg.recipients_count = count; - + break; } @@ -329,9 +304,9 @@ { uint16_t len; char *buf; - + if (p + 3 > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n"); goto malformed; } @@ -339,18 +314,18 @@ len = gg_fix16(len); if (!(buf = malloc(len))) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n"); goto fail; } p += 3; if (p + len > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); free(buf); goto malformed; } - + memcpy(buf, p, len); e->event.msg.formats = buf; @@ -366,17 +341,17 @@ struct gg_msg_image_request *i = (void*) p; if (p + sizeof(*i) > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); goto malformed; } - e->event.image_request.sender = gg_fix32(r->sender); + e->event.image_request.sender = sender; e->event.image_request.size = gg_fix32(i->size); e->event.image_request.crc32 = gg_fix32(i->crc32); e->type = GG_EVENT_IMAGE_REQUEST; - return 0; + goto handled; } case 0x05: /* image_reply */ @@ -386,75 +361,347 @@ if (p + sizeof(struct gg_msg_image_reply) == packet_end) { - /* pusta odpowiedĽ - klient po drugiej stronie nie ma ż±danego obrazka */ + /* pusta odpowiedĹş - klient po drugiej stronie nie ma ĹĽÄ…danego obrazka */ e->type = GG_EVENT_IMAGE_REPLY; - e->event.image_reply.sender = gg_fix32(r->sender); + e->event.image_reply.sender = sender; e->event.image_reply.size = 0; e->event.image_reply.crc32 = gg_fix32(rep->crc32); e->event.image_reply.filename = NULL; e->event.image_reply.image = NULL; - return 0; + goto handled; } else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n"); goto malformed; } rep->size = gg_fix32(rep->size); rep->crc32 = gg_fix32(rep->crc32); - gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, gg_fix32(r->sender)); - - return 0; + gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, sender); + + goto handled; } default: { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p); p = packet_end; } } } + return 0; + +handled: + return -1; + +fail: + return -2; + +malformed: + return -3; +} + +/** + * \internal Analizuje przychodzÄ…cy pakiet z wiadomoĹ›ciÄ…. + * + * Rozbija pakiet na poszczegĂłlne skĹ‚adniki -- tekst, informacje + * o konferencjach, formatowani itd. + * + * \param h WskaĹşnik do odebranego pakietu + * \param e Struktura zdarzenia + * \param sess Struktura sesji + * + * \return 0 jeĹ›li siÄ™ powiodĹ‚o, -1 w przypadku bĹ‚Ä™du + */ +static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess) +{ + struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header)); + char *p, *packet_end = (char*) r + h->length; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e); + + if (!r->seq && !r->msgclass) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n"); + e->type = GG_EVENT_NONE; + return 0; + } + + /* znajdĹş \0 */ + for (p = (char*) r + sizeof(*r); ; p++) { + if (p >= packet_end) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n"); + goto malformed; + } + + if (*p == 0x02 && p == packet_end - 1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n"); + break; + } + + if (!*p) + break; + } + + p++; + + switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), p, packet_end)) { + case -1: // handled + return 0; + + case -2: // failed + goto fail; + + case -3: // malformed + goto malformed; + } + e->type = GG_EVENT_MSG; e->event.msg.msgclass = gg_fix32(r->msgclass); e->event.msg.sender = gg_fix32(r->sender); e->event.msg.time = gg_fix32(r->time); - e->event.msg.message = (unsigned char *)strdup((char*) r + sizeof(*r)); + e->event.msg.seq = gg_fix32(r->seq); + e->event.msg.message = (unsigned char*) strdup((char*) r + sizeof(*r)); return 0; malformed: e->type = GG_EVENT_NONE; - + free(e->event.msg.message); free(e->event.msg.recipients); free(e->event.msg.formats); return 0; fail: + free(e->event.msg.message); free(e->event.msg.recipients); free(e->event.msg.formats); return -1; } -/* - * gg_watch_fd_connected() // funkcja wewnętrzna +/** + * \internal Zamienia tekst w formacie HTML na czysty tekst. + * + * \param dst Bufor wynikowy (moĹĽe być \c NULL) + * \param html Tekst ĹşrĂłdĹ‚owy + * + * \note Dokleja \c \\0 na koĹ„cu bufora wynikowego. * - * patrzy na gniazdo, odbiera pakiet i wypełnia strukturę zdarzenia. + * \return DĹ‚ugość tekstu wynikowego bez \c \\0 (nawet jeĹ›li \c dst to \c NULL). + */ +static int gg_convert_from_html(char *dst, const char *html) +{ + const char *src, *entity, *tag; + int len, in_tag, in_entity; + + len = 0; + in_tag = 0; + tag = NULL; + in_entity = 0; + entity = NULL; + + for (src = html; *src != 0; src++) { + if (*src == '<') { + tag = src; + in_tag = 1; + continue; + } + + if (in_tag && (*src == '>')) { + if (strncmp(tag, "<br", 3) == 0) { + if (dst != NULL) + dst[len] = '\n'; + len++; + } + in_tag = 0; + continue; + } + + if (in_tag) + continue; + + if (*src == '&') { + in_entity = 1; + entity = src; + continue; + } + + if (in_entity && *src == ';') { + in_entity = 0; + if (dst != NULL) { + if (strncmp(entity, "<", 4) == 0) + dst[len] = '<'; + else if (strncmp(entity, ">", 4) == 0) + dst[len] = '>'; + else if (strncmp(entity, """, 6) == 0) + dst[len] = '"'; + else if (strncmp(entity, "'", 6) == 0) + dst[len] = '\''; + else if (strncmp(entity, "&", 5) == 0) + dst[len] = '&'; + else + dst[len] = '?'; + } + len++; + continue; + } + + if (in_entity && !(isalnum(*src) || *src == '#')) + in_entity = 0; + + if (in_entity) + continue; + + if (dst != NULL) + dst[len] = *src; + + len++; + } + + if (dst != NULL) + dst[len] = 0; + + return len; +} + +/** + * \internal Analizuje przychodzÄ…cy pakiet z wiadomoĹ›ciÄ… protokoĹ‚u Gadu-Gadu 8.0. + * + * Rozbija pakiet na poszczegĂłlne skĹ‚adniki -- tekst, informacje + * o konferencjach, formatowani itd. + * + * \param h WskaĹşnik do odebranego pakietu + * \param e Struktura zdarzenia + * \param sess Struktura sesji * - * - sess - struktura opisuj±ca sesję - * - e - opis zdarzenia + * \return 0 jeĹ›li siÄ™ powiodĹ‚o, -1 w przypadku bĹ‚Ä™du + */ +static int gg_handle_recv_msg80(struct gg_header *h, struct gg_event *e, struct gg_session *sess) +{ + char *packet = (char*) h + sizeof(struct gg_header); + struct gg_recv_msg80 *r = (struct gg_recv_msg80*) packet; + uint32_t offset_plain; + uint32_t offset_attr; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg80(%p, %p);\n", h, e); + + if (!r->seq && !r->msgclass) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() oops, silently ignoring the bait\n"); + goto malformed; + } + + offset_plain = gg_fix32(r->offset_plain); + offset_attr = gg_fix32(r->offset_attr); + + if (offset_plain < sizeof(struct gg_recv_msg80) || offset_plain >= h->length) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (0)\n"); + goto malformed; + } + + if (offset_attr < sizeof(struct gg_recv_msg80) || offset_attr > h->length) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, attr out of bounds (1)\n"); + offset_attr = 0; /* nie parsuj attr. */ + /* goto ignore; */ + } + + /* Normalna sytuacja, wiÄ™c nie podpada pod powyĹĽszy warunek. */ + if (offset_attr == h->length) + offset_attr = 0; + + if (memchr(packet + offset_plain, 0, h->length - offset_plain) == NULL) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (2)\n"); + goto malformed; + } + + if (offset_plain > sizeof(struct gg_recv_msg80) && memchr(packet + sizeof(struct gg_recv_msg80), 0, offset_plain - sizeof(struct gg_recv_msg80)) == NULL) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (3)\n"); + goto malformed; + } + + e->type = GG_EVENT_MSG; + e->event.msg.msgclass = gg_fix32(r->msgclass); + e->event.msg.sender = gg_fix32(r->sender); + e->event.msg.time = gg_fix32(r->time); + e->event.msg.seq = gg_fix32(r->seq); + + if (sess->encoding == GG_ENCODING_CP1250) { + e->event.msg.message = (unsigned char*) strdup(packet + offset_plain); + } else { + if (offset_plain > sizeof(struct gg_recv_msg80)) { + int len; + + len = gg_convert_from_html(NULL, packet + sizeof(struct gg_recv_msg80)); + + e->event.msg.message = malloc(len + 1); + + if (e->event.msg.message == NULL) + goto fail; + + gg_convert_from_html((char*) e->event.msg.message, packet + sizeof(struct gg_recv_msg80)); + } else { + e->event.msg.message = (unsigned char*) gg_cp_to_utf8(packet + offset_plain); + } + } + + if (offset_plain > sizeof(struct gg_recv_msg80)) { + if (sess->encoding == GG_ENCODING_UTF8) + e->event.msg.xhtml_message = strdup(packet + sizeof(struct gg_recv_msg80)); + else + e->event.msg.xhtml_message = gg_utf8_to_cp(packet + sizeof(struct gg_recv_msg80)); + } else { + e->event.msg.xhtml_message = NULL; + } + + if (offset_attr != 0) { + switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + h->length)) { + case -1: // handled + return 0; + + case -2: // failed + goto fail; + + case -3: // malformed + goto malformed; + } + } + + return 0; + +fail: + free(e->event.msg.message); + free(e->event.msg.xhtml_message); + free(e->event.msg.recipients); + free(e->event.msg.formats); + return -1; + +malformed: + e->type = GG_EVENT_NONE; + free(e->event.msg.message); + free(e->event.msg.xhtml_message); + free(e->event.msg.recipients); + free(e->event.msg.formats); + return 0; +} + +/** + * \internal Odbiera pakiet od serwera. * - * 0, -1. + * Analizuje pakiet i wypeĹ‚nia strukturÄ™ zdarzenia. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * + * \return 0 jeĹ›li siÄ™ powiodĹ‚o, -1 jeĹ›li wystÄ…piĹ‚ bĹ‚Ä…d */ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e) { struct gg_header *h = NULL; char *p; - gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e); if (!sess) { errno = EFAULT; @@ -462,76 +709,86 @@ } if (!(h = gg_recv_packet(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail; } p = (char*) h + sizeof(struct gg_header); - + switch (h->type) { case GG_RECV_MSG: { if (h->length >= sizeof(struct gg_recv_msg)) if (gg_handle_recv_msg(h, e, sess)) goto fail; - + break; } + case GG_RECV_MSG80: + { + if (h->length >= sizeof(struct gg_recv_msg80)) + if (gg_handle_recv_msg80(h, e, sess)) + goto fail; + + break; + } + + case GG_NOTIFY_REPLY: { struct gg_notify_reply *n = (void*) p; unsigned int count, i; char *tmp; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); if (h->length < sizeof(*n)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n"); errno = EINVAL; goto fail; } 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) { e->type = GG_EVENT_NOTIFY_DESCR; - + if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } e->event.notify_descr.notify[1].uin = 0; memcpy(e->event.notify_descr.notify, p, sizeof(*n)); e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin); e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status); - e->event.notify_descr.notify[0].remote_ip = e->event.notify_descr.notify[0].remote_ip; e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port); + e->event.notify_descr.notify[0].version = gg_fix32(e->event.notify_descr.notify[0].version); count = h->length - sizeof(*n); if (!(tmp = malloc(count + 1))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } memcpy(tmp, p + sizeof(*n), count); tmp[count] = 0; e->event.notify_descr.descr = tmp; - + } else { e->type = GG_EVENT_NOTIFY; - + if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } - + memcpy(e->event.notify, p, h->length); count = h->length / sizeof(*n); e->event.notify[count].uin = 0; - + for (i = 0; i < count; i++) { e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin); e->event.notify[i].status = gg_fix32(e->event.notify[i].status); - e->event.notify[i].remote_ip = e->event.notify[i].remote_ip; e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port); + e->event.notify[i].version = gg_fix32(e->event.notify[i].version); } } @@ -542,7 +799,7 @@ { struct gg_status *s = (void*) p; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); if (h->length >= sizeof(*s)) { e->type = GG_EVENT_STATUS; @@ -564,23 +821,176 @@ break; } - case GG_NOTIFY_REPLY60: + case GG_NOTIFY_REPLY77: + case GG_NOTIFY_REPLY80BETA: { - struct gg_notify_reply60 *n = (void*) p; + struct gg_notify_reply77 *n = (void*) p; unsigned int length = h->length, i = 0; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); e->type = GG_EVENT_NOTIFY60; e->event.notify60 = malloc(sizeof(*e->event.notify60)); if (!e->event.notify60) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } e->event.notify60[0].uin = 0; - + + while (length >= sizeof(struct gg_notify_reply77)) { + uin_t uin = gg_fix32(n->uin); + char *tmp; + + e->event.notify60[i].uin = uin & 0x00ffffff; + e->event.notify60[i].status = n->status; + e->event.notify60[i].remote_ip = n->remote_ip; + e->event.notify60[i].remote_port = gg_fix16(n->remote_port); + e->event.notify60[i].version = n->version; + e->event.notify60[i].image_size = n->image_size; + e->event.notify60[i].descr = NULL; + e->event.notify60[i].time = 0; + + if (uin & 0x40000000) + e->event.notify60[i].version |= GG_HAS_AUDIO_MASK; + if (uin & 0x20000000) + e->event.notify60[i].version |= GG_HAS_AUDIO7_MASK; + if (uin & 0x08000000) + e->event.notify60[i].version |= GG_ERA_OMNIX_MASK; + + if (GG_S_D(n->status)) { + unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply77)); + + if (sizeof(struct gg_notify_reply77) + descr_len <= length) { + char *descr; + + if (!(descr = malloc(descr_len + 1))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + memcpy(descr, (char*) n + sizeof(struct gg_notify_reply77) + 1, descr_len); + descr[descr_len] = 0; + + if (h->type == GG_NOTIFY_REPLY80BETA && sess->encoding != GG_ENCODING_UTF8) { + char *cp_descr = gg_utf8_to_cp(descr); + + if (!cp_descr) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(descr); + goto fail; + } + + free(descr); + descr = cp_descr; + } + + e->event.notify60[i].descr = descr; + + /* XXX czas */ + + length -= sizeof(struct gg_notify_reply77) + descr_len + 1; + n = (void*) ((char*) n + sizeof(struct gg_notify_reply77) + descr_len + 1); + } else { + length = 0; + } + + } else { + length -= sizeof(struct gg_notify_reply77); + n = (void*) ((char*) n + sizeof(struct gg_notify_reply77)); + } + + if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(e->event.notify60); + goto fail; + } + + e->event.notify60 = (void*) tmp; + e->event.notify60[++i].uin = 0; + } + + break; + } + + case GG_STATUS77: + case GG_STATUS80BETA: + { + struct gg_status77 *s = (void*) p; + uint32_t uin; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + + if (h->length < sizeof(*s)) + break; + + uin = gg_fix32(s->uin); + + e->type = GG_EVENT_STATUS60; + e->event.status60.uin = uin & 0x00ffffff; + e->event.status60.status = s->status; + e->event.status60.remote_ip = s->remote_ip; + e->event.status60.remote_port = gg_fix16(s->remote_port); + e->event.status60.version = s->version; + e->event.status60.image_size = s->image_size; + e->event.status60.descr = NULL; + e->event.status60.time = 0; + + if (uin & 0x40000000) + e->event.status60.version |= GG_HAS_AUDIO_MASK; + if (uin & 0x20000000) + e->event.status60.version |= GG_HAS_AUDIO7_MASK; + if (uin & 0x08000000) + e->event.status60.version |= GG_ERA_OMNIX_MASK; + + if (h->length > sizeof(*s)) { + int len = h->length - sizeof(*s); + char *buf = malloc(len + 1); + + /* XXX, jesli malloc() sie nie uda to robic tak samo jak przy GG_NOTIFY_REPLY* ? + * - goto fail; (?) + */ + if (buf) { + memcpy(buf, (char*) p + sizeof(*s), len); + buf[len] = 0; + + if (h->type == GG_STATUS80BETA && sess->encoding != GG_ENCODING_UTF8) { + char *cp_buf = gg_utf8_to_cp(buf); + free(buf); + buf = cp_buf; + } + } + + e->event.status60.descr = buf; + + if (len > 4 && p[h->length - 5] == 0) { + uint32_t t; + memcpy(&t, p + h->length - 4, sizeof(uint32_t)); + e->event.status60.time = gg_fix32(t); + } + } + + break; + } + + case GG_NOTIFY_REPLY60: + { + struct gg_notify_reply60 *n = (void*) p; + unsigned int length = h->length, i = 0; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + + e->type = GG_EVENT_NOTIFY60; + e->event.notify60 = malloc(sizeof(*e->event.notify60)); + + if (!e->event.notify60) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + e->event.notify60[0].uin = 0; + while (length >= sizeof(struct gg_notify_reply60)) { uin_t uin = gg_fix32(n->uin); char *tmp; @@ -602,9 +1012,9 @@ if (GG_S_D(n->status)) { unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60)); - if (descr_len < length) { + if (sizeof(struct gg_notify_reply60) + descr_len <= length) { if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } @@ -612,17 +1022,20 @@ e->event.notify60[i].descr[descr_len] = 0; /* XXX czas */ + + length -= sizeof(struct gg_notify_reply60) + descr_len + 1; + n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1); + } else { + length = 0; } - - length -= sizeof(struct gg_notify_reply60) + descr_len + 1; - n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1); + } else { length -= sizeof(struct gg_notify_reply60); n = (void*) ((char*) n + sizeof(struct gg_notify_reply60)); } if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); free(e->event.notify60); goto fail; } @@ -633,13 +1046,13 @@ break; } - + case GG_STATUS60: { struct gg_status60 *s = (void*) p; uint32_t uin; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); if (h->length < sizeof(*s)) break; @@ -682,11 +1095,132 @@ break; } + case GG_STATUS80: + { + struct gg_notify_reply80 *s = (void*) p; + uint32_t descr_len; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + + if (h->length < sizeof(*s)) + break; + + e->type = GG_EVENT_STATUS60; + e->event.status60.uin = gg_fix32(s->uin); + e->event.status60.status = gg_fix32(s->status); + e->event.status60.remote_ip = s->remote_ip; + e->event.status60.remote_port = gg_fix16(s->remote_port); + e->event.status60.image_size = s->image_size; + e->event.status60.descr = NULL; + e->event.status60.version = 0x00; /* not-supported */ + e->event.status60.time = 0; /* not-supported */ + + descr_len = gg_fix32(s->descr_len); + + if (descr_len > 0 && h->length-sizeof(*s) >= descr_len) { + char *buf = malloc(descr_len + 1); + + if (buf) { + memcpy(buf, (char*) p + sizeof(*s), descr_len); + buf[descr_len] = 0; + + if (sess->encoding != GG_ENCODING_UTF8) { + char *cp_buf = gg_utf8_to_cp(buf); + free(buf); + buf = cp_buf; + } + } + + e->event.status60.descr = buf; + } + break; + } + + case GG_NOTIFY_REPLY80: + { + struct gg_notify_reply80 *n = (void*) p; + unsigned int length = h->length, i = 0; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + + e->type = GG_EVENT_NOTIFY60; + e->event.notify60 = malloc(sizeof(*e->event.notify60)); + + if (!e->event.notify60) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + e->event.notify60[0].uin = 0; + + while (length >= sizeof(struct gg_notify_reply80)) { + uint32_t descr_len; + char *tmp; + + e->event.notify60[i].uin = gg_fix32(n->uin); + e->event.notify60[i].status = gg_fix32(n->status); + e->event.notify60[i].remote_ip = n->remote_ip; + e->event.notify60[i].remote_port= gg_fix16(n->remote_port); + e->event.notify60[i].image_size = n->image_size; + e->event.notify60[i].descr = NULL; + e->event.notify60[i].version = 0x00; /* not-supported */ + e->event.notify60[i].time = 0; /* not-supported */ + + descr_len = gg_fix32(n->descr_len); + + length -= sizeof(struct gg_notify_reply80); + n = (void*) ((char*) n + sizeof(struct gg_notify_reply80)); + + if (descr_len) { + if (length >= descr_len) { + /* XXX, GG_S_D(n->status) */ + char *descr; + + if (!(descr = malloc(descr_len + 1))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + memcpy(descr, n, descr_len); + descr[descr_len] = 0; + + if (sess->encoding != GG_ENCODING_UTF8) { + char *cp_descr = gg_utf8_to_cp(descr); + + if (!cp_descr) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(descr); + goto fail; + } + + free(descr); + descr = cp_descr; + } + e->event.notify60[i].descr = descr; + + length -= descr_len; + n = (void*) ((char*) n + descr_len); + } else + length = 0; + } + + if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(e->event.notify60); + goto fail; + } + + e->event.notify60 = (void*) tmp; + e->event.notify60[++i].uin = 0; + } + break; + } + case GG_SEND_MSG_ACK: { struct gg_send_msg_ack *s = (void*) p; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n"); if (h->length < sizeof(*s)) break; @@ -699,9 +1233,9 @@ break; } - case GG_PONG: + case GG_PONG: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n"); e->type = GG_EVENT_PONG; sess->last_pong = time(NULL); @@ -711,27 +1245,47 @@ case GG_DISCONNECTING: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n"); e->type = GG_EVENT_DISCONNECT; break; } + case GG_DISCONNECT_ACK: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection acknowledge\n"); + e->type = GG_EVENT_DISCONNECT_ACK; + break; + } + + case GG_XML_EVENT: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n"); + e->type = GG_EVENT_XML_EVENT; + if (!(e->event.xml_event.data = (char *) malloc(h->length + 1))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for XML event data\n"); + goto fail; + } + memcpy(e->event.xml_event.data, p, h->length); + e->event.xml_event.data[h->length] = 0; + break; + } + case GG_PUBDIR50_REPLY: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n"); - if (gg_pubdir50_handle_reply(e, p, h->length) == -1) + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n"); + if (gg_pubdir50_handle_reply_sess(sess, e, p, h->length) == -1) goto fail; break; } case GG_USERLIST_REPLY: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n"); if (h->length < 1) break; - /* je¶li odpowiedĽ na eksport, wywołaj zdarzenie tylko + /* jeĹ›li odpowiedĹş na eksport, wywoĹ‚aj zdarzenie tylko * gdy otrzymano wszystkie odpowiedzi */ if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) { if (--sess->userlist_blocks) @@ -743,11 +1297,11 @@ if (h->length > 1) { char *tmp; unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0; - - gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len); - + + gg_debug_session(sess, GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len); + if (!(tmp = realloc(sess->userlist_reply, len + h->length))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n"); free(sess->userlist_reply); sess->userlist_reply = NULL; goto fail; @@ -769,10 +1323,75 @@ break; } + case GG_DCC7_ID_REPLY: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n"); + + if (h->length < sizeof(struct gg_dcc7_id_reply)) + break; + + if (gg_dcc7_handle_id(sess, e, p, h->length) == -1) + goto fail; + + break; + } + + case GG_DCC7_ACCEPT: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n"); + + if (h->length < sizeof(struct gg_dcc7_accept)) + break; + + if (gg_dcc7_handle_accept(sess, e, p, h->length) == -1) + goto fail; + + break; + } + + case GG_DCC7_NEW: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n"); + + if (h->length < sizeof(struct gg_dcc7_new)) + break; + + if (gg_dcc7_handle_new(sess, e, p, h->length) == -1) + goto fail; + + break; + } + + case GG_DCC7_REJECT: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n"); + + if (h->length < sizeof(struct gg_dcc7_reject)) + break; + + if (gg_dcc7_handle_reject(sess, e, p, h->length) == -1) + goto fail; + + break; + } + + case GG_DCC7_INFO: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n"); + + if (h->length < sizeof(struct gg_dcc7_info)) + break; + + if (gg_dcc7_handle_info(sess, e, p, h->length) == -1) + goto fail; + + break; + } + default: - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type); } - + free(h); return 0; @@ -781,19 +1400,20 @@ return -1; } -/* - * gg_watch_fd() - * - * funkcja, któr± należy wywołać, gdy co¶ się stanie z obserwowanym - * deskryptorem. zwraca klientowi informację o tym, co się dzieje. +/** \endcond */ + +/** + * Funkcja wywoĹ‚ywana po zaobserwowaniu zmian na deskryptorze sesji. * - * - sess - opis sesji + * Funkcja zwraca strukturÄ™ zdarzenia \c gg_event. JeĹ›li rodzaj zdarzenia + * to \c GG_EVENT_NONE, nie wydarzyĹ‚o siÄ™ jeszcze nic wartego odnotowania. + * StrukturÄ™ zdarzenia naleĹĽy zwolnić funkcja \c gg_event_free(). * - * wskaĽnik do struktury gg_event, któr± trzeba zwolnić póĽniej - * za pomoc± gg_event_free(). jesli rodzaj zdarzenia jest równy - * GG_EVENT_NONE, należy je zignorować. je¶li zwróciło NULL, - * stało się co¶ niedobrego -- albo zabrakło pamięci albo zerwało - * poł±czenie. + * \param sess Struktura sesji + * + * \return Struktura zdarzenia lub \c NULL jeĹ›li wystÄ…piĹ‚ bĹ‚Ä…d + * + * \ingroup events */ struct gg_event *gg_watch_fd(struct gg_session *sess) { @@ -802,68 +1422,79 @@ int port = 0; int errno2 = 0; - gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); - + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); + if (!sess) { errno = EFAULT; return NULL; } if (!(e = (void*) calloc(1, sizeof(*e)))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); return NULL; } e->type = GG_EVENT_NONE; + if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left); + + res = write(sess->fd, sess->send_buf, sess->send_left); + + if (res == -1 && errno != EAGAIN) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno)); + + if (sess->state == GG_STATE_READING_REPLY) + goto fail_connecting; + else + goto done; + } + + if (res == sess->send_left) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n"); + free(sess->send_buf); + sess->send_buf = NULL; + sess->send_left = 0; + } else if (res > 0) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d bytes of queued data, %d bytes left\n", res, sess->send_left - res); + + memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res); + sess->send_left -= res; + } + } + switch (sess->state) { case GG_STATE_RESOLVING: { struct in_addr addr; int failed = 0; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n"); if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); failed = 1; errno2 = errno; } - + close(sess->fd); sess->fd = -1; -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (sess->resolver) { - pthread_cancel(*((pthread_t*) sess->resolver)); - free(sess->resolver); - sess->resolver = NULL; - } -#elif defined _WIN32 - if (sess->resolver) { - HANDLE h = sess->resolver; - TerminateThread(h, 0); - CloseHandle(h); - sess->resolver = NULL; - } -#else - waitpid(sess->pid, NULL, 0); - sess->pid = -1; -#endif + sess->resolver_cleanup(&sess->resolver, 0); if (failed) { errno = errno2; goto fail_resolving; } - /* je¶li jeste¶my w resolverze i mamy ustawiony port - * proxy, znaczy, że resolvowali¶my proxy. zatem + /* jeĹ›li jesteĹ›my w resolverze i mamy ustawiony port + * proxy, znaczy, ĹĽe resolvowaliĹ›my proxy. zatem * wpiszmy jego adres. */ if (sess->proxy_port) sess->proxy_addr = addr.s_addr; /* zapiszmy sobie adres huba i adres serwera (do - * bezpo¶redniego poł±czenia, je¶li hub leży) + * bezpoĹ›redniego poĹ‚Ä…czenia, jeĹ›li hub leĹĽy) * z resolvera. */ if (sess->proxy_addr && sess->proxy_port) port = sess->proxy_port; @@ -872,21 +1503,27 @@ port = GG_APPMSG_PORT; } - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port); - - /* ł±czymy się albo z hubem, albo z proxy, zależnie - * od tego, co resolvowali¶my. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port); + + /* Ĺ‚Ä…czymy siÄ™ albo z hubem, albo z proxy, zaleĹĽnie + * od tego, co resolvowaliĹ›my. */ if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) { - /* je¶li w trybie asynchronicznym gg_connect() - * zwróci bł±d, nie ma sensu próbować dalej. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); + /* jeĹ›li w trybie asynchronicznym gg_connect() + * zwrĂłci bĹ‚Ä…d, nie ma sensu prĂłbować dalej. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); goto fail_connecting; } - /* je¶li podano serwer i ł±czmy się przez proxy, - * jest to bezpo¶rednie poł±czenie, inaczej jest + /* jeĹ›li podano serwer i Ĺ‚Ä…czmy siÄ™ przez proxy, + * jest to bezpoĹ›rednie poĹ‚Ä…czenie, inaczej jest * do huba. */ - sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB; + + if (sess->proxy_addr && sess->proxy_port && sess->server_addr) { + sess->state = GG_STATE_CONNECTING_GG; + sess->soft_timeout = 1; + } else + sess->state = GG_STATE_CONNECTING_HUB; + sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; @@ -897,43 +1534,26 @@ { char buf[1024], *client, *auth; int res = 0; - socklen_t res_size = sizeof(res); - const char *host, *appmsg; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); - - /* je¶li asynchroniczne, sprawdzamy, czy nie wyst±pił - * przypadkiem jaki¶ bł±d. */ + unsigned int res_size = sizeof(res); + const char *host; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); + + /* jeĹ›li asynchroniczne, sprawdzamy, czy nie wystÄ…piĹ‚ + * przypadkiem jakiĹ› bĹ‚Ä…d. */ if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { - /* no tak, nie udało się poł±czyć z proxy. nawet - * nie próbujemy dalej. */ - if (sess->proxy_addr && sess->proxy_port) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); - goto fail_connecting; - } - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res)); - close(sess->fd); - - if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) { - /* przy asynchronicznych, gg_connect() - * zwraca -1 przy błędach socket(), - * ioctl(), braku routingu itd. dlatego - * nawet nie próbujemy dalej. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); - goto fail_connecting; - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; + if (sess->proxy_addr && sess->proxy_port) + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); + else + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s)\n", res, strerror(res)); + + goto fail_connecting; } - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); goto fail_connecting; } @@ -942,41 +1562,43 @@ else host = ""; -#ifdef __GG_LIBGADU_HAVE_OPENSSL - if (sess->ssl) - appmsg = "appmsg3.asp"; - else + auth = gg_proxy_auth(); + +#ifdef GG_CONFIG_HAVE_OPENSSL + if (sess->ssl) { + snprintf(buf, sizeof(buf) - 1, + "GET %s/appsvc/appmsg3.asp?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n" + "Host: " GG_APPMSG_HOST "\r\n" + "User-Agent: " GG_HTTP_USERAGENT "\r\n" + "Pragma: no-cache\r\n" + "%s" + "\r\n", host, sess->uin, client, sess->last_sysmsg, (auth) ? auth : ""); + } else #endif - appmsg = "appmsg2.asp"; - - auth = gg_proxy_auth(); - - snprintf(buf, sizeof(buf) - 1, - "GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n" - "Host: " GG_APPMSG_HOST "\r\n" - "User-Agent: " GG_HTTP_USERAGENT "\r\n" - "Pragma: no-cache\r\n" - "%s" - "\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : ""); - - if (auth) - free(auth); - + { + snprintf(buf, sizeof(buf) - 1, + "GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n" + "Host: " GG_APPMSG_HOST "\r\n" + "%s" + "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : ""); + } + + free(auth); free(client); - /* zwolnij pamięć po wersji klienta. */ + /* zwolnij pamięć po wersji klienta. */ if (sess->client_version) { free(sess->client_version); sess->client_version = NULL; } - gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); - - /* zapytanie jest krótkie, więc zawsze zmie¶ci się - * do bufora gniazda. je¶li write() zwróci mniej, - * stało się co¶ złego. */ + gg_debug_session(sess, GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); + + /* zapytanie jest krĂłtkie, wiÄ™c zawsze zmieĹ›ci siÄ™ + * do bufora gniazda. jeĹ›li write() zwrĂłci mniej, + * staĹ‚o siÄ™ coĹ› zĹ‚ego. */ if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_WRITING; @@ -999,72 +1621,36 @@ int port = GG_DEFAULT_PORT; struct in_addr addr; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); - - /* czytamy linię z gniazda i obcinamy \r\n. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); + + /* czytamy liniÄ™ z gniazda i obcinamy \r\n. */ gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); - - /* sprawdzamy, czy wszystko w porz±dku. */ + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); + + /* sprawdzamy, czy wszystko w porzÄ…dku. */ if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n"); - - close(sess->fd); - - /* je¶li otrzymali¶my jakie¶ dziwne informacje, - * próbujemy się ł±czyć z pominięciem huba. */ - if (sess->proxy_addr && sess->proxy_port) { - if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { - /* trudno. nie wyszło. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail_connecting; - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; - } - - sess->port = GG_DEFAULT_PORT; - - /* ł±czymy się na port 8074 huba. */ - if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); - - sess->port = GG_HTTPS_PORT; - - /* ł±czymy się na port 443. */ - if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail_connecting; - } - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n"); + goto fail_connecting; } - - /* ignorujemy resztę nagłówka. */ + + /* ignorujemy resztÄ™ nagĹ‚Ăłwka. */ while (strcmp(buf, "\r\n") && strcmp(buf, "")) gg_read_line(sess->fd, buf, sizeof(buf) - 1); - /* czytamy pierwsz± linię danych. */ + /* czytamy pierwszÄ… liniÄ™ danych. */ gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); - - /* je¶li pierwsza liczba w linii nie jest równa zeru, - * oznacza to, że mamy wiadomo¶ć systemow±. */ + + /* jeĹ›li pierwsza liczba w linii nie jest rĂłwna zeru, + * oznacza to, ĹĽe mamy wiadomość systemowÄ…. */ if (atoi(buf)) { char tmp[1024], *foo, *sysmsg_buf = NULL; int len = 0; - + while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) { if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); break; } @@ -1074,23 +1660,32 @@ strcpy(sysmsg_buf, tmp); else strcat(sysmsg_buf, tmp); - + len += strlen(tmp); } - + e->type = GG_EVENT_MSG; e->event.msg.msgclass = atoi(buf); e->event.msg.sender = 0; - e->event.msg.message = (unsigned char *)sysmsg_buf; + e->event.msg.message = (unsigned char*) sysmsg_buf; } - + close(sess->fd); - - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); + + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); /* analizujemy otrzymane dane. */ tmp = buf; - + +#ifdef GG_CONFIG_HAVE_OPENSSL + if (!sess->ssl) +#endif + { + while (*tmp && *tmp != ' ') + tmp++; + while (*tmp && *tmp == ' ') + tmp++; + } while (*tmp && *tmp != ' ') tmp++; while (*tmp && *tmp == ' ') @@ -1105,36 +1700,43 @@ port = atoi(tmp + 1); } + if (!strcmp(host, "notoperating")) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno)); + sess->fd = -1; + goto fail_unavailable; + } + addr.s_addr = inet_addr(host); sess->server_addr = addr.s_addr; if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) { - /* je¶li mamy proxy, ł±czymy się z nim. */ + /* jeĹ›li mamy proxy, Ĺ‚Ä…czymy siÄ™ z nim. */ if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { - /* nie wyszło? trudno. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); + /* nie wyszĹ‚o? trudno. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } - + sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; + sess->soft_timeout = 1; break; } sess->port = port; - /* ł±czymy się z wła¶ciwym serwerem. */ + /* Ĺ‚Ä…czymy siÄ™ z wĹ‚aĹ›ciwym serwerem. */ if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); sess->port = GG_HTTPS_PORT; - /* nie wyszło? próbujemy portu 443. */ + /* nie wyszĹ‚o? prĂłbujemy portu 443. */ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) { - /* ostatnia deska ratunku zawiodła? + /* ostatnia deska ratunku zawiodĹ‚a? * w takim razie zwijamy manatki. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } } @@ -1142,23 +1744,26 @@ sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; - + sess->soft_timeout = 1; + break; } case GG_STATE_CONNECTING_GG: { int res = 0; - socklen_t res_size = sizeof(res); - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); - - /* je¶li wyst±pił bł±d podczas ł±czenia się... */ + unsigned int res_size = sizeof(res); + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); + + sess->soft_timeout = 0; + + /* jeĹ›li wystÄ…piĹ‚ bĹ‚Ä…d podczas Ĺ‚Ä…czenia siÄ™... */ if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { - /* je¶li nie udało się poł±czenie z proxy, - * nie mamy czego próbować więcej. */ + /* jeĹ›li nie udaĹ‚o siÄ™ poĹ‚Ä…czenie z proxy, + * nie mamy czego prĂłbować wiÄ™cej. */ if (sess->proxy_addr && sess->proxy_port) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); goto fail_connecting; } @@ -1170,36 +1775,46 @@ errno = ETIMEDOUT; #endif -#ifdef __GG_LIBGADU_HAVE_OPENSSL - /* je¶li logujemy się po TLS, nie próbujemy - * się ł±czyć już z niczym innym w przypadku - * błędu. nie do¶ć, że nie ma sensu, to i - * trzeba by się bawić w tworzenie na nowo +#ifdef GG_CONFIG_HAVE_OPENSSL + /* jeĹ›li logujemy siÄ™ po TLS, nie prĂłbujemy + * siÄ™ Ĺ‚Ä…czyć juĹĽ z niczym innym w przypadku + * bĹ‚Ä™du. nie dość, ĹĽe nie ma sensu, to i + * trzeba by siÄ™ bawić w tworzenie na nowo * SSL i SSL_CTX. */ if (sess->ssl) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); goto fail_connecting; } #endif - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); + + if (sess->port == GG_HTTPS_PORT) + goto fail_connecting; sess->port = GG_HTTPS_PORT; - /* próbujemy na port 443. */ + /* prĂłbujemy na port 443. */ if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } + + sess->state = GG_STATE_CONNECTING_GG; + sess->check = GG_CHECK_WRITE; + sess->timeout = GG_DEFAULT_TIMEOUT; + sess->soft_timeout = 1; + + break; } - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); - + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); + if (gg_proxy_http_only) sess->proxy_port = 0; - /* je¶li mamy proxy, wy¶lijmy zapytanie. */ + /* jeĹ›li mamy proxy, wyĹ›lijmy zapytanie. */ if (sess->proxy_addr && sess->proxy_port) { char buf[100], *auth = gg_proxy_auth(); struct in_addr addr; @@ -1211,20 +1826,22 @@ snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf); - - /* wysyłamy zapytanie. jest ono na tyle krótkie, - * że musi się zmie¶cić w buforze gniazda. je¶li - * write() zawiedzie, stało się co¶ złego. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf); + + /* wysyĹ‚amy zapytanie. jest ono na tyle krĂłtkie, + * ĹĽe musi siÄ™ zmieĹ›cić w buforze gniazda. jeĹ›li + * write() zawiedzie, staĹ‚o siÄ™ coĹ› zĹ‚ego. */ if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + free(auth); goto fail_connecting; } if (auth) { - gg_debug(GG_DEBUG_MISC, "// %s", auth); + gg_debug_session(sess, GG_DEBUG_MISC, "// %s", auth); if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + free(auth); goto fail_connecting; } @@ -1232,12 +1849,12 @@ } if (write(sess->fd, "\r\n", 2) < 2) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); goto fail_connecting; } } -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) { SSL_set_fd(sess->ssl, sess->fd); @@ -1256,19 +1873,19 @@ break; } -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL case GG_STATE_TLS_NEGOTIATION: { int res; X509 *peer; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); if ((res = SSL_connect(sess->ssl)) <= 0) { int err = SSL_get_error(sess->ssl, res); if (res == 0) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_TLS; @@ -1277,9 +1894,9 @@ sess->fd = -1; break; } - + if (err == SSL_ERROR_WANT_READ) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_READ; @@ -1287,7 +1904,7 @@ break; } else if (err == SSL_ERROR_WANT_WRITE) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_WRITE; @@ -1299,8 +1916,8 @@ ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); - + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); + e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_TLS; sess->state = GG_STATE_IDLE; @@ -1310,20 +1927,20 @@ } } - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl)); peer = SSL_get_peer_certificate(sess->ssl); if (!peer) - gg_debug(GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); else { char buf[1024]; X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf)); - gg_debug(GG_DEBUG_MISC, "// cert subject: %s\n", buf); + gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf); X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf)); - gg_debug(GG_DEBUG_MISC, "// cert issuer: %s\n", buf); + gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf); } sess->state = GG_STATE_READING_KEY; @@ -1336,45 +1953,48 @@ case GG_STATE_READING_KEY: { - struct gg_header *h; + struct gg_header *h; struct gg_welcome *w; - struct gg_login60 l; - unsigned int hash; - char *password = sess->password; + unsigned char *password = (unsigned char*) sess->password; int ret; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); - - memset(&l, 0, sizeof(l)); - l.dunno2 = 0xbe; - - /* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie - * się tekstu wrzucanego przez proxy. */ + uint8_t login_hash[64]; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); + + memset(login_hash, 0, sizeof(login_hash)); + + /* XXX bardzo, bardzo, bardzo gĹ‚upi pomysĹ‚ na pozbycie + * siÄ™ tekstu wrzucanego przez proxy. */ if (sess->proxy_addr && sess->proxy_port) { char buf[100]; strcpy(buf, ""); gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf); - + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf); + while (strcmp(buf, "")) { gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); if (strcmp(buf, "")) - gg_debug(GG_DEBUG_MISC, "// %s\n", buf); + gg_debug_session(sess, GG_DEBUG_MISC, "// %s\n", buf); } /* XXX niech czeka jeszcze raz w tej samej - * fazie. głupio, ale działa. */ + * fazie. gĹ‚upio, ale dziaĹ‚a. */ sess->proxy_port = 0; - + break; } /* czytaj pierwszy pakiet. */ if (!(h = gg_recv_packet(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); + if (errno == EAGAIN) { + sess->check = GG_CHECK_READ; + break; + } + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_READING; @@ -1387,7 +2007,7 @@ } if (h->type != GG_WELCOME) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); free(h); close(sess->fd); sess->fd = -1; @@ -1397,61 +2017,127 @@ sess->state = GG_STATE_IDLE; break; } - + w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header)); w->key = gg_fix32(w->key); - hash = gg_login_hash((unsigned char *)password, w->key); - - gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash); - + switch (sess->hash_type) { + case GG_LOGIN_HASH_GG32: + { + unsigned int hash; + + hash = gg_fix32(gg_login_hash(password, w->key)); + gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> GG32 hash %.8x\n", w->key, hash); + memcpy(login_hash, &hash, sizeof(hash)); + + break; + } + + case GG_LOGIN_HASH_SHA1: + { + char tmp[41]; + int i; + + gg_login_hash_sha1((char*) password, w->key, login_hash); + for (i = 0; i < 40; i += 2) + snprintf(tmp + i, sizeof(tmp) - i, "%02x", login_hash[i / 2]); + + gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> SHA1 hash: %s\n", w->key, tmp); + + break; + } + } + free(h); - free(sess->password); sess->password = NULL; - { - struct in_addr dcc_ip; - dcc_ip.s_addr = gg_dcc_ip; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip)); - } - if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) { struct sockaddr_in sin; - socklen_t sin_len = sizeof(sin); - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); + unsigned int sin_len = sizeof(sin); + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); - l.local_ip = sin.sin_addr.s_addr; + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); + sess->client_addr = sin.sin_addr.s_addr; } else { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); - l.local_ip = 0; + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); + sess->client_addr = 0; } - } else - l.local_ip = gg_dcc_ip; - - l.uin = gg_fix32(sess->uin); - l.hash = gg_fix32(hash); - l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); - l.version = gg_fix32(sess->protocol_version); - l.local_port = gg_fix16(gg_dcc_port); - l.image_size = sess->image_size; - - if (sess->external_addr && sess->external_port > 1023) { - l.external_ip = sess->external_addr; - l.external_port = gg_fix16(sess->external_port); + } else + sess->client_addr = gg_dcc_ip; + + if (sess->protocol_version >= 0x2e) { + struct gg_login80 l; + + uint32_t tmp_version_len = gg_fix32(strlen(GG8_VERSION)); + uint32_t tmp_descr_len = gg_fix32((sess->initial_descr) ? strlen(sess->initial_descr) : 0); + + memset(&l, 0, sizeof(l)); + l.uin = gg_fix32(sess->uin); + memcpy(l.language, GG8_LANG, sizeof(l.language)); + l.hash_type = sess->hash_type; + memcpy(l.hash, login_hash, sizeof(login_hash)); + l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); + l.flags = gg_fix32(0x00800001); + l.features = gg_fix32(sess->protocol_features); + l.image_size = sess->image_size; + l.dunno2 = 0x64; + + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80 packet\n"); + ret = gg_send_packet(sess, GG_LOGIN80, + &l, sizeof(l), + &tmp_version_len, sizeof(uint32_t), GG8_VERSION, strlen(GG8_VERSION), + &tmp_descr_len, sizeof(uint32_t), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, + NULL); + + } else if (sess->protocol_version == 0x2d) { + struct gg_login70 l; + + memset(&l, 0, sizeof(l)); + l.uin = gg_fix32(sess->uin); + l.local_ip = (sess->external_addr) ? sess->external_addr : sess->client_addr; + l.local_port = gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port); + l.hash_type = sess->hash_type; + memcpy(l.hash, login_hash, sizeof(login_hash)); + l.image_size = sess->image_size; + l.dunno2 = 0x64; + l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); + l.version = gg_fix32(sess->protocol_version | sess->protocol_flags); + + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80BETA packet\n"); + ret = gg_send_packet(sess, GG_LOGIN80BETA, + &l, sizeof(l), + sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, + (sess->initial_descr) ? "\0" : NULL, (sess->initial_descr) ? 1 : 0, + NULL); + } else { + struct gg_login70 l; + + memset(&l, 0, sizeof(l)); + l.local_ip = (sess->external_addr) ? sess->external_addr : sess->client_addr; + l.uin = gg_fix32(sess->uin); + l.local_port = gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port); + l.hash_type = sess->hash_type; + memcpy(l.hash, login_hash, sizeof(login_hash)); + l.image_size = sess->image_size; + l.dunno2 = 0xbe; + l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); + l.version = gg_fix32(sess->protocol_version | sess->protocol_flags); + + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN70 packet\n"); + ret = gg_send_packet(sess, GG_LOGIN70, + &l, sizeof(l), + sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, + NULL); } - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n"); - ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL); - free(sess->initial_descr); sess->initial_descr = NULL; if (ret == -1) { - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); errno2 = errno; close(sess->fd); errno = errno2; @@ -1461,8 +2147,9 @@ sess->state = GG_STATE_IDLE; break; } - + sess->state = GG_STATE_READING_REPLY; + sess->check = GG_CHECK_READ; break; } @@ -1471,10 +2158,15 @@ { struct gg_header *h; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); if (!(h = gg_recv_packet(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); + if (errno == EAGAIN) { + sess->check = GG_CHECK_READ; + break; + } + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_READING; sess->state = GG_STATE_IDLE; @@ -1484,11 +2176,12 @@ sess->fd = -1; break; } - - if (h->type == GG_LOGIN_OK) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); + + if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL || h->type == GG_LOGIN80_OK) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); e->type = GG_EVENT_CONN_SUCCESS; sess->state = GG_STATE_CONNECTED; + sess->check = GG_CHECK_READ; sess->timeout = -1; sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL; free(h); @@ -1496,15 +2189,15 @@ } if (h->type == GG_LOGIN_FAILED) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); e->event.failure = GG_FAILURE_PASSWORD; errno = EACCES; - } else if (h->type == GG_NEED_EMAIL) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() email change needed\n"); - e->event.failure = GG_FAILURE_NEED_EMAIL; + } else if (h->type == GG_DISCONNECTING) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n"); + e->event.failure = GG_FAILURE_INTRUDER; errno = EACCES; } else { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); e->event.failure = GG_FAILURE_INVALID; errno = EINVAL; } @@ -1522,13 +2215,13 @@ case GG_STATE_CONNECTED: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); sess->last_event = time(NULL); - + if ((res = gg_watch_fd_connected(sess, e)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); if (errno == EAGAIN) { e->type = GG_EVENT_NONE; @@ -1536,6 +2229,9 @@ } else res = -1; } + + sess->check = GG_CHECK_READ; + break; } } @@ -1544,10 +2240,13 @@ if (res == -1) { free(e); e = NULL; + } else { + if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) + sess->check |= GG_CHECK_WRITE; } return e; - + fail_connecting: if (sess->fd != -1) { errno2 = errno; @@ -1565,6 +2264,12 @@ e->event.failure = GG_FAILURE_RESOLVING; sess->state = GG_STATE_IDLE; goto done; + +fail_unavailable: + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_UNAVAILABLE; + sess->state = GG_STATE_IDLE; + goto done; } /*