changeset 2792:9123abd0db92

[gaim-migrate @ 2805] Arkadiusz Miskiewicz's updates to Gadu-Gadu committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Mon, 26 Nov 2001 21:22:56 +0000
parents 8f6365332a05
children 4cfd2c69dd16
files src/protocols/gg/gg.c src/protocols/gg/libgg.c src/protocols/gg/libgg.h
diffstat 3 files changed, 766 insertions(+), 444 deletions(-) [+]
line wrap: on
line diff
--- a/src/protocols/gg/gg.c	Mon Nov 26 20:39:54 2001 +0000
+++ b/src/protocols/gg/gg.c	Mon Nov 26 21:22:56 2001 +0000
@@ -1,8 +1,8 @@
 /*
  * gaim - Gadu-Gadu Protocol Plugin
- * $Id: gg.c 2804 2001-11-26 20:39:54Z warmenhoven $
+ * $Id: gg.c 2805 2001-11-26 21:22:56Z warmenhoven $
  *
- * Copyright (C) 2001, Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
+ * Copyright (C) 2001 Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -62,7 +62,9 @@
 
 #define AGG_GENDER_NONE -1
 
-#define AGG_PUBDIR_FORM "/appsvc/fmpubquery2.asp"
+#define AGG_PUBDIR_USERLIST_EXPORT_FORM "/appsvc/fmcontactsput.asp"
+#define AGG_PUBDIR_USERLIST_IMPORT_FORM "/appsvc/fmcontactsget.asp"
+#define AGG_PUBDIR_SEARCH_FORM "/appsvc/fmpubquery2.asp"
 #define AGG_PUBDIR_MAX_ENTRIES 200
 
 #define AGG_STATUS_AVAIL              _("Available")
@@ -73,16 +75,24 @@
 #define AGG_STATUS_INVISIBLE_FRIENDS  _("Invisible for friends only")
 #define AGG_STATUS_NOT_AVAIL          _("Unavailable")
 
+#define AGG_HTTP_NONE			0
+#define AGG_HTTP_SEARCH			1
+#define AGG_HTTP_USERLIST_IMPORT	2
+#define AGG_HTTP_USERLIST_EXPORT	3
+
 #define UC_NORMAL 2
 
 struct agg_data {
 	struct gg_session *sess;
 };
 
-struct agg_search {
+struct agg_http {
 	struct gaim_connection *gc;
-	gchar *search_data;
+	gchar *request;
+	gchar *form;
+	gchar *host;
 	int inpa;
+	int type;
 };
 
 static char *agg_name()
@@ -94,7 +104,7 @@
 {
 #ifdef HAVE_ICONV
 	gchar *result = NULL;
-	if (iconv_string(encdst, encsrc, locstr, locstr+strlen(locstr)+1, &result, NULL) >= 0)
+	if (iconv_string(encdst, encsrc, locstr, locstr + strlen(locstr) + 1, &result, NULL) >= 0)
 		return result;
 #endif
 	return g_strdup(locstr);
@@ -202,8 +212,7 @@
 		if ((data[i] >= 'a' && data[i] <= 'z')
 		    || (data[i] >= 'A' && data[i] <= 'Z')
 		    || (data[i] >= '0' && data[i] <= '9')
-		    || data[i] == '=' || data[i] == '&'
-		    || data[i] == '\n' || data[i] == '\r' || data[i] == '\t' || data[i] == '\014') {
+		    || data[i] == '=' || data[i] == '&') {
 			p = g_realloc(p, j + 1);
 			p[j] = data[i];
 			j++;
@@ -371,10 +380,11 @@
 			gchar *imsg;
 			gchar user[20];
 
-			g_snprintf(user, sizeof(user), "%u", e->event.msg.sender);
+			g_snprintf(user, sizeof(user), "%lu", e->event.msg.sender);
 			if (!allowed_uin(gc, user))
 				break;
 			imsg = charset_convert(e->event.msg.message, "CP1250", find_local_charset());
+			/* e->event.msg.time - we don't know what this time is for */
 			serv_got_im(gc, user, imsg, 0, time((time_t) NULL));
 			g_free(imsg);
 		}
@@ -401,7 +411,7 @@
 					break;
 				}
 
-				g_snprintf(user, sizeof(user), "%u", n->uin);
+				g_snprintf(user, sizeof(user), "%lu", n->uin);
 				serv_got_update(gc, user, (status == UC_UNAVAILABLE) ? 0 : 1, 0, 0, 0,
 						status, 0);
 				n++;
@@ -428,7 +438,7 @@
 				break;
 			}
 
-			g_snprintf(user, sizeof(user), "%u", e->event.status.uin);
+			g_snprintf(user, sizeof(user), "%lu", e->event.status.uin);
 			serv_got_update(gc, user, (status == UC_UNAVAILABLE) ? 0 : 1, 0, 0, 0, status,
 					0);
 		}
@@ -648,50 +658,13 @@
 	}
 }
 
