Libgadu jest biblioteką napisaną w języku C służącą do komunikacji z protokołem Gadu-Gadu rozwijaną od 2006 roku. Wraz z rozwojem protokołu GG rozwija się także ta biblioteka, dzięki czemu możliwe jest tworzenie własnych komunikatorów obsługujących „słoneczko”. Przykładami takich komunikatorów są Kadu, Pidgin oraz EKG.
Wraz z pojawieniem się testowej wersji Libgadu w wersji 1.9.0-rc2 autor postanowił dołączyć do niej kilka przykładowych programów, które w znaczący sposób powinny pomóc w zrozumieniu zasady jej działania.
Przed rozpoczęciem zabawy z językiem C jak i Libgadu należy zaopatrzyć się przede wszystkim w kompilator oraz samą bibliotekę.
W celu zademonstrowania zasady działania Libgadu posłużę się systemem Ubuntu 9.10. Zainstalujmy konsolowy kompilator gcc
sudo apt-get install build-essentialsudo apt-get install gcc
Teraz czas na bibliotekę Libgadu. Pobierzmy ją z oficjalnej strony jej autora. Jak już wcześniej wspomniano interesujemy się wersją 1.9.0-rc2 pomimo, iż jest to wersja testowa, w starszych wydaniach nie ma zamieszczonych przykładów.
Zapiszmy archiwum w katalogu domowym, rozpakujmy je i przejdźmy do nowo utworzonego katalogu libgadu-1.9.0-rc2
tar -zxvf libgadu-1.9.0-rc2.tar.gzcd libgadu-1.9.0-rc2
Wydajmy kolejno 3 polecenia instalacyjne
./configuremakesudo make install
Jeśli wszystko poszło zgodnie z planem przejdźmy do katalogu examples i wylistujmy jego zawartość
cd examplesls
Zauważymy następujące pliki
root@cyber-jadro:/home/komeniusz/libgadu-1.9.0-rc2/examples# ls conn-async register_async-register.o send.c conn-async.c register.c send.o conn-async.o register.o status httphash register-sync status.c httphash.c remind-async status.o httphash-httphash.o remind_async-remind.o token-async Makefile remind.c token_async-token.o Makefile.am remind.o token.c Makefile.in remind-sync token.o register-async send token-sync root@cyber-jadro:/home/komeniusz/libgadu-1.9.0-rc2/examples#
Zainteresujmy się plikiem status.c
[codesyntax lang=”c”]
/* * przykład prostego programu łączącego się z serwerem i zmieniającego opis. */ //Deklaracja niezbędnych bibliotek #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include "libgadu.h" int main(int argc, char **argv) { //Po wykonaniu połączenia struktura gg_session //Jest wypełniania informacjami o aktualnej sesji użytkownika struct gg_session *gs; //Struktura gg_login_params zawiera parametry //Służące do połączenia z serwerem //Takie jak numer, hasło, port, czy opis struct gg_login_params glp; //Program pobiera od użytkownika 3 parametry //Takie jak numer, hasło oraz opis, //Który program powinien ustawić //Jeśli ich nie podamy kończymy działanie //Programu - return 1; if (argc < 4) { fprintf(stderr, "użycie: %s <mójnumerek> <mojehasło> <opis>n", argv[0]); return 1; } //Zmienna gg_debug_level odpowiada za informowanie //O stanie połączenia z serwerem. //Wartość 255 informuje nas o wszystkim. //Jeśli nie chcemy wyświetlać tych informacji //Zmieńmy tą wartość na 0 gg_debug_level = 255; //Poniżej następuje zwolnienie struktury //Parametrów logowania oraz ich ponowne //Wypełnienie memset(&glp, 0, sizeof(glp)); glp.uin = atoi(argv[1]); glp.password = argv[2]; // glp.encoding = GG_ENCODING_UTF8; // glp.protocol_version = 0x2d; //Ustawiamy status, na jaki //Mamy ustawić opis (niewidoczny) //Inne statusy, które można wybrać /* GG_STATUS_NOT_AVAIL Niedostępny. GG_STATUS_NOT_AVAIL_DESCR Niedostępny z opisem. GG_STATUS_FFC PoGGadaj ze mną. GG_STATUS_FFC_DESCR PoGGadaj ze mną z opisem. GG_STATUS_AVAIL Dostępny. GG_STATUS_AVAIL_DESCR Dostępny z opisem. GG_STATUS_BUSY Zajęty. GG_STATUS_BUSY_DESCR Zajęty z opisem. GG_STATUS_DND Nie przeszkadzać. GG_STATUS_DND_DESCR Nie przeszakdzać z opisem. GG_STATUS_INVISIBLE Niewidoczny (tylko własny status). GG_STATUS_INVISIBLE_DESCR Niewidoczny z opisem (tylko własny status). GG_STATUS_BLOCKED Zablokowany (tylko status innych). GG_STATUS_FRIENDS_MASK Flaga bitowa dostępności tylko dla znajomych. */ //Swoją drogą ta linijka jest zbędna.. // glp.status = GG_STATUS_INVISIBLE_DESCR; //Ustawiamy wartość opisu //Na podany w parametrze //Swoją drogą ta linijka jest zbędna.. // glp.status_descr = argv[3]; //Jeśli serwer zwróci błąd //Kończymy działanie programu if (!(gs = gg_login(&glp))) { printf("Nie udało się połączyć: %sn", strerror(errno)); gg_free_session(gs); return 1; } //Aby serwer GG zaakceptował nasze połączenie //Tak, abyśmy mogli wysyłać wiadomości //Należy wysłać listę kontaktów, //Nawet, jeżeli jest ona pusta gg_notify(gs, NULL, 0); //Informacja o prawidłowym połączeniu printf("Połączono.n"); //Jesteśmy połączeni z serwerem //Zmieniamy status i opis przy pomocy //Funkcji gg_change_status_descr() //Jako parametr przyjmuje ona sesje, //Status, oraz opis. //W tym wypadku status //Niedostępny z opisem podanym jako //Trzeci parametr //--- //Lecz, jeśli zwróci ona błąd //Kończymy działanie programu //--- if (gg_change_status_descr(gs, GG_STATUS_NOT_AVAIL_DESCR, argv[3]) == -1) { printf("Połączenie przerwane: %sn", strerror(errno)); //Zwolnienie pamięci sesyjnej gg_free_session(gs); return 1; } //Wylogowujemy się gg_logoff(gs); //Zwalniamy pamięć związaną z sesją gg_free_session(gs); //Kończymy działanie programu return 0; }
[/codesyntax]
Myślę, że wystarczająco dobrze skomentowałem powyższy przykład programu do zmiany statusu. Spróbujmy go skompilować oraz uruchomić dodając do kompilatora parametr -lgadu.
gcc -o status status.c -lgadukomeniusz@cyber-jadro:~/libgadu-1.9.0-rc2/examples$ ./status użycie: ./status <mójnumerek> <mojehasło> <opis> komeniusz@cyber-jadro:~/libgadu-1.9.0-rc2/examples$./status 12345678905 nasze_haslo "Nie zwracaj na mnie uwagi :-)"
Dzięki wartości zmiennej gg_debug_level ustawionej na 255 otrzymujemy w oknie terminala dokładny przebieg czynności, jakie wykonał program.
Teraz czas na coś z nieco innej półki. Przeanalizujmy kod programu send.c, służącego do wysyłania wiadomości pod określony numer
[codesyntax lang=”c”]
/* * przykład prostego programu łączącego się z serwerem i wysyłającego * jedną wiadomość. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include "libgadu.h" int main(int argc, char **argv) { struct gg_session *sess; //Struktura gg_event //Zawiera zdarzenia odebrane //Z serwera, takie //Jak potwierdzenie dostarczenia //Wysłanej wiadomości struct gg_event *e; struct gg_login_params p; //Program pobiera 4 argumenty //Nasz numer, nasze hasło, //Numer na jaki wysłać wiadomość //Oraz samą wiadomość if (argc < 5) { fprintf(stderr, "użycie: %s <mójnumerek> <mojehasło> <numerek> <wiadomość>n", argv[0]); return 1; } gg_debug_level = 255; memset(&p, 0, sizeof(p)); p.uin = atoi(argv[2]); p.password = argv[1]; if (!(sess = gg_login(&p))) { printf("Nie udało się połączyć: %sn", strerror(errno)); gg_free_session(sess); return 1; } printf("Połączono.n"); if (gg_notify(sess, NULL, 0) == -1) { /* serwery gg nie pozwalaja wysylac wiadomosci bez powiadomienia o userliscie (przetestowane p.protocol_version [0x15; def] */ printf("Połączenie przerwane: %sn", strerror(errno)); gg_free_session(sess); return 1; } //Główne serce naszego programu //Funkcja gg_send_message() przyjmuje //Jako parametr sesje, klasę wiadomości /* GG_CLASS_MSG Wiadomość ma pojawić się w osobnym oknie. GG_CLASS_CHAT Wiadomość ma pojawić się w oknie rozmowy. GG_CLASS_CTCP Wiadomość przeznaczona dla klienta Gadu-Gadu. GG_CLASS_ACK Klient nie życzy sobie potwierdzenia. GG_CLASS_QUEUED Wiadomość zakolejkowana na serwerze (tylko przy odbieraniu). */ //Numer na jaki wysłać wiadomość //Oraz samą wiadomość :-) //--- //Jeśli coś pójdzie nie tak //Kończymy działanie programu //--- if (gg_send_message(sess, GG_CLASS_MSG, atoi(argv[3]), (unsigned char*) argv[4]) == -1) { printf("Połączenie przerwane: %sn", strerror(errno)); gg_free_session(sess); return 1; } /* poniższą część można olać, ale poczekajmy na potwierdzenie */ while (0) { //Jeśli wiadomość zostanie odrzucona przez serwer //Kończymy działanie programu //I informujemy o błędzie //--- //Funkcja gg_watch_fd() po odebraniu informacji //Od serwera zwraca informacje o zdarzeniu //W strukturze gg_event, lub NULL //W przypadku błędu //--- if (!(e = gg_watch_fd(sess))) { printf("Połączenie przerwane: %sn", strerror(errno)); gg_logoff(sess); gg_free_session(sess); return 1; } //Jeśli odebrano wiadomość - zdarzenie GG_EVENT_ACK //Informujemy o powodzeniu i zamykamy program //Pełna lista zdarzeń /* GG_EVENT_NONE Nie wydarzyło się nic wartego uwagi. GG_EVENT_MSG Otrzymano wiadomość. Przekazuje również wiadomości systemowe od numeru 0. GG_EVENT_NOTIFY Informacja o statusach osób z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. GG_EVENT_NOTIFY_DESCR Informacja o statusie opisowym osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. GG_EVENT_STATUS Zmiana statusu osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. GG_EVENT_ACK Potwierdzenie doręczenia wiadomości. GG_EVENT_PONG Utrzymanie połączenia. Obecnie serwer nie wysyła już do klienta ramek utrzymania połączenia, polega wyłącznie na wysyłaniu ramek przez klienta. GG_EVENT_CONN_FAILED Nie udało się połączyć. GG_EVENT_CONN_SUCCESS Połączono z serwerem. Pierwszą rzeczą, jaką należy zrobić jest wysłanie listy kontaktów. GG_EVENT_DISCONNECT Serwer zrywa połączenie. Zdarza się, gdy równolegle do serwera podłączy się druga sesja i trzeba zerwać połączenie z pierwszą. GG_EVENT_DCC_NEW Nowe połączenie bezpośrednie (6.x). GG_EVENT_DCC_ERROR Błąd połączenia bezpośredniego (6.x). GG_EVENT_DCC_DONE Zakończono połączenie bezpośrednie (6.x). GG_EVENT_DCC_CLIENT_ACCEPT Moment akceptacji klienta w połączeniu bezpośrednim (6.x). GG_EVENT_DCC_CALLBACK Zwrotne połączenie bezpośrednie (6.x). GG_EVENT_DCC_NEED_FILE_INFO Należy wypełnić file_info dla połączenia bezpośredniego (6.x). GG_EVENT_DCC_NEED_FILE_ACK Czeka na potwierdzenie pliku w połączeniu bezpośrednim (6.x). GG_EVENT_DCC_NEED_VOICE_ACK Czeka na potwierdzenie rozmowy w połączeniu bezpośrednim (6.x). GG_EVENT_DCC_VOICE_DATA Dane bezpośredniego połączenia głosowego (6.x). GG_EVENT_PUBDIR50_SEARCH_REPLY Odpowiedź katalogu publicznego. GG_EVENT_PUBDIR50_READ Odczytano własne dane z katalogu publicznego. GG_EVENT_PUBDIR50_WRITE Zmieniono własne dane w katalogu publicznym. GG_EVENT_STATUS60 Zmiana statusu osoby z listy kontaktów. GG_EVENT_NOTIFY60 Informacja o statusach osób z listy kontaktów. GG_EVENT_USERLIST Wynik importu lub eksportu listy kontaktów. GG_EVENT_IMAGE_REQUEST Żądanie przesłania obrazka z wiadommości. GG_EVENT_IMAGE_REPLY Przysłano obrazek z wiadomości. GG_EVENT_DCC_ACK Potwierdzenie transmisji w połączeniu bezpośrednim (6.x). GG_EVENT_DCC7_NEW Nowe połączenie bezpośrednie (7.x). GG_EVENT_DCC7_ACCEPT Zaakceptowano połączenie bezpośrednie (7.x), nowy deskryptor. GG_EVENT_DCC7_REJECT Odrzucono połączenie bezpośrednie (7.x). GG_EVENT_DCC7_CONNECTED Zestawiono połączenie bezpośrednie (7.x), nowy deskryptor. GG_EVENT_DCC7_ERROR Błąd połączenia bezpośredniego (7.x). GG_EVENT_DCC7_DONE Zakończono połączenie bezpośrednie (7.x). GG_EVENT_DCC7_PENDING Trwa próba połączenia bezpośredniego (7.x), nowy deskryptor. GG_EVENT_XML_EVENT Otrzymano komunikat systemowy (7.7). GG_EVENT_DISCONNECT_ACK Potwierdzenie zakończenia sesji. */ if (e->type == GG_EVENT_ACK) { printf("Wysłano.n"); gg_free_event(e); break; } gg_free_event(e); } //Wylogowujemy się gg_logoff(sess); //Zwalniamy pamięć związaną z sesją gg_free_session(sess); return 0; }
[/codesyntax]
Jak można zauważyć pobawiliśmy się zdarzeniami, a dokładniej mówiąc odebraliśmy jedno GG_EVENT_ACK informujące o poprawnym wysłaniu wiadomości. Po zaznajomieniu się z kodem czas na część praktyczną, czyli kompilacje i uruchomienie.
UWAGA!
W programie zawarte jest zabezpieczenie anti script kiddie, aby ktoś, kto nie rozumie programu nie miał możliwości bezmyślnego SPIM’owania innych użytkowników.
Jeśli jesteśmy na tyle inteligentni i poprawiliśmy kod możemy zacząć proces kompilacji.
gcc -o send send.c -lgadu
komeniusz@cyber-jadro:~/libgadu-1.9.0-rc2/examples$ ./send użycie: ./send <mójnumerek> <mojehasło> <numerek> <wiadomość> komeniusz@cyber-jadro:~/libgadu-1.9.0-rc2/examples$
./send 12345678905 nasze_haslo 50987654321 "Kocham Cie :*"
Program ten wysyła jedną wiadomość i zamyka połączenie z serwerem. Nic nie stoi jednak na przeszkodzie, abyśmy wysłali więcej jednakowych wiadomości pod dany numer, aby mieć, pewność, że odbiorca ją przeczyta 😉