// jakitam sobie opis API libgadu
// (c) copyright 2001-2003 by wojtek kaniewski <wojtekka@irc.pl>
//			      robert j. wozny <speedy@ziew.org>

kada sesja jest opisywana przez ,,struct gg_session''. biblioteka moe
w ramach jednego procesu/wtku obsugiwa tyle sesji, na ile pozwol
zasoby. deklarujemy sobie...

	struct gg_session *blah;

nastpnie wypadaoby si poczy. przykad bdzie dotyczy socketw
nieblokujcych, bo w wikszoci aplikacji ciko sobie pozwoli na
zawieszanie programu na czas czenia.

	struct gg_login_params p;

	memset(&p, 0, sizeof(p));
	p.uin = 123456;
	p.password = "haso";
	p.async = 1;
	p.status = GG_STATUS_INVISIBLE;

	if (!(blah = gg_login(&p)))
		my_error();

jeli uda si rozpocz proces czenia, dostajemy wskanik do struktury,
inaczej NULL. wywoanie gg_login() powoduje uruchomienie drugiego procesu
w tle, ktry wywoa gethostbyname() i przez pipe'a zwrci wynik. pniej
poczy si z serwerem, wyle, odbierze, poczy si ze wskazanym adresem
IP, zaloguje si itd. jako e wszystko dziej si w tle, klient musi
sprawdza cay czas podane deskryptory. pole ,,blah->fd'' zawiera
deskryptor, a ,,blah->check'' jest bitmap i zawiera GG_CHECK_READ i/lub
GG_CHECK_WRITE jeli mamy sprawdzi czy przyszy nowe dane i/lub moemy
wysya. jeli co si wydarzy, wywoujemy ,,gg_watch_fd()'', a libgadu
sobie ju sprawdzi, co takiego si zdarzyo:

	while (1) {
		fd_set rd, wr, ex;

		FD_ZERO(&rd);
		FD_ZERO(&wr);
		FD_ZERO(&ex);

		if ((blah->check & GG_CHECK_READ))
			FD_SET(blah->fd, &rd);
		if ((blah->check & GG_CHECK_WRITE))
			FD_SET(blah->fd, &wr);
		FD_SET(blah->fd, &ex);

		if (select(blah->fd + 1, &rd, &wr, &ex, NULL) == -1)
			my_error();

		if (FD_ISSET(blah->fd, &ex))
			my_error();

		if (FD_ISSET(blah->fd, &rd) || FD_ISSET(blah->fd, &wr))
			my_handle_event();
	}

nie ma tutaj obsugi timeoutw itp. jeli komu zaley, niech sobie
sam dopisze. poza tym, jeli program sprawdza te inne deskryptory
jak np. stdin dla klientw konsolowych, dobrze byoby sprawdzi, czy
dana sesja co robi i nie sprawdza ,,blah->fd'' jeli
,,blah->state == GG_STATE_IDLE''. no i od czasu do czasu mona by
dawa zna serwerowi, e co si dzieje, za pomoc...

	gg_ping(blah);

ale to ju wymaga implementacji jakich timeoutw i liczenia czasu od
ostatniego pinga. ,,blah->last_pong'' mwi nam, kiedy serwer ostatni
raz odpowiedzia na pakiet ping, a ,,blah->last_event'' mwi, kiedy
dostalimy cokolwiek ostatnio od serwera.