-static void search_results(gpointer data, gint source, GaimInputCondition cond)
+static void search_results(struct gaim_connection *gc, gchar *webdata)
 {
-	struct agg_search *srch = data;
-	struct gaim_connection *gc = srch->gc;
+	gchar **webdata_tbl;
 	gchar *buf;
 	char *ptr;
-	char *webdata;
-	int len;
-	char read_data;
-	gchar **webdata_tbl;
 	int i, j;
 
-	if (!g_slist_find(connections, gc)) {
-		debug_printf("search_callback: g_slist_find error\n");
-		gaim_input_remove(srch->inpa);
-		g_free(srch);
-		close(source);
-		return;
-	}
-
-	webdata = NULL;
-	len = 0;
-
-	while (read(source, &read_data, 1) > 0 || errno == EWOULDBLOCK) {
-		if (errno == EWOULDBLOCK) {
-			errno = 0;
-			continue;
-		}
-
-		if (!read_data)
-			continue;
-
-		len++;
-		webdata = g_realloc(webdata, len);
-		webdata[len - 1] = read_data;
-	}
-
-	webdata = g_realloc(webdata, len + 1);
-	webdata[len] = 0;
-
-	gaim_input_remove(srch->inpa);
-	g_free(srch);
-	close(source);
-
 	if ((ptr = strstr(webdata, "query_results:")) == NULL || (ptr = strchr(ptr, '\n')) == NULL) {
 		debug_printf("search_callback: pubdir result [%s]\n", webdata);
 		g_free(webdata);
@@ -795,87 +768,294 @@
 	g_free(buf);
 }
 
-static void search_callback(gpointer data, gint source, GaimInputCondition cond)
+static void agg_import_buddies_results(struct gaim_connection *gc, gchar *webdata)
+{
+	gchar *ptr;
+	gchar **users_tbl;
+	int i;
+	if (strstr(webdata, "no_data:")) {
+		g_free(webdata);
+		do_error_dialog(_("There is no Buddy List stored on server. Sorry!"),
+				_("Gadu-Gadu Error"));
+		return;
+	}
+
+	if ((ptr = strstr(webdata, "get_results:")) == NULL || (ptr = strchr(ptr, ':')) == NULL) {
+		debug_printf("agg_import_buddies_list: import buddies result [%s]\n", webdata);
+		g_free(webdata);
+		do_error_dialog(_("Couldn't Import Buddies List from Server"), _("Gadu-Gadu Error"));
+		return;
+	}
+	ptr++;
+
+	users_tbl = g_strsplit(ptr, "\n", AGG_PUBDIR_MAX_ENTRIES);
+	g_free(webdata);
+
+	/* Parse array of Buddies List */
+	for (i = 0; users_tbl[i] != NULL; i++) {
+		gchar **data_tbl;
+		gchar *name, *show;
+
+		g_strdelimit(users_tbl[i], "\r\t\n\015", ' ');
+		data_tbl = g_strsplit(users_tbl[i], ";", 8);
+
+		show = data_tbl[3];
+		name = data_tbl[6];
+
+		if (invalid_uin(name)) {
+			continue;
+		}
+
+		debug_printf("uin: %s\n", name);
+		if (!find_buddy(gc, name)) {
+			/* Default group if none specified on server */
+			gchar *group = g_strdup("Gadu-Gadu");
+			if (strlen(data_tbl[5])) {
+				gchar **group_tbl = g_strsplit(data_tbl[5], ",", 2);
+				if (strlen(group_tbl[0])) {
+					g_free(group);
+					group = g_strdup(group_tbl[0]);
+				}
+				g_strfreev(group_tbl);
+			}
+			/* Add Buddy to our userlist */
+			add_buddy(gc, group, name, strlen(show) ? show : name);
+			do_export(gc);
+			g_free(group);
+		}
+		g_strfreev(data_tbl);
+	}
+	g_strfreev(users_tbl);
+}
+
+static void agg_export_buddies_results(struct gaim_connection *gc, gchar *webdata)
 {
-	struct agg_search *srch = data;
-	struct gaim_connection *gc = srch->gc;
-	gchar *search_data = srch->search_data;
+	if (strstr(webdata, "put_success:")) {
+		g_free(webdata);
+		do_error_dialog(_("Buddies List sucessfully transfered into Server"),
+				_("Gadu-Gadu Information"));
+		return;
+	}
+
+	debug_printf("agg_export_buddies_results: webdata [%s]\n", webdata);
+	g_free(webdata);
+	do_error_dialog(_("Couldn't transfer Buddies List into Server"), _("Gadu-Gadu Error"));
+}
+
+static void http_results(gpointer data, gint source, GaimInputCondition cond)
+{
+	struct agg_http *hdata = data;
+	struct gaim_connection *gc = hdata->gc;
+	char *webdata;
+	int len;
+	char read_data;
+
+	debug_printf("http_results: begin\n");
+
+	if (!g_slist_find(connections, gc)) {
+		debug_printf("search_callback: g_slist_find error\n");
+		gaim_input_remove(hdata->inpa);
+		g_free(hdata);
+		close(source);
+		return;
+	}
+
+	webdata = NULL;
+	len = 0;
+
+	while (read(source, &read_data, 1) > 0 || errno == EWOULDBLOCK) {
+		if (errno == EWOULDBLOCK) {
+			errno = 0;
+			continue;
+		}
+
+		if (!read_data)
+			continue;
+
+		len++;
+		webdata = g_realloc(webdata, len);
+		webdata[len - 1] = read_data;
+	}
+
+	webdata = g_realloc(webdata, len + 1);
+	webdata[len] = 0;
+
+	gaim_input_remove(hdata->inpa);
+	close(source);
+
+	debug_printf("http_results: type %d, webdata [%s]\n", hdata->type, webdata);
+
+	switch (hdata->type) {
+	case AGG_HTTP_SEARCH:
+		search_results(gc, webdata);
+		break;
+	case AGG_HTTP_USERLIST_IMPORT:
+		agg_import_buddies_results(gc, webdata);
+		break;
+	case AGG_HTTP_USERLIST_EXPORT:
+		agg_export_buddies_results(gc, webdata);
+		break;
+	case AGG_HTTP_NONE:
+	default:
+		debug_printf("http_results: unsupported type %d\n", hdata->type);
+		break;
+	}
+
+	g_free(hdata);
+}
+
+static void http_req_callback(gpointer data, gint source, GaimInputCondition cond)
+{
+	struct agg_http *hdata = data;
+	struct gaim_connection *gc = hdata->gc;
+	gchar *request = hdata->request;
 	gchar *buf;
 	char *ptr;
 
-	debug_printf("search_callback enter: begin\n");
+	debug_printf("http_req_callback: begin\n");
 
 	if (!g_slist_find(connections, gc)) {
-		debug_printf("search_callback: g_slist_find error\n");
-		g_free(search_data);
-		g_free(srch);
+		debug_printf("http_req_callback: g_slist_find error\n");
+		g_free(request);
+		g_free(hdata);
 		close(source);
 		return;
 	}
 
 	if (source == -1) {
-		g_free(search_data);
-		g_free(srch);
+		g_free(request);
+		g_free(hdata);
 		return;
 	}
 
-	ptr = encode_postdata(search_data);
-	g_free(search_data);
+	ptr = encode_postdata(request);
+	g_free(request);
 
-	debug_printf("search_callback: pubdir request [%s]\n", ptr);
+	debug_printf("http_req_callback: http request [%s]\n", ptr);
 
-	buf = g_strdup_printf("POST " AGG_PUBDIR_FORM " HTTP/1.0\r\n"
-			      "Host: " GG_PUBDIR_HOST "\r\n"
+	buf = g_strdup_printf("POST %s HTTP/1.0\r\n"
+			      "Host: %s\r\n"
 			      "Content-Type: application/x-www-form-urlencoded\r\n"
-			      "User-Agent: Mozilla/4.7 [en] (Win98; I)\r\n"
+			      "User-Agent: " GG_HTTP_USERAGENT "\r\n"
 			      "Content-Length: %d\r\n"
-			      "Pragma: no-cache\r\n" "\r\n" "%s\r\n", strlen(ptr), ptr);
+			      "Pragma: no-cache\r\n" "\r\n" "%s\r\n",
+			      hdata->form, hdata->host, strlen(ptr), ptr);
 
 	g_free(ptr);
 
 	if (write(source, buf, strlen(buf)) < strlen(buf)) {
 		g_free(buf);
-		g_free(srch);
+		g_free(hdata);
 		close(source);
-		do_error_dialog(_("Couldn't send search request"), _("Gadu-Gadu Error"));
+		do_error_dialog(_("Couldn't send http request"), _("Gadu-Gadu Error"));
 		return;
 	}
 
 	g_free(buf);
 
-	srch->inpa = gaim_input_add(source, GAIM_INPUT_READ, search_results, srch);
+	hdata->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_results, hdata);
+}
+
+static void agg_import_buddies(struct gaim_connection *gc)
+{
+	struct agg_http *hi = g_new0(struct agg_http, 1);
+	static char msg[AGG_BUF_LEN];
+
+	hi->gc = gc;
+	hi->type = AGG_HTTP_USERLIST_IMPORT;
+	hi->form = AGG_PUBDIR_USERLIST_IMPORT_FORM;
+	hi->host = GG_PUBDIR_HOST;
+	hi->request = g_strdup_printf("FmNum=%s&Pass=%s", gc->username, gc->password);
+
+	if (proxy_connect(GG_PUBDIR_HOST, GG_PUBDIR_PORT, http_req_callback, hi) < 0) {
+		g_snprintf(msg, sizeof(msg), _("Buddies List import from Server failed (%s)"),
+			   GG_PUBDIR_HOST);
+		do_error_dialog(msg, _("Gadu-Gadu Error"));
+		g_free(hi->request);
+		g_free(hi);
+		return;
+	}
+}
+
+static void agg_export_buddies(struct gaim_connection *gc)
+{
+	struct agg_http *he = g_new0(struct agg_http, 1);
+	static char msg[AGG_BUF_LEN];
+	gchar *ptr;
+	GSList *gr = gc->groups;
+
+	he->gc = gc;
+	he->type = AGG_HTTP_USERLIST_EXPORT;
+	he->form = AGG_PUBDIR_USERLIST_EXPORT_FORM;
+	he->host = GG_PUBDIR_HOST;
+	he->request = g_strdup_printf("FmNum=%s&Pass=%s&Contacts=", gc->username, gc->password);
+
+	while (gr) {
+		struct group *g = gr->data;
+		GSList *m = g->members;
+		while (m) {
+			struct buddy *b = m->data;
+			gchar *newdata;
+			/* GG Number */
+			gchar *name = b->name;
+			/* GG Pseudo */
+			gchar *show = strlen(b->show) ? b->show : b->name;
+
+			ptr = he->request;
+			newdata = g_strdup_printf("%s;%s;%s;%s;%s;%s;%s\r\n",
+						  show, show, show, show, "", g->name, name);
+			he->request = g_strconcat(ptr, newdata, NULL);
+			g_free(newdata);
+			g_free(ptr);
+
+			m = g_slist_next(m);
+		}
+		gr = g_slist_next(gr);
+	}
+
+	if (proxy_connect(GG_PUBDIR_HOST, GG_PUBDIR_PORT, http_req_callback, he) < 0) {
+		g_snprintf(msg, sizeof(msg), _("Buddies List export to Server failed (%s)"),
+			   GG_PUBDIR_HOST);
+		do_error_dialog(msg, _("Gadu-Gadu Error"));
+		g_free(he->request);
+		g_free(he);
+		return;
+	}
 }
 
 static void agg_dir_search(struct gaim_connection *gc, char *first, char *middle,
 			   char *last, char *maiden, char *city, char *state, char *country, char *email)
 {
-	struct agg_search *srch = g_new0(struct agg_search, 1);
+	struct agg_http *srch = g_new0(struct agg_http, 1);
 	static char msg[AGG_BUF_LEN];
 
 	srch->gc = gc;
+	srch->type = AGG_HTTP_SEARCH;
+	srch->form = AGG_PUBDIR_SEARCH_FORM;
+	srch->host = GG_PUBDIR_HOST;
 
 	if (email && strlen(email)) {
-		srch->search_data = g_strdup_printf("Mode=1&Email=%s", email);
+		srch->request = g_strdup_printf("Mode=1&Email=%s", email);
 	} else {
 		gchar *new_first = charset_convert(first, find_local_charset(), "CP1250");
 		gchar *new_last = charset_convert(last, find_local_charset(), "CP1250");
 		gchar *new_city = charset_convert(city, find_local_charset(), "CP1250");
 
 		/* For active only add &ActiveOnly= */
-		srch->search_data = g_strdup_printf("Mode=0&FirstName=%s&LastName=%s&Gender=%d"
-						    "&NickName=%s&City=%s&MinBirth=%d&MaxBirth=%d",
-						    new_first, new_last, AGG_GENDER_NONE,
-						    "", new_city, 0, 0);
+		srch->request = g_strdup_printf("Mode=0&FirstName=%s&LastName=%s&Gender=%d"
+						"&NickName=%s&City=%s&MinBirth=%d&MaxBirth=%d",
+						new_first, new_last, AGG_GENDER_NONE,
+						"", new_city, 0, 0);
 
 		g_free(new_first);
 		g_free(new_last);
 		g_free(new_city);
 	}
 
-	if (proxy_connect(GG_PUBDIR_HOST, GG_PUBDIR_PORT, search_callback, srch) < 0) {
+	if (proxy_connect(GG_PUBDIR_HOST, GG_PUBDIR_PORT, http_req_callback, srch) < 0) {
 		g_snprintf(msg, sizeof(msg), _("Connect to search service failed (%s)"), GG_PUBDIR_HOST);
 		do_error_dialog(msg, _("Gadu-Gadu Error"));
-		g_free(srch->search_data);
+		g_free(srch->request);
 		g_free(srch);
 		return;
 	}
@@ -885,6 +1065,10 @@
 {
 	if (!strcmp(action, _("Directory Search"))) {
 		show_find_info(gc);
+	} else if (!strcmp(action, _("Import Buddies List from Server"))) {
+		agg_import_buddies(gc);
+	} else if (!strcmp(action, _("Export Buddies List to Server"))) {
+		agg_export_buddies(gc);
 	}
 }
 
@@ -893,33 +1077,38 @@
 	GList *m = NULL;
 
 	m = g_list_append(m, _("Directory Search"));
+	m = g_list_append(m, _("Import Buddies List from Server"));
+	m = g_list_append(m, _("Export Buddies List to Server"));
 
 	return m;
 }
 
 static void agg_get_info(struct gaim_connection *gc, char *who)
 {
-	struct agg_search *srch = g_new0(struct agg_search, 1);
+	struct agg_http *srch = g_new0(struct agg_http, 1);
 	static char msg[AGG_BUF_LEN];
 
 	srch->gc = gc;
+	srch->type = AGG_HTTP_SEARCH;
+	srch->form = AGG_PUBDIR_SEARCH_FORM;
+	srch->host = GG_PUBDIR_HOST;
 
 	/* If it's invalid uin then maybe it's nickname? */
 	if (invalid_uin(who)) {
 		gchar *new_who = charset_convert(who, find_local_charset(), "CP1250");
 
-		srch->search_data = g_strdup_printf("Mode=0&FirstName=%s&LastName=%s&Gender=%d"
-						    "&NickName=%s&City=%s&MinBirth=%d&MaxBirth=%d",
-						    "", "", AGG_GENDER_NONE, new_who, "", 0, 0);
+		srch->request = g_strdup_printf("Mode=0&FirstName=%s&LastName=%s&Gender=%d"
+						"&NickName=%s&City=%s&MinBirth=%d&MaxBirth=%d",
+						"", "", AGG_GENDER_NONE, new_who, "", 0, 0);
 
 		g_free(new_who);
 	} else
-		srch->search_data = g_strdup_printf("Mode=3&UserId=%s", who);
+		srch->request = g_strdup_printf("Mode=3&UserId=%s", who);
 
-	if (proxy_connect(GG_PUBDIR_HOST, GG_PUBDIR_PORT, search_callback, srch) < 0) {
+	if (proxy_connect(GG_PUBDIR_HOST, GG_PUBDIR_PORT, http_req_callback, srch) < 0) {
 		g_snprintf(msg, sizeof(msg), _("Connect to search service failed (%s)"), GG_PUBDIR_HOST);
 		do_error_dialog(msg, _("Gadu-Gadu Error"));
-		g_free(srch->search_data);
+		g_free(srch->request);
 		g_free(srch);
 		return;
 	}
--- a/src/protocols/gg/libgg.c	Mon Nov 26 20:39:54 2001 +0000
+++ b/src/protocols/gg/libgg.c	Mon Nov 26 21:22:56 2001 +0000
@@ -1,7 +1,8 @@
-/* $Id: libgg.c 2608 2001-10-24 08:35:55Z warmenhoven $ */
+/* $Id: libgg.c 2805 2001-11-26 21:22:56Z warmenhoven $ */
 
 /*
- *  (C) Copyright 2001 Wojtek Kaniewski <wojtekka@irc.pl>
+ *  (C) Copyright 2001 Wojtek Kaniewski <wojtekka@irc.pl>,
+ *                     Robert J. Woźny <speedy@atman.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
@@ -26,22 +27,39 @@
 #include <arpa/inet.h>
 #include <sys/ioctl.h>
 #include <sys/wait.h>
-#include <sys/types.h>
-#include <fcntl.h>
+#include <sys/time.h>
 #include <netdb.h>
 #include <errno.h>
-#include <string.h>
+#ifndef _AIX
+#  include <string.h>
+#endif
 #include <stdarg.h>
 #include <pwd.h>
+#include <time.h>
+#ifdef sun
+#include <sys/filio.h>
+#endif
 #include <glib.h>
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+#  define WORDS_BIGENDIAN 1
+#endif
 #include "libgg.h"
+#include "config.h"
 
 int gg_debug_level = 0;
 
-#ifdef GG_DEBUG
+#ifndef lint
+
+static char rcsid[]
+#ifdef __GNUC__
+    __attribute__ ((unused))
+#endif
+    = "$Id: libgg.c 2805 2001-11-26 21:22:56Z warmenhoven $";
+
+#endif
 
 /*
- * gg_debug_real()
+ * gg_debug()
  *
  * wyrzuca komunikat o danym poziomie, o ile użytkownik sobie tego życzy.
  *
@@ -50,10 +68,10 @@
  *
  * niczego nie zwraca.
  */
-void gg_debug_real(int level, char *format, ...)
+void gg_debug(int level, char *format, ...)
 {
 	va_list ap;
-	
+
 	if ((gg_debug_level & level)) {
 		va_start(ap, format);
 		vprintf(format, ap);
@@ -61,8 +79,6 @@
 	}
 }
 
-#endif /* GG_DEBUG */
-
 /*
  * fix32() // funkcja wewnętrzna
  *
@@ -70,15 +86,14 @@
  */
 static inline unsigned long fix32(unsigned long x)
 {
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#ifndef WORDS_BIGENDIAN
 	return x;
 #else
 	return (unsigned long)
-		(((x & (unsigned long) 0x000000ffU) << 24) |
-                 ((x & (unsigned long) 0x0000ff00U) << 8) |
-                 ((x & (unsigned long) 0x00ff0000U) >> 8) |
-                 ((x & (unsigned long) 0xff000000U) >> 24));
-#endif		
+	    (((x & (unsigned long)0x000000ffU) << 24) |
+	     ((x & (unsigned long)0x0000ff00U) << 8) |
+	     ((x & (unsigned long)0x00ff0000U) >> 8) | ((x & (unsigned long)0xff000000U) >> 24));
+#endif
 }
 
 /*
@@ -88,12 +103,11 @@
  */
 static inline unsigned short fix16(unsigned short x)
 {
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#ifndef WORDS_BIGENDIAN
 	return x;
 #else
 	return (unsigned short)
-		(((x & (unsigned short) 0x00ffU) << 8) |
-                 ((x & (unsigned short) 0xff00U) >> 8));
+	    (((x & (unsigned short)0x00ffU) << 8) | ((x & (unsigned short)0xff00U) >> 8));
 #endif
 }
 
@@ -101,8 +115,8 @@
  * 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 ;)
+ * miejsce na dane. powinno działać na tych maszynach, które mają funkcję
+ * vsnprintf() zgodną z C99, jak i na wcześniejszych.
  *
  *  - format, ... - parametry takie same jak w innych funkcjach *printf()
  *
@@ -112,16 +126,26 @@
 char *gg_alloc_sprintf(char *format, ...)
 {
 	va_list ap;
-	char *buf = NULL;
-	int size;
+	char *buf = NULL, *tmp;
+	int size = 0, res;
 
 	va_start(ap, format);
 
-	if ((size = vsnprintf(buf, 0, format, ap)) < 0)
-		return NULL;
-
-	if (!(buf = malloc(size + 1)))
-		return NULL;
+	if ((size = vsnprintf(buf, 0, format, ap)) < 1) {
+		size = 128;
+		do {
+			size *= 2;
+			if (!(tmp = realloc(buf, size))) {
+				free(buf);
+				return NULL;
+			}
+			buf = tmp;
+			res = vsnprintf(buf, size, format, ap);
+		} while (res == size - 1);
+	} else {
+		if (!(buf = malloc(size + 1)))
+			return NULL;
+	}
 
 	vsnprintf(buf, size + 1, format, ap);
 
@@ -131,6 +155,40 @@
 }
 
 /*
+ * 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.
+ */
+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_resolve() // funkcja wewnętrzna
  *
  * tworzy pipe'y, forkuje się i w drugim procesie zaczyna resolvować 
@@ -150,7 +208,7 @@
 	struct in_addr a;
 
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(..., \"%s\");\n", hostname);
-	
+
 	if (!fd | !pid) {
 		errno = EFAULT;
 		return -1;
@@ -165,11 +223,11 @@
 	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));
+				memcpy((char *)&a, he->h_addr, sizeof(a));
 		}
 
 		write(pipes[1], &a, sizeof(a));
