Mercurial > pidgin
comparison libgaim/protocols/yahoo/yahoo_packet.c @ 14192:60b1bc8dbf37
[gaim-migrate @ 16863]
Renamed 'core' to 'libgaim'
committer: Tailor Script <tailor@pidgin.im>
author | Evan Schoenberg <evan.s@dreskin.net> |
---|---|
date | Sat, 19 Aug 2006 01:50:10 +0000 |
parents | |
children | 0ac0f16aa006 |
comparison
equal
deleted
inserted
replaced
14191:009db0b357b5 | 14192:60b1bc8dbf37 |
---|---|
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; | |
116 char key[64], *delimiter, *esc; | |
117 gboolean accept; | |
118 int x; | |
119 struct yahoo_pair *pair; | |
120 | |
121 while (pos + 1 < len) | |
122 { | |
123 /* this is weird, and in one of the chat packets, and causes us to | |
124 * think all the values are keys and all the keys are values after | |
125 * this point if we don't handle it */ | |
126 if (data[pos] == '\0') { | |
127 while (pos + 1 < len) { | |
128 if (data[pos] == 0xc0 && data[pos + 1] == 0x80) | |
129 break; | |
130 pos++; | |
131 } | |
132 pos += 2; | |
133 continue; | |
134 } | |
135 | |
136 pair = g_new0(struct yahoo_pair, 1); | |
137 | |
138 x = 0; | |
139 while (pos + 1 < len) { | |
140 if (data[pos] == 0xc0 && data[pos + 1] == 0x80) | |
141 break; | |
142 if (x >= sizeof(key)-1) { | |
143 x++; | |
144 pos++; | |
145 continue; | |
146 } | |
147 key[x++] = data[pos++]; | |
148 } | |
149 if (x >= sizeof(key)-1) { | |
150 x = 0; | |
151 } | |
152 key[x] = 0; | |
153 pos += 2; | |
154 pair->key = strtol(key, NULL, 10); | |
155 accept = x; /* if x is 0 there was no key, so don't accept it */ | |
156 | |
157 if (len - pos + 1 <= 0) { | |
158 /* Truncated. Garbage or something. */ | |
159 accept = FALSE; | |
160 } | |
161 | |
162 if (accept) { | |
163 delimiter = strstr((char *)&data[pos], "\xc0\x80"); | |
164 if (delimiter == NULL) | |
165 { | |
166 /* Malformed packet! (it doesn't end in 0xc0 0x80) */ | |
167 g_free(pair); | |
168 pos = len; | |
169 continue; | |
170 } | |
171 x = (guint64)delimiter - (guint64)data; | |
172 pair->value = g_strndup((const gchar *)&data[pos], x - pos); | |
173 pos = x; | |
174 pkt->hash = g_slist_prepend(pkt->hash, pair); | |
175 | |
176 #ifdef DEBUG | |
177 esc = g_strescape(pair->value, NULL); | |
178 gaim_debug(GAIM_DEBUG_MISC, "yahoo", | |
179 "Key: %d \tValue: %s\n", pair->key, esc); | |
180 g_free(esc); | |
181 #endif | |
182 } else { | |
183 g_free(pair); | |
184 } | |
185 pos += 2; | |
186 | |
187 /* Skip over garbage we've noticed in the mail notifications */ | |
188 if (data[0] == '9' && data[pos] == 0x01) | |
189 pos++; | |
190 } | |
191 | |
192 /* | |
193 * Originally this function used g_slist_append(). I changed | |
194 * it to use g_slist_prepend() for improved performance. | |
195 * Ideally the Yahoo! PRPL code would be indifferent to the | |
196 * order of the key/value pairs, but I don't know if this is | |
197 * the case for all incoming messages. To be on the safe side | |
198 * we reverse the list. | |
199 */ | |
200 pkt->hash = g_slist_reverse(pkt->hash); | |
201 } | |
202 | |
203 void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data) | |
204 { | |
205 GSList *l = pkt->hash; | |
206 int pos = 0; | |
207 | |
208 while (l) { | |
209 struct yahoo_pair *pair = l->data; | |
210 gchar buf[100]; | |
211 | |
212 g_snprintf(buf, sizeof(buf), "%d", pair->key); | |
213 strcpy((char *)&data[pos], buf); | |
214 pos += strlen(buf); | |
215 data[pos++] = 0xc0; | |
216 data[pos++] = 0x80; | |
217 | |
218 strcpy((char *)&data[pos], pair->value); | |
219 pos += strlen(pair->value); | |
220 data[pos++] = 0xc0; | |
221 data[pos++] = 0x80; | |
222 | |
223 l = l->next; | |
224 } | |
225 } | |
226 | |
227 void yahoo_packet_dump(guchar *data, int len) | |
228 { | |
229 #ifdef YAHOO_DEBUG | |
230 int i; | |
231 | |
232 gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); | |
233 | |
234 for (i = 0; i + 1 < len; i += 2) { | |
235 if ((i % 16 == 0) && i) { | |
236 gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); | |
237 gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); | |
238 } | |
239 | |
240 gaim_debug(GAIM_DEBUG_MISC, NULL, "%02x%02x ", data[i], data[i + 1]); | |
241 } | |
242 if (i < len) | |
243 gaim_debug(GAIM_DEBUG_MISC, NULL, "%02x", data[i]); | |
244 | |
245 gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); | |
246 gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); | |
247 | |
248 for (i = 0; i < len; i++) { | |
249 if ((i % 16 == 0) && i) { | |
250 gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); | |
251 gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); | |
252 } | |
253 | |
254 if (g_ascii_isprint(data[i])) | |
255 gaim_debug(GAIM_DEBUG_MISC, NULL, "%c ", data[i]); | |
256 else | |
257 gaim_debug(GAIM_DEBUG_MISC, NULL, ". "); | |
258 } | |
259 | |
260 gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); | |
261 #endif | |
262 } | |
263 | |
264 static void | |
265 yahoo_packet_send_can_write(gpointer data, gint source, GaimInputCondition cond) | |
266 { | |
267 struct yahoo_data *yd = data; | |
268 int ret, writelen; | |
269 | |
270 writelen = gaim_circ_buffer_get_max_read(yd->txbuf); | |
271 | |
272 if (writelen == 0) { | |
273 gaim_input_remove(yd->txhandler); | |
274 yd->txhandler = -1; | |
275 return; | |
276 } | |
277 | |
278 ret = write(yd->fd, yd->txbuf->outptr, writelen); | |
279 | |
280 if (ret < 0 && errno == EAGAIN) | |
281 return; | |
282 else if (ret < 0) { | |
283 /* TODO: what to do here - do we really have to disconnect? */ | |
284 gaim_connection_error(yd->gc, _("Write Error")); | |
285 return; | |
286 } | |
287 | |
288 gaim_circ_buffer_mark_read(yd->txbuf, ret); | |
289 } | |
290 | |
291 | |
292 size_t yahoo_packet_build(struct yahoo_packet *pkt, int pad, gboolean wm, | |
293 guchar **buf) | |
294 { | |
295 size_t pktlen = yahoo_packet_length(pkt); | |
296 size_t len = YAHOO_PACKET_HDRLEN + pktlen; | |
297 guchar *data; | |
298 int pos = 0; | |
299 | |
300 data = g_malloc0(len + 1); | |
301 | |
302 memcpy(data + pos, "YMSG", 4); pos += 4; | |
303 | |
304 if (wm) | |
305 pos += yahoo_put16(data + pos, YAHOO_WEBMESSENGER_PROTO_VER); | |
306 else | |
307 pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); | |
308 pos += yahoo_put16(data + pos, 0x0000); | |
309 pos += yahoo_put16(data + pos, pktlen + pad); | |
310 pos += yahoo_put16(data + pos, pkt->service); | |
311 pos += yahoo_put32(data + pos, pkt->status); | |
312 pos += yahoo_put32(data + pos, pkt->id); | |
313 | |
314 yahoo_packet_write(pkt, data + pos); | |
315 | |
316 *buf = data; | |
317 | |
318 return len; | |
319 } | |
320 | |
321 int yahoo_packet_send(struct yahoo_packet *pkt, struct yahoo_data *yd) | |
322 { | |
323 size_t len; | |
324 int ret; | |
325 guchar *data; | |
326 | |
327 if (yd->fd < 0) | |
328 return -1; | |
329 | |
330 len = yahoo_packet_build(pkt, 0, yd->wm, &data); | |
331 | |
332 yahoo_packet_dump(data, len); | |
333 if (yd->txhandler == -1) | |
334 ret = write(yd->fd, data, len); | |
335 else { | |
336 ret = -1; | |
337 errno = EAGAIN; | |
338 } | |
339 | |
340 if (ret < 0 && errno == EAGAIN) | |
341 ret = 0; | |
342 else if (ret <= 0) { | |
343 gaim_debug_warning("yahoo", "Only wrote %d of %d bytes!", ret, len); | |
344 g_free(data); | |
345 return ret; | |
346 } | |
347 | |
348 if (ret < len) { | |
349 if (yd->txhandler == -1) | |
350 yd->txhandler = gaim_input_add(yd->fd, GAIM_INPUT_WRITE, | |
351 yahoo_packet_send_can_write, yd); | |
352 gaim_circ_buffer_append(yd->txbuf, data + ret, len - ret); | |
353 } | |
354 | |
355 g_free(data); | |
356 | |
357 return ret; | |
358 } | |
359 | |
360 int yahoo_packet_send_and_free(struct yahoo_packet *pkt, struct yahoo_data *yd) | |
361 { | |
362 int ret; | |
363 | |
364 ret = yahoo_packet_send(pkt, yd); | |
365 yahoo_packet_free(pkt); | |
366 return ret; | |
367 } | |
368 | |
369 void yahoo_packet_free(struct yahoo_packet *pkt) | |
370 { | |
371 while (pkt->hash) { | |
372 struct yahoo_pair *pair = pkt->hash->data; | |
373 g_free(pair->value); | |
374 g_free(pair); | |
375 pkt->hash = g_slist_remove(pkt->hash, pair); | |
376 } | |
377 g_free(pkt); | |
378 } |