Libgadu #1 – Examples

Kadu_logoLibgadu 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-essential
sudo 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.gz
cd libgadu-1.9.0-rc2

Wydajmy kolejno 3 polecenia instalacyjne

./configure
make
sudo make install

Jeśli wszystko poszło zgodnie z planem przejdźmy do katalogu examples i wylistujmy jego zawartość

cd examples
ls

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 -lgadu
komeniusz@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 😉

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *