From: Eric W. <war...@us...> - 2001-09-29 23:06:33
|
Update of /cvsroot/gaim/gaim/src/protocols/gg In directory usw-pr-cvs1:/tmp/cvs-serv5211/src/protocols/gg Added Files: .cvsignore Makefile.am gg.c libgg.c libgg.h protocol.txt Log Message: Arkadiusz Miskiewicz\'s Gadu-Gadu plugin. I was able to figure out enough polish to be able to download Gadu-Gadu, create an account, and test the plugin. Imagine my shock when I got my info and it said I was a woman. Whoops. Also splitting plugins.c so that non-gtk stuff is in modules.c. gaim-core is almost ready for protocol implantaion. Also fixing an IRC bug. Also patiently waiting for anoncvs_gaim's lock in /cvsroot/gaim/gaim/pixmaps --- NEW FILE: .cvsignore --- .deps .libs Makefile Makefile.in gg.lo libgg.la libgg.lo --- NEW FILE: Makefile.am --- EXTRA_DIST = protocol.txt pkgdir = $(libdir)/gaim CFLAGS += -I\$(top_srcdir)/src $(st) $(DEBUG_CFLAGS) libgg_la_LDFLAGS = -avoid-version if STATIC_GG st = -DSTATIC pkg_LTLIBRARIES = noinst_LIBRARIES = libgg.a libgg_a_SOURCES = libgg.c \ libgg.h \ gg.c else st = pkg_LTLIBRARIES = libgg.la noinst_LIBRARIES = libgg_la_SOURCES = libgg.c \ libgg.h \ gg.c endif --- NEW FILE: gg.c --- /* * gaim - Gadu-Gadu Protocol Plugin * $Id: gg.c,v 1.1 2001/09/29 23:06:30 warmenhoven Exp $ * * Copyright (C) 2001, Arkadiusz Mi¶kiewicz <mi...@pl...> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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 General Public License for more details. * * You should have received a copy of the GNU 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 [...1001 lines suppressed...] } void gaim_plugin_remove() { struct prpl *p = find_prpl(PROTO_GADUGADU); if (p == my_protocol) unload_protocol(p); } char *name() { return "Gadu-Gadu"; } char *description() { return PRPL_DESC("Gadu-Gadu"); } #endif --- NEW FILE: libgg.c --- /* $Id: libgg.c,v 1.1 2001/09/29 23:06:30 warmenhoven Exp $ */ /* * (C) Copyright 2001 Wojtek Kaniewski <woj...@ir...> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ [...1208 lines suppressed...] if (errno == EAGAIN) { e->type = GG_EVENT_NONE; res = 0; } else res = -1; } break; } } if (res == -1) { free(e); e = NULL; } return e; } --- NEW FILE: libgg.h --- /* $Id: libgg.h,v 1.1 2001/09/29 23:06:30 warmenhoven Exp $ */ /* * (C) Copyright 2001 Wojtek Kaniewski <woj...@ir...> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __LIBGG_H #define __LIBGG_H #ifdef __cplusplus extern "C" { #endif #define GG_DEBUG 1 /* * typ zmiennej okre¶laj±cej numerek danej osoby. */ typedef unsigned int uin_t; /* * co¶tam. */ struct gg_session { int state, check; int fd, pid; int port; int seq, async; uin_t uin; char *password; char *recv_buf; int recv_done, recv_left; }; /* * ró¿ne stany asynchronicznej maszynki. */ enum { GG_STATE_IDLE = 0, /* wspólne */ GG_STATE_RESOLVING, GG_STATE_CONNECTING_HTTP, GG_STATE_WRITING_HTTP, /* gg_login */ GG_STATE_CONNECTING_GG, GG_STATE_WAITING_FOR_KEY, GG_STATE_SENDING_KEY, GG_STATE_CONNECTED, GG_STATE_READING_HEADER, /* gg_search */ GG_STATE_READING_DATA, GG_STATE_PARSING, GG_STATE_FINISHED, }; /* * co proces klienta powinien sprawdzaæ w deskryptorach? */ enum { GG_CHECK_NONE = 0, GG_CHECK_WRITE = 1, GG_CHECK_READ = 2, }; 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_change_status(struct gg_session *sess, int status); int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, unsigned char *message); int gg_ping(struct gg_session *sess); struct gg_notify_reply { uin_t uin; /* numerek */ int status; /* status danej osoby */ int remote_ip; /* adres ip delikwenta */ short remote_port; /* port, na którym s³ucha klient */ int dunno1; /* == 0x0b */ short dunno2; /* znowu port? */ } __attribute__ ((packed)); struct gg_status { uin_t uin; /* numerek */ int status; /* nowy stan */ } __attribute__ ((packed)); enum { GG_EVENT_NONE = 0, GG_EVENT_MSG, GG_EVENT_NOTIFY, GG_EVENT_STATUS, GG_EVENT_ACK, GG_EVENT_CONN_FAILED, GG_EVENT_CONN_SUCCESS, }; enum { GG_FAILURE_RESOLVING = 1, GG_FAILURE_CONNECTING, GG_FAILURE_INVALID, GG_FAILURE_READING, GG_FAILURE_WRITING, GG_FAILURE_PASSWORD, }; struct gg_event { int type; union { struct { uin_t sender; int msgclass; unsigned char *message; } msg; struct gg_notify_reply *notify; struct gg_status status; struct { uin_t recipient; int status; int seq; } ack; int failure; } event; }; struct gg_event *gg_watch_fd(struct gg_session *sess); void gg_free_event(struct gg_event *e); int gg_notify(struct gg_session *sess, uin_t *userlist, int count); int gg_add_notify(struct gg_session *sess, uin_t uin); int gg_remove_notify(struct gg_session *sess, uin_t uin); /* * jakie¶tam bzdurki dotycz±ce szukania userów. */ struct gg_search_result { uin_t uin; char *first_name; char *last_name; char *nickname; int born; int gender; char *city; int active; }; struct gg_search_request { /* czy ma szukaæ tylko aktywnych? */ int active; /* mode 0 */ char *nickname, *first_name, *last_name, *city; int gender, min_birth, max_birth; /* mode 1 */ char *email; /* mode 2 */ char *phone; /* mode 3 */ uin_t uin; }; struct gg_search { struct gg_search_request request; /* bzdurki */ int mode, fd, async, state, check, error, pid; char *header_buf, *data_buf; int header_size, data_size; /* wyniki */ int count; struct gg_search_result *results; }; #define GG_GENDER_NONE 0 #define GG_GENDER_FEMALE 1 #define GG_GENDER_MALE 2 struct gg_search *gg_search(struct gg_search_request *r, int async); int gg_search_watch_fd(struct gg_search *f); void gg_free_search(struct gg_search *f); void gg_search_cancel(struct gg_search *f); /* * je¶li chcemy sobie podebugowaæ, wystarczy zdefiniowaæ GG_DEBUG. */ int gg_debug_level; #ifdef GG_DEBUG # define GG_DEBUG_NET 1 # define GG_DEBUG_TRAFFIC 2 # define GG_DEBUG_DUMP 4 # define GG_DEBUG_FUNCTION 8 # define GG_DEBUG_MISC 16 void gg_debug_real(int level, char *format, ...); # define gg_debug(x, y...) gg_debug_real(x, y) #else # define gg_debug(x, y...) while(0) { }; #endif /* GG_DEBUG */ /* * ------------------------------------------------------------------------- * poni¿ej znajduj± siê wewnêtrzne sprawy biblioteki. zwyk³y klient nie * powinien ich w ogóle ruszaæ, bo i nie ma po co. wszystko mo¿na za³atwiæ * procedurami wy¿szego poziomu, których definicje znajduj± siê na pocz±tku * tego pliku. * ------------------------------------------------------------------------- */ int gg_resolve(int *fd, int *pid, char *hostname); int gg_connect(void *addr, int port, int async); char *gg_alloc_sprintf(char *format, ...); #define GG_APPMSG_HOST "appmsg.gadu-gadu.pl" #define GG_APPMSG_PORT 80 #define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl" #define GG_PUBDIR_PORT 80 #define GG_DEFAULT_PORT 8074 struct gg_header { int type; /* typ pakietu */ int length; /* d³ugo¶æ reszty pakietu */ } __attribute__ ((packed)); #define GG_WELCOME 0x0001 struct gg_welcome { int key; /* klucz szyfrowania has³a */ } __attribute__ ((packed)); #define GG_LOGIN 0x000c struct gg_login { uin_t uin; /* twój numerek */ int hash; /* hash has³a */ int status; /* status na dzieñ dobry */ int dunno; /* == 0x0b */ int local_ip; /* mój adres ip */ short local_port; /* port, na którym s³ucham */ } __attribute__ ((packed)); #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_FRIENDS_MASK 0x8000 /* tylko dla znajomych (GG 4.6) */ struct gg_new_status { int status; /* na jaki zmieniæ? */ } __attribute__ ((packed)); #define GG_NOTIFY 0x0010 struct gg_notify { uin_t uin; /* numerek danej osoby */ char dunno1; /* == 3 */ } __attribute__ ((packed)); #define GG_NOTIFY_REPLY 0x000c /* tak, to samo co GG_LOGIN */ /* struct gg_notify_reply zadeklarowane wy¿ej */ #define GG_ADD_NOTIFY 0x000d #define GG_REMOVE_NOTIFY 0x000e struct gg_add_remove { uin_t uin; /* numerek */ char dunno1; /* == 3 */ } __attribute__ ((packed)); #define GG_STATUS 0x0002 /* struct gg_status zadeklarowane wcze¶niej */ #define GG_SEND_MSG 0x000b #define GG_CLASS_MSG 0x0004 #define GG_CLASS_CHAT 0x0008 struct gg_send_msg { int recipient; int seq; int msgclass; } __attribute__ ((packed)); #define GG_SEND_MSG_ACK 0x0005 #define GG_ACK_DELIVERED 0x0002 #define GG_ACK_QUEUED 0x0003 struct gg_send_msg_ack { int status; int recipient; int seq; } __attribute__ ((packed)); #define GG_RECV_MSG 0x000a struct gg_recv_msg { int sender; int dunno1; int dunno2; int msgclass; } __attribute__ ((packed)); #define GG_PING 0x0008 #define GG_PONG 0x0007 #ifdef __cplusplus } #endif #endif --- NEW FILE: protocol.txt --- --------------------------------------------------------------------------- protokó³ g*du-g*du 4.x (c) copyright 2001 by wojtek kaniewski <woj...@ir...> --- 0) disclaimer --------------------------------------------------------- wszystkie informacje bazuj± na do¶wiadczeniach przeprowadzonych na moim domowym komputerze. ¿aden klient g*du-g*du nie zosta³ skrzywdzony podczas przeprowadzania badañ, blabla. --- 1) transmisja, format wszystkich pakietów ----------------------------- w przeciwieñstwie do zabawek typu icq, g*du-g*du korzysta z protoko³u tcp. ka¿dy pakiet zawiera dwa sta³e pola: struct gg_header { int type; /* typ pakietu */ int length; /* d³ugo¶æ reszty pakietu */ }; dla u³atwienia przyjmujê nastêpuj±ce d³ugo¶ci zmiennych: sizeof(char) = 1, sizeof(short) = 2, sizeof(int) = 4. oczywi¶cie wszystkie liczby s± zgodnie z intelowym endianem. zak³adam te¿, ¿e wszystkie zmienne s± bez znaku. nie chce mi siê wszêdzie pisaæ `unsigned'. pola, co do których znaczenia nie mam pewno¶ci, lub w ogóle nie mam pojêcia, sk±d siê tam wziê³y, oznaczam `dunno'. --- 2) zanim siê po³±czymy ------------------------------------------------- ¿eby wiedzieæ, z jakim serwerem mamy siê po³±czyæ, nale¿y poudawaæ przez chwilê Internet Explorera, po³±czyæ siê z hostem `appmsg.gadu-gadu.pl'. GET /appsvc/appmsg.asp?fmnumber=<tutaj_numerek_gg> HTTP/1.0 Host: appmsg.gadu-gadu.pl User-Agent: Mozilla/4.7 [en] (Win98; I) Pragma: no-cache na co powinni¶my dostaæ odpowied¼ w stylu: HTTP/1.0 200 OK 0 1 0 217.17.33.21:8074 217.17.33.21 217.17.33.21 co to oznacza? nie mam pojêcia ;) wygl±da na to, ¿e ca³y g*du-g*du jest przemy¶lany i w przysz³o¶ci bêdzie mo¿na u¿ywaæ ró¿nych serwerów do ró¿nych rzeczy, typu szukanie, obs³uga klientów itd. póki co, ³±czyæ siê trzeba na pierwszy adres (tak, ten z portem). --- 3) logowanie siê ------------------------------------------------------- po po³±czeniu siê portem 8074 serwera g*du-g*du, dostajemy pakiet typu 0x0001, który na potrzeby tego dokumentu nazwiemy: #define GG_WELCOME 0x0001 reszta pakietu zawiera liczbê, na podstawie której liczony jest hash z has³a klienta: struct gg_welcome { int key; /* klucz szyfrowania has³a */ }; kiedy mamy ju¿ t± warto¶æ mo¿emy odes³aæ pakiet logowania #define GG_LOGIN 0x000c musimy podaæ kilka informacji: struct gg_login { int uin; /* twój numerek */ int hash; /* hash has³a */ int status; /* status na dzieñ dobry */ int dunno1; /* == 0x0b */ int local_ip; /* mój adres ip */ short local_port; /* port, na którym s³ucham */ }; jak obliczyæ hash has³a? hmm... nic prostszego. do ka¿dej literki has³a dodaje siê jedynkê, mno¿y wszystko razem, a potem przez liczbê podan± przez serwer. for (hash = 1; *passwd; passwd++) hash *= (*passwd) + 1; zrozumia³e, racja? je¶li wszystko siê powiedzie, dostaniemy w odpowiedzi pakiet typu #define GG_LOGIN_OK 0x0003 z polem header->length = 0, lub pakiet #define GG_LOGIN_FAILED 0x0009 --- 4) zmiana statusu ----------------------------------------------------- g*du-g*du przewiduje trzy stany klienta, które zmieniamy pakietem #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 */ #define GG_STATUS_FRIENDS_MASK 0x8000 /* tylko dla przyjació³ */ struct gg_new_status { int status; /* na jaki zmieniæ? */ } nale¿y pamiêtaæ, ¿eby przed roz³±czeniem siê z serwerem nale¿y zmieniæ stan na GG_STATUS_NOT_AVAIL. je¶li ma byæ widoczny tylko dla przyjació³, nale¿y dodaæ GG_STATUS_FRIENDS do normalnej warto¶ci stanu. --- 5) ludzie przychodz±, ludzie odchodz± --------------------------------- zaraz po zalogowaniu mo¿emy wys³aæ serwerowi listê ludzików w naszej li¶cie kontaktów, ¿eby dowiedzieæ siê, czy s± w tej chwili dostêpni. pakiet zawiera dowoln± ilo¶æ struktur gg_notify: #define GG_NOTIFY 0x0010 struct gg_notify { int uin; /* numerek danej osoby */ char dunno1; /* == 3 */ }; je¶li kto¶ jest, serwer odpowie pakietem zawieraj±cym jedn± lub wiêcej struktur gg_notify_reply: #define GG_NOTIFY_REPLY 0x000c /* tak, to samo co GG_LOGIN */ struct gg_notify_reply { int uin; /* numerek */ int status; /* status danej osoby */ int remote_ip; /* adres ip delikwenta */ short remote_port; /* port, na którym s³ucha klient */ int dunno1; /* == 0x0b */ short dunno2; /* znowu port? */ }; je¶li klient nie obs³uguje po³±czeñ miêdzy klientami (np. g*du-g*du 3.x) zamiast adresu ip jest 0, zamiast portu jest 2. niewa¿ne ;) w ka¿dym razie, je¶li kto¶ siê pojawi w trakcie pracy, równie¿ zostanie przys³any ten pakiet. proste? proste :) ¿eby dodaæ kogo¶ do listy w trakcie pracy, trzeba wys³aæ ni¿ej opisany pakiet. jego format jest identyczny jak przy GG_NOTIFY. #define GG_ADD 0x000d struct gg_add { int uin; /* numerek */ char dunno1; /* == 3 */ }; je¶li kto¶ opu¶ci g*du-g*du lub zmieni stan, otrzymamy pakiet #define GG_STATUS 0x0002 struct gg_status { int uin; /* numerek */ int status; /* nowy stan */ }; --- 6) wysy³anie wiadomo¶ci ------------------------------------------------ przejd¼my do sedna sprawy ;) #define GG_SEND_MSG 0x000b #define GG_CLASS_MSG 0x0004 #define GG_CLASS_CHAT 0x0008 struct gg_send_msg { int recipient; int seq; int class; char message[]; }; wiadomo, odbiorca. numer sekwencyjny, który wykorzystujemy potem do potwierdzenia. nie wykluczone, ¿e w jakis sposób odró¿nia siê ró¿ne rozmowy za pomoc± czê¶ci bajtów, ale raczej nie ma znaczenia. klasa wiadomo¶ci pozwala odró¿niæ, czy wiadomo¶æ ma siê pokazaæ w osobym okienku czy jako kolejna linijka w okienku rozmowy. wygl±da na to, ¿e to jaka¶ bitmapa, wiêc najlepiej olaæ inne bity ni¿ 0x0f. (czasem klienty wysy³aj± 0x04, czasem 0x24) serwer po otrzymaniu wiadomo¶ci odsy³a informacjê o tym. przy okazji mówi, czy wiadomo¶æ dotar³a do odbiorcy (status == GG_ACK_DELIVERED), czy mo¿e jest offline i zosta³a zakolejkowana (GG_ACK_QUEUED): #define GG_SEND_MSG_ACK 0x0005 #define GG_ACK_DELIVERED 0x0002 #define GG_ACK_QUEUED 0x0003 struct gg_send_msg_ack { int status; int recipient; int seq; }; numer sekwencyjny i adresat ten sam, co przy wysy³aniu. --- 7) otrzymywanie wiadomo¶ci --------------------------------------------- zbyt wiele wyja¶nieñ chyba nie trzeba. wiadomo od kogo. nieznane pola to co¶ a'la numer sekwencyjny albo identyfikator okienka z rozmow± albo nowe dane dla setiathome. klasa wiadomo¶ci taka sama jak przy wysy³aniu: #define GG_RECV_MSG 0x000a struct gg_recv_msg { int sender; int dunno1; int dunno2; int class; char message[]; }; --- 8) otrzymywanie wiadomo¶ci --------------------------------------------- od czasu do czasu klient wysy³a pakiet a'la ping do serwera i dostaje pust± odpowied¼. ciê¿ko stwierdziæ, czy serwer wywala, je¶li nie dostanie pinga przez jaki¶ czas, czy klient siê roz³±cza, je¶li serwer mu nie odpowie. jako¶ nie chce mi siê sprawdzaæ ;) #define GG_PING 0x0008 /* nie ma niczego */ #define GG_PONG 0x0007 /* nie ma niczego */ --- 9) podziêkowania ------------------------------------------------------- swój wk³ad w poznanie protoko³u mia³ Robert Wo¼ny, który opisa³ nowo¶ci w GG 4.6. ---------------------------------------------------------------------------- $Id: protocol.txt,v 1.1 2001/09/29 23:06:30 warmenhoven Exp $ |