Mercurial > pidgin.yaz
diff src/protocols/yahoo/ycht.c @ 9376:3aa848ccf986
[gaim-migrate @ 10184]
*** Danger Will Robinson!!!
committer: Tailor Script <tailor@pidgin.im>
author | Tim Ringenbach <marv@pidgin.im> |
---|---|
date | Thu, 24 Jun 2004 07:08:33 +0000 |
parents | |
children | 3b0c6255033e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/yahoo/ycht.c Thu Jun 24 07:08:33 2004 +0000 @@ -0,0 +1,600 @@ +/** + * @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 "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) { + GList l; + /*if (!strcmp(members[i], gaim_connection_get_display_name(ycht->gc))) + continue;*/ + l.data = members[i]; + l.next = l.prev = NULL; + gaim_conv_chat_add_users(GAIM_CONV_CHAT(c), &l); + } 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 char *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(YchtConn *ycht, YchtPkt *pkt) +{ + int len, pos; + 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); + } + } + + write(ycht->fd, buf, len); + 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->rxqueue) + g_free(ycht->rxqueue); + + g_free(ycht); +} + +static void ycht_connection_error(YchtConn *ycht, const gchar *error) +{ +#if 0 +/* string freeze */ + gaim_notify_info(ycht->gc, NULL, _("Connection problem with the YCHT server."), error); +#endif + 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) { + /*ycht_connection_error(ycht, _("Unable to read"));*/ + ycht_connection_error(ycht, NULL); + 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", 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, ycht->rxqueue + pos, pktlen); + + ycht->rxlen -= YCHT_HEADER_LEN + pktlen; + if (ycht->rxlen) { + char *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, GaimInputCondition cond) +{ + 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."));*/ + ycht_connection_error(ycht, NULL); + 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) != 0) + { + /*ycht_connection_error(ycht, _("Connection problem"));*/ + ycht_connection_error(ycht, NULL); + 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); + if (ycht->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); +}