Mercurial > pidgin
annotate libgaim/protocols/yahoo/yahoo_packet.c @ 14446:1bee09450652
[gaim-migrate @ 17160]
This patch was mentioned by Mark on gaim-devel; Tim replied, "That sounds good. As long as they're both #define's I can easily change." which they are :)
Yahoo! Japan appears to not support - or at least not support in its current iteration -- the new Yahoo protocol version (0x000f), disconnecting us immediately if we send it. When connecting to Yahoo Japan, we now send the old version, (0x000c).
committer: Tailor Script <tailor@pidgin.im>
author | Evan Schoenberg <evan.s@dreskin.net> |
---|---|
date | Mon, 04 Sep 2006 21:58:49 +0000 |
parents | accae094598f |
children | 74511b8e9b46 |
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 | |
158 if (len - pos + 1 <= 0) { | |
159 /* Truncated. Garbage or something. */ | |
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 { | |
167 /* Malformed packet! (it doesn't end in 0xc0 0x80) */ | |
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 } |