# HG changeset patch # User Luke Schierer # Date 1082741059 0 # Node ID ce90b119b103bcd298f1b14d4b0cc79d04eab955 # Parent 5205743477bba2905b6173b6bed601d0cc472656 [gaim-migrate @ 9537] " Updates gadu-gadu to use the new version 6.0 protocol. This patch was compiled with the use of much code from ekg (http: //dev.null.pl/ekg/). It hasn't been extensively tested (I only speak English, so how it behaves for polish speakers would be good to know!), so more testing would be great." --Andrew Wellington committer: Tailor Script diff -r 5205743477bb -r ce90b119b103 src/protocols/gg/gg.c --- a/src/protocols/gg/gg.c Fri Apr 23 17:13:33 2004 +0000 +++ b/src/protocols/gg/gg.c Fri Apr 23 17:24:19 2004 +0000 @@ -1,6 +1,6 @@ /* * gaim - Gadu-Gadu Protocol Plugin - * $Id: gg.c 9504 2004-04-22 01:53:18Z chipx86 $ + * $Id: gg.c 9537 2004-04-23 17:24:19Z lschiere $ * * Copyright (C) 2001 Arkadiusz Miśkiewicz * @@ -265,6 +265,125 @@ return m; } +static void agg_load_buddy_list(GaimConnection *gc, char *buddylist) +{ + struct agg_data *gd = (struct agg_data *)gc->proto_data; + gchar *ptr = buddylist; + gchar **users_tbl; + int i; + uin_t *userlist = NULL; + int userlist_size = 0; + + users_tbl = g_strsplit(ptr, "\r\n", AGG_PUBDIR_MAX_ENTRIES); + + /* Parse array of Buddies List */ + for (i = 0; users_tbl[i] != NULL; i++) { + gchar **data_tbl; + gchar *name, *show; + + if (strlen(users_tbl[i])==0) { + gaim_debug(GAIM_DEBUG_MISC, "gg", + "import_buddies_server_results: users_tbl[i] is empty\n"); + continue; + } + + data_tbl = g_strsplit(users_tbl[i], ";", 8); + + show = charset_convert(data_tbl[3], "CP1250", "UTF-8"); + name = data_tbl[6]; + + if (invalid_uin(name)) { + continue; + } + + gaim_debug(GAIM_DEBUG_MISC, "gg", + "import_buddies_server_results: uin: %s\n", name); + if (!gaim_find_buddy(gc->account, name)) { + GaimBuddy *b; + GaimGroup *g; + /* Default group if none specified on server */ + gchar *group = g_strdup("Gadu-Gadu"); + if (strlen(data_tbl[5])) { + gchar **group_tbl = g_strsplit(data_tbl[5], ",", 2); + if (strlen(group_tbl[0])) { + g_free(group); + group = g_strdup(group_tbl[0]); + } + g_strfreev(group_tbl); + } + /* Add Buddy to our userlist */ + if (!(g = gaim_find_group(group))) { + g = gaim_group_new(group); + gaim_blist_add_group(g, NULL); + } + b = gaim_buddy_new(gc->account, name, strlen(show) ? show : NULL); + gaim_blist_add_buddy(b,NULL,g,NULL); + gaim_blist_save(); + + userlist_size++; + userlist = g_renew(uin_t, userlist, userlist_size); + userlist[userlist_size - 1] = + (uin_t) strtol((char *)name, (char **)NULL, 10); + + g_free(group); + } + g_free(show); + g_strfreev(data_tbl); + } + g_strfreev(users_tbl); + + if (userlist) { + gg_notify(gd->sess, userlist, userlist_size); + g_free(userlist); + } +} + +static void agg_save_buddy_list (GaimConnection *gc, char *existlist) +{ + GaimBlistNode *gnode, *cnode, *bnode; + char *buddylist = g_strdup(existlist ? existlist : ""); + char *ptr; + struct agg_data *gd = (struct agg_data *)gc->proto_data; + + for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) { + GaimGroup *g = (GaimGroup *)gnode; + if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) + continue; + for(cnode = gnode->child; cnode; cnode = cnode->next) { + if(!GAIM_BLIST_NODE_IS_CONTACT(cnode)) + continue; + for(bnode = cnode->child; bnode; bnode = bnode->next) { + GaimBuddy *b = (GaimBuddy *)bnode; + + if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) + continue; + + if(b->account == gc->account) { + gchar *newdata; + /* GG Number */ + gchar *name = b->name; + /* GG Pseudo */ + gchar *show = b->alias ? b->alias : b->name; + /* Group Name */ + gchar *gname = g->name; + + newdata = g_strdup_printf("%s;%s;%s;%s;%s;%s;%s;%s%s\r\n", + show, show, show, show, "", gname, name, "", ""); + + ptr = buddylist; + buddylist = g_strconcat(ptr, newdata, NULL); + + g_free(newdata); + g_free(ptr); + } + } + } + } + + /* save the list to the gadu gadu server */ + gg_userlist_request(gd->sess, GG_USERLIST_PUT, buddylist); +} + static void main_callback(gpointer data, gint source, GaimInputCondition cond) { GaimConnection *gc = data; @@ -347,6 +466,34 @@ } } break; + case GG_EVENT_NOTIFY60: + { + gchar user[20]; + struct gg_notify_reply60 *n = (void *)e->event.notify60; + guint status; + + while (n->uin) { + switch (n->status) { + case GG_STATUS_NOT_AVAIL: + status = UC_UNAVAILABLE; + break; + case GG_STATUS_AVAIL: + case GG_STATUS_BUSY: + case GG_STATUS_INVISIBLE: + status = UC_NORMAL | (n->status << 5); + break; + default: + status = UC_NORMAL; + break; + } + + g_snprintf(user, sizeof(user), "%lu", (long unsigned int)n->uin); + serv_got_update(gc, user, (status == UC_UNAVAILABLE) ? 0 : 1, 0, 0, 0, + status); + n++; + } + } + break; case GG_EVENT_STATUS: { gchar user[20]; @@ -371,11 +518,55 @@ status); } break; + case GG_EVENT_STATUS60: + { + gchar user[20]; + guint status; + + switch (e->event.status60.status) { + case GG_STATUS_NOT_AVAIL: + status = UC_UNAVAILABLE; + break; + case GG_STATUS_AVAIL: + case GG_STATUS_BUSY: + case GG_STATUS_INVISIBLE: + status = UC_NORMAL | (e->event.status60.status << 5); + break; + default: + status = UC_NORMAL; + break; + } + + g_snprintf(user, sizeof(user), "%lu", e->event.status60.uin); + serv_got_update(gc, user, (status == UC_UNAVAILABLE) ? 0 : 1, 0, 0, 0, + status); + } + break; case GG_EVENT_ACK: gaim_debug(GAIM_DEBUG_MISC, "gg", "main_callback: message %d to %lu sent with status %d\n", e->event.ack.seq, e->event.ack.recipient, e->event.ack.status); break; + case GG_EVENT_USERLIST: + { + gaim_debug(GAIM_DEBUG_MISC, "gg", "main_callback: received userlist reply\n"); + switch (e->event.userlist.type) { + case GG_USERLIST_GET_REPLY: + { + + if (e->event.userlist.reply) { + agg_load_buddy_list(gc, e->event.userlist.reply); + } + break; + } + + case GG_USERLIST_PUT_REPLY: + { + /* ignored */ + } + } + } + default: gaim_debug(GAIM_DEBUG_ERROR, "gg", "main_callback: unsupported event %d\n", e->type); @@ -588,6 +779,7 @@ if (invalid_uin(who)) return; gg_add_notify(gd->sess, strtol(who, (char **)NULL, 10)); + agg_save_buddy_list(gc, NULL); } static void agg_rem_buddy(GaimConnection *gc, const char *who, const char *group) @@ -596,6 +788,7 @@ if (invalid_uin(who)) return; gg_remove_notify(gd->sess, strtol(who, (char **)NULL, 10)); + agg_save_buddy_list(gc, NULL); } static void agg_add_buddies(GaimConnection *gc, GList *whos) @@ -618,6 +811,8 @@ gg_notify(gd->sess, userlist, userlist_size); g_free(userlist); } + + agg_save_buddy_list(gc, NULL); } static void search_results(GaimConnection *gc, gchar *webdata) @@ -734,6 +929,7 @@ gaim_account_request_change_password(gaim_connection_get_account(gc)); } +#if 0 static void import_buddies_server_results(GaimConnection *gc, gchar *webdata) { gchar *ptr; @@ -839,6 +1035,7 @@ _("Couldn't delete Buddy List from Gadu-Gadu server"), NULL); } +#endif static void password_change_server_results(GaimConnection *gc, gchar *webdata) { @@ -903,6 +1100,7 @@ case AGG_HTTP_SEARCH: search_results(gc, webdata); break; +#if 0 case AGG_HTTP_USERLIST_IMPORT: import_buddies_server_results(gc, webdata); break; @@ -912,6 +1110,7 @@ case AGG_HTTP_USERLIST_DELETE: delete_buddies_server_results(gc, webdata); break; +#endif case AGG_HTTP_PASSWORD_CHANGE: password_change_server_results(gc, webdata); break; @@ -980,6 +1179,7 @@ hdata->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_results, hdata); } +#if 0 static void import_buddies_server(GaimConnection *gc) { struct agg_http *hi = g_new0(struct agg_http, 1); @@ -1102,6 +1302,7 @@ return; } } +#endif static void agg_dir_search(GaimConnection *gc, const char *first, const char *middle, const char *last, const char *maiden, const char *city, const char *state, @@ -1209,8 +1410,8 @@ pam->gc = gc; m = g_list_append(m, pam); +#if 0 m = g_list_append(m, NULL); - pam = g_new0(struct proto_actions_menu, 1); pam->label = _("Import Buddy List from Server"); pam->callback = import_buddies_server; @@ -1228,6 +1429,7 @@ pam->callback = delete_buddies_server; pam->gc = gc; m = g_list_append(m, pam); +#endif return m; } @@ -1301,6 +1503,30 @@ /* It's implemented on client side because GG server doesn't support this */ } +static void agg_group_buddy (GaimConnection * gc, const char *who, + const char *old_group, const char *new_group) +{ + GaimBuddy *buddy = gaim_find_buddy(gaim_connection_get_account(gc), who); + gchar *newdata; + /* GG Number */ + gchar *name = buddy->name; + /* GG Pseudo */ + gchar *show = buddy->alias ? buddy->alias : buddy->name; + /* Group Name */ + const gchar *gname = new_group; + + newdata = g_strdup_printf("%s;%s;%s;%s;%s;%s;%s;%s%s\r\n", + show, show, show, show, "", gname, name, "", ""); + agg_save_buddy_list(gc, newdata); + g_free(newdata); +} + +static void agg_rename_group (GaimConnection *gc, const char *old_group, + const char *new_group, GList *members) +{ + agg_save_buddy_list(gc, NULL); +} + static GaimPlugin *my_protocol = NULL; static GaimPluginProtocolInfo prpl_info = @@ -1351,8 +1577,8 @@ NULL, NULL, NULL, - NULL, - NULL, + agg_group_buddy, + agg_rename_group, NULL, NULL, NULL diff -r 5205743477bb -r ce90b119b103 src/protocols/gg/libgg.c --- a/src/protocols/gg/libgg.c Fri Apr 23 17:13:33 2004 +0000 +++ b/src/protocols/gg/libgg.c Fri Apr 23 17:24:19 2004 +0000 @@ -1,4 +1,4 @@ -/* $Id: libgg.c 6741 2003-07-20 19:11:13Z thekingant $ */ +/* $Id: libgg.c 9537 2004-04-23 17:24:19Z lschiere $ */ /* * (C) Copyright 2001 Wojtek Kaniewski , @@ -73,7 +73,7 @@ #ifdef __GNUC__ __attribute__ ((unused)) #endif -= "$Id: libgg.c 6741 2003-07-20 19:11:13Z thekingant $"; += "$Id: libgg.c 9537 2004-04-23 17:24:19Z lschiere $"; #endif @@ -101,7 +101,6 @@ * dla maszyn big-endianowych zamienia kolejność bajtów w ,,short''ach. */ -/* not currently used static inline unsigned short fix16(unsigned short x) { #ifndef WORDS_BIGENDIAN @@ -112,7 +111,6 @@ ((x & (unsigned short) 0xff00U) >> 8)); #endif } -*/ #ifndef _WIN32 /* @@ -277,67 +275,135 @@ /* * gg_send_packet() // funkcja wewnętrzna * - * konstruuje pakiet i wysyła go w do serwera. + * konstruuje pakiet i wysyła go do serwera. * - * - sock - połączony socket, - * - type - typ pakietu, - * - packet - wskaźnik do struktury pakietu, - * - length - długość struktury pakietu, - * - payload - dodatkowy tekst doklejany do pakietu (np. wiadomość), - * - payload_length - długość dodatkowego tekstu. + * - sock - deskryptor gniazda + * - type - typ pakietu + * - payload_1 - pierwsza część pakietu + * - payload_length_1 - długość pierwszej części + * - payload_2 - druga część pakietu + * - payload_length_2 - długość drugiej części + * - ... - kolejne części pakietu i ich długości + * - NULL - końcowym parametr (konieczny!) * - * jeśli poszło dobrze, zwraca 0. w przypadku błędu -1. jeśli errno=ENOMEM, - * zabrakło pamięci. inaczej był błąd przy wysyłaniu pakietu. dla errno=0 + * jeśli się powiodło, zwraca 0, w przypadku błędu -1. jeśli errno == ENOMEM, + * zabrakło pamięci. inaczej był błąd przy wysyłaniu pakietu. dla errno == 0 * nie wysłano całego pakietu. */ -static int gg_send_packet(int sock, int type, void *packet, int length, void *payload, int payload_length) +int gg_send_packet(struct gg_session *sess, int type, ...) { struct gg_header *h; - int res, plen; char *tmp; + int tmp_length; + void *payload; + int payload_length; + va_list ap; + int res; - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(0x%.2x, %d, %d);\n", type, length, payload_length); - - if (length < 0 || payload_length < 0) { - gg_debug(GG_DEBUG_MISC, "-- invalid packet/payload length\n"); - errno = ERANGE; - return -1; - } + gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type); - if (!(tmp = malloc(sizeof(struct gg_header) + length + payload_length))) { - gg_debug(GG_DEBUG_MISC, "-- not enough memory\n"); + tmp_length = 0; + + if (!(tmp = malloc(sizeof(struct gg_header)))) { + gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n"); return -1; } h = (struct gg_header*) tmp; h->type = fix32(type); - h->length = fix32(length + payload_length); + h->length = fix32(0); + + va_start(ap, type); + + payload = va_arg(ap, void *); + + while (payload) { + char *tmp2; + + payload_length = va_arg(ap, int); - if (packet) - memcpy(tmp + sizeof(struct gg_header), packet, length); - if (payload) - memcpy(tmp + sizeof(struct gg_header) + length, payload, payload_length); + if (payload_length < 0) + gg_debug(GG_DEBUG_MISC, "// gg_send_packet() invalid payload length (%d)\n", payload_length); + + if (!(tmp2 = realloc(tmp, sizeof(struct gg_header) + tmp_length + payload_length))) { + gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n"); + free(tmp); + va_end(ap); + return -1; + } + + tmp = tmp2; + + memcpy(tmp + sizeof(struct gg_header) + tmp_length, payload, payload_length); + tmp_length += payload_length; + + payload = va_arg(ap, void *); + } + + va_end(ap); + + h = (struct gg_header*) tmp; + h->length = fix32(tmp_length); if ((gg_debug_level & GG_DEBUG_DUMP)) { - int i; - - gg_debug(GG_DEBUG_DUMP, "%%%% sending packet (type=%.2x):", fix32(h->type)); - for (i = 0; i < sizeof(struct gg_header) + fix32(h->length); i++) - gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); - gg_debug(GG_DEBUG_DUMP, "\n"); - } - - plen = sizeof(struct gg_header) + length + payload_length; - if ((res = write(sock, tmp, plen)) < plen) { - gg_debug(GG_DEBUG_MISC, "-- write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno)); + unsigned int i; + + gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", fix32(h->type)); + for (i = 0; i < sizeof(struct gg_header) + fix32(h->length); i++) + gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); + gg_debug(GG_DEBUG_DUMP, "\n"); + } + + tmp_length += sizeof(struct gg_header); + + if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) { + gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno)); free(tmp); return -1; } - + free(tmp); return 0; } +/* + * gg_write() // funkcja pomocnicza + * + * zapisuje do gniazda określoną ilość bajtów. bierze pod uwagę, czy mamy + * połączenie zwykłe czy TLS. + * + * - sess - sesja, + * - buf - bufor, + * - length - ilość bajtów, + * + * takie same wartości jak write(). + */ +int gg_write(struct gg_session *sess, const char *buf, int length) +{ + int res; + +#ifdef __GG_LIBGADU_HAVE_OPENSSL + if (sess->ssl) { + int err; + + res = SSL_write(sess->ssl, buf, length); + + if (res < 0) { + err = SSL_get_error(sess->ssl, res); + + if (err == SSL_ERROR_WANT_WRITE) + errno = EAGAIN; + + return -1; + } + } else +#endif + res = write(sess->fd, buf, length); + + return res; +} + + #ifndef _WIN32 /* * gg_login() @@ -443,6 +509,40 @@ #endif /*!_WIN32*/ /* + * gg_login_hash() // funkcja wewnętrzna + * + * liczy hash z hasła i danego seeda. + * + * - password - hasło do hashowania + * - seed - wartość podana przez serwer + * + * hash. + */ +unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) +{ + unsigned int x, y, z; + + y = seed; + + for (x = 0; *password; password++) { + x = (x & 0xffffff00) | *password; + y ^= x; + y += x; + x <<= 8; + y ^= x; + x <<= 8; + y -= x; + x <<= 8; + y ^= x; + + z = y & 0x1F; + y = (y << z) | (y >> (32 - z)); + } + + return y; +} + +/* * gg_free_session() * * zwalnia pamięć zajmowaną przez opis sesji. @@ -488,7 +588,7 @@ p.status = fix32(status); - return gg_send_packet(sess->fd, GG_NEW_STATUS, &p, sizeof(p), NULL, 0); + return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL, 0); } /* @@ -520,19 +620,43 @@ * gg_send_message() * * wysyła wiadomość do innego użytkownika. zwraca losowy numer - * sekwencyjny, który można olać albo wykorzystać do potwierdzenia. + * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia. + * + * - sess - opis sesji + * - msgclass - rodzaj wiadomości + * - recipient - numer adresata + * - message - treść wiadomości * - * - sess - opis sesji, - * - msgclass - rodzaj wiadomości, - * - recipient - numer adresata, - * - message - treść wiadomości. + * numer sekwencyjny wiadomości lub -1 w przypadku błędu. + */ +int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message) +{ + gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message); + + return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0); +} + +/* + * gg_send_message_richtext() * - * w przypadku błędu zwraca -1, inaczej numer sekwencyjny. + * wysyła kolorową wiadomość do innego użytkownika. zwraca losowy numer + * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia. + * + * - sess - opis sesji + * - msgclass - rodzaj wiadomości + * - recipient - numer adresata + * - message - treść wiadomości + * - format - informacje o formatowaniu + * - formatlen - długość informacji o formatowaniu + * + * numer sekwencyjny wiadomości lub -1 w przypadku błędu. */ -int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, char *message) +int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen) { struct gg_send_msg s; + gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen); + if (!sess) { errno = EFAULT; return -1; @@ -543,8 +667,6 @@ return -1; } - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(..., %d, %u, \"...\");\n", msgclass, recipient); - s.recipient = fix32(recipient); if (!sess->seq) sess->seq = 0x01740000 | (rand() & 0xffff); @@ -552,7 +674,7 @@ s.msgclass = fix32(msgclass); sess->seq += (rand() % 0x300) + 0x300; - if (gg_send_packet(sess->fd, GG_SEND_MSG, &s, sizeof(s), message, strlen(message) + 1) == -1) + if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen(message) + 1, format, formatlen, NULL) == -1) return -1; return fix32(s.seq); @@ -581,15 +703,7 @@ gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(...);\n"); - if(ping_outstanding) { - gaim_debug(GAIM_DEBUG_INFO, "gg", - "Trying to send ping, when we havn't been ponged on last ping\n"); - return 1; - } - else { - ping_outstanding = 1; - return gg_send_packet(sess->fd, GG_PING, NULL, 0, NULL, 0); - } + return gg_send_packet(sess, GG_PING, NULL); } /* @@ -652,7 +766,7 @@ n[i].dunno1 = 3; } - if (gg_send_packet(sess->fd, GG_NOTIFY, n, sizeof(*n) * count, NULL, 0) == -1) + if (gg_send_packet(sess, GG_NOTIFY, n, sizeof(*n) * count, NULL, 0) == -1) res = -1; free(n); @@ -689,7 +803,7 @@ a.uin = fix32(uin); a.dunno1 = 3; - return gg_send_packet(sess->fd, GG_ADD_NOTIFY, &a, sizeof(a), NULL, 0); + return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL); } /* @@ -721,7 +835,54 @@ a.uin = fix32(uin); a.dunno1 = 3; - return gg_send_packet(sess->fd, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL, 0); + return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL, 0); +} + +/* + * gg_userlist_request() + * + * wysyła żądanie/zapytanie listy kontaktów na serwerze. + * + * - sess - opis sesji + * - type - rodzaj zapytania/żądania + * - request - treść zapytania/żądania (może być NULL) + * + * 0, -1 + */ +int gg_userlist_request(struct gg_session *sess, char type, const char *request) +{ + int len; + + if (!sess) { + errno = EINVAL; + return -1; + } + + if (!request) { + sess->userlist_blocks = 1; + return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL); + } + + len = strlen(request); + + sess->userlist_blocks = 0; + + while (len > 2047) { + sess->userlist_blocks++; + + if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1) + return -1; + + if (type == GG_USERLIST_PUT) + type = GG_USERLIST_PUT_MORE; + + request += 2047; + len -= 2047; + } + + sess->userlist_blocks++; + + return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); } /* @@ -736,7 +897,7 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e) { struct gg_header *h; - void *p; + char *p; if (!sess) { errno = EFAULT; @@ -752,8 +913,11 @@ p = (void *)h + sizeof(struct gg_header); - if (h->type == GG_RECV_MSG) { - struct gg_recv_msg *r = p; + + switch (h->type) { + case GG_RECV_MSG: + { + struct gg_recv_msg *r = (void *)p; gg_debug(GG_DEBUG_MISC, "-- received a message\n"); @@ -764,10 +928,11 @@ e->event.msg.message = strdup((char*) r + sizeof(*r)); e->event.msg.time = fix32(r->time); } - } - - if (h->type == GG_NOTIFY_REPLY) { - struct gg_notify_reply *n = p; + break; + } + case GG_NOTIFY_REPLY: + { + struct gg_notify_reply *n = (void *)p; int count, i; gg_debug(GG_DEBUG_MISC, "-- received a notify reply\n"); @@ -785,10 +950,77 @@ e->event.notify[i].uin = fix32(e->event.notify[i].uin); e->event.notify[i].status = fix32(e->event.notify[i].status); } - } + break; + } + + case GG_NOTIFY_REPLY60: + { + struct gg_notify_reply60 *n = (void*) p; + unsigned int length = h->length, i = 0; + + gg_debug(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"); + goto fail; + } + + e->event.notify60[0].uin = 0; + + while (length >= sizeof(struct gg_notify_reply60)) { + uin_t uin = 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 = 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 (GG_S_D(n->status)) { + unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60)); - if (h->type == GG_STATUS) { - struct gg_status *s = p; + if (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"); + goto fail; + } + + memcpy(e->event.notify60[i].descr, (char*) n + sizeof(struct gg_notify_reply60) + 1, descr_len); + 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 -= 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"); + free(e->event.notify60); + goto fail; + } + + e->event.notify60 = (void*) tmp; + e->event.notify60[++i].uin = 0; + } + + break; + } + + case GG_STATUS: + { + struct gg_status *s = (void *)p; gg_debug(GG_DEBUG_MISC, "-- received a status change\n"); @@ -798,10 +1030,55 @@ e->event.status.uin = fix32(e->event.status.uin); e->event.status.status = fix32(e->event.status.status); } - } + 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"); + + if (h->length < sizeof(*s)) + break; + + uin = 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 = 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 (h->type == GG_SEND_MSG_ACK) { - struct gg_send_msg_ack *s = p; + if (uin & 0x40000000) + e->event.status60.version |= GG_HAS_AUDIO_MASK; + + if (h->length > sizeof(*s)) { + int len = h->length - sizeof(*s); + char *buf = malloc(len + 1); + + if (buf) { + memcpy(buf, (char*) p + sizeof(*s), len); + buf[len] = 0; + } + + e->event.status60.descr = buf; + + if (len > 4 && p[h->length - 5] == 0) + e->event.status60.time = *((int*) (p + h->length - 4)); + } + + break; + } + + case GG_SEND_MSG_ACK: + { + struct gg_send_msg_ack *s = (void *)p; gg_debug(GG_DEBUG_MISC, "-- received a message ack\n"); @@ -811,17 +1088,73 @@ e->event.ack.recipient = fix32(s->recipient); e->event.ack.seq = fix32(s->seq); } - } + break; + } - if (h->type == GG_PONG) { + case GG_PONG: + { gg_debug(GG_DEBUG_MISC, "-- received a pong\n"); ping_outstanding = 0; sess->last_pong = time(NULL); + break; + } + + case GG_USERLIST_REPLY: + { + gg_debug(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 + * gdy otrzymano wszystkie odpowiedzi */ + if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) { + if (--sess->userlist_blocks) + break; + + p[0] = GG_USERLIST_PUT_REPLY; + } + + if (h->length > 1) { + char *tmp; + 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); + + 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"); + free(sess->userlist_reply); + sess->userlist_reply = NULL; + goto fail; + } + + sess->userlist_reply = tmp; + sess->userlist_reply[len + h->length - 1] = 0; + memcpy(sess->userlist_reply + len, p + 1, h->length - 1); + } + + if (p[0] == GG_USERLIST_GET_MORE_REPLY) + break; + + e->type = GG_EVENT_USERLIST; + e->event.userlist.type = p[0]; + e->event.userlist.reply = sess->userlist_reply; + sess->userlist_reply = NULL; + + break; + } + + default: + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type); } free(h); return 0; + +fail: + free(h); + return -1; } /* @@ -1023,6 +1356,7 @@ gg_debug(GG_DEBUG_TRAFFIC, "-- received http data (%s)\n", buf); + /* analizujemy otrzymane dane. */ tmp = buf; while (*tmp && *tmp != ' ') @@ -1049,6 +1383,7 @@ a.s_addr = inet_addr(host); sess->server_ip = a.s_addr; + #if 0 /* We need to watch this non-blocking socket so lets use gaim_proxy_connect in gg.c - Herman */ @@ -1104,7 +1439,7 @@ { struct gg_header *h; struct gg_welcome *w; - struct gg_login l; + struct gg_login60 l; unsigned int hash; char *password = sess->password; @@ -1133,10 +1468,8 @@ w = (struct gg_welcome *)((void *)h + sizeof(struct gg_header)); w->key = fix32(w->key); - - for (hash = 1; *password; password++) - hash *= (*password) + 1; - hash *= w->key; + + hash = gg_login_hash(password, w->key); gg_debug(GG_DEBUG_DUMP, "%%%% klucz serwera %.4x, hash hasła %.8x\n", w->key, hash); @@ -1148,13 +1481,13 @@ l.uin = fix32(sess->uin); l.hash = fix32(hash); l.status = fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); - l.dunno = fix32(0x0b); + l.version = fix32(0x20); l.local_ip = 0; l.local_port = 0; gg_debug(GG_DEBUG_TRAFFIC, "-- sending GG_LOGIN packet\n"); - if (gg_send_packet(sess->fd, GG_LOGIN, &l, sizeof(l), NULL, 0) == -1) { + if (gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), NULL, 0) == -1) { gg_debug(GG_DEBUG_TRAFFIC, "-- oops, failed. errno = %d (%s)\n", errno, strerror(errno)); close(sess->fd); diff -r 5205743477bb -r ce90b119b103 src/protocols/gg/libgg.h --- a/src/protocols/gg/libgg.h Fri Apr 23 17:13:33 2004 +0000 +++ b/src/protocols/gg/libgg.h Fri Apr 23 17:24:19 2004 +0000 @@ -1,4 +1,4 @@ -/* $Id: libgg.h 8872 2004-01-21 05:34:32Z lschiere $ */ +/* $Id: libgg.h 9537 2004-04-23 17:24:19Z lschiere $ */ /* * (C) Copyright 2001 Wojtek Kaniewski , @@ -29,6 +29,7 @@ #define INADDR_NONE 0xffffffff #endif +#include #include /* @@ -64,6 +65,10 @@ char *recv_buf; /* bufor na otrzymywane pakiety */ int recv_done; /* ile już wczytano do bufora */ int recv_left; /* i ile jeszcze trzeba wczytać */ + + char *userlist_reply; /* fragment odpowiedzi listy kontaktów */ + + int userlist_blocks; /* na ile kawałków podzielono listę kontaktów */ }; /* @@ -153,9 +158,12 @@ struct gg_session *gg_login(uin_t uin, char *password, int async); void gg_free_session(struct gg_session *sess); void gg_logoff(struct gg_session *sess); +int gg_write(struct gg_session *sess, const char *buf, int length); int gg_change_status(struct gg_session *sess, int status); -int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, char *message); +int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message); +int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen); int gg_ping(struct gg_session *sess); +int gg_userlist_request(struct gg_session *sess, char type, const char *request); struct gg_notify_reply { uin_t uin; /* numerek */ @@ -170,6 +178,39 @@ #endif ; +#define GG_NOTIFY_REPLY60 0x0011 + +struct gg_notify_reply60 { + uint32_t uin; /* numerek plus flagi w MSB */ + uint8_t status; /* status danej osoby */ + uint32_t remote_ip; /* adres ip delikwenta */ + uint16_t remote_port; /* port, na którym słucha klient */ + uint8_t version; /* wersja klienta */ + uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ + uint8_t dunno1; /* 0x00 */ +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +; + +#define GG_STATUS60 0x000f + +struct gg_status60 { + uint32_t uin; /* numerek plus flagi w MSB */ + uint8_t status; /* status danej osoby */ + uint32_t remote_ip; /* adres ip delikwenta */ + uint16_t remote_port; /* port, na którym słucha klient */ + uint8_t version; /* wersja klienta */ + uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ + uint8_t dunno1; /* 0x00 */ +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +; + + struct gg_status { uin_t uin; /* numerek */ unsigned long status; /* nowy stan */ @@ -186,7 +227,10 @@ GG_EVENT_STATUS, GG_EVENT_ACK, GG_EVENT_CONN_FAILED, - GG_EVENT_CONN_SUCCESS + GG_EVENT_CONN_SUCCESS, + GG_EVENT_STATUS60, /* ktoś zmienił stan w GG 6.0 */ + GG_EVENT_NOTIFY60, /* ktoś się pojawił w GG 6.0 */ + GG_EVENT_USERLIST, /* odpowiedź listy kontaktów w GG 6.0 */ }; /* @@ -228,7 +272,31 @@ unsigned char *message; } msg; struct gg_notify_reply *notify; + struct { /* @notify60 informacja o liście kontaktów -- GG_EVENT_NOTIFY60 */ + uin_t uin; /* numer */ + int status; /* stan */ + uint32_t remote_ip; /* adres ip */ + uint16_t remote_port; /* port */ + int version; /* wersja klienta */ + int image_size; /* maksymalny rozmiar grafiki w KiB */ + char *descr; /* opis stanu */ + time_t time; /* czas powrotu */ + } *notify60; struct gg_status status; + struct { /* @status60 zmiana stanu -- GG_EVENT_STATUS60 */ + uin_t uin; /* numer */ + int status; /* nowy stan */ + uint32_t remote_ip; /* adres ip */ + uint16_t remote_port; /* port */ + int version; /* wersja klienta */ + int image_size; /* maksymalny rozmiar grafiki w KiB */ + char *descr; /* opis stanu */ + time_t time; /* czas powrotu */ + } status60; + struct { /* @userlist odpowiedź listy kontaktów serwera */ + char type; /* rodzaj odpowiedzi */ + char *reply; /* treść odpowiedzi */ + } userlist; struct { uin_t recipient; int status; @@ -387,6 +455,7 @@ #define GG_DEFAULT_PORT 8074 #define GG_HTTPS_PORT 443 #define GG_HTTP_USERAGENT "Mozilla/4.0 (compatible MSIE 5.0; Windows 98; I)" +#define GG_HAS_AUDIO_MASK 0x40000000 struct gg_header { unsigned long type; /* typ pakietu */ @@ -413,7 +482,7 @@ uin_t uin; /* twój numerek */ unsigned long hash; /* hash hasła */ unsigned long status; /* status na dzień dobry */ - unsigned long dunno; /* == 0x0b */ + unsigned long version; /* == 0x20 */ unsigned long local_ip; /* mój adres ip */ unsigned short local_port; /* port, na którym słucham */ } @@ -422,18 +491,49 @@ #endif ; +#define GG_LOGIN60 0x0015 + +struct gg_login60 { + uint32_t uin; /* mój numerek */ + uint32_t hash; /* hash hasła */ + uint32_t status; /* status na dzień dobry */ + uint32_t version; /* moja wersja klienta */ + uint8_t dunno1; /* 0x00 */ + uint32_t local_ip; /* mój adres ip */ + uint16_t local_port; /* port, na którym słucham */ + uint32_t external_ip; /* zewnętrzny adres ip */ + uint16_t external_port; /* zewnętrzny port */ + uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ + uint8_t dunno2; /* 0xbe */ +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +; + #define GG_LOGIN_OK 0x0003 #define GG_LOGIN_FAILED 0x0009 #define GG_NEW_STATUS 0x0002 -#define GG_STATUS_NOT_AVAIL 0x0001 /* rozłączony */ -#define GG_STATUS_AVAIL 0x0002 /* dostępny */ -#define GG_STATUS_BUSY 0x0003 /* zajęty */ -#define GG_STATUS_INVISIBLE 0x0014 /* niewidoczny (GG 4.6) */ +#define GG_STATUS_NOT_AVAIL 0x0001 /* niedostępny */ +#define GG_STATUS_NOT_AVAIL_DESCR 0x0015 /* niedostępny z opisem (4.8) */ +#define GG_STATUS_AVAIL 0x0002 /* dostępny */ +#define GG_STATUS_AVAIL_DESCR 0x0004 /* dostępny z opisem (4.9) */ +#define GG_STATUS_BUSY 0x0003 /* zajęty */ +#define GG_STATUS_BUSY_DESCR 0x0005 /* zajęty z opisem (4.8) */ +#define GG_STATUS_INVISIBLE 0x0014 /* niewidoczny (4.6) */ +#define GG_STATUS_INVISIBLE_DESCR 0x0016 /* niewidoczny z opisem (4.9) */ +#define GG_STATUS_BLOCKED 0x0006 /* zablokowany */ -#define GG_STATUS_FRIENDS_MASK 0x8000 /* tylko dla znajomych (GG 4.6) */ +#define GG_STATUS_FRIENDS_MASK 0x8000 /* tylko dla znajomych (4.6) */ + +/* GG_S() stan bez uwzględnienia trybu tylko dla znajomych */ +#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK) + +/* GG_S_D() stan opisowy */ +#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || GG_S(x) == GG_STATUS_AVAIL_DESCR || GG_S(x) == GG_STATUS_BUSY_DESCR || GG_S(x) == GG_STATUS_INVISIBLE_DESCR) struct gg_new_status { unsigned long status; /* na jaki zmienić? */ @@ -523,6 +623,45 @@ #define GG_PONG 0x0007 +#define GG_USERLIST_REQUEST 0x0016 + +#define GG_USERLIST_PUT 0x00 +#define GG_USERLIST_PUT_MORE 0x01 +#define GG_USERLIST_GET 0x02 + +struct gg_userlist_request { + uint8_t type; +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +; + +#define GG_USERLIST_REPLY 0x0010 + +#define GG_USERLIST_PUT_REPLY 0x00 +#define GG_USERLIST_PUT_MORE_REPLY 0x02 +#define GG_USERLIST_GET_REPLY 0x06 +#define GG_USERLIST_GET_MORE_REPLY 0x04 + +struct gg_userlist_reply { + uint8_t type; +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +; + +/* listy */ + +struct list { + void *data; + struct list *next; +}; + +typedef struct list * list_t; + + #ifdef __cplusplus } #endif