Commit message: Poprawka ograniczania długości opisu.
CVE: CVE-2013-4488
CWE: CWE-310
Vulnerability description: libgadu before 1.12.0 does not verify X.509 certificates from SSL servers, which allows man-in-the-middle attackers to spoof servers.

Project: libgadu
File: libgadu.c

int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
{
	struct gg_notify *n;
	uin_t *u;
	char *t;
	int i, res = 0;

	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);

	if (!sess) {
		errno = EFAULT;
		return -1;
	}

	if (sess->state != GG_STATE_CONNECTED) {
		errno = ENOTCONN;
		return -1;
	}

	if (sess->protocol_version >= GG_PROTOCOL_110)
		return gg_notify105_ex(sess, userlist, types, count);

	if (!userlist || !count)
		return gg_send_packet(sess, GG_LIST_EMPTY, NULL);

	while (count > 0) {
		int part_count, packet_type;

		if (count > 400) {
			part_count = 400;
			packet_type = GG_NOTIFY_FIRST;
		} else {
			part_count = count;
			packet_type = GG_NOTIFY_LAST;
		}

		if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
			return -1;

		for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) {
			n[i].uin = gg_fix32(*u);
			if (types == NULL)
				n[i].dunno1 = GG_USER_NORMAL;
			else
				n[i].dunno1 = *t;
		}

		if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
			free(n);
			res = -1;
			break;
		}

		count -= part_count;
		userlist += part_count;
		if (types != NULL)
			types += part_count;

		free(n);
	}

	return res;
}

Project: libgadu
File: libgadu.c

int gg_change_status(struct gg_session *sess, int status)
{
	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);

	return gg_change_status_descr(sess, status, NULL);
}

Project: libgadu
File: libgadu.c

int gg_remove_notify(struct gg_session *sess, uin_t uin)
{
	return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL);
}

Project: libgadu
File: libgadu.c

int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id)
{
	struct gg_multilogon_disconnect pkt;

	pkt.conn_id = conn_id;

	return gg_send_packet(gs, GG_MULTILOGON_DISCONNECT, &pkt, sizeof(pkt), NULL);
}

Project: libgadu
File: libgadu.c

int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32)
{
	struct gg_send_msg s;
	struct gg_msg_image_request r;
	char dummy = 0;
	int res;

	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32);

	if (!sess) {
		errno = EFAULT;
		return -1;
	}

	if (sess->state != GG_STATE_CONNECTED) {
		errno = ENOTCONN;
		return -1;
	}

	if (size < 0) {
		errno = EINVAL;
		return -1;
	}

	s.recipient = gg_fix32(recipient);
	s.seq = gg_fix32(0);
	s.msgclass = gg_fix32(GG_CLASS_MSG);

	r.flag = GG_MSG_OPTION_IMAGE_REQUEST;
	r.size = gg_fix32(size);
	r.crc32 = gg_fix32(crc32);

	res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL);

	if (!res) {
		struct gg_image_queue *q = malloc(sizeof(*q));
		char *buf;

		if (!q) {
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n");
			return -1;
		}

		buf = malloc(size);
		if (size && !buf)
		{
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
			free(q);
			return -1;
		}

		memset(q, 0, sizeof(*q));

		q->sender = recipient;
		q->size = size;
		q->crc32 = crc32;
		q->image = buf;

		if (!sess->images)
			sess->images = q;
		else {
			struct gg_image_queue *qq;

			for (qq = sess->images; qq->next; qq = qq->next)
				;

			qq->next = q;
		}
	}

	return res;
}

Project: libgadu
File: libgadu.c

int gg_ping(struct gg_session *sess)
{
	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess);

	if (!sess) {
		errno = EFAULT;
		return -1;
	}

	if (sess->state != GG_STATE_CONNECTED) {
		errno = ENOTCONN;
		return -1;
	}

	return gg_send_packet(sess, GG_PING, NULL);
}

Project: libgadu
File: libgadu.c

static int gg_session_callback(struct gg_session *sess)
{
	if (!sess) {
		errno = EFAULT;
		return -1;
	}

	return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1;
}

Project: libgadu
File: libgadu.c

int gg_write(struct gg_session *sess, const char *buf, int length)
{
	int res = 0;

	if (!sess->async) {
		int written = 0;

		while (written < length) {
			res = gg_write_common(sess, buf + written, length - written);

			if (res == -1)
				return -1;

			written += res;
			res = written;
		}
	} else {
		if (sess->send_buf == NULL) {
			res = gg_write_common(sess, buf, length);

			if (res == -1 && errno == EAGAIN)
				res = 0;
			if (res == -1)
				return -1;
		}

		if (res < length) {
			char *tmp;

			if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) {
				errno = ENOMEM;
				return -1;
			}

			sess->send_buf = tmp;

			memcpy(sess->send_buf + sess->send_left, buf + res, length - res);

			sess->send_left += length - res;
		}
	}

	return res;
}

Project: libgadu
File: libgadu.c

