Copyright © 2002 libtlen-dev Team
Bibliotekę libtlen można wykorzystać w programach pisanych w C oraz C++. Należy tylko dołączać plik nagłówkowy biblioteki jak na przykładzie poniżej
#include <libtlen/libtlen.h> |
oraz linkować z biblioteką tlen, np:
gcc -g -Wall -O2 -ltlen main.c -o main |
Wszystkie funkcje oraz nazwy struktur z biblioteki dostępne dla programisty mają przedrostek tlen_, natomiast stałe zdefiniowane przedrostek TLEN_. Przed połączeniem z serwerem należy zadeklarować wskaźnik na strukturę tlen_session. W tej strukturze przechowywane są informacje o danej sesji. Jeśli chcemy, aby biblioteka wypisywała informacje o wykonywanych operacjach (np. łączenie z serwerem, autoryzacja) uruchamiamy funkcję
tlen_setdebug(1); |
Teraz można przejść do logowania. Dostępne są dwie funkcje: tlen_login oraz tlen_login_nb. Obie wymagają podania dwóch parametrów, odpowiednio loginu i hasła w postaci 'const char *'. Różnią się tym, że pierwsza z nich blokuje program zanim połączy się z serwerem, druga natomiast łączy się z serwerem w oddzielnym procesie, dzięki czemu program nie jest blokowany i przechodzi do obsługi zdarzeń (o tym później). Obie funkcję zwracają wskaźnik na strukturę tlen_session, który należy zachować (będzie potrzebny we wszystkich dalej opisanych funkcjach), lub NULL jeśli logowanie nie powiodło się. Po wywołaniu jednej z tych funkcji przechodzimy do obsługi zdarzeń.
O wszystkich zdarzeniach (nowa wiadomość, zmiana statusu kogoś z listy użytkowników, itp) program jest powiadamiany za pomocą eventów, których nazwy mają przedrostek TLEN_EVENT_. Poniżej opiszę poszczególne eventy, a potem pokażę przykładową pętlę obsługującą je.
Pierwszym zdarzeniem jest TLEN_EVENT_NOWGETID. Mówi nam ono o gotowości serwera, teraz można wysłać żądanie o identyfikator sesji. Służy do tego funkcja tlen_getid, której jako jedyny parametr przekazujemy wskaźnik do sesji. W rezultacie otrzymujemy event TLEN_EVENT_GOTID, a więc mamy już id sesji, czas na autoryzację. W tym celu wywołujemy funkcję tlen_authorize z tym samym parametrem co poprzednio. Jeśli ta operacja się powiedzie program otrzyma zdarzenie TLEN_EVENT_AUTHORIZED, w którego obsłudze można wysłać żądanie o książkę adresową, tlen_getroster (wywołanie również z jednym parametrem). O niepowodzeniu autoryzacji jesteśmy informowani zdarzeniem TLEN_EVENT_UNAUTHORIZED, w tym przypadku należy zakończyć działanie programu lub spróbować zalogować sie ponownie. Zdarzenie TLEN_EVENT_GOTROSTERITEM to pojedynczy wpis w naszej książce adresowej. Aby dostać się do tych informacji odwołujemy się do pola roster w strukturze tlen_event (o niej poźniej w opisie pętli). Pole te jest strukturą o nazwie tlen_user, zawiera nazwę osoby (name), identyfikator (jid) w postaci login@tlen.pl, nazwę grupy (group), rodzaj subskrypcji (subscription) oraz pole ask, które jeśli zawiera napis "subscribe", to znaczy, że czekamy na pozwolenie o subskrypcję u danej osoby. W przeciwnym razie pole ask zawiera NULL. Jeśli chcemy dodać kogoś do naszej listy, to musimy ją poprosić o subskrypcję, inaczej dana osoba nie będzie widoczna. Pole subscription natomiast określa poziom subskrypcji, jeśli zawiera napis "none" to oznacza brak subskrypcji u obu stron, "from" oznacza subskrypcję danej osoby w naszej książce (np. gdy czekamy na pozwolenie na subskrypcję), "to" oznacza, że jesteśmy zapisani u danej osoby ale ta osoba nie jest zapisana u nas (np. gdy zrezygnowaliśmy z kontaktu). Najwyższy poziom subskrypcji to "both", oznacza, że obie strony są zapisane w książce partnera. O końcu książki adresowej jesteśmy powiadamiani zdarzeniem TLEN_EVENT_ENDROSTER. Na tym kończy się obsługa tej części zdarzeń, teraz wywołujemy funkcję tlen_presence jak na przykładzie:
tlen_presence(sesja,TLEN_STATUS_AVAILABLE,"Dostępny"); |
gdzie pierwszy parametr jest wskaźnikiem na sesję, drugi oznacza status (w tym przypadku dostępny), w trzecim zaś podajemy własny opis.
Gdy ktoś dodaje nas do swojej listy kontaktów, jego program wysyła do nas prośbę o subskrypcję, wtedy otrzymujemy event SUBSCRIBE (dokładnie TLEN_EVENT_SUBSCRIBE). Aby subskrypcja przebiegła prawidłowo, należy najpierw zaakceptować nowy kontakt, nawet jeśli chcemy odrzucić prośbę. Inaczej druga strona nie dowie się o naszej decyzji. Jeśli dodatkowo nie mamy tej osoby na liście kontaktów, to należy ją dodać. Służą do tego funkcje odpowiednio "tlen_accept_subscribe" oraz "tlen_addcontact". Teraz należy zapytać użytkownika czy chce subskrybować daną osobę. Jeśli nie, to wywołujemy funkcje "tlen_removecontact" oraz "tlen_accept_unsubscribe".
Event SUBSCRIBED powiadamia nas o tym, że otrzymaliśmy pozwolenie na subskrypcję u danej osoby. Teraz możemy odświeżyć listę kontaktów.
Event UNSUBSCRIBE otrzymujemy, gdy ktoś usunie nas ze swojej listy kontaktów. Nie mamy wyboru, musimy ten fakt zaakceptować, wywołując funkcję "tlen_accept_unsubscribe". Zmieniamy pole "subscribe" na "none", odświeżamy listę i żądamy tego samego, czyli "tlen_request_unsubscribe". W rezultacie powinniśmy otrzymać event UNSUBSCRIBED.
Przechodzimy do ciekawszych rzeczy, czyli wiadomości. MESSAGE powiadamia nas o nowej wiadomości, która może być w postaci rozmowy (TLEN_CHAT) lub pojedynczej wiadomości.
Kiedy osoba zapisana na naszej liście zmienia swój status lub opis otrzymujemy event PRESENCE.
Event NEWMAIL to powiadomienie o nowej poczcie na skrzynce tlen.pl
Event GOTPUBDIRDATA informuje nas o nadejściu naszych danych z katalogu publicznego. Jest rezultatem wywołania funkcji "tlen_get_pubdir".
Aby zmienić nasze dane publiczne wywołujemy funkcję "tlen_change_pubdir". Jeśli ta operacja się powiedzie otrzymujemy event GOTPUBDIRUPDATEOK.
Funkcja "tlen_search" służy do przeszukiwania katalogu publicznego. O znalezionych pozycjach informuje nas event GOTSEARCHITEM. Event ENDSEARCH oznacza koniec znalezionych rekordów.
Każde zdarzenie uzupełnia odpowiednio strukturę tlen_event. Np. event MESSAGE musi gdzieś zapisywać informacje od kogo jest wiadomość i jaką ma treść. O tym mówi następna sekcja.
Ponieważ mamy kilka rodzaji zdarzeń, struktura musi zawierać pole określające typ, jest nim "type", które przyjmuje wartości TLEN_EVENT_* (dokładny opis znajduje się w pliku nagłówkowym biblioteki). Poza tym, znajdziemy wskaźniki do struktur odpowiednich typów. Dla danego zdarzenia alokowana jest pamieć dla struktury odpowiadającej temu zdarzeniu, na którą wskazuje odpowiedni wskaźnik. Dokladny opis tej struktury, jak i innych dla konkretnych zdarzeń, znajduje się w samym pliku nagłówkowym.
Załóżmy, że mamy już zadeklarowany w programie wskaźnik na strukturę tlen_session, czyli "struct tlen_session *sesja". Aby pobrać z kolejki zdarzenie potrzebujemy następujących zmiennych:
fd_set rd, wr; // obserwowane deskryptory socketów struct tlen_event *event; // wskaźnik na zdarzenie struct timeval tv; // czas blokowania programu przez select |
Inicjalizujemy te zmienne:
tv.tv_sec = 0; tv.tv_usec = 1; // jeśli brak danych to funkcja select nie blokuje programu FD_ZERO (&rd); FD_ZERO (&wr); if ((sesja->check & TLEN_CHECK_READ)) // jeśli czytamy dane FD_SET (sesja->fd, &rd); if ((sesja->check & TLEN_CHECK_WRITE)) // jeśli wysyłamy dane FD_SET (sesja->fd, &wr); |
Sprawdzamy obecność danych:
if (select (sesja->fd + 1, &rd, &wr, NULL, &tv) == -1) { perror("select"); // błąd z deskryptorem? exit(1); //wychodzimy z programu } |
Pobieramy zdarzenie:
if (sesja && (FD_ISSET (sesja->fd, &rd) || FD_ISSET (sesja->fd, &wr))) { tlen_watch_fd (sesja); while ((event=tlen_getevent(sesja))!=NULL) { switch (event->type) { case TLEN_EVENT_NOWGETID: //... } tlen_freeevent(event); //zwalniamy pamięć } |
Tu zaczyna się obsługa zdarzeń, o której była mowa wcześniej. Powyższy kod należy umieścic w funkcji, którą wywołujemy np. co sekundę. Ponadto aby serwer nie zerwał połączenia należy co jedną minutę wywoływać funkcję "tlen_ping(sesja)". Podczas działania programu sprawdzamy pole "error" w strukturze tlen_session, jeśli jest różne od zera, ponownie łączymy się z serwerem.
tlen_addcontact - dodanie osoby do ksiązki adresowej lub aktualizacja danych, oprócz loginu możemy podać nazwę dla danej osoby i nazwę grupy, do której chcemy ją zapisać.
tlen_removecontact - usuwanie osoby z książki.
tlen_request_subscribe - prośba o autoryzację u danej osoby.
tlen_request_unsubscribe - prośba o wypisanie się z książki danej osoby.
tlen_accept_subscribe - autoryzowanie danej osoby w naszej książce.
tlen_accept_unsubscribe - zatwierdzenie wypisania z naszej książki danej osoby.
tlen_presence - zmiana statusu, podajemy TLEN_STATUS_* oraz opis, jeśli podamy TLEN_STATUS_UNAVAILABLE to zerwiemy połączenie z serwerem.
tlen_sendmsg - wysłanie komunikatu, musimy określić typ (TLEN_CHAT dla rozmowy lub TLEN_MESSAGE dla wiadomości).
tlen_search - wyszukiwanie w katalogu publicznym.
tlen_new_pubdir - inicjalizacja struktury tlen_pubdir.
tlen_free_pubdir - zwolnienie pamięci zajmowanem przez strukturę tlen_pubdir.
tlen_get_pubdir - pobranie z serwera naszych danych.
tlen_change_pubdir - zmiana naszych danych w katalogu publicznym.
Parametry wyszukiwania:
<first> imię </first>
<last> nazwisko </last>
<nick> nick </nick>
<email> email </email>
<c> miasto </c>
<s> płeć </s> (TLEN_PUBDIR_GENDER_)
<d> minimalny wiek </d>
<u> maksymalny wiek </u>
<r> poszukuję </r> (TLEN_PUBDIR_LOOK_)
<g> rozmowa głosowa </g> (TLEN_PUBDIR_VOICE_)
<j> zawód </j> (TLEN_PUBDIR_JOB_)
<m> stan </m> (TLEN_STATUS_)
<p> plany </p> (TLEN_PUBDIR_PLANS_)
<e> szkoła </e>
<i> identyfikator </i>
Parametry naszych danych z katalogu publicznego są takie same jak wyszkiwaniu z róznicami:
<v> widoczność naszego stanu </v>
<d>, <u> zastąpione przez <b> - rok urodzenia
W naszych danych nie ma pola <i> !
Aby odczytać nasze dane z katalogu publicznego, wysyłamy zapytanie:
<iq type="get" id="tr" to="tuba"><query xmlns="jabber:iq:register"></query></iq> |
Serwer zwróci nam:
<iq type="result" id="tr" to="id@tlen.pl/t" from="tuba"><query xmlns="jabber:iq:register"><item>parametry</item></query></iq> |
parametry to tagi, które musimy parsować
Aby zmienić nasze dane w katalogu publicznym, wysyłamy zapytanie:
<iq type="set" id="tr" to="tuba"><query xmlns="jabber:iq:register">parametry</query></iq> |
gdzie parametry to tagi (takie samej jak przy odbiorze danych). Jeżeli wszystko poszło prawidłowo i nasze dane zostały uaktualnione, serwer zwróci potwierdzenie w postaci
<iq type="result" id="tw" to="id@tlen.pl/t" from="tuba"><query xmlns="jabber:iq:register"></query></iq> |
Aby przeszukać katalog publiczny wysyłamy do serwera zapytanie:
<iq type="get" id="src" to="tuba"><query xmlns="jabber:iq:search">tutaj to czego szukamy</query></iq> |
to czego szukamy to lista tagów z wybranymi przez nas właściwościami.
Jeżeli przy wyszukiwaniu nie szukamy wg. któregoś z tych parametrów, to po prostu nie umieszczamy go w zapytaniu. Większość parametrów liczbowych opisywana jest przez #define'y TLEN_PUBDIR_, które ułatwiają nam rozumienie zapytań. Nie dotyczy to parametrów wieku - gdzie po prostu podajemy odpowiednią liczbę. Po złożeniu zapytania wysyłamy je do serwera po czym czekamy na odpowiedź z wynikami.
Po wysłaniu zapytania serwer odpowie nam takim tagiem:
<iq type="result" id="src" to="id/@tlen.pl/t" from="tuba" n="1"><query xmlns="jabber:iq:search"> <item jid="jakistamid@tlen.pl">parametry</item> <item jid="jakistamid@tlen.pl">parametry</item> </query></iq> |
Zwrócona parametry są takie same jak te, które wysyłamy w zapytaniu z kilkoma wyjątkami:
Zamiast <d> i <u> zwracany jest tag <b> - wiek
Zamiast <m> stan zwracany jest w tagu <a>
Id użytkownika zwracany jest w głównym tagu <item> w parametrze jid, zamiast w <i>
Otrzymany wynik parsujemy. Każdy tag <item> odpowiada jednemu użytkownikowi. Ze względu na ograniczenia spamu serwer zwraca maksymalnie 20 wyników. Jeżeli nie ma w nich poszukwianej przez nas osoby, dokładniej sprecyzujmy parametry przeszukiwania.
Opis wszystkich funkcji biblioteki wraz z ich parametrami i dodatkowymi komentarzami.
Opis funkcji zawartych w pliku auth.c
Generuje hash potrzebny do zalogowania się
- pass - hasło - id - identyfikator sesji |
Funkcja wewnętrzna na potrzeby tlen_authorize
Deklaracja: char *tlen_hash (const char *pass, const char *id)
Wysyła prośbę o ID do serwera
- sesja - nasza sesja |
Wywoływane zaraz po połączeniu
Deklaracja: int tlen_getid (struct tlen_session *sesja)
Wysyła zapytanie logujące do serwera
- sesja - nasza sesja |
Wywoływane po otrzymaniu ID
Deklaracja: int tlen_authorize (struct tlen_session *sesja)
Opis funkcji zawartych w pliku events.c
Handler wykonywany przy rozpoczęciu tagu
- userData - dane parsera - name - nazwa obsługiwanego tagu - atts - parametry w tagu |
Funkcja wewnętrzna parsera zdarzeń
Deklaracja: void tlen_starttag_handler(void *userData, const XML_Char *name, const XML_Char **atts)
Handler wykonywany przy zakończeniu tagu
- userData - dane parsera - name - nazwa obsługiwanego tagu |
Funkcja wewnętrzna parsera zdarzeń
Deklaracja: void tlen_endtag_handler (void *userData, const XML_Char *name)
Buforuje otrzymywany tekst w celu otrzymania kompletnego tagu
- userData - dane parsera - s - tekst dodawany do bufora - len - długość tekstu |
Funkcja wewnętrzna parsera zdarzeń
Deklaracja: void tlen_char_handler (void *userData, const XML_Char *s, int len)
Parsuje jeden kompletny tag XML otrzymany od serwera
- sesja - sesja, w której obsługujemy ewentualne zdarzenia |
Funkcja wewnętrzna parsera zdarzeń
Deklaracja: void tlen_parsebuffer(struct tlen_session *sesja) {
Dodaje zdarzenie do kolejki zdarzeń
- sesja - sesja, do której kolejki dodajemy - e - zdarzenie |
Funkcja wewnętrzna parsera zdarzeń
Deklaracja: void tlen_addevent(struct tlen_session *sesja, struct tlen_event *e)
Pobiera kolejne (jeżeli istnieje) zdarzenie z kolejki
- sesja - sesja, której zdarzenia obsługujemy |
Zwraca zdarzenie, lub NULL jeżeli nie ma więcej
Deklaracja: struct tlen_event* tlen_getevent(struct tlen_session *sesja)
Tworzy nowe zdarzenie
- type - typ zdarzenia |
Zwraca zaalokowaną strukturę zdarzenia
Deklaracja: struct tlen_event* tlen_newevent(int type)
Zwalnia pamięć po zdarzeniu
- e - zdarzenie |
Wewnętrzna funkcja parsowania zdarzeń
Deklaracja: void tlen_freeevent(struct tlen_event *e)
funkcja sprawdzająca socket w poszukiwaniu jakiegoś zdarzenia
- sesja - nasza sesja |
Podczas błędów ustawia sesja->error i potrafi nawet rozłączyć gniazdo!
Deklaracja: void tlen_watch_fd (struct tlen_session *sesja)
Opis funkcji zawartych w pliku groupchat.c
Inicjuje konferencję
- sesja - nasza sesja - to - użytkownik z którym chcemy konferować |
Niezaimplementowana obsługa konferencji, funkcja testowa!
Deklaracja: int tlen_groupchat_init(struct tlen_session *sesja, char *to)
Zwraca sekundę danego dnia
- brak parametrów |
Potrzebna przy zapytaniach konferencyjnych
Deklaracja: int tlen_get_second()
Opis funkcji zawartych w pliku hub.c
Łączy się z serwerem
- host - adres - port - port |
Wewnętrzna funkcja dla resolvera huba
Deklaracja: int tlen_connect_server(const char* host, int port)
Tworzy i wysyła zapytanie do huba
- user - nazwa użytkownika |
Funkcja wewnętrzna resolvera huba
Deklaracja: xmlnode tlen_hub_query (const char* user)
Znajduje adres i port serwera z którym można się połączyć
- username - nazwa użytkownika - port - port (wskaźnik!) |
Funkcja wewnętrzna do resolvera huba
Deklaracja: char* tlen_find_server (const char* username, int* port)
Tworzy i obsługuje proces łączenia z hubem
- user - nazwa użytkownika - pipe - rórka |
Funkcja wewnętrzna resolvera huba
Deklaracja: void tlen_connect_hub_process (const char* user, int pipe)
Łączy się z hubem
- sess - sesja - blocking - blokowalne, lub nie |
jeśli blocking=0 uruchamia proces łączący się w tle z hubem
Deklaracja: int tlen_connect_hub (struct tlen_session *sess, int blocking)
Opis funkcji zawartych w pliku libtlen.c
Wywoływana na początku, w celu zainicjowania struktury sesji
- brak parametrów |
Przed wykonaniem tej funkcji nie wolno nic robić z sesją!
Deklaracja: struct tlen_session *tlen_init (void)
Ustawia, czy proces resolvovania huba ma być synchroniczny, czy asynchroniczny
- sess - sesja dla której ustawiamy parametr - blocking - 0 lub 1 |
Nie ustawiamy tego ręcznie, poprzez edycję struktury, a jedynie
Deklaracja: void tlen_set_hub_blocking(struct tlen_session *sess, int blocking)
Ustawia nazwę użytkownika i hasło w sesji
- sess - sesja dla której ustawiamy parametr - username - nazwa użytkownika - password - hasło |
Nie ustawiamy tego ręcznie, poprzez edycję struktury, a jedynie
Deklaracja: void tlen_set_auth(struct tlen_session *sess, char *username, char *password)
Ustawia adres i port serwera proxy dla transmisji
- sess - sesja dla której ustawiamy parametr - addr - adres serwera - port - port |
NIEZAIMPLEMENTOWANE
Deklaracja: void tlen_set_proxy(struct tlen_session *sess, char *addr, int port)
Podstawowa funkcja rozpoczynająca połączenie z serwerem
- sess - sesja opisująca połączenie |
Funkcja wypełnia różne pola w sesji, jako jej parametr podajemy
Deklaracja: void tlen_login (struct tlen_session *sess)
Wywoływane w celu zwolnienia pamięci po sesji
- sess - sesja, którą zwalniamy |
Najpierw nalezy się rozłączyć z serwerem wysyłając status
Deklaracja: int tlen_freesession (struct tlen_session *sess)
Opis funkcji zawartych w pliku message.c
Wysyła wiadomość
- sesja - nasza sesja - destination - osoba, do której wysyłamy wiadomość - message - treść wiadomości - type - typ wiadomości, TLEN_CHAT lub TLEN_MESSAGE |
Trzeba określić w parametrze type rodzaj wysyłanej wiadomości
Deklaracja: int tlen_sendmsg(struct tlen_session *sesja, const char *destination, const char *message, int type)
Opis funkcji zawartych w pliku pubdir.c
Inicjuje wyszukiwanie w katalogu publicznym
- sesja - nasza sesja - search - struktura opisujaca wyszukiwanie |
Uwagi do poniższego kodu:
Deklaracja: int tlen_search(struct tlen_session *sesja, struct tlen_pubdir *search)
Wysyła prośbę o nasze dane z katalogu publicznego
- sesja - nasza sesja |
Wraca zaraz po wysłaniu zapytania
Deklaracja: int tlen_get_pubdir (struct tlen_session *sesja)
Wysyła żądanie o zmianę danych w katalogu publicznym
- sesja - nasza sesja - pubdir - struktura zawierająca nowe dane |
Wraca zaraz po wysłaniu zapytania
Deklaracja: int tlen_change_pubdir(struct tlen_session *sesja, struct tlen_pubdir *pubdir)
Inicjuje strukturę zawierającą dane typu katalog publiczny (wyszukiwanie, itp.)
- brak parametrów |
Zwraca nową strukturę tlen_pubdir
Deklaracja: struct tlen_pubdir *tlen_new_pubdir ()
Zwalnia pamięć po strukturze tlen_pubdir
- pubdir - struktura, którą chcemy zwolnić |
Brak uwag dodatkowych
Deklaracja: int tlen_free_pubdir (struct tlen_pubdir *pubdir)
Opis funkcji zawartych w pliku roster.c
Pozwala na ustawienie swojego stanu dostępności
- sesja - nasza sesja - status - stan w postaci TLEN_STATUS_* - description - opcjonalny status opisowy, jeżeli NULL to "" |
Brak uwag dodatkowych
Deklaracja: int tlen_presence (struct tlen_session *sesja, int status, const char *description)
Funkcja wewnętrzna zamykająca połączenie z serwerem, wywoływana przez wysłanie statusu TLEN_STATUS_UNAVAILABLE
- sesja - nasza sesja |
Nie używać w swoich programach, od tego jest tlen_presence()
Deklaracja: int tlen_presence_disconnect (struct tlen_session *sesja)
Wysyła prośbę o ksiażkę adresową
- sesja - nasza sesja |
brak uwag
Deklaracja: int tlen_getroster (struct tlen_session *sesja)
Dodaje użytkownika do książki adresowej
- sesja - nasza sesja - name - nazwa użytkownika - jid - identyfikator użytkownika w @tlen.pl - group - grupa dla użytkownika |
Po dodaniu wypadałoby poprosić o subskrypcję przez
Deklaracja: int tlen_addcontact (struct tlen_session *sesja, const char *name,
Usuwa użytkownika z książki adresowej
- sesja - nasza sesja - jid - identyfikator w @tlen.pl |
Przed należy poprosić o desubskrypcję przez tlen_request_unsubscribe()
Deklaracja: int tlen_removecontact (struct tlen_session *sesja, const char *jid)
Opis funkcji zawartych w pliku sockets.c
tworzy socket i laczy sie z serwerem
- address - adres IP serwera - port - port na który się łączymy |
zwraca gniazdo
Deklaracja: int tlen_socket_create (const char *address, int port)
pisze do gniazda
- sess - sesja, w której jest nasze gniazdo - data - to co chcemy wysłać - len - długość tego co chcemy wysłać |
zwraca 0 w przypadku błędu lub 1 jak się powiedzie
Deklaracja: int tlen_socket_write (struct tlen_session *sess, const void *data, size_t len)
Pisze tekst do gniazda
- sess - sesja z gniazdem - string - tekst do wyslania |
Zwraca 0 w przypadku bledu, lub 1 jak operacja się powiodła
Deklaracja: int tlen_socket_write_string (struct tlen_session *sess, const void *string)
niszczy gniazdo
- sess - sesja w której jest gniazdo |
Zwraca 1 jak się powiedzie, lub 0 w przypadku błędu
Deklaracja: int tlen_socket_destroy (struct tlen_session *sess)
Opis funkcji zawartych w pliku utils.c
Wysyła pakiet kontrolny keep-alive do serwera
- sesja - identyfikator sesji |
Ping powinien być wysyłany co około 60 sekund
Deklaracja: void tlen_ping(struct tlen_session *sesja)
Koduje tekst przy pomocy urlencode
- what - tekst który chcemy zakodować |
Dla zwracanego tekstu alokowana jest pamięć, trzeba go
Deklaracja: char *tlen_encode (const char *what)
Dekoduje tekst przy pomocy urldecode
- what - tekst który chcemy rozkodować |
Dla zwracanego tekstu alokowana jest pamięć, trzeba go
Deklaracja: char *tlen_decode(const char *what)
Wyświetla na stderr tekst pomocniczy przy debugowaniu
- name - nazwa funkcji z której debug został wywołany - format - format tekstu do wyświetlenia - ... - wyświetlany tekst(y) |
Funkcja wewnętrzna, należy używać wrappera tlen_debug(),
Deklaracja: void tlen_debug_raw(const char *name, char *format, ...) {
Usuwa z podanego tekstu wszystko co po znaku /
- jid - tekst z którego ma zostać usuniety "resource" |
Używane dla identyfikatorów jabbera dla przychodzących
Deklaracja: int tlen_stripresource (char *jid)
Funkcja kodująca tekst przy użyciu base64
- buf - tekst do zakodowania |
Wartość zwracana jest alokowana, należy zwolnić przez free()
Deklaracja: char *tlen_base64_encode(const char *buf)
Funkcja dekodująca tekst przy użyciu base64
- buf - tekst do zdekodowania |
Wartość zwracana jest alokowana, należy zwolnić przez free()
Deklaracja: char *tlen_base64_decode(const char *buf)
Automatycznie wygenerowane na podstawie kodu źródłowego omówienie programów przykładowych libtlen
Omówienie programu examples/sendmsg.c
Po więcej informacji zajżyj do rozdziału o lib/testclient.c, gdyż podstawa dokumentacji na nim się opiera.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/select.h> #include <sys/time.h> #include <sys/types.h> #include <signal.h> |
Inkludujemy nagłówek libtlen
#include "libtlen.h" int main (int argc, char **argv) { |
Deklarujemy strukturę sesji
struct tlen_session *sesja; struct tlen_event *event; struct timeval tv; tlen_setdebug(0); if (argc==5) { |
Inicjujemy sesję
sesja = tlen_init(); |
Ustawiamy użytkownika i hasło z parametru programu
tlen_set_auth(sesja,argv[1],argv[2]); |
Łączenie z hubem ma przebiegać jako nieblokowalne
tlen_set_hub_blocking(sesja,0); |
Okej, łączymy
tlen_login(sesja); } else { printf("Usage: %s login password user message\n",argv[0]); return 1; } // tutaj jest obserwowany socket while ((!sesja->error)) { fd_set rd, wr; int retval; tv.tv_sec = 60; tv.tv_usec = 0; FD_ZERO (&rd); FD_ZERO (&wr); |
Ustawiamy opcje monitorowania deskryptorów
if ((sesja->check & TLEN_CHECK_READ)) FD_SET (sesja->fd, &rd); if ((sesja->check & TLEN_CHECK_WRITE)) FD_SET (sesja->fd, &wr); if ((retval = select (sesja->fd + 1, &rd, &wr, NULL, &tv) == -1)) { perror ("select"); } else if (sesja && (FD_ISSET (sesja->fd, &rd) || FD_ISSET (sesja->fd, &wr))) { |
Sprawdzamy co się zmieniło na gnieździe
tlen_watch_fd (sesja); |
Pętla obsługi zdarzeń
while ((event=tlen_getevent(sesja))!=NULL) { switch (event->type) { |
Połączony! Pobieramy id.
case TLEN_EVENT_NOWGETID: { tlen_getid(sesja); break; } |
Logujemy się
case TLEN_EVENT_GOTID: { tlen_authorize(sesja); break; } |
Udało się - pobieramy książkę adresową
case TLEN_EVENT_AUTHORIZED: { tlen_getroster(sesja); break; } |
Ups - coś nie wyszło. Wychodzimy
case TLEN_EVENT_UNAUTHORIZED: { exit(0); break; } |
Książka adresowa się odebrała (nie obsługujemy poszczególnych elementów, bo nie ma po co. Możemy więc wysłać wiadomość (TLEN_MESSAGE, nie TLEN_CHAT), a następnie się rozłączyć
case TLEN_EVENT_ENDROSTER: { tlen_sendmsg(sesja,argv[3],argv[4],TLEN_MESSAGE); tlen_presence_disconnect (sesja); tlen_freesession (sesja); break; } } |
Zwalniamy zdarzenie
tlen_freeevent(event); } } return 0; } |
Omówienie programu lib/testclient.c
Ostatnia aktualizacja: - $Id: libtlen.html,v 1.1.1.1 2004/11/12 10:46:56 maticompxp Exp $
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/select.h> #include <sys/time.h> #include <sys/types.h> #include <signal.h> |
Dołączamy nagłówek libtlen.h w którym zawarte są wszystkie informacje o zdarzeniach, funkcjach, strukturach itp. Bez tego ani rusz!
#include "libtlen.h" |
Zmienna służąca do zamknięcia klienta - obsługiwana w handlerze dla wciśnięcia CTRL+C
int terminate=0; |
Zmienna klienta informująca kiedy był ostatni ping do serwera - w tym przypadku potrzebna, żeby pingać tylko co 60 sekund.
int last_ping_time; |
Wewnętrzny handler klienta ustawiający zmienną na 1 - wywoływany podczas CTRL+C
void termhandler(int sig) { terminate=1; } |
Głowna funkcja programu - tutaj będzie miąchanie z tlenem
int main (int argc, char **argv) { |
Definiujemy strukturę typu tlen_session opisującą wszystko co z naszym tlenowym połączeniem związane. Przechowuje ona gniazda, zdarzenia i inne potrzebne rzeczy
struct tlen_session *sesja; |
Struktura typu tlen_event służy do przechowywania informacji o zdarzeniach - do niej będą pobierane zdarzenia w głównej pętli tlenowej.
struct tlen_event *event; |
Struktura nie związana z libtlen - potrzebna przy takim a nie innym rozwiązaniu monitorowania gniazda - określa timeout.
struct timeval tv; |
Łączymy SIGINT (Ctrl+C) z naszym handlerem (termhandler), pozwoli to na poprawne zamknięcie połączenia - tzn. jego obsłużenie, zamiast twardego wyjścia z programu
signal(SIGINT,termhandler); |
Funkcja biblioteki, pozwalająca na sterowanie informacjami dodatkowymi (debug). Ustawiając jej parametr 1 włączamy debugowanie, od tej pory biblioteka będzie wypluwała na stderr wszystkie informacje pomocnicze.
tlen_setdebug(1); |
Sprawdzamy, czy liczba parametrów przekazanych do programu jest równa 2 - użytkownik i hasło (+1, gdyż pierwszym z nich jest sama nazwa programu, której my nie przekazujemy).
if (argc==3) { |
Inicjujemy sesję przy użyciu funkcji tlen_init()
sesja = tlen_init(); |
Przy użyciu funkcji tlen_set_auth() przekazujemy do biblioteki nazwę użytkownika i hasło
tlen_set_auth(sesja,argv[1],argv[2]); |
Funkcja tlen_set_hub_blocking() ustawia, czy proces łączenia z HUBem ma być synchroniczny, czy asynchroniczny (blokowalny/nieblokowalny) - ustawiamy asynchroniczność.
tlen_set_hub_blocking(sesja,0); |
tlen_login() inicjuje połączenie z serwerem Tlenu. Najpierw łączy się z hubem, następnie pobiera adres serwera i się z nim łączy. Pobranie ID oraz autoryzacja następują automagicznie.
tlen_login(sesja); } else { printf("Usage: %s login password\n",argv[0]); return 1; } |
Punkt kulminacyjny klienta - główna pętla obsługi zdarzeń
while ((!terminate)&&(!sesja->error)) { |
zmienne potrzebne do poprawnego sterowania obserwacją gniazda
fd_set rd, wr; int retval; |
Ustawiamy timeout dla serwera na 60 sekund
tv.tv_sec = 60; tv.tv_usec = 0; |
Zerujemy deskryptory
FD_ZERO (&rd); FD_ZERO (&wr); |
Wchodzi kawałek obsługi biblioteki - jeżeli libtlen prosi nas o monitorowanie kanału przychodzącego, to ustawiamy RD, zaś jeżeli kanału wychodzącego to WR. Kanał wychodzący przede wszystkim sprawdza, czy gniazdo nadaje się już do zapisy, czyli czy jesteśmy już połączeni
if ((sesja->check & TLEN_CHECK_READ)) FD_SET (sesja->fd, &rd); if ((sesja->check & TLEN_CHECK_WRITE)) FD_SET (sesja->fd, &wr); |
Funkcja select - systemowa, nie związana z biblioteką, obserwuje deskryptor połączenia z serwerem - gniazdo znajduje się w strukturze sesji - jako sesja->fd
if ((retval = select (sesja->fd + 1, &rd, &wr, NULL, &tv) == -1)) { perror ("select"); terminate = 1; } |
Znaleziono coś w gnieździe! Obsługujemy zdarzenia.
else if (sesja && (FD_ISSET (sesja->fd, &rd) || FD_ISSET (sesja->fd, &wr))) { |
Funkcja biblioteki - tlen_watch_fd() - sprawdza, co się zmieniło na gnieździe, odczytuje dane, a następnie uzupełnia zdarzenia, które dalej będziemy obsługiwać.
tlen_watch_fd (sesja); |
W pętli pobieramy zdarzenia poprzez tlen_getevent do struktury typu tlen_event (którą zadeklarowaliśmy na początku programu). Pętla obsługi zdarzeń wykonuje się tak długo, aż wszystkie czekające zdarzenia zostaną obsłużone
while ((event=tlen_getevent(sesja))!=NULL) { |
Przełącznikiem switch sprawdzamy typ zdarzenia
switch (event->type) { |
Dostaliśmy informację, że logowanie się powiodło, możemy więc pobrać z serwera książkę adresową - funkcja tlen_getroster()
case TLEN_EVENT_AUTHORIZED: { tlen_getroster(sesja); break; } |
Po wywołaniu tlen_getroster będziemy po kolei dostawać zdarzenia typu TLEN_EVENT_GOTROSTERITEM informujące o pobraniu danych jednego użytkownika. W tym programie wypisujemy je jedynie na ekran. Dane użytkownika zawarte są w strukturze typu tlen_user. Przy każdym zdarzeniu, w którym otrzymujemy jakieś dane, do struktury tlen_event dodawana jest struktura danego zdarzenia - w tym wypadku książki adresowej.
case TLEN_EVENT_GOTROSTERITEM: { printf("Got user: %s\n",event->roster->jid); break; } |
Otrzymaliśmy całą książkę adresową - informuje o tym zdarzenie ENDROSTER. Skoro tak, to powinnismy wysłać informację o swojej dostępności. Służy do tego funkcja tlen_presence pobierająca informacje o stanie - TLEN_STATUS_* oraz nasz status opisowy - jeżeli żaden wstawiamy "" lub NULL.
case TLEN_EVENT_ENDROSTER: { printf("Roster received\n"); tlen_presence(sesja,TLEN_STATUS_AVAILABLE,"Dostępny"); break; } |
Jednym z najtrudniejszych elementów protokołu tlenu/jabbera są subskrybcje. Jest to system pozwalający na kontrolę naszej znajomości. Generalnie - nikt nie będzie widział naszego stanu, jeżeli nie wyrazimy na to zgody, tak samo nikt nie doda nas do książki adresowej jeżeli nie wyrazimy na to zgody. Zdarzenie TLEN_EVENT_SUBSCRIBE mówi nam, że ktoś prosi o zgodę na autoryzację. Identyfikator danej osoby znajduje się w polu event->subscribe->jid. Jeżeli zgadzamy się - tutaj zawsze się zgadzamy, nie jest zaimplementowana obsługa niezgadzania sie ;) - to akceptujemy prośbę funkcją tlen_accept_subscribe, no i żeby być fair prosimy użytkownika o subskrybcję, żeby też mieć go w książce - tlen_request_subscribe.
case TLEN_EVENT_SUBSCRIBE: { tlen_accept_subscribe(sesja,event->subscribe->jid); tlen_request_subscribe(sesja,event->subscribe->jid); break; } |
"Ktoś" zgodził się abyśmy go dodali do swojej książki - a więc to robimy, funkcja tlen_addcontact.
case TLEN_EVENT_SUBSCRIBED: { tlen_addcontact(sesja,"nazwa",event->subscribe->jid,"grupa"); break; } |
BUU - ktoś chce nas usunąć ze swojej książki - więć prosi abyśmy my również go usuęli - zgadzamy się na to poprzez tlen_accept_unsubscribe, i żeby dobić wszystkich formalności prosimy o to samo - tlen_request_unsubscribe.
case TLEN_EVENT_UNSUBSCRIBE: { tlen_accept_unsubscribe(sesja,event->subscribe->jid); tlen_request_unsubscribe(sesja,event->subscribe->jid); break; } |
"Ktoś" nas właśnie pomyślnie usunął, więc my też usuwamy - tlen_removecontact
case TLEN_EVENT_UNSUBSCRIBED: { tlen_removecontact(sesja,event->subscribe->jid); break; } |
Oho - przyszła wiadomość! Dane wiadomości są w strukturze event->message, podstawowymi są body - treść, oraz from - od kogo, a na szarym końcu type - typ (TLEN_CHAT albo TLEN_MESSAGE). Nasz prosty klient obsługuje wiadomości w sposób bardzo prowizoryczny, np. zmienia stan, odpowiada pingiem, i inne takie pierdoły. Generalnie nie pojawia się tu na razie nic ważnego - do czasu...
case TLEN_EVENT_MESSAGE: { if (strcmp(event->message->body,"exit")==0) { terminate=1; tlen_sendmsg(sesja,event->message->from,"exiting...",TLEN_CHAT); } if (strcmp(event->message->body,"available")==0) { tlen_presence(sesja,TLEN_STATUS_AVAILABLE,"available"); tlen_sendmsg(sesja,event->message->from,"status changed to available", TLEN_CHAT); } if (strcmp(event->message->body,"away")==0) { tlen_presence(sesja,TLEN_STATUS_AWAY,"away"); tlen_sendmsg(sesja,event->message->from,"status changed to away", TLEN_CHAT); } if (strcmp(event->message->body,"chatty")==0) { tlen_presence(sesja,TLEN_STATUS_CHATTY,"away"); tlen_sendmsg(sesja,event->message->from,"status changed to chatty", TLEN_CHAT); } if (strcmp(event->message->body,"xa")==0) { tlen_presence(sesja,TLEN_STATUS_EXT_AWAY,"away"); tlen_sendmsg(sesja,event->message->from,"status changed to xa", TLEN_CHAT); } if (strcmp(event->message->body,"dnd")==0) { tlen_presence(sesja,TLEN_STATUS_DND,"away"); tlen_sendmsg(sesja,event->message->from,"status changed to dnd", TLEN_CHAT); } if (strcmp(event->message->body,"invisible")==0) { tlen_presence(sesja,TLEN_STATUS_INVISIBLE,"away"); tlen_sendmsg(sesja,event->message->from,"status changed to invisible", TLEN_CHAT); } if (strcmp(event->message->body,"ping")==0) { tlen_sendmsg(sesja,event->message->from,"pong", TLEN_CHAT); } |
Jeżeli wyślemy klientowi wiadomość pubdir, wyśle on prośbę o swoje dane z katalogu publicznego - tlen_get_pubdir()
if (strcmp(event->message->body,"pubdir")==0) { tlen_get_pubdir(sesja); } if (strcmp(event->message->body,"groupchat")==0) { tlen_groupchat_init(sesja,event->message->from); } |
Jeżeli wyślemy do testclienta wiadomość o treści search, klient rozpocznie proces wyszukiwania w katalogu publicznym. W tym celu inicjowana jest struktura tlen_pubdir, przy użyciu funkcji tlen_new_pubdir(), a następnie wypełniane są jej pola. Tutaj akutat wykonujemy odpowiednik windowsowej "wizytówki" - tzn. wyszukujemy po identyfikatorze tlenu. Gdy wyślemy zamówienie poprzez tlen_search, zwalniamy strukturę funkcją tlen_free_pubdir().
if (strcmp(event->message->body,"search")==0) { struct tlen_pubdir *sercz = tlen_new_pubdir(); sercz->id = "matijozef"; // tutaj sobie wpiszcie jaki wam sie podoba tlen_search(sesja,sercz); tlen_free_pubdir(sercz); } |
Analogicznie - tutaj obsługiwana jest prośba o zmianę naszych danych w katalogu. Również inicjujemy strukturę tlen_pubdir, uzupełniamy jej pola, a następnie funkcją tlen_change_pubdir wysyłamy do serwera nowe dane. Na koniec zwalniamy strukturę poprzez tlen_free_pubdir()
if (strcmp(event->message->body,"pubdir_change")==0) { struct tlen_pubdir *pub = tlen_new_pubdir(); pub->firstname = "matijozef"; // tutaj sobie wpiszcie jaki wam sie podoba pub->lastname = "krzysztoffelix"; tlen_change_pubdir(sesja,pub); tlen_free_pubdir(pub); } break; } |
Ktoś zmienił stan - np. na niedostępny, zajęty, lub zmienił stan opisowy Aktualnie nie jest to u nas obsługiwane :) Dane oczywiście zawarte są w event->presence.
case TLEN_EVENT_PRESENCE: { break; } |
Zintegrowany system kont na o2.pl / tlen.pl jest połączony z serwerem tlenu. Tak więc możemy otrzymać informację o nowej poczcie - tutaj właśnie tak się dzieje. Informacje o tym są wysyłane na ekran - dane zaś znajdziemy w polu event->newmail
case TLEN_EVENT_NEWMAIL: { printf("Masz nową pocztę od %s o temacie %s!\n",event->newmail->from, event->newmail->subject); break; } |
Na razie jeszcze nie mamy w testcliencie obsługi wyszukiwania, więć program poinformuje nas jedynie o tym, że wyszukiwanie skończył. Obsługa jednak jest analogiczna do książki adresowej - GOTSEARCHITEM a na końcu ENDSEARCH.
case TLEN_EVENT_ENDSEARCH: { printf("Wyszukiwanie zakończone!\n"); break; } |
Serwer tlenu pomyślnie zwrócił nam swoje dane, znajdziemy je w event->pubdir.
case TLEN_EVENT_PUBDIR_GOTDATA: { printf("Odebralem swoje dane!\n"); break; } |
"Pusty" event. Dostajemy informacje o tym, że nasze dane się ładnie zmieniły.
case TLEN_EVENT_PUBDIR_GOTUPDATEOK: { printf("Zmienilem swoje dane!\n"); break; } } tlen_freeevent(event); } |
Co 60 sekund musimy pingać, zapewnia nam to prosty trick z funkcją time.
if (time(NULL)-last_ping_time>60) { tlen_ping (sesja); last_ping_time = time(NULL); } } |
obsluga m.in bledu autoryzacji
if(sesja->error) { printf("Error occured while talking to server!\n"); exit(1); } |
Jeżeli dostaliśmy CTRL+C to wychodzimy i zamykamy połączenie z serwerem.
if (terminate) printf("SIGINT received, exiting...\n"); |
Wysyłamy zamknięcie połączenia poprzez stan TLEN_STATUS_UNAVAILABLE.
tlen_presence(sesja, TLEN_STATUS_UNAVAILABLE, ""); |
A na koniec zwalniamy pamięć po sesji.
tlen_freesession (sesja); return 0; } |