| |
1 /* |
| |
2 * Gaim's oscar protocol plugin |
| |
3 * This file is the legal property of its developers. |
| |
4 * Please see the AUTHORS file distributed alongside this file. |
| |
5 * |
| |
6 * This library is free software; you can redistribute it and/or |
| |
7 * modify it under the terms of the GNU Lesser General Public |
| |
8 * License as published by the Free Software Foundation; either |
| |
9 * version 2 of the License, or (at your option) any later version. |
| |
10 * |
| |
11 * This library is distributed in the hope that it will be useful, |
| |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| |
14 * Lesser General Public License for more details. |
| |
15 * |
| |
16 * You should have received a copy of the GNU Lesser General Public |
| |
17 * License along with this library; if not, write to the Free Software |
| |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
19 */ |
| |
20 |
| |
21 |
| |
22 #include "oscar.h" |
| |
23 |
| |
24 static aim_tlv_t * |
| |
25 createtlv(guint16 type, guint16 length, guint8 *value) |
| |
26 { |
| |
27 aim_tlv_t *ret; |
| |
28 |
| |
29 ret = g_new(aim_tlv_t, 1); |
| |
30 ret->type = type; |
| |
31 ret->length = length; |
| |
32 ret->value = value; |
| |
33 |
| |
34 return ret; |
| |
35 } |
| |
36 |
| |
37 static void |
| |
38 freetlv(aim_tlv_t **oldtlv) |
| |
39 { |
| |
40 |
| |
41 if (!oldtlv || !*oldtlv) |
| |
42 return; |
| |
43 |
| |
44 free((*oldtlv)->value); |
| |
45 free(*oldtlv); |
| |
46 *oldtlv = NULL; |
| |
47 |
| |
48 return; |
| |
49 } |
| |
50 |
| |
51 /** |
| |
52 * Read a TLV chain from a buffer. |
| |
53 * |
| |
54 * Reads and parses a series of TLV patterns from a data buffer; the |
| |
55 * returned structure is manipulatable with the rest of the TLV |
| |
56 * routines. When done with a TLV chain, aim_tlvlist_free() should |
| |
57 * be called to free the dynamic substructures. |
| |
58 * |
| |
59 * XXX There should be a flag setable here to have the tlvlist contain |
| |
60 * bstream references, so that at least the ->value portion of each |
| |
61 * element doesn't need to be malloc/memcpy'd. This could prove to be |
| |
62 * just as efficient as the in-place TLV parsing used in a couple places |
| |
63 * in libfaim. |
| |
64 * |
| |
65 * @param bs Input bstream |
| |
66 * @return Return the TLV chain read |
| |
67 */ |
| |
68 aim_tlvlist_t *aim_tlvlist_read(ByteStream *bs) |
| |
69 { |
| |
70 aim_tlvlist_t *list = NULL, *cur; |
| |
71 |
| |
72 while (byte_stream_empty(bs) > 0) { |
| |
73 guint16 type, length; |
| |
74 |
| |
75 type = byte_stream_get16(bs); |
| |
76 length = byte_stream_get16(bs); |
| |
77 |
| |
78 #if 0 /* temporarily disabled until I know if they're still doing it or not */ |
| |
79 /* |
| |
80 * Okay, so now AOL has decided that any TLV of |
| |
81 * type 0x0013 can only be two bytes, despite |
| |
82 * what the actual given length is. So here |
| |
83 * we dump any invalid TLVs of that sort. Hopefully |
| |
84 * there's no special cases to this special case. |
| |
85 * - mid (30jun2000) |
| |
86 */ |
| |
87 if ((type == 0x0013) && (length != 0x0002)) |
| |
88 length = 0x0002; |
| |
89 #else |
| |
90 if (0) |
| |
91 ; |
| |
92 #endif |
| |
93 else { |
| |
94 |
| |
95 if (length > byte_stream_empty(bs)) { |
| |
96 aim_tlvlist_free(&list); |
| |
97 return NULL; |
| |
98 } |
| |
99 |
| |
100 cur = g_new0(aim_tlvlist_t, 1); |
| |
101 cur->tlv = createtlv(type, length, NULL); |
| |
102 if (cur->tlv->length > 0) { |
| |
103 cur->tlv->value = byte_stream_getraw(bs, length); |
| |
104 if (!cur->tlv->value) { |
| |
105 freetlv(&cur->tlv); |
| |
106 free(cur); |
| |
107 aim_tlvlist_free(&list); |
| |
108 return NULL; |
| |
109 } |
| |
110 } |
| |
111 |
| |
112 cur->next = list; |
| |
113 list = cur; |
| |
114 } |
| |
115 } |
| |
116 |
| |
117 return list; |
| |
118 } |
| |
119 |
| |
120 /** |
| |
121 * Read a TLV chain from a buffer. |
| |
122 * |
| |
123 * Reads and parses a series of TLV patterns from a data buffer; the |
| |
124 * returned structure is manipulatable with the rest of the TLV |
| |
125 * routines. When done with a TLV chain, aim_tlvlist_free() should |
| |
126 * be called to free the dynamic substructures. |
| |
127 * |
| |
128 * XXX There should be a flag setable here to have the tlvlist contain |
| |
129 * bstream references, so that at least the ->value portion of each |
| |
130 * element doesn't need to be malloc/memcpy'd. This could prove to be |
| |
131 * just as efficient as the in-place TLV parsing used in a couple places |
| |
132 * in libfaim. |
| |
133 * |
| |
134 * @param bs Input bstream |
| |
135 * @param num The max number of TLVs that will be read, or -1 if unlimited. |
| |
136 * There are a number of places where you want to read in a tlvchain, |
| |
137 * but the chain is not at the end of the SNAC, and the chain is |
| |
138 * preceded by the number of TLVs. So you can limit that with this. |
| |
139 * @return Return the TLV chain read |
| |
140 */ |
| |
141 aim_tlvlist_t *aim_tlvlist_readnum(ByteStream *bs, guint16 num) |
| |
142 { |
| |
143 aim_tlvlist_t *list = NULL, *cur; |
| |
144 |
| |
145 while ((byte_stream_empty(bs) > 0) && (num != 0)) { |
| |
146 guint16 type, length; |
| |
147 |
| |
148 type = byte_stream_get16(bs); |
| |
149 length = byte_stream_get16(bs); |
| |
150 |
| |
151 if (length > byte_stream_empty(bs)) { |
| |
152 aim_tlvlist_free(&list); |
| |
153 return NULL; |
| |
154 } |
| |
155 |
| |
156 cur = g_new0(aim_tlvlist_t, 1); |
| |
157 cur->tlv = createtlv(type, length, NULL); |
| |
158 if (cur->tlv->length > 0) { |
| |
159 cur->tlv->value = byte_stream_getraw(bs, length); |
| |
160 if (!cur->tlv->value) { |
| |
161 freetlv(&cur->tlv); |
| |
162 free(cur); |
| |
163 aim_tlvlist_free(&list); |
| |
164 return NULL; |
| |
165 } |
| |
166 } |
| |
167 |
| |
168 if (num > 0) |
| |
169 num--; |
| |
170 cur->next = list; |
| |
171 list = cur; |
| |
172 } |
| |
173 |
| |
174 return list; |
| |
175 } |
| |
176 |
| |
177 /** |
| |
178 * Read a TLV chain from a buffer. |
| |
179 * |
| |
180 * Reads and parses a series of TLV patterns from a data buffer; the |
| |
181 * returned structure is manipulatable with the rest of the TLV |
| |
182 * routines. When done with a TLV chain, aim_tlvlist_free() should |
| |
183 * be called to free the dynamic substructures. |
| |
184 * |
| |
185 * XXX There should be a flag setable here to have the tlvlist contain |
| |
186 * bstream references, so that at least the ->value portion of each |
| |
187 * element doesn't need to be malloc/memcpy'd. This could prove to be |
| |
188 * just as efficient as the in-place TLV parsing used in a couple places |
| |
189 * in libfaim. |
| |
190 * |
| |
191 * @param bs Input bstream |
| |
192 * @param len The max length in bytes that will be read. |
| |
193 * There are a number of places where you want to read in a tlvchain, |
| |
194 * but the chain is not at the end of the SNAC, and the chain is |
| |
195 * preceded by the length of the TLVs. So you can limit that with this. |
| |
196 * @return Return the TLV chain read |
| |
197 */ |
| |
198 aim_tlvlist_t *aim_tlvlist_readlen(ByteStream *bs, guint16 len) |
| |
199 { |
| |
200 aim_tlvlist_t *list = NULL, *cur; |
| |
201 |
| |
202 while ((byte_stream_empty(bs) > 0) && (len > 0)) { |
| |
203 guint16 type, length; |
| |
204 |
| |
205 type = byte_stream_get16(bs); |
| |
206 length = byte_stream_get16(bs); |
| |
207 |
| |
208 if (length > byte_stream_empty(bs)) { |
| |
209 aim_tlvlist_free(&list); |
| |
210 return NULL; |
| |
211 } |
| |
212 |
| |
213 cur = g_new0(aim_tlvlist_t, 1); |
| |
214 cur->tlv = createtlv(type, length, NULL); |
| |
215 if (cur->tlv->length > 0) { |
| |
216 cur->tlv->value = byte_stream_getraw(bs, length); |
| |
217 if (!cur->tlv->value) { |
| |
218 freetlv(&cur->tlv); |
| |
219 free(cur); |
| |
220 aim_tlvlist_free(&list); |
| |
221 return NULL; |
| |
222 } |
| |
223 } |
| |
224 |
| |
225 len -= aim_tlvlist_size(&cur); |
| |
226 cur->next = list; |
| |
227 list = cur; |
| |
228 } |
| |
229 |
| |
230 return list; |
| |
231 } |
| |
232 |
| |
233 /** |
| |
234 * Duplicate a TLV chain. |
| |
235 * This is pretty self explanatory. |
| |
236 * |
| |
237 * @param orig The TLV chain you want to make a copy of. |
| |
238 * @return A newly allocated TLV chain. |
| |
239 */ |
| |
240 aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig) |
| |
241 { |
| |
242 aim_tlvlist_t *new = NULL; |
| |
243 |
| |
244 while (orig) { |
| |
245 aim_tlvlist_add_raw(&new, orig->tlv->type, orig->tlv->length, orig->tlv->value); |
| |
246 orig = orig->next; |
| |
247 } |
| |
248 |
| |
249 return new; |
| |
250 } |
| |
251 |
| |
252 /* |
| |
253 * Compare two TLV lists for equality. This probably is not the most |
| |
254 * efficient way to do this. |
| |
255 * |
| |
256 * @param one One of the TLV chains to compare. |
| |
257 * @param two The other TLV chain to compare. |
| |
258 * @return Return 0 if the lists are the same, return 1 if they are different. |
| |
259 */ |
| |
260 int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two) |
| |
261 { |
| |
262 ByteStream bs1, bs2; |
| |
263 |
| |
264 if (aim_tlvlist_size(&one) != aim_tlvlist_size(&two)) |
| |
265 return 1; |
| |
266 |
| |
267 byte_stream_init(&bs1, ((guint8 *)malloc(aim_tlvlist_size(&one)*sizeof(guint8))), aim_tlvlist_size(&one)); |
| |
268 byte_stream_init(&bs2, ((guint8 *)malloc(aim_tlvlist_size(&two)*sizeof(guint8))), aim_tlvlist_size(&two)); |
| |
269 |
| |
270 aim_tlvlist_write(&bs1, &one); |
| |
271 aim_tlvlist_write(&bs2, &two); |
| |
272 |
| |
273 if (memcmp(bs1.data, bs2.data, bs1.len)) { |
| |
274 free(bs1.data); |
| |
275 free(bs2.data); |
| |
276 return 1; |
| |
277 } |
| |
278 |
| |
279 free(bs1.data); |
| |
280 free(bs2.data); |
| |
281 |
| |
282 return 0; |
| |
283 } |
| |
284 |
| |
285 /** |
| |
286 * Free a TLV chain structure |
| |
287 * |
| |
288 * Walks the list of TLVs in the passed TLV chain and |
| |
289 * frees each one. Note that any references to this data |
| |
290 * should be removed before calling this. |
| |
291 * |
| |
292 * @param list Chain to be freed |
| |
293 */ |
| |
294 void aim_tlvlist_free(aim_tlvlist_t **list) |
| |
295 { |
| |
296 aim_tlvlist_t *cur; |
| |
297 |
| |
298 if (!list || !*list) |
| |
299 return; |
| |
300 |
| |
301 for (cur = *list; cur; ) { |
| |
302 aim_tlvlist_t *tmp; |
| |
303 |
| |
304 freetlv(&cur->tlv); |
| |
305 |
| |
306 tmp = cur->next; |
| |
307 free(cur); |
| |
308 cur = tmp; |
| |
309 } |
| |
310 |
| |
311 list = NULL; |
| |
312 |
| |
313 return; |
| |
314 } |
| |
315 |
| |
316 /** |
| |
317 * Count the number of TLVs in a chain. |
| |
318 * |
| |
319 * @param list Chain to be counted. |
| |
320 * @return The number of TLVs stored in the passed chain. |
| |
321 */ |
| |
322 int aim_tlvlist_count(aim_tlvlist_t **list) |
| |
323 { |
| |
324 aim_tlvlist_t *cur; |
| |
325 int count; |
| |
326 |
| |
327 if (!list || !*list) |
| |
328 return 0; |
| |
329 |
| |
330 for (cur = *list, count = 0; cur; cur = cur->next) |
| |
331 count++; |
| |
332 |
| |
333 return count; |
| |
334 } |
| |
335 |
| |
336 /** |
| |
337 * Count the number of bytes in a TLV chain. |
| |
338 * |
| |
339 * @param list Chain to be sized |
| |
340 * @return The number of bytes that would be needed to |
| |
341 * write the passed TLV chain to a data buffer. |
| |
342 */ |
| |
343 int aim_tlvlist_size(aim_tlvlist_t **list) |
| |
344 { |
| |
345 aim_tlvlist_t *cur; |
| |
346 int size; |
| |
347 |
| |
348 if (!list || !*list) |
| |
349 return 0; |
| |
350 |
| |
351 for (cur = *list, size = 0; cur; cur = cur->next) |
| |
352 size += (4 + cur->tlv->length); |
| |
353 |
| |
354 return size; |
| |
355 } |
| |
356 |
| |
357 /** |
| |
358 * Adds the passed string as a TLV element of the passed type |
| |
359 * to the TLV chain. |
| |
360 * |
| |
361 * @param list Desination chain (%NULL pointer if empty). |
| |
362 * @param type TLV type. |
| |
363 * @param length Length of string to add (not including %NULL). |
| |
364 * @param value String to add. |
| |
365 * @return The size of the value added. |
| |
366 */ |
| |
367 int aim_tlvlist_add_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value) |
| |
368 { |
| |
369 aim_tlvlist_t *newtlv, *cur; |
| |
370 |
| |
371 if (list == NULL) |
| |
372 return 0; |
| |
373 |
| |
374 newtlv = g_new0(aim_tlvlist_t, 1); |
| |
375 newtlv->tlv = createtlv(type, length, NULL); |
| |
376 if (newtlv->tlv->length > 0) |
| |
377 newtlv->tlv->value = g_memdup(value, length); |
| |
378 |
| |
379 if (!*list) |
| |
380 *list = newtlv; |
| |
381 else { |
| |
382 for(cur = *list; cur->next; cur = cur->next) |
| |
383 ; |
| |
384 cur->next = newtlv; |
| |
385 } |
| |
386 |
| |
387 return newtlv->tlv->length; |
| |
388 } |
| |
389 |
| |
390 /** |
| |
391 * Add a one byte integer to a TLV chain. |
| |
392 * |
| |
393 * @param list Destination chain. |
| |
394 * @param type TLV type to add. |
| |
395 * @param value Value to add. |
| |
396 * @return The size of the value added. |
| |
397 */ |
| |
398 int aim_tlvlist_add_8(aim_tlvlist_t **list, const guint16 type, const guint8 value) |
| |
399 { |
| |
400 guint8 v8[1]; |
| |
401 |
| |
402 aimutil_put8(v8, value); |
| |
403 |
| |
404 return aim_tlvlist_add_raw(list, type, 1, v8); |
| |
405 } |
| |
406 |
| |
407 /** |
| |
408 * Add a two byte integer to a TLV chain. |
| |
409 * |
| |
410 * @param list Destination chain. |
| |
411 * @param type TLV type to add. |
| |
412 * @param value Value to add. |
| |
413 * @return The size of the value added. |
| |
414 */ |
| |
415 int aim_tlvlist_add_16(aim_tlvlist_t **list, const guint16 type, const guint16 value) |
| |
416 { |
| |
417 guint8 v16[2]; |
| |
418 |
| |
419 aimutil_put16(v16, value); |
| |
420 |
| |
421 return aim_tlvlist_add_raw(list, type, 2, v16); |
| |
422 } |
| |
423 |
| |
424 /** |
| |
425 * Add a four byte integer to a TLV chain. |
| |
426 * |
| |
427 * @param list Destination chain. |
| |
428 * @param type TLV type to add. |
| |
429 * @param value Value to add. |
| |
430 * @return The size of the value added. |
| |
431 */ |
| |
432 int aim_tlvlist_add_32(aim_tlvlist_t **list, const guint16 type, const guint32 value) |
| |
433 { |
| |
434 guint8 v32[4]; |
| |
435 |
| |
436 aimutil_put32(v32, value); |
| |
437 |
| |
438 return aim_tlvlist_add_raw(list, type, 4, v32); |
| |
439 } |
| |
440 |
| |
441 /** |
| |
442 * Add a string to a TLV chain. |
| |
443 * |
| |
444 * @param list Destination chain. |
| |
445 * @param type TLV type to add. |
| |
446 * @param value Value to add. |
| |
447 * @return The size of the value added. |
| |
448 */ |
| |
449 int aim_tlvlist_add_str(aim_tlvlist_t **list, const guint16 type, const char *value) |
| |
450 { |
| |
451 return aim_tlvlist_add_raw(list, type, strlen(value), (guint8 *)value); |
| |
452 } |
| |
453 |
| |
454 /** |
| |
455 * Adds a block of capability blocks to a TLV chain. The bitfield |
| |
456 * passed in should be a bitwise %OR of any of the %AIM_CAPS constants: |
| |
457 * |
| |
458 * %OSCAR_CAPABILITY_BUDDYICON Supports Buddy Icons |
| |
459 * %OSCAR_CAPABILITY_TALK Supports Voice Chat |
| |
460 * %OSCAR_CAPABILITY_IMIMAGE Supports DirectIM/IMImage |
| |
461 * %OSCAR_CAPABILITY_CHAT Supports Chat |
| |
462 * %OSCAR_CAPABILITY_GETFILE Supports Get File functions |
| |
463 * %OSCAR_CAPABILITY_SENDFILE Supports Send File functions |
| |
464 * |
| |
465 * @param list Destination chain |
| |
466 * @param type TLV type to add |
| |
467 * @param caps Bitfield of capability flags to send |
| |
468 * @return The size of the value added. |
| |
469 */ |
| |
470 int aim_tlvlist_add_caps(aim_tlvlist_t **list, const guint16 type, const guint32 caps) |
| |
471 { |
| |
472 guint8 buf[16*16]; /* XXX icky fixed length buffer */ |
| |
473 ByteStream bs; |
| |
474 |
| |
475 if (!caps) |
| |
476 return 0; /* nothing there anyway */ |
| |
477 |
| |
478 byte_stream_init(&bs, buf, sizeof(buf)); |
| |
479 |
| |
480 byte_stream_putcaps(&bs, caps); |
| |
481 |
| |
482 return aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf); |
| |
483 } |
| |
484 |
| |
485 /** |
| |
486 * Adds the given userinfo struct to a TLV chain. |
| |
487 * |
| |
488 * @param list Destination chain. |
| |
489 * @param type TLV type to add. |
| |
490 * @return The size of the value added. |
| |
491 */ |
| |
492 int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *userinfo) |
| |
493 { |
| |
494 guint8 buf[1024]; /* bleh */ |
| |
495 ByteStream bs; |
| |
496 |
| |
497 byte_stream_init(&bs, buf, sizeof(buf)); |
| |
498 |
| |
499 aim_putuserinfo(&bs, userinfo); |
| |
500 |
| |
501 return aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf); |
| |
502 } |
| |
503 |
| |
504 /** |
| |
505 * Adds the given chatroom info to a TLV chain. |
| |
506 * |
| |
507 * @param list Destination chain. |
| |
508 * @param type TLV type to add. |
| |
509 * @param roomname The name of the chat. |
| |
510 * @param instance The instance. |
| |
511 * @return The size of the value added. |
| |
512 */ |
| |
513 int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance) |
| |
514 { |
| |
515 guint8 *buf; |
| |
516 int len; |
| |
517 ByteStream bs; |
| |
518 |
| |
519 len = 2 + 1 + strlen(roomname) + 2; |
| |
520 |
| |
521 buf = malloc(len); |
| |
522 byte_stream_init(&bs, buf, len); |
| |
523 |
| |
524 byte_stream_put16(&bs, exchange); |
| |
525 byte_stream_put8(&bs, strlen(roomname)); |
| |
526 byte_stream_putstr(&bs, roomname); |
| |
527 byte_stream_put16(&bs, instance); |
| |
528 |
| |
529 len = aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf); |
| |
530 |
| |
531 free(buf); |
| |
532 |
| |
533 return len; |
| |
534 } |
| |
535 |
| |
536 /** |
| |
537 * Adds a TLV with a zero length to a TLV chain. |
| |
538 * |
| |
539 * @param list Destination chain. |
| |
540 * @param type TLV type to add. |
| |
541 * @return The size of the value added. |
| |
542 */ |
| |
543 int aim_tlvlist_add_noval(aim_tlvlist_t **list, const guint16 type) |
| |
544 { |
| |
545 return aim_tlvlist_add_raw(list, type, 0, NULL); |
| |
546 } |
| |
547 |
| |
548 /* |
| |
549 * Note that the inner TLV chain will not be modifiable as a tlvchain once |
| |
550 * it is written using this. Or rather, it can be, but updates won't be |
| |
551 * made to this. |
| |
552 * |
| |
553 * XXX should probably support sublists for real. |
| |
554 * |
| |
555 * This is so neat. |
| |
556 * |
| |
557 * @param list Destination chain. |
| |
558 * @param type TLV type to add. |
| |
559 * @param t1 The TLV chain you want to write. |
| |
560 * @return The number of bytes written to the destination TLV chain. |
| |
561 * 0 is returned if there was an error or if the destination |
| |
562 * TLV chain has length 0. |
| |
563 */ |
| |
564 int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl) |
| |
565 { |
| |
566 guint8 *buf; |
| |
567 int buflen; |
| |
568 ByteStream bs; |
| |
569 |
| |
570 buflen = aim_tlvlist_size(tl); |
| |
571 |
| |
572 if (buflen <= 0) |
| |
573 return 0; |
| |
574 |
| |
575 buf = malloc(buflen); |
| |
576 |
| |
577 byte_stream_init(&bs, buf, buflen); |
| |
578 |
| |
579 aim_tlvlist_write(&bs, tl); |
| |
580 |
| |
581 aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf); |
| |
582 |
| |
583 free(buf); |
| |
584 |
| |
585 return buflen; |
| |
586 } |
| |
587 |
| |
588 /** |
| |
589 * Substitute a TLV of a given type with a new TLV of the same type. If |
| |
590 * you attempt to replace a TLV that does not exist, this function will |
| |
591 * just add a new TLV as if you called aim_tlvlist_add_raw(). |
| |
592 * |
| |
593 * @param list Desination chain (%NULL pointer if empty). |
| |
594 * @param type TLV type. |
| |
595 * @param length Length of string to add (not including %NULL). |
| |
596 * @param value String to add. |
| |
597 * @return The length of the TLV. |
| |
598 */ |
| |
599 int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value) |
| |
600 { |
| |
601 aim_tlvlist_t *cur; |
| |
602 |
| |
603 if (list == NULL) |
| |
604 return 0; |
| |
605 |
| |
606 for (cur = *list; ((cur != NULL) && (cur->tlv->type != type)); cur = cur->next); |
| |
607 if (cur == NULL) |
| |
608 return aim_tlvlist_add_raw(list, type, length, value); |
| |
609 |
| |
610 free(cur->tlv->value); |
| |
611 cur->tlv->length = length; |
| |
612 if (cur->tlv->length > 0) { |
| |
613 cur->tlv->value = g_memdup(value, length); |
| |
614 } else |
| |
615 cur->tlv->value = NULL; |
| |
616 |
| |
617 return cur->tlv->length; |
| |
618 } |
| |
619 |
| |
620 /** |
| |
621 * Substitute a TLV of a given type with a new TLV of the same type. If |
| |
622 * you attempt to replace a TLV that does not exist, this function will |
| |
623 * just add a new TLV as if you called aim_tlvlist_add_str(). |
| |
624 * |
| |
625 * @param list Desination chain (%NULL pointer if empty). |
| |
626 * @param type TLV type. |
| |
627 * @param str String to add. |
| |
628 * @return The length of the TLV. |
| |
629 */ |
| |
630 int aim_tlvlist_replace_str(aim_tlvlist_t **list, const guint16 type, const char *str) |
| |
631 { |
| |
632 return aim_tlvlist_replace_raw(list, type, strlen(str), (const guchar *)str); |
| |
633 } |
| |
634 |
| |
635 /** |
| |
636 * Substitute a TLV of a given type with a new TLV of the same type. If |
| |
637 * you attempt to replace a TLV that does not exist, this function will |
| |
638 * just add a new TLV as if you called aim_tlvlist_add_raw(). |
| |
639 * |
| |
640 * @param list Desination chain (%NULL pointer if empty). |
| |
641 * @param type TLV type. |
| |
642 * @return The length of the TLV. |
| |
643 */ |
| |
644 int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const guint16 type) |
| |
645 { |
| |
646 return aim_tlvlist_replace_raw(list, type, 0, NULL); |
| |
647 } |
| |
648 |
| |
649 /** |
| |
650 * Substitute a TLV of a given type with a new TLV of the same type. If |
| |
651 * you attempt to replace a TLV that does not exist, this function will |
| |
652 * just add a new TLV as if you called aim_tlvlist_add_raw(). |
| |
653 * |
| |
654 * @param list Desination chain (%NULL pointer if empty). |
| |
655 * @param type TLV type. |
| |
656 * @param value 8 bit value to add. |
| |
657 * @return The length of the TLV. |
| |
658 */ |
| |
659 int aim_tlvlist_replace_8(aim_tlvlist_t **list, const guint16 type, const guint8 value) |
| |
660 { |
| |
661 guint8 v8[1]; |
| |
662 |
| |
663 aimutil_put8(v8, value); |
| |
664 |
| |
665 return aim_tlvlist_replace_raw(list, type, 1, v8); |
| |
666 } |
| |
667 |
| |
668 /** |
| |
669 * Substitute a TLV of a given type with a new TLV of the same type. If |
| |
670 * you attempt to replace a TLV that does not exist, this function will |
| |
671 * just add a new TLV as if you called aim_tlvlist_add_raw(). |
| |
672 * |
| |
673 * @param list Desination chain (%NULL pointer if empty). |
| |
674 * @param type TLV type. |
| |
675 * @param value 32 bit value to add. |
| |
676 * @return The length of the TLV. |
| |
677 */ |
| |
678 int aim_tlvlist_replace_32(aim_tlvlist_t **list, const guint16 type, const guint32 value) |
| |
679 { |
| |
680 guint8 v32[4]; |
| |
681 |
| |
682 aimutil_put32(v32, value); |
| |
683 |
| |
684 return aim_tlvlist_replace_raw(list, type, 4, v32); |
| |
685 } |
| |
686 |
| |
687 /** |
| |
688 * Remove a TLV of a given type. If you attempt to remove a TLV that |
| |
689 * does not exist, nothing happens. |
| |
690 * |
| |
691 * @param list Desination chain (%NULL pointer if empty). |
| |
692 * @param type TLV type. |
| |
693 */ |
| |
694 void aim_tlvlist_remove(aim_tlvlist_t **list, const guint16 type) |
| |
695 { |
| |
696 aim_tlvlist_t *del; |
| |
697 |
| |
698 if (!list || !(*list)) |
| |
699 return; |
| |
700 |
| |
701 /* Remove the item from the list */ |
| |
702 if ((*list)->tlv->type == type) { |
| |
703 del = *list; |
| |
704 *list = (*list)->next; |
| |
705 } else { |
| |
706 aim_tlvlist_t *cur; |
| |
707 for (cur=*list; (cur->next && (cur->next->tlv->type!=type)); cur=cur->next); |
| |
708 if (!cur->next) |
| |
709 return; |
| |
710 del = cur->next; |
| |
711 cur->next = del->next; |
| |
712 } |
| |
713 |
| |
714 /* Free the removed item */ |
| |
715 free(del->tlv->value); |
| |
716 free(del->tlv); |
| |
717 free(del); |
| |
718 } |
| |
719 |
| |
720 /** |
| |
721 * Write a TLV chain into a data buffer. |
| |
722 * |
| |
723 * Copies a TLV chain into a raw data buffer, writing only the number |
| |
724 * of bytes specified. This operation does not free the chain; |
| |
725 * aim_tlvlist_free() must still be called to free up the memory used |
| |
726 * by the chain structures. |
| |
727 * |
| |
728 * XXX clean this up, make better use of bstreams |
| |
729 * |
| |
730 * @param bs Input bstream |
| |
731 * @param list Source TLV chain |
| |
732 * @return Return 0 if the destination bstream is too small. |
| |
733 */ |
| |
734 int aim_tlvlist_write(ByteStream *bs, aim_tlvlist_t **list) |
| |
735 { |
| |
736 int goodbuflen; |
| |
737 aim_tlvlist_t *cur; |
| |
738 |
| |
739 /* do an initial run to test total length */ |
| |
740 goodbuflen = aim_tlvlist_size(list); |
| |
741 |
| |
742 if (goodbuflen > byte_stream_empty(bs)) |
| |
743 return 0; /* not enough buffer */ |
| |
744 |
| |
745 /* do the real write-out */ |
| |
746 for (cur = *list; cur; cur = cur->next) { |
| |
747 byte_stream_put16(bs, cur->tlv->type); |
| |
748 byte_stream_put16(bs, cur->tlv->length); |
| |
749 if (cur->tlv->length) |
| |
750 byte_stream_putraw(bs, cur->tlv->value, cur->tlv->length); |
| |
751 } |
| |
752 |
| |
753 return 1; /* XXX this is a nonsensical return */ |
| |
754 } |
| |
755 |
| |
756 |
| |
757 /** |
| |
758 * Grab the Nth TLV of type type in the TLV list list. |
| |
759 * |
| |
760 * Returns a pointer to an aim_tlv_t of the specified type; |
| |
761 * %NULL on error. The @nth parameter is specified starting at %1. |
| |
762 * In most cases, there will be no more than one TLV of any type |
| |
763 * in a chain. |
| |
764 * |
| |
765 * @param list Source chain. |
| |
766 * @param type Requested TLV type. |
| |
767 * @param nth Index of TLV of type to get. |
| |
768 * @return The TLV you were looking for, or NULL if one could not be found. |
| |
769 */ |
| |
770 aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, const guint16 type, const int nth) |
| |
771 { |
| |
772 aim_tlvlist_t *cur; |
| |
773 int i; |
| |
774 |
| |
775 for (cur = list, i = 0; cur; cur = cur->next) { |
| |
776 if (cur && cur->tlv) { |
| |
777 if (cur->tlv->type == type) |
| |
778 i++; |
| |
779 if (i >= nth) |
| |
780 return cur->tlv; |
| |
781 } |
| |
782 } |
| |
783 |
| |
784 return NULL; |
| |
785 } |
| |
786 |
| |
787 /** |
| |
788 * Get the length of the data of the nth TLV in the given TLV chain. |
| |
789 * |
| |
790 * @param list Source chain. |
| |
791 * @param type Requested TLV type. |
| |
792 * @param nth Index of TLV of type to get. |
| |
793 * @return The length of the data in this TLV, or -1 if the TLV could not be |
| |
794 * found. Unless -1 is returned, this value will be 2 bytes. |
| |
795 */ |
| |
796 int aim_tlv_getlength(aim_tlvlist_t *list, const guint16 type, const int nth) |
| |
797 { |
| |
798 aim_tlvlist_t *cur; |
| |
799 int i; |
| |
800 |
| |
801 for (cur = list, i = 0; cur; cur = cur->next) { |
| |
802 if (cur && cur->tlv) { |
| |
803 if (cur->tlv->type == type) |
| |
804 i++; |
| |
805 if (i >= nth) |
| |
806 return cur->tlv->length; |
| |
807 } |
| |
808 } |
| |
809 |
| |
810 return -1; |
| |
811 } |
| |
812 |
| |
813 char * |
| |
814 aim_tlv_getvalue_as_string(aim_tlv_t *tlv) |
| |
815 { |
| |
816 char *ret; |
| |
817 |
| |
818 ret = malloc(tlv->length + 1); |
| |
819 memcpy(ret, tlv->value, tlv->length); |
| |
820 ret[tlv->length] = '\0'; |
| |
821 |
| |
822 return ret; |
| |
823 } |
| |
824 |
| |
825 /** |
| |
826 * Retrieve the data from the nth TLV in the given TLV chain as a string. |
| |
827 * |
| |
828 * @param list Source TLV chain. |
| |
829 * @param type TLV type to search for. |
| |
830 * @param nth Index of TLV to return. |
| |
831 * @return The value of the TLV you were looking for, or NULL if one could |
| |
832 * not be found. This is a dynamic buffer and must be freed by the |
| |
833 * caller. |
| |
834 */ |
| |
835 char *aim_tlv_getstr(aim_tlvlist_t *list, const guint16 type, const int nth) |
| |
836 { |
| |
837 aim_tlv_t *tlv; |
| |
838 |
| |
839 if (!(tlv = aim_tlv_gettlv(list, type, nth))) |
| |
840 return NULL; |
| |
841 |
| |
842 return aim_tlv_getvalue_as_string(tlv); |
| |
843 } |
| |
844 |
| |
845 /** |
| |
846 * Retrieve the data from the nth TLV in the given TLV chain as an 8bit |
| |
847 * integer. |
| |
848 * |
| |
849 * @param list Source TLV chain. |
| |
850 * @param type TLV type to search for. |
| |
851 * @param nth Index of TLV to return. |
| |
852 * @return The value the TLV you were looking for, or 0 if one could |
| |
853 * not be found. |
| |
854 */ |
| |
855 guint8 aim_tlv_get8(aim_tlvlist_t *list, const guint16 type, const int nth) |
| |
856 { |
| |
857 aim_tlv_t *tlv; |
| |
858 |
| |
859 if (!(tlv = aim_tlv_gettlv(list, type, nth))) |
| |
860 return 0; /* erm */ |
| |
861 return aimutil_get8(tlv->value); |
| |
862 } |
| |
863 |
| |
864 /** |
| |
865 * Retrieve the data from the nth TLV in the given TLV chain as a 16bit |
| |
866 * integer. |
| |
867 * |
| |
868 * @param list Source TLV chain. |
| |
869 * @param type TLV type to search for. |
| |
870 * @param nth Index of TLV to return. |
| |
871 * @return The value the TLV you were looking for, or 0 if one could |
| |
872 * not be found. |
| |
873 */ |
| |
874 guint16 aim_tlv_get16(aim_tlvlist_t *list, const guint16 type, const int nth) |
| |
875 { |
| |
876 aim_tlv_t *tlv; |
| |
877 |
| |
878 if (!(tlv = aim_tlv_gettlv(list, type, nth))) |
| |
879 return 0; /* erm */ |
| |
880 return aimutil_get16(tlv->value); |
| |
881 } |
| |
882 |
| |
883 /** |
| |
884 * Retrieve the data from the nth TLV in the given TLV chain as a 32bit |
| |
885 * integer. |
| |
886 * |
| |
887 * @param list Source TLV chain. |
| |
888 * @param type TLV type to search for. |
| |
889 * @param nth Index of TLV to return. |
| |
890 * @return The value the TLV you were looking for, or 0 if one could |
| |
891 * not be found. |
| |
892 */ |
| |
893 guint32 aim_tlv_get32(aim_tlvlist_t *list, const guint16 type, const int nth) |
| |
894 { |
| |
895 aim_tlv_t *tlv; |
| |
896 |
| |
897 if (!(tlv = aim_tlv_gettlv(list, type, nth))) |
| |
898 return 0; /* erm */ |
| |
899 return aimutil_get32(tlv->value); |
| |
900 } |