int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size)
{
	struct gg_msg_image_reply *r;
	struct gg_send_msg s;
	const char *tmp;
	char buf[1910];
	int res = -1;

	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size);

	if (!sess || !filename || !image) {
		errno = EFAULT;
		return -1;
	}

	if (sess->state != GG_STATE_CONNECTED) {
		errno = ENOTCONN;
		return -1;
	}

	if (size < 0) {
		errno = EINVAL;
		return -1;
	}

	/* wytnij ścieżki, zostaw tylko nazwę pliku */
	while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\')))
		filename = tmp + 1;

	if (strlen(filename) < 1 || strlen(filename) > 1024) {
		errno = EINVAL;
		return -1;
	}

	s.recipient = gg_fix32(recipient);
	s.seq = gg_fix32(0);
	s.msgclass = gg_fix32(GG_CLASS_MSG);

	buf[0] = 0;
	r = (void*) &buf[1];

	r->flag = GG_MSG_OPTION_IMAGE_REPLY;
	r->size = gg_fix32(size);
	r->crc32 = gg_fix32(gg_crc32(0, (const unsigned char*) image, size));

	while (size > 0) {
		size_t buflen, chunklen;

		/* \0 + struct gg_msg_image_reply */
		buflen = sizeof(struct gg_msg_image_reply) + 1;

		/* w pierwszym kawałku jest nazwa pliku */
		if (r->flag == GG_MSG_OPTION_IMAGE_REPLY) {
			strcpy(buf + buflen, filename);
			buflen += strlen(filename) + 1;
		}

		chunklen = ((size_t) size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t) size;

		memcpy(buf + buflen, image, chunklen);
		size -= chunklen;
		image += chunklen;

		res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), buf, buflen + chunklen, NULL);

		if (res == -1)
			break;

		r->flag = GG_MSG_OPTION_IMAGE_REPLY_MORE;
	}

	return res;
}

Project: libgadu
File: libgadu.c

static int gg_send_message_110(struct gg_session *sess,
	uin_t recipient, uint64_t chat_id,
	const char *message, int is_html)
{
	gg_tvbuilder_t *tvb;
	char *html_message_gen = NULL, *plain_message_gen = NULL;
	int seq;
	const char *html_message, *plain_message;

	gg_debug_session(sess, GG_DEBUG_FUNCTION,
		"** gg_send_message_110(%p, %u, %llu, %p, %d);\n",
		sess, recipient, chat_id, message, is_html);

	if (message == NULL)
		return -1;

	if ((recipient == 0) == (chat_id == 0))
		return -1;

	if (is_html) {
		html_message = message;

		if (sess->encoding != GG_ENCODING_UTF8) {
			html_message = html_message_gen = gg_encoding_convert(
				html_message, sess->encoding, GG_ENCODING_UTF8,
				-1, -1);
			if (html_message_gen == NULL)
				return -1;
		}

		plain_message = plain_message_gen =
			gg_message_html_to_text_110(html_message);
		if (plain_message_gen == NULL) {
			free(html_message_gen);
			return -1;
		}
	} else {
		plain_message = message;

		if (sess->encoding != GG_ENCODING_UTF8) {
			plain_message = plain_message_gen = gg_encoding_convert(
				plain_message, sess->encoding, GG_ENCODING_UTF8,
				-1, -1);
			if (plain_message_gen == NULL)
				return -1;
		}
		
		html_message = html_message_gen =
			gg_message_text_to_html_110(plain_message, -1);
		if (html_message_gen == NULL) {
			free(plain_message_gen);
			return -1;
		}
	}

	seq = ++sess->seq;

	tvb = gg_tvbuilder_new(sess, NULL);
	gg_tvbuilder_expected_size(tvb, 50 + 2 * strlen(html_message));

	if (recipient) {
		gg_tvbuilder_write_uint8(tvb, 0x0a);
		gg_tvbuilder_write_uin(tvb, recipient, 1);
	}

	gg_tvbuilder_write_uint8(tvb, 0x10);
	gg_tvbuilder_write_uint8(tvb, 0x08); /* magic */

	gg_tvbuilder_write_uint8(tvb, 0x18);
	gg_tvbuilder_write_packed_uint(tvb, seq);

	gg_tvbuilder_write_uint8(tvb, 0x2a);
	gg_tvbuilder_write_str(tvb, plain_message, -1);

	gg_tvbuilder_write_uint8(tvb, 0x32);
	gg_tvbuilder_write_str(tvb, html_message, -1);

	if (chat_id) {
		gg_tvbuilder_write_uint8(tvb, 0x3a);
		gg_tvbuilder_write_str(tvb, "", 0); /* magic */

		gg_tvbuilder_write_uint8(tvb, 0x51);
		gg_tvbuilder_write_uint64(tvb, chat_id);
	}

	free(html_message_gen);
	free(plain_message_gen);

	if (!gg_tvbuilder_send(tvb, recipient ?
		GG_SEND_MSG110 : GG_CHAT_SEND_MSG))
		return -1;

	return seq;
}

Project: libgadu
File: libgadu.c

struct gg_session *gg_login(const struct gg_login_params *p)
{
	struct gg_session *sess = NULL;

