diff options
Diffstat (limited to 'protocols/Gadu-Gadu/src/libgadu')
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/common.cpp | 1 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/dcc.cpp | 1 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/dcc7.cpp | 74 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/debug.cpp | 70 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/debug.h | 2 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/deflate.cpp | 226 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/deflate.h | 29 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/encoding.cpp | 38 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/events.cpp | 162 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/handlers.cpp | 103 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/http.cpp | 9 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/internal.h | 1 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/libgadu.cpp | 356 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/libgadu.h | 143 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/message.cpp | 10 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/obsolete.cpp | 1 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/protocol.h | 28 | ||||
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/resolver.cpp | 30 |
18 files changed, 877 insertions, 407 deletions
diff --git a/protocols/Gadu-Gadu/src/libgadu/common.cpp b/protocols/Gadu-Gadu/src/libgadu/common.cpp index 07e6a2646b..3ca168abee 100644 --- a/protocols/Gadu-Gadu/src/libgadu/common.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/common.cpp @@ -293,6 +293,7 @@ SOCKET gg_connect(void *addr, int port, int async) }
}
+ memset(&sin, 0, sizeof(sin));
sin.sin_port = htons((uint16_t)port);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = a->s_addr;
diff --git a/protocols/Gadu-Gadu/src/libgadu/dcc.cpp b/protocols/Gadu-Gadu/src/libgadu/dcc.cpp index 5c89476efc..3e65559964 100644 --- a/protocols/Gadu-Gadu/src/libgadu/dcc.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/dcc.cpp @@ -442,6 +442,7 @@ struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port) port = GG_DEFAULT_DCC_PORT;
while (!bound) {
+ memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(port);
diff --git a/protocols/Gadu-Gadu/src/libgadu/dcc7.cpp b/protocols/Gadu-Gadu/src/libgadu/dcc7.cpp index 465a3cae4a..b9f6ef2faa 100644 --- a/protocols/Gadu-Gadu/src/libgadu/dcc7.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/dcc7.cpp @@ -154,7 +154,7 @@ static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_ for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) { if (empty) { - if (tmp->peer_uin == uin && !tmp->state == GG_STATE_WAITING_FOR_ACCEPT) + if (tmp->peer_uin == uin && tmp->state == GG_STATE_WAITING_FOR_ACCEPT) return tmp; } else { if (!memcmp(&tmp->cid, &id, sizeof(id))) @@ -228,11 +228,12 @@ static int gg_dcc7_connect(struct gg_dcc7 *dcc) * \internal Tworzy gniazdo nasłuchujące dla połączenia bezpośredniego * * \param dcc Struktura połączenia - * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego) + * \param addr Preferowany adres (jeśli równy 0, nasłuchujemy na wszystkich interfejsach) + * \param port Preferowany port (jeśli równy 0, nasłuchujemy na losowym) * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ -static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint16_t port) +static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint32_t addr, uint16_t port) { struct sockaddr_in sin; int sin_len = sizeof(sin); @@ -252,12 +253,13 @@ static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint16_t port) return -1; } + memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_addr.s_addr = addr; sin.sin_port = htons(port); if (bind(fd, (struct sockaddr*) &sin, sizeof(sin)) == -1) { - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to bind to port %d\n", port); + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to bind to %s:%d\n", inet_ntoa(sin.sin_addr), port); goto fail; } @@ -272,6 +274,7 @@ static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint16_t port) } dcc->fd = fd; + dcc->local_addr = sin.sin_addr.s_addr; dcc->local_port = ntohs(sin.sin_port); dcc->state = GG_STATE_LISTENING; @@ -298,39 +301,36 @@ static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc) { struct gg_dcc7_info pkt; uint16_t external_port; - uint16_t local_port; - uint32_t count; + uint32_t external_addr; + struct in_addr addr; gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc); if (dcc == NULL) return -1; - if (!dcc->sess->client_port) - local_port = dcc->sess->external_port; - else - local_port = dcc->sess->client_port; - - if (gg_dcc7_listen(dcc, local_port) == -1) + if (gg_dcc7_listen(dcc, dcc->sess->client_addr, dcc->sess->client_port) == -1) return -1; - if (!dcc->sess->external_port || dcc->local_port != local_port) - external_port = dcc->local_port; - else + if (dcc->sess->external_port != 0) external_port = dcc->sess->external_port; - - if (!dcc->sess->external_addr || dcc->local_port != local_port) - dcc->local_addr = dcc->sess->client_addr; else - dcc->local_addr = dcc->sess->external_addr; + external_port = dcc->local_port; + + if (dcc->sess->external_addr != 0) + external_addr = dcc->sess->external_addr; + else + external_addr = dcc->local_addr; + + addr.s_addr = external_addr; - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() sending IP address %s and port %d\n", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), external_port); + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() sending IP address %s and port %d\n", inet_ntoa(addr), external_port); memset(&pkt, 0, sizeof(pkt)); pkt.uin = gg_fix32(dcc->peer_uin); pkt.type = GG_DCC7_TYPE_P2P; pkt.id = dcc->cid; - snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), external_port); - snprintf((char*) pkt.hash, sizeof(pkt.hash), "%u", dcc->local_addr + external_port * rand()); + snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(addr), external_port); + snprintf((char*) pkt.hash, sizeof(pkt.hash), "%u", external_addr + external_port * rand()); return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL); } @@ -446,7 +446,7 @@ static struct gg_dcc7 *gg_dcc7_send_file_common(struct gg_session *sess, uin_t r dcc->size = (unsigned int)size; dcc->seek = seek; - strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN - 1); + strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN); dcc->filename[GG_DCC7_FILENAME_LEN] = 0; memcpy(dcc->hash, hash, GG_DCC7_HASH_LEN); @@ -699,7 +699,9 @@ int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, const void *p s.uin_to = gg_fix32(tmp->peer_uin); s.size = gg_fix32(tmp->size); - strncpy((char*) s.filename, (char*) tmp->filename, GG_DCC7_FILENAME_LEN); + /* Uwaga: To nie jest ciąg kończony zerem. + * Note: This is not a null-terminated string. */ + strncpy((char*) s.filename, (char*) tmp->filename, sizeof(s.filename)); tmp->state = GG_STATE_WAITING_FOR_ACCEPT; tmp->timeout = GG_DCC7_TIMEOUT_FILE_ACK; @@ -805,6 +807,8 @@ int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, const void if (dcc->state == GG_STATE_WAITING_FOR_INFO) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() waiting for info so send one\n"); gg_dcc7_listen_and_send_info(dcc); + e->type = GG_EVENT_DCC7_PENDING; + e->event.dcc7_pending.dcc7 = dcc; return 0; } @@ -1025,7 +1029,7 @@ int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, const void * } dcc->size = gg_fix32(p->size); - strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN - 1); + strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN); dcc->filename[GG_DCC7_FILENAME_LEN] = 0; memcpy(dcc->hash, p->hash, GG_DCC7_HASH_LEN); @@ -1489,6 +1493,9 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc) dcc->check = GG_CHECK_WRITE; dcc->timeout = GG_DEFAULT_TIMEOUT; + e->type = GG_EVENT_DCC7_PENDING; + e->event.dcc7_pending.dcc7 = dcc; + return e; } @@ -1538,6 +1545,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc) char buf[256]; struct gg_dcc7_relay_reply *pkt; struct gg_dcc7_relay_reply_server srv; + size_t max_relay_count = (sizeof(buf) - sizeof(*pkt)) / sizeof(srv); int res; int i; @@ -1555,7 +1563,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc) pkt = (struct gg_dcc7_relay_reply*) buf; - if (gg_fix32(pkt->magic) != GG_DCC7_RELAY_REPLY || gg_fix32(pkt->rcount) < 1 || gg_fix32(pkt->rcount > 256) || gg_fix32(pkt->len) < sizeof(*pkt) + gg_fix32(pkt->rcount) * sizeof(srv)) { + if (gg_fix32(pkt->magic) != GG_DCC7_RELAY_REPLY || gg_fix32(pkt->rcount) < 1 || gg_fix32(pkt->rcount) > 256 || gg_fix32(pkt->len) < sizeof(*pkt) + gg_fix32(pkt->rcount) * sizeof(srv)) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_wathc_fd() invalid reply\n"); errno = EINVAL; e->type = GG_EVENT_DCC7_ERROR; @@ -1571,6 +1579,18 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc) dcc->relay_index = 0; dcc->relay_count = gg_fix32(pkt->rcount); + + if (dcc->relay_count > 0xffff || + (size_t)dcc->relay_count > max_relay_count) + { + gg_debug_dcc(dcc, GG_DEBUG_MISC, + "// gg_dcc7_watch_fd() relay_count out " + "of bounds (%d)\n", dcc->relay_count); + dcc->relay_count = 0; + free(e); + return NULL; + } + dcc->relay_list = (gg_dcc7_relay*)malloc(dcc->relay_count * sizeof(gg_dcc7_relay_t)); if (dcc->relay_list == NULL) { diff --git a/protocols/Gadu-Gadu/src/libgadu/debug.cpp b/protocols/Gadu-Gadu/src/libgadu/debug.cpp index 5b7c0c6577..fcadfe3ed1 100644 --- a/protocols/Gadu-Gadu/src/libgadu/debug.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/debug.cpp @@ -268,8 +268,74 @@ const char *gg_debug_state(enum gg_state_t state) GG_DEBUG_STATE(GG_STATE_CONNECTING_RELAY) GG_DEBUG_STATE(GG_STATE_READING_RELAY) GG_DEBUG_STATE(GG_STATE_DISCONNECTING) +#undef GG_DEBUG_STATE - // Celowo nie ma default, żeby kompilator wyłapał brakujące stany + /* Celowo nie ma default, żeby kompilator wyłapał brakujące stany */ + + } + + return NULL; +} + +/** + * \internal Zwraca ciąg z nazwą podanego zdarzenia. + * + * \param event Zdarzenie. + * + * \return Ciąg z nazwą zdarzenia + * + * \ingroup debug + */ +const char *gg_debug_event(enum gg_event_t event) +{ + switch (event) { +#define GG_DEBUG_EVENT(x) case x: return #x; + GG_DEBUG_EVENT(GG_EVENT_NONE) + GG_DEBUG_EVENT(GG_EVENT_MSG) + GG_DEBUG_EVENT(GG_EVENT_NOTIFY) + GG_DEBUG_EVENT(GG_EVENT_NOTIFY_DESCR) + GG_DEBUG_EVENT(GG_EVENT_STATUS) + GG_DEBUG_EVENT(GG_EVENT_ACK) + GG_DEBUG_EVENT(GG_EVENT_PONG) + GG_DEBUG_EVENT(GG_EVENT_CONN_FAILED) + GG_DEBUG_EVENT(GG_EVENT_CONN_SUCCESS) + GG_DEBUG_EVENT(GG_EVENT_DISCONNECT) + GG_DEBUG_EVENT(GG_EVENT_DCC_NEW) + GG_DEBUG_EVENT(GG_EVENT_DCC_ERROR) + GG_DEBUG_EVENT(GG_EVENT_DCC_DONE) + GG_DEBUG_EVENT(GG_EVENT_DCC_CLIENT_ACCEPT) + GG_DEBUG_EVENT(GG_EVENT_DCC_CALLBACK) + GG_DEBUG_EVENT(GG_EVENT_DCC_NEED_FILE_INFO) + GG_DEBUG_EVENT(GG_EVENT_DCC_NEED_FILE_ACK) + GG_DEBUG_EVENT(GG_EVENT_DCC_NEED_VOICE_ACK) + GG_DEBUG_EVENT(GG_EVENT_DCC_VOICE_DATA) + GG_DEBUG_EVENT(GG_EVENT_PUBDIR50_SEARCH_REPLY) + GG_DEBUG_EVENT(GG_EVENT_PUBDIR50_READ) + GG_DEBUG_EVENT(GG_EVENT_PUBDIR50_WRITE) + GG_DEBUG_EVENT(GG_EVENT_STATUS60) + GG_DEBUG_EVENT(GG_EVENT_NOTIFY60) + GG_DEBUG_EVENT(GG_EVENT_USERLIST) + GG_DEBUG_EVENT(GG_EVENT_IMAGE_REQUEST) + GG_DEBUG_EVENT(GG_EVENT_IMAGE_REPLY) + GG_DEBUG_EVENT(GG_EVENT_DCC_ACK) + GG_DEBUG_EVENT(GG_EVENT_DCC7_NEW) + GG_DEBUG_EVENT(GG_EVENT_DCC7_ACCEPT) + GG_DEBUG_EVENT(GG_EVENT_DCC7_REJECT) + GG_DEBUG_EVENT(GG_EVENT_DCC7_CONNECTED) + GG_DEBUG_EVENT(GG_EVENT_DCC7_ERROR) + GG_DEBUG_EVENT(GG_EVENT_DCC7_DONE) + GG_DEBUG_EVENT(GG_EVENT_DCC7_PENDING) + GG_DEBUG_EVENT(GG_EVENT_XML_EVENT) + GG_DEBUG_EVENT(GG_EVENT_DISCONNECT_ACK) + GG_DEBUG_EVENT(GG_EVENT_TYPING_NOTIFICATION) + GG_DEBUG_EVENT(GG_EVENT_USER_DATA) + GG_DEBUG_EVENT(GG_EVENT_MULTILOGON_MSG) + GG_DEBUG_EVENT(GG_EVENT_MULTILOGON_INFO) + GG_DEBUG_EVENT(GG_EVENT_USERLIST100_VERSION) + GG_DEBUG_EVENT(GG_EVENT_USERLIST100_REPLY) +#undef GG_DEBUG_EVENT + + /* Celowo nie ma default, żeby kompilator wyłapał brakujące zdarzenia */ } @@ -294,7 +360,7 @@ void gg_debug_session(struct gg_session *gs, int level, const char *format, ...) } #undef gg_debug_dump -void gg_debug_dump(struct gg_session *gs, int level, const char *buf, int len) +void gg_debug_dump(struct gg_session *gs, int level, const char *buf, size_t len) { } diff --git a/protocols/Gadu-Gadu/src/libgadu/debug.h b/protocols/Gadu-Gadu/src/libgadu/debug.h index 8c51853b3e..917705eef9 100644 --- a/protocols/Gadu-Gadu/src/libgadu/debug.h +++ b/protocols/Gadu-Gadu/src/libgadu/debug.h @@ -22,6 +22,8 @@ #include "libgadu.h" const char *gg_debug_state(enum gg_state_t state); +const char *gg_debug_event(enum gg_event_t event); void gg_debug_dump(struct gg_session *sess, int level, const char *buf, size_t len); +void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap); #endif /* LIBGADU_DEBUG_H */ diff --git a/protocols/Gadu-Gadu/src/libgadu/deflate.cpp b/protocols/Gadu-Gadu/src/libgadu/deflate.cpp new file mode 100644 index 0000000000..46f36f1e69 --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/deflate.cpp @@ -0,0 +1,226 @@ +/* $Id$ */ + +/* + * (C) Copyright 2011 Bartosz Brachaczek <b.brachaczek@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/** + * \file deflate.c + * + * \brief Funkcje kompresji Deflate + */ + +#ifndef _WIN64 +#define _USE_32BIT_TIME_T +#endif + +#ifdef _WIN32 +#include "win32.h" +#endif /* _WIN32 */ + +#include <stdlib.h> +#include <string.h> + +#include "libgadu.h" +#include "deflate.h" + +#ifdef GG_CONFIG_HAVE_ZLIB +#include <zlib.h> +#endif + +/** + * \internal Kompresuje dane wejściowe algorytmem Deflate z najwyższym + * stopniem kompresji, tak samo jak oryginalny klient. + * + * Wynik funkcji należy zwolnić za pomocą \c free. + * + * \param in Ciąg znaków do skompresowania, zakończony \c \\0 + * \param out_lenp Wskaźnik na zmienną, do której zostanie zapisana + * długość bufora wynikowego + * + * \return Skompresowany ciąg znaków lub \c NULL w przypadku niepowodzenia. + */ +unsigned char *gg_deflate(const char *in, size_t *out_lenp) +{ +#ifdef GG_CONFIG_HAVE_ZLIB + int ret; + z_stream strm; + unsigned char *out, *out2; + size_t out_len; + + if (in == NULL || out_lenp == NULL) + return NULL; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = strlen(in); + strm.next_in = (unsigned char*) in; + + ret = deflateInit(&strm, Z_BEST_COMPRESSION); + if (ret != Z_OK) { + gg_debug(GG_DEBUG_MISC, "// gg_deflate() deflateInit() failed (%d)\n", ret); + return NULL; + } + + out_len = deflateBound(&strm, strm.avail_in); + out = (unsigned char*)malloc(out_len); + + if (out == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for output data (%d)\n", out_len); + goto fail; + } + + strm.avail_out = out_len; + strm.next_out = out; + + for (;;) { + ret = deflate(&strm, Z_FINISH); + + if (ret == Z_STREAM_END) + break; + + /* raczej nie powinno się zdarzyć przy Z_FINISH i out_len == deflateBound(), + * ale dokumentacja zlib nie wyklucza takiej możliwości */ + if (ret == Z_OK) { + out_len *= 2; + out2 = (unsigned char*)realloc(out, out_len); + + if (out2 == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for output data (%d)\n", out_len); + goto fail; + } + + out = out2; + + strm.avail_out = out_len / 2; + strm.next_out = out + out_len / 2; + } else { + gg_debug(GG_DEBUG_MISC, "// gg_deflate() deflate() failed (ret=%d, msg=%s)\n", ret, strm.msg != NULL ? strm.msg : "no error message provided"); + goto fail; + } + } + + out_len = strm.total_out; + out2 = (unsigned char*)realloc(out, out_len); + + if (out2 == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for output data (%d)\n", out_len); + goto fail; + } + + *out_lenp = out_len; + deflateEnd(&strm); + + return out2; + +fail: + *out_lenp = 0; + deflateEnd(&strm); + free(out); +#endif + return NULL; +} + +/** + * \internal Dekompresuje dane wejściowe w formacie Deflate. + * + * Wynik funkcji należy zwolnić za pomocą \c free. + * + * \param in Bufor danych skompresowanych algorytmem Deflate + * \param length Długość bufora wejściowego + * + * \note Dokleja \c \\0 na końcu bufora wynikowego. + * + * \return Zdekompresowany ciąg znaków, zakończony \c \\0, + * lub \c NULL w przypadku niepowodzenia. + */ +char *gg_inflate(const unsigned char *in, size_t length) +{ +#ifdef GG_CONFIG_HAVE_ZLIB + int ret; + z_stream strm; + char *out = NULL, *out2; + size_t out_len = 1024; + int first = 1; + + if (in == NULL) + return NULL; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = length; + strm.next_in = (unsigned char*) in; + + ret = inflateInit(&strm); + if (ret != Z_OK) { + gg_debug(GG_DEBUG_MISC, "// gg_inflate() inflateInit() failed (%d)\n", ret); + return NULL; + } + + do { + out_len *= 2; + out2 = (char*)realloc(out, out_len); + + if (out2 == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_inflate() not enough memory for output data (%d)\n", out_len); + goto fail; + } + + out = out2; + + if (first) { + strm.avail_out = out_len; + strm.next_out = (unsigned char*) out; + } else { + strm.avail_out = out_len / 2; + strm.next_out = (unsigned char*) out + out_len / 2; + } + + ret = inflate(&strm, Z_NO_FLUSH); + + if (ret != Z_OK && ret != Z_STREAM_END) { + gg_debug(GG_DEBUG_MISC, "// gg_inflate() inflate() failed (ret=%d, msg=%s)\n", ret, strm.msg != NULL ? strm.msg : "no error message provided"); + goto fail; + } + + first = 0; + } while (ret != Z_STREAM_END); + + /* rezerwujemy ostatni znak na NULL-a */ + out_len = strm.total_out + 1; + out2 = (char*)realloc(out, out_len); + + if (out2 == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_inflate() not enough memory for output data (%d)\n", out_len); + goto fail; + } + + out = out2; + out[out_len - 1] = '\0'; + + inflateEnd(&strm); + + return out; + +fail: + inflateEnd(&strm); + free(out); +#endif + return NULL; +} diff --git a/protocols/Gadu-Gadu/src/libgadu/deflate.h b/protocols/Gadu-Gadu/src/libgadu/deflate.h new file mode 100644 index 0000000000..9e2f78053e --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/deflate.h @@ -0,0 +1,29 @@ +/* $Id$ */ + +/* + * (C) Copyright 2011 Bartosz Brachaczek <b.brachaczek@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_DEFLATE_H +#define LIBGADU_DEFLATE_H + +#include "libgadu.h" + +unsigned char *gg_deflate(const char *in, size_t *out_lenp); +char *gg_inflate(const unsigned char *in, size_t length); + +#endif /* LIBGADU_DEFLATE_H */ diff --git a/protocols/Gadu-Gadu/src/libgadu/encoding.cpp b/protocols/Gadu-Gadu/src/libgadu/encoding.cpp index fa6af7eb6d..3d157573d1 100644 --- a/protocols/Gadu-Gadu/src/libgadu/encoding.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/encoding.cpp @@ -26,6 +26,7 @@ #include <errno.h> #include "libgadu.h" +#include "encoding.h" /** * \file encoding.c @@ -38,22 +39,22 @@ */ static const uint16_t table_cp1250[] = { - 0x20ac, '?', 0x201a, '?', 0x201e, 0x2026, 0x2020, 0x2021, - '?', 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179, - '?', 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, - '?', 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a, - 0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7, - 0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b, - 0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7, - 0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c, - 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, - 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, - 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, - 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, - 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, - 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, - 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, - 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, + 0x20ac, '?', 0x201a, '?', 0x201e, 0x2026, 0x2020, 0x2021, + '?', 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179, + '?', 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, + '?', 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a, + 0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7, + 0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b, + 0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7, + 0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c, + 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, + 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, + 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, + 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, + 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, + 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, + 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, + 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, }; /** @@ -139,11 +140,8 @@ static char *gg_encoding_convert_utf8_cp1250(const char *src, int src_length, in uint32_t uc = 0, uc_min = 0; for (i = 0, len = 0; (src[i] != 0) && (i < src_length); i++) { - if ((src[i] & 0xc0) == 0xc0) { + if ((src[i] & 0xc0) != 0x80) len++; - } else if ((src[i] & 0x80) == 0x00) { - len++; - } } if ((dst_length != -1) && (len > dst_length)) diff --git a/protocols/Gadu-Gadu/src/libgadu/events.cpp b/protocols/Gadu-Gadu/src/libgadu/events.cpp index 9cfd036730..2374ade04c 100644 --- a/protocols/Gadu-Gadu/src/libgadu/events.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/events.cpp @@ -178,6 +178,10 @@ void gg_event_free(struct gg_event *e) break;
}
+
+ case GG_EVENT_USERLIST100_REPLY:
+ free(e->event.userlist100_reply.reply);
+ break;
}
free(e);
@@ -268,9 +272,9 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) 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;
+ e->event.failure = GG_FAILURE_CONNECTING;
+
+ goto fail;
}
if (res == sess->send_left) {
@@ -309,7 +313,7 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) if (failed) {
errno = errno2;
- goto fail_resolving;
+ goto fail_proxy_hub;
}
/* jeśli jesteśmy w resolverze i mamy ustawiony port
@@ -336,7 +340,7 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) /* 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;
+ goto fail_proxy_hub;
}
/* jeśli podano serwer i łączmy się przez proxy,
@@ -367,12 +371,8 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) /* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił
* przypadkiem jakiś błąd. */
if (sess->async && (gg_getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
- 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to %s failed (errno=%d, %s)\n", (sess->proxy_addr && sess->proxy_port) ? "proxy" : "hub", res, strerror(res));
+ goto fail_proxy_hub;
}
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
@@ -384,7 +384,7 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) if (client == NULL) {
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
- goto fail_connecting;
+ goto fail;
}
if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port)
@@ -431,13 +431,7 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) * stało się coś złego. */
if (gg_sock_write(sess->fd, buf, (int)strlen(buf)) < (signed)strlen(buf)) {
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;
- sess->state = GG_STATE_IDLE;
- gg_sock_close(sess->fd);
- sess->fd = -1;
- break;
+ goto fail_proxy_hub;
}
sess->state = GG_STATE_READING_DATA;
@@ -463,7 +457,7 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) /* sprawdzamy, czy wszystko w porządku. */
if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) {
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
- goto fail_connecting;
+ goto fail_proxy_hub;
}
/* ignorujemy resztę nagłówka. */
@@ -471,7 +465,10 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) gg_read_line(sess->fd, buf, sizeof(buf) - 1);
/* czytamy pierwszą linię danych. */
- gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+ if (gg_read_line(sess->fd, buf, sizeof(buf) - 1) == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() read error\n");
+ goto fail_proxy_hub;
+ }
gg_chomp(buf);
/* jeśli pierwsza liczba w linii nie jest równa zeru,
@@ -503,6 +500,7 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) }
gg_sock_close(sess->fd);
+ sess->fd = -1;
gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
@@ -527,10 +525,16 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) port = atoi(tmp + 1);
}
+ if (strcmp(host, "") == 0) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid response\n");
+ e->event.failure = GG_FAILURE_HUB;
+ goto fail;
+ }
+
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;
+ e->event.failure = GG_FAILURE_UNAVAILABLE;
+ goto fail;
}
addr.s_addr = inet_addr(host);
@@ -541,7 +545,8 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
/* 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;
+ e->event.failure = GG_FAILURE_PROXY;
+ goto fail;
}
sess->state = GG_STATE_CONNECTING_GG;
@@ -557,7 +562,7 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) if (sess->server_addr == INADDR_NONE) {
if (sess->resolver_start(&sess->fd, &sess->resolver, host) == -1) {
gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
- goto fail_resolving;
+ goto fail;
}
sess->state = GG_STATE_RESOLVING_GG;
@@ -577,7 +582,8 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) /* ostatnia deska ratunku zawiodła?
* w takim razie zwijamy manatki. */
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
- goto fail_connecting;
+ e->event.failure = GG_FAILURE_CONNECTING;
+ goto fail;
}
}
@@ -609,7 +615,8 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) if (failed) {
errno = errno2;
- goto fail_resolving;
+ e->event.failure = GG_FAILURE_RESOLVING;
+ goto fail;
}
sess->server_addr = addr.s_addr;
@@ -625,7 +632,8 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) /* ostatnia deska ratunku zawiodła?
* w takim razie zwijamy manatki. */
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
- goto fail_connecting;
+ e->event.failure = GG_FAILURE_CONNECTING;
+ goto fail;
}
}
@@ -652,7 +660,8 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) * nie mamy czego próbować więcej. */
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));
- goto fail_connecting;
+ e->event.failure = GG_FAILURE_PROXY;
+ goto fail;
}
gg_sock_close(sess->fd);
@@ -672,21 +681,25 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) if (sess->ssl) {
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
- goto fail_connecting;
+ e->event.failure = GG_FAILURE_CONNECTING;
+ goto fail;
}
#endif
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;
+ if (sess->port == GG_HTTPS_PORT) {
+ e->event.failure = GG_FAILURE_CONNECTING;
+ goto fail;
+ }
sess->port = GG_HTTPS_PORT;
/* próbujemy na port 443. */
if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
- goto fail_connecting;
+ e->event.failure = GG_FAILURE_CONNECTING;
+ goto fail;
}
sess->state = GG_STATE_CONNECTING_GG;
@@ -722,7 +735,8 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) if (gg_sock_write(sess->fd, buf, (int)strlen(buf)) < (signed)strlen(buf)) {
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
free(auth);
- goto fail_connecting;
+ e->event.failure = GG_FAILURE_PROXY;
+ goto fail;
}
if (auth) {
@@ -730,7 +744,8 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) if (gg_sock_write(sess->fd, auth, (int)strlen(auth)) < (signed)strlen(auth)) {
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
free(auth);
- goto fail_connecting;
+ e->event.failure = GG_FAILURE_PROXY;
+ goto fail;
}
free(auth);
@@ -738,7 +753,8 @@ struct gg_event *gg_watch_fd(struct gg_session *sess) if (gg_sock_write(sess->fd, "\r\n", 2) < 2) {
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
- goto fail_connecting;
+ e->event.failure = GG_FAILURE_PROXY;
+ goto fail;
}
}
@@ -850,14 +866,14 @@ gnutls_handshake_repeat: const gnutls_datum_t *peers;
gnutls_x509_crt_t cert;
- if (gnutls_x509_crt_init(&cert) >= 0) {
+ if (gnutls_x509_crt_init(&cert) == 0) {
peers = gnutls_certificate_get_peers(GG_SESSION_GNUTLS(sess), &peer_count);
if (peers != NULL) {
char buf[256];
size_t size;
- if (gnutls_x509_crt_import(cert, &peers[0], GNUTLS_X509_FMT_DER) >= 0) {
+ if (gnutls_x509_crt_import(cert, &peers[0], GNUTLS_X509_FMT_DER) == 0) {
size = sizeof(buf);
gnutls_x509_crt_get_dn(cert, buf, &size);
gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf);
@@ -866,6 +882,8 @@ gnutls_handshake_repeat: gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf);
}
}
+
+ gnutls_x509_crt_deinit(cert);
}
}
@@ -999,23 +1017,17 @@ gnutls_handshake_repeat: if (gh == NULL) {
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
- if (errno == EAGAIN) {
- e->type = GG_EVENT_NONE;
- res = 0;
- } else {
- res = -1;
- }
-
- goto done;
- }
- if (gg_session_handle_packet(sess, gh->type, (const char *) gh + sizeof(struct gg_header), gh->length, e) == -1) {
- free(gh);
- res = -1;
- goto done;
- }
+ if (errno != EAGAIN)
+ goto fail;
+ } else {
+ if (gg_session_handle_packet(sess, gh->type, (const char *) gh + sizeof(struct gg_header), gh->length, e) == -1) {
+ free(gh);
+ goto fail;
+ }
free(gh);
+ }
sess->check = GG_CHECK_READ;
@@ -1023,40 +1035,38 @@ gnutls_handshake_repeat: }
}
-done:
- 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;
- }
+ if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED))
+ sess->check |= GG_CHECK_WRITE;
return e;
-fail_connecting:
+fail_proxy_hub:
+ if (sess->proxy_port)
+ e->event.failure = GG_FAILURE_PROXY;
+ else
+ e->event.failure = GG_FAILURE_HUB;
+
+fail:
+ sess->resolver_cleanup(&sess->resolver, 1);
+
+ sess->state = GG_STATE_IDLE;
+
if (sess->fd != -1) {
+ int errno2;
+
errno2 = errno;
gg_sock_close(sess->fd);
errno = errno2;
sess->fd = -1;
}
- e->type = GG_EVENT_CONN_FAILED;
- e->event.failure = GG_FAILURE_CONNECTING;
- sess->state = GG_STATE_IDLE;
- goto done;
-
-fail_resolving:
- e->type = GG_EVENT_CONN_FAILED;
- 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;
+ if (e->event.failure != 0) {
+ e->type = GG_EVENT_CONN_FAILED;
+ return e;
+ } else {
+ free(e);
+ return NULL;
+ }
}
/*
diff --git a/protocols/Gadu-Gadu/src/libgadu/handlers.cpp b/protocols/Gadu-Gadu/src/libgadu/handlers.cpp index da09c40b5f..3c0018c8c6 100644 --- a/protocols/Gadu-Gadu/src/libgadu/handlers.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/handlers.cpp @@ -51,6 +51,7 @@ #include "encoding.h" #include "message.h" #include "internal.h" +#include "deflate.h" #include <errno.h> #ifndef _WIN32 @@ -97,6 +98,8 @@ static int gg_session_handle_welcome(struct gg_session *gs, uint32_t type, const int ret; uint8_t hash_buf[64]; uint32_t local_ip; + struct sockaddr_in sin; + int sin_len = sizeof(sin); if (len < sizeof(struct gg_welcome)) { ge->type = GG_EVENT_CONN_FAILED; @@ -155,29 +158,22 @@ static int gg_session_handle_welcome(struct gg_session *gs, uint32_t type, const } #endif - if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) { - struct sockaddr_in sin; - int sin_len = sizeof(sin); - - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); - - if (!getsockname(gs->fd, (struct sockaddr*) &sin, &sin_len)) { - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); - local_ip = sin.sin_addr.s_addr; - } else { - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); - local_ip = 0; - } - } else - local_ip = gg_dcc_ip; - - gs->client_addr = local_ip; + if (!getsockname(gs->fd, (struct sockaddr*) &sin, &sin_len)) { + gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); + local_ip = sin.sin_addr.s_addr; + } else { + gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); + local_ip = 0; + } if (GG_SESSION_IS_PROTOCOL_8_0(gs)) { struct gg_login80 l80; const char *client_name, *version, *descr; uint32_t client_name_len, version_len, descr_len; + if (gs->external_addr == 0) + gs->external_addr = local_ip; + memset(&l80, 0, sizeof(l80)); gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() sending GG_LOGIN80 packet\n"); l80.uin = gg_fix32(gs->uin); @@ -216,6 +212,11 @@ static int gg_session_handle_welcome(struct gg_session *gs, uint32_t type, const } else { struct gg_login70 l70; + if (gg_dcc_ip != (unsigned long) inet_addr("255.255.255.255")) + local_ip = gg_dcc_ip; + + gs->client_addr = local_ip; + memset(&l70, 0, sizeof(l70)); l70.uin = gg_fix32(gs->uin); l70.hash_type = gs->hash_type; @@ -777,7 +778,7 @@ malformed: * * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd */ -static int gg_session_send_msg_ack(struct gg_session *gs) +static int gg_session_send_msg_ack(struct gg_session *gs, uint32_t seq) { struct gg_recv_msg_ack pkt; @@ -786,8 +787,11 @@ static int gg_session_send_msg_ack(struct gg_session *gs) if ((gs->protocol_features & GG_FEATURE_MSG_ACK) == 0) return 0; + /* Kiedyś zdawało nam się, że mamy wysyłać liczbę odebranych + * wiadomości, ale okazało się, że numer sekwencyjny. */ gs->recv_msg_count++; - pkt.count = gg_fix32(gs->recv_msg_count); + + pkt.seq = gg_fix32(seq); return gg_send_packet(gs, GG_RECV_MSG_ACK, &pkt, sizeof(pkt), NULL); } @@ -829,7 +833,7 @@ static int gg_session_handle_recv_msg(struct gg_session *sess, uint32_t type, co switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), options + 1, payload_end)) { case -1: // handled - gg_session_send_msg_ack(sess); + gg_session_send_msg_ack(sess, gg_fix32(r->seq)); return 0; case -2: // failed @@ -851,7 +855,7 @@ static int gg_session_handle_recv_msg(struct gg_session *sess, uint32_t type, co goto fail; e->event.msg.message = tmp; - gg_session_send_msg_ack(sess); + gg_session_send_msg_ack(sess, gg_fix32(r->seq)); return 0; fail: @@ -866,7 +870,7 @@ malformed: free(e->event.msg.xhtml_message); free(e->event.msg.recipients); free(e->event.msg.formats); - gg_session_send_msg_ack(sess); + gg_session_send_msg_ack(sess, gg_fix32(r->seq)); return 0; } @@ -924,7 +928,7 @@ static int gg_session_handle_recv_msg_80(struct gg_session *sess, uint32_t type, if (offset_attr != 0) { switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + length)) { case -1: // handled - gg_session_send_msg_ack(sess); + gg_session_send_msg_ack(sess, gg_fix32(r->seq)); return 0; case -2: // failed @@ -958,7 +962,7 @@ static int gg_session_handle_recv_msg_80(struct gg_session *sess, uint32_t type, else e->event.msg.xhtml_message = NULL; - gg_session_send_msg_ack(sess); + gg_session_send_msg_ack(sess, gg_fix32(r->seq)); return 0; fail: @@ -974,7 +978,7 @@ malformed: free(e->event.msg.xhtml_message); free(e->event.msg.recipients); free(e->event.msg.formats); - gg_session_send_msg_ack(sess); + gg_session_send_msg_ack(sess, gg_fix32(r->seq)); return 0; } @@ -1705,6 +1709,53 @@ malformed: } /** + * \internal Obsługuje pakiet GG_USERLIST100_VERSION. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_userlist_100_version(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ + struct gg_userlist100_version *version = (struct gg_userlist100_version*) ptr; + + gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist 100 version\n"); + + ge->type = GG_EVENT_USERLIST100_VERSION; + ge->event.userlist100_version.version = gg_fix32(version->version); + + return 0; +} + +/** + * \internal Obsługuje pakiet GG_USERLIST100_REPLY. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_userlist_100_reply(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ + struct gg_userlist100_reply *reply = (struct gg_userlist100_reply*) ptr; + char *data = NULL; + + gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist 100 reply\n"); + + if (len > sizeof(*reply)) { + data = gg_inflate((const unsigned char*) ptr + sizeof(*reply), len - sizeof(*reply)); + + if (data == NULL) { + gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_userlist_100_reply() gg_inflate() failed\n"); + return -1; + } + } + + ge->type = GG_EVENT_USERLIST100_REPLY; + ge->event.userlist100_reply.type = reply->type; + ge->event.userlist100_reply.version = gg_fix32(reply->version); + ge->event.userlist100_reply.format_type = reply->format_type; + ge->event.userlist100_reply.reply = data; + + return 0; +} + +/** * \internal Tablica obsługiwanych pakietów */ static const gg_packet_handler_t handlers[] = @@ -1744,6 +1795,8 @@ static const gg_packet_handler_t handlers[] = { GG_MULTILOGON_INFO, GG_STATE_CONNECTED, sizeof(struct gg_multilogon_info), gg_session_handle_multilogon_info }, { GG_XML_ACTION, GG_STATE_CONNECTED, 0, gg_session_handle_xml_event }, { GG_RECV_OWN_MSG, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg80), gg_session_handle_recv_msg_80 }, + { GG_USERLIST100_VERSION, GG_STATE_CONNECTED, sizeof(struct gg_userlist100_version), gg_session_handle_userlist_100_version }, + { GG_USERLIST100_REPLY, GG_STATE_CONNECTED, sizeof(struct gg_userlist100_reply), gg_session_handle_userlist_100_reply }, }; /** diff --git a/protocols/Gadu-Gadu/src/libgadu/http.cpp b/protocols/Gadu-Gadu/src/libgadu/http.cpp index 5b8549dc3b..b6e4b8a7fc 100644 --- a/protocols/Gadu-Gadu/src/libgadu/http.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/http.cpp @@ -1,4 +1,4 @@ -/* $Id: http.c 11370 2010-03-13 16:17:54Z dezred $ */
+/* $Id$ */
/*
* (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -51,6 +51,8 @@ #include <unistd.h>
#endif /* _WIN32 */
+#define GG_HTTP_MAX_LENGTH 1000000000
+
/**
* Rozpoczyna połączenie HTTP.
*
@@ -368,6 +370,11 @@ int gg_http_watch_fd(struct gg_http *h) h->body_size = left;
}
+ if (h->body_size > GG_HTTP_MAX_LENGTH) {
+ gg_debug(GG_DEBUG_MISC, "=> http, content-length too big\n");
+ h->body_size = GG_HTTP_MAX_LENGTH;
+ }
+
if (left > h->body_size) {
gg_debug(GG_DEBUG_MISC, "=> http, oversized reply (%d bytes needed, %d bytes left)\n", h->body_size, left);
h->body_size = left;
diff --git a/protocols/Gadu-Gadu/src/libgadu/internal.h b/protocols/Gadu-Gadu/src/libgadu/internal.h index ba9655288a..7818cf7f31 100644 --- a/protocols/Gadu-Gadu/src/libgadu/internal.h +++ b/protocols/Gadu-Gadu/src/libgadu/internal.h @@ -22,6 +22,7 @@ #define LIBGADU_INTERNAL_H
#include "libgadu.h"
+// #include "config.h"
struct gg_dcc7_relay {
uint32_t addr;
diff --git a/protocols/Gadu-Gadu/src/libgadu/libgadu.cpp b/protocols/Gadu-Gadu/src/libgadu/libgadu.cpp index be9c2499b6..edfc7fd034 100644 --- a/protocols/Gadu-Gadu/src/libgadu/libgadu.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/libgadu.cpp @@ -52,6 +52,8 @@ #include "encoding.h"
#include "debug.h"
#include "session.h"
+#include "message.h"
+#include "deflate.h"
#include <errno.h>
#ifndef _WIN32
@@ -461,11 +463,11 @@ int gg_write(struct gg_session *sess, const char *buf, int length) res = written;
}
} else {
- res = 0;
-
if (sess->send_buf == NULL) {
res = gg_write_common(sess, buf, length);
+ if (res == -1 && errno == EAGAIN)
+ res = 0;
if (res == -1)
return -1;
}
@@ -529,7 +531,7 @@ void *gg_recv_packet(struct gg_session *sess) while (sess->header_done < sizeof(h)) {
ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done);
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret);
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, (char*) &h + sess->header_done, sizeof(h) - sess->header_done, ret);
if (!ret) {
errno = ECONNRESET;
@@ -759,6 +761,9 @@ static int gg_session_callback(struct gg_session *sess) * \note Po nawiązaniu połączenia z serwerem należy wysłać listę kontaktów
* za pomocą funkcji \c gg_notify() lub \c gg_notify_ex().
*
+ * \note Funkcja zwróci błąd ENOSYS jeśli połączenie SSL było wymagane, ale
+ * obsługa SSL nie jest wkompilowana.
+ *
* \param p Struktura opisująca parametry połączenia. Wymagane pola: uin,
* password, async.
*
@@ -822,6 +827,7 @@ struct gg_session *gg_login(const struct gg_login_params *p) sess->server_addr = p->server_addr;
sess->external_port = p->external_port;
sess->external_addr = p->external_addr;
+ sess->client_addr = p->client_addr;
sess->client_port = p->client_port;
if (p->protocol_features == 0) {
@@ -884,7 +890,7 @@ struct gg_session *gg_login(const struct gg_login_params *p) #ifdef GG_CONFIG_MIRANDA
sess->tls = p->tls;
#endif
- if (p->tls == 1) {
+ if (p->tls != GG_SSL_DISABLED) {
#ifdef GG_CONFIG_HAVE_GNUTLS
gg_session_gnutls_t *tmp;
@@ -900,8 +906,7 @@ struct gg_session *gg_login(const struct gg_login_params *p) gnutls_global_init();
gnutls_certificate_allocate_credentials(&tmp->xcred);
gnutls_init(&tmp->session, GNUTLS_CLIENT);
- gnutls_priority_set_direct(tmp->session, "NORMAL:-VERS-TLS", NULL);
-// gnutls_priority_set_direct(tmp->session, "NONE:+VERS-SSL3.0:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL", NULL);
+ gnutls_set_default_priority(tmp->session);
gnutls_credentials_set(tmp->session, GNUTLS_CRD_CERTIFICATE, tmp->xcred);
#elif defined(GG_CONFIG_HAVE_OPENSSL)
char buf[1024];
@@ -941,6 +946,11 @@ struct gg_session *gg_login(const struct gg_login_params *p) }
#elif !defined(GG_CONFIG_MIRANDA)
gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n");
+
+ if (p->tls == GG_SSL_REQUIRED) {
+ errno = ENOSYS;
+ goto fail;
+ }
#endif
}
@@ -1142,18 +1152,6 @@ void gg_logoff(struct gg_session *sess) sess->fd = -1;
}
-#ifdef GG_CONFIG_HAVE_GNUTLS
- if (sess->ssl != NULL) {
- gg_session_gnutls_t *tmp;
-
- tmp = (gg_session_gnutls_t*) sess->ssl;
- gnutls_deinit(tmp->session);
- gnutls_certificate_free_credentials(tmp->xcred);
- gnutls_global_deinit();
- free(sess->ssl);
- }
-#endif
-
if (sess->send_buf) {
free(sess->send_buf);
sess->send_buf = NULL;
@@ -1188,6 +1186,16 @@ void gg_free_session(struct gg_session *sess) #ifdef GG_CONFIG_MIRANDA
if (sess->ssl != NULL)
sslApi.sfree(sess->ssl);
+#elif GG_CONFIG_HAVE_GNUTLS
+ if (sess->ssl != NULL) {
+ gg_session_gnutls_t *tmp;
+
+ tmp = (gg_session_gnutls_t*) sess->ssl;
+ gnutls_deinit(tmp->session);
+ gnutls_certificate_free_credentials(tmp->xcred);
+ gnutls_global_deinit();
+ free(sess->ssl);
+ }
#elif GG_CONFIG_HAVE_OPENSSL
if (sess->ssl != NULL)
SSL_free(sess->ssl);
@@ -1483,209 +1491,6 @@ int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients }
/**
- * \internal Dodaje tekst na koniec bufora.
- *
- * \param dst Wskaźnik na bufor roboczy
- * \param pos Wskaźnik na aktualne położenie w buforze roboczym
- * \param src Dodawany tekst
- * \param len Długość dodawanego tekstu
- */
-static void gg_append(char *dst, int *pos, const void *src, int len)
-{
- if (dst != NULL)
- memcpy(&dst[*pos], src, len);
-
- *pos += len;
-}
-
-/**
- * \internal Zamienia tekst z formatowaniem Gadu-Gadu na HTML.
- *
- * \param dst Bufor wynikowy (może być \c NULL)
- * \param src Tekst źródłowy w UTF-8
- * \param format Atrybuty tekstu źródłowego
- * \param format_len Długość bloku atrybutów tekstu źródłowego
- *
- * \note Wynikowy tekst nie jest idealnym kodem HTML, ponieważ ma jak
- * dokładniej odzwierciedlać to, co wygenerowałby oryginalny klient.
- *
- * \note Dokleja \c \\0 na końcu bufora wynikowego.
- *
- * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
- */
-static int gg_convert_to_html(char *dst, const char *src, const unsigned char *format, int format_len)
-{
- const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">";
- const int span_len = 75;
- const char img_fmt[] = "<img name=\"%02x%02x%02x%02x%02x%02x%02x%02x\">";
- const int img_len = 29;
- int char_pos = 0;
- int format_idx = 0;
- unsigned char old_attr = 0;
- const unsigned char *color = (const unsigned char*) "\x00\x00\x00";
- int len, i;
- const unsigned char *format_ = (const unsigned char*) format;
-
- len = 0;
-
- /* Nie mamy atrybutów dla pierwsze znaku, a tekst nie jest pusty, więc
- * tak czy inaczej trzeba otworzyć <span>. */
-
- if (src[0] != 0 && (format_idx + 3 > format_len || (format_[format_idx] | (format_[format_idx + 1] << 8)) != 0)) {
- if (dst != NULL)
- sprintf(&dst[len], span_fmt, 0, 0, 0);
-
- len += span_len;
- }
-
- /* Pętla przechodzi też przez kończące \0, żeby móc dokleić obrazek
- * na końcu tekstu. */
-
- for (i = 0; ; i++) {
- /* Analizuj atrybuty tak długo jak dotyczą aktualnego znaku. */
- for (;;) {
- unsigned char attr;
- int attr_pos;
-
- if (format_idx + 3 > format_len)
- break;
-
- attr_pos = format_[format_idx] | (format_[format_idx + 1] << 8);
-
- if (attr_pos != char_pos)
- break;
-
- attr = format_[format_idx + 2];
-
- /* Nie doklejaj atrybutów na końcu, co najwyżej obrazki. */
-
- if (src[i] == 0)
- attr &= ~(GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR);
-
- format_idx += 3;
-
- if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0 || (attr == 0 && old_attr != 0)) {
- if (char_pos != 0) {
- if ((old_attr & GG_FONT_UNDERLINE) != 0)
- gg_append(dst, &len, "</u>", 4);
-
- if ((old_attr & GG_FONT_ITALIC) != 0)
- gg_append(dst, &len, "</i>", 4);
-
- if ((old_attr & GG_FONT_BOLD) != 0)
- gg_append(dst, &len, "</b>", 4);
-
- if (src[i] != 0)
- gg_append(dst, &len, "</span>", 7);
- }
-
- if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) {
- color = &format_[format_idx];
- format_idx += 3;
- } else {
- color = (unsigned char*) "\x00\x00\x00";
- }
-
- if (src[i] != 0) {
- if (dst != NULL)
- sprintf(&dst[len], span_fmt, color[0], color[1], color[2]);
- len += span_len;
- }
- } else if (char_pos == 0 && src[0] != 0) {
- if (dst != NULL)
- sprintf(&dst[len], span_fmt, 0, 0, 0);
- len += span_len;
- }
-
- if ((attr & GG_FONT_BOLD) != 0)
- gg_append(dst, &len, "<b>", 3);
-
- if ((attr & GG_FONT_ITALIC) != 0)
- gg_append(dst, &len, "<i>", 3);
-
- if ((attr & GG_FONT_UNDERLINE) != 0)
- gg_append(dst, &len, "<u>", 3);
-
- if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) {
- if (dst != NULL) {
- sprintf(&dst[len], img_fmt,
- format_[format_idx + 9],
- format_[format_idx + 8],
- format_[format_idx + 7],
- format_[format_idx + 6],
- format_[format_idx + 5],
- format_[format_idx + 4],
- format_[format_idx + 3],
- format_[format_idx + 2]);
- }
-
- len += img_len;
- format_idx += 10;
- }
-
- old_attr = attr;
- }
-
- /* Doklej znak zachowując htmlowe escapowanie. */
-
- switch (src[i]) {
- case '&':
- gg_append(dst, &len, "&", 5);
- break;
- case '<':
- gg_append(dst, &len, "<", 4);
- break;
- case '>':
- gg_append(dst, &len, ">", 4);
- break;
- case '\'':
- gg_append(dst, &len, "'", 6);
- break;
- case '\"':
- gg_append(dst, &len, """, 6);
- break;
- case '\n':
- gg_append(dst, &len, "<br>", 4);
- break;
- case '\r':
- case 0:
- break;
- default:
- if (dst != NULL)
- dst[len] = src[i];
- len++;
- }
-
- /* Sprawdź, czy bajt nie jest kontynuacją znaku unikodowego. */
-
- if ((src[i] & 0xc0) != 0xc0)
- char_pos++;
-
- if (src[i] == 0)
- break;
- }
-
- /* Zamknij tagi. */
-
- if ((old_attr & GG_FONT_UNDERLINE) != 0)
- gg_append(dst, &len, "</u>", 4);
-
- if ((old_attr & GG_FONT_ITALIC) != 0)
- gg_append(dst, &len, "</i>", 4);
-
- if ((old_attr & GG_FONT_BOLD) != 0)
- gg_append(dst, &len, "</b>", 4);
-
- if (src[0] != 0)
- gg_append(dst, &len, "</span>", 7);
-
- if (dst != NULL)
- dst[len] = 0;
-
- return len;
-}
-
-/**
* Wysyła wiadomość formatowaną w ramach konferencji.
*
* Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
@@ -1773,7 +1578,7 @@ int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int r formatlen = 9;
}
- len = gg_convert_to_html(NULL, utf_msg, format + 3, formatlen - 3);
+ len = gg_message_text_to_html(NULL, utf_msg, (char*) format + 3, formatlen - 3);
html_msg = (char*)malloc(len + 1);
@@ -1782,7 +1587,7 @@ int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int r goto cleanup;
}
- gg_convert_to_html(html_msg, utf_msg, format + 3, formatlen - 3);
+ gg_message_text_to_html(html_msg, utf_msg, (char*) format + 3, formatlen - 3);
s80.seq = gg_fix32(seq_no);
s80.msgclass = gg_fix32(msgclass);
@@ -2383,6 +2188,70 @@ int gg_userlist_request(struct gg_session *sess, char type, const char *request) }
/**
+ * Wysyła do serwera zapytanie dotyczące listy kontaktów (10.0).
+ *
+ * Funkcja służy do importu lub eksportu listy kontaktów do serwera.
+ * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez
+ * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format
+ * listy kontaktów jest jednak weryfikowany przez serwer, który stara się
+ * synchronizować listę kontaktów zapisaną w formatach GG 7.0 oraz GG 10.0.
+ * Serwer przyjmuje listy kontaktów przysłane w formacie niezgodnym z podanym
+ * jako \c format_type, ale nie zachowuje ich, a przesłanie takiej listy jest
+ * równoznaczne z usunięciem listy kontaktów.
+ *
+ * Program nie musi się przejmować kompresją listy kontaktów zgodną
+ * z protokołem -- wysyła i odbiera kompletną listę zapisaną czystym tekstem.
+ *
+ * \param sess Struktura sesji
+ * \param type Rodzaj zapytania
+ * \param version Numer ostatniej znanej programowi wersji listy kontaktów lub 0
+ * \param format_type Typ formatu listy kontaktów
+ * \param request Treść zapytania (może być równe NULL)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup importexport
+ */
+int gg_userlist100_request(struct gg_session *sess, char type, unsigned int version, char format_type, const char *request)
+{
+ struct gg_userlist100_request pkt;
+ unsigned char *zrequest;
+ size_t zrequest_len;
+ int ret;
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ pkt.type = type;
+ pkt.version = gg_fix32(version);
+ pkt.format_type = format_type;
+ pkt.unknown1 = 0x01;
+
+ if (request == NULL)
+ return gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), NULL);
+
+ zrequest = gg_deflate(request, &zrequest_len);
+
+ if (zrequest == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_userlist100_request() gg_deflate() failed");
+ return -1;
+ }
+
+ ret = gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), zrequest, zrequest_len, NULL);
+
+ free(zrequest);
+
+ return ret;
+}
+
+/**
* Informuje rozmówcę o pisaniu wiadomości.
*
* \param sess Struktura sesji
@@ -2425,6 +2294,47 @@ int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id) /* @} */
+/**
+ * Sprawdza czy biblioteka obsługuje daną funkcję.
+ *
+ * \param feature Identyfikator funkcji.
+ *
+ * \return Wartość niezerowa jeśli funkcja jest obsłgiwana.
+ *
+ * \ingroup version
+ */
+int gg_libgadu_check_feature(gg_libgadu_feature_t feature)
+{
+ switch (feature)
+ {
+ case GG_LIBGADU_FEATURE_SSL:
+#if defined(GG_CONFIG_HAVE_OPENSSL) || defined(GG_CONFIG_HAVE_GNUTLS)
+ return 1;
+#else
+ return 0;
+#endif
+
+ case GG_LIBGADU_FEATURE_PTHREAD:
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ return 1;
+#else
+ return 0;
+#endif
+
+ case GG_LIBGADU_FEATURE_USERLIST100:
+#ifdef GG_CONFIG_HAVE_ZLIB
+ return 1;
+#else
+ return 0;
+#endif
+
+ /* Celowo nie ma default, żeby kompilator wyłapał brakujące funkcje */
+
+ }
+
+ return 0;
+}
+
/*
* Local variables:
* c-indentation-style: k&r
diff --git a/protocols/Gadu-Gadu/src/libgadu/libgadu.h b/protocols/Gadu-Gadu/src/libgadu/libgadu.h index 44e8318b8d..8c5e8e626a 100644 --- a/protocols/Gadu-Gadu/src/libgadu/libgadu.h +++ b/protocols/Gadu-Gadu/src/libgadu/libgadu.h @@ -1,4 +1,4 @@ -/* $Id: libgadu.h 13762 2011-08-09 12:35:16Z dezred $ */
+/* $Id$ */
/*
* (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -81,6 +81,9 @@ /* Defined if libgadu was compiled and linked with OpenSSL support. */
#undef GG_CONFIG_HAVE_OPENSSL
+/* Defined if libgadu was compiled and linked with zlib support. */
+#define GG_CONFIG_HAVE_ZLIB
+
/* Defined if uintX_t types are defined in <stdint.h>. */
#undef GG_CONFIG_HAVE_STDINT_H
@@ -271,11 +274,11 @@ struct gg_session { uint32_t hub_addr; /**< Adres huba po rozwiązaniu nazwy */
uint32_t server_addr; /**< Adres serwera otrzymany od huba */
- uint32_t client_addr; /**< Adres gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
- uint16_t client_port; /**< Port gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
+ uint32_t client_addr; /**< Adres gniazda dla połączeń bezpośrednich */
+ uint16_t client_port; /**< Port gniazda dla połączeń bezpośrednich */
- uint32_t external_addr; /**< Publiczny adres dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
- uint16_t external_port; /**< Publiczny port dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
+ uint32_t external_addr; /**< Publiczny adres dla połączeń bezpośrednich */
+ uint16_t external_port; /**< Publiczny port dla połączeń bezpośrednich */
uin_t uin; /**< Własny numer Gadu-Gadu */
char *password; /**< Hasło (zwalniane po użyciu) */
@@ -624,6 +627,17 @@ enum gg_check_t { };
/**
+ * Flaga połączenia szyfrowanego.
+ *
+ * \ingroup login
+ */
+typedef enum {
+ GG_SSL_DISABLED = 0, /**< Połączenie SSL wyłączone */
+ GG_SSL_ENABLED, /**< Połączenie SSL włączone gdy dostępne */
+ GG_SSL_REQUIRED /**< Połączenie SSL wymagane */
+} gg_ssl_t;
+
+/**
* Parametry połączenia z serwerem Gadu-Gadu. Parametry zostały przeniesione
* do struktury, by uniknąć zmian API po rozszerzeniu protokołu i dodaniu
* kolejnych opcji połączenia. Część parametrów, które nie są już aktualne
@@ -639,19 +653,15 @@ struct gg_login_params { char *status_descr; /**< Początkowy opis użytkownika (domyślnie brak) */
uint32_t server_addr; /**< Adres serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
uint16_t server_port; /**< Port serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
-#ifndef DOXYGEN
- uint32_t client_addr; /**< Adres połączeń bezpośrednich (nieaktualne) */
- uint16_t client_port; /**< Port połączeń bezpośrednich (nieaktualne) */
-#endif
+ uint32_t client_addr; /**< Adres połączeń bezpośrednich (domyślnie dobierany automatycznie) */
+ uint16_t client_port; /**< Port połączeń bezpośrednich (domyślnie dobierany automatycznie) */
int protocol_version; /**< Wersja protokołu wysyłana do serwera (domyślnie najnowsza obsługiwana) */
char *client_version; /**< Wersja klienta wysyłana do serwera (domyślnie najnowsza znana) */
int has_audio; /**< Flaga obsługi połączeń głosowych */
int last_sysmsg; /**< Numer ostatnio odebranej wiadomości systemowej */
- uint32_t external_addr; /**< Adres publiczny dla połączeń bezpośrednich (6.x) */
- uint16_t external_port; /**< Port publiczny dla połączeń bezpośrednich (6.x) */
-#ifndef DOXYGEN
- int tls; /**< Flaga połączenia szyfrowanego (nieaktualna) */
-#endif
+ uint32_t external_addr; /**< Adres publiczny dla połączeń bezpośrednich (domyślnie dobierany automatycznie) */
+ uint16_t external_port; /**< Port publiczny dla połączeń bezpośrednich (domyślnie dobierany automatycznie) */
+ int tls; /**< Flaga połączenia szyfrowanego (patrz \ref gg_ssl_t) */
int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w kilobajtach */
#ifndef DOXYGEN
int era_omnix; /**< Flaga udawania klienta Era Omnix (nieaktualna) */
@@ -688,6 +698,7 @@ int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int r int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len);
int gg_ping(struct gg_session *sess);
int gg_userlist_request(struct gg_session *sess, char type, const char *request);
+int gg_userlist100_request(struct gg_session *sess, char type, unsigned int version, char format_type, const char *request);
int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32);
int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size);
int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length);
@@ -759,7 +770,10 @@ enum gg_event_t { GG_EVENT_TYPING_NOTIFICATION, /**< Powiadomienie o pisaniu */
GG_EVENT_USER_DATA, /**< Informacja o kontaktach */
GG_EVENT_MULTILOGON_MSG, /**< Wiadomość wysłana z innej sesji multilogowania */
- GG_EVENT_MULTILOGON_INFO /**< Informacja o innych sesjach multilogowania */
+ GG_EVENT_MULTILOGON_INFO, /**< Informacja o innych sesjach multilogowania */
+
+ GG_EVENT_USERLIST100_VERSION, /**< Otrzymano numer wersji listy kontaktów na serwerze (10.0) */
+ GG_EVENT_USERLIST100_REPLY /**< Wynik importu lub eksportu listy kontaktów (10.0) */
};
#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
@@ -778,7 +792,9 @@ enum gg_failure_t { GG_FAILURE_TLS, /**< Błąd negocjacji szyfrowanego połączenia */
GG_FAILURE_NEED_EMAIL, /**< Serwer rozłączył nas z prośbą o zmianę adresu e-mail */
GG_FAILURE_INTRUDER, /**< Zbyt wiele prób połączenia z nieprawidłowym hasłem */
- GG_FAILURE_UNAVAILABLE /**< Serwery są wyłączone */
+ GG_FAILURE_UNAVAILABLE, /**< Serwery są wyłączone */
+ GG_FAILURE_PROXY, /**< Błąd serwera pośredniczącego */
+ GG_FAILURE_HUB, /**< Błąd połączenia z hubem */
};
/**
@@ -1060,6 +1076,23 @@ struct gg_event_multilogon_info { };
/**
+ * Opis zdarzenia \c GG_EVENT_USERLIST100_VERSION.
+ */
+struct gg_event_userlist100_version {
+ uint32_t version; /**< Numer wersji listy kontaktów na serwerze */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_USERLIST100_REPLY.
+ */
+struct gg_event_userlist100_reply {
+ char type; /**< Rodzaj odpowiedzi */
+ uint32_t version; /**< Aktualna wersja listy kontaktów na serwerze */
+ char format_type; /**< Typ formatu listy kontaktów (żądany w \c gg_userlist100_request.format_type) */
+ char *reply; /**< Treść listy kontaktów w przesyłanej wersji i formacie */
+};
+
+/**
* Unia wszystkich zdarzeń zwracanych przez funkcje \c gg_watch_fd(),
* \c gg_dcc_watch_fd() i \c gg_dcc7_watch_fd().
*
@@ -1094,6 +1127,8 @@ union gg_event_union { struct gg_event_user_data user_data; /**< Informacje o kontaktach */
struct gg_event_msg multilogon_msg; /**< Inna sesja wysłała wiadomość (\c GG_EVENT_MULTILOGON_MSG) */
struct gg_event_multilogon_info multilogon_info; /**< Informacja o innych sesjach multilogowania (\c GG_EVENT_MULTILOGON_INFO) */
+ struct gg_event_userlist100_version userlist100_version; /**< Informacja o numerze wersji listy kontaktów na serwerze (\c GG_EVENT_USERLIST100_VERSION) */
+ struct gg_event_userlist100_reply userlist100_reply; /**< Odpowiedź listy kontaktów (10.0) (\c GG_EVENT_USERLIST100_REPLY) */
};
/**
@@ -1324,6 +1359,19 @@ void gg_debug_session(struct gg_session *sess, int level, const char *format, .. const char *gg_libgadu_version(void);
+/**
+ * Lista funkcji biblioteki, które zależą od zewnętrznych bibliotek.
+ *
+ * \ingroup version
+ */
+typedef enum {
+ GG_LIBGADU_FEATURE_SSL, /**< Biblioteka obsługuje połączenia szyfrowane */
+ GG_LIBGADU_FEATURE_PTHREAD, /**< Biblioteka obsługuje rozwiązywanie nazw za pomocą wątków */
+ GG_LIBGADU_FEATURE_USERLIST100, /**< Biblioteka obsługuje listę kontaktów zgodną z Gadu-Gadu 10 */
+} gg_libgadu_feature_t;
+
+int gg_libgadu_check_feature(gg_libgadu_feature_t feature);
+
extern int gg_proxy_enabled;
extern char *gg_proxy_host;
extern int gg_proxy_port;
@@ -2154,6 +2202,67 @@ struct gg_userlist_reply { uint8_t type;
} GG_PACKED;
+#ifndef DOXYGEN
+
+#define GG_USERLIST100_PUT 0x00
+#define GG_USERLIST100_GET 0x02
+
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Rodzaj zapytania (10.0).
+ */
+enum {
+ GG_USERLIST100_PUT, /**< Eksport listy kontaktów. */
+ GG_USERLIST100_GET, /**< Import listy kontaktów. */
+};
+
+#endif /* DOXYGEN */
+
+#ifndef DOXYGEN
+
+#define GG_USERLIST100_FORMAT_TYPE_NONE 0x00
+#define GG_USERLIST100_FORMAT_TYPE_GG70 0x01
+#define GG_USERLIST100_FORMAT_TYPE_GG100 0x02
+
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Typ formatu listy kontaktów (10.0).
+ */
+enum {
+ GG_USERLIST100_FORMAT_TYPE_NONE, /**< Brak treści listy kontaktów. */
+ GG_USERLIST100_FORMAT_TYPE_GG70, /**< Format listy kontaktów zgodny z Gadu-Gadu 7.0. */
+ GG_USERLIST100_FORMAT_TYPE_GG100, /**< Format listy kontaktów zgodny z Gadu-Gadu 10.0. */
+};
+
+#endif /* DOXYGEN */
+
+#ifndef DOXYGEN
+
+#define GG_USERLIST100_REPLY_LIST 0x00
+#define GG_USERLIST100_REPLY_ACK 0x10
+#define GG_USERLIST100_REPLY_REJECT 0x12
+
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Typ odpowiedzi listy kontaktów (10.0).
+ */
+enum {
+ GG_USERLIST100_REPLY_LIST, /**< W odpowiedzi znajduje się aktualna lista kontaktów na serwerze. */
+ GG_USERLIST100_REPLY_ACK, /**< Potwierdzenie odebrania nowej wersji listy kontaktów. W polu \c gg_userlist100_reply.version znajduje się numer nowej wersji listy kontaktów. */
+ GG_USERLIST100_REPLY_REJECT, /**< Odmowa przyjęcia nowej wersji listy kontaktów. W polu \c gg_userlist100_reply.version znajduje się numer wersji listy kontaktów aktualnie przechowywanej przez serwer. */
+};
+
+#endif /* DOXYGEN */
+
struct gg_dcc_tiny_packet {
uint8_t type; /* rodzaj pakietu */
} GG_PACKED;
@@ -2232,7 +2341,7 @@ struct gg_dcc7_reject { // XXX API
#define GG_DCC7_REJECT_BUSY 0x00000001 /**< Połączenie bezpośrednie już trwa, nie umiem obsłużyć więcej */
#define GG_DCC7_REJECT_USER 0x00000002 /**< Użytkownik odrzucił połączenie */
-#define GG_DCC7_REJECT_HIDDEN 0x00000003 /* użytkownik ojest ukryty i nie możesz mu wysłać pliku */
+#define GG_DCC7_REJECT_HIDDEN 0x00000003 /* użytkownik jest ukryty i nie możesz mu wysłać pliku */
#define GG_DCC7_REJECT_VERSION 0x00000006 /**< Druga strona ma wersję klienta nieobsługującą połączeń bezpośrednich tego typu */
#define GG_DCC7_ID_REQUEST 0x23
diff --git a/protocols/Gadu-Gadu/src/libgadu/message.cpp b/protocols/Gadu-Gadu/src/libgadu/message.cpp index d0872c32e9..2e81c6b0de 100644 --- a/protocols/Gadu-Gadu/src/libgadu/message.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/message.cpp @@ -369,7 +369,7 @@ int gg_message_get_attributes(gg_message_t *gm, const char **attributes, size_t * \param src Dodawany tekst * \param len Długość dodawanego tekstu */ -static void gg_append(char *dst, int *pos, const void *src, int len) +static void gg_append(char *dst, size_t *pos, const void *src, int len) { if (dst != NULL) memcpy(&dst[*pos], src, len); @@ -402,7 +402,8 @@ size_t gg_message_text_to_html(char *dst, const char *src, const char *format, s int format_idx = 0; unsigned char old_attr = 0; const unsigned char *color = (const unsigned char*) "\x00\x00\x00"; - int len, i; + int i; + size_t len; const unsigned char *format_ = (const unsigned char*) format; len = 0; @@ -592,6 +593,11 @@ size_t gg_message_html_to_text(char *dst, const char *html) entity = NULL; for (src = html; *src != 0; src++) { + if (in_entity && !(isalnum(*src) || *src == '#' || *src == ';')) { + in_entity = 0; + gg_append(dst, &len, entity, src - entity); + } + if (*src == '<') { tag = src; in_tag = 1; diff --git a/protocols/Gadu-Gadu/src/libgadu/obsolete.cpp b/protocols/Gadu-Gadu/src/libgadu/obsolete.cpp index 0f75fd2dd2..3fb2d917fb 100644 --- a/protocols/Gadu-Gadu/src/libgadu/obsolete.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/obsolete.cpp @@ -34,6 +34,7 @@ #include <errno.h>
#include "libgadu.h"
+#include "internal.h"
struct gg_http *gg_userlist_get(uin_t uin, const char *passwd, int async)
{
diff --git a/protocols/Gadu-Gadu/src/libgadu/protocol.h b/protocols/Gadu-Gadu/src/libgadu/protocol.h index 16bde0d424..09ec9e7cf9 100644 --- a/protocols/Gadu-Gadu/src/libgadu/protocol.h +++ b/protocols/Gadu-Gadu/src/libgadu/protocol.h @@ -136,7 +136,7 @@ struct gg_recv_msg80 { #define GG_RECV_MSG_ACK 0x0046
struct gg_recv_msg_ack {
- uint32_t count;
+ uint32_t seq;
} GG_PACKED;
#define GG_USER_DATA 0x0044
@@ -293,6 +293,32 @@ struct gg_dcc7_welcome_p2p { #define GG_TIMEOUT_DISCONNECT 5 /**< Maksymalny czas oczekiwania na rozłączenie */
+#define GG_USERLIST100_VERSION 0x5c
+
+struct gg_userlist100_version {
+ uint32_t version; /* numer wersji listy kontaktów */
+} GG_PACKED;
+
+#define GG_USERLIST100_REQUEST 0x0040
+
+struct gg_userlist100_request {
+ uint8_t type; /* rodzaj żądania */
+ uint32_t version; /* numer ostatniej znanej wersji listy kontaktów bądź 0 */
+ uint8_t format_type; /* rodzaj żądanego typu formatu listy kontaktów */
+ uint8_t unknown1; /* 0x01 */
+ /* char request[]; */
+} GG_PACKED;
+
+#define GG_USERLIST100_REPLY 0x41
+
+struct gg_userlist100_reply {
+ uint8_t type; /* rodzaj odpowiedzi */
+ uint32_t version; /* numer wersji listy kontaktów aktualnie przechowywanej przez serwer */
+ uint8_t format_type; /* rodzaj przesyłanego typu formatu listy kontaktów */
+ uint8_t unknown1; /* 0x01 */
+ /* char reply[]; */
+} GG_PACKED;
+
#ifdef _WIN32
#pragma pack(pop)
#endif
diff --git a/protocols/Gadu-Gadu/src/libgadu/resolver.cpp b/protocols/Gadu-Gadu/src/libgadu/resolver.cpp index 065169007f..b8972f26d1 100644 --- a/protocols/Gadu-Gadu/src/libgadu/resolver.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/resolver.cpp @@ -63,7 +63,6 @@ static void (*gg_global_resolver_cleanup)(void **private_data, int force); #include <pthread.h> -#ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R /** * \internal Funkcja pomocnicza zwalniająca zasoby po rozwiązywaniu nazwy * w wątku. @@ -79,7 +78,7 @@ static void gg_gethostbyname_cleaner(void *data) *buf_ptr = NULL; } } -#endif + #endif /* GG_CONFIG_HAVE_PTHREAD */ /** @@ -244,7 +243,7 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr **result, int *co /* Kopiuj */ for (i = 0; he->h_addr_list[i] != NULL; i++) - memcpy(&((*result)[i]), he->h_addr_list[0], sizeof(struct in_addr)); + memcpy(&((*result)[i]), he->h_addr_list[i], sizeof(struct in_addr)); (*result)[i].s_addr = INADDR_NONE; @@ -257,22 +256,24 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr **result, int *co /** * \internal Rozwiązuje nazwę i zapisuje wynik do podanego desktyptora. * + * \note Użycie logowania w tej funkcji może mieć negatywny wpływ na + * aplikacje jednowątkowe korzystające. + * * \param fd Deskryptor * \param hostname Nazwa serwera * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ -int gg_resolver_run(int fd, const char *hostname) +static int gg_resolver_run(int fd, const char *hostname) { struct in_addr addr_ip[2], *addr_list; int addr_count; int res = 0; - gg_debug(GG_DEBUG_MISC, "// gg_resolver_run(%d, %s)\n", fd, hostname); - if ((addr_ip[0].s_addr = inet_addr(hostname)) == INADDR_NONE) { if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 1) == -1) { addr_list = addr_ip; + addr_count = 0; /* addr_ip[0] już zawiera INADDR_NONE */ } } else { @@ -281,8 +282,6 @@ int gg_resolver_run(int fd, const char *hostname) addr_count = 1; } - gg_debug(GG_DEBUG_MISC, "// gg_resolver_run() count = %d\n", addr_count); - if (write(fd, addr_list, (addr_count + 1) * sizeof(struct in_addr)) != (addr_count + 1) * sizeof(struct in_addr)) res = -1; @@ -374,12 +373,17 @@ static int gg_resolver_fork_start(SOCKET *fd, void **priv_data, const char *host } if (data->pid == 0) { + int status; + gg_sock_close(pipes[0]); - if (gg_resolver_run(pipes[1], hostname) == -1) - exit(1); - else - exit(0); + status = (gg_resolver_run(pipes[1], hostname) == -1) ? 1 : 0; + +#ifdef HAVE__EXIT + _exit(status); +#else + exit(status); +#endif } gg_sock_close(pipes[1]); @@ -411,7 +415,7 @@ cleanup: * danych * \param force Flaga usuwania zasobów przed zakończeniem działania */ -void gg_resolver_fork_cleanup(void **priv_data, int force) +static void gg_resolver_fork_cleanup(void **priv_data, int force) { struct gg_resolver_fork_data *data; |