@@ -205,21 +263,16 @@
 	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) {
-#ifdef FIONBIO
 		if (ioctl(sock, FIONBIO, &one) == -1) {
-#else
-		int flags = fcntl (sock, F_GETFL);
-		if (flags < 0 ||
-			fcntl (sock, F_SETFL, flags | O_NONBLOCK) < 0) {
-#endif
-			gg_debug(GG_DEBUG_MISC, "-- ioctl() failed. errno = %d (%s)\n", errno, strerror(errno));
+			gg_debug(GG_DEBUG_MISC, "-- ioctl() failed. errno = %d (%s)\n", errno,
+				 strerror(errno));
 			return -1;
 		}
 	}
@@ -227,13 +280,16 @@
 	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))
+
+	if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
+		if (errno && (!async || errno != EINPROGRESS)) {
+			gg_debug(GG_DEBUG_MISC, "-- connect() failed. errno = %d (%s)\n", errno,
+				 strerror(errno));
 			return -1;
+		}
+		gg_debug(GG_DEBUG_MISC, "-- connect() in progress\n");
 	}
-	
+
 	return sock;
 }
 
@@ -253,7 +309,7 @@
 	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) {
@@ -289,7 +345,7 @@
 	int ret = 0, offset, size = 0;
 
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(...);\n");
-	
+
 	if (!sess) {
 		errno = EFAULT;
 		return NULL;
@@ -301,7 +357,8 @@
 			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));