	if (p == NULL) {
		gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p);
		errno = EFAULT;
		return NULL;
	}

	gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async);

	sess = malloc(sizeof(struct gg_session));

	if (sess == NULL) {
		gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n");
		goto fail;
	}

	memset(sess, 0, sizeof(struct gg_session));

	if (p->password == NULL || p->uin == 0) {
		gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n");
		errno = EFAULT;
		goto fail;
	}

	if (!(sess->password = strdup(p->password))) {
		gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n");
		goto fail;
	}

	if (p->hash_type < 0 || p->hash_type > GG_LOGIN_HASH_SHA1) {
		gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unknown hash type (%d)\n", p->hash_type);
		errno = EFAULT;
		goto fail;
	}

	sess->uin = p->uin;
	sess->state = GG_STATE_RESOLVING;
	sess->check = GG_CHECK_READ;
	sess->timeout = GG_DEFAULT_TIMEOUT;
	sess->async = p->async;
	sess->type = GG_SESSION_GG;
	sess->initial_status = p->status;
	sess->callback = gg_session_callback;
	sess->destroy = gg_free_session;
	sess->port = p->server_port;
	sess->server_addr = p->server_addr;
	sess->external_port = p->external_port;
	sess->external_addr = p->external_addr;
	sess->client_addr = p->client_addr;
	sess->client_port = p->client_port;

	if (p->protocol_features == 0) {
		sess->protocol_features = GG_FEATURE_MSG80 | GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION;
	} else {
		sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77));

		if (!(p->protocol_features & GG_FEATURE_STATUS77))
			sess->protocol_features |= GG_FEATURE_STATUS80;

		if (!(p->protocol_features & GG_FEATURE_MSG77))
			sess->protocol_features |= GG_FEATURE_MSG80;
	}

	if (!(sess->status_flags = p->status_flags))
		sess->status_flags = GG_STATUS_FLAG_UNKNOWN | GG_STATUS_FLAG_SPAM;

	if (!p->protocol_version)
		sess->protocol_version = GG_DEFAULT_PROTOCOL_VERSION;
	else if (p->protocol_version < 0x2e) {
		gg_debug(GG_DEBUG_MISC, "// gg_login() libgadu no longer support protocol < 0x2e\n");
		sess->protocol_version = 0x2e;
	} else
		sess->protocol_version = p->protocol_version;

	sess->client_version = (p->client_version) ? strdup(p->client_version) : NULL;
	sess->last_sysmsg = p->last_sysmsg;
	sess->image_size = p->image_size;
	sess->pid = -1;
	sess->encoding = p->encoding;

	if (gg_session_set_resolver(sess, p->resolver) == -1) {
		gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unsupported resolver type (%d)\n", p->resolver);
		errno = EFAULT;
		goto fail;
	}

	if (p->status_descr) {
		sess->initial_descr = gg_encoding_convert(p->status_descr, p->encoding, GG_ENCODING_UTF8, -1, -1);

		if (!sess->initial_descr) {
			gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n");
			goto fail;
		}
		
		/* XXX pamiętać, żeby nie ciąć w środku znaku utf-8 */
		
		if (strlen(sess->initial_descr) > GG_STATUS_DESCR_MAXSIZE)
			sess->initial_descr[GG_STATUS_DESCR_MAXSIZE] = 0;
	}

	if (p->tls != GG_SSL_DISABLED) {
#if !defined(GG_CONFIG_HAVE_GNUTLS) && !defined(GG_CONFIG_HAVE_OPENSSL)
		gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n");

		if (p->tls == GG_SSL_REQUIRED) {
			errno = ENOSYS;
			goto fail;
		}
#else
		sess->ssl_flag = p->tls;
#endif
	}

	if (p->hash_type)
		sess->hash_type = p->hash_type;
	else
		sess->hash_type = GG_LOGIN_HASH_SHA1;

	if (sess->server_addr == 0) {
		if (gg_proxy_enabled) {
			sess->resolver_host = gg_proxy_host;
			sess->proxy_port = gg_proxy_port;
			sess->state = (sess->async) ? GG_STATE_RESOLVE_PROXY_HUB_ASYNC : GG_STATE_RESOLVE_PROXY_HUB_SYNC;
		} else {
			sess->resolver_host = GG_APPMSG_HOST;
			sess->proxy_port = 0;
			sess->state = (sess->async) ? GG_STATE_RESOLVE_HUB_ASYNC : GG_STATE_RESOLVE_HUB_SYNC;
		}
	} else {
		// XXX inet_ntoa i wielowątkowość
		sess->connect_host = strdup(inet_ntoa(*(struct in_addr*) &sess->server_addr));
		if (sess->connect_host == NULL)
			goto fail;
		sess->connect_index = 0;

		if (gg_proxy_enabled) {
			sess->resolver_host = gg_proxy_host;
			sess->proxy_port = gg_proxy_port;
			if (sess->port == 0)
				sess->connect_port[0] = GG_HTTPS_PORT;
			else
				sess->connect_port[0] = sess->port;
			sess->connect_port[1] = 0;
			sess->state = (sess->async) ? GG_STATE_RESOLVE_PROXY_GG_ASYNC : GG_STATE_RESOLVE_PROXY_GG_SYNC;
		} else {
			sess->resolver_host = sess->connect_host;
			if (sess->port == 0) {
				if (sess->ssl_flag == GG_SSL_DISABLED) {
					sess->connect_port[0] = GG_DEFAULT_PORT;
					sess->connect_port[1] = GG_HTTPS_PORT;
				} else {
					sess->connect_port[0] = GG_HTTPS_PORT;
					sess->connect_port[1] = 0;
				}
			} else {
				sess->connect_port[0] = sess->port;
				sess->connect_port[1] = 0;
			}
			sess->state = (sess->async) ? GG_STATE_RESOLVE_GG_ASYNC : GG_STATE_RESOLVE_GG_SYNC;
		}
	}

	// XXX inaczej gg_watch_fd() wyjdzie z timeoutem
	sess->timeout = GG_DEFAULT_TIMEOUT;

	if (!sess->async) {
		while (!GG_SESSION_IS_CONNECTED(sess)) {
			struct gg_event *ge;

			ge = gg_watch_fd(sess);

			if (ge == NULL) {
				gg_debug(GG_DEBUG_MISC, "// gg_session_connect() critical error in gg_watch_fd()\n");
				goto fail;
			}

			if (ge->type == GG_EVENT_CONN_FAILED) {
				errno = EACCES;
				gg_debug(GG_DEBUG_MISC, "// gg_session_connect() could not login\n");
				gg_event_free(ge);
				goto fail;
			}

			gg_event_free(ge);
		}
	} else {
		struct gg_event *ge;

		ge = gg_watch_fd(sess);

		if (ge == NULL) {
			gg_debug(GG_DEBUG_MISC, "// gg_session_connect() critical error in gg_watch_fd()\n");
			goto fail;
		}

		gg_event_free(ge);
	}

	

	return sess;

fail:
	gg_free_session(sess);

	return NULL;
}

Project: libgadu
File: libgadu.c

