diff src/protocols/yahoo/yahoochat.c @ 8113:d60272410bd5

[gaim-migrate @ 8817] Tim 'marv' Ringenbach wrote us a wonderful core/ui split chat room list thing-a-ma-jig. I've taken the liberty of adding jabber support to it as well. committer: Tailor Script <tailor@pidgin.im>
author Nathan Walp <nwalp@pidgin.im>
date Thu, 15 Jan 2004 22:20:29 +0000
parents fa6395637e2c
children 1f56ea865926
line wrap: on
line diff
--- a/src/protocols/yahoo/yahoochat.c	Thu Jan 15 17:08:38 2004 +0000
+++ b/src/protocols/yahoo/yahoochat.c	Thu Jan 15 22:20:29 2004 +0000
@@ -1004,3 +1004,403 @@
 	}
 }
 
+
+struct yahoo_roomlist {
+	int fd;
+	int inpa;
+	guchar *rxqueue;
+	int rxlen;
+	gboolean started;
+	char *path;
+	char *host;
+	GaimRoomlist *list;
+	GaimRoomlistRoom *cat;
+	GaimRoomlistRoom *ucat;
+	GMarkupParseContext *parse;
+
+};
+
+static void yahoo_roomlist_destroy(struct yahoo_roomlist *yrl)
+{
+	if (yrl->inpa)
+		gaim_input_remove(yrl->inpa);
+	if (yrl->rxqueue)
+		g_free(yrl->rxqueue);
+	if (yrl->path)
+		g_free(yrl->path);
+	if (yrl->host)
+		g_free(yrl->host);
+	if (yrl->parse)
+		g_markup_parse_context_free(yrl->parse);
+}
+
+enum yahoo_room_type {
+	yrt_yahoo,
+	yrt_user,
+};
+
+struct yahoo_chatxml_state {
+	GaimRoomlist *list;
+	struct yahoo_roomlist *yrl;
+	GQueue *q;
+	struct {
+		enum yahoo_room_type type;
+		char *name;
+		char *topic;
+		char *id;
+		int users, voices, webcams;
+	} room;
+};
+
+struct yahoo_lobby {
+	int count, users, voices, webcams;
+};
+
+static struct yahoo_chatxml_state *yahoo_chatxml_state_new(GaimRoomlist *list, struct yahoo_roomlist *yrl)
+{
+	struct yahoo_chatxml_state *s;
+
+	s = g_new0(struct yahoo_chatxml_state, 1);
+
+	s->list = list;
+	s->yrl = yrl;
+	s->q = g_queue_new();
+
+	return s;
+}
+
+static void yahoo_chatxml_state_destroy(struct yahoo_chatxml_state *s)
+{
+	g_queue_free(s->q);
+	if (s->room.name)
+		g_free(s->room.name);
+	if (s->room.topic)
+		g_free(s->room.topic);
+	if (s->room.id)
+		g_free(s->room.id);
+	g_free(s);
+}
+
+static void yahoo_chatlist_start_element(GMarkupParseContext *context, const gchar *ename,
+                                  const gchar **anames, const gchar **avalues,
+                                  gpointer user_data, GError **error)
+{
+	struct yahoo_chatxml_state *s = user_data;
+	GaimRoomlist *list = s->list;
+	GaimRoomlistRoom *r;
+	GaimRoomlistRoom *parent;
+	int i;
+
+	if (!strcmp(ename, "category")) {
+		const gchar *name = NULL, *id = NULL;
+
+		for (i = 0; anames[i]; i++) {
+			if (!strcmp(anames[i], "id"))
+				id = avalues[i];
+			if (!strcmp(anames[i], "name"))
+				name = avalues[i];
+		}
+		if (!name || !id)
+			return;
+
+		parent = g_queue_peek_head(s->q);
+		r = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_CATAGORY, name, parent);
+		gaim_roomlist_room_add_field(list, r, (gpointer)name);
+		gaim_roomlist_room_add_field(list, r, (gpointer)id);
+		gaim_roomlist_room_add(list, r);
+		g_queue_push_head(s->q, r);
+	} else if (!strcmp(ename, "room")) {
+		s->room.users = s->room.voices = s->room.webcams = 0;
+
+		for (i = 0; anames[i]; i++) {
+			if (!strcmp(anames[i], "id")) {
+				if (s->room.id)
+					g_free(s->room.id);
+				s->room.id = g_strdup(avalues[i]);
+			} else if (!strcmp(anames[i], "name")) {
+				if (s->room.name)
+					g_free(s->room.name);
+				s->room.name = g_strdup(avalues[i]);
+			} else if (!strcmp(anames[i], "topic")) {
+				if (s->room.topic)
+					g_free(s->room.topic);
+				s->room.topic = g_strdup(avalues[i]);
+			} else if (!strcmp(anames[i], "type")) {
+				if (!strcmp("yahoo", avalues[i]))
+					s->room.type = yrt_yahoo;
+				else
+					s->room.type = yrt_user;
+			}
+		}
+
+	} else if (!strcmp(ename, "lobby")) {
+		struct yahoo_lobby *lob = g_new0(struct yahoo_lobby, 1);
+
+		for (i = 0; anames[i]; i++) {
+			if (!strcmp(anames[i], "count")) {
+				lob->count = strtol(avalues[i], NULL, 10);
+			} else if (!strcmp(anames[i], "users")) {
+				s->room.users += lob->users = strtol(avalues[i], NULL, 10);
+			} else if (!strcmp(anames[i], "voices")) {
+				s->room.voices += lob->voices = strtol(avalues[i], NULL, 10);
+			} else if (!strcmp(anames[i], "webcams")) {
+				s->room.webcams += lob->webcams = strtol(avalues[i], NULL, 10);
+			}
+		}
+
+		g_queue_push_head(s->q, lob);
+	}
+
+}
+
+static void yahoo_chatlist_end_element(GMarkupParseContext *context, const gchar *ename,
+                                       gpointer user_data, GError **error)
+{
+	struct yahoo_chatxml_state *s = user_data;
+
+	if (!strcmp(ename, "category")) {
+		g_queue_pop_head(s->q);
+	} else if (!strcmp(ename, "room")) {
+		struct yahoo_lobby *lob;
+		GaimRoomlistRoom *r, *l;
+
+		if (s->room.type == yrt_yahoo)
+			r = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_CATAGORY|GAIM_ROOMLIST_ROOMTYPE_ROOM,
+		                                   s->room.name, s->yrl->cat);
+		else
+			r = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_CATAGORY|GAIM_ROOMLIST_ROOMTYPE_ROOM,
+		                                   s->room.name, s->yrl->ucat);
+
+		gaim_roomlist_room_add_field(s->list, r, s->room.name);
+		gaim_roomlist_room_add_field(s->list, r, s->room.id);
+		gaim_roomlist_room_add_field(s->list, r, GINT_TO_POINTER(s->room.users));
+		gaim_roomlist_room_add_field(s->list, r, GINT_TO_POINTER(s->room.voices));
+		gaim_roomlist_room_add_field(s->list, r, GINT_TO_POINTER(s->room.webcams));
+		gaim_roomlist_room_add_field(s->list, r, s->room.topic);
+		gaim_roomlist_room_add(s->list, r);
+
+		while ((lob = g_queue_pop_head(s->q))) {
+			char *name = g_strdup_printf("%s:%d", s->room.name, lob->count);
+			l = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_ROOM, name, r);
+
+			gaim_roomlist_room_add_field(s->list, l, name);
+			gaim_roomlist_room_add_field(s->list, l, s->room.id);
+			gaim_roomlist_room_add_field(s->list, l, GINT_TO_POINTER(lob->users));
+			gaim_roomlist_room_add_field(s->list, l, GINT_TO_POINTER(lob->voices));
+			gaim_roomlist_room_add_field(s->list, l, GINT_TO_POINTER(lob->webcams));
+			gaim_roomlist_room_add_field(s->list, l, s->room.topic);
+			gaim_roomlist_room_add(s->list, l);
+
+			g_free(name);
+			g_free(lob);
+		}
+
+	}
+
+}
+
+static GMarkupParser parser = {
+	yahoo_chatlist_start_element,
+	yahoo_chatlist_end_element,
+	NULL,
+	NULL,
+	NULL
+};
+
+static void yahoo_roomlist_cleanup(GaimRoomlist *list, struct yahoo_roomlist *yrl)
+{
+	gaim_roomlist_set_in_progress(list, FALSE);
+
+	if (yrl) {
+		list->proto_data = g_list_remove(list->proto_data, yrl);
+		yahoo_roomlist_destroy(yrl);
+	}
+
+	gaim_roomlist_unref(list);
+}
+
+static void yahoo_roomlist_pending(gpointer data, gint source, GaimInputCondition cond)
+{
+	struct yahoo_roomlist *yrl = data;
+	GaimRoomlist *list = yrl->list;
+	char buf[1024];
+	int len;
+	guchar *start;
+	struct yahoo_chatxml_state *s;
+
+	len = read(yrl->fd, buf, sizeof(buf));
+
+	if (len <= 0) {
+		if (yrl->parse)
+			g_markup_parse_context_end_parse(yrl->parse, NULL);
+		yahoo_roomlist_cleanup(list, yrl);
+		return;
+	}
+
+
+	yrl->rxqueue = g_realloc(yrl->rxqueue, len + yrl->rxlen);
+	memcpy(yrl->rxqueue + yrl->rxlen, buf, len);
+	yrl->rxlen += len;
+
+	if (!yrl->started) {
+		yrl->started = TRUE;
+		start = g_strstr_len(yrl->rxqueue, yrl->rxlen, "\r\n\r\n");
+		if (!start || (start - yrl->rxqueue + 4) >= yrl->rxlen)
+			return;
+		start += 4;
+	} else {
+		start = yrl->rxqueue;
+	}
+
+	if (yrl->parse == NULL) {
+		s = yahoo_chatxml_state_new(list, yrl);
+		yrl->parse = g_markup_parse_context_new(&parser, 0, s,
+		             (GDestroyNotify)yahoo_chatxml_state_destroy);
+	}
+
+	if (!g_markup_parse_context_parse(yrl->parse, start, (yrl->rxlen - (start - yrl->rxqueue)), NULL)) {
+
+		yahoo_roomlist_cleanup(list, yrl);
+		return;
+	}
+
+	yrl->rxlen = 0;
+}
+
+static void yahoo_roomlist_got_connected(gpointer data, gint source, GaimInputCondition cond)
+{
+	struct yahoo_roomlist *yrl = data;
+	GaimRoomlist *list = yrl->list;
+	char *buf, *cookie;
+	struct yahoo_data *yd = gaim_account_get_connection(list->account)->proto_data;
+
+	if (source < 0) {
+		gaim_notify_error(gaim_account_get_connection(list->account), NULL, _("Unable to connect"), _("Fetching the room list failed."));
+		yahoo_roomlist_cleanup(list, yrl);
+		return;
+	}
+
+	yrl->fd = source;
+
+	cookie = g_strdup_printf("Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
+	buf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\nCookie: %s\r\n\r\n", yrl->path, yrl->host, cookie);
+	write(yrl->fd, buf, strlen(buf));
+	g_free(cookie);
+	g_free(buf);
+	yrl->inpa = gaim_input_add(yrl->fd, GAIM_INPUT_READ, yahoo_roomlist_pending, yrl);
+
+}
+
+GaimRoomlist *yahoo_roomlist_get_list(GaimConnection *gc)
+{
+	struct yahoo_roomlist *yrl;
+	GaimRoomlist *rl;
+	char *url;
+	GList *fields = NULL;
+	GaimRoomlistField *f;
+
+	url = g_strdup_printf("%s?chatcat=0",
+	                      gaim_account_get_string(
+	                      gaim_connection_get_account(gc),
+	                      "room_list", YAHOO_ROOMLIST_URL));
+
+	yrl = g_new0(struct yahoo_roomlist, 1);
+	rl = gaim_roomlist_new(gaim_connection_get_account(gc));
+	yrl->list = rl;
+
+	gaim_url_parse(url, &(yrl->host), NULL, &(yrl->path));
+	g_free(url);
+
+	f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, "", "room", TRUE);
+	fields = g_list_append(fields, f);
+
+	f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, "", "id", TRUE);
+	fields = g_list_append(fields, f);
+
+	f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_INT, _("Users"), "users", FALSE);
+	fields = g_list_append(fields, f);
+
+	f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_INT, _("Voices"), "voices", FALSE);
+	fields = g_list_append(fields, f);
+
+	f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_INT, _("Webcams"), "webcams", FALSE);
+	fields = g_list_append(fields, f);
+
+	f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, _("Topic"), "topic", FALSE);
+	fields = g_list_append(fields, f);
+
+	gaim_roomlist_set_fields(rl, fields);
+
+	if (gaim_proxy_connect(gaim_connection_get_account(gc),
+	                       yrl->host, 80, yahoo_roomlist_got_connected, yrl) != 0)
+	{
+		gaim_notify_error(gc, NULL, _("Connection problem"), _("Unable to fetch room list."));
+		yahoo_roomlist_cleanup(rl, yrl);
+		return NULL;
+	}
+
+	rl->proto_data = g_list_append(rl->proto_data, yrl);
+
+	gaim_roomlist_set_in_progress(rl, TRUE);
+	return rl;
+}
+
+void yahoo_roomlist_cancel(GaimRoomlist *list)
+{
+	GList *l, *k;
+
+	k = l = list->proto_data;
+	list->proto_data = NULL;
+
+	gaim_roomlist_set_in_progress(list, FALSE);
+
+	for (; l; l = l->next) {
+		yahoo_roomlist_destroy(l->data);
+		gaim_roomlist_unref(l->data);
+	}
+	g_list_free(k);
+}
+
+void yahoo_roomlist_expand_catagory(GaimRoomlist *list, GaimRoomlistRoom *catagory)
+{
+	struct yahoo_roomlist *yrl;
+	char *url;
+	char *id;
+
+	if (catagory->type != GAIM_ROOMLIST_ROOMTYPE_CATAGORY)
+		return;
+
+	if (!(id = g_list_nth_data(catagory->fields, 1))) {
+		gaim_roomlist_set_in_progress(list, FALSE);
+		return;
+	}
+
+	url = g_strdup_printf("%s?chatroom_%s=0",
+	                      gaim_account_get_string(
+	                      list->account,
+	                      "room_list", YAHOO_ROOMLIST_URL), id);
+
+	yrl = g_new0(struct yahoo_roomlist, 1);
+	yrl->list = list;
+	yrl->cat = catagory;
+	list->proto_data = g_list_append(list->proto_data, yrl);
+
+	gaim_url_parse(url, &(yrl->host), NULL, &(yrl->path));
+	g_free(url);
+
+	yrl->ucat = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_CATAGORY, _("User Rooms"), yrl->cat);
+	gaim_roomlist_room_add(list, yrl->ucat);
+
+	if (gaim_proxy_connect(list->account,
+	                       yrl->host, 80, yahoo_roomlist_got_connected, yrl) != 0)
+	{
+		gaim_notify_error(gaim_account_get_connection(list->account),
+		                  NULL, _("Connection problem"), _("Unable to fetch room list."));
+		yahoo_roomlist_cleanup(list, yrl);
+		return;
+	}
+
+	gaim_roomlist_set_in_progress(list, TRUE);
+	gaim_roomlist_ref(list);
+}
+