+					gg_debug(GG_DEBUG_MISC, "-- errno = %d (%s)\n", errno,
+						 strerror(errno));
 					return NULL;
 				}
 			}
@@ -343,7 +400,7 @@
 		if (ret > -1 && ret <= size) {
 			offset += ret;
 			size -= ret;
-		} else if (ret == -1) {	
+		} 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);
@@ -353,7 +410,7 @@
 				return NULL;
 			}
 			if (errno != EINTR) {
-				/* errno = EINVAL; */
+//                              errno = EINVAL;
 				free(buf);
 				return NULL;
 			}
@@ -362,16 +419,14 @@
 
 	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]);
+		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;
 }
@@ -392,14 +447,16 @@
  * 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)
+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);
-	
+	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;
@@ -411,7 +468,7 @@
 		return -1;
 	}
 
-	h = (struct gg_header*) tmp;
+	h = (struct gg_header *)tmp;
 	h->type = fix32(type);
 	h->length = fix32(length + payload_length);
 
@@ -420,26 +477,25 @@
 	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, " %.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));
+		gg_debug(GG_DEBUG_MISC, "-- write() failed. res = %d, errno = %d (%s)\n", res, errno,
+			 strerror(errno));
 		free(tmp);
 		return -1;
 	}
 
-	free(tmp);	
+	free(tmp);
 	return 0;
 }
 