int gg_change_status_descr(struct gg_session *sess, int status, const char *descr)
{
	struct gg_new_status80 p;
	char *gen_descr = NULL;
	int descr_len = 0;
	int descr_null_len = 0;
	int res;

	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);

	if (!sess) {
		errno = EFAULT;
		return -1;
	}

	if (sess->state != GG_STATE_CONNECTED) {
		errno = ENOTCONN;
		return -1;
	}

	sess->status = status;

	if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) {
		descr = gen_descr = gg_encoding_convert(descr, GG_ENCODING_CP1250, GG_ENCODING_UTF8, -1, -1);

		if (!gen_descr)
			return -1;
	}

	if (descr) {
		descr_len = strlen(descr);

		if (descr_len > GG_STATUS_DESCR_MAXSIZE)
			descr_len = GG_STATUS_DESCR_MAXSIZE;

		/* XXX pamiętać o tym, żeby nie ucinać w środku znaku utf-8 */
	} else {
		descr = "";
	}

	p.status		= gg_fix32(status);
	p.flags			= gg_fix32(sess->status_flags);
	p.description_size	= gg_fix32(descr_len);

	if (sess->protocol_version >= GG_PROTOCOL_110) {
		p.flags = gg_fix32(0x00000014);
		descr_null_len = 1;
	}

	res = gg_send_packet(sess, GG_NEW_STATUS80, 
			&p, sizeof(p), 
			descr, descr_len,
			"\x00", descr_null_len,
			NULL);

	free(gen_descr);

	if (GG_S_NA(status)) {
		sess->state = GG_STATE_DISCONNECTING;
		sess->timeout = GG_TIMEOUT_DISCONNECT;
	}

	return res;
}

Project: libgadu
File: libgadu.c

int gg_change_status_flags(struct gg_session *sess, int flags)
{
	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_flags(%p, 0x%08x);\n", sess, flags);

	if (sess == NULL) {
		errno = EFAULT;
		return -1;
	}

	sess->status_flags = flags;

	return 0;
}

Project: libgadu
File: libgadu.c

int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen)
{
	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen);

	return gg_send_message_common(sess, msgclass, recipients_count, recipients, message, format, formatlen, NULL);
}

Project: libgadu
File: libgadu.c

int gg_userlist_request(struct gg_session *sess, char type, const char *request)
{
	int len;

	if (!sess) {
		errno = EFAULT;
		return -1;
	}

	if (sess->state != GG_STATE_CONNECTED) {
		errno = ENOTCONN;
		return -1;
	}

	if (!request) {
		sess->userlist_blocks = 1;
		return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
	}

	len = strlen(request);

	sess->userlist_blocks = 0;

	while (len > 2047) {
		sess->userlist_blocks++;

		if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1)
			return -1;

		if (type == GG_USERLIST_PUT)
			type = GG_USERLIST_PUT_MORE;

		request += 2047;
		len -= 2047;
	}

	sess->userlist_blocks++;

	return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
}

Project: libgadu
File: libgadu.c

unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
{
	unsigned int x, y, z;

	y = seed;

	for (x = 0; *password; password++) {
		x = (x & 0xffffff00) | *password;
		y ^= x;
		y += x;
		x <<= 8;
		y ^= x;
		x <<= 8;
		y -= x;
		x <<= 8;
		y ^= x;

		z = y & 0x1F;
		y = (y << z) | (y >> (32 - z));
	}

	return y;
}

Project: libgadu
File: libgadu.c

int gg_read(struct gg_session *sess, char *buf, int length)
{
	int res;

#ifdef GG_CONFIG_HAVE_GNUTLS
	if (sess->ssl != NULL) {
		for (;;) {
			res = gnutls_record_recv(GG_SESSION_GNUTLS(sess), buf, length);

			if (res < 0) {
				if (res == GNUTLS_E_AGAIN)
					errno = EAGAIN;
				else if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
					continue;
				else
					errno = EINVAL;

				return -1;
			}

			return res;
		}
	}
#endif

#ifdef GG_CONFIG_HAVE_OPENSSL
	if (sess->ssl != NULL) {
		for (;;) {
			int err;

			res = SSL_read(sess->ssl, buf, length);

			if (res < 0) {
				err = SSL_get_error(sess->ssl, res);

				if (err == SSL_ERROR_SYSCALL && errno == EINTR)
					continue;

				if (err == SSL_ERROR_WANT_READ)
					errno = EAGAIN;
				else if (err != SSL_ERROR_SYSCALL)
					errno = EINVAL;

				return -1;
			}

			return res;
		}
	}
#endif

	for (;;) {
		res = recv(sess->fd, buf, length, 0);

		if (res == -1 && errno == EINTR)
			continue;

		return res;
	}
}

Project: libgadu
File: libgadu.c

int gg_add_notify(struct gg_session *sess, uin_t uin)
{
	return gg_add_notify_ex(sess, uin, GG_USER_NORMAL);
}

Project: libgadu
File: libgadu.c

int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length){
	struct gg_typing_notification pkt;
	uin_t uin;

	pkt.length = gg_fix16(length);
	uin = gg_fix32(recipient);
	memcpy(&pkt.uin, &uin, sizeof(uin_t));

	return gg_send_packet(sess, GG_TYPING_NOTIFICATION, &pkt, sizeof(pkt), NULL);
}

Project: libgadu
File: libgadu.c

int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message)
{
	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message);

	return gg_send_message_common(sess, msgclass, recipients_count, recipients, message, (const unsigned char*) "\x02\x06\x00\x00\x00\x08\x00\x00\x00", 9, NULL);
}

Project: libgadu
File: libgadu.c

int gg_chat_send_message(struct gg_session *gs, uint64_t id, const char *message, int is_html)
{
	return gg_send_message_110(gs, 0, id, message, is_html);
}

Project: libgadu
File: libgadu.c

const char *gg_libgadu_version(void)
{
	return GG_LIBGADU_VERSION;
}

Project: libgadu
File: libgadu.c

