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
/*
* 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;
}
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
/*
* 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;
}
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 😉