@@ -479,6 +535,9 @@
 	sess->async = async;
 	sess->seq = 0;
 	sess->recv_left = 0;
+	sess->last_pong = 0;
+	sess->server_ip = 0;
+	sess->initial_status = 0;
 
 	if (async) {
 		if (gg_resolve(&sess->fd, &sess->pid, GG_APPMSG_HOST)) {
@@ -491,13 +550,13 @@
 
 		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));
+				memcpy((char *)&a, he->h_addr, sizeof(a));
 		}
 
 		if (!(sess->fd = gg_connect(&a, GG_APPMSG_PORT, 0)) == -1) {
@@ -518,6 +577,7 @@
 			}
 
 			if (e->type == GG_EVENT_CONN_FAILED) {
+				errno = EACCES;
 				gg_debug(GG_DEBUG_MISC, "-- could not login\n");
 				gg_free_event(e);
 				free(sess);
@@ -598,7 +658,7 @@
 
 	if (sess->state == GG_STATE_CONNECTED)
 		gg_change_status(sess, GG_STATUS_NOT_AVAIL);
-	
+
 	if (sess->fd) {
 		shutdown(sess->fd, 2);
 		close(sess->fd);
@@ -626,7 +686,7 @@
 		errno = EFAULT;
 		return -1;
 	}
-	
+
 	if (sess->state != GG_STATE_CONNECTED) {
 		errno = ENOTCONN;
 		return -1;
@@ -640,7 +700,7 @@
 	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;
 
@@ -704,7 +764,7 @@
  *
  * 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)
+int gg_notify(struct gg_session *sess, uin_t * userlist, int count)
 {
 	struct gg_notify *n;
 	uin_t *u;
@@ -714,25 +774,25 @@
 		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)))
+
+	if (!(n = (struct gg_notify *)malloc(sizeof(*n) * count)))
 		return -1;
-	
-	for (u = userlist, i = 0; i < count; u++, i++) { 
+
+	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;
 
@@ -764,12 +824,12 @@
 		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);
 }
 
@@ -798,10 +858,10 @@
 	}
 
 	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);
 }
 
@@ -827,12 +887,13 @@
 	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));
+		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);
-	
+	p = (void *)h + sizeof(struct gg_header);
+
 	if (h->type == GG_RECV_MSG) {
 		struct gg_recv_msg *r = p;
 
@@ -842,7 +903,8 @@
 			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));
+			e->event.msg.message = strdup((char *)r + sizeof(*r));
+			e->event.msg.time = fix32(r->time);
 		}
 	}
 
@@ -851,9 +913,9 @@
 		int count, i;
 
 		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)))) {
+		if (!(e->event.notify = (void *)malloc(h->length + 2 * sizeof(*n)))) {
 			gg_debug(GG_DEBUG_MISC, "-- not enough memory\n");
 			free(h);
 			return -1;
@@ -893,6 +955,12 @@
 		}
 	}
 
+	if (h->type == GG_PONG) {
+		gg_debug(GG_DEBUG_MISC, "-- received a pong\n");
+
+		sess->last_pong = time(NULL);
+	}
+
 	free(h);
 
 	return 0;
@@ -944,7 +1012,7 @@
 
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(...);\n");
 
-	if (!(e = (void*) malloc(sizeof(*e)))) {
+	if (!(e = (void *)malloc(sizeof(*e)))) {
 		gg_debug(GG_DEBUG_MISC, "-- not enough memory\n");
 		return NULL;
 	}
@@ -952,15 +1020,16 @@
 	e->type = GG_EVENT_NONE;
 
 	switch (sess->state) {
-		case GG_STATE_RESOLVING:
+	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");				
+				gg_debug(GG_DEBUG_MISC, "-- resolving failed\n");
 
+				errno = ENOENT;
 				e->type = GG_EVENT_CONN_FAILED;
 				e->event.failure = GG_FAILURE_RESOLVING;
 				sess->state = GG_STATE_IDLE;
@@ -970,6 +1039,8 @@
 				break;
 			}
 
+			sess->server_ip = a.s_addr;
+
 			close(sess->fd);
 
 			waitpid(sess->pid, NULL, 0);
@@ -977,50 +1048,83 @@
 			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");
+				struct in_addr *addr = (struct in_addr *)&sess->server_ip;
+
+				gg_debug(GG_DEBUG_MISC,
+					 "-- connection failed, trying direct connection\n");
 
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_CONNECTING;
-				sess->state = GG_STATE_IDLE;
-				break;
+				if ((sess->fd = gg_connect(addr, GG_DEFAULT_PORT, sess->async)) == -1) {
+					gg_debug(GG_DEBUG_MISC,
+						 "-- connection failed, trying https connection\n");
+					if ((sess->fd =
+					     gg_connect(&a, GG_HTTPS_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;
+			} else {
+				sess->state = GG_STATE_CONNECTING_HTTP;
+				sess->check = GG_CHECK_WRITE;
 			}
 
-			sess->state = GG_STATE_CONNECTING_HTTP;
-			sess->check = GG_CHECK_WRITE;
-
 			break;
 		}
 
-		case GG_STATE_CONNECTING_HTTP:
+	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));
+			if (sess->async
+			    && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+				struct in_addr *addr = (struct in_addr *)&sess->server_ip;
+				gg_debug(GG_DEBUG_MISC,
+					 "-- http connection failed, errno = %d (%s), trying direct connection\n",
+					 res, strerror(res));
 
-				errno = res;
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_CONNECTING;
-				sess->state = GG_STATE_IDLE;
+				if ((sess->fd = gg_connect(addr, GG_DEFAULT_PORT, sess->async)) == -1) {
+					gg_debug(GG_DEBUG_MISC,
+						 "-- connection failed, trying https connection\n");
+					if ((sess->fd =
+					     gg_connect(addr, GG_HTTPS_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;
 			}
 
 			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);
+				 "GET /appsvc/appmsg.asp?fmnumber=%lu HTTP/1.0\r\n"
+				 "Host: " GG_APPMSG_HOST "\r\n"
+				 "User-Agent: " GG_HTTP_USERAGENT "\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");
 
+				errno = EIO;
 				e->type = GG_EVENT_CONN_FAILED;
 				e->event.failure = GG_FAILURE_WRITING;
 				sess->state = GG_STATE_IDLE;
@@ -1033,7 +1137,7 @@
 			break;
 		}
 
-		case GG_STATE_WRITING_HTTP:
+	case GG_STATE_WRITING_HTTP:
 		{
 			char buf[1024], *tmp, *host;
 			int port = GG_DEFAULT_PORT;
@@ -1043,26 +1147,27 @@
 
 			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");
 
+				errno = EINVAL;
 				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;
@@ -1085,53 +1190,68 @@
 
 			if ((tmp = strchr(host, ':'))) {
 				*tmp = 0;
-				port = atoi(tmp+1);
+				port = atoi(tmp + 1);
 			}
 
 			a.s_addr = inet_addr(host);
+			sess->server_ip = a.s_addr;
 
 			if ((sess->fd = gg_connect(&a, port, sess->async)) == -1) {
-				gg_debug(GG_DEBUG_MISC, "-- connect() failed. errno = %d (%s)\n", errno, strerror(errno));
+				gg_debug(GG_DEBUG_MISC,
+					 "-- connection failed, trying https connection\n");
+				if ((sess->fd = gg_connect(&a, GG_HTTPS_PORT, sess->async)) == -1) {
+					gg_debug(GG_DEBUG_MISC,
+						 "-- connection 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;
+					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:
+	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));
+			if (sess->async
+			    && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+				struct in_addr *addr = (struct in_addr *)&sess->server_ip;
 
-				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,
+					 "-- connection failed, trying https connection\n");
+				if ((sess->fd = gg_connect(addr, GG_HTTPS_PORT, sess->async)) == -1) {
+					gg_debug(GG_DEBUG_MISC,
+						 "-- connection 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;
+				}
 			}
 
 			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:
+	case GG_STATE_WAITING_FOR_KEY:
 		{
-			struct gg_header *h;			
+			struct gg_header *h;
 			struct gg_welcome *w;
 			struct gg_login l;
 			unsigned int hash;
@@ -1140,7 +1260,8 @@
 			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));
+				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;
@@ -1148,43 +1269,46 @@
 				close(sess->fd);
 				break;
 			}
-	
+
 			if (h->type != GG_WELCOME) {
 				gg_debug(GG_DEBUG_MISC, "-- invalid packet received\n");
 
 				free(h);
 				close(sess->fd);
+				errno = EINVAL;
 				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 = (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);
-	
+
+			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.status = fix32(sess->initial_status ? sess->initial_status : 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));
+				gg_debug(GG_DEBUG_TRAFFIC, "-- oops, failed. errno = %d (%s)\n", errno,
+					 strerror(errno));
 
 				close(sess->fd);
 				e->type = GG_EVENT_CONN_FAILED;
@@ -1192,13 +1316,13 @@
 				sess->state = GG_STATE_IDLE;
 				break;
 			}
-	
+
 			sess->state = GG_STATE_SENDING_KEY;
 
 			break;
 		}
 
