---------------------------------------------------------------------------

                          Protok G*du-G*du 4.x

     (C) Copyright 2001 by Wojtek Kaniewski <wojtekka@irc.pl>,
                           Robert J. Wony <speedy@atman.pl>,
                           Tomasz Jarzynka <tomee@cpi.pl>,
                           Adam Ludwikowski <adam.ludwikowski@wp.pl>,
                           Marek Kozina <klith@hybrid.art.pl>,
			   Rafa Florek <raf@regionet.regionet.pl>,
			   Igor Popik <igipop@wsfiz.edu.pl>

--- 0) disclaimer ---------------------------------------------------------

opis protokou bazuj na dowiadczeniach przeprowadzonych na moim
domowym komputerze oraz informacjach przysanych do mnie przez rnych
ludzi. aden klient g*du-g*du nie zosta skrzywdzony podczas
przeprowadzania bada, blabla.

--- 1) transmisja, format wszystkich pakietw -----------------------------

w przeciwiestwie do zabawek typu icq, g*du-g*du korzysta z protokou tcp.
kady pakiet zawiera dwa stae pola:

        struct gg_header {
		int type;		/* typ pakietu */
		int length;		/* dugo reszty pakietu */
	};

dla uatwienia przyjmuj nastpujce dugoci zmiennych: sizeof(char) = 1,
sizeof(short) = 2, sizeof(int) = 4. oczywicie wszystkie liczby s zgodnie
z intelowym endianem. zakadam te, e wszystkie zmienne s bez znaku. nie
chce mi si wszdzie pisa `unsigned'.

pola, co do ktrych znaczenia nie mam pewnoci, lub w ogle nie mam pojcia,
skd si tam wziy, oznaczam `dunno'.

--- 2) zanim si poczymy -------------------------------------------------

eby wiedzie, z jakim serwerem mamy si poczy, naley poudawa przez
chwil Internet Explorera, poczy 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

oryginalny klient moe wysa jeden z podanych identyfikatorw przegldarki:

	Mozilla/4.04 [en] (Win95; I ;Nav)
	Mozilla/4.7 [en] (Win98; I)
	Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)
	Mozilla/4.0 (compatible; MSIE 5.0; Windows NT)
	Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)
	Mozilla/4.0 (compatible; MSIE 5.0; Windows 98)

