/* $Id: libgg.h,v 1.15 2004/08/04 18:27:09 lschiere Exp $ */ /* * (C) Copyright 2001 Wojtek Kaniewski , * Robert J. Woźny * * 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 __GG_LIBGG_H #define __GG_LIBGG_H #ifdef __cplusplus extern "C" { #endif #if defined(sun) && !defined(INADDR_NONE) #define INADDR_NONE 0xffffffff #endif #ifdef HAVE_STDINT_H # include #endif #include /* * typ zmiennej określającej numerek danej osoby. */ typedef unsigned long uin_t; /* * struktura opisująca daną sesję. tworzona przez gg_login(). */ struct gg_session { int fd; /* podglądany deskryptor */ int check; /* sprawdzamy zapis czy odczyt */ int state; /* aktualny stan maszynki */ int error; /* kod błędu dla GG_STATE_ERROR */ int type; /* rodzaj sesji. == GG_SESSION_GG */ int async; /* czy połączenie jest asynchroniczne */ int pid; /* pid procesu resolvera */ int port; /* port, z którym się łączymy */ int seq; /* numer sekwencyjny ostatniej wiadomości */ int last_pong; /* czas otrzymania ostatniego ping/pong */ unsigned int server_ip; /* adres serwera */ unsigned int client_ip; /* adres klienta */ int client_port; /* port, na którym klient słucha */ uin_t uin; /* numerek klienta */ char *password; /* i jego hasło. zwalniane automagicznie */ int initial_status; /* początkowy stan klienta */ char *recv_buf; /* bufor na otrzymywane pakiety */ int recv_done; /* ile już wczytano do bufora */ int recv_left; /* i ile jeszcze trzeba wczytać */ char *userlist_reply; /* fragment odpowiedzi listy kontaktów */ int userlist_blocks; /* na ile kawałków podzielono listę kontaktów */ }; /* * ogólna struktura opisująca stan wszystkich operacji http. */ struct gg_http { int fd; /* podglądany deskryptor */ int check; /* sprawdzamy zapis czy odczyt */ int state; /* aktualny stan maszynki */ int error; /* kod błędu dla GG_STATE_ERROR */ int type; /* rodzaj sesji. == GG_SESSION_HTTP */ int async; /* czy połączenie asynchroniczne */ int pid; /* pid procesu resolvera */ int port; /* port, z którym się łączymy */ char *query; /* bufor zapytania http */ char *header; /* bufor nagłówka */ int header_size; /* rozmiar wczytanego nagłówka */ char *body; /* bufor otrzymanych informacji */ int body_size; /* ilość informacji */ void *data; /* dane danej operacji http */ }; /* * ogólna struktura opisująca różne sesje. przydatna w klientach. */ struct gg_common { int fd; /* podglądany deskryptor */ int check; /* sprawdzamy zapis czy odczyt */ int state; /* aktualny stan maszynki */ int error; /* kod błędu dla GG_STATE_ERROR */ int type; /* rodzaj sesji */ }; /* * rodzaje sesji. */ enum { GG_SESSION_GG = 1, /* połączenie z serwerem gg */ GG_SESSION_HTTP, /* ogólna sesja http */ GG_SESSION_SEARCH, /* szukanie */ GG_SESSION_REGISTER /* rejestrowanie */ }; /* * różne stany asynchronicznej maszynki. */ enum { /* wspólne */ GG_STATE_IDLE = 0, /* nie powinno wystąpić. */ GG_STATE_RESOLVING, /* wywołał gethostbyname() */ GG_STATE_CONNECTING, /* wywołał connect() */ GG_STATE_READING_DATA, /* czeka na dane http */ GG_STATE_ERROR, /* wystąpił błąd. kod w x->error */ /* gg_session */ GG_STATE_CONNECTING_GG, /* wywołał connect() */ GG_STATE_READING_KEY, /* czeka na klucz */ GG_STATE_READING_REPLY, /* czeka na odpowiedź */ GG_STATE_CONNECTED, /* połączył się */ /* gg_http */ GG_STATE_READING_HEADER, /* czeka na nagłówek http */ GG_STATE_PARSING, /* przetwarza dane */ GG_STATE_DONE /* skończył */ }; /* * dla zachowania kompatybilności wstecz. w wersji 1.0 będzie usunięte. oby. */ #define GG_STATE_WRITING_HTTP GG_STATE_READING_DATA #define GG_STATE_WAITING_FOR_KEY GG_STATE_READING_KEY #define GG_STATE_SENDING_KEY GG_STATE_READING_REPLY #define GG_STATE_FINISHED GG_STATE_DONE /* * co proces klienta powinien sprawdzać w deskryptorach? */ enum { GG_CHECK_NONE = 0, /* nic. nie powinno wystąpić */ GG_CHECK_WRITE = 1, /* sprawdzamy możliwość zapisu */ GG_CHECK_READ = 2 /* sprawdzamy możliwość odczytu */ }; 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_write(struct gg_session *sess, const char *buf, int length); int gg_change_status(struct gg_session *sess, int status); int gg_change_status_descr(struct gg_session *sess, int status, const char *descr); int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message); int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen); int gg_ping(struct gg_session *sess); int gg_userlist_request(struct gg_session *sess, char type, const char *request); struct gg_notify_reply { uin_t uin; /* numerek */ unsigned long status; /* status danej osoby */ unsigned long remote_ip; /* adres ip delikwenta */ unsigned short remote_port; /* port, na którym słucha klient */ unsigned long version; /* == 0x0b */ unsigned short dunno2; /* znowu port? */ } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #define GG_NOTIFY_REPLY60 0x0011 struct gg_notify_reply60 { uint32_t uin; /* numerek plus flagi w MSB */ uint8_t status; /* status danej osoby */ uint32_t remote_ip; /* adres ip delikwenta */ uint16_t remote_port; /* port, na którym słucha klient */ uint8_t version; /* wersja klienta */ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ uint8_t dunno1; /* 0x00 */ } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #define GG_STATUS60 0x000f struct gg_status60 { uint32_t uin; /* numerek plus flagi w MSB */ uint8_t status; /* status danej osoby */ uint32_t remote_ip; /* adres ip delikwenta */ uint16_t remote_port; /* port, na którym słucha klient */ uint8_t version; /* wersja klienta */ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ uint8_t dunno1; /* 0x00 */ } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; struct gg_status { uin_t uin; /* numerek */ unsigned long status; /* nowy stan */ } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; 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, GG_EVENT_STATUS60, /* ktoś zmienił stan w GG 6.0 */ GG_EVENT_NOTIFY60, /* ktoś się pojawił w GG 6.0 */ GG_EVENT_USERLIST, /* odpowiedź listy kontaktów w GG 6.0 */ }; /* * niedługo się tego pozbędę na rzecz sensownej obsługi błędów. --w */ enum { GG_FAILURE_RESOLVING = 1, GG_FAILURE_CONNECTING, GG_FAILURE_INVALID, GG_FAILURE_READING, GG_FAILURE_WRITING, GG_FAILURE_PASSWORD, GG_FAILURE_404 }; /* * rodzaje błędów, na razie używane przez http. bez rozczulania się nad * powodami. klient powie, że albo nie znalazł hosta, albo nie mógł się * połączyć, albo nie mógł wysłać, albo nie mógł odebrac. i tyle. jak * ktoś będzie chciał, to będzie mógł sprawdzić errno. ale po co? */ enum { GG_ERROR_RESOLVING = 1, GG_ERROR_CONNECTING, GG_ERROR_READING, GG_ERROR_WRITING }; /* * struktura opisująca rodzaj zdarzenia. wychodzi z gg_watch_fd() */ struct gg_event { int type; union { struct { uin_t sender; int msgclass; time_t time; unsigned char *message; } msg; struct gg_notify_reply *notify; struct { /* @notify60 informacja o liście kontaktów -- GG_EVENT_NOTIFY60 */ uin_t uin; /* numer */ int status; /* stan */ uint32_t remote_ip; /* adres ip */ uint16_t remote_port; /* port */ int version; /* wersja klienta */ int image_size; /* maksymalny rozmiar grafiki w KiB */ char *descr; /* opis stanu */ time_t time; /* czas powrotu */ } *notify60; struct gg_status status; struct { /* @status60 zmiana stanu -- GG_EVENT_STATUS60 */ uin_t uin; /* numer */ int status; /* nowy stan */ uint32_t remote_ip; /* adres ip */ uint16_t remote_port; /* port */ int version; /* wersja klienta */ int image_size; /* maksymalny rozmiar grafiki w KiB */ char *descr; /* opis stanu */ time_t time; /* czas powrotu */ } status60; struct { /* @userlist odpowiedź listy kontaktów serwera */ char type; /* rodzaj odpowiedzi */ char *reply; /* treść odpowiedzi */ } userlist; 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); /* * OBSŁUGA HTTP */ struct gg_http *gg_http_connect(char *hostname, int port, int async, char *method, char *path, char *header); int gg_http_watch_fd(struct gg_http *h); void gg_http_stop(struct gg_http *h); void gg_free_http(struct gg_http *h); /* * SZUKANIE UŻYTKOWNIKÓW */ /* * struktura opisująca kryteria wyszukiwania. argument gg_search(). */ struct gg_search_request { int active; /* czy ma szukać tylko aktywnych? */ /* mode 0 */ char *nickname; /* pseudonim */ char *first_name; /* imię */ char *last_name; /* nazwisko */ char *city; /* miasto */ int gender; /* płeć */ int min_birth; /* urodzony od roku... */ int max_birth; /* urodzony do roku... */ /* mode 1 */ char *email; /* adres e-mail */ /* mode 2 */ char *phone; /* numer telefonu */ /* mode 3 */ uin_t uin; /* numerek */ }; /* * struktura opisująca rezultat wyszukiwania. pole gg_http. */ struct gg_search { int count; /* ilość znalezionych */ struct gg_search_result *results; /* tabelka z nimi */ }; /* * pojedynczy rezultat wyszukiwania. */ struct gg_search_result { uin_t uin; /* numerek */ char *first_name; /* imię */ char *last_name; /* nazwisko */ char *nickname; /* pseudonim */ int born; /* rok urodzenia */ int gender; /* płeć */ char *city; /* miasto */ int active; /* czy jest aktywny */ }; #define GG_GENDER_NONE 0 /* nie podano lub bez znaczenia */ #define GG_GENDER_FEMALE 1 /* kobieta */ #define GG_GENDER_MALE 2 /* mężczyzna */ struct gg_http *gg_search(struct gg_search_request *r, int async); int gg_search_watch_fd(struct gg_http *f); void gg_free_search(struct gg_http *f); struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active); struct gg_search_request *gg_search_request_mode_1(char *email, int active); struct gg_search_request *gg_search_request_mode_2(char *phone, int active); struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active); /* * OPERACJE NA KATALOGU PUBLICZNYM */ struct gg_pubdir { int success; /* czy się udało */ uin_t uin; /* otrzymany numerek. 0 jeśli błąd */ }; struct gg_http *gg_register(char *email, char *password, int async); void gg_free_register(struct gg_http *f); int gg_pubdir_watch_fd(struct gg_http *f); #define gg_register_watch_fd gg_pubdir_watch_fd /* * jeśli chcemy sobie podebugować, wystarczy ustawić `gg_debug_level'. * niestety w miarę przybywania wpisów `gg_debug(...)' nie chciało mi * się ustawiać odpowiednich leveli, więc większość szła do _MISC. */ extern int gg_debug_level; #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(int level, char *format, ...); /* * Pare małych zmiennych do obsługi "http proxy" * */ extern int gg_http_use_proxy; extern char *gg_http_proxy_host; extern int gg_http_proxy_port; /* * ------------------------------------------------------------------------- * 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); void gg_debug(int level, char *format, ...); char *gg_alloc_sprintf(char *format, ...); char *gg_get_line(char **ptr); int gg_connect(void *addr, int port, int async); void gg_read_line(int sock, char *buf, int length); void gg_chomp(char *line); char *gg_urlencode(const char *str); int gg_http_hash(const unsigned char *email, const unsigned char *password); #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_REGISTER_HOST "register.gadu-gadu.pl" #define GG_REGISTER_PORT 80 #define GG_DEFAULT_PORT 8074 #define GG_HTTPS_PORT 443 #define GG_HTTP_USERAGENT "Mozilla/4.0 (compatible MSIE 5.0; Windows 98; I)" #define GG_HAS_AUDIO_MASK 0x40000000 #define GG_DEFAULT_CLIENT_VERSION "6, 0, 0, 132" struct gg_header { unsigned long type; /* typ pakietu */ unsigned long length; /* długość reszty pakietu */ } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #define GG_WELCOME 0x0001 struct gg_welcome { unsigned long key; /* klucz szyfrowania hasła */ } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #define GG_LOGIN 0x000c struct gg_login { uin_t uin; /* twój numerek */ unsigned long hash; /* hash hasła */ unsigned long status; /* status na dzień dobry */ unsigned long version; /* == 0x20 */ unsigned long local_ip; /* mój adres ip */ unsigned short local_port; /* port, na którym słucham */ } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #define GG_LOGIN60 0x0015 struct gg_login60 { uint32_t uin; /* mój numerek */ uint32_t hash; /* hash hasła */ uint32_t status; /* status na dzień dobry */ uint32_t version; /* moja wersja klienta */ uint8_t dunno1; /* 0x00 */ uint32_t local_ip; /* mój adres ip */ uint16_t local_port; /* port, na którym słucham */ uint32_t external_ip; /* zewnętrzny adres ip */ uint16_t external_port; /* zewnętrzny port */ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ uint8_t dunno2; /* 0xbe */ } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #define GG_LOGIN_OK 0x0003 #define GG_LOGIN_FAILED 0x0009 #define GG_NEW_STATUS 0x0002 #define GG_STATUS_NOT_AVAIL 0x0001 /* niedostępny */ #define GG_STATUS_NOT_AVAIL_DESCR 0x0015 /* niedostępny z opisem (4.8) */ #define GG_STATUS_AVAIL 0x0002 /* dostępny */ #define GG_STATUS_AVAIL_DESCR 0x0004 /* dostępny z opisem (4.9) */ #define GG_STATUS_BUSY 0x0003 /* zajęty */ #define GG_STATUS_BUSY_DESCR 0x0005 /* zajęty z opisem (4.8) */ #define GG_STATUS_INVISIBLE 0x0014 /* niewidoczny (4.6) */ #define GG_STATUS_INVISIBLE_DESCR 0x0016 /* niewidoczny z opisem (4.9) */ #define GG_STATUS_BLOCKED 0x0006 /* zablokowany */ #define GG_STATUS_FRIENDS_MASK 0x8000 /* tylko dla znajomych (4.6) */ #define GG_STATUS_DESCR_MAXSIZE 70 /* GG_S() stan bez uwzględnienia trybu tylko dla znajomych */ #define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK) /* GG_S_D() stan opisowy */ #define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || GG_S(x) == GG_STATUS_AVAIL_DESCR || GG_S(x) == GG_STATUS_BUSY_DESCR || GG_S(x) == GG_STATUS_INVISIBLE_DESCR) struct gg_new_status { unsigned long status; /* na jaki zmienić? */ } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #define GG_NOTIFY 0x0010 struct gg_notify { uin_t uin; /* numerek danej osoby */ char dunno1; /* == 3 */ } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #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 */ } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #define GG_STATUS 0x0002 /* struct gg_status zadeklarowane wcześniej */ #define GG_SEND_MSG 0x000b #define GG_CLASS_QUEUED 0x0001 #define GG_CLASS_OFFLINE GG_CLASS_QUEUED #define GG_CLASS_MSG 0x0004 #define GG_CLASS_CHAT 0x0008 struct gg_send_msg { unsigned long recipient; unsigned long seq; unsigned long msgclass; } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #define GG_SEND_MSG_ACK 0x0005 #define GG_ACK_DELIVERED 0x0002 #define GG_ACK_QUEUED 0x0003 struct gg_send_msg_ack { unsigned long status; unsigned long recipient; unsigned long seq; } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #define GG_RECV_MSG 0x000a struct gg_recv_msg { unsigned long sender; unsigned long seq; unsigned long time; unsigned long msgclass; } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #define GG_PING 0x0008 #define GG_PONG 0x0007 #define GG_USERLIST_REQUEST 0x0016 #define GG_USERLIST_PUT 0x00 #define GG_USERLIST_PUT_MORE 0x01 #define GG_USERLIST_GET 0x02 struct gg_userlist_request { uint8_t type; } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; #define GG_USERLIST_REPLY 0x0010 #define GG_USERLIST_PUT_REPLY 0x00 #define GG_USERLIST_PUT_MORE_REPLY 0x02 #define GG_USERLIST_GET_REPLY 0x06 #define GG_USERLIST_GET_MORE_REPLY 0x04 struct gg_userlist_reply { uint8_t type; } #ifdef __GNUC__ __attribute__ ((packed)) #endif ; /* listy */ struct list { void *data; struct list *next; }; typedef struct list * list_t; #ifdef __cplusplus } #endif #endif /* __GG_LIBGG_H */ /* * Local variables: * c-indentation-style: k&r * c-basic-offset: 8 * indent-tabs-mode: notnil * End: * * vim: shiftwidth=8: */