wracajc do obsugi deskryptorw -- jeli klient zauway, e co si
zmienio na podanym sockecie, powinien wywoa ,,gg_watch_fd()'',
ktra wszystkim si zajmie. zwraca ona wskanik do zaalokowanej
struktury opisujcej zdarzenie. po obejrzeniu naley zwolni j za
pomoc ,,gg_event_free()''. w powyszym przykadzie jest wywoywana
funkcja ,,my_handle_event()'', ktra moe wyglda tak:

	struct gg_event *e;
	
	if (!(e = gg_watch_fd(blah)))
		my_error();

	switch (e->type) {
		case GG_EVENT_NONE:
		case GG_EVENT_PONG:
			/* olewamy */
			break;

		case GG_EVENT_CONN_SUCCESS:
			printf("poczono!\n");
			/* tutaj wysyamy userlist za pomoc gg_notify() */
			break;

		case GG_EVENT_CONN_FAILED:
			printf("nie udao si\n");
			/* powd w e->event.failure, stae GG_FAILURE_... */
			break;

		case GG_EVENT_MSG:
			printf("masz wiadomo!\n");
			printf("od: %d\n", e->event.msg.sender);
			printf("tre: %s\n", e->event.msg.message);
			/* e->event.msg.class mwi czy rozmowa czy wiad. */
			/* jeli e->event.msg.sender rwny 0, to mamy */
			/* wiadomo systemow o numerze w msg.class */
			break;

		case GG_EVENT_NOTIFY:
			printf("oto ludzie, ktrzy si pojawili: ");
			/* tutaj sprawdzanie tablicy e->event.notify */
			break;

		case GG_EVENT_STATUS:
			printf("kto %d zmieni stan\n", e->event.status.uin);
			/* nowy stan w e->event.status.status */
			break;

		case GG_EVENT_ACK:
			printf("wiadomo dotara do %d.\n",
				e->event.ack.recipient);
			/* e->event.ack.status mwi czy dotara do klienta */
			/* czy ley na serwerze, stae GG_ACK_... */
			/* e->event.ack.seq to numerek wiadomoci */
			break;

		case GG_EVENT_PUBDIR50_REPLY:
			printf("znalazo kogo\n");
			/* opisane niej */
			break;
	}

	gg_event_free(e);

przy okazji wiadomo, co oznacza ktry event. cz z nich mona ignorowa,
jeli robi si okrojonego klienta, np. commandline'owego wysyajcego
jedn wiadomo.

po zalogowaniu wypadaoby wysa serwerowi list uytkownikw, ktrzy nas
interesuj. ,,gg_notify()'' przyjmuje za argument tablic zmiennych typu
,,uin_t''. w odpowiedzi dostaniemy GG_EVENT_NOTIFY i tablic struktur
,,struct gg_notify_reply'', jeli kto jest. po szczegy odsyam do
libgadu.c, libgadu.h i rde konsolowego klienta. jeli dodajemy lub
usuwamy kogo w trakcie dziaania, naley skorzysta z ,,gg_add_notify()''
lub ,,gg_remove_notify()''.

eby zmieni stan na zajty lub dostpny, uywamy ,,gg_change_status()''.

wysyanie wiadomoci za pomoc ,,gg_send_message()''. parametr ,,class''
mwi, czy ma sie pojawi w osobnym okienku (GG_CLASS_MSG) czy w okienku
rozmowy (GG_CLASS_CHAT). funkcja zwraca numer sekwencyjny wiadomoci,
ktrego moemy uy do potwierdzenia. wiadomo, ktra ma by sformatowana
w odpowiedni sposb (pogrubienie, kursywa, kolory, itp.) wysyamy za pomoc
,,gg_send_message_richtext()''.

jedyne zalenoci libgadu w domylnej wersji to <sys/types.h> i <stdint.h>

***

OBSUGA KATALOGU PUBLICZNEGO GG 5.0 (na podstawie listu na ekg-devel)

skoro ju dziaa, opisz aktualne API. gwnym zaoeniem bya
maksymalna niezaleno od zmian w protokole, zmian nazw pl, dodawania
nowych itd. zastosowane podejcie moe by troch dziwne na pierwszy rzut
oka, ale podpatrzyem to w powaniejszych projektach (np. libdbi).

