diff libpurple/protocols/gg/lib/pubdir50.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 7ade887fd3f6
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/pubdir50.c	Sat Jan 20 02:32:10 2007 +0000
@@ -0,0 +1,467 @@
+/* $Id: pubdir50.c 16856 2006-08-19 01:13:25Z evands $ */
+
+/*
+ *  (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "libgadu.h"
+
+/*
+ * gg_pubdir50_new()
+ *
+ * tworzy nową zmienną typu gg_pubdir50_t.
+ *
+ * zaalokowana zmienna lub NULL w przypadku braku pamięci.
+ */
+gg_pubdir50_t gg_pubdir50_new(int type)
+{
+	gg_pubdir50_t res = malloc(sizeof(struct gg_pubdir50_s));
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_new(%d);\n", type);
+
+	if (!res) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_new() out of memory\n");
+		return NULL;
+	}
+
+	memset(res, 0, sizeof(struct gg_pubdir50_s));
+
+	res->type = type;
+
+	return res;
+}
+
+/*
+ * gg_pubdir50_add_n()  // funkcja wewnętrzna
+ *
+ * funkcja dodaje lub zastępuje istniejące pole do zapytania lub odpowiedzi.
+ *
+ *  - req - wskaźnik opisu zapytania,
+ *  - num - numer wyniku (0 dla zapytania),
+ *  - field - nazwa pola,
+ *  - value - wartość pola,
+ *
+ * 0/-1
+ */
+static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value)
+{
+	struct gg_pubdir50_entry *tmp = NULL, *entry;
+	char *dupfield, *dupvalue;
+	int i;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_add_n(%p, %d, \"%s\", \"%s\");\n", req, num, field, value);
+
+	if (!(dupvalue = strdup(value))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
+		return -1;
+	}
+
+	for (i = 0; i < req->entries_count; i++) {
+		if (req->entries[i].num != num || strcmp(req->entries[i].field, field))
+			continue;
+
+		free(req->entries[i].value);
+		req->entries[i].value = dupvalue;
+
+		return 0;
+	}
+		
+	if (!(dupfield = strdup(field))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
+		free(dupvalue);
+		return -1;
+	}
+
+	if (!(tmp = realloc(req->entries, sizeof(struct gg_pubdir50_entry) * (req->entries_count + 1)))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
+		free(dupfield);
+		free(dupvalue);
+		return -1;
+	}
+
+	req->entries = tmp;
+
+	entry = &req->entries[req->entries_count];
+	entry->num = num;
+	entry->field = dupfield;
+	entry->value = dupvalue;
+
+	req->entries_count++;
+
+	return 0;
+}
+
+/*
+ * gg_pubdir50_add()
+ *
+ * funkcja dodaje pole do zapytania.
+ *
+ *  - req - wskaźnik opisu zapytania,
+ *  - field - nazwa pola,
+ *  - value - wartość pola,
+ *
+ * 0/-1
+ */
+int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value)
+{
+	return gg_pubdir50_add_n(req, 0, field, value);
+}
+
+/*
+ * gg_pubdir50_seq_set()
+ *
+ * ustawia numer sekwencyjny zapytania.
+ *
+ *  - req - zapytanie,
+ *  - seq - nowy numer sekwencyjny.
+ *
+ * 0/-1.
+ */
+int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq)
+{
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_seq_set(%p, %d);\n", req, seq);
+	
+	if (!req) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_seq_set() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	req->seq = seq;
+
+	return 0;
+}
+
+/*
+ * gg_pubdir50_free()
+ *
+ * zwalnia pamięć po zapytaniu lub rezultacie szukania użytkownika.
+ *
+ *  - s - zwalniana zmienna,
+ */
+void gg_pubdir50_free(gg_pubdir50_t s)
+{
+	int i;
+
+	if (!s)
+		return;
+	
+	for (i = 0; i < s->entries_count; i++) {
+		free(s->entries[i].field);
+		free(s->entries[i].value);
+	}
+
+	free(s->entries);
+	free(s);
+}
+
+/*
+ * gg_pubdir50()
+ *
+ * wysyła zapytanie katalogu publicznego do serwera.
+ *
+ *  - sess - sesja,
+ *  - req - zapytanie.
+ *
+ * numer sekwencyjny wyszukiwania lub 0 w przypadku błędu.
+ */
+uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
+{
+	int i, size = 5;
+	uint32_t res;
+	char *buf, *p;
+	struct gg_pubdir50_request *r;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
+	
+	if (!sess || !req) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
+		errno = EFAULT;
+		return 0;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
+		errno = ENOTCONN;
+		return 0;
+	}
+
+	for (i = 0; i < req->entries_count; i++) {
+		/* wyszukiwanie bierze tylko pierwszy wpis */
+		if (req->entries[i].num)
+			continue;
+		
+		size += strlen(req->entries[i].field) + 1;
+		size += strlen(req->entries[i].value) + 1;
+	}
+
+	if (!(buf = malloc(size))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
+		return 0;
+	}
+
+	r = (struct gg_pubdir50_request*) buf;
+	res = time(NULL);
+	r->type = req->type;
+	r->seq = (req->seq) ? gg_fix32(req->seq) : gg_fix32(time(NULL));
+	req->seq = gg_fix32(r->seq);
+
+	for (i = 0, p = buf + 5; i < req->entries_count; i++) {
+		if (req->entries[i].num)
+			continue;
+
+		strcpy(p, req->entries[i].field);
+		p += strlen(p) + 1;
+
+		strcpy(p, req->entries[i].value);
+		p += strlen(p) + 1;
+	}
+
+	if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1)
+		res = 0;
+
+	free(buf);
+
+	return res;
+}
+
+/*
+ * gg_pubdir50_handle_reply()  // funkcja wewnętrzna
+ *
+ * analizuje przychodzący pakiet odpowiedzi i zapisuje wynik w struct gg_event.
+ *
+ *  - e - opis zdarzenia
+ *  - packet - zawartość pakietu odpowiedzi
+ *  - length - długość pakietu odpowiedzi
+ *
+ * 0/-1
+ */
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
+{
+	const char *end = packet + length, *p;
+	struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet;
+	gg_pubdir50_t res;
+	int num = 0;
+	
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply(%p, %p, %d);\n", e, packet, length);
+
+	if (!e || !packet) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (length < 5) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() packet too short\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!(res = gg_pubdir50_new(r->type))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() unable to allocate reply\n");
+		return -1;
+	}
+
+	e->event.pubdir50 = res;
+
+	res->seq = gg_fix32(r->seq);
+
+	switch (res->type) {
+		case GG_PUBDIR50_READ:
+			e->type = GG_EVENT_PUBDIR50_READ;
+			break;
+
+		case GG_PUBDIR50_WRITE:
+			e->type = GG_EVENT_PUBDIR50_WRITE;
+			break;
+
+		default:
+			e->type = GG_EVENT_PUBDIR50_SEARCH_REPLY;
+			break;
+	}
+
+	/* brak wyników? */
+	if (length == 5)
+		return 0;
+
+	/* pomiń początek odpowiedzi */
+	p = packet + 5;
+
+	while (p < end) {
+		const char *field, *value;
+
+		field = p;
+
+		/* sprawdź, czy nie mamy podziału na kolejne pole */
+		if (!*field) {
+			num++;
+			field++;
+		}
+
+		value = NULL;
+		
+		for (p = field; p < end; p++) {
+			/* jeśli mamy koniec tekstu... */
+			if (!*p) {
+				/* ...i jeszcze nie mieliśmy wartości pola to
+				 * wiemy, że po tym zerze jest wartość... */
+				if (!value)
+					value = p + 1;
+				else
+					/* ...w przeciwym wypadku koniec
+					 * wartości i możemy wychodzić
+					 * grzecznie z pętli */
+					break;
+			}
+		}
+		
+		/* sprawdźmy, czy pole nie wychodzi poza pakiet, żeby nie
+		 * mieć segfaultów, jeśli serwer przestanie zakańczać pakietów
+		 * przez \0 */
+
+		if (p == end) {
+			gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() premature end of packet\n");
+			goto failure;
+		}
+
+		p++;
+
+		/* jeśli dostaliśmy namier na następne wyniki, to znaczy że
+		 * mamy koniec wyników i nie jest to kolejna osoba. */
+		if (!strcasecmp(field, "nextstart")) {
+			res->next = atoi(value);
+			num--;
+		} else {
+			if (gg_pubdir50_add_n(res, num, field, value) == -1)
+				goto failure;
+		}
+	}	
+
+	res->count = num + 1;
+	
+	return 0;
+
+failure:
+	gg_pubdir50_free(res);
+	return -1;
+}
+
+/*
+ * gg_pubdir50_get()
+ *
+ * pobiera informację z rezultatu wyszukiwania.
+ *
+ *  - res - rezultat wyszukiwania,
+ *  - num - numer odpowiedzi,
+ *  - field - nazwa pola (wielkość liter nie ma znaczenia).
+ *
+ * wartość pola lub NULL, jeśli nie znaleziono.
+ */
+const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field)
+{
+	char *value = NULL;
+	int i;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_get(%p, %d, \"%s\");\n", res, num, field);
+
+	if (!res || num < 0 || !field) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_get() invalid arguments\n");
+		errno = EINVAL;
+		return NULL;
+	}
+
+	for (i = 0; i < res->entries_count; i++) {
+		if (res->entries[i].num == num && !strcasecmp(res->entries[i].field, field)) {
+			value = res->entries[i].value;
+			break;
+		}
+	}
+
+	return value;
+}
+
+/*
+ * gg_pubdir50_count()
+ *
+ * zwraca ilość wyników danego zapytania.
+ *
+ *  - res - odpowiedź
+ *
+ * ilość lub -1 w przypadku błędu.
+ */
+int gg_pubdir50_count(gg_pubdir50_t res)
+{
+	return (!res) ? -1 : res->count;
+}
+
+/*
+ * gg_pubdir50_type()
+ *
+ * zwraca rodzaj zapytania lub odpowiedzi.
+ *
+ *  - res - zapytanie lub odpowiedź
+ *
+ * ilość lub -1 w przypadku błędu.
+ */
+int gg_pubdir50_type(gg_pubdir50_t res)
+{
+	return (!res) ? -1 : res->type;
+}
+
+/*
+ * gg_pubdir50_next()
+ *
+ * zwraca numer, od którego należy rozpocząć kolejne wyszukiwanie, jeśli
+ * zależy nam na kolejnych wynikach.
+ *
+ *  - res - odpowiedź
+ *
+ * numer lub -1 w przypadku błędu.
+ */
+uin_t gg_pubdir50_next(gg_pubdir50_t res)
+{
+	return (!res) ? (unsigned) -1 : res->next;
+}
+
+/*
+ * gg_pubdir50_seq()
+ *
+ * zwraca numer sekwencyjny zapytania lub odpowiedzi.
+ *
+ *  - res - zapytanie lub odpowiedź
+ *
+ * numer lub -1 w przypadku błędu.
+ */
+uint32_t gg_pubdir50_seq(gg_pubdir50_t res)
+{
+	return (!res) ? (unsigned) -1 : res->seq;
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */