libpurple/protocols/yahoo/yahoo_packet.c

branch
cpw.khc.msnp14
changeset 20481
65485e2ed8a3
parent 20472
6a6d2ef151e6
parent 20478
46933dc62880
equal deleted inserted replaced
20480:df9df972434f 20481:65485e2ed8a3
1 /*
2 * purple
3 *
4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24 #include "internal.h"
25 #include "debug.h"
26
27 #include "yahoo.h"
28 #include "yahoo_packet.h"
29
30 struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, enum yahoo_status status, int id)
31 {
32 struct yahoo_packet *pkt = g_new0(struct yahoo_packet, 1);
33
34 pkt->service = service;
35 pkt->status = status;
36 pkt->id = id;
37
38 return pkt;
39 }
40
41 void yahoo_packet_hash_str(struct yahoo_packet *pkt, int key, const char *value)
42 {
43 struct yahoo_pair *pair;
44
45 g_return_if_fail(value != NULL);
46
47 pair = g_new0(struct yahoo_pair, 1);
48 pair->key = key;
49 pair->value = g_strdup(value);
50 pkt->hash = g_slist_prepend(pkt->hash, pair);
51 }
52
53 void yahoo_packet_hash_int(struct yahoo_packet *pkt, int key, int value)
54 {
55 struct yahoo_pair *pair;
56
57 pair = g_new0(struct yahoo_pair, 1);
58 pair->key = key;
59 pair->value = g_strdup_printf("%d", value);
60 pkt->hash = g_slist_prepend(pkt->hash, pair);
61 }
62
63 void yahoo_packet_hash(struct yahoo_packet *pkt, const char *fmt, ...)
64 {
65 char *strval;
66 int key, intval;
67 const char *cur;
68 va_list ap;
69
70 va_start(ap, fmt);
71 for (cur = fmt; *cur; cur++) {
72 key = va_arg(ap, int);
73 switch (*cur) {
74 case 'i':
75 intval = va_arg(ap, int);
76 yahoo_packet_hash_int(pkt, key, intval);
77 break;
78 case 's':
79 strval = va_arg(ap, char *);
80 yahoo_packet_hash_str(pkt, key, strval);
81 break;
82 default:
83 purple_debug_error("yahoo", "Invalid format character '%c'\n", *cur);
84 break;
85 }
86 }
87 va_end(ap);
88 }
89
90 size_t yahoo_packet_length(struct yahoo_packet *pkt)
91 {
92 GSList *l;
93
94 size_t len = 0;
95
96 l = pkt->hash;
97 while (l) {
98 struct yahoo_pair *pair = l->data;
99 int tmp = pair->key;
100 do {
101 tmp /= 10;
102 len++;
103 } while (tmp);
104 len += 2;
105 len += strlen(pair->value);
106 len += 2;
107 l = l->next;
108 }
109
110 return len;
111 }
112
113 void yahoo_packet_read(struct yahoo_packet *pkt, const guchar *data, int len)
114 {
115 int pos = 0;
116 char key[64];
117 const guchar *delimiter;
118 gboolean accept;
119 int x;
120 struct yahoo_pair *pair;
121
122 while (pos + 1 < len)
123 {
124 /* this is weird, and in one of the chat packets, and causes us to
125 * think all the values are keys and all the keys are values after
126 * this point if we don't handle it */
127 if (data[pos] == '\0') {
128 while (pos + 1 < len) {
129 if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
130 break;
131 pos++;
132 }
133 pos += 2;
134 continue;
135 }
136
137 pair = g_new0(struct yahoo_pair, 1);
138
139 x = 0;
140 while (pos + 1 < len) {
141 if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
142 break;
143 if (x >= sizeof(key)-1) {
144 x++;
145 pos++;
146 continue;
147 }
148 key[x++] = data[pos++];
149 }
150 if (x >= sizeof(key)-1) {
151 x = 0;
152 }
153 key[x] = 0;
154 pos += 2;
155 pair->key = strtol(key, NULL, 10);
156 accept = x; /* if x is 0 there was no key, so don't accept it */
157
158 if (pos + 1 > len) {
159 /* Malformed packet! (Truncated--garbage or something) */
160 accept = FALSE;
161 }
162
163 if (accept) {
164 /* TODO: strstr() should not be used here because data isn't NULL terminated */
165 delimiter = (const guchar *)strstr((char *)&data[pos], "\xc0\x80");
166 if (delimiter == NULL)
167 {
168 /* Malformed packet! (It doesn't end in 0xc0 0x80) */
169 g_free(pair);
170 pos = len;
171 continue;
172 }
173 x = delimiter - data;
174 pair->value = g_strndup((const gchar *)&data[pos], x - pos);
175 pos = x;
176 pkt->hash = g_slist_prepend(pkt->hash, pair);
177
178 #ifdef DEBUG
179 {
180 char *esc;
181 esc = g_strescape(pair->value, NULL);
182 purple_debug(PURPLE_DEBUG_MISC, "yahoo",
183 "Key: %d \tValue: %s\n", pair->key, esc);
184 g_free(esc);
185 }
186 #endif
187 } else {
188 g_free(pair);
189 }
190 pos += 2;
191
192 /* Skip over garbage we've noticed in the mail notifications */
193 if (data[0] == '9' && data[pos] == 0x01)
194 pos++;
195 }
196
197 /*
198 * Originally this function used g_slist_append(). I changed
199 * it to use g_slist_prepend() for improved performance.
200 * Ideally the Yahoo! PRPL code would be indifferent to the
201 * order of the key/value pairs, but I don't know if this is
202 * the case for all incoming messages. To be on the safe side
203 * we reverse the list.
204 */
205 pkt->hash = g_slist_reverse(pkt->hash);
206 }
207
208 void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data)
209 {
210 GSList *l = pkt->hash;
211 int pos = 0;
212
213 while (l) {
214 struct yahoo_pair *pair = l->data;
215 gchar buf[100];
216
217 g_snprintf(buf, sizeof(buf), "%d", pair->key);
218 strcpy((char *)&data[pos], buf);
219 pos += strlen(buf);
220 data[pos++] = 0xc0;
221 data[pos++] = 0x80;
222
223 strcpy((char *)&data[pos], pair->value);
224 pos += strlen(pair->value);
225 data[pos++] = 0xc0;
226 data[pos++] = 0x80;
227
228 l = l->next;
229 }
230 }
231
232 void yahoo_packet_dump(guchar *data, int len)
233 {
234 #ifdef YAHOO_DEBUG
235 int i;
236
237 purple_debug(PURPLE_DEBUG_MISC, "yahoo", "");
238
239 for (i = 0; i + 1 < len; i += 2) {
240 if ((i % 16 == 0) && i) {
241 purple_debug(PURPLE_DEBUG_MISC, NULL, "\n");
242 purple_debug(PURPLE_DEBUG_MISC, "yahoo", "");
243 }
244
245 purple_debug(PURPLE_DEBUG_MISC, NULL, "%02x%02x ", data[i], data[i + 1]);
246 }
247 if (i < len)
248 purple_debug(PURPLE_DEBUG_MISC, NULL, "%02x", data[i]);
249
250 purple_debug(PURPLE_DEBUG_MISC, NULL, "\n");
251 purple_debug(PURPLE_DEBUG_MISC, "yahoo", "");
252
253 for (i = 0; i < len; i++) {
254 if ((i % 16 == 0) && i) {
255 purple_debug(PURPLE_DEBUG_MISC, NULL, "\n");
256 purple_debug(PURPLE_DEBUG_MISC, "yahoo", "");
257 }
258
259 if (g_ascii_isprint(data[i]))
260 purple_debug(PURPLE_DEBUG_MISC, NULL, "%c ", data[i]);
261 else
262 purple_debug(PURPLE_DEBUG_MISC, NULL, ". ");
263 }
264
265 purple_debug(PURPLE_DEBUG_MISC, NULL, "\n");
266 #endif
267 }
268
269 static void
270 yahoo_packet_send_can_write(gpointer data, gint source, PurpleInputCondition cond)
271 {
272 struct yahoo_data *yd = data;
273 int ret, writelen;
274
275 writelen = purple_circ_buffer_get_max_read(yd->txbuf);
276
277 if (writelen == 0) {
278 purple_input_remove(yd->txhandler);
279 yd->txhandler = -1;
280 return;
281 }
282
283 ret = write(yd->fd, yd->txbuf->outptr, writelen);
284
285 if (ret < 0 && errno == EAGAIN)
286 return;
287 else if (ret < 0) {
288 /* TODO: what to do here - do we really have to disconnect? */
289 purple_connection_error(yd->gc, _("Write Error"));
290 return;
291 }
292
293 purple_circ_buffer_mark_read(yd->txbuf, ret);
294 }
295
296
297 size_t yahoo_packet_build(struct yahoo_packet *pkt, int pad, gboolean wm,
298 gboolean jp, guchar **buf)
299 {
300 size_t pktlen = yahoo_packet_length(pkt);
301 size_t len = YAHOO_PACKET_HDRLEN + pktlen;
302 guchar *data;
303 int pos = 0;
304
305 data = g_malloc0(len + 1);
306
307 memcpy(data + pos, "YMSG", 4); pos += 4;
308
309 if (wm)
310 pos += yahoo_put16(data + pos, YAHOO_WEBMESSENGER_PROTO_VER);
311 else if (jp)
312 pos += yahoo_put16(data + pos, YAHOO_PROTO_VER_JAPAN);
313 else
314 pos += yahoo_put16(data + pos, YAHOO_PROTO_VER);
315 pos += yahoo_put16(data + pos, 0x0000);
316 pos += yahoo_put16(data + pos, pktlen + pad);
317 pos += yahoo_put16(data + pos, pkt->service);
318 pos += yahoo_put32(data + pos, pkt->status);
319 pos += yahoo_put32(data + pos, pkt->id);
320
321 yahoo_packet_write(pkt, data + pos);
322
323 *buf = data;
324
325 return len;
326 }
327
328 int yahoo_packet_send(struct yahoo_packet *pkt, struct yahoo_data *yd)
329 {
330 size_t len;
331 int ret;
332 guchar *data;
333
334 if (yd->fd < 0)
335 return -1;
336
337 len = yahoo_packet_build(pkt, 0, yd->wm, yd->jp, &data);
338
339 yahoo_packet_dump(data, len);
340 if (yd->txhandler == -1)
341 ret = write(yd->fd, data, len);
342 else {
343 ret = -1;
344 errno = EAGAIN;
345 }
346
347 if (ret < 0 && errno == EAGAIN)
348 ret = 0;
349 else if (ret <= 0) {
350 purple_debug_warning("yahoo", "Only wrote %d of %d bytes!", ret, len);
351 g_free(data);
352 return ret;
353 }
354
355 if (ret < len) {
356 if (yd->txhandler == -1)
357 yd->txhandler = purple_input_add(yd->fd, PURPLE_INPUT_WRITE,
358 yahoo_packet_send_can_write, yd);
359 purple_circ_buffer_append(yd->txbuf, data + ret, len - ret);
360 }
361
362 g_free(data);
363
364 return ret;
365 }
366
367 int yahoo_packet_send_and_free(struct yahoo_packet *pkt, struct yahoo_data *yd)
368 {
369 int ret;
370
371 ret = yahoo_packet_send(pkt, yd);
372 yahoo_packet_free(pkt);
373 return ret;
374 }
375
376 void yahoo_packet_free(struct yahoo_packet *pkt)
377 {
378 while (pkt->hash) {
379 struct yahoo_pair *pair = pkt->hash->data;
380 g_free(pair->value);
381 g_free(pair);
382 pkt->hash = g_slist_remove(pkt->hash, pair);
383 }
384 g_free(pkt);
385 }

mercurial