jeli chcemy szuka:

        gg_pubdir50_t req = gg_pubdir50_new(GG_PUBDIR50_SEARCH_REQUEST);

        if (!req)
                out_of_memory();

        /* szukamy po numerku... */

        gg_pubdir50_add(req, GG_PUBDIR50_UIN, "123456");

        /* lub... */

        gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, "Ania");
        gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_FEMALE);

        /* lub... */

        gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, "1979 1985");
        gg_pubdir50_add(req, GG_PUBDIR50_START, "0");
        gg_pubdir50_add(req, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE);

        /* i w kocu... */

        gg_pubdir50(sesja, req);

        /* i zwalniamy pami, albo sobie gdzie zachowujemy. whatever */

        gg_pubdir50_free(req);

jak wita, gg_pubdir50_new() tworzy obiekt opisujcy operacj katalogu,
gg_pubdir50_add() dodaje kolejne parametry. rodzaj parametru jest w
rzeczywici sta tekstow, np. GG_PUBDIR50_UIN to "FmNumber". naley
pamita, e wszystkie argumenty s tekstami. nie trzeba si bawi w ich
alokowanie czy co takiego. biblioteka sobie sama zapamita. teksty musz
by oczywicie w CP1250. na kocu wywoujemy gg_pubdir50() i tyle. funkcja
ta zwraca numer sekwencyjny wyszukiwania, ktry moemy sobie zachowa dla
pniejszych referencji.

eby otrzyma wynik, naley obsuy zdarzenia GG_EVENT_PUBDIR50_SEARCH_REPLY,
GG_EVENT_PUBDIR50_WRITE i GG_EVENT_PUBDIR50_READ. dla przykadu, obsuga
wynikw wyszukiwania wyglda nastpujco:

        gg_search50_t res = zdarzenie->event.search50;
        int count = gg_search50_count(res);

        if (count < 1) {
                wiadomo("Nie znaleziono");
                return;
        }

        for (int i = 0; i < count; i++) {
                const char *uin, *first, *nick, *born, *city, *status;

                uin = gg_pubdir50_get(res, i, GG_PUBDIR50_UIN);
                first = gg_pubdir50_get(res, i, GG_PUBDIR50_FIRSTNAME);
                nick = gg_pubdir50_get(res, i, GG_PUBDIR50_NICK);
                born = gg_pubdir50_get(res, i, GG_PUBDIR50_BIRTHYEAR);
                city = gg_pubdir50_get(res, i, GG_PUBDIR50_CITY);
                status = gg_pubdir50_get(res, i, GG_PUBDIR50_STATUS);

                printf("Numer: %s\nImi: %s\nPseudonim: %s\n"
                        "Urodzony: %s\nMiejscowo: %s\n", uin,
                        first, nick, born, city);

/*
  status w wynikach wyszukiwania w przypadku ,,tylko dla znajomych''
  jest oznaczony innym bitem ni GG_STATUS_FRIENDS_MASK.
 */
#define GG_SEARCH_FRIENDS_MASK	0x0080

                switch ((status) ? (atoi(status) & ~GG_SEARCH_FRIENDS_MASK) : -1) {
                        case GG_STATUS_AVAIL:
                                printf("Dostpny\n");
                                break;
                        case GG_STATUS_BUSY:
                                printf("Zajty\n");
                                break;
                        default:
                                printf("Niedostpny\n");
                }

                printf("\n");
        }

        gg_event_free(zdarzenie);

jeli chcemy wiedzie, od jakiego numeru zaczc wyszukiwanie, eby dosta
dalsz cz, uzywamy gg_pubdir50_next(). jeli chcemy numer sekwencyjny,
uywamy gg_pubdir50_seq().

w adnym wypadku nie mona si odwoywa do pl gg_pubdir50_t, poniewa
mog si zmienia midzy wersjami biblioteki. dziki odwoywaniu si przez
funkcje, mamy pewno, e bez wzgldu na zmiany API/ABI mamy to samo.
dodatkowo, jeli dojd jakies opcje wyszukiwania, nie trzeba w bibliotece
niczego zmienia -- adnych struktur, itd. po prostu odwoujemy si do
kolejnego pola przez gg_pubdir50_add() i gg_pubdir50_get().


$Id: api.txt,v 1.14 2003/01/29 15:38:41 szalik Exp $