nowsze wersje klienta do zapytania dodaj rwnie `version=...' opisujce,
z jakim klientem serwer ma do czynienia. jednak ze wzgldu na moliwe
rnice w protokole, lepiej pomija ten parametr i uwaga GG 4.0. w kadym
razie na to zapytanie serwer powinien odpowiedzie:

	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 pojcia ;) wyglda na to, e cay g*du-g*du jest
przemylany i w przyszoci bdzie mona uywa rnych serwerw do rnych
rzeczy, typu szukanie, obsuga klientw itd. pki co, czy si trzeba na
pierwszy adres (tak, ten z portem).
Jeeli poczenie z portem 8074 nie wyjdzie z jaki specyficznych powodw -
mona si czy na port 443. 

--- 3) logowanie si -------------------------------------------------------

po poczeniu si portem serwera g*du-g*du, dostajemy pakiet typu 0x0001,
ktry na potrzeby tego dokumentu nazwiemy:

	#define GG_WELCOME 0x0001

reszta pakietu zawiera liczb, na podstawie ktrej liczony jest hash z hasa
klienta:

	struct gg_welcome {
		int key;		/* klucz szyfrowania hasa */
	};
	
kiedy mamy ju t warto moemy odesa pakiet logowania

	#define GG_LOGIN 0x000c

musimy poda kilka informacji:

	struct gg_login {
		int uin;		/* twj numerek */
		int hash;		/* hash hasa */
		int status;		/* status na dzie dobry */
		int version;		/* wersja klienta */
		int local_ip;		/* mj adres ip */
		short local_port;	/* port, na ktrym sucham */
	};

jak obliczy hash hasa? hmm... nic prostszego. do kadej literki hasa
dodaje si jedynk, mnoy wszystko razem, a potem przez liczb podan przez
serwer. 

	for (hash = 1; *passwd; passwd++)
		hash *= (*passwd) + 1;

zrozumiae, racja? liczba oznaczajca wersj moe by jedn z poniszych:

	0x11 - 4.6.1
	0x10 - 4.5.22, 4.5.21, 4.5.19, 4.5.17, 4.5.15
	0x0f - 4.5.12
	0x0b - 4.0.30, 4.0.29, 4.0.28, 4.0.25

oczywicie nie s to wszystkie moliwe wersje klientw, lecz te, ktre
udao si sprawdzi. najbezpieczniej bdzie przedstawia si jako ta
wersja, ktrej ficzerw uywamy. wiadomo, e 4.0.x nie obsugiway trybu
ukrytego, ani tylko dla znajomych itd.

jeli 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, ktre zmieniamy pakietem

	#define GG_NEW_STATUS 0x0002

	#define GG_STATUS_NOT_AVAIL 0x0001	/* rozczony */
	#define GG_STATUS_AVAIL 0x0002		/* dostpny */
	#define GG_STATUS_BUSY 0x0003		/* zajty */
	#define GG_STATUS_INVISIBLE 0x0014	/* niewidoczny */

	#define GG_STATUS_FRIENDS_MASK 0x8000	/* tylko dla przyjaci */
	
	struct gg_new_status {
		int status;			/* na jaki zmieni? */
	}

naley pamita, eby przed rozczeniem si z serwerem naley zmieni
stan na GG_STATUS_NOT_AVAIL. jeli ma by widoczny tylko dla przyjaci,
naley doda GG_STATUS_FRIENDS do normalnej wartoci stanu.

--- 5) ludzie przychodz, ludzie odchodz ---------------------------------

zaraz po zalogowaniu moemy wysa serwerowi list ludzikw w naszej licie
kontaktw, eby dowiedzie si, czy s w tej chwili dostpni. pakiet zawiera
dowoln ilo struktur gg_notify:

	#define GG_NOTIFY 0x0010
	
	struct gg_notify {
		int uin;		/* numerek danej osoby */
		char dunno1;		/* == 3 */
	};
	
jeli kto jest, serwer odpowie pakietem zawierajcym jedn lub wicej
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 ktrym sucha klient */
		int version;		/* wersja klienta */
		short dunno1;		/* znowu port? */
	};

jeli klient nie obsuguje pocze midzy klientami (np. g*du-g*du 3.x)
zamiast adresu ip jest 0, zamiast portu moe by 0, 1, 2... niewane ;)
port moe przyjmowa warto 1, jeli klient znajduje si za jakim
firewallem lub innym urzdzeniem robicym NAT. w kadym razie, jeli kto
si pojawi w trakcie pracy, rwnie zostanie przysany ten pakiet.
proste? proste :)

eby doda kogo do listy w trakcie pracy, trzeba wysa niej opisany
pakiet. jego format jest identyczny jak przy GG_NOTIFY.

	#define GG_ADD 0x000d
	
	struct gg_add {
		int uin;		/* numerek */
		char dunno1;		/* == 3 */
	};

jeli kto opuci g*du-g*du lub zmieni stan, otrzymamy pakiet

	#define GG_STATUS 0x0002
	
	struct gg_status {
		int uin;		/* numerek */
		int status;		/* nowy stan */
	};

--- 6) wysyanie wiadomoci ------------------------------------------------
	
przejdmy do sedna sprawy ;)

	#define GG_SEND_MSG 0x000b

	#define GG_CLASS_QUEUED 0x0001	/* tylko przy odbieraniu */
	#define GG_CLASS_MSG 0x0004
	#define GG_CLASS_CHAT 0x0008
	#define GG_CLASS_UNKNOWN_1 0x0020

	struct gg_send_msg {
		int recipient;
		int seq;
		int class;
		char message[];
	};

wiadomo, odbiorca. numer sekwencyjny, ktry wykorzystujemy potem do
potwierdzenia. nie wykluczone, e w jakis sposb odrnia si rne
rozmowy za pomoc czci bajtw, ale raczej nie ma znaczenia. klasa
wiadomoci pozwala odrni, czy wiadomo ma si pokaza w osobym
okienku czy jako kolejna linijka w okienku rozmowy. wyglda na to,
e to jaka bitmapa, wic najlepiej ola inne bity ni 0x0e. (czasem
klienty wysyaj 0x04, czasem 0x24 -- widocznie 0x20 to te jaka
flaga). jeli odbiorca by niedostpny podczas wysyania wiadomoci,
zostanie zaznaczony bit 0x01.

oryginalny klient wysyajc wiadomo do kilku uytkownikw, wysya po
prostu kilka takich samych pakietw z rnymi numerkami odbiorcw. nie
ma osobnego pakietu do tego. natomiast jeli chodzi o ,,konferencyjn''
do pakietu doklejana jest za ,,char message[];'' nastpujca struktura:

	struct gg_send_recipients {
		char flag;		/* == 1 */
		int count;		/* ilo odbiorcw */
		int recipients[];	/* tablica odbiorcw */
	};

na przykad, by wysa do trzech ludzi, naley wysa pakiet:

	- -- --- --+--+--+--+--+--+--+-----------+-----------+
          tre    |\0|\1|    0x02   |    uin1   |   uin2    |
	- -- -- ---+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

serwer po otrzymaniu wiadomoci odsya informacj o tym. przy okazji
mwi, czy wiadomo dotara do odbiorcy (status == GG_ACK_DELIVERED),
czy moe jest offline i zostaa 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 wysyaniu.

--- 7) otrzymywanie wiadomoci ---------------------------------------------

zbyt wiele wyjanie chyba nie trzeba. wiadomo od kogo. drugie pole to
najprawdopodobniej jaki numerek sekwencyjny. trzecie oznacza czas nadania
wiadomoci. klasa wiadomoci taka sama jak przy wysyaniu:

	#define GG_RECV_MSG 0x000a
	
	struct gg_recv_msg {
		int sender;
		int seq;
		int time;
		int class;
		char message[];
	};

w przypadku pakietw ,,konferencyjnych'' na koncu pakietu doklejona jest
struktura identyczna ze struct gg_send_recipients zawierajca pozostaych
rozmwcw.

--- 8) ping/pong -----------------------------------------------------------

od czasu do czasu klient wysya pakiet a'la ping do serwera i dostaje pust
odpowied. o ile dobrze pamitam, serwer rozcza si po upywie 5 minut od
otrzymania ostatniej informacji.

	#define GG_PING 0x0008
	
	/* nie ma niczego */
	
	#define GG_PONG 0x0007
	
	/* nie ma niczego */

--- 9) podzikowania -------------------------------------------------------

swj wkad w poznanie protokou mieli:
 - Robert J. Wony <speedy@atman.pl>:
   opis nowoci w protokole GG 4.6,
 - Tomasz Jarzynka <tomee@cpi.pl>:
   badanie timeoutw,
 - Adam Ludwikowski <adam.ludwikowski@wp.pl>:
   wiele rnych poprawek do tekstu, badanie wersji,
 - Marek Kozina <klith@hybrid.art.pl>:
   czas otrzymania wiadomoci,
 - Rafa Florek <raf@regionet.regionet.pl>:
   konferencje,
 - Igor Popik <igipop@wsfiz.edu.pl>:
   klasy wiadomoci przy odbieraniu zakolejkowanej.

----------------------------------------------------------------------------

$Id: protocol.txt,v 1.2 2001/11/27 22:54:32 warmenhoven Exp $