int gg_libgadu_check_feature(gg_libgadu_feature_t feature)
{
	switch (feature)
	{
	case GG_LIBGADU_FEATURE_SSL:
#if defined(GG_CONFIG_HAVE_OPENSSL) || defined(GG_CONFIG_HAVE_GNUTLS)
		return 1;
#else
		return 0;
#endif

	case GG_LIBGADU_FEATURE_PTHREAD:
#ifdef GG_CONFIG_HAVE_PTHREAD
		return 1;
#else
		return 0;
#endif

	case GG_LIBGADU_FEATURE_USERLIST100:
#ifdef GG_CONFIG_HAVE_ZLIB
		return 1;
#else
		return 0;
#endif

	/* Celowo nie ma default, żeby kompilator wyłapał brakujące funkcje */

	}

	return 0;
}

Project: libgadu
File: libgadu.c

static int gg_send_message_common(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen, const unsigned char *html_message)
{
	struct gg_send_msg80 s80;
	const char *cp_msg = NULL, *utf_html_msg = NULL;
	char *recoded_msg = NULL, *recoded_html_msg = NULL;
	unsigned char *generated_format = NULL;
	int seq_no = -1;

	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_common(%p, %d, %d, %p, %p, %p, %d, %p);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen, html_message);

	if (!sess) {
		errno = EFAULT;
		return -1;
	}

	if (sess->state != GG_STATE_CONNECTED) {
		errno = ENOTCONN;
		return -1;
	}

	if ((message == NULL && html_message == NULL) || recipients_count <= 0 || recipients_count > 0xffff || recipients == NULL || (format == NULL && formatlen != 0)) {
		errno = EINVAL;
		return -1;
	}

	if (sess->protocol_version >= GG_PROTOCOL_110 && recipients_count == 1)
	{
		int is_html = (html_message != NULL);
		seq_no = gg_send_message_110(sess, recipients[0], 0,
			(const char*)(is_html ? html_message : message),
			is_html);
		goto cleanup;
	}

	if (message == NULL) {
		char *tmp_msg;
		size_t len, fmt_len;
		uint16_t fixed_fmt_len;

		len = gg_message_html_to_text(NULL, NULL, &fmt_len, (const char*) html_message, sess->encoding);

		tmp_msg = malloc(len + 1);

		if (tmp_msg == NULL)
			goto cleanup;

		if (fmt_len != 0) {
			generated_format = malloc(fmt_len + 3);

			if (generated_format == NULL) {
				free(tmp_msg);
				goto cleanup;
			}

			generated_format[0] = '\x02';
			fixed_fmt_len = gg_fix16(fmt_len);
			memcpy(generated_format + 1, &fixed_fmt_len, sizeof(fixed_fmt_len));
			gg_message_html_to_text(tmp_msg, generated_format + 3, NULL, (const char*) html_message, sess->encoding);

			format = generated_format;
			formatlen = fmt_len + 3;
		} else {
			gg_message_html_to_text(tmp_msg, NULL, NULL, (const char*) html_message, sess->encoding);

			format = NULL;
			formatlen = 0;
		}

		if (sess->encoding == GG_ENCODING_UTF8) {
			cp_msg = recoded_msg = gg_encoding_convert(tmp_msg, sess->encoding, GG_ENCODING_CP1250, -1, -1);
			free(tmp_msg);

			if (cp_msg == NULL)
				goto cleanup;
		} else {
			cp_msg = recoded_msg = tmp_msg;
		}
	} else {
		if (sess->encoding == GG_ENCODING_UTF8) {
			cp_msg = recoded_msg = gg_encoding_convert((const char*) message, sess->encoding, GG_ENCODING_CP1250, -1, -1);

			if (cp_msg == NULL)
				goto cleanup;
		} else {
			cp_msg = (const char*) message;
		}
	}

	if (html_message == NULL) {
		size_t len;
		char *tmp;
		const char *utf_msg;
		const unsigned char *format_ = NULL;
		size_t formatlen_ = 0;

		if (sess->encoding == GG_ENCODING_UTF8) {
			utf_msg = (const char*) message;
		} else {
			utf_msg = recoded_msg = gg_encoding_convert((const char*) message, sess->encoding, GG_ENCODING_UTF8, -1, -1);

			if (utf_msg == NULL)
				goto cleanup;
		}

		if (format != NULL && formatlen >= 3) {
			format_ = format + 3;
			formatlen_ = formatlen - 3;
		}

		len = gg_message_text_to_html(NULL, utf_msg, GG_ENCODING_UTF8, format_, formatlen_);

		tmp = malloc(len + 1);

		if (tmp == NULL)
			goto cleanup;

		gg_message_text_to_html(tmp, utf_msg, GG_ENCODING_UTF8, format_, formatlen_);

		utf_html_msg = recoded_html_msg = tmp;
	} else {
		if (sess->encoding == GG_ENCODING_UTF8) {
			utf_html_msg = (const char*) html_message;
		} else {
			utf_html_msg = recoded_html_msg = gg_encoding_convert((const char*) html_message, sess->encoding, GG_ENCODING_UTF8, -1, -1);

			if (utf_html_msg == NULL)
				goto cleanup;
		}
	}

	/* Drobne odchylenie od protokołu. Jeśli wysyłamy kilka
	 * wiadomości w ciągu jednej sekundy, zwiększamy poprzednią
	 * wartość, żeby każda wiadomość miała unikalny numer.
	 */

	seq_no = time(NULL);

	if (seq_no <= sess->seq)
		seq_no = sess->seq + 1;

	sess->seq = seq_no;

	s80.seq = gg_fix32(seq_no);
	s80.msgclass = gg_fix32(msgclass);
	s80.offset_plain = gg_fix32(sizeof(s80) + strlen(utf_html_msg) + 1);
	s80.offset_attr = gg_fix32(sizeof(s80) + strlen(utf_html_msg) + 1 + strlen(cp_msg) + 1);

	if (recipients_count > 1) {
		struct gg_msg_recipients r;
		int i, j, k;
		uin_t *recps;

		r.flag = GG_MSG_OPTION_CONFERENCE;
		r.count = gg_fix32(recipients_count - 1);

		recps = malloc(sizeof(uin_t) * (recipients_count - 1));

		if (!recps) {
			seq_no = -1;
			goto cleanup;
		}

		for (i = 0; i < recipients_count; i++) {
			for (j = 0, k = 0; j < recipients_count; j++) {
				if (j != i) {
					recps[k] = gg_fix32(recipients[j]);
					k++;
				}
			}

			s80.recipient = gg_fix32(recipients[i]);

			if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), utf_html_msg, strlen(utf_html_msg) + 1, cp_msg, strlen(cp_msg) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1)
				seq_no = -1;
		}

		free(recps);
	} else {
		s80.recipient = gg_fix32(recipients[0]);

		if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), utf_html_msg, strlen(utf_html_msg) + 1, cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1)
			seq_no = -1;
	}

