/**

\defgroup login Logowanie się do serwera
\ingroup session

\details

każda sesja jest opisywana przez ,,struct gg_session''. biblioteka może
w ramach jednego procesu/wątku obsługiwać tyle sesji, na ile pozwolą
zasoby. na początku deklarujemy:

\code
...
\endcode

następnie będziemy się łączyć. przykład będzie dotyczył socketów
nieblokujących, bo w większości aplikacji ciężko sobie pozwolić na
zawieszanie programu na czas łączenia.

\code
	struct gg_session *blah;
	struct gg_login_params p;

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

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

jeśli uda się rozpocząć proces łączenia, dostajemy wskaźnik do struktury,
inaczej NULL. wywołanie gg_login() powoduje uruchomienie drugiego procesu
lub wątku w tle, który wywoła gethostbyname() i potokiem zwróci wynik.
później połączy się z serwerem, wyśle, odbierze, połączy się ze wskazanym
adresem IP, zaloguje się itd. jako że wszystko dzieję się w tle, klient
musi sprawdzać cały czas podane deskryptory. pole ,,blah->fd'' zawiera
deskryptor, a ,,blah->check'' jest bitmapą i zawiera GG_CHECK_READ i/lub
GG_CHECK_WRITE jeśli mamy sprawdzić czy przyszły nowe dane i/lub możemy
wysyłać. jeśli coś się wydarzy, wywołujemy ,,gg_watch_fd()'', a libgadu
sobie już sprawdzi, co takiego się zdarzyło:

\code
	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();
	}
\endcode

dla uproszczenia, nie ma tutaj obsługi timeoutów i tym podobnych dodatków.
poza tym, jeśli program sprawdza też inne deskryptory (np. stdin dla
klientów konsolowych), dobrze byłoby sprawdzić, czy dana sesja coś robi i
nie sprawdzać ,,blah->fd'' jeśli ,,blah->state == GG_STATE_IDLE''. od czasu
do czasu można dać serwerowi znać, że coś się dzieje, za pomocą...

\code
	gg_ping(blah);
\endcode

ale to już wymaga implementacji timerów i liczenia czasu od ostatniego
pinga. ,,blah->last_event'' mówi, kiedy dostaliśmy cokolwiek ostatnio od
serwera. wszystkie pola struktury są opisane w pliku libgadu.h.

wracając do obsługi deskryptorów -- jeśli klient zauważy, że coś się
zmieniło na podanym sockecie, powinien wywołać ,,gg_watch_fd()'',
która wszystkim się zajmie. zwraca ona wskaźnik do zaalokowanej
struktury opisującej zdarzenie. po obejrzeniu należy zwolnić ją za
pomocą ,,gg_event_free()''. w powyższym przykładzie jest wywoływana
funkcja ,,my_handle_event()'', która może wyglądać tak:

\code
	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("połączono!\n");
			/* tutaj wysyłamy userlistę za pomocą gg_notify() */
			break;

		case GG_EVENT_CONN_FAILED:
			printf("nie udało się\n");
			/* powód w e->event.failure, stałe 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 mówi czy rozmowa czy wiad. */
			/* jeśli e->event.msg.sender równy 0, to mamy */
			/* wiadomość systemową o numerze w msg.class */
			break;

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

		case GG_EVENT_NOTIFY60:
			printf("oto ludzie, którzy się pojawili: ");
			/* tutaj sprawdzanie tablicy e->event.notify60 */
			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_STATUS60:
			printf("ktoś %d zmienił stan\n", e->event.status60.uin);
			/* nowy stan w e->event.status60.status */
			break;

		case GG_EVENT_ACK:
			printf("wiadomość dotarła do %d.\n",
				e->event.ack.recipient);
			/* e->event.ack.status mówi czy dotarła do klienta */
			/* czy leży na serwerze, stałe GG_ACK_... */
			/* e->event.ack.seq to numerek wiadomości */
			break;

		case GG_EVENT_PUBDIR50_REPLY:
			printf("znalazło kogoś\n");
			/* opisane niżej */
			break;

		case GG_EVENT_USERLIST:
			printf("wysłano lub odebrano listę z serwera\n");
			/* opisane niżej */
			break;
	}

	gg_event_free(e);
\endcode

przy okazji wiadomo, co oznaczają zdarzenia. część z nich można ignorować,
jeśli robi się okrojonego klienta, np. wysyłającego jedną wiadomość z linii
komend.

po zalogowaniu należy wysłać serwerowi listę użytkowników, których mamy
w liście kontaktów. ,,gg_notify()'' przyjmuje za argument tablicę zmiennych
typu ,,uin_t''. w odpowiedzi dostaniemy GG_EVENT_NOTIFY i tablicę struktur
,,struct gg_notify_reply'', jeśli ktoś jest. po szczegóły odsyłam do
libgadu.c, libgadu.h i źródeł konsolowego klienta. jeśli dodajemy lub
usuwamy kogoś w trakcie działania, należy skorzystać z ,,gg_add_notify()''
lub ,,gg_remove_notify()''. jeśli chcemy korzystać z listy osób blokowanych
lub takich, przed którymi się ukrywamy, należy korzystać z funkcji
,,gg_notify_ex()'', ,,gg_add_notify_ex()'' i ,,gg_remove_notify_ex()'',
które biorą dodatkowy argument mówiący, jak traktować użytkownika.
odpowiadają za to stałe GG_USER_NORMAL, GG_USER_BLOCKED i GG_USER_OFFLINE.

żeby zmienić stan na zajęty lub dostępny, używamy ,,gg_change_status()'',
,,gg_change_status_descr()'' lub ,,gg_change_status_descr_time()''.

wysyłanie wiadomości za pomocą ,,gg_send_message()''. parametr ,,class''
mówi, czy ma się pojawić w osobnym okienku (GG_CLASS_MSG) czy w okienku
rozmowy (GG_CLASS_CHAT). funkcja zwraca numer sekwencyjny wiadomości,
którego możemy użyć do potwierdzenia. wiadomość, która ma być sformatowana
w odpowiedni sposób (pogrubienie, kursywa, kolory, itp.) wysyłamy za pomocą
,,gg_send_message_richtext()''. wiadomości konferencyjne wysyłamy funkcjami
,,gg_send_message_confer()'' lub ,,gg_send_message_confer_richtext()''.

jeśli chcemy się wylogować, wywołujemy ,,gg_logoff()'' i potem zwalniamy
pamięć związaną z sesją funkcją ,,gg_free_session()''.

jeśli chcemy przypomnieć swoje hasło, wywołujemy funkcję
,,gg_remind_passwd2()'', a wynikową strukturę ,,gg_http'' traktujemy
podobnie do ,,gg_session'':
 - sprawdzamy ->fd i ->check,
 - wywołujemy ,,gg_remind_passwd_watch_fd()'', gdy coś się dzieje. funkcja
   ta zwraca -1 w przypadku błędu. jeśli zwraca 0, wywołujemy ją, póki
   ->state nie będzie równe GG_STATE_DONE lub GG_STATE_ERROR.
 - po zakończeniu, wywołujemy ,,gg_remind_passwd_free()''.

*/
