Sprawdzanie zlib w configure.ac wymaga poprawki przez kogoś zaznajomionego z autotools. Sam nie mam pojęcia o autotools i zrobiłem to tylko tak, żeby na moim systemie libgadu w ogóle chciało się linkować ;). --- configure.ac | 4 ++ include/libgadu.h.in | 53 ++++++++++++++++++++++++++++ src/common.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/libgadu.c | 64 ++++++++++++++++++++++++++++++++++ src/libgadu.sym | 1 + 5 files changed, 216 insertions(+), 0 deletions(-)
diff --git a/configure.ac b/configure.ac index ec4d048..e83c1e5 100644 --- a/configure.ac +++ b/configure.ac @@ -194,6 +194,10 @@ if test "x$with_pthread" = "xyes"; then AC_DEFINE([GG_CONFIG_PTHREAD_DEFAULT], [], [Defined if pthread resolver is the default one.]) fi +dnl zlib (TODO!) + +LIBS="$LIBS -lz" + dnl dnl Sprawdzamy GnuTLS dnl diff --git a/include/libgadu.h.in b/include/libgadu.h.in index fb1d5ef..a785809 100644 --- a/include/libgadu.h.in +++ b/include/libgadu.h.in @@ -623,12 +623,15 @@ 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); uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len); +unsigned char *gg_deflate(const char *in, size_t *out_lenp); + int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type); gg_resolver_t gg_session_get_resolver(struct gg_session *gs); int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int)); @@ -2030,6 +2033,8 @@ struct gg_recv_msg { #define GG_USERLIST_REQUEST 0x0016 +#define GG_USERLIST100_REQUEST 0x0040 + #define GG_XML_EVENT 0x0027 #ifndef DOXYGEN @@ -2083,6 +2088,54 @@ 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 */ + +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; + struct gg_dcc_tiny_packet { uint8_t type; /* rodzaj pakietu */ } GG_PACKED; diff --git a/src/common.c b/src/common.c index 55e77d9..3951c47 100644 --- a/src/common.c +++ b/src/common.c @@ -42,6 +42,8 @@ #include <string.h> #include <unistd.h> +#include <zlib.h> + #include "libgadu.h" /** @@ -576,6 +578,98 @@ char *gg_proxy_auth(void) } /** + * \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) +{ + 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 = 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 = 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 = 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); + return NULL; +} + +/** * \internal Tablica pomocnicza do wyznaczania sumy kontrolnej. */ static const uint32_t gg_crc32_table[256] = diff --git a/src/libgadu.c b/src/libgadu.c index 294ed85..de270dc 100644 --- a/src/libgadu.c +++ b/src/libgadu.c @@ -2344,6 +2344,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 diff --git a/src/libgadu.sym b/src/libgadu.sym index 7e97f0f..8b00077 100644 --- a/src/libgadu.sym +++ b/src/libgadu.sym @@ -149,6 +149,7 @@ gg_userlist_remove gg_userlist_remove_free gg_userlist_remove_watch_fd gg_userlist_request +gg_userlist100_request gg_vsaprintf gg_watch_fd gg_write -- 1.7.5.rc1 _______________________________________________ libgadu-devel mailing list libgadu-devel@lists.ziew.org http://lists.ziew.org/mailman/listinfo/libgadu-devel