cleanup:
	free(recoded_msg);
	free(recoded_html_msg);
	free(generated_format);

	return seq_no;
}

Project: libgadu
File: libgadu.c

int gg_userlist100_request(struct gg_session *sess, char type, unsigned int version, char format_type, const char *request)
{
	struct gg_userlist100_request pkt;
	unsigned char *zrequest;
	size_t zrequest_len;
	int ret;

	if (!sess) {
		errno = EFAULT;
		return -1;
	}

	if (sess->state != GG_STATE_CONNECTED) {
		errno = ENOTCONN;
		return -1;
	}

	pkt.type = type;
	pkt.version = gg_fix32(version);
	pkt.format_type = format_type;
	pkt.unknown1 = 0x01;

	if (request == NULL)
		return gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), NULL);

	zrequest = gg_deflate(request, &zrequest_len);

	if (zrequest == NULL) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_userlist100_request() gg_deflate() failed\n");
		return -1;
	}

	ret = gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), zrequest, zrequest_len, NULL);

	free(zrequest);

	return ret;
}

Project: libgadu
File: libgadu.c

void *gg_recv_packet(struct gg_session *sess)
{
	struct gg_header *gh;
	char *packet;
	int res;
	size_t len;

	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);

	if (sess == NULL) {
		errno = EFAULT;
		return NULL;
	}

	for (;;) {
		if (sess->recv_buf == NULL && sess->recv_done == 0) {
			sess->recv_buf = malloc(sizeof(struct gg_header) + 1);

			if (sess->recv_buf == NULL) {
				gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() out of memory\n");
				return NULL;
			}
		}

		gh = (struct gg_header*) sess->recv_buf;

		if ((size_t) sess->recv_done < sizeof(struct gg_header)) {
			len = sizeof(struct gg_header) - sess->recv_done;
			gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() header: %d done, %d to go\n", sess->recv_done, len);
		} else {
			if ((size_t) sess->recv_done >= sizeof(struct gg_header) + gg_fix32(gh->length)) {
				gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() and that's it\n");
				break;
			}

			len = sizeof(struct gg_header) + gg_fix32(gh->length) - sess->recv_done;

			gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() payload: %d done, %d length, %d to go\n", sess->recv_done, gg_fix32(gh->length), len);
		}

		res = gg_read(sess, sess->recv_buf + sess->recv_done, len);

		if (res == 0) {
			errno = ECONNRESET;
			gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() connection broken\n");
			goto fail;
		}

		if (res == -1 && errno == EAGAIN) {
			gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() resource temporarily unavailable\n");
			goto eagain;
		}

		if (res == -1) {
			gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() read failed: errno=%d, %s\n", errno, strerror(errno));
			goto fail;
		}

		gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() read %d bytes\n", res);

		if (sess->recv_done + res == sizeof(struct gg_header)) {
			char *tmp;

			gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() header complete, payload %d bytes\n", gg_fix32(gh->length));

			if (gg_fix32(gh->length == 0))
				break;

			if (gg_fix32(gh->length) > 65535) {
				gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() invalid packet length (%d)\n", gg_fix32(gh->length));
				errno = ERANGE;
				goto fail;
			}

			tmp = realloc(sess->recv_buf, sizeof(struct gg_header) + gg_fix32(gh->length) + 1);

			if (tmp == NULL) {
				gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() out of memory\n");
				goto fail;
			}

			sess->recv_buf = tmp;
		}

		sess->recv_done += res;
	}

	packet = sess->recv_buf;
	sess->recv_buf = NULL;
	sess->recv_done = 0;

	/* Czasami zakładamy, że teksty w pakietach są zakończone zerem */
	packet[sizeof(struct gg_header) + gg_fix32(gh->length)] = 0;

	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet(type=0x%.2x, length=%d)\n", gg_fix32(gh->type), gg_fix32(gh->length));
	gg_debug_dump(sess, GG_DEBUG_DUMP, packet, sizeof(struct gg_header) + gg_fix32(gh->length));

	gh->type = gg_fix32(gh->type);
	gh->length = gg_fix32(gh->length);

	return packet;

fail:
	free(sess->recv_buf);
	sess->recv_buf = NULL;
	sess->recv_done = 0;

eagain:
	return NULL;
}

Project: libgadu
File: libgadu.c

int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
{
	return gg_notify_ex(sess, userlist, NULL, count);
}

Project: libgadu
File: libgadu.c

