Mercurial > pidgin
diff libgaim/protocols/yahoo/ycht.c @ 14192:60b1bc8dbf37
[gaim-migrate @ 16863]
Renamed 'core' to 'libgaim'
committer: Tailor Script <tailor@pidgin.im>
author | Evan Schoenberg <evan.s@dreskin.net> |
---|---|
date | Sat, 19 Aug 2006 01:50:10 +0000 |
parents | |
children | b6047532826b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgaim/protocols/yahoo/ycht.c Sat Aug 19 01:50:10 2006 +0000 @@ -0,0 +1,649 @@ +/** + * @file ycht.c The Yahoo! protocol plugin, YCHT protocol stuff. + * + * gaim + * + * Copyright (C) 2004 Timothy Ringenbach <omarvo@hotmail.com> + * Liberal amounts of code borrowed from the rest of the Yahoo! prpl. + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <string.h> + +#include "internal.h" +#include "prpl.h" +#include "notify.h" +#include "account.h" +#include "proxy.h" +#include "debug.h" +#include "conversation.h" +#include "util.h" + +#include "yahoo.h" +#include "yahoo_packet.h" +#include "ycht.h" +#include "yahoochat.h" + +/* + * dword: YCHT + * dword: 0x000000AE + * dword: service + * word: status + * word: size + */ +#define YAHOO_CHAT_ID (1) +/************************************************************************************ + * Functions to process various kinds of packets. + ************************************************************************************/ +static void ycht_process_login(YchtConn *ycht, YchtPkt *pkt) +{ + GaimConnection *gc = ycht->gc; + struct yahoo_data *yd = gc->proto_data; + + if (ycht->logged_in) + return; + + yd->chat_online = TRUE; + ycht->logged_in = TRUE; + + if (ycht->room) + ycht_chat_join(ycht, ycht->room); +} + +static void ycht_process_logout(YchtConn *ycht, YchtPkt *pkt) +{ + GaimConnection *gc = ycht->gc; + struct yahoo_data *yd = gc->proto_data; + + yd->chat_online = FALSE; + ycht->logged_in = FALSE; +} + +static void ycht_process_chatjoin(YchtConn *ycht, YchtPkt *pkt) +{ + char *room, *topic; + GaimConnection *gc = ycht->gc; + GaimConversation *c = NULL; + gboolean new_room = FALSE; + char **members; + int i; + + room = g_list_nth_data(pkt->data, 0); + topic = g_list_nth_data(pkt->data, 1); + if (!g_list_nth_data(pkt->data, 4)) + return; + if (!room) + return; + + members = g_strsplit(g_list_nth_data(pkt->data, 4), "\001", 0); + for (i = 0; members[i]; i++) { + char *tmp = strchr(members[i], '\002'); + if (tmp) + *tmp = '\0'; + } + + if (g_list_length(pkt->data) > 5) + new_room = TRUE; + + if (new_room && ycht->changing_rooms) { + serv_got_chat_left(gc, YAHOO_CHAT_ID); + ycht->changing_rooms = FALSE; + c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room); + } else { + c = gaim_find_chat(gc, YAHOO_CHAT_ID); + } + + if (topic) + gaim_conv_chat_set_topic(GAIM_CONV_CHAT(c), NULL, topic); + + for (i = 0; members[i]; i++) { + if (new_room) { + /*if (!strcmp(members[i], gaim_connection_get_display_name(ycht->gc))) + continue;*/ + gaim_conv_chat_add_user(GAIM_CONV_CHAT(c), members[i], NULL, GAIM_CBFLAGS_NONE, TRUE); + } else { + yahoo_chat_add_user(GAIM_CONV_CHAT(c), members[i], NULL); + } + } + + g_strfreev(members); +} + +static void ycht_process_chatpart(YchtConn *ycht, YchtPkt *pkt) +{ + char *room, *who; + + room = g_list_nth_data(pkt->data, 0); + who = g_list_nth_data(pkt->data, 1); + + if (who && room) { + GaimConversation *c = gaim_find_chat(ycht->gc, YAHOO_CHAT_ID); + if (c && !gaim_utf8_strcasecmp(gaim_conversation_get_name(c), room)) + gaim_conv_chat_remove_user(GAIM_CONV_CHAT(c), who, NULL); + + } +} + +static void ycht_progress_chatmsg(YchtConn *ycht, YchtPkt *pkt) +{ + char *who, *what, *msg; + GaimConversation *c; + GaimConnection *gc = ycht->gc; + + who = g_list_nth_data(pkt->data, 1); + what = g_list_nth_data(pkt->data, 2); + + if (!who || !what) + return; + + c = gaim_find_chat(gc, YAHOO_CHAT_ID); + if (!c) + return; + + msg = yahoo_string_decode(gc, what, 1); + what = yahoo_codes_to_html(msg); + g_free(msg); + + if (pkt->service == YCHT_SERVICE_CHATMSG_EMOTE) { + char *tmp = g_strdup_printf("/me %s", what); + g_free(what); + what = tmp; + } + + serv_got_chat_in(gc, YAHOO_CHAT_ID, who, 0, what, time(NULL)); + g_free(what); +} + +static void ycht_progress_online_friends(YchtConn *ycht, YchtPkt *pkt) +{ +#if 0 + GaimConnection *gc = ycht->gc; + struct yahoo_data *yd = gc->proto_data; + + if (ycht->logged_in) + return; + + yd->chat_online = TRUE; + ycht->logged_in = TRUE; + + if (ycht->room) + ycht_chat_join(ycht, ycht->room); +#endif +} + +/***************************************************************************** + * Functions dealing with YCHT packets and their contents directly. + *****************************************************************************/ +static void ycht_packet_dump(const guchar *data, int len) +{ +#ifdef YAHOO_YCHT_DEBUG + int i; + + gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); + + for (i = 0; i + 1 < len; i += 2) { + if ((i % 16 == 0) && i) { + gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); + gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); + } + + gaim_debug(GAIM_DEBUG_MISC, NULL, "%02hhx%02hhx ", data[i], data[i + 1]); + } + if (i < len) + gaim_debug(GAIM_DEBUG_MISC, NULL, "%02hhx", data[i]); + + gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); + gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); + + for (i = 0; i < len; i++) { + if ((i % 16 == 0) && i) { + gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); + gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); + } + + if (g_ascii_isprint(data[i])) + gaim_debug(GAIM_DEBUG_MISC, NULL, "%c ", data[i]); + else + gaim_debug(GAIM_DEBUG_MISC, NULL, ". "); + } + + gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); +#endif +} + +static YchtPkt *ycht_packet_new(guint version, guint service, int status) +{ + YchtPkt *ret; + + ret = g_new0(YchtPkt, 1); + + ret->version = version; + ret->service = service; + ret->status = status; + + return ret; +} + +static void ycht_packet_append(YchtPkt *pkt, const char *str) +{ + g_return_if_fail(pkt != NULL); + g_return_if_fail(str != NULL); + + pkt->data = g_list_append(pkt->data, g_strdup(str)); +} + +static int ycht_packet_length(YchtPkt *pkt) +{ + int ret; + GList *l; + + ret = YCHT_HEADER_LEN; + + for (l = pkt->data; l; l = l->next) { + ret += strlen(l->data); + if (l->next) + ret += strlen(YCHT_SEP); + } + + return ret; +} + +static void ycht_packet_send_write_cb(gpointer data, gint source, GaimInputCondition cond) +{ + YchtConn *ycht = data; + int ret, writelen; + + writelen = gaim_circ_buffer_get_max_read(ycht->txbuf); + + if (writelen == 0) { + gaim_input_remove(ycht->tx_handler); + ycht->tx_handler = 0; + return; + } + + ret = write(ycht->fd, ycht->txbuf->outptr, writelen); + + if (ret < 0 && errno == EAGAIN) + return; + else if (ret <= 0) { + /* TODO: error handling */ +/* + gaim_connection_error(gaim_account_get_connection(irc->account), + _("Server has disconnected")); +*/ + return; + } + + gaim_circ_buffer_mark_read(ycht->txbuf, ret); + +} + +static void ycht_packet_send(YchtConn *ycht, YchtPkt *pkt) +{ + int len, pos, written; + char *buf; + GList *l; + + g_return_if_fail(ycht != NULL); + g_return_if_fail(pkt != NULL); + g_return_if_fail(ycht->fd != -1); + + pos = 0; + len = ycht_packet_length(pkt); + buf = g_malloc(len); + + memcpy(buf + pos, "YCHT", 4); pos += 4; + pos += yahoo_put32(buf + pos, pkt->version); + pos += yahoo_put32(buf + pos, pkt->service); + pos += yahoo_put16(buf + pos, pkt->status); + pos += yahoo_put16(buf + pos, len - YCHT_HEADER_LEN); + + for (l = pkt->data; l; l = l->next) { + int slen = strlen(l->data); + memcpy(buf + pos, l->data, slen); pos += slen; + + if (l->next) { + memcpy(buf + pos, YCHT_SEP, strlen(YCHT_SEP)); + pos += strlen(YCHT_SEP); + } + } + + if (!ycht->tx_handler) + written = write(ycht->fd, buf, len); + else { + written = -1; + errno = EAGAIN; + } + + if (written < 0 && errno == EAGAIN) + written = 0; + else if (written <= 0) { + /* TODO: Error handling (was none before NBIO changes) */ + written = 0; + } + + if (written < len) { + if (!ycht->tx_handler) + ycht->tx_handler = gaim_input_add(ycht->fd, + GAIM_INPUT_WRITE, ycht_packet_send_write_cb, + ycht); + gaim_circ_buffer_append(ycht->txbuf, buf + written, + len - written); + } + + g_free(buf); +} + +static void ycht_packet_read(YchtPkt *pkt, const char *buf, int len) +{ + const char *pos = buf; + const char *needle; + char *tmp, *tmp2; + int i = 0; + + while (len > 0 && (needle = g_strstr_len(pos, len, YCHT_SEP))) { + tmp = g_strndup(pos, needle - pos); + pkt->data = g_list_append(pkt->data, tmp); + len -= needle - pos + strlen(YCHT_SEP); + pos = needle + strlen(YCHT_SEP); + tmp2 = g_strescape(tmp, NULL); + gaim_debug_misc("yahoo", "Data[%d]:\t%s\n", i++, tmp2); + g_free(tmp2); + } + + if (len) { + tmp = g_strndup(pos, len); + pkt->data = g_list_append(pkt->data, tmp); + tmp2 = g_strescape(tmp, NULL); + gaim_debug_misc("yahoo", "Data[%d]:\t%s\n", i, tmp2); + g_free(tmp2); + }; + + gaim_debug_misc("yahoo", "--==End of incoming YCHT packet==--\n"); +} + +static void ycht_packet_process(YchtConn *ycht, YchtPkt *pkt) +{ + if (pkt->data && !strncmp(pkt->data->data, "*** Danger Will Robinson!!!", strlen("*** Danger Will Robinson!!!"))) + return; + + switch (pkt->service) { + case YCHT_SERVICE_LOGIN: + ycht_process_login(ycht, pkt); + break; + case YCHT_SERVICE_LOGOUT: + ycht_process_logout(ycht, pkt); + break; + case YCHT_SERVICE_CHATJOIN: + ycht_process_chatjoin(ycht, pkt); + break; + case YCHT_SERVICE_CHATPART: + ycht_process_chatpart(ycht, pkt); + break; + case YCHT_SERVICE_CHATMSG: + case YCHT_SERVICE_CHATMSG_EMOTE: + ycht_progress_chatmsg(ycht, pkt); + break; + case YCHT_SERVICE_ONLINE_FRIENDS: + ycht_progress_online_friends(ycht, pkt); + break; + default: + gaim_debug_warning("yahoo", "YCHT: warning, unhandled service 0x%02x\n", pkt->service); + } +} + +static void ycht_packet_free(YchtPkt *pkt) +{ + GList *l; + + g_return_if_fail(pkt != NULL); + + for (l = pkt->data; l; l = l->next) + g_free(l->data); + g_list_free(pkt->data); + g_free(pkt); +} + +/************************************************************************************ + * Functions dealing with connecting and disconnecting and reading data into YchtPkt + * structs, and all that stuff. + ************************************************************************************/ + +void ycht_connection_close(YchtConn *ycht) +{ + struct yahoo_data *yd = ycht->gc->proto_data; + + if (yd) { + yd->ycht = NULL; + yd->chat_online = FALSE; + } + + if (ycht->fd > 0) + close(ycht->fd); + if (ycht->inpa) + gaim_input_remove(ycht->inpa); + + if (ycht->tx_handler) + gaim_input_remove(ycht->tx_handler); + + gaim_circ_buffer_destroy(ycht->txbuf); + + g_free(ycht->rxqueue); + + g_free(ycht); +} + +static void ycht_connection_error(YchtConn *ycht, const gchar *error) +{ + + gaim_notify_info(ycht->gc, NULL, _("Connection problem with the YCHT server."), error); + ycht_connection_close(ycht); +} + +static void ycht_pending(gpointer data, gint source, GaimInputCondition cond) +{ + YchtConn *ycht = data; + char buf[1024]; + int len; + + len = read(ycht->fd, buf, sizeof(buf)); + + if (len < 0 && errno == EAGAIN) + return; + + if (len <= 0) { + ycht_connection_error(ycht, _("Unable to read")); + return; + } + + ycht->rxqueue = g_realloc(ycht->rxqueue, len + ycht->rxlen); + memcpy(ycht->rxqueue + ycht->rxlen, buf, len); + ycht->rxlen += len; + + while (1) { + YchtPkt *pkt; + int pos = 0; + int pktlen; + guint service; + guint version; + gint status; + + if (ycht->rxlen < YCHT_HEADER_LEN) + return; + + if (strncmp("YCHT", (char *)ycht->rxqueue, 4) != 0) + gaim_debug_error("yahoo", "YCHT: protocol error.\n"); + + pos += 4; /* YCHT */ + + version = yahoo_get32(ycht->rxqueue + pos); pos += 4; + service = yahoo_get32(ycht->rxqueue + pos); pos += 4; + status = yahoo_get16(ycht->rxqueue + pos); pos += 2; + pktlen = yahoo_get16(ycht->rxqueue + pos); pos += 2; + gaim_debug(GAIM_DEBUG_MISC, "yahoo", + "ycht: %d bytes to read, rxlen is %d\n", pktlen, ycht->rxlen); + + if (ycht->rxlen < (YCHT_HEADER_LEN + pktlen)) + return; + + gaim_debug_misc("yahoo", "--==Incoming YCHT packet==--\n"); + gaim_debug(GAIM_DEBUG_MISC, "yahoo", + "YCHT Service: 0x%02x Version: 0x%02x Status: 0x%02x\n", + service, version, status); + ycht_packet_dump(ycht->rxqueue, YCHT_HEADER_LEN + pktlen); + + pkt = ycht_packet_new(version, service, status); + ycht_packet_read(pkt, (char *)ycht->rxqueue + pos, pktlen); + + ycht->rxlen -= YCHT_HEADER_LEN + pktlen; + if (ycht->rxlen) { + guchar *tmp = g_memdup(ycht->rxqueue + YCHT_HEADER_LEN + pktlen, ycht->rxlen); + g_free(ycht->rxqueue); + ycht->rxqueue = tmp; + } else { + g_free(ycht->rxqueue); + ycht->rxqueue = NULL; + } + + ycht_packet_process(ycht, pkt); + + ycht_packet_free(pkt); + } +} + +static void ycht_got_connected(gpointer data, gint source, const gchar *error_message) +{ + YchtConn *ycht = data; + GaimConnection *gc = ycht->gc; + struct yahoo_data *yd = gc->proto_data; + YchtPkt *pkt; + char *buf; + + if (source < 0) { + ycht_connection_error(ycht, _("Unable to connect.")); + return; + } + + ycht->fd = source; + + pkt = ycht_packet_new(YCHT_VERSION, YCHT_SERVICE_LOGIN, 0); + + buf = g_strdup_printf("%s\001Y=%s; T=%s", gaim_connection_get_display_name(gc), yd->cookie_y, yd->cookie_t); + ycht_packet_append(pkt, buf); + g_free(buf); + + ycht_packet_send(ycht, pkt); + + ycht_packet_free(pkt); + + ycht->inpa = gaim_input_add(ycht->fd, GAIM_INPUT_READ, ycht_pending, ycht); +} + +void ycht_connection_open(GaimConnection *gc) +{ + YchtConn *ycht; + struct yahoo_data *yd = gc->proto_data; + GaimAccount *account = gaim_connection_get_account(gc); + + ycht = g_new0(YchtConn, 1); + ycht->gc = gc; + ycht->fd = -1; + + yd->ycht = ycht; + + if (gaim_proxy_connect(account, + gaim_account_get_string(account, "ycht-server", YAHOO_YCHT_HOST), + gaim_account_get_int(account, "ycht-port", YAHOO_YCHT_PORT), + ycht_got_connected, ycht) == NULL) + { + ycht_connection_error(ycht, _("Connection problem")); + return; + } +} + +/******************************************************************************************* + * These are functions called because the user did something. + *******************************************************************************************/ + +void ycht_chat_join(YchtConn *ycht, const char *room) +{ + YchtPkt *pkt; + char *tmp; + + tmp = g_strdup(room); + g_free(ycht->room); + ycht->room = tmp; + + if (!ycht->logged_in) + return; + + ycht->changing_rooms = TRUE; + pkt = ycht_packet_new(YCHT_VERSION, YCHT_SERVICE_CHATJOIN, 0); + ycht_packet_append(pkt, ycht->room); + ycht_packet_send(ycht, pkt); + ycht_packet_free(pkt); +} + +int ycht_chat_send(YchtConn *ycht, const char *room, const char *what) +{ + YchtPkt *pkt; + char *msg1, *msg2, *buf; + + if (strcmp(room, ycht->room)) + gaim_debug_warning("yahoo", "uhoh, sending to the wrong room!\n"); + + pkt = ycht_packet_new(YCHT_VERSION, YCHT_SERVICE_CHATMSG, 0); + + msg1 = yahoo_html_to_codes(what); + msg2 = yahoo_string_encode(ycht->gc, msg1, NULL); + g_free(msg1); + + buf = g_strdup_printf("%s\001%s", ycht->room, msg2); + ycht_packet_append(pkt, buf); + g_free(msg2); + g_free(buf); + + ycht_packet_send(ycht, pkt); + ycht_packet_free(pkt); + return 1; +} + +void ycht_chat_leave(YchtConn *ycht, const char *room, gboolean logout) +{ + if (logout) + ycht_connection_close(ycht); +} + +void ycht_chat_send_invite(YchtConn *ycht, const char *room, const char *buddy, const char *msg) +{ +} + +void ycht_chat_goto_user(YchtConn *ycht, const char *name) +{ +} + +void ycht_chat_send_keepalive(YchtConn *ycht) +{ + YchtPkt *pkt; + + pkt = ycht_packet_new(YCHT_VERSION, YCHT_SERVICE_PING, 0); + ycht_packet_send(ycht, pkt); + ycht_packet_free(pkt); +}