Mercurial > pidgin
annotate libgaim/protocols/yahoo/yahoo_packet.c @ 15221:3043806ad900
[gaim-migrate @ 18011]
I think this'll fix an occasional "invalid read of size 1 bytes"
message from valgrind. I'm not sure when it happens... it seems
like it would only happen for invalid packets (ones that don't
end in 0xc0 80 or whatever it is)
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Sun, 17 Dec 2006 04:55:12 +0000 |
parents | 74511b8e9b46 |
children |
rev | line source |
---|---|
14192 | 1 /* |
2 * gaim | |
3 * | |
4 * Gaim 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 gaim_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; | |
14260 | 116 char key[64]; |
117 const guchar *delimiter; | |
14192 | 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 | |
15221 | 158 if (pos + 1 > len) { |
159 /* Malformed packet! (Truncated--garbage or something) */ | |
14192 | 160 accept = FALSE; |
161 } | |
162 | |
163 if (accept) { | |
14260 | 164 delimiter = (const guchar *)strstr((char *)&data[pos], "\xc0\x80"); |
14192 | 165 if (delimiter == NULL) |
166 { | |
15221 | 167 /* Malformed packet! (It doesn't end in 0xc0 0x80) */ |
14192 | 168 g_free(pair); |
169 pos = len; | |
170 continue; | |
171 } | |
14260 | 172 x = delimiter - data; |
14192 | 173 pair->value = g_strndup((const gchar *)&data[pos], x - pos); |
174 pos = x; | |
175 pkt->hash = g_slist_prepend(pkt->hash, pair); | |
176 | |
177 #ifdef DEBUG | |
14259 | 178 { |
179 char *esc; | |
180 esc = g_strescape(pair->value, NULL); | |
181 gaim_debug(GAIM_DEBUG_MISC, "yahoo", | |
182 "Key: %d \tValue: %s\n", pair->key, esc); | |
183 g_free(esc); | |
184 } | |
14192 | 185 #endif |
186 } else { | |
187 g_free(pair); | |
188 } | |
189 pos += 2; | |
190 | |
191 /* Skip over garbage we've noticed in the mail notifications */ | |
192 if (data[0] == '9' && data[pos] == 0x01) | |
193 pos++; | |
194 } | |
195 | |
196 /* | |
197 * Originally this function used g_slist_append(). I changed | |
198 * it to use g_slist_prepend() for improved performance. | |
199 * Ideally the Yahoo! PRPL code would be indifferent to the | |
200 * order of the key/value pairs, but I don't know if this is | |
201 * the case for all incoming messages. To be on the safe side | |
202 * we reverse the list. | |
203 */ | |
204 pkt->hash = g_slist_reverse(pkt->hash); | |
205 } | |
206 | |
207 void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data) | |
208 { | |
209 GSList *l = pkt->hash; | |
210 int pos = 0; | |
211 | |
212 while (l) { | |
213 struct yahoo_pair *pair = l->data; | |
214 gchar buf[100]; | |
215 | |
216 g_snprintf(buf, sizeof(buf), "%d", pair->key); | |
217 strcpy((char *)&data[pos], buf); | |
218 pos += strlen(buf); | |
219 data[pos++] = 0xc0; | |
220 data[pos++] = 0x80; | |
221 | |
222 strcpy((char *)&data[pos], pair->value); | |
223 pos += strlen(pair->value); | |
224 data[pos++] = 0xc0; | |
225 data[pos++] = 0x80; | |
226 | |
227 l = l->next; | |
228 } | |
229 } | |
230 | |
231 void yahoo_packet_dump(guchar *data, int len) | |
232 { | |
233 #ifdef YAHOO_DEBUG | |
234 int i; | |
235 | |
236 gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); | |
237 | |
238 for (i = 0; i + 1 < len; i += 2) { | |
239 if ((i % 16 == 0) && i) { | |
240 gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); | |
241 gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); | |
242 } | |
243 | |
244 gaim_debug(GAIM_DEBUG_MISC, NULL, "%02x%02x ", data[i], data[i + 1]); | |
245 } | |
246 if (i < len) | |
247 gaim_debug(GAIM_DEBUG_MISC, NULL, "%02x", data[i]); | |
248 | |
249 gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); | |
250 gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); | |
251 | |
252 for (i = 0; i < len; i++) { | |
253 if ((i % 16 == 0) && i) { | |
254 gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); | |
255 gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); | |
256 } | |
257 | |
258 if (g_ascii_isprint(data[i])) | |
259 gaim_debug(GAIM_DEBUG_MISC, NULL, "%c ", data[i]); | |
260 else | |
261 gaim_debug(GAIM_DEBUG_MISC, NULL, ". "); | |
262 } | |
263 | |
264 gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); | |
265 #endif | |
266 } | |
267 | |
268 static void | |
269 yahoo_packet_send_can_write(gpointer data, gint source, GaimInputCondition cond) | |
270 { | |
271 struct yahoo_data *yd = data; | |
272 int ret, writelen; | |
273 | |
274 writelen = gaim_circ_buffer_get_max_read(yd->txbuf); | |
275 | |
276 if (writelen == 0) { | |
277 gaim_input_remove(yd->txhandler); | |
278 yd->txhandler = -1; | |
279 return; | |
280 } | |
281 | |
282 ret = write(yd->fd, yd->txbuf->outptr, writelen); | |
283 | |
284 if (ret < 0 && errno == EAGAIN) | |
285 return; | |
286 else if (ret < 0) { | |
287 /* TODO: what to do here - do we really have to disconnect? */ | |
288 gaim_connection_error(yd->gc, _("Write Error")); | |
289 return; | |
290 } | |
291 | |
292 gaim_circ_buffer_mark_read(yd->txbuf, ret); | |
293 } | |
294 | |
295 | |
296 size_t yahoo_packet_build(struct yahoo_packet *pkt, int pad, gboolean wm, | |
14446
1bee09450652
[gaim-migrate @ 17160]
Evan Schoenberg <evan.s@dreskin.net>
parents:
14260
diff
changeset
|
297 gboolean jp, guchar **buf) |
14192 | 298 { |
299 size_t pktlen = yahoo_packet_length(pkt); | |
300 size_t len = YAHOO_PACKET_HDRLEN + pktlen; | |
301 guchar *data; | |
302 int pos = 0; | |
303 | |
304 data = g_malloc0(len + 1); | |
305 | |
306 memcpy(data + pos, "YMSG", 4); pos += 4; | |
307 | |
308 if (wm) | |
309 pos += yahoo_put16(data + pos, YAHOO_WEBMESSENGER_PROTO_VER); | |
14446
1bee09450652
[gaim-migrate @ 17160]
Evan Schoenberg <evan.s@dreskin.net>
parents:
14260
diff
changeset
|
310 else if (jp) |
1bee09450652
[gaim-migrate @ 17160]
Evan Schoenberg <evan.s@dreskin.net>
parents:
14260
diff
changeset
|
311 pos += yahoo_put16(data + pos, YAHOO_PROTO_VER_JAPAN); |
14192 | 312 else |
313 pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); | |
314 pos += yahoo_put16(data + pos, 0x0000); | |
315 pos += yahoo_put16(data + pos, pktlen + pad); | |
316 pos += yahoo_put16(data + pos, pkt->service); | |
317 pos += yahoo_put32(data + pos, pkt->status); | |
318 pos += yahoo_put32(data + pos, pkt->id); | |
319 | |
320 yahoo_packet_write(pkt, data + pos); | |
321 | |
322 *buf = data; | |
323 | |
324 return len; | |
325 } | |
326 | |
327 int yahoo_packet_send(struct yahoo_packet *pkt, struct yahoo_data *yd) | |
328 { | |
329 size_t len; | |
330 int ret; | |
331 guchar *data; | |
332 | |
333 if (yd->fd < 0) | |
334 return -1; | |
335 | |
14446
1bee09450652
[gaim-migrate @ 17160]
Evan Schoenberg <evan.s@dreskin.net>
parents:
14260
diff
changeset
|
336 len = yahoo_packet_build(pkt, 0, yd->wm, yd->jp, &data); |
14192 | 337 |
338 yahoo_packet_dump(data, len); | |
339 if (yd->txhandler == -1) | |
340 ret = write(yd->fd, data, len); | |
341 else { | |
342 ret = -1; | |
343 errno = EAGAIN; | |
344 } | |
345 | |
346 if (ret < 0 && errno == EAGAIN) | |
347 ret = 0; | |
348 else if (ret <= 0) { | |
349 gaim_debug_warning("yahoo", "Only wrote %d of %d bytes!", ret, len); | |
350 g_free(data); | |
351 return ret; | |
352 } | |
353 | |
354 if (ret < len) { | |
355 if (yd->txhandler == -1) | |
356 yd->txhandler = gaim_input_add(yd->fd, GAIM_INPUT_WRITE, | |
357 yahoo_packet_send_can_write, yd); | |
358 gaim_circ_buffer_append(yd->txbuf, data + ret, len - ret); | |
359 } | |
360 | |
361 g_free(data); | |
362 | |
363 return ret; | |
364 } | |
365 | |
366 int yahoo_packet_send_and_free(struct yahoo_packet *pkt, struct yahoo_data *yd) | |
367 { | |
368 int ret; | |
369 | |
370 ret = yahoo_packet_send(pkt, yd); | |
371 yahoo_packet_free(pkt); | |
372 return ret; | |
373 } | |
374 | |
375 void yahoo_packet_free(struct yahoo_packet *pkt) | |
376 { | |
377 while (pkt->hash) { | |
378 struct yahoo_pair *pair = pkt->hash->data; | |
379 g_free(pair->value); | |
380 g_free(pair); | |
381 pkt->hash = g_slist_remove(pkt->hash, pair); | |
382 } | |
383 g_free(pkt); | |
384 } |