void gg_free_session(struct gg_session *sess)
{
	struct gg_dcc7 *dcc;
	struct gg_chat_list *chat;

	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_free_session(%p);\n", sess);

	if (sess == NULL)
		return;

	/* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */

	free(sess->resolver_result);
	free(sess->connect_host);
	free(sess->password);
	free(sess->initial_descr);
	free(sess->client_version);
	free(sess->header_buf);
	free(sess->recv_buf);

#ifdef GG_CONFIG_HAVE_GNUTLS
	if (sess->ssl != NULL) {
		gg_session_gnutls_t *tmp;

		tmp = (gg_session_gnutls_t*) sess->ssl;
		gnutls_deinit(tmp->session);
		gnutls_certificate_free_credentials(tmp->xcred);
		gnutls_global_deinit();
		free(sess->ssl);
	}
#endif

#ifdef GG_CONFIG_HAVE_OPENSSL
	if (sess->ssl)
		SSL_free(sess->ssl);

	if (sess->ssl_ctx)
		SSL_CTX_free(sess->ssl_ctx);
#endif

	sess->resolver_cleanup(&sess->resolver, 1);

	if (sess->fd != -1)
		close(sess->fd);

	while (sess->images)
		gg_image_queue_remove(sess, sess->images, 1);

	free(sess->send_buf);

	for (dcc = sess->dcc7_list; dcc; dcc = dcc->next)
		dcc->sess = NULL;

	chat = sess->chat_list;
	while (chat != NULL) {
		struct gg_chat_list *next = chat->next;
		free(chat->participants);
		free(chat);
		chat = next;
	}

	free(sess);
}

Project: libgadu
File: libgadu.c

int gg_send_message_confer_html(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *html_message)
{
	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer_html(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, html_message);

	return gg_send_message_common(sess, msgclass, recipients_count, recipients, NULL, NULL, 0, html_message);
}

Project: libgadu
File: libgadu.c

void gg_logoff(struct gg_session *sess)
{
	if (!sess)
		return;

	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);

#ifdef GG_CONFIG_HAVE_GNUTLS
	if (sess->ssl != NULL)
		gnutls_bye(GG_SESSION_GNUTLS(sess), GNUTLS_SHUT_RDWR);
#endif

#ifdef GG_CONFIG_HAVE_OPENSSL
	if (sess->ssl != NULL)
		SSL_shutdown(sess->ssl);
#endif

	sess->resolver_cleanup(&sess->resolver, 1);

	if (sess->fd != -1) {
		close(sess->fd);
		sess->fd = -1;
	}

	if (sess->send_buf) {
		free(sess->send_buf);
		sess->send_buf = NULL;
		sess->send_left = 0;
	}
}

Project: libgadu
File: libgadu.c

int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int ts)
{
	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, ts);

	return gg_change_status_descr(sess, status, descr);
}

Project: libgadu
File: libgadu.c

int gg_send_message_html(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *html_message)
{
	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_html(%p, %d, %u, %p);\n", sess, msgclass, recipient, html_message);

	return gg_send_message_common(sess, msgclass, 1, &recipient, NULL, NULL, 0, html_message);
}

Project: libgadu
File: libgadu.c

int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen)
{
	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen);

	return gg_send_message_common(sess, msgclass, 1, &recipient, message, format, formatlen, NULL);
}

Project: libgadu
File: libgadu.c

static int gg_write_common(struct gg_session *sess, const char *buf, int length)
{
	int res;

#ifdef GG_CONFIG_HAVE_GNUTLS
	if (sess->ssl != NULL) {
		for (;;) {
			res = gnutls_record_send(GG_SESSION_GNUTLS(sess), buf, length);

			if (res < 0) {
				if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
					continue;

				if (res == GNUTLS_E_AGAIN)
					errno = EAGAIN;
				else
					errno = EINVAL;

				return -1;
			}

			return res;
		}
	}
#endif

#ifdef GG_CONFIG_HAVE_OPENSSL
	if (sess->ssl != NULL) {
		for (;;) {
			int err;

			res = SSL_write(sess->ssl, buf, length);

			if (res < 0) {
				err = SSL_get_error(sess->ssl, res);

				if (err == SSL_ERROR_SYSCALL && errno == EINTR)
					continue;

				if (err == SSL_ERROR_WANT_WRITE)
					errno = EAGAIN;
				else if (err != SSL_ERROR_SYSCALL)
					errno = EINVAL;

				return -1;
			}

			return res;
		}
	}
#endif

	for (;;) {
		res = send(sess->fd, buf, length, 0);

		if (res == -1 && errno == EINTR)
			continue;

		return res;
	}
}

Project: libgadu
File: libgadu.c

int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type)
{
	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);

	if (!sess) {
		errno = EFAULT;
		return -1;
	}

	if (sess->state != GG_STATE_CONNECTED) {
		errno = ENOTCONN;
		return -1;
	}

	if (sess->protocol_version >= GG_PROTOCOL_110) {
		gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
		gg_tvbuilder_expected_size(tvb, 16);

		gg_tvbuilder_write_uin(tvb, uin, 0);
		gg_tvbuilder_write_uint8(tvb, type);

		if (!gg_tvbuilder_send(tvb, GG_ADD_NOTIFY105))
			return -1;
		return 0;
	} else {
		struct gg_add_remove a;

		a.uin = gg_fix32(uin);
		a.dunno1 = type;

		return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL);
	}
}

Project: libgadu
File: libgadu.c

int gg_chat_invite(struct gg_session *gs, uint64_t id, uin_t *participants,
	unsigned int participants_count)
{
	struct gg_chat_invite pkt;
	int i, seq, ret;
	struct gg_chat_participant
	{
		uint32_t uin;
		uint32_t dummy;
	} GG_PACKED;
	struct gg_chat_participant *participants_list;
	size_t participants_list_size;

	if (participants_count == 0 || participants_count >= ~0 / sizeof(struct gg_chat_participant))
		return -1;

	participants_list_size = sizeof(struct gg_chat_participant) *
		participants_count;
	participants_list = malloc(participants_list_size);
	if (participants_list == NULL)
		return -1;

	seq = ++gs->seq;
	pkt.id = gg_fix64(id);
	pkt.seq = gg_fix32(seq);
	pkt.participants_count = gg_fix32(participants_count);

	for (i = 0; i < participants_count; i++)
	{
		participants_list[i].uin = gg_fix32(participants[i]);
		participants_list[i].dummy = gg_fix32(0x1e);
	}

	ret = gg_send_packet(gs, GG_CHAT_INVITE,
		&pkt, sizeof(pkt),
		participants_list, participants_list_size,
		NULL);
	free(participants_list);

	if (ret == -1)
		return -1;
	return seq;
}