-		case GG_STATE_SENDING_KEY:
+	case GG_STATE_SENDING_KEY:
 		{
 			struct gg_header *h;
 
@@ -1212,11 +1336,12 @@
 				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;
+				free(h);
 				break;
 			}
 
@@ -1227,24 +1352,28 @@
 			} else {
 				gg_debug(GG_DEBUG_MISC, "-- invalid packet\n");
 				e->event.failure = GG_FAILURE_INVALID;
+				errno = EINVAL;
 			}
 
 			e->type = GG_EVENT_CONN_FAILED;
 			sess->state = GG_STATE_IDLE;
 			close(sess->fd);
+			free(h);
 
 			break;
 		}
 
-		case GG_STATE_CONNECTED:
+	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));
+				gg_debug(GG_DEBUG_MISC,
+					 "-- watch_fd_connected failed. errno = %d (%s)\n", errno,
+					 strerror(errno));
 
- 				if (errno == EAGAIN) {
+				if (errno == EAGAIN) {
 					e->type = GG_EVENT_NONE;
 					res = 0;
 				} else
@@ -1261,5 +1390,3 @@
 
 	return e;
 }
-
-
--- a/src/protocols/gg/libgg.h	Mon Nov 26 20:39:54 2001 +0000
+++ b/src/protocols/gg/libgg.h	Mon Nov 26 21:22:56 2001 +0000
@@ -1,7 +1,8 @@
-/* $Id: libgg.h 2523 2001-10-15 19:18:19Z warmenhoven $ */
+/* $Id: libgg.h 2805 2001-11-26 21:22:56Z warmenhoven $ */
 
 /*
- *  (C) Copyright 2001 Wojtek Kaniewski <wojtekka@irc.pl>
+ *  (C) Copyright 2001 Wojtek Kaniewski <wojtekka@irc.pl>,
+ *                     Robert J. Woźny <speedy@atman.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
@@ -24,201 +25,201 @@
 extern "C" {
 #endif
 
-#define GG_DEBUG 1
-
-#ifndef INADDR_NONE
+#if defined(sun) && !defined(INADDR_NONE)
 #define INADDR_NONE 0xffffffff
 #endif
 
+#include <sys/types.h>
+
 /*
  * typ zmiennej określającej numerek danej osoby.
  */
-typedef unsigned int uin_t;
+	typedef unsigned long uin_t;
 
 /*
  * cośtam.
  */
-struct gg_session {
-	int state, check;
-	int fd, pid;
-	int port;
-	int seq, async;
+	struct gg_session {
+		int state, check;
+		int fd, pid;
+		int port;
+		int seq, async;
+		int last_pong;
 
-	uin_t uin;
-	char *password;
+		/* powinno być ,,in_addr'', ale nie chcę inkludować sieci tutaj */
+		unsigned long server_ip;
 
-	char *recv_buf;
-	int recv_done, recv_left;
-};
+		uin_t uin;
+		char *password;
+		int initial_status;
+
+		char *recv_buf;
+		int recv_done, recv_left;
+	};
 
 /*
  * różne stany asynchronicznej maszynki.
  */
-enum {
-	GG_STATE_IDLE = 0,		/* wspólne */
-	GG_STATE_RESOLVING,
-	GG_STATE_CONNECTING_HTTP,
+	enum {
+		GG_STATE_IDLE = 0,	/* wspólne */
+		GG_STATE_RESOLVING,
+		GG_STATE_CONNECTING_HTTP,
 
-	GG_STATE_WRITING_HTTP,		/* gg_login */	
-	GG_STATE_CONNECTING_GG,
-	GG_STATE_WAITING_FOR_KEY,
-	GG_STATE_SENDING_KEY,
-	GG_STATE_CONNECTED,
+		GG_STATE_WRITING_HTTP,	/* gg_login */
+		GG_STATE_CONNECTING_GG,
+		GG_STATE_WAITING_FOR_KEY,
+		GG_STATE_SENDING_KEY,
+		GG_STATE_CONNECTED,
 
-	GG_STATE_READING_HEADER,	/* gg_search */
-	GG_STATE_READING_DATA,
-	GG_STATE_PARSING,
-	GG_STATE_FINISHED,
-};
+		GG_STATE_READING_HEADER,	/* gg_search */
+		GG_STATE_READING_DATA,
+		GG_STATE_PARSING,
+		GG_STATE_FINISHED,
+	};
 
 /*
  * co proces klienta powinien sprawdzać w deskryptorach?
  */
