nie ma cie na mojej liscie kontkatow przedstaw itp.
Jak to zrobic ??
--- 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= HTTP/1.0
Host: appmsg.gadu-gadu.pl
User-Agent: Mozilla/4.7 [en] (Win98; I)
Pragma: no-cache
oryginalny klient może wysłać jeden z podanych identyfikatorów przeglądarki:
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ą również `version=...' opisujące,
z jakim klientem serwer ma do czynienia. jednak ze względu na możliwe
różnice w protokole, lepiej pomijać ten parametr i uwagać GG 4.0. w każdym
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 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 może być 0, 1, 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 -------------------------------------------------------
Użytkownicy przeglądający to forum: Brak zarejestrowanych użytkowników oraz 33 gości