Project: libgadu
File: libgadu.c

int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len)
{
	struct gg_send_msg s;

	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient);

	if (!sess) {
		errno = EFAULT;
		return -1;
	}

	if (sess->state != GG_STATE_CONNECTED) {
		errno = ENOTCONN;
		return -1;
	}

	s.recipient = gg_fix32(recipient);
	s.seq = gg_fix32(0);
	s.msgclass = gg_fix32(msgclass);

	return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL);
}

Project: libgadu
File: libgadu.c

int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message)
{
	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message);

	return gg_send_message_common(sess, msgclass, 1, &recipient, message, (const unsigned char*) "\x02\x06\x00\x00\x00\x08\x00\x00\x00", 9, NULL);
}

Project: libgadu
File: libgadu.c

int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type)
{
	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);

	if (!sess) {
		errno = EFAULT;
		return -1;
	}

	if (sess->state != GG_STATE_CONNECTED) {
		errno = ENOTCONN;
		return -1;
	}

	if (sess->protocol_version >= GG_PROTOCOL_110) {
		gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
		gg_tvbuilder_expected_size(tvb, 16);

		gg_tvbuilder_write_uin(tvb, uin, 0);
		gg_tvbuilder_write_uint8(tvb, type);

		if (!gg_tvbuilder_send(tvb, GG_REMOVE_NOTIFY105))
			return -1;
		return 0;
	} else {
		struct gg_add_remove a;

		a.uin = gg_fix32(uin);
		a.dunno1 = type;

		return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL);
	}
}

Project: libgadu
File: libgadu.c

static int gg_notify105_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
{
	int i = 0;

	if (!userlist || !count)
		return gg_send_packet(sess, GG_NOTIFY105_LIST_EMPTY, NULL);

	while (i < count) {
		gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
		gg_tvbuilder_expected_size(tvb, 2100);

		while (i < count) {
			size_t prev_size = gg_tvbuilder_get_size(tvb);
			gg_tvbuilder_write_uin(tvb, userlist[i], 0);
			gg_tvbuilder_write_uint8(tvb,
				(types == NULL) ? GG_USER_NORMAL : types[i]);

			/* Oryginalny klient wysyła maksymalnie 2048 bajtów 
			 * danych w każdym pakiecie tego typu.
			 */
			if (gg_tvbuilder_get_size(tvb) > 2048) {
				gg_tvbuilder_strip(tvb, prev_size);
				break;
			}
			i++;
		}

		if (!gg_tvbuilder_send(tvb, (i < count) ?
			GG_NOTIFY105_FIRST : GG_NOTIFY105_LAST)) {
			return -1;
		}
	}

	return 0;
}

Project: libgadu
File: libgadu.c

int gg_send_packet(struct gg_session *sess, int type, ...)
{
	struct gg_header *h;
	char *tmp;
	unsigned int tmp_length;
	void *payload;
	unsigned int payload_length;
	va_list ap;
	int res;

	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...);\n", sess, type);

	tmp_length = sizeof(struct gg_header);

	if (!(tmp = malloc(tmp_length))) {
		gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() not enough memory for packet header\n");
		return -1;
	}

	va_start(ap, type);

	payload = va_arg(ap, void *);

	while (payload) {
		char *tmp2;

		payload_length = va_arg(ap, unsigned int);

		if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) {
			gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() not enough memory for payload\n");
			free(tmp);
			va_end(ap);
			return -1;
		}

		tmp = tmp2;

		memcpy(tmp + tmp_length, payload, payload_length);
		tmp_length += payload_length;

		payload = va_arg(ap, void *);
	}

	va_end(ap);

	h = (struct gg_header*) tmp;
	h->type = gg_fix32(type);
	h->length = gg_fix32(tmp_length - sizeof(struct gg_header));

	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet(type=0x%.2x, length=%d)\n", gg_fix32(h->type), gg_fix32(h->length));
	gg_debug_dump(sess, GG_DEBUG_DUMP, tmp, tmp_length);

	res = gg_write(sess, tmp, tmp_length);

	free(tmp);

	if (res == -1) {
		gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno));
		return -1;
	}

	if (sess->async)
		gg_debug_session(sess, GG_DEBUG_NET, "// gg_send_packet() partial write(), %d sent, %d left, %d total left\n", res, tmp_length - res, sess->send_left);

	if (sess->send_buf)
		sess->check |= GG_CHECK_WRITE;

	return 0;
}

Project: libgadu
File: libgadu.c

int gg_chat_create(struct gg_session *gs)
{
	struct gg_chat_create pkt;
	int seq = ++gs->seq;

	pkt.seq = gg_fix32(seq);
	pkt.dummy = 0;

	if (gg_send_packet(gs, GG_CHAT_CREATE, &pkt, sizeof(pkt), NULL) == -1)
		return -1;

	return seq;
}

Project: libgadu
File: libgadu.c

int gg_chat_leave(struct gg_session *gs, uint64_t id)
{
	struct gg_chat_leave pkt;
	int seq = ++gs->seq;

	pkt.id = gg_fix64(id);
	pkt.seq = gg_fix32(seq);

	if (gg_send_packet(gs, GG_CHAT_LEAVE, &pkt, sizeof(pkt), NULL) == -1)
		return -1;

	return seq;
}