-enum {
-	GG_CHECK_NONE = 0,
-	GG_CHECK_WRITE = 1,
-	GG_CHECK_READ = 2,
-};
+	enum {
+		GG_CHECK_NONE = 0,
+		GG_CHECK_WRITE = 1,
+		GG_CHECK_READ = 2,
+	};
 
-struct gg_session *gg_login(uin_t uin, char *password, int async);
-void gg_free_session(struct gg_session *sess);
-void gg_logoff(struct gg_session *sess);
-int gg_change_status(struct gg_session *sess, int status);
-int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, unsigned char *message);
-int gg_ping(struct gg_session *sess);
+	struct gg_session *gg_login(uin_t uin, char *password, int async);
+	void gg_free_session(struct gg_session *sess);
+	void gg_logoff(struct gg_session *sess);
+	int gg_change_status(struct gg_session *sess, int status);
+	int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient,
+			    unsigned char *message);
+	int gg_ping(struct gg_session *sess);
 
-struct gg_notify_reply {
-	uin_t uin;		/* numerek */
-	int status;		/* status danej osoby */
-	int remote_ip;		/* adres ip delikwenta */
-	short remote_port;	/* port, na którym słucha klient */
-	int dunno1;		/* == 0x0b */
-	short dunno2;		/* znowu port? */
-} __attribute__ ((packed));
+	struct gg_notify_reply {
+		uin_t uin;	/* numerek */
+		unsigned long status;	/* status danej osoby */
+		unsigned long remote_ip;	/* adres ip delikwenta */
+		unsigned short remote_port;	/* port, na którym słucha klient */
+		unsigned long dunno1;	/* == 0x0b */
+		unsigned short dunno2;	/* znowu port? */
+	} __attribute__ ((packed));
 
-struct gg_status {
-	uin_t uin;		/* numerek */
-	int status;		/* nowy stan */
-} __attribute__ ((packed));
+	struct gg_status {
+		uin_t uin;	/* numerek */
+		unsigned long status;	/* nowy stan */
+	} __attribute__ ((packed));
 
-enum {
-	GG_EVENT_NONE = 0,
-	GG_EVENT_MSG,
-	GG_EVENT_NOTIFY,
-	GG_EVENT_STATUS,
-	GG_EVENT_ACK,
-	GG_EVENT_CONN_FAILED,
-	GG_EVENT_CONN_SUCCESS,
-};
+	enum {
+		GG_EVENT_NONE = 0,
+		GG_EVENT_MSG,
+		GG_EVENT_NOTIFY,
+		GG_EVENT_STATUS,
+		GG_EVENT_ACK,
+		GG_EVENT_CONN_FAILED,
+		GG_EVENT_CONN_SUCCESS,
+	};
 
-enum {
-	GG_FAILURE_RESOLVING = 1,
-	GG_FAILURE_CONNECTING,
-	GG_FAILURE_INVALID,
-	GG_FAILURE_READING,
-	GG_FAILURE_WRITING,
-	GG_FAILURE_PASSWORD,
-};
+	enum {
+		GG_FAILURE_RESOLVING = 1,
+		GG_FAILURE_CONNECTING,
+		GG_FAILURE_INVALID,
+		GG_FAILURE_READING,
+		GG_FAILURE_WRITING,
+		GG_FAILURE_PASSWORD,
+	};
 
-struct gg_event {
-        int type;
-        union {
-                struct {
-                        uin_t sender;
-			int msgclass;
-                        unsigned char *message;
-                } msg;
-                struct gg_notify_reply *notify;
-                struct gg_status status;
-                struct {
-                        uin_t recipient;
-                        int status;
-                        int seq;
-                } ack;
-		int failure;
-        } event;
-};
+	struct gg_event {
+		int type;
+		union {
+			struct {
+				uin_t sender;
+				int msgclass;
+				time_t time;
+				unsigned char *message;
+			} msg;
+			struct gg_notify_reply *notify;
+			struct gg_status status;
+			struct {
+				uin_t recipient;
+				int status;
+				int seq;
+			} ack;
+			int failure;
+		} event;
+	};
 
-struct gg_event *gg_watch_fd(struct gg_session *sess);
-void gg_free_event(struct gg_event *e);
+	struct gg_event *gg_watch_fd(struct gg_session *sess);
+	void gg_free_event(struct gg_event *e);
 
-int gg_notify(struct gg_session *sess, uin_t *userlist, int count);
-int gg_add_notify(struct gg_session *sess, uin_t uin);
-int gg_remove_notify(struct gg_session *sess, uin_t uin);
+	int gg_notify(struct gg_session *sess, uin_t * userlist, int count);
+	int gg_add_notify(struct gg_session *sess, uin_t uin);
+	int gg_remove_notify(struct gg_session *sess, uin_t uin);
 
 /*
  * jakieśtam bzdurki dotyczące szukania userów.
  */
 
-struct gg_search_result {
-	uin_t uin;
-	char *first_name;
-	char *last_name;
-	char *nickname;
-	int born;
-	int gender;
-	char *city;
-	int active;
-};
+	struct gg_search_result {
+		uin_t uin;
+		char *first_name;
+		char *last_name;
+		char *nickname;
+		int born;
+		int gender;
+		char *city;
+		int active;
+	};
 
-struct gg_search_request {
-	/* czy ma szukać tylko aktywnych? */
-	int active;
-	/* mode 0 */
-	char *nickname, *first_name, *last_name, *city;
-	int gender, min_birth, max_birth;
-	/* mode 1 */
-	char *email;
-	/* mode 2 */
-	char *phone;
-	/* mode 3 */
-	uin_t uin;
-};
+	struct gg_search_request {
+		/* czy ma szukać tylko aktywnych? */
+		int active;
+		/* mode 0 */
+		char *nickname, *first_name, *last_name, *city;
+		int gender, min_birth, max_birth;
+		/* mode 1 */
+		char *email;
+		/* mode 2 */
+		char *phone;
+		/* mode 3 */
+		uin_t uin;
+	};
 
-struct gg_search {
-	struct gg_search_request request;
+	struct gg_search {
+		struct gg_search_request request;
 
-	/* bzdurki */
-	int mode, fd, async, state, check, error, pid;
-	char *header_buf, *data_buf;
-	int header_size, data_size;
+		/* bzdurki */
+		int mode, fd, async, state, check, error, pid;
+		char *header_buf, *data_buf;
+		int header_size, data_size;
 
-	/* wyniki */
-	int count;
-	struct gg_search_result *results;
-};
+		/* wyniki */
+		int count;
+		struct gg_search_result *results;
+	};
 
 #define GG_GENDER_NONE 0
 #define GG_GENDER_FEMALE 1
 #define GG_GENDER_MALE 2
 
-struct gg_search *gg_search(struct gg_search_request *r, int async);
-int gg_search_watch_fd(struct gg_search *f);
-void gg_free_search(struct gg_search *f);
-void gg_search_cancel(struct gg_search *f);
+	struct gg_search *gg_search(struct gg_search_request *r, int async);
+	int gg_search_watch_fd(struct gg_search *f);
+	void gg_free_search(struct gg_search *f);
+	void gg_search_cancel(struct gg_search *f);
 
 /*
- * jeśli chcemy sobie podebugować, wystarczy zdefiniować GG_DEBUG.
+ * jeśli chcemy sobie podebugować, wystarczy ustawić `gg_debug_level'.
+ * niestety w miarę przybywania wpisów `gg_debug(...)' nie chciało mi
+ * się ustawiać odpowiednich leveli, więc większość szła do _MISC.
  */
 
