+++ /dev/null
-diff -urN kadu-0.1.6b.org/kadu/Makefile.am kadu-0.1.6b/kadu/Makefile.am
---- kadu-0.1.6b.org/kadu/Makefile.am Sat Sep 29 16:56:11 2001
-+++ kadu-0.1.6b/kadu/Makefile.am Sat Sep 29 17:00:54 2001
-@@ -1,7 +1,7 @@
- ####### kdevelop will overwrite this part!!! (begin)##########
- bin_PROGRAMS = kadu
--kadu_SOURCES = search.cpp userlist.cpp history.cpp chat.cpp events.cpp kadu.cpp main.cpp
--kadu_LDADD = -lgg -lpthread $(LIB_KDEUI) $(LIB_KDECORE) $(LIB_QT) $(LIBSOCKET)
-+kadu_SOURCES = search.cpp userlist.cpp history.cpp chat.cpp events.cpp kadu.cpp main.cpp libgg.c libgg_search.c
-+kadu_LDADD = -lpthread $(LIB_KDEUI) $(LIB_KDECORE) $(LIB_QT) $(LIBSOCKET)
-
-
- EXTRA_DIST = main.cpp kadu.cpp kadu.h kadu.desktop lo32-app-kadu.png lo16-app-kadu.png events.h events.cpp chat.cpp chat.h history.h history.cpp pixmaps.h userlist.cpp search.h search.cpp libgg.h
-diff -urN kadu-0.1.6b.org/kadu/libgg.c kadu-0.1.6b/kadu/libgg.c
---- kadu-0.1.6b.org/kadu/libgg.c Thu Jan 1 01:00:00 1970
-+++ kadu-0.1.6b/kadu/libgg.c Sat Sep 29 17:00:32 2001
-@@ -0,0 +1,1247 @@
-+/* $Id$ */
-+
-+/*
-+ * (C) Copyright 2001 Wojtek Kaniewski <wojtekka@irc.pl>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License Version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <stdio.h>
-+#include <sys/socket.h>
-+#include <netinet/in.h>
-+#include <arpa/inet.h>
-+#include <sys/ioctl.h>
-+#include <sys/wait.h>
-+#include <netdb.h>
-+#include <errno.h>
-+#include <string.h>
-+#include <stdarg.h>
-+#include <pwd.h>
-+#include "endian.h"
-+#include "libgg.h"
-+
-+extern int gg_debug_level;
-+
-+#ifdef GG_DEBUG
-+
-+/*
-+ * gg_debug_real()
-+ *
-+ * wyrzuca komunikat o danym poziomie, o ile u¿ytkownik sobie tego ¿yczy.
-+ *
-+ * - level - poziom wiadomo¶ci,
-+ * - format... - tre¶æ wiadomo¶ci (printf-alike.)
-+ *
-+ * niczego nie zwraca.
-+ */
-+void gg_debug_real(int level, char *format, ...)
-+{
-+ va_list ap;
-+
-+ if ((gg_debug_level & level)) {
-+ va_start(ap, format);
-+ vprintf(format, ap);
-+ va_end(ap);
-+ }
-+}
-+
-+#endif /* GG_DEBUG */
-+
-+/*
-+ * fix32() // funkcja wewnêtrzna
-+ *
-+ * dla maszyn big-endianowych zamienia kolejno¶æ bajtów w ,,long''ach.
-+ */
-+static inline unsigned long fix32(unsigned long x)
-+{
-+#if __BYTE_ORDER == __LITTLE_ENDIAN
-+ return x;
-+#else
-+ char *y = &x;
-+
-+ return (y[0] << 24 + y[1] << 16 + y[2] << 8 + y[3]);
-+#endif
-+}
-+
-+/*
-+ * fix16() // funkcja wewnêtrzna
-+ *
-+ * dla maszyn big-endianowych zamienia kolejno¶æ bajtów w ,,short''ach.
-+ */
-+static inline unsigned short fix16(unsigned short x)
-+{
-+#if __BYTE_ORDER == __LITTLE_ENDIAN
-+ return x;
-+#else
-+ char *y = &x;
-+
-+ return (y[0] << 8 + y[1]);
-+#endif
-+}
-+
-+/*
-+ * gg_alloc_sprintf() // funkcja wewnêtrzna
-+ *
-+ * robi dok³adnie to samo, co sprintf(), tyle ¿e alokuje sobie wcze¶niej
-+ * miejsce na dane. jak znam ¿ycie, ze wzglêdu na ró¿nice miêdzy funkcjami
-+ * vsnprintf() na ró¿nych platformach, nie bêdzie dzia³a³o ;)
-+ *
-+ * - format, ... - parametry takie same jak w innych funkcjach *printf()
-+ *
-+ * zwraca zaalokowany buforek, który wypada³oby pó¼niej zwolniæ, lub NULL
-+ * je¶li nie uda³o siê wykonaæ zadania.
-+ */
-+char *gg_alloc_sprintf(char *format, ...)
-+{
-+ va_list ap;
-+ char *buf = NULL;
-+ int size;
-+
-+ va_start(ap, format);
-+
-+ if ((size = vsnprintf(buf, 0, format, ap)) < 0)
-+ return NULL;
-+
-+ if (!(buf = malloc(size + 1)))
-+ return NULL;
-+
-+ vsnprintf(buf, size + 1, format, ap);
-+
-+ va_end(ap);
-+
-+ return buf;
-+}
-+
-+/*
-+ * gg_resolve() // funkcja wewnêtrzna
-+ *
-+ * tworzy pipe'y, forkuje siê i w drugim procesie zaczyna resolvowaæ
-+ * podanego hosta. zapisuje w sesji deskryptor pipe'u. je¶li co¶ tam
-+ * bêdzie gotowego, znaczy, ¿e mo¿na wczytaæ ,,struct in_addr''. je¶li
-+ * nie znajdzie, zwraca INADDR_NONE.
-+ *
-+ * - fd - wska¼nik gdzie wrzuciæ deskryptor,
-+ * - pid - gdzie wrzuciæ pid dzieciaka,
-+ * - hostname - nazwa hosta do zresolvowania.
-+ *
-+ * zwraca 0 je¶li uda³o siê odpaliæ proces lub -1 w przypadku b³êdu.
-+ */
-+int gg_resolve(int *fd, int *pid, char *hostname)
-+{
-+ int pipes[2], res;
-+ struct in_addr a;
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(..., \"%s\");\n", hostname);
-+
-+ if (!fd | !pid) {
-+ errno = EFAULT;
-+ return -1;
-+ }
-+
-+ if (pipe(pipes) == -1)
-+ return -1;
-+
-+ if ((res = fork()) == -1)
-+ return -1;
-+
-+ if (!res) {
-+ if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) {
-+ struct hostent *he;
-+
-+ if (!(he = gethostbyname(hostname)))
-+ a.s_addr = INADDR_NONE;
-+ else
-+ memcpy((char*) &a, he->h_addr, sizeof(a));
-+ }
-+
-+ write(pipes[1], &a, sizeof(a));
-+
-+ exit(0);
-+ }
-+
-+ close(pipes[1]);
-+
-+ *fd = pipes[0];
-+ *pid = res;
-+
-+ return 0;
-+}
-+
-+/*
-+ * gg_connect() // funkcja wewnêtrzna
-+ *
-+ * ³±czy siê z serwerem. pierwszy argument jest typu (void *), ¿eby nie
-+ * musieæ niczego inkludowaæ w libgg.h i nie psuæ jaki¶ g³upich zale¿no¶ci
-+ * na dziwnych systemach.
-+ *
-+ * - addr - adres serwera (struct in_addr *),
-+ * - port - port serwera,
-+ * - async - ma byæ asynchroniczne po³±czenie?
-+ *
-+ * zwraca po³±czonego socketa lub -1 w przypadku b³êdu. zobacz errno.
-+ */
-+int gg_connect(void *addr, int port, int async)
-+{
-+ int sock, one = 1;
-+ struct sockaddr_in sin;
-+ struct in_addr *a = addr;
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async);
-+
-+ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-+ gg_debug(GG_DEBUG_MISC, "-- socket() failed. errno = %d (%s)\n", errno, strerror(errno));
-+ return -1;
-+ }
-+
-+ if (async) {
-+ if (ioctl(sock, FIONBIO, &one) == -1) {
-+ gg_debug(GG_DEBUG_MISC, "-- ioctl() failed. errno = %d (%s)\n", errno, strerror(errno));
-+ return -1;
-+ }
-+ }
-+
-+ sin.sin_port = htons(port);
-+ sin.sin_family = AF_INET;
-+ sin.sin_addr.s_addr = a->s_addr;
-+
-+ if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
-+ gg_debug(GG_DEBUG_MISC, "-- connect() failed. errno = %d (%s)\n", errno, strerror(errno));
-+ if (errno && (!async || errno != EINPROGRESS))
-+ return -1;
-+ }
-+
-+ return sock;
-+}
-+
-+/*
-+ * gg_read_line() // funkcja wewnêtrzna
-+ *
-+ * czyta jedn± liniê tekstu z socketa.
-+ *
-+ * - sock - socket,
-+ * - buf - wska¼nik bufora,
-+ * - length - d³ugo¶æ bufora.
-+ *
-+ * olewa b³êdy. je¶li na jaki¶ trafi, potraktuje go jako koniec linii.
-+ */
-+static void gg_read_line(int sock, char *buf, int length)
-+{
-+ int ret;
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_read_line(...);\n");
-+
-+ for (; length > 1; buf++, length--) {
-+ do {
-+ if ((ret = read(sock, buf, 1)) == -1 && errno != EINTR) {
-+ *buf = 0;
-+ return;
-+ }
-+ } while (ret == -1 && errno == EINTR);
-+
-+ if (*buf == '\n') {
-+ buf++;
-+ break;
-+ }
-+ }
-+
-+ *buf = 0;
-+ return;
-+}
-+
-+/*
-+ * gg_recv_packet() // funkcja wewnêtrzna
-+ *
-+ * odbiera jeden pakiet gg i zwraca wska¼nik do niego. pamiêæ po nim
-+ * wypada³oby uwolniæ.
-+ *
-+ * - sock - po³±czony socket.
-+ *
-+ * je¶li wyst±pi³ b³±d, zwraca NULL. reszta w errno.
-+ */
-+static void *gg_recv_packet(struct gg_session *sess)
-+{
-+ struct gg_header h;
-+ char *buf = NULL;
-+ int ret = 0, offset, size = 0;
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(...);\n");
-+
-+ if (!sess) {
-+ errno = EFAULT;
-+ return NULL;
-+ }
-+
-+ if (sess->recv_left < 1) {
-+ while (ret != sizeof(h)) {
-+ ret = read(sess->fd, &h, sizeof(h));
-+ gg_debug(GG_DEBUG_MISC, "-- header recv(..., %d) = %d\n", sizeof(h), ret);
-+ if (ret < sizeof(h)) {
-+ if (errno != EINTR) {
-+ gg_debug(GG_DEBUG_MISC, "-- errno = %d (%s)\n", errno, strerror(errno));
-+ return NULL;
-+ }
-+ }
-+ }
-+
-+ h.type = fix32(h.type);
-+ h.length = fix32(h.length);
-+ } else {
-+ memcpy(&h, sess->recv_buf, sizeof(h));
-+ }
-+
-+ /* jakie¶ sensowne limity na rozmiar pakietu */
-+ if (h.length < 0 || h.length > 65535) {
-+ gg_debug(GG_DEBUG_MISC, "-- invalid packet length (%d)\n", h.length);
-+ errno = ERANGE;
-+ return NULL;
-+ }
-+
-+ if (sess->recv_left > 0) {
-+ gg_debug(GG_DEBUG_MISC, "-- resuming last gg_recv_packet()\n");
-+ size = sess->recv_left;
-+ offset = sess->recv_done;
-+ buf = sess->recv_buf;
-+ } else {
-+ if (!(buf = malloc(sizeof(h) + h.length + 1))) {
-+ gg_debug(GG_DEBUG_MISC, "-- not enough memory\n");
-+ return NULL;
-+ }
-+
-+ memcpy(buf, &h, sizeof(h));
-+
-+ offset = 0;
-+ size = h.length;
-+ }
-+
-+ while (size > 0) {
-+ ret = read(sess->fd, buf + sizeof(h) + offset, size);
-+ gg_debug(GG_DEBUG_MISC, "-- body recv(..., %d) = %d\n", size, ret);
-+ if (ret > -1 && ret <= size) {
-+ offset += ret;
-+ size -= ret;
-+ } else if (ret == -1) {
-+ gg_debug(GG_DEBUG_MISC, "-- errno = %d (%s)\n", errno, strerror(errno));
-+ if (errno == EAGAIN) {
-+ gg_debug(GG_DEBUG_MISC, "-- %d bytes received, %d left\n", offset, size);
-+ sess->recv_buf = buf;
-+ sess->recv_left = size;
-+ sess->recv_done = offset;
-+ return NULL;
-+ }
-+ if (errno != EINTR) {
-+// errno = EINVAL;
-+ free(buf);
-+ return NULL;
-+ }
-+ }
-+ }
-+
-+ sess->recv_left = 0;
-+
-+#ifdef GG_DEBUG
-+ if ((gg_debug_level & GG_DEBUG_DUMP)) {
-+ int i;
-+
-+ gg_debug(GG_DEBUG_DUMP, ">> received packet (type=%.2x):", h.type);
-+ for (i = 0; i < sizeof(h) + h.length; i++)
-+ gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]);
-+ gg_debug(GG_DEBUG_DUMP, "\n");
-+ }
-+#endif
-+
-+ return buf;
-+}
-+
-+/*
-+ * gg_send_packet() // funkcja wewnêtrzna
-+ *
-+ * konstruuje pakiet i wysy³a go w do serwera.
-+ *
-+ * - sock - po³±czony socket,
-+ * - type - typ pakietu,
-+ * - packet - wska¼nik do struktury pakietu,
-+ * - length - d³ugo¶æ struktury pakietu,
-+ * - payload - dodatkowy tekst doklejany do pakietu (np. wiadomo¶æ),
-+ * - payload_length - d³ugo¶æ dodatkowego tekstu.
-+ *
-+ * je¶li posz³o dobrze, zwraca 0. w przypadku b³êdu -1. je¶li errno=ENOMEM,
-+ * zabrak³o pamiêci. inaczej by³ b³±d przy wysy³aniu pakietu. dla errno=0
-+ * nie wys³ano ca³ego pakietu.
-+ */
-+static int gg_send_packet(int sock, int type, void *packet, int length, void *payload, int payload_length)
-+{
-+ struct gg_header *h;
-+ int res, plen;
-+ char *tmp;
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(0x%.2x, %d, %d);\n", type, length, payload_length);
-+
-+ if (length < 0 || payload_length < 0) {
-+ gg_debug(GG_DEBUG_MISC, "-- invalid packet/payload length\n");
-+ errno = ERANGE;
-+ return -1;
-+ }
-+
-+ if (!(tmp = malloc(sizeof(struct gg_header) + length + payload_length))) {
-+ gg_debug(GG_DEBUG_MISC, "-- not enough memory\n");
-+ return -1;
-+ }
-+
-+ h = (struct gg_header*) tmp;
-+ h->type = fix32(type);
-+ h->length = fix32(length + payload_length);
-+
-+ if (packet)
-+ memcpy(tmp + sizeof(struct gg_header), packet, length);
-+ if (payload)
-+ memcpy(tmp + sizeof(struct gg_header) + length, payload, payload_length);
-+
-+#ifdef GG_DEBUG
-+ if ((gg_debug_level & GG_DEBUG_DUMP)) {
-+ int i;
-+
-+ gg_debug(GG_DEBUG_DUMP, "%%%% sending packet (type=%.2x):", fix32(h->type));
-+ for (i = 0; i < sizeof(struct gg_header) + fix32(h->length); i++)
-+ gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]);
-+ gg_debug(GG_DEBUG_DUMP, "\n");
-+ }
-+#endif
-+
-+ plen = sizeof(struct gg_header) + length + payload_length;
-+
-+ if ((res = write(sock, tmp, plen)) < plen) {
-+ gg_debug(GG_DEBUG_MISC, "-- write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno));
-+ free(tmp);
-+ return -1;
-+ }
-+
-+ free(tmp);
-+ return 0;
-+}
-+
-+
-+/*
-+ * gg_login()
-+ *
-+ * rozpoczyna procedurê ³±czenia siê z serwerem. resztê obs³guje siê przez
-+ * gg_watch_event.
-+ *
-+ * - uin - numerek usera,
-+ * - password - jego hase³ko,
-+ * - async - ma byæ asynchronicznie?
-+ *
-+ * UWAGA! program musi obs³u¿yæ SIGCHLD, je¶li ³±czy siê asynchronicznie,
-+ * ¿eby zrobiæ pogrzeb zmar³emu procesowi resolvera.
-+ *
-+ * w przypadku b³êdu zwraca NULL, je¶li idzie dobrze (async) albo posz³o
-+ * dobrze (sync), zwróci wska¼nik do zaalokowanej struktury `gg_session'.
-+ */
-+struct gg_session *gg_login(uin_t uin, char *password, int async)
-+{
-+ struct gg_session *sess;
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%u, \"...\", %d);\n", uin, async);
-+
-+ if (!(sess = malloc(sizeof(*sess))))
-+ return NULL;
-+
-+ sess->uin = uin;
-+ if (!(sess->password = strdup(password))) {
-+ free(sess);
-+ return NULL;
-+ }
-+ sess->state = GG_STATE_RESOLVING;
-+ sess->check = GG_CHECK_READ;
-+ sess->async = async;
-+ sess->seq = 0;
-+ sess->recv_left = 0;
-+
-+ if (async) {
-+ if (gg_resolve(&sess->fd, &sess->pid, GG_APPMSG_HOST)) {
-+ gg_debug(GG_DEBUG_MISC, "-- resolving failed\n");
-+ free(sess);
-+ return NULL;
-+ }
-+ } else {
-+ struct in_addr a;
-+
-+ if ((a.s_addr = inet_addr(GG_APPMSG_HOST)) == INADDR_NONE) {
-+ struct hostent *he;
-+
-+ if (!(he = gethostbyname(GG_APPMSG_HOST))) {
-+ gg_debug(GG_DEBUG_MISC, "-- host %s not found\n", GG_APPMSG_HOST);
-+ free(sess);
-+ return NULL;
-+ } else
-+ memcpy((char*) &a, he->h_addr, sizeof(a));
-+ }
-+
-+ if (!(sess->fd = gg_connect(&a, GG_APPMSG_PORT, 0)) == -1) {
-+ gg_debug(GG_DEBUG_MISC, "-- connection failed\n");
-+ free(sess);
-+ return NULL;
-+ }
-+
-+ sess->state = GG_STATE_CONNECTING_HTTP;
-+
-+ while (sess->state != GG_STATE_CONNECTED) {
-+ struct gg_event *e;
-+
-+ if (!(e = gg_watch_fd(sess))) {
-+ gg_debug(GG_DEBUG_MISC, "-- some nasty error in gg_watch_fd()\n");
-+ free(sess);
-+ return NULL;
-+ }
-+
-+ if (e->type == GG_EVENT_CONN_FAILED) {
-+ gg_debug(GG_DEBUG_MISC, "-- could not login\n");
-+ gg_free_event(e);
-+ free(sess);
-+ return NULL;
-+ }
-+
-+ gg_free_event(e);
-+ }
-+ }
-+
-+ return sess;
-+}
-+
-+/*
-+ * gg_free_session()
-+ *
-+ * zwalnia pamiêæ zajmowan± przez opis sesji.
-+ *
-+ * - sess - opis sesji.
-+ *
-+ * nie zwraca niczego, bo i po co?
-+ */
-+void gg_free_session(struct gg_session *sess)
-+{
-+ if (!sess)
-+ return;
-+
-+ free(sess->password);
-+ free(sess);
-+}
-+
-+/*
-+ * gg_change_status()
-+ *
-+ * zmienia status u¿ytkownika. przydatne do /away i /busy oraz /quit.
-+ *
-+ * - sess - opis sesji,
-+ * - status - nowy status u¿ytkownika.
-+ *
-+ * je¶li wys³a³ pakiet zwraca 0, je¶li nie uda³o siê, zwraca -1.
-+ */
-+int gg_change_status(struct gg_session *sess, int status)
-+{
-+ struct gg_new_status p;
-+
-+ if (!sess) {
-+ errno = EFAULT;
-+ return -1;
-+ }
-+
-+ if (sess->state != GG_STATE_CONNECTED) {
-+ errno = ENOTCONN;
-+ return -1;
-+ }
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(..., %d);\n", status);
-+
-+ p.status = fix32(status);
-+
-+ return gg_send_packet(sess->fd, GG_NEW_STATUS, &p, sizeof(p), NULL, 0);
-+}
-+
-+/*
-+ * gg_logoff()
-+ *
-+ * wylogowuje u¿ytkownika i zamyka po³±czenie.
-+ *
-+ * - sock - deskryptor socketu.
-+ *
-+ * nie zwraca b³êdów. skoro siê ¿egnamy, to olewamy wszystko.
-+ */
-+void gg_logoff(struct gg_session *sess)
-+{
-+ if (!sess)
-+ return;
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(...);\n");
-+
-+ if (sess->state == GG_STATE_CONNECTED)
-+ gg_change_status(sess, GG_STATUS_NOT_AVAIL);
-+
-+ if (sess->fd) {
-+ shutdown(sess->fd, 2);
-+ close(sess->fd);
-+ }
-+}
-+
-+/*
-+ * gg_send_message()
-+ *
-+ * wysy³a wiadomo¶æ do innego u¿ytkownika. zwraca losowy numer
-+ * sekwencyjny, który mo¿na olaæ albo wykorzystaæ do potwierdzenia.
-+ *
-+ * - sess - opis sesji,
-+ * - msgclass - rodzaj wiadomo¶ci,
-+ * - recipient - numer adresata,
-+ * - message - tre¶æ wiadomo¶ci.
-+ *
-+ * w przypadku b³êdu zwraca -1, inaczej numer sekwencyjny.
-+ */
-+int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, unsigned char *message)
-+{
-+ struct gg_send_msg s;
-+
-+ if (!sess) {
-+ errno = EFAULT;
-+ return -1;
-+ }
-+
-+ if (sess->state != GG_STATE_CONNECTED) {
-+ errno = ENOTCONN;
-+ return -1;
-+ }
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(..., %d, %u, \"...\");\n", msgclass, recipient);
-+
-+ s.recipient = fix32(recipient);
-+ if (!sess->seq)
-+ sess->seq = 0x01740000 | (rand() & 0xffff);
-+ s.seq = fix32(sess->seq);
-+ s.msgclass = fix32(msgclass);
-+ sess->seq += (rand() % 0x300) + 0x300;
-+
-+ if (gg_send_packet(sess->fd, GG_SEND_MSG, &s, sizeof(s), message, strlen(message) + 1) == -1)
-+ return -1;
-+
-+ return fix32(s.seq);
-+}
-+
-+/*
-+ * gg_ping()
-+ *
-+ * wysy³a do serwera pakiet typu yeah-i'm-still-alive.
-+ *
-+ * - sess - zgadnij.
-+ *
-+ * je¶li nie powiod³o siê wys³anie pakietu, zwraca -1. otherwise 0.
-+ */
-+int gg_ping(struct gg_session *sess)
-+{
-+ if (!sess) {
-+ errno = EFAULT;
-+ return -1;
-+ }
-+
-+ if (sess->state != GG_STATE_CONNECTED) {
-+ errno = ENOTCONN;
-+ return -1;
-+ }
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(...);\n");
-+
-+ return gg_send_packet(sess->fd, GG_PING, NULL, 0, NULL, 0);
-+}
-+
-+/*
-+ * gg_free_event()
-+ *
-+ * zwalnia pamiêæ zajmowan± przez informacjê o zdarzeniu
-+ *
-+ * - event - wska¼nik do informacji o zdarzeniu
-+ *
-+ * nie ma czego zwracaæ.
-+ */
-+void gg_free_event(struct gg_event *e)
-+{
-+ if (!e)
-+ return;
-+ if (e->type == GG_EVENT_MSG)
-+ free(e->event.msg.message);
-+ if (e->type == GG_EVENT_NOTIFY)
-+ free(e->event.notify);
-+ free(e);
-+}
-+
-+/*
-+ * gg_notify()
-+ *
-+ * wysy³a serwerowi listê ludków, za którymi têsknimy.
-+ *
-+ * - sess - identyfikator sesji,
-+ * - userlist - wska¼nik do tablicy numerów,
-+ * - count - ilo¶æ numerków.
-+ *
-+ * je¶li uda³o siê, zwraca 0. je¶li b³±d, dostajemy -1.
-+ */
-+int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
-+{
-+ struct gg_notify *n;
-+ uin_t *u;
-+ int i, res = 0;
-+
-+ if (!sess) {
-+ errno = EFAULT;
-+ return -1;
-+ }
-+
-+ if (sess->state != GG_STATE_CONNECTED) {
-+ errno = ENOTCONN;
-+ return -1;
-+ }
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(..., %d);\n", count);
-+
-+ if (!userlist || !count)
-+ return 0;
-+
-+ if (!(n = (struct gg_notify*) malloc(sizeof(*n) * count)))
-+ return -1;
-+
-+ for (u = userlist, i = 0; i < count; u++, i++) {
-+ n[i].uin = fix32(*u);
-+ n[i].dunno1 = 3;
-+ }
-+
-+ if (gg_send_packet(sess->fd, GG_NOTIFY, n, sizeof(*n) * count, NULL, 0) == -1)
-+ res = -1;
-+
-+ free(n);
-+
-+ return res;
-+}
-+
-+/*
-+ * gg_add_notify()
-+ *
-+ * dodaje w locie do listy ukochanych dany numerek.
-+ *
-+ * - sess - identyfikator sesji,
-+ * - uin - numerek ukochanej.
-+ *
-+ * je¶li uda³o siê wys³aæ, daje 0. inaczej -1.
-+ */
-+int gg_add_notify(struct gg_session *sess, uin_t uin)
-+{
-+ struct gg_add_remove a;
-+
-+ if (!sess) {
-+ errno = EFAULT;
-+ return -1;
-+ }
-+
-+ if (sess->state != GG_STATE_CONNECTED) {
-+ errno = ENOTCONN;
-+ return -1;
-+ }
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_add_notify(..., %u);\n", uin);
-+
-+ a.uin = fix32(uin);
-+ a.dunno1 = 3;
-+
-+ return gg_send_packet(sess->fd, GG_ADD_NOTIFY, &a, sizeof(a), NULL, 0);
-+}
-+
-+/*
-+ * gg_remove_notify()
-+ *
-+ * w locie usuwa z listy zainteresowanych.
-+ *
-+ * - sess - id sesji,
-+ * - uin - numerek.
-+ *
-+ * zwraca -1 je¶li by³ b³±d, 0 je¶li siê uda³o wys³aæ pakiet.
-+ */
-+int gg_remove_notify(struct gg_session *sess, uin_t uin)
-+{
-+ struct gg_add_remove a;
-+
-+ if (!sess) {
-+ errno = EFAULT;
-+ return -1;
-+ }
-+
-+ if (sess->state != GG_STATE_CONNECTED) {
-+ errno = ENOTCONN;
-+ return -1;
-+ }
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify(..., %u);\n", uin);
-+
-+ a.uin = fix32(uin);
-+ a.dunno1 = 3;
-+
-+ return gg_send_packet(sess->fd, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL, 0);
-+}
-+
-+/*
-+ * gg_watch_fd_connected() // funkcja wewnêtrzna
-+ *
-+ * patrzy na socketa, odbiera pakiet i wype³nia strukturê zdarzenia.
-+ *
-+ * - sock - lalala, trudno zgadn±æ.
-+ *
-+ * je¶li b³±d -1, je¶li dobrze 0.
-+ */
-+static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
-+{
-+ struct gg_header *h;
-+ void *p;
-+
-+ if (!sess) {
-+ errno = EFAULT;
-+ return -1;
-+ }
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(...);\n");
-+
-+ if (!(h = gg_recv_packet(sess))) {
-+ gg_debug(GG_DEBUG_MISC, "-- gg_recv_packet failed. errno = %d (%d)\n", errno, strerror(errno));
-+ return -1;
-+ }
-+
-+ p = (void*) h + sizeof(struct gg_header);
-+
-+ if (h->type == GG_RECV_MSG) {
-+ struct gg_recv_msg *r = p;
-+
-+ gg_debug(GG_DEBUG_MISC, "-- received a message\n");
-+
-+ if (h->length >= sizeof(*r)) {
-+ e->type = GG_EVENT_MSG;
-+ e->event.msg.msgclass = fix32(r->msgclass);
-+ e->event.msg.sender = fix32(r->sender);
-+ e->event.msg.message = strdup((char*) r + sizeof(*r));
-+ }
-+ }
-+
-+ if (h->type == GG_NOTIFY_REPLY) {
-+ struct gg_notify_reply *n = p;
-+
-+ gg_debug(GG_DEBUG_MISC, "-- received a notify reply\n");
-+
-+ e->type = GG_EVENT_NOTIFY;
-+ if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) {
-+ gg_debug(GG_DEBUG_MISC, "-- not enough memory\n");
-+ free(h);
-+ return -1;
-+ }
-+ memcpy(e->event.notify, p, h->length);
-+ e->event.notify[h->length / sizeof(*n)].uin = 0;
-+ }
-+
-+ if (h->type == GG_STATUS) {
-+ struct gg_status *s = p;
-+
-+ gg_debug(GG_DEBUG_MISC, "-- received a status change\n");
-+
-+ if (h->length >= sizeof(*s)) {
-+ e->type = GG_EVENT_STATUS;
-+ memcpy(&e->event.status, p, h->length);
-+ }
-+ }
-+
-+ if (h->type == GG_SEND_MSG_ACK) {
-+ struct gg_send_msg_ack *s = p;
-+
-+ gg_debug(GG_DEBUG_MISC, "-- received a message ack\n");
-+
-+ if (h->length >= sizeof(*s)) {
-+ e->type = GG_EVENT_ACK;
-+ e->event.ack.status = fix32(s->status);
-+ e->event.ack.recipient = fix32(s->recipient);
-+ e->event.ack.seq = fix32(s->seq);
-+ }
-+ }
-+
-+ free(h);
-+
-+ return 0;
-+}
-+
-+/*
-+ * gg_chomp() // funkcja wewnêtrzna
-+ *
-+ * ucina "\r\n" lub "\n" z koñca linii.
-+ *
-+ * - line - ofiara operacji plastycznej.
-+ *
-+ * niczego nie zwraca.
-+ */
-+static void gg_chomp(char *line)
-+{
-+ if (!line || strlen(line) < 1)
-+ return;
-+
-+ if (line[strlen(line) - 1] == '\n')
-+ line[strlen(line) - 1] = 0;
-+ if (line[strlen(line) - 1] == '\r')
-+ line[strlen(line) - 1] = 0;
-+}
-+
-+/*
-+ * gg_watch_fd()
-+ *
-+ * funkcja wywo³ywana, gdy co¶ siê stanie na obserwowanym deskryptorze.
-+ * zwraca klientowi informacjê o tym, co siê dzieje.
-+ *
-+ * - sess - identyfikator sesji.
-+ *
-+ * zwraca wska¼nik do struktury gg_event, któr± trzeba zwolniæ pó¼niej
-+ * za pomoc± gg_free_event(). jesli rodzaj zdarzenia jest równy
-+ * GG_EVENT_NONE, nale¿y je olaæ kompletnie. je¶li zwróci³o NULL,
-+ * sta³o siê co¶ niedobrego -- albo brak³o pamiêci albo zerwa³o
-+ * po³±czenie albo co¶ takiego.
-+ */
-+struct gg_event *gg_watch_fd(struct gg_session *sess)
-+{
-+ struct gg_event *e;
-+ int res = 0;
-+
-+ if (!sess) {
-+ errno = EFAULT;
-+ return NULL;
-+ }
-+
-+ gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(...);\n");
-+
-+ if (!(e = (void*) malloc(sizeof(*e)))) {
-+ gg_debug(GG_DEBUG_MISC, "-- not enough memory\n");
-+ return NULL;
-+ }
-+
-+ e->type = GG_EVENT_NONE;
-+
-+ switch (sess->state) {
-+ case GG_STATE_RESOLVING:
-+ {
-+ struct in_addr a;
-+
-+ gg_debug(GG_DEBUG_MISC, "== GG_STATE_RESOLVING\n");
-+
-+ if (read(sess->fd, &a, sizeof(a)) < sizeof(a) || a.s_addr == INADDR_NONE) {
-+ gg_debug(GG_DEBUG_MISC, "-- resolving failed\n");
-+
-+ e->type = GG_EVENT_CONN_FAILED;
-+ e->event.failure = GG_FAILURE_RESOLVING;
-+ sess->state = GG_STATE_IDLE;
-+
-+ close(sess->fd);
-+
-+ break;
-+ }
-+
-+ close(sess->fd);
-+
-+ waitpid(sess->pid, NULL, 0);
-+
-+ gg_debug(GG_DEBUG_MISC, "-- resolved, now connecting\n");
-+
-+ if ((sess->fd = gg_connect(&a, GG_APPMSG_PORT, sess->async)) == -1) {
-+ gg_debug(GG_DEBUG_MISC, "-- connection failed\n");
-+
-+ e->type = GG_EVENT_CONN_FAILED;
-+ e->event.failure = GG_FAILURE_CONNECTING;
-+ sess->state = GG_STATE_IDLE;
-+ break;
-+ }
-+
-+ sess->state = GG_STATE_CONNECTING_HTTP;
-+ sess->check = GG_CHECK_WRITE;
-+
-+ break;
-+ }
-+
-+ case GG_STATE_CONNECTING_HTTP:
-+ {
-+ char buf[1024];
-+ int res, res_size = sizeof(res);
-+
-+ gg_debug(GG_DEBUG_MISC, "== GG_STATE_CONNECTING_HTTP\n");
-+
-+ if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-+ gg_debug(GG_DEBUG_MISC, "-- http connection failed, errno = %d (%s)\n", res, strerror(res));
-+
-+ errno = res;
-+ e->type = GG_EVENT_CONN_FAILED;
-+ e->event.failure = GG_FAILURE_CONNECTING;
-+ sess->state = GG_STATE_IDLE;
-+ break;
-+ }
-+
-+ gg_debug(GG_DEBUG_MISC, "-- http connection succeded, sending query\n");
-+
-+
-+ snprintf(buf, sizeof(buf) - 1,
-+ "GET /appsvc/appmsg.asp?fmnumber=%u HTTP/1.0\r\n"
-+ "Host: " GG_APPMSG_HOST "\r\n"
-+ "User-Agent: Mozilla/4.7 [en] (Win98; I)\r\n"
-+ "Pragma: no-cache\r\n"
-+ "\r\n", sess->uin);
-+
-+ if (write(sess->fd, buf, strlen(buf)) < strlen(buf)) {
-+ gg_debug(GG_DEBUG_MISC, "-- sending query failed\n");
-+
-+ e->type = GG_EVENT_CONN_FAILED;
-+ e->event.failure = GG_FAILURE_WRITING;
-+ sess->state = GG_STATE_IDLE;
-+ break;
-+ }
-+
-+ sess->state = GG_STATE_WRITING_HTTP;
-+ sess->check = GG_CHECK_READ;
-+
-+ break;
-+ }
-+
-+ case GG_STATE_WRITING_HTTP:
-+ {
-+ char buf[1024], *tmp, *host;
-+ int port = GG_DEFAULT_PORT;
-+ struct in_addr a;
-+
-+ gg_debug(GG_DEBUG_MISC, "== GG_STATE_WRITING_HTTP\n");
-+
-+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
-+ gg_chomp(buf);
-+
-+ gg_debug(GG_DEBUG_TRAFFIC, "-- got http response (%s)\n", buf);
-+
-+ if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) {
-+ gg_debug(GG_DEBUG_MISC, "-- but that's not what we've expected\n");
-+
-+ e->type = GG_EVENT_CONN_FAILED;
-+ e->event.failure = GG_FAILURE_INVALID;
-+ sess->state = GG_STATE_IDLE;
-+ break;
-+ }
-+
-+ while (strcmp(buf, "\r\n") && strcmp(buf, ""))
-+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
-+
-+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
-+ gg_chomp(buf);
-+
-+ close(sess->fd);
-+
-+ gg_debug(GG_DEBUG_TRAFFIC, "-- received http data (%s)\n", buf);
-+
-+ tmp = buf;
-+ while (*tmp && *tmp != ' ')
-+ tmp++;
-+ while (*tmp && *tmp == ' ')
-+ tmp++;
-+ while (*tmp && *tmp != ' ')
-+ tmp++;
-+ while (*tmp && *tmp == ' ')
-+ tmp++;
-+ while (*tmp && *tmp != ' ')
-+ tmp++;
-+ while (*tmp && *tmp == ' ')
-+ tmp++;
-+ host = tmp;
-+ while (*tmp && *tmp != ' ')
-+ tmp++;
-+ *tmp = 0;
-+
-+ if ((tmp = strchr(host, ':'))) {
-+ *tmp = 0;
-+ port = atoi(tmp+1);
-+ }
-+
-+ a.s_addr = inet_addr(host);
-+
-+ if ((sess->fd = gg_connect(&a, port, sess->async)) == -1) {
-+ gg_debug(GG_DEBUG_MISC, "-- connect() failed. errno = %d (%s)\n", errno, strerror(errno));
-+
-+ e->type = GG_EVENT_CONN_FAILED;
-+ e->event.failure = GG_FAILURE_CONNECTING;
-+ sess->state = GG_STATE_IDLE;
-+ break;
-+ }
-+
-+ sess->state = GG_STATE_CONNECTING_GG;
-+ sess->check = GG_CHECK_WRITE;
-+
-+ break;
-+ }
-+
-+ case GG_STATE_CONNECTING_GG:
-+ {
-+ int res, res_size = sizeof(res);
-+
-+ gg_debug(GG_DEBUG_MISC, "== GG_STATE_CONNECTING_GG\n");
-+
-+ if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-+ gg_debug(GG_DEBUG_MISC, "-- connection failed, errno = %d (%s)\n", errno, strerror(errno));
-+
-+ errno = res;
-+ e->type = GG_EVENT_CONN_FAILED;
-+ e->event.failure = GG_FAILURE_CONNECTING;
-+ sess->state = GG_STATE_IDLE;
-+ break;
-+ }
-+
-+ gg_debug(GG_DEBUG_MISC, "-- connected\n");
-+
-+ sess->state = GG_STATE_WAITING_FOR_KEY;
-+ sess->check = GG_CHECK_READ;
-+
-+ break;
-+ }
-+
-+ case GG_STATE_WAITING_FOR_KEY:
-+ {
-+ struct gg_header *h;
-+ struct gg_welcome *w;
-+ struct gg_login l;
-+ unsigned int hash;
-+ char *password = sess->password;
-+
-+ gg_debug(GG_DEBUG_MISC, "== GG_STATE_WAITING_FOR_KEY\n");
-+
-+ if (!(h = gg_recv_packet(sess))) {
-+ gg_debug(GG_DEBUG_MISC, "-- gg_recv_packet() failed. errno = %d (%s)\n", errno, strerror(errno));
-+
-+ e->type = GG_EVENT_CONN_FAILED;
-+ e->event.failure = GG_FAILURE_READING;
-+ sess->state = GG_STATE_IDLE;
-+ close(sess->fd);
-+ break;
-+ }
-+
-+ if (h->type != GG_WELCOME) {
-+ gg_debug(GG_DEBUG_MISC, "-- invalid packet received\n");
-+
-+ free(h);
-+ close(sess->fd);
-+ e->type = GG_EVENT_CONN_FAILED;
-+ e->event.failure = GG_FAILURE_INVALID;
-+ sess->state = GG_STATE_IDLE;
-+ break;
-+ }
-+
-+ w = (void*) h + sizeof(struct gg_header);
-+ w->key = fix32(w->key);
-+
-+ for (hash = 1; *password; password++)
-+ hash *= (*password) + 1;
-+ hash *= w->key;
-+
-+ gg_debug(GG_DEBUG_DUMP, "%%%% klucz serwera %.4x, hash has³a %.8x\n", w->key, hash);
-+
-+ free(h);
-+
-+ free(sess->password);
-+ sess->password = NULL;
-+
-+ l.uin = fix32(sess->uin);
-+ l.hash = fix32(hash);
-+ l.status = fix32(GG_STATUS_AVAIL);
-+ l.dunno = fix32(0x0b);
-+ l.local_ip = 0;
-+ l.local_port = 0;
-+
-+ gg_debug(GG_DEBUG_TRAFFIC, "-- sending GG_LOGIN packet\n");
-+
-+ if (gg_send_packet(sess->fd, GG_LOGIN, &l, sizeof(l), NULL, 0) == -1) {
-+ gg_debug(GG_DEBUG_TRAFFIC, "-- oops, failed. errno = %d (%s)\n", errno, strerror(errno));
-+
-+ close(sess->fd);
-+ e->type = GG_EVENT_CONN_FAILED;
-+ e->event.failure = GG_FAILURE_WRITING;
-+ sess->state = GG_STATE_IDLE;
-+ break;
-+ }
-+
-+ sess->state = GG_STATE_SENDING_KEY;
-+
-+ break;
-+ }
-+
-+ case GG_STATE_SENDING_KEY:
-+ {
-+ struct gg_header *h;
-+
-+ gg_debug(GG_DEBUG_MISC, "== GG_STATE_SENDING_KEY\n");
-+
-+ if (!(h = gg_recv_packet(sess))) {
-+ gg_debug(GG_DEBUG_MISC, "-- recv_packet failed\n");
-+ e->type = GG_EVENT_CONN_FAILED;
-+ e->event.failure = GG_FAILURE_READING;
-+ sess->state = GG_STATE_IDLE;
-+ close(sess->fd);
-+ break;
-+ }
-+
-+ if (h->type == GG_LOGIN_OK) {
-+ gg_debug(GG_DEBUG_MISC, "-- login succeded\n");
-+ e->type = GG_EVENT_CONN_SUCCESS;
-+ sess->state = GG_STATE_CONNECTED;
-+ break;
-+ }
-+
-+ if (h->type == GG_LOGIN_FAILED) {
-+ gg_debug(GG_DEBUG_MISC, "-- login failed\n");
-+ e->event.failure = GG_FAILURE_PASSWORD;
-+ errno = EACCES;
-+ } else {
-+ gg_debug(GG_DEBUG_MISC, "-- invalid packet\n");
-+ e->event.failure = GG_FAILURE_INVALID;
-+ }
-+
-+ e->type = GG_EVENT_CONN_FAILED;
-+ sess->state = GG_STATE_IDLE;
-+ close(sess->fd);
-+
-+ break;
-+ }
-+
-+ case GG_STATE_CONNECTED:
-+ {
-+ gg_debug(GG_DEBUG_MISC, "== GG_STATE_CONNECTED\n");
-+
-+ if ((res = gg_watch_fd_connected(sess, e)) == -1) {
-+
-+ gg_debug(GG_DEBUG_MISC, "-- watch_fd_connected failed. errno = %d (%s)\n", errno, strerror(errno));
-+
-+ if (errno == EAGAIN) {
-+ e->type = GG_EVENT_NONE;
-+ res = 0;
-+ } else
-+ res = -1;
-+ }
-+ break;
-+ }
-+ }
-+
-+ if (res == -1) {
-+ free(e);
-+ e = NULL;
-+ }
-+
-+ return e;
-+}
-+
-+
-diff -urN kadu-0.1.6b.org/kadu/libgg.h kadu-0.1.6b/kadu/libgg.h
---- kadu-0.1.6b.org/kadu/libgg.h Sat Sep 29 16:56:11 2001
-+++ kadu-0.1.6b/kadu/libgg.h Sat Sep 29 17:00:22 2001
-@@ -1,4 +1,21 @@
--/* $Id$ */
-+/* $Id$ */
-+
-+/*
-+ * (C) Copyright 2001 Wojtek Kaniewski <wojtekka@irc.pl>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License Version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-
- #ifndef __LIBGG_H
- #define __LIBGG_H
-@@ -19,7 +36,7 @@
- */
- struct gg_session {
- int state, check;
-- int fd;
-+ int fd, pid;
- int port;
- int seq, async;
-
-@@ -158,7 +175,7 @@
- struct gg_search_request request;
-
- /* bzdurki */
-- int mode, fd, async, state, check, error;
-+ int mode, fd, async, state, check, error, pid;
- char *header_buf, *data_buf;
- int header_size, data_size;
-
-@@ -180,7 +197,7 @@
- * je¶li chcemy sobie podebugowaæ, wystarczy zdefiniowaæ GG_DEBUG.
- */
-
--// int gg_debug_level;
-+//int gg_debug_level;
-
- #ifdef GG_DEBUG
-
-@@ -208,7 +225,7 @@
- * -------------------------------------------------------------------------
- */
-
--int gg_resolve(int *fd, char *hostname);
-+int gg_resolve(int *fd, int *pid, char *hostname);
- int gg_connect(void *addr, int port, int async);
- char *gg_alloc_sprintf(char *format, ...);
-
-diff -urN kadu-0.1.6b.org/kadu/libgg_search.c kadu-0.1.6b/kadu/libgg_search.c
---- kadu-0.1.6b.org/kadu/libgg_search.c Thu Jan 1 01:00:00 1970
-+++ kadu-0.1.6b/kadu/libgg_search.c Sat Sep 29 11:45:22 2001
-@@ -0,0 +1,665 @@
-+/* $Id$ */
-+
-+/*
-+ * (C) Copyright 2001 Wojtek Kaniewski <wojtekka@irc.pl>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License Version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <stdio.h>
-+#include <sys/socket.h>
-+#include <netinet/in.h>
-+#include <arpa/inet.h>
-+#include <sys/wait.h>
-+#include <netdb.h>
-+#include <errno.h>
-+#include <string.h>
-+#include <stdarg.h>
-+#include <ctype.h>
-+#include "libgg.h"
-+
-+/*
-+ * gg_urlencode() // funkcja wewnêtrzna
-+ *
-+ * zamienia podany tekst na ci±g znaków do formularza http. przydaje siê
-+ * przy szukaniu userów z dziwnymi znaczkami.
-+ *
-+ * - str - ci±g znaków do poprawki.
-+ *
-+ * zwraca zaalokowany bufor, który wypada³oby kiedy¶ zwolniæ albo NULL
-+ * w przypadku b³êdu.
-+ */
-+static char *gg_urlencode(char *str)
-+{
-+ char *p, *q, *buf, hex[] = "0123456789abcdef";
-+ int size = 0;
-+
-+ for (p = str; *p; p++, size++) {
-+ if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9'))
-+ size += 2;
-+ }
-+
-+ if (!(buf = malloc(size + 1)))
-+ return NULL;
-+
-+ for (p = str, q = buf; *p; p++, q++) {
-+ if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9'))
-+ *q = *p;
-+ else {
-+ *q++ = '%';
-+ *q++ = hex[*p >> 4 & 15];
-+ *q = hex[*p & 15];
-+ }
-+ }
-+
-+ *q = 0;
-+
-+ return buf;
-+}
-+
-+/*
-+ * gg_get_line() // funkcja wewnêtrzna
-+ *
-+ * podaje kolejn± liniê z bufora tekstowego. psuje co bezpowrotnie, dziel±c
-+ * na kolejne stringi. zdarza siê, nie ma potrzeby pisania funkcji dubluj±cej
-+ * bufor ¿eby tylko mieæ nieruszone dane wej¶ciowe, skoro i tak nie bêd± nam
-+ * po¼niej potrzebne. obcina `\r\n'.
-+ *
-+ * - ptr - wska¼nik do zmiennej, która przechowuje aktualn± pozycjê
-+ * w przemiatanym buforze.
-+ *
-+ * wska¼nik do kolejnej linii tekstu lub NULL, je¶li to ju¿ koniec bufora.
-+ */
-+static char *gg_get_line(char **ptr)
-+{
-+ char *foo, *res;
-+
-+ if (!ptr || !*ptr || !strcmp(*ptr, ""))
-+ return NULL;
-+
-+ res = *ptr;
-+
-+ if (!(foo = strchr(*ptr, '\n')))
-+ *ptr += strlen(*ptr);
-+ else {
-+ *ptr = foo + 1;
-+ *foo = 0;
-+ if (res[strlen(res) - 1] == '\r')
-+ res[strlen(res) - 1] = 0;
-+ }
-+
-+ return res;
-+}
-+
-+
-+/*
-+ * gg_search()
-+ *
-+ * rozpoczyna szukanie u¿ytkowników. informacje o tym, czego dok³adnie szukamy
-+ * s± zawarte w strukturze `gg_search_request'. ze wzglêdu na specyfikê ich
-+ * przeszukiwarki, niektórych pól nie mo¿na mieszaæ. s± oznaczone w libgg.h
-+ * jako osobne mode'y.
-+ *
-+ * - r - informacja o tym, czego szukamy,
-+ * - async - ma byæ asynchronicznie?
-+ *
-+ * zwraca zaalokowan± strukturê `gg_search', któr± po¼niej nale¿y zwolniæ
-+ * funkcj± gg_free_search(), albo NULL je¶li wyst±pi³ b³±d.
-+ */
-+struct gg_search *gg_search(struct gg_search_request *r, int async)
-+{
-+ struct gg_search *f;
-+ int mode = -1;
-+
-+ if (!r) {
-+ errno = EFAULT;
-+ return NULL;
-+ }
-+
-+ if (r->nickname || r->first_name || r->last_name || r->city || r->gender || r->min_birth || r->max_birth)
-+ mode = 0;
-+
-+ if (r->email) {
-+ if (mode != -1) {
-+ errno = EINVAL;
-+ return NULL;
-+ }
-+ mode = 1;
-+ }
-+
-+ if (r->phone) {
-+ if (mode != -1) {
-+ errno = EINVAL;
-+ return NULL;
-+ }
-+ mode = 2;
-+ }
-+
-+ if (r->uin) {
-+ if (mode != -1) {
-+ errno = EINVAL;
-+ return NULL;
-+ }
-+ mode = 3;
-+ }
-+
-+ if (mode == -1) {
-+ errno = EINVAL;
-+ return NULL;
-+ }
-+
-+ if (!(f = malloc(sizeof(*f))))
-+ return NULL;
-+
-+ memset(f, 0, sizeof(*f));
-+
-+ if (r->first_name)
-+ f->request.first_name = strdup(r->first_name);
-+ if (r->last_name)
-+ f->request.last_name = strdup(r->last_name);
-+ if (r->nickname)
-+ f->request.nickname = strdup(r->nickname);
-+ if (r->city)
-+ f->request.city = strdup(r->city);
-+ if (r->email)
-+ f->request.email = strdup(r->email);
-+ if (r->phone)
-+ f->request.phone = strdup(r->phone);
-+ f->request.active = r->active;
-+ f->request.gender = r->gender;
-+ if (f->request.gender == GG_GENDER_NONE)
-+ f->request.gender = -1;
-+ f->request.min_birth = r->min_birth;
-+ f->request.max_birth = r->max_birth;
-+ f->request.uin = r->uin;
-+
-+ f->async = async;
-+ f->mode = mode;
-+
-+ if (async) {
-+ if (gg_resolve(&f->fd, &f->pid, GG_PUBDIR_HOST)) {
-+ gg_free_search(f);
-+ return NULL;
-+ }
-+
-+ f->state = GG_STATE_RESOLVING;
-+ f->check = GG_CHECK_READ;
-+ } else {
-+ struct hostent *he;
-+ struct in_addr a;
-+
-+ if (!(he = gethostbyname(GG_PUBDIR_HOST))) {
-+ gg_free_search(f);
-+ return NULL;
-+ } else
-+ memcpy((char*) &a, he->h_addr, sizeof(a));
-+
-+ if (!(f->fd = gg_connect(&a, GG_PUBDIR_PORT, 0)) == -1) {
-+ gg_free_search(f);
-+ return NULL;
-+ }
-+
-+ f->state = GG_STATE_CONNECTING_HTTP;
-+
-+ while (f->state != GG_STATE_IDLE && f->state != GG_STATE_FINISHED) {
-+ if (gg_search_watch_fd(f) == -1)
-+ break;
-+ }
-+
-+ if (f->state != GG_STATE_FINISHED) {
-+ gg_free_search(f);
-+ return NULL;
-+ }
-+ }
-+
-+ return f;
-+}
-+
-+#define GET_LOST(x) \
-+ close(f->fd); \
-+ f->state = GG_STATE_IDLE; \
-+ f->error = x; \
-+ f->fd = 0; \
-+ return -1;
-+
-+/*
-+ * gg_search_watch_fd()
-+ *
-+ * przy asynchronicznym szukaniu userów wypada³oby wywo³aæ t± funkcjê przy
-+ * jaki¶ zmianach na gg_search->fd.
-+ *
-+ * - f - to co¶, co zwróci³o gg_search()
-+ *
-+ * je¶li wszystko posz³o dobrze to 0, inaczej -1. przeszukiwanie bêdzie
-+ * zakoñczone, je¶li f->state == GG_STATE_FINISHED. je¶li wyst±pi jaki¶
-+ * b³±d, to bêdzie tam GG_STATE_IDLE i odpowiedni kod b³êdu w f->error.
-+ */
-+int gg_search_watch_fd(struct gg_search *f)
-+{
-+ if (!f) {
-+ errno = EINVAL;
-+ return -1;
-+ }
-+
-+ if (f->state == GG_STATE_RESOLVING) {
-+ struct in_addr a;
-+
-+ gg_debug(GG_DEBUG_MISC, "=> resolved\n");
-+
-+ if (read(f->fd, &a, sizeof(a)) < sizeof(a) || a.s_addr == INADDR_NONE) {
-+ gg_debug(GG_DEBUG_MISC, "=> resolver thread failed\n");
-+ GET_LOST(GG_FAILURE_RESOLVING);
-+ }
-+
-+ close(f->fd);
-+
-+ waitpid(f->pid, NULL, 0);
-+
-+ gg_debug(GG_DEBUG_MISC, "=> connecting\n");
-+
-+ if ((f->fd = gg_connect(&a, GG_PUBDIR_PORT, f->async)) == -1) {
-+ gg_debug(GG_DEBUG_MISC, "=> connection failed\n");
-+ GET_LOST(GG_FAILURE_CONNECTING);
-+ }
-+
-+ f->state = GG_STATE_CONNECTING_HTTP;
-+ f->check = GG_CHECK_WRITE;
-+
-+ return 0;
-+ }
-+
-+ if (f->state == GG_STATE_CONNECTING_HTTP) {
-+ int res, res_size = sizeof(res);
-+ char *form, *query;
-+
-+ if (f->async && (getsockopt(f->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-+ gg_debug(GG_DEBUG_MISC, "=> async connection failed\n");
-+ GET_LOST(GG_FAILURE_CONNECTING);
-+ }
-+
-+ if (f->mode == 0) {
-+ char *__first_name, *__last_name, *__nickname, *__city;
-+
-+ __first_name = gg_urlencode((f->request.first_name) ? f->request.first_name : "");
-+ __last_name = gg_urlencode((f->request.last_name) ? f->request.last_name : "");
-+ __nickname = gg_urlencode((f->request.nickname) ? f->request.nickname : "");
-+ __city = gg_urlencode((f->request.city) ? f->request.city : "");
-+
-+ if (!__first_name || !__last_name || !__nickname || !__city) {
-+ free(__first_name);
-+ free(__last_name);
-+ free(__nickname);
-+ free(__city);
-+ gg_debug(GG_DEBUG_MISC, "=> not enough memory for form fields\n");
-+ GET_LOST(GG_FAILURE_WRITING);
-+ }
-+
-+ form = gg_alloc_sprintf("Mode=0&FirstName=%s&LastName=%s&Gender=%d&NickName=%s&City=%s&MinBirth=%d&MaxBirth=%d%s", __first_name, __last_name, f->request.gender, __nickname, __city, f->request.min_birth, f->request.max_birth, (f->request.active) ? "&ActiveOnly=" : "");
-+
-+ free(__first_name);
-+ free(__last_name);
-+ free(__nickname);
-+ free(__city);
-+
-+ } else if (f->mode == 1) {
-+ char *__email = gg_urlencode((f->request.email) ? f->request.email : "");
-+
-+ if (!__email) {
-+ gg_debug(GG_DEBUG_MISC, "=> not enough memory for form fields\n");
-+ GET_LOST(GG_FAILURE_WRITING);
-+ }
-+
-+ form = gg_alloc_sprintf("Mode=1&Email=%s%s", __email, (f->request.active) ? "&ActiveOnly=" : "");
-+
-+ free(__email);
-+
-+ } else if (f->mode == 2) {
-+ char *__phone = gg_urlencode((f->request.phone) ? f->request.phone : "");
-+
-+ if (!__phone) {
-+ gg_debug(GG_DEBUG_MISC, "=> not enough memory for form fields\n");
-+ GET_LOST(GG_FAILURE_WRITING);
-+ }
-+
-+ form = gg_alloc_sprintf("Mode=2&MobilePhone=%s%s", __phone, (f->request.active) ? "&ActiveOnly=" : "");
-+
-+ free(__phone);
-+
-+ } else
-+ form = gg_alloc_sprintf("Mode=3&UserId=%u%s", f->request.uin, (f->request.active) ? "&ActiveOnly=" : "");
-+
-+ if (!form) {
-+ gg_debug(GG_DEBUG_MISC, "=> not enough memory for form query\n");
-+ GET_LOST(GG_FAILURE_WRITING);
-+ }
-+
-+ gg_debug(GG_DEBUG_MISC, "=> %s\n", form);
-+
-+ query = gg_alloc_sprintf(
-+ "POST /appsvc/fmpubquery2.asp HTTP/1.0\r\n"
-+ "Host: " GG_PUBDIR_HOST "\r\n"
-+ "Content-Type: application/x-www-form-urlencoded\r\n"
-+ "User-Agent: Mozilla/4.7 [en] (Win98; I)\r\n"
-+ "Content-Length: %d\r\n"
-+ "Pragma: no-cache\r\n"
-+ "\r\n"
-+ "%s",
-+ strlen(form), form);
-+
-+ free(form);
-+
-+ if (!query) {
-+ gg_debug(GG_DEBUG_MISC, "=> not enough memory for query string\n");
-+ GET_LOST(GG_FAILURE_WRITING);
-+ }
-+
-+ if ((res = write(f->fd, query, strlen(query))) < strlen(query)) {
-+ gg_debug(GG_DEBUG_MISC, "=> http request failed (len=%d, res=%d, errno=%d)\n", strlen(query), res, errno);
-+ free(query);
-+ GET_LOST(GG_FAILURE_WRITING);
-+ }
-+
-+ free(query);
-+
-+ gg_debug(GG_DEBUG_MISC, "=> http request sent (len=%d)\n", strlen(query));
-+
-+ f->state = GG_STATE_READING_HEADER;
-+ f->check = GG_CHECK_READ;
-+
-+ return 0;
-+
-+ }
-+
-+ if (f->state == GG_STATE_READING_HEADER) {
-+ char buf[1024], *tmp;
-+ int res;
-+
-+ if ((res = read(f->fd, buf, sizeof(buf))) == -1) {
-+ gg_debug(GG_DEBUG_MISC, "=> reading http header failed (errno=%d)\n", errno);
-+ if (f->header_buf) {
-+ free(f->header_buf);
-+ f->header_buf = NULL;
-+ }
-+ GET_LOST(GG_FAILURE_READING);
-+ }
-+
-+ gg_debug(GG_DEBUG_MISC, "=> read %d bytes\n", res);
-+
-+#if 0
-+ if (!f->header_buf) {
-+ if (!(f->header_buf = malloc(res + 1))) {
-+ gg_debug(GG_DEBUG_MISC, "=> not enough memory for header\n");
-+ GET_LOST(GG_FAILURE_READING);
-+ }
-+ memcpy(f->header_buf, buf, res);
-+ f->header_size = res;
-+ } else {
-+ if (!(f->header_buf = realloc(f->header_buf, f->header_size + res + 1))) {
-+ gg_debug(GG_DEBUG_MISC, "=> not enough memory for header\n");
-+ GET_LOST(GG_FAILURE_READING);
-+ }
-+ memcpy(f->header_buf + f->header_size, buf, res);
-+ f->header_size += res;
-+ }
-+#endif
-+
-+ if (!(f->header_buf = realloc(f->header_buf, f->header_size + res + 1))) {
-+ gg_debug(GG_DEBUG_MISC, "=> not enough memory for header\n");
-+ GET_LOST(GG_FAILURE_READING);
-+ }
-+ memcpy(f->header_buf + f->header_size, buf, res);
-+ f->header_size += res;
-+
-+ gg_debug(GG_DEBUG_MISC, "=> header_buf=%p, header_size=%d\n", f->header_buf, f->header_size);
-+
-+ f->header_buf[f->header_size] = 0;
-+
-+ if ((tmp = strstr(f->header_buf, "\r\n\r\n")) || (tmp = strstr(f->header_buf, "\n\n"))) {
-+ int sep_len = (*tmp == '\r') ? 4 : 2, left;
-+ char *line;
-+
-+ left = f->header_size - ((int)(tmp) - (int)(f->header_buf) + sep_len);
-+
-+ gg_debug(GG_DEBUG_MISC, "=> got all header (%d bytes, %d left)\n", f->header_size - left, left);
-+
-+ /* HTTP/1.1 200 OK */
-+ if (strlen(f->header_buf) < 16 || strncmp(f->header_buf + 9, "200", 3)) {
-+ gg_debug(GG_DEBUG_MISC, "=> didn't get 200 OK -- no results\n");
-+ free(f->header_buf);
-+ f->header_buf = NULL;
-+ close(f->fd);
-+ f->state = GG_STATE_FINISHED;
-+ f->fd = 0;
-+ f->count = 0;
-+ f->results = NULL;
-+
-+ return 0;
-+ }
-+
-+ f->data_size = 0;
-+ line = f->header_buf;
-+ *tmp = 0;
-+
-+ while (line) {
-+ if (!strncasecmp(line, "Content-length: ", 16)) {
-+ f->data_size = atoi(line + 16);
-+ }
-+ line = strchr(line, '\n');
-+ if (line)
-+ line++;
-+ }
-+
-+ if (!f->data_size) {
-+ gg_debug(GG_DEBUG_MISC, "=> content-length not found\n");
-+ free(f->header_buf);
-+ f->header_buf = NULL;
-+ GET_LOST(GG_FAILURE_READING);
-+ }
-+
-+ gg_debug(GG_DEBUG_MISC, "=> data_size=%d\n", f->data_size);
-+
-+ if (!(f->data_buf = malloc(f->data_size + 1))) {
-+ gg_debug(GG_DEBUG_MISC, "=> not enough memory (%d bytes for data_buf)\n", f->data_size + 1);
-+ free(f->header_buf);
-+ f->header_buf = NULL;
-+ GET_LOST(GG_FAILURE_READING);
-+ }
-+
-+ if (left) {
-+ if (left > f->data_size) {
-+ gg_debug(GG_DEBUG_MISC, "=> too much data (%d bytes left, %d needed)\n", left, f->data_size);
-+ free(f->header_buf);
-+ free(f->data_buf);
-+ f->header_buf = NULL;
-+ f->data_buf = NULL;
-+ GET_LOST(GG_FAILURE_READING);
-+ }
-+
-+ memcpy(f->data_buf, tmp + sep_len, left);
-+ f->data_buf[left] = 0;
-+ }
-+
-+ free(f->header_buf);
-+ f->header_buf = NULL;
-+ f->header_size = 0;
-+
-+ if (left && left == f->data_size) {
-+ gg_debug(GG_DEBUG_MISC, "=> wow, we already have all data\n");
-+ f->state = GG_STATE_PARSING;
-+ } else {
-+ f->state = GG_STATE_READING_DATA;
-+ f->check = GG_CHECK_READ;
-+ return 0;
-+ }
-+ } else
-+ return 0;
-+ }
-+
-+ if (f->state == GG_STATE_READING_DATA) {
-+ char buf[1024];
-+ int res;
-+
-+ if ((res = read(f->fd, buf, sizeof(buf))) == -1) {
-+ gg_debug(GG_DEBUG_MISC, "=> reading http data failed (errno=%d)\n", errno);
-+ if (f->data_buf) {
-+ free(f->data_buf);
-+ f->data_buf = NULL;
-+ }
-+ GET_LOST(GG_FAILURE_READING);
-+ }
-+
-+ gg_debug(GG_DEBUG_MISC, "=> read %d bytes of data\n", res);
-+
-+ if (strlen(f->data_buf) + res > f->data_size) {
-+ gg_debug(GG_DEBUG_MISC, "=> too much data (%d bytes, %d needed), truncating\n", strlen(f->data_buf) + res, f->data_size);
-+ res = f->data_size - strlen(f->data_buf);
-+ }
-+
-+ memcpy(f->data_buf + strlen(f->data_buf), buf, res);
-+
-+ gg_debug(GG_DEBUG_MISC, "=> strlen(data_buf)=%d, data_size=%d\n", strlen(f->data_buf), f->data_size);
-+
-+ if (strlen(f->data_buf) >= f->data_size) {
-+ gg_debug(GG_DEBUG_MISC, "=> okay, we've got all the data, closing socket\n");
-+ f->state = GG_STATE_PARSING;
-+ close(f->fd);
-+ f->fd = 0;
-+ } else
-+ return 0;
-+ }
-+
-+ if (f->state == GG_STATE_PARSING) {
-+ char *foo = f->data_buf;
-+
-+ gg_debug(GG_DEBUG_MISC, "=> ladies and gentlemen, parsing begins\n");
-+ f->count = 0;
-+ f->results = NULL;
-+ f->state = GG_STATE_FINISHED;
-+
-+ if (!gg_get_line(&foo))
-+ return 0;
-+
-+ while (1) {
-+ char *tmp[8];
-+ int i;
-+
-+ for (i = 0; i < 8; i++) {
-+ if (!(tmp[i] = gg_get_line(&foo))) {
-+ free(f->data_buf);
-+ f->data_buf = NULL;
-+ return 0;
-+ }
-+ gg_debug(GG_DEBUG_MISC, "=> [%s]\n", tmp[i]);
-+ }
-+
-+ if (!(f->results = realloc(f->results, (f->count + 1) * sizeof(struct gg_search_result)))) {
-+ gg_debug(GG_DEBUG_MISC, "=> not enough memory for results (non critical)\n");
-+ free(f->data_buf);
-+ f->data_buf = NULL;
-+ return 0;
-+ }
-+
-+ f->results[f->count].active = (atoi(tmp[0]) == 2);
-+ f->results[f->count].uin = (strtol(tmp[1], NULL, 0));
-+ f->results[f->count].first_name = strdup(tmp[2]);
-+ f->results[f->count].last_name = strdup(tmp[3]);
-+ f->results[f->count].nickname = strdup(tmp[4]);
-+ f->results[f->count].born = atoi(tmp[5]);
-+ f->results[f->count].gender = atoi(tmp[6]);
-+ f->results[f->count].city = strdup(tmp[7]);
-+
-+ f->count++;
-+ }
-+
-+ free(f->data_buf);
-+ f->data_buf = NULL;
-+
-+ f->state = GG_STATE_FINISHED;
-+
-+ return 0;
-+ }
-+
-+ if (f->fd)
-+ close(f->fd);
-+
-+ f->state = GG_STATE_IDLE;
-+ f->error = 0;
-+
-+ return -1;
-+}
-+
-+#undef GET_LOST
-+
-+/*
-+ * gg_search_cancel()
-+ *
-+ * je¶li szukanie jest w trakcie, przerywa.
-+ *
-+ * - f - to co¶, co zwróci³o gg_search().
-+ *
-+ * UWAGA! funkcja potencjalnie niebezpieczna, bo mo¿e pozwalniaæ bufory
-+ * i pozamykaæ sockety, kiedy co¶ siê dzieje. ale to ju¿ nie mój problem ;)
-+ */
-+void gg_search_cancel(struct gg_search *f)
-+{
-+ if (!f)
-+ return;
-+
-+ if (f->state == GG_STATE_IDLE || f->state == GG_STATE_FINISHED)
-+ return;
-+
-+ if (f->fd)
-+ close(f->fd);
-+
-+ if (f->header_buf)
-+ free(f->header_buf);
-+ if (f->data_buf)
-+ free(f->data_buf);
-+}
-+
-+/*
-+ * gg_free_search()
-+ *
-+ * zwalnia pamiêæ po efektach szukania userów.
-+ *
-+ * - f - to co¶, co nie jest ju¿ nam potrzebne.
-+ *
-+ * nie zwraca niczego. najwy¿ej segfaultnie ;)
-+ */
-+void gg_free_search(struct gg_search *f)
-+{
-+ int i;
-+
-+ if (!f)
-+ return;
-+
-+ for (i = 0; i < f->count; i++) {
-+ free(f->results[i].first_name);
-+ free(f->results[i].last_name);
-+ free(f->results[i].nickname);
-+ free(f->results[i].city);
-+ }
-+
-+ free(f->results);
-+ free(f->request.first_name);
-+ free(f->request.last_name);
-+ free(f->request.nickname);
-+ free(f->request.city);
-+ free(f->request.email);
-+ free(f->request.phone);
-+ free(f);
-+}
-+