| |
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 } |