-extern int gg_debug_level;
-
-#ifdef GG_DEBUG
+	extern int gg_debug_level;
 
-#	define GG_DEBUG_NET 1
-#	define GG_DEBUG_TRAFFIC 2
-#	define GG_DEBUG_DUMP 4
-#	define GG_DEBUG_FUNCTION 8
-#	define GG_DEBUG_MISC 16
+#define GG_DEBUG_NET 1
+#define GG_DEBUG_TRAFFIC 2
+#define GG_DEBUG_DUMP 4
+#define GG_DEBUG_FUNCTION 8
+#define GG_DEBUG_MISC 16
 
-	void gg_debug_real(int level, char *format, ...);
-#	define gg_debug(x, y...) gg_debug_real(x, y)
-
-#else
-
-#	define gg_debug(x, y...) while(0) { };
-
-#endif /* GG_DEBUG */
+	void gg_debug(int level, char *format, ...);
 
 /*
  * -------------------------------------------------------------------------
@@ -229,37 +230,41 @@
  * -------------------------------------------------------------------------
  */
 
-int gg_resolve(int *fd, int *pid, char *hostname);
-int gg_connect(void *addr, int port, int async);
-char *gg_alloc_sprintf(char *format, ...);
+	int gg_resolve(int *fd, int *pid, char *hostname);
+	int gg_connect(void *addr, int port, int async);
+	char *gg_alloc_sprintf(char *format, ...);
+	char *gg_get_line(char **ptr);
+	char *gg_urlencode(char *str);
 
 #define GG_APPMSG_HOST "appmsg.gadu-gadu.pl"
 #define GG_APPMSG_PORT 80
 #define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl"
 #define GG_PUBDIR_PORT 80
 #define GG_DEFAULT_PORT 8074
+#define GG_HTTPS_PORT 443
+#define GG_HTTP_USERAGENT "Mozilla/4.7 [en] (Win98; I)"
 
-struct gg_header {
-	int type;		/* typ pakietu */
-	int length;		/* długość reszty pakietu */
-} __attribute__ ((packed));
+	struct gg_header {
+		unsigned long type;	/* typ pakietu */
+		unsigned long length;	/* długość reszty pakietu */
+	} __attribute__ ((packed));
 
 #define GG_WELCOME 0x0001
 
-struct gg_welcome {
-	int key;		/* klucz szyfrowania hasła */
-} __attribute__ ((packed));
-	
+	struct gg_welcome {
+		unsigned long key;	/* klucz szyfrowania hasła */
+	} __attribute__ ((packed));
+
 #define GG_LOGIN 0x000c
 
-struct gg_login {
-	uin_t uin;		/* twój numerek */
-	int hash;		/* hash hasła */
-	int status;		/* status na dzień dobry */
-	int dunno;		/* == 0x0b */
-	int local_ip;		/* mój adres ip */
-	short local_port;	/* port, na którym słucham */
-} __attribute__ ((packed));
+	struct gg_login {
+		uin_t uin;	/* twój numerek */
+		unsigned long hash;	/* hash hasła */
+		unsigned long status;	/* status na dzień dobry */
+		unsigned long dunno;	/* == 0x0b */
+		unsigned long local_ip;	/* mój adres ip */
+		unsigned short local_port;	/* port, na którym słucham */
+	} __attribute__ ((packed));
 
 #define GG_LOGIN_OK 0x0003
 
@@ -268,76 +273,77 @@
 #define GG_NEW_STATUS 0x0002
 
 #define GG_STATUS_NOT_AVAIL 0x0001	/* rozłączony */
-#define GG_STATUS_AVAIL 0x0002		/* dostępny */
-#define GG_STATUS_BUSY 0x0003		/* zajęty */
+#define GG_STATUS_AVAIL 0x0002	/* dostępny */
+#define GG_STATUS_BUSY 0x0003	/* zajęty */
 #define GG_STATUS_INVISIBLE 0x0014	/* niewidoczny (GG 4.6) */
 
 #define GG_STATUS_FRIENDS_MASK 0x8000	/* tylko dla znajomych (GG 4.6) */
 
-struct gg_new_status {
-	int status;			/* na jaki zmienić? */
-} __attribute__ ((packed));
+	struct gg_new_status {
+		unsigned long status;	/* na jaki zmienić? */
+	} __attribute__ ((packed));
 
 #define GG_NOTIFY 0x0010
-	
-struct gg_notify {
-	uin_t uin;		/* numerek danej osoby */
-	char dunno1;		/* == 3 */
-} __attribute__ ((packed));
-	
+
+	struct gg_notify {
+		uin_t uin;	/* numerek danej osoby */
+		char dunno1;	/* == 3 */
+	} __attribute__ ((packed));
+
 #define GG_NOTIFY_REPLY 0x000c	/* tak, to samo co GG_LOGIN */
-	
+
 /* struct gg_notify_reply zadeklarowane wyżej */
 
 #define GG_ADD_NOTIFY 0x000d
 #define GG_REMOVE_NOTIFY 0x000e
-	
-struct gg_add_remove {
-	uin_t uin;		/* numerek */
-	char dunno1;		/* == 3 */
-} __attribute__ ((packed));
+
+	struct gg_add_remove {
+		uin_t uin;	/* numerek */
+		char dunno1;	/* == 3 */
+	} __attribute__ ((packed));
 
 #define GG_STATUS 0x0002
 
 /* struct gg_status zadeklarowane wcześniej */
-	
+
 #define GG_SEND_MSG 0x000b
 
+#define GG_CLASS_QUEUED 0x0001
+#define GG_CLASS_OFFLINE GG_CLASS_QUEUED
 #define GG_CLASS_MSG 0x0004
 #define GG_CLASS_CHAT 0x0008
 
-struct gg_send_msg {
-	int recipient;
-	int seq;
-	int msgclass;
-} __attribute__ ((packed));
+	struct gg_send_msg {
+		unsigned long recipient;
+		unsigned long seq;
+		unsigned long msgclass;
+	} __attribute__ ((packed));
 
 #define GG_SEND_MSG_ACK 0x0005
 
 #define GG_ACK_DELIVERED 0x0002
 #define GG_ACK_QUEUED 0x0003
-	
-struct gg_send_msg_ack {
-	int status;
-	int recipient;
-	int seq;
-} __attribute__ ((packed));
+
+	struct gg_send_msg_ack {
+		unsigned long status;
+		unsigned long recipient;
+		unsigned long seq;
+	} __attribute__ ((packed));
 
 #define GG_RECV_MSG 0x000a
-	
-struct gg_recv_msg {
-	int sender;
-	int dunno1;
-	int dunno2;
-	int msgclass;
-} __attribute__ ((packed));
+
+	struct gg_recv_msg {
+		unsigned long sender;
+		unsigned long seq;
+		unsigned long time;
+		unsigned long msgclass;
+	} __attribute__ ((packed));
 
 #define GG_PING 0x0008
-	
+
 #define GG_PONG 0x0007
 
 #ifdef __cplusplus
 }
 #endif
-
 #endif