a98726f86e492d64366f2c05b0ae4ceb944b8722
[silc.git] / lib / silccore / silcargument.c
1 /*
2
3   silcargument.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2006 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
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 */
19 /* Implementation of Argument Payload routines */
20 /* $Id$ */
21
22 #include "silc.h"
23 #include "silcargument.h"
24
25 /*************************** Argument Payload *******************************/
26
27 struct SilcArgumentPayloadStruct {
28   SilcUInt32 argc;
29   unsigned char **argv;
30   SilcUInt32 *argv_lens;
31   SilcUInt32 *argv_types;
32   SilcUInt32 pos;
33 };
34
35 /* Parses arguments and returns them into Argument Payload structure. */
36
37 SilcArgumentPayload silc_argument_payload_parse(const unsigned char *payload,
38                                                 SilcUInt32 payload_len,
39                                                 SilcUInt32 argc)
40 {
41   SilcBufferStruct buffer;
42   SilcArgumentPayload newp;
43   SilcUInt16 p_len = 0;
44   unsigned char arg_num = 0;
45   unsigned char arg_type = 0;
46   SilcUInt32 pull_len = 0;
47   int i = 0, ret;
48
49   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
50   newp = silc_calloc(1, sizeof(*newp));
51   if (!newp)
52     return NULL;
53   newp->argv = silc_calloc(argc, sizeof(unsigned char *));
54   if (!newp->argv)
55     goto err;
56   newp->argv_lens = silc_calloc(argc, sizeof(SilcUInt32));
57   if (!newp->argv_lens)
58     goto err;
59   newp->argv_types = silc_calloc(argc, sizeof(SilcUInt32));
60   if (!newp->argv_types)
61     goto err;
62
63   /* Get arguments */
64   arg_num = 1;
65   for (i = 0; i < argc; i++) {
66     ret = silc_buffer_unformat(&buffer,
67                                SILC_STR_UI_SHORT(&p_len),
68                                SILC_STR_UI_CHAR(&arg_type),
69                                SILC_STR_END);
70     if (ret == -1 || p_len > silc_buffer_len(&buffer) - 3)
71       goto err;
72
73     newp->argv_lens[i] = p_len;
74     newp->argv_types[i] = arg_type;
75
76     /* Get argument data */
77     silc_buffer_pull(&buffer, 3);
78     ret = silc_buffer_unformat(&buffer,
79                                SILC_STR_UI_XNSTRING_ALLOC(&newp->argv[i],
80                                                           p_len),
81                                SILC_STR_END);
82     if (ret == -1)
83       goto err;
84
85     silc_buffer_pull(&buffer, p_len);
86     pull_len += 3 + p_len;
87   }
88
89   if (silc_buffer_len(&buffer) != 0) {
90     SILC_LOG_DEBUG(("Malformed argument payload"));
91     goto err;
92   }
93
94   newp->argc = argc;
95   newp->pos = 0;
96
97   silc_buffer_push(&buffer, pull_len);
98
99   return newp;
100
101  err:
102   SILC_LOG_DEBUG(("Error parsing argument payload"));
103   if (i)
104     for (ret = 0; ret < i; ret++)
105       silc_free(newp->argv[ret]);
106
107   silc_free(newp->argv);
108   silc_free(newp->argv_lens);
109   silc_free(newp->argv_types);
110   silc_free(newp);
111
112   return NULL;
113 }
114
115 /* Encodes arguments in to Argument Paylods returning them to SilcBuffer. */
116
117 SilcBuffer silc_argument_payload_encode(SilcUInt32 argc,
118                                         unsigned char **argv,
119                                         SilcUInt32 *argv_lens,
120                                         SilcUInt32 *argv_types)
121 {
122   SilcBuffer buffer;
123   SilcUInt32 len;
124   int i;
125
126   len = 0;
127   for (i = 0; i < argc; i++)
128     len += 3 + (SilcUInt16)argv_lens[i];
129
130   buffer = silc_buffer_alloc_size(len);
131   if (!buffer)
132     return NULL;
133
134   /* Put arguments */
135   for (i = 0; i < argc; i++) {
136     silc_buffer_format(buffer,
137                        SILC_STR_UI_SHORT(argv_lens[i]),
138                        SILC_STR_UI_CHAR(argv_types[i]),
139                        SILC_STR_UI_XNSTRING(argv[i], (SilcUInt16)argv_lens[i]),
140                        SILC_STR_END);
141     silc_buffer_pull(buffer, 3 + (SilcUInt16)argv_lens[i]);
142   }
143
144   silc_buffer_push(buffer, len);
145
146   return buffer;
147 }
148
149 /* Encode one argument to buffer */
150
151 SilcBuffer silc_argument_payload_encode_one(SilcBuffer args,
152                                             unsigned char *arg,
153                                             SilcUInt32 arg_len,
154                                             SilcUInt32 arg_type)
155 {
156   SilcBuffer buffer = args;
157   SilcUInt32 len;
158
159   len = 3 + (SilcUInt16)arg_len;
160   buffer = silc_buffer_realloc(buffer,
161                                (buffer ? silc_buffer_truelen(buffer) +
162                                 len : len));
163   if (!buffer)
164     return NULL;
165   silc_buffer_pull(buffer, silc_buffer_len(buffer));
166   silc_buffer_pull_tail(buffer, len);
167   silc_buffer_format(buffer,
168                      SILC_STR_UI_SHORT(arg_len),
169                      SILC_STR_UI_CHAR(arg_type),
170                      SILC_STR_UI_XNSTRING(arg, (SilcUInt16)arg_len),
171                      SILC_STR_END);
172   silc_buffer_push(buffer, buffer->data - buffer->head);
173
174   return buffer;
175 }
176
177 /* Same as above but encode the buffer from SilcArgumentPayload structure
178    instead of raw data. */
179
180 SilcBuffer silc_argument_payload_encode_payload(SilcArgumentPayload payload)
181 {
182   SilcBuffer buffer;
183   SilcUInt32 len;
184   int i;
185
186   len = 0;
187   for (i = 0; i < payload->argc; i++)
188     len += 3 + payload->argv_lens[i];
189
190   buffer = silc_buffer_alloc_size(len);
191   if (!buffer)
192     return NULL;
193
194   /* Put arguments */
195   for (i = 0; i < payload->argc; i++) {
196     silc_buffer_format(buffer,
197                        SILC_STR_UI_SHORT(payload->argv_lens[i]),
198                        SILC_STR_UI_CHAR(payload->argv_types[i]),
199                        SILC_STR_UI_XNSTRING(payload->argv[i],
200                                             payload->argv_lens[i]),
201                        SILC_STR_END);
202     silc_buffer_pull(buffer, 3 + payload->argv_lens[i]);
203   }
204
205   silc_buffer_push(buffer, len);
206
207   return buffer;
208 }
209
210 /* Frees Argument Payload */
211
212 void silc_argument_payload_free(SilcArgumentPayload payload)
213 {
214   int i;
215
216   if (payload) {
217     for (i = 0; i < payload->argc; i++)
218       silc_free(payload->argv[i]);
219
220     silc_free(payload->argv);
221     silc_free(payload->argv_lens);
222     silc_free(payload->argv_types);
223     silc_free(payload);
224   }
225 }
226
227 /* Returns number of arguments in payload */
228
229 SilcUInt32 silc_argument_get_arg_num(SilcArgumentPayload payload)
230 {
231   return payload ? payload->argc : 0;
232 }
233
234 /* Returns first argument from payload. */
235
236 unsigned char *silc_argument_get_first_arg(SilcArgumentPayload payload,
237                                            SilcUInt32 *type,
238                                            SilcUInt32 *ret_len)
239 {
240   if (!payload)
241     return NULL;
242
243   payload->pos = 0;
244
245   if (type)
246     *type = payload->argv_types[payload->pos];
247   if (ret_len)
248     *ret_len = payload->argv_lens[payload->pos];
249
250   return payload->argv[payload->pos++];
251 }
252
253 /* Returns next argument from payload or NULL if no more arguments. */
254
255 unsigned char *silc_argument_get_next_arg(SilcArgumentPayload payload,
256                                           SilcUInt32 *type,
257                                           SilcUInt32 *ret_len)
258 {
259   if (!payload)
260     return NULL;
261
262   if (payload->pos >= payload->argc)
263     return NULL;
264
265   if (type)
266     *type = payload->argv_types[payload->pos];
267   if (ret_len)
268     *ret_len = payload->argv_lens[payload->pos];
269
270   return payload->argv[payload->pos++];
271 }
272
273 /* Returns argument which type is `type'. */
274
275 unsigned char *silc_argument_get_arg_type(SilcArgumentPayload payload,
276                                           SilcUInt32 type,
277                                           SilcUInt32 *ret_len)
278 {
279   int i;
280
281   if (!payload)
282     return NULL;
283
284   for (i = 0; i < payload->argc; i++)
285     if (payload->argv_types[i] == type)
286       break;
287
288   if (i >= payload->argc)
289     return NULL;
290
291   if (ret_len)
292     *ret_len = payload->argv_lens[i];
293
294   return payload->argv[i];
295 }
296
297 /* Return argument already decoded */
298
299 static SilcBool silc_argument_decode(unsigned char *data,
300                                      SilcUInt32 data_len,
301                                      SilcArgumentDecodeType dec_type,
302                                      void *ret_arg,
303                                      void **ret_arg_alloc)
304 {
305   switch (dec_type) {
306
307   case SILC_ARGUMENT_ID:
308     if (ret_arg)
309       if (!silc_id_payload_parse_id(data, data_len, (SilcID *)ret_arg))
310         return FALSE;
311
312     if (ret_arg_alloc) {
313       SilcID id;
314       if (!silc_id_payload_parse_id(data, data_len, &id))
315         return FALSE;
316       *ret_arg_alloc = silc_memdup(&id, sizeof(id));
317     }
318     break;
319
320   case SILC_ARGUMENT_PUBLIC_KEY:
321     {
322       SilcPublicKey public_key;
323
324       if (!ret_arg_alloc)
325         return FALSE;
326
327       if (!silc_public_key_payload_decode(data, data_len, &public_key))
328         return FALSE;
329
330       *ret_arg_alloc = public_key;
331     }
332     break;
333
334   case SILC_ARGUMENT_ATTRIBUTES:
335     if (!ret_arg_alloc)
336       return FALSE;
337
338     *ret_arg_alloc = silc_attribute_payload_parse(data, data_len);
339     break;
340
341   case SILC_ARGUMENT_UINT32:
342     if (data_len != 4)
343       return FALSE;
344
345     if (ret_arg) {
346       SilcUInt32 *i = ret_arg;
347       SILC_GET32_MSB(*i, data);
348     }
349
350     if (ret_arg_alloc) {
351       SilcUInt32 i;
352       SILC_GET32_MSB(i, data);
353       *ret_arg_alloc = silc_memdup(&i, sizeof(i));
354     }
355     break;
356
357   case SILC_ARGUMENT_BOOL:
358     if (data_len != sizeof(SilcBool))
359       return FALSE;
360
361     if (ret_arg) {
362       SilcBool *b = ret_arg;
363       *b = (data[0] == 0x01 ? TRUE : FALSE);
364     }
365
366     if (ret_arg_alloc) {
367       SilcBool b;
368       b = (data[0] == 0x01 ? TRUE : FALSE);
369       *ret_arg_alloc = silc_memdup(&b, sizeof(b));
370     }
371     break;
372
373   default:
374     return FALSE;
375   }
376
377   return TRUE;
378 }
379
380 /* Return argument already decoded */
381
382 SilcBool silc_argument_get_decoded(SilcArgumentPayload payload,
383                                    SilcUInt32 type,
384                                    SilcArgumentDecodeType dec_type,
385                                    void *ret_arg,
386                                    void **ret_arg_alloc)
387 {
388   unsigned char *tmp;
389   SilcUInt32 tmp_len;
390
391   tmp = silc_argument_get_arg_type(payload, type, &tmp_len);
392   if (!tmp)
393     return FALSE;
394
395   return silc_argument_decode(tmp, tmp_len, dec_type, ret_arg, ret_arg_alloc);
396 }
397
398 /************************* Argument List Payload ****************************/
399
400 /* Parses argument payload list */
401
402 SilcArgumentPayload
403 silc_argument_list_parse(const unsigned char *payload,
404                          SilcUInt32 payload_len)
405 {
406   SilcArgumentPayload arg;
407   SilcUInt16 argc;
408
409   if (payload_len < 5)
410     return NULL;
411
412   SILC_GET16_MSB(argc, payload);
413
414   arg = silc_argument_payload_parse(payload + 2, payload_len - 2, argc);
415
416   return arg;
417 }
418
419 /* Parses argument payload list of specific argument types */
420
421 SilcDList
422 silc_argument_list_parse_decoded(const unsigned char *payload,
423                                  SilcUInt32 payload_len,
424                                  SilcArgumentDecodeType dec_type)
425 {
426   SilcArgumentPayload arg;
427   SilcArgumentDecodedList dec;
428   unsigned char *data;
429   SilcUInt32 data_len, type;
430   SilcDList list;
431
432   arg = silc_argument_list_parse(payload, payload_len);
433   if (!arg)
434     return NULL;
435
436   list = silc_dlist_init();
437   if (!list) {
438     silc_argument_payload_free(arg);
439     return NULL;
440   }
441
442   data = silc_argument_get_first_arg(arg, &type, &data_len);
443   while (data) {
444     dec = silc_calloc(1, sizeof(*dec));
445     if (!dec)
446       continue;
447     dec->arg_type = type;
448     if (silc_argument_decode(data, data_len, dec_type, NULL, &dec->argument))
449       silc_dlist_add(list, dec);
450     else
451       silc_free(dec);
452     data = silc_argument_get_next_arg(arg, &type, &data_len);
453   }
454
455   silc_argument_payload_free(arg);
456
457   silc_dlist_start(list);
458
459   return list;
460 }
461
462 /* Free decoded argument payload list */
463
464 void silc_argument_list_free(SilcDList list, SilcArgumentDecodeType dec_type)
465 {
466   SilcArgumentDecodedList dec;
467
468   if (!list)
469     return;
470
471   silc_dlist_start(list);
472   while ((dec = silc_dlist_get(list))) {
473     switch (dec_type) {
474
475     case SILC_ARGUMENT_ID:
476     case SILC_ARGUMENT_UINT32:
477     case SILC_ARGUMENT_BOOL:
478       silc_free(dec->argument);
479       break;
480
481     case SILC_ARGUMENT_PUBLIC_KEY:
482       silc_pkcs_public_key_free(dec->argument);
483       break;
484
485     case SILC_ARGUMENT_ATTRIBUTES:
486       silc_attribute_payload_free(dec->argument);
487       break;
488
489     default:
490       break;
491     }
492
493     silc_free(dec);
494   }
495
496   silc_dlist_uninit(list);
497 }