pidgin/pidgin

Bump the version for release
release-2.x.y v2.10.12
2015-12-31, Gary Kramlich
2ac1dcb9eb2e
Bump the version for release
/*
* purple
*
* 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
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
#include "internal.h"
#include "debug.h"
#include "libymsg.h"
#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);
pkt->service = service;
pkt->status = status;
pkt->id = id;
return pkt;
}
void yahoo_packet_hash_str(struct yahoo_packet *pkt, int key, const char *value)
{
struct yahoo_pair *pair;
g_return_if_fail(value != NULL);
pair = g_new0(struct yahoo_pair, 1);
pair->key = key;
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)
{
struct yahoo_pair *pair;
pair = g_new0(struct yahoo_pair, 1);
pair->key = key;
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, ...)
{
char *strval;
int key, intval;
const char *cur;
va_list ap;
va_start(ap, fmt);
for (cur = fmt; *cur; cur++) {
key = va_arg(ap, int);
switch (*cur) {
case 'i':
intval = va_arg(ap, int);
yahoo_packet_hash_int(pkt, key, intval);
break;
case 's':
strval = va_arg(ap, char *);
yahoo_packet_hash_str(pkt, key, strval);
break;
default:
purple_debug_error("yahoo", "Invalid format character '%c'\n", *cur);
break;
}
}
va_end(ap);
}
size_t yahoo_packet_length(struct yahoo_packet *pkt)
{
GSList *l;
size_t len = 0;
l = pkt->hash;
while (l) {
struct yahoo_pair *pair = l->data;
int tmp = pair->key;
do {
tmp /= 10;
len++;
} while (tmp);
len += 2;
len += strlen(pair->value);
len += 2;
l = l->next;
}
return len;
}
/*
* '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
* in their server code.
*
* 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)
{
int pos = 0;
char key[64];
const guchar *delimiter;
gboolean accept;
guint x;
struct yahoo_pair *pair;
while (pos + 1 < len)
{
if (data[pos] == '\0')
break;
pair = g_new0(struct yahoo_pair, 1);
x = 0;
while (pos + 1 < len) {
if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
break;
if (x >= sizeof(key)-1) {
x++;
pos++;
continue;
}
key[x++] = data[pos++];
}
if (x >= sizeof(key)-1) {
x = 0;
}
key[x] = 0;
pos += 2;
pair->key = strtol(key, NULL, 10);
accept = x; /* if x is 0 there was no key, so don't accept it */
if (pos + 1 > len) {
/* Malformed packet! (Truncated--garbage or something) */
accept = FALSE;
}
if (accept) {
delimiter = (const guchar *)g_strstr_len((const char *)&data[pos], len - pos, "\xc0\x80");
if (delimiter == NULL)
{
/* Malformed packet! (It doesn't end in 0xc0 0x80) */
g_free(pair);
pos = len;
continue;
}
x = delimiter - data;
pair->value = g_strndup((const gchar *)&data[pos], x - pos);
pos = x;
pkt->hash = g_slist_prepend(pkt->hash, pair);
if (purple_debug_is_verbose() || g_getenv("PURPLE_YAHOO_DEBUG")) {
char *esc;
esc = g_strescape(pair->value, NULL);
purple_debug_misc("yahoo", "Key: %d \tValue: %s\n", pair->key, esc);
g_free(esc);
}
} else {
g_free(pair);
}
pos += 2;
if (pos + 1 > len) break;
/* Skip over garbage we've noticed in the mail notifications */
if (data[0] == '9' && data[pos] == 0x01)
pos++;
}
/*
* 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
* we reverse the list.
*/
pkt->hash = g_slist_reverse(pkt->hash);
}
void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data)
{
GSList *l;
int pos = 0;
/* This is only called from one place, and the list is
* always backwards */
l = pkt->hash = g_slist_reverse(pkt->hash);
while (l) {
struct yahoo_pair *pair = l->data;
gchar buf[100];
g_snprintf(buf, sizeof(buf), "%d", pair->key);
strcpy((char *)&data[pos], buf);
pos += strlen(buf);
data[pos++] = 0xc0;
data[pos++] = 0x80;
strcpy((char *)&data[pos], pair->value);
pos += strlen(pair->value);
data[pos++] = 0xc0;
data[pos++] = 0x80;
l = l->next;
}
}
void yahoo_packet_dump(guchar *data, int len)
{
#ifdef YAHOO_DEBUG
int i;
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]);
}
if (i < len)
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]);
else
purple_debug_misc(NULL, ". ");
}
purple_debug_misc(NULL, "\n");
#endif /* YAHOO_DEBUG */
}
static void
yahoo_packet_send_can_write(gpointer data, gint source, PurpleInputCondition cond)
{
YahooData *yd = data;
int ret, writelen;
writelen = purple_circ_buffer_get_max_read(yd->txbuf);
if (writelen == 0) {
purple_input_remove(yd->txhandler);
yd->txhandler = 0;
return;
}
ret = write(yd->fd, yd->txbuf->outptr, writelen);
if (ret < 0 && errno == EAGAIN)
return;
else if (ret < 0) {
/* TODO: what to do here - do we really have to disconnect? */
purple_connection_error_reason(yd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Write Error"));
return;
}
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;
guchar *data;
int pos = 0;
data = g_malloc0(len + 1);
memcpy(data + pos, "YMSG", 4); pos += 4;
if (wm)
pos += yahoo_put16(data + pos, YAHOO_WEBMESSENGER_PROTO_VER);
else if (jp)
pos += yahoo_put16(data + pos, YAHOO_PROTO_VER_JAPAN);
else
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);
*buf = data;
return len;
}
int yahoo_packet_send(struct yahoo_packet *pkt, YahooData *yd)
{
size_t len;
gssize ret;
guchar *data;
if (yd->fd < 0)
return -1;
len = yahoo_packet_build(pkt, 0, yd->wm, yd->jp, &data);
yahoo_packet_dump(data, len);
if (yd->txhandler == 0)
ret = write(yd->fd, data, len);
else {
ret = -1;
errno = EAGAIN;
}
if (ret < 0 && errno == EAGAIN)
ret = 0;
else if (ret <= 0) {
purple_debug_warning("yahoo", "Only wrote %" G_GSSIZE_FORMAT
" of %" G_GSIZE_FORMAT " bytes!\n", ret, len);
g_free(data);
return ret;
}
if ((gsize)ret < len) {
if (yd->txhandler == 0)
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);
}
g_free(data);
return ret;
}
int yahoo_packet_send_and_free(struct yahoo_packet *pkt, YahooData *yd)
{
int ret;
ret = yahoo_packet_send(pkt, yd);
yahoo_packet_free(pkt);
return ret;
}
void yahoo_packet_free(struct yahoo_packet *pkt)
{
while (pkt->hash) {
struct yahoo_pair *pair = pkt->hash->data;
g_free(pair->value);
g_free(pair);
pkt->hash = g_slist_delete_link(pkt->hash, pkt->hash);
}
g_free(pkt);
}