Potrafiąc już masowo wysyłać jedną wiadomość pod kilka wcześniej zapisanych w pliku tekstowym numerów możemy z dużym powodzeniem poinformować w bardzo szybki i wygodny sposób naszych znajomych o jakimś wydarzeniu.
Co jednak, jeśli tą wiadomość zechcemy wysłać do całego miasta, aby poinformować wszystkich jego mieszkańców o jakiejś uroczystości? W takiej sytuacji z pomocą może nam przyjść katalog publiczny, dzięki któremu wyszukamy interesujące nas osoby na podstawie imienia, pseudonimu, płci, czy też miasta.
W examplach libgadu nie znajdziemy programu łączącego się z katalogiem publicznym, zatem musimy napisać go sami od podstaw. Przed przystąpieniem do pisania kodu wypadałoby zaznajomić się z strukturą katalogu publicznego. Jak widać otrzymujemy prawie, że gotowca z strony projektu. Jedyne co musimy zrobić to wyeliminować zabezpieczenie anty script kiddle oraz dopisać kilka linijek.
Najważniejsze rzeczy, na które należy zwrócić uwagę to:
- Struktura sesji (gg_login_params)
- Strultura zdarzenia (gg_event)
- Zdarzenie GG_EVENT_PUBDIR50_SEARCH_REPLY
- Wysłanie listy kontaktów przy pomocy funkcji gg_notify(), aby móc komunikować się z serwerem
- Optymalizacja kodu pod kątem odbierania „paczek” danych, ale tym zajmiemy się na końcu
Najsampierw sklepmy to, co już mamy wyłożone na tacy 🙂
[codesyntax lang=”c”]
/* Program łączy się z katalogiem Publicznym i pobiera dane. Autor: Komeniusz [Nie odpowiadam za użycie tego programu!] Licencja: GNU - edukacyjna! Program testowano na: System: Ubuntu 9.10 Biblioteka: Libgadu 1.9.0-rc2 (wersja testowa) Download Libgadu: http://toxygen.net/libgadu/ Dokumentacja Libgadu: http://toxygen.net/libgadu/doc/ */ //Niezbędne biblioteki #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include "libgadu.h" int main(int argc, char **argv) { if(argc < 3) { printf("Użycie: %s <numer> <hasło>n", argv[0]); return 1; } //Nasze strukturki struct gg_session *sesja; struct gg_event *event; struct gg_login_params p; gg_debug_level = 0; memset(&p, 0, sizeof(p)); p.uin = atoi(argv[1]); p.password = argv[2]; p.encoding = GG_ENCODING_UTF8; if(!(sesja = gg_login(&p))) { printf("Nie udało się połączyć: %sn", strerror(errno)); gg_free_session(sesja); return 1; } //Tworzymy nowe zapytanie do serwera gg_pubdir50_t zapytanie; zapytanie = gg_pubdir50_new(GG_PUBDIR50_SEARCH_REQUEST); if(!zapytanie) { printf("Brak pamięci"); } //Wypełniamy pola zapytania do wyszukiwania w katalogu //Struktura ogólna - gg_pubdir50_add(zapytanie, pole, "wartość"); //Zaczynamy od numeru - 0 gg_pubdir50_add(zapytanie, GG_PUBDIR50_START, "0"); //Przeszukiwanie według imienia - Anna gg_pubdir50_add(zapytanie, GG_PUBDIR50_FIRSTNAME, "Anna"); //Żądana płeć - kobieta gg_pubdir50_add(zapytanie, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_FEMALE); //Osoby aktualnie dostępne gg_pubdir50_add(zapytanie, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE); //Wszystkie rodzaje pól zapytań /* GG_PUBDIR50_UIN Numer Gadu-Gadu. GG_PUBDIR50_STATUS Status (tylko wynik wyszukiwania). GG_PUBDIR50_FIRSTNAME Imię. GG_PUBDIR50_LASTNAME Nazwisko. GG_PUBDIR50_NICKNAME Pseudonim. GG_PUBDIR50_BIRTHYEAR Rok urodzenia lub przedział lat oddzielony spacją. GG_PUBDIR50_CITY Miejscowość. GG_PUBDIR50_GENDER Płeć. GG_PUBDIR50_GENDER_FEMALE Kobieta. GG_PUBDIR50_GENDER_MALE Mężczyzna. GG_PUBDIR50_ACTIVE Osoba dostępna (tylko wyszukiwanie). GG_PUBDIR50_ACTIVE_TRUE Wyszukaj tylko osoby dostępne. GG_PUBDIR50_START Numer początkowy wyszukiwania (tylko wyszukiwanie). GG_PUBDIR50_FAMILYNAME Nazwisko rodowe (tylko wysyłanie informacji o sobie). GG_PUBDIR50_FAMILYCITY Miejscowość pochodzenia (tylko wysyłanie informacji o sobie). */ //Wykonujemy zapytanie gg_pubdir50(sesja, zapytanie); //Aby móc komunikować się z serwerem //Wysyłamy pustą listę kontaktów if(gg_notify(sesja, NULL, 0) == -1) { printf("Polaczenie przerwane: %sn", strerror(errno)); gg_free_session(sesja); return 1; } while(1) { if(!(event = gg_watch_fd(sesja))) { printf("Polaczenie przerwane: %sn", strerror(errno)); gg_logoff(sesja); gg_free_session(sesja); return 1; } //Jeśli odebraliśmy odpowiedź od //Katalogu publicznego /* GG_PUBDIR50_WRITE Wysłanie do serwera informacji o sobie. GG_PUBDIR50_READ Pobranie z serwera informacji o sobie. GG_PUBDIR50_SEARCH Wyszukiwanie w katalogu publicznym. GG_PUBDIR50_SEARCH_REPLY Wynik wyszukiwania w katalogu publicznym. */ if(event->type == GG_EVENT_PUBDIR50_SEARCH_REPLY) { gg_pubdir50_t wynik; int i, ilosc; wynik = event->event.pubdir50; ilosc = gg_pubdir50_count(wynik); if(ilosc < 1) { printf("Nie znaleziono"); return; } //Wyciąganie informacji for(i = 0; i < ilosc; i++) { const char *numer, *imie, *pseudo, *urodzony, *miasto, *status; numer = gg_pubdir50_get(wynik, i, GG_PUBDIR50_UIN); imie = gg_pubdir50_get(wynik, i, GG_PUBDIR50_FIRSTNAME); pseudo = gg_pubdir50_get(wynik, i, GG_PUBDIR50_NICKNAME); urodzony = gg_pubdir50_get(wynik, i, GG_PUBDIR50_BIRTHYEAR); miasto = gg_pubdir50_get(wynik, i, GG_PUBDIR50_CITY); status = gg_pubdir50_get(wynik, i, GG_PUBDIR50_STATUS); //Drukowanie informacji printf("Numer: %snImię: %snPseudonim: %sn" "Urodzony: %snMiejscowość: %sn", numer, imie, pseudo, urodzony, miasto); //Wyświetlamy status znalezionej osoby switch((status) ? atoi(status) : -1) { case GG_STATUS_AVAIL: printf("Dostępnyn"); break; case GG_STATUS_BUSY: printf("Zajętyn"); break; default: printf("(?)n"); } printf("n"); } } //Zwolnienie pamięci dla struktury zdarzenia gg_event_free(event); } //Zwolnienie pamięci dla struktury sesji gg_free_session(sesja); //Zwolnienie pamięci dla zapytania i odpowiedzi serwera gg_pubdir50_free(zapytanie); return 0; }
[/codesyntax]
Program przyjmuje 2 parametry – numer i hasło. Jako wynik zwraca 20 numerów z katalogu publicznego, pasujących do naszego zapytania. Przykładowy wynik może wyglądać następująco
Numer: 1213***
Imię: Anna
Pseudonim: anna
Urodzony: (null)
Miejscowość: Łódź
ZajętyNumer: 2095***
Imię: Anna
Pseudonim: Anna
Urodzony: 1994
Miejscowość: (null)
DostępnyNumer: 496***
Imię: Anna
Pseudonim: (null)
Urodzony: 1977
Miejscowość: (null)
Zajęty[…]
Program zwraca jedynie 20 numerów, ponieważ serwer GG zwraca taką ilość po wykonaniu zapytania. Aby wyciągnąć z niego wszystkie wyniki pasujące do naszego zapytania należy wykonać kilka zapytań z zmieniającym się numerem sekwencyjnym od, którego będziemy kontynuować przeszukiwanie bazy danych.
[codesyntax lang=”c”]
/* Program łączy się z katalogiem Publicznym i pobiera dane. Autor: Komeniusz [Nie odpowiadam za użycie tego programu!] Licencja: GNU - edukacyjna! Program testowano na: System: Ubuntu 9.10 Biblioteka: Libgadu 1.9.0-rc2 (wersja testowa) Download Libgadu: http://toxygen.net/libgadu/ Dokumentacja Libgadu: http://toxygen.net/libgadu/doc/ */ //Niezbędne biblioteki #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include "libgadu.h" int main(int argc, char **argv) { if(argc < 5) { printf("Użycie: %s <numer> <hasło> <limit> <tryb>n", argv[0]); return 1; } //Nasze strukturki struct gg_session *sesja; struct gg_event *event; struct gg_login_params p; //Licznik znalezionych osób int osoby; //Limit odbieranych danych int limit = atoi(argv[3]); //Rodzaj wyprowadzanych danych int tryb = atoi(argv[4]); gg_debug_level = 0; memset(&p, 0, sizeof(p)); p.uin = atoi(argv[1]); p.password = argv[2]; p.encoding = GG_ENCODING_UTF8; if(!(sesja = gg_login(&p))) { printf("Nie udało się połączyć: %sn", strerror(errno)); gg_free_session(sesja); return 1; } //Tworzymy nowe zapytanie do serwera gg_pubdir50_t zapytanie; zapytanie = gg_pubdir50_new(GG_PUBDIR50_SEARCH_REQUEST); if(!zapytanie) { printf("Brak pamięci"); } //Wypełniamy pola zapytania do wyszukiwania w katalogu //Struktura ogólna - gg_pubdir50_add(zapytanie, pole, "wartość"); //Zaczynamy od numeru - 0 gg_pubdir50_add(zapytanie, GG_PUBDIR50_START, "0"); //Przeszukiwanie według imienia - Anna gg_pubdir50_add(zapytanie, GG_PUBDIR50_FIRSTNAME, "Anna"); //Żądana płeć - kobieta //gg_pubdir50_add(zapytanie, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_FEMALE); //Osoby aktualnie dostępne //gg_pubdir50_add(zapytanie, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE); //Wszystkie rodzaje pól zapytań /* GG_PUBDIR50_UIN Numer Gadu-Gadu. GG_PUBDIR50_STATUS Status (tylko wynik wyszukiwania). GG_PUBDIR50_FIRSTNAME Imię. GG_PUBDIR50_LASTNAME Nazwisko. GG_PUBDIR50_NICKNAME Pseudonim. GG_PUBDIR50_BIRTHYEAR Rok urodzenia lub przedział lat oddzielony spacją. GG_PUBDIR50_CITY Miejscowość. GG_PUBDIR50_GENDER Płeć. GG_PUBDIR50_GENDER_FEMALE Kobieta. GG_PUBDIR50_GENDER_MALE Mężczyzna. GG_PUBDIR50_ACTIVE Osoba dostępna (tylko wyszukiwanie). GG_PUBDIR50_ACTIVE_TRUE Wyszukaj tylko osoby dostępne. GG_PUBDIR50_START Numer początkowy wyszukiwania (tylko wyszukiwanie). GG_PUBDIR50_FAMILYNAME Nazwisko rodowe (tylko wysyłanie informacji o sobie). GG_PUBDIR50_FAMILYCITY Miejscowość pochodzenia (tylko wysyłanie informacji o sobie). */ //Wykonujemy zapytanie gg_pubdir50(sesja, zapytanie); //Aby móc komunikować się z serwerem //Wysyłamy pustą listę kontaktów if(gg_notify(sesja, NULL, 0) == -1) { printf("Polaczenie przerwane: %sn", strerror(errno)); gg_free_session(sesja); return 1; } while(1) { if(!(event = gg_watch_fd(sesja))) { printf("Polaczenie przerwane: %sn", strerror(errno)); gg_logoff(sesja); gg_free_session(sesja); return 1; } //Jeśli odebraliśmy odpowiedź od //Katalogu publicznego /* GG_PUBDIR50_WRITE Wysłanie do serwera informacji o sobie. GG_PUBDIR50_READ Pobranie z serwera informacji o sobie. GG_PUBDIR50_SEARCH Wyszukiwanie w katalogu publicznym. GG_PUBDIR50_SEARCH_REPLY Wynik wyszukiwania w katalogu publicznym. */ if(event->type == GG_EVENT_PUBDIR50_SEARCH_REPLY) { gg_pubdir50_t wynik; int i, ilosc; wynik = event->event.pubdir50; ilosc = gg_pubdir50_count(wynik); char tab[32]; if(ilosc < 1) { printf("Nie znaleziono"); return; } //Łączna ilość znalezionych osób osoby += ilosc; //Wyciąganie informacji for(i = 0; i < ilosc; i++) { const char *numer, *imie, *pseudo, *urodzony, *miasto, *status; //printf("[%d]",i); numer = gg_pubdir50_get(wynik, i, GG_PUBDIR50_UIN); imie = gg_pubdir50_get(wynik, i, GG_PUBDIR50_FIRSTNAME); pseudo = gg_pubdir50_get(wynik, i, GG_PUBDIR50_NICKNAME); urodzony = gg_pubdir50_get(wynik, i, GG_PUBDIR50_BIRTHYEAR); miasto = gg_pubdir50_get(wynik, i, GG_PUBDIR50_CITY); status = gg_pubdir50_get(wynik, i, GG_PUBDIR50_STATUS); //Jeśli drukujemy wszystkie informacje if(tryb != 1) { //Drukowanie informacji printf("Numer: %snImię: %snPseudonim: %sn" "Urodzony: %snMiejscowość: %sn", numer, imie, pseudo, urodzony, miasto); //Wyświetlamy status znalezionej osoby switch((status) ? atoi(status) : -1) { case GG_STATUS_AVAIL: printf("Dostępnyn"); break; case GG_STATUS_BUSY: printf("Zajętyn"); break; default: printf("(?)n"); } //Jeśli drukujemy wyłącznie numery } else { printf("%s",numer); } printf("n"); } //Jeśli otrzymamy mniej, niż 20 wyników //Oznacza to, że nie ma więcej danych do pobrania if(ilosc < 20) { break; } //Jeśli ilość odebranych wyników będzie //Większa od ustalonego limitu if(osoby > limit) { break; } snprintf(tab, sizeof(tab), "%d", gg_pubdir50_next(wynik)); gg_pubdir50_add(zapytanie, GG_PUBDIR50_START, tab); gg_pubdir50(sesja, zapytanie); } //Zwolnienie pamięci dla struktury zdarzenia gg_event_free(event); } //Zwolnienie pamięci dla struktury sesji gg_free_session(sesja); //Zwolnienie pamięci dla zapytania i odpowiedzi serwera gg_pubdir50_free(zapytanie); return 0; }
[/codesyntax]
Powyższy program przyjmuje dodatkowo 2 parametry, maksymalną ilość pobranych wyników, nie przełamując sesji, tzn. jeśli podamy liczbę 31, a serwer będzie miał 237 wyników, dostaniemy ich 40. Drugi parametr natomiast to rodzaj wyświetlania pobranych danych. Jeśli będzie on równy 1 to program wyświetli wyłącznie numery, bez dodatkowych danych. Może nam się to przydać do sporządzenia pliku *.txt z numerami użytkowników naszego miasta, a następnie wykorzystanie ich przy pomocy programu dostępnego w poprzedniej części artykułu o bibliotece Libgadu.
komeniusz@cyber-jadro:~/libgadu/examples$ ./katalog numerek hasełko 2 1 > numery.txt
komeniusz@cyber-jadro:~/libgadu/examples$ cat numery.txt
1672****
11991****
593****
882****
3419****
3323****
1153****
621****
8006****
130****
592****
648****
9690****
131****
174****
283****
879****
649****
642****
8847****
komeniusz@cyber-jadro:~/libgadu/examples$
Jak widać plik *.txt możemy utworzyć wykorzystując standardowy potok Linuksowy >
Co jednak, jeśli zależy nam na mniejszej komplikacji operacji, bez potrzeby korzystania z dwóch programów? Mhh… Można połączyć powyższy katalog.c z plikiem send.c.
[codesyntax lang=”c”]
/* Program łączy się z katalogiem Publicznym, pobiera dane I wysyła ustaloną wiadomość. Autor: Komeniusz [Nie odpowiadam za użycie tego programu!] Licencja: GNU - edukacyjna! Program testowano na: System: Ubuntu 9.10 Biblioteka: Libgadu 1.9.0-rc2 (wersja testowa) Download Libgadu: http://toxygen.net/libgadu/ Dokumentacja Libgadu: http://toxygen.net/libgadu/doc/ */ //Niezbędne biblioteki #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include "libgadu.h" int main(int argc, char **argv) { if(argc < 4) { printf("Użycie: %s <numer> <hasło> <limit>n", argv[0]); return 1; } //Nasze strukturki struct gg_session *sesja; struct gg_event *event; struct gg_login_params p; //Licznik znalezionych osób int osoby; //Limit odbieranych danych int limit = atoi(argv[3]); gg_debug_level = 0; memset(&p, 0, sizeof(p)); p.uin = atoi(argv[1]); p.password = argv[2]; p.encoding = GG_ENCODING_UTF8; if(!(sesja = gg_login(&p))) { printf("Nie udało się połączyć: %sn", strerror(errno)); gg_free_session(sesja); return 1; } //Tworzymy nowe zapytanie do serwera gg_pubdir50_t zapytanie; zapytanie = gg_pubdir50_new(GG_PUBDIR50_SEARCH_REQUEST); if(!zapytanie) { printf("Brak pamięci"); } //Wypełniamy pola zapytania do wyszukiwania w katalogu //Struktura ogólna - gg_pubdir50_add(zapytanie, pole, "wartość"); //Zaczynamy od numeru - 0 gg_pubdir50_add(zapytanie, GG_PUBDIR50_START, "0"); //Miasto gg_pubdir50_add(zapytanie, GG_PUBDIR50_CITY, "Warszawa"); //Przeszukiwanie według imienia - Anna //gg_pubdir50_add(zapytanie, GG_PUBDIR50_FIRSTNAME, "wojtek"); //Żądana płeć - kobieta //gg_pubdir50_add(zapytanie, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_MALE); //Osoby aktualnie dostępne gg_pubdir50_add(zapytanie, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE); //Wszystkie rodzaje pól zapytań /* GG_PUBDIR50_UIN Numer Gadu-Gadu. GG_PUBDIR50_STATUS Status (tylko wynik wyszukiwania). GG_PUBDIR50_FIRSTNAME Imię. GG_PUBDIR50_LASTNAME Nazwisko. GG_PUBDIR50_NICKNAME Pseudonim. GG_PUBDIR50_BIRTHYEAR Rok urodzenia lub przedział lat oddzielony spacją. GG_PUBDIR50_CITY Miejscowość. GG_PUBDIR50_GENDER Płeć. GG_PUBDIR50_GENDER_FEMALE Kobieta. GG_PUBDIR50_GENDER_MALE Mężczyzna. GG_PUBDIR50_ACTIVE Osoba dostępna (tylko wyszukiwanie). GG_PUBDIR50_ACTIVE_TRUE Wyszukaj tylko osoby dostępne. GG_PUBDIR50_START Numer początkowy wyszukiwania (tylko wyszukiwanie). GG_PUBDIR50_FAMILYNAME Nazwisko rodowe (tylko wysyłanie informacji o sobie). GG_PUBDIR50_FAMILYCITY Miejscowość pochodzenia (tylko wysyłanie informacji o sobie). */ //Wykonujemy zapytanie gg_pubdir50(sesja, zapytanie); //Aby móc komunikować się z serwerem //Wysyłamy pustą listę kontaktów if(gg_notify(sesja, NULL, 0) == -1) { printf("Polaczenie przerwane: %sn", strerror(errno)); gg_free_session(sesja); return 1; } while(1) { if(!(event = gg_watch_fd(sesja))) { printf("Polaczenie przerwane: %sn", strerror(errno)); gg_logoff(sesja); gg_free_session(sesja); return 1; } //Jeśli odebraliśmy odpowiedź od //Katalogu publicznego /* GG_PUBDIR50_WRITE Wysłanie do serwera informacji o sobie. GG_PUBDIR50_READ Pobranie z serwera informacji o sobie. GG_PUBDIR50_SEARCH Wyszukiwanie w katalogu publicznym. GG_PUBDIR50_SEARCH_REPLY Wynik wyszukiwania w katalogu publicznym. */ if(event->type == GG_EVENT_PUBDIR50_SEARCH_REPLY) { gg_pubdir50_t wynik; int i, ilosc; wynik = event->event.pubdir50; ilosc = gg_pubdir50_count(wynik); char tab[32]; if(ilosc < 1) { printf("Nie znaleziono"); return; } //Łączna ilość znalezionych osób osoby += ilosc; //Wyciąganie informacji for(i = 0; i > ilosc; i++) { const char *numer, *imie, *pseudo, *urodzony, *miasto, *status; //printf("[%d]",i); numer = gg_pubdir50_get(wynik, i, GG_PUBDIR50_UIN); imie = gg_pubdir50_get(wynik, i, GG_PUBDIR50_FIRSTNAME); pseudo = gg_pubdir50_get(wynik, i, GG_PUBDIR50_NICKNAME); urodzony = gg_pubdir50_get(wynik, i, GG_PUBDIR50_BIRTHYEAR); miasto = gg_pubdir50_get(wynik, i, GG_PUBDIR50_CITY); status = gg_pubdir50_get(wynik, i, GG_PUBDIR50_STATUS); //Jeśli drukujemy wszystkie informacje //Wysyłanie wiadomości if(gg_send_message(sesja, GG_CLASS_MSG, atoi(numer), (unsigned char*) ":)") == -1) { printf("Połączenie przerwane: %sn", strerror(errno)); gg_free_session(sesja); return 1; } //Drukowanie informacji printf("Wysłano do %sn",numer); } //Jeśli otrzymamy mniej, niż 20 wyników //Oznacza to, że nie ma więcej danych do pobrania if(ilosc < 20) { break; } //Jeśli ilość odebranych wyników będzie //Większa od ustalonego limitu if(osoby > limit) { break; } snprintf(tab, sizeof(tab), "%d", gg_pubdir50_next(wynik)); gg_pubdir50_add(zapytanie, GG_PUBDIR50_START, tab); gg_pubdir50(sesja, zapytanie); } //Zwolnienie pamięci dla struktury zdarzenia gg_event_free(event); } //Zwolnienie pamięci dla struktury sesji gg_free_session(sesja); //Zwolnienie pamięci dla zapytania i odpowiedzi serwera gg_pubdir50_free(zapytanie); return 0; }
[/codesyntax]
Od teraz w celu wysłania jakiejś wiadomości do danej grupy sprecyzowanych osób wystarczy wypełnić interesujące nas kryteria, skompilować oraz uruchomić program.
komeniusz@cyber-jadro:~/libgadu/examples$ ./katalog2
Użycie: ./katalog2 <numer> <hasło> <limit>
komeniusz@cyber-jadro:~/libgadu/examples$ ./katalog2 numerek haselko 5
Wysłano do 175***
Wysłano do 340***
Wysłano do 659***
Wysłano do 122***
Wysłano do 128***
Wysłano do 18***
Wysłano do 61***
Wysłano do 729***
[…]
komeniusz@cyber-jadro:~/libgadu/examples$
Jako zadanie dodatkowe można uzupełnić program o pobieranie danych do zapytania poprzez parametry w sposób
./katalog2 -a numer -b haslo -c imie -d miasto
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.