* Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA #include "yahoo_packet.h" struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, enum yahoo_status status, int id) struct yahoo_packet *pkt = g_new0(struct yahoo_packet, 1); void yahoo_packet_hash_str(struct yahoo_packet *pkt, int key, const char *value) g_return_if_fail(value != NULL); pair = g_new0(struct yahoo_pair, 1); pair->value = g_strdup(value); pkt->hash = g_slist_prepend(pkt->hash, pair); void yahoo_packet_hash_int(struct yahoo_packet *pkt, int key, int value) pair = g_new0(struct yahoo_pair, 1); pair->value = g_strdup_printf("%d", value); pkt->hash = g_slist_prepend(pkt->hash, pair); void yahoo_packet_hash(struct yahoo_packet *pkt, const char *fmt, ...) for (cur = fmt; *cur; cur++) { intval = va_arg(ap, int); yahoo_packet_hash_int(pkt, key, intval); strval = va_arg(ap, char *); yahoo_packet_hash_str(pkt, key, strval); purple_debug_error("yahoo", "Invalid format character '%c'\n", *cur); size_t yahoo_packet_length(struct yahoo_packet *pkt) struct yahoo_pair *pair = l->data; len += strlen(pair->value); * 'len' is the value given to us by the server that is supposed to * be the length of 'data'. But apparently there's a time when this * length is incorrect. Christopher Layne thinks it might be a bug * The following information is from Christopher: * It sometimes happens when Yahoo! sends a packet continuation within * chat. Sometimes when joining a large chatroom the initial * SERVICE_CHATJOIN packet will be so large that it will need to be * split into multiple packets. That's fine, except that the length * of the second packet is wrong. The packet has the same length as * the first packet, and the length given in the header is the same, * however the actual data in the packet is shorter than this length. * So half of the packet contains good, valid data, and then the rest * of the packet is junk. Luckily there is a null terminator after * the valid data and before the invalid data. * What does all this mean? It means that we parse through the data * pulling out key/value pairs until we've parsed 'len' bytes, or until * we run into a null terminator, whichever comes first. void yahoo_packet_read(struct yahoo_packet *pkt, const guchar *data, int len) pair = g_new0(struct yahoo_pair, 1); if (data[pos] == 0xc0 && data[pos + 1] == 0x80) if (x >= sizeof(key)-1) { if (x >= sizeof(key)-1) { pair->key = strtol(key, NULL, 10); accept = x; /* if x is 0 there was no key, so don't accept it */ /* Malformed packet! (Truncated--garbage or something) */ delimiter = (const guchar *)g_strstr_len((const char *)&data[pos], len - pos, "\xc0\x80"); /* Malformed packet! (It doesn't end in 0xc0 0x80) */ pair->value = g_strndup((const gchar *)&data[pos], x - pos); pkt->hash = g_slist_prepend(pkt->hash, pair); if (purple_debug_is_verbose() || g_getenv("PURPLE_YAHOO_DEBUG")) { esc = g_strescape(pair->value, NULL); purple_debug_misc("yahoo", "Key: %d \tValue: %s\n", pair->key, esc); if (pos + 1 > len) break; /* Skip over garbage we've noticed in the mail notifications */ if (data[0] == '9' && data[pos] == 0x01) * Originally this function used g_slist_append(). I changed * it to use g_slist_prepend() for improved performance. * Ideally the Yahoo! PRPL code would be indifferent to the * order of the key/value pairs, but I don't know if this is * the case for all incoming messages. To be on the safe side pkt->hash = g_slist_reverse(pkt->hash); void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data) /* This is only called from one place, and the list is l = pkt->hash = g_slist_reverse(pkt->hash); struct yahoo_pair *pair = l->data; g_snprintf(buf, sizeof(buf), "%d", pair->key); strcpy((char *)&data[pos], buf); strcpy((char *)&data[pos], pair->value); pos += strlen(pair->value); void yahoo_packet_dump(guchar *data, int len) purple_debug_misc("yahoo", ""); for (i = 0; i + 1 < len; i += 2) { if ((i % 16 == 0) && i) { purple_debug_misc(NULL, "\n"); purple_debug_misc("yahoo", ""); purple_debug_misc(NULL, "%02x%02x ", data[i], data[i + 1]); purple_debug_misc(NULL, "%02x", data[i]); purple_debug_misc(NULL, "\n"); purple_debug_misc("yahoo", ""); for (i = 0; i < len; i++) { if ((i % 16 == 0) && i) { purple_debug_misc(NULL, "\n"); purple_debug_misc("yahoo", ""); if (g_ascii_isprint(data[i])) purple_debug_misc(NULL, "%c ", data[i]); purple_debug_misc(NULL, ". "); purple_debug_misc(NULL, "\n"); yahoo_packet_send_can_write(gpointer data, gint source, PurpleInputCondition cond) writelen = purple_circ_buffer_get_max_read(yd->txbuf); purple_input_remove(yd->txhandler); ret = write(yd->fd, yd->txbuf->outptr, writelen); if (ret < 0 && errno == EAGAIN) /* TODO: what to do here - do we really have to disconnect? */ purple_connection_error_reason(yd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, purple_circ_buffer_mark_read(yd->txbuf, ret); size_t yahoo_packet_build(struct yahoo_packet *pkt, int pad, gboolean wm, gboolean jp, guchar **buf) size_t pktlen = yahoo_packet_length(pkt); size_t len = YAHOO_PACKET_HDRLEN + pktlen; data = g_malloc0(len + 1); memcpy(data + pos, "YMSG", 4); pos += 4; pos += yahoo_put16(data + pos, YAHOO_WEBMESSENGER_PROTO_VER); pos += yahoo_put16(data + pos, YAHOO_PROTO_VER_JAPAN); pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); pos += yahoo_put16(data + pos, 0x0000); pos += yahoo_put16(data + pos, pktlen + pad); pos += yahoo_put16(data + pos, pkt->service); pos += yahoo_put32(data + pos, pkt->status); pos += yahoo_put32(data + pos, pkt->id); yahoo_packet_write(pkt, data + pos); int yahoo_packet_send(struct yahoo_packet *pkt, YahooData *yd) len = yahoo_packet_build(pkt, 0, yd->wm, yd->jp, &data); yahoo_packet_dump(data, len); ret = write(yd->fd, data, len); if (ret < 0 && errno == EAGAIN) purple_debug_warning("yahoo", "Only wrote %" G_GSSIZE_FORMAT " of %" G_GSIZE_FORMAT " bytes!\n", ret, len); yd->txhandler = purple_input_add(yd->fd, PURPLE_INPUT_WRITE, yahoo_packet_send_can_write, yd); purple_circ_buffer_append(yd->txbuf, data + ret, len - ret); int yahoo_packet_send_and_free(struct yahoo_packet *pkt, YahooData *yd) ret = yahoo_packet_send(pkt, yd); void yahoo_packet_free(struct yahoo_packet *pkt) struct yahoo_pair *pair = pkt->hash->data; pkt->hash = g_slist_delete_link(pkt->hash, pkt->hash);