A LOT updates. Cannot separate. :)
[silc.git] / lib / silccore / silcpayload.c
1 /*
2
3   silcpayload.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2000 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* Implementation of generic payloads described in the protocol 
21    specification drafts. */
22 /* $Id$ */
23
24 #include "silcincludes.h"
25 #include "silcpayload.h"
26
27 /******************************************************************************
28
29                                 ID Payload
30
31 ******************************************************************************/
32
33 struct SilcIDPayloadStruct {
34   SilcIdType type;
35   unsigned short len;
36   unsigned char *id;
37 };
38
39 /* Parses buffer and return ID payload into payload structure */
40
41 SilcIDPayload silc_id_payload_parse(SilcBuffer buffer)
42 {
43   SilcIDPayload new;
44
45   SILC_LOG_DEBUG(("Parsing ID payload"));
46
47   new = silc_calloc(1, sizeof(*new));
48
49   silc_buffer_unformat(buffer,
50                        SILC_STR_UI_SHORT(&new->type),
51                        SILC_STR_UI_SHORT(&new->len),
52                        SILC_STR_END);
53
54   silc_buffer_pull(buffer, 4);
55
56   if (new->len > buffer->len)
57     goto err;
58
59   silc_buffer_unformat(buffer,
60                        SILC_STR_UI_XNSTRING_ALLOC(&new->id, new->len),
61                        SILC_STR_END);
62   silc_buffer_push(buffer, 4);
63
64   return new;
65
66  err:
67   silc_free(new);
68   return NULL;
69 }
70
71 /* Parses data and return ID payload into payload structure. */
72
73 SilcIDPayload silc_id_payload_parse_data(unsigned char *data, 
74                                          unsigned int len)
75 {
76   SilcIDPayload new;
77   SilcBuffer buffer;
78
79   SILC_LOG_DEBUG(("Parsing ID payload"));
80
81   buffer = silc_buffer_alloc(len);
82   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
83   silc_buffer_put(buffer, data, len);
84
85   new = silc_calloc(1, sizeof(*new));
86
87   silc_buffer_unformat(buffer,
88                        SILC_STR_UI_SHORT(&new->type),
89                        SILC_STR_UI_SHORT(&new->len),
90                        SILC_STR_END);
91
92   silc_buffer_pull(buffer, 4);
93
94   if (new->len > buffer->len)
95     goto err;
96
97   silc_buffer_unformat(buffer,
98                        SILC_STR_UI_XNSTRING_ALLOC(&new->id, new->len),
99                        SILC_STR_END);
100
101   silc_buffer_free(buffer);
102   return new;
103
104  err:
105   silc_buffer_free(buffer);
106   silc_free(new);
107   return NULL;
108 }
109
110 /* Return the ID directly from the raw payload data. */
111
112 void *silc_id_payload_parse_id(unsigned char *data, unsigned int len)
113 {
114   SilcBuffer buffer;
115   SilcIdType type;
116   unsigned short idlen;
117   unsigned char *id;
118
119   buffer = silc_buffer_alloc(len);
120   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
121   silc_buffer_put(buffer, data, len);
122
123   silc_buffer_unformat(buffer,
124                        SILC_STR_UI_SHORT(&type),
125                        SILC_STR_UI_SHORT(&idlen),
126                        SILC_STR_END);
127
128   silc_buffer_pull(buffer, 4);
129
130   if (idlen > buffer->len)
131     goto err;
132
133   silc_buffer_unformat(buffer,
134                        SILC_STR_UI_XNSTRING_ALLOC(&id, idlen),
135                        SILC_STR_END);
136
137   silc_buffer_free(buffer);
138
139   return silc_id_str2id(id, type);
140
141  err:
142   silc_buffer_free(buffer);
143   return NULL;
144 }
145
146 /* Encodes ID Payload */
147
148 SilcBuffer silc_id_payload_encode(void *id, SilcIdType type)
149 {
150   SilcBuffer buffer;
151   unsigned char *id_data;
152   unsigned int len;
153
154   SILC_LOG_DEBUG(("Parsing ID payload"));
155
156   id_data = silc_id_id2str(id, type);
157   len = silc_id_get_len(type);
158
159   buffer = silc_buffer_alloc(4 + len);
160   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
161   silc_buffer_format(buffer,
162                      SILC_STR_UI_SHORT(type),
163                      SILC_STR_UI_SHORT(len),
164                      SILC_STR_UI_XNSTRING(id_data, len),
165                      SILC_STR_END);
166   silc_free(id_data);
167
168   return buffer;
169 }
170
171 /* Free ID Payload */
172
173 void silc_id_payload_free(SilcIDPayload payload)
174 {
175   if (payload) {
176     silc_free(payload->id);
177   }
178 }
179
180 /* Get ID type */
181
182 SilcIdType silc_id_payload_get_type(SilcIDPayload payload)
183 {
184   return payload->type;
185 }
186
187 /* Get ID */
188
189 void *silc_id_payload_get_id(SilcIDPayload payload)
190 {
191   return silc_id_str2id(payload->id, payload->type);
192 }
193
194 /* Get raw ID data. Data is duplicated. */
195
196 unsigned char *silc_id_payload_get_data(SilcIDPayload payload)
197 {
198   unsigned char *ret = silc_calloc(payload->len, sizeof(*ret));
199   memcpy(ret, payload->id, payload->len);
200   return ret;
201 }
202
203 /* Get length of ID */
204
205 unsigned int silc_id_payload_get_len(SilcIDPayload payload)
206 {
207   return payload->len;
208 }
209
210 /******************************************************************************
211
212                              Argument Payload
213
214 ******************************************************************************/
215
216 struct SilcArgumentPayloadStruct {
217   unsigned int argc;
218   unsigned char **argv;
219   unsigned int *argv_lens;
220   unsigned int *argv_types;
221   unsigned int pos;
222 };
223
224 /* Parses arguments and returns them into Argument Payload structure. */
225
226 SilcArgumentPayload silc_argument_payload_parse(SilcBuffer buffer,
227                                                 unsigned int argc)
228 {
229   SilcArgumentPayload new;
230   unsigned short payload_len = 0;
231   unsigned char arg_num = 0;
232   unsigned int arg_type = 0;
233   unsigned int pull_len = 0;
234   int i = 0;
235
236   SILC_LOG_DEBUG(("Parsing argument payload"));
237
238   new = silc_calloc(1, sizeof(*new));
239   new->argv = silc_calloc(argc, sizeof(unsigned char *));
240   new->argv_lens = silc_calloc(argc, sizeof(unsigned int));
241   new->argv_types = silc_calloc(argc, sizeof(unsigned int));
242     
243   /* Get arguments */
244   arg_num = 1;
245   for (i = 0; i < argc; i++) {
246     silc_buffer_unformat(buffer,
247                          SILC_STR_UI_SHORT(&payload_len),
248                          SILC_STR_UI_CHAR(&arg_type),
249                          SILC_STR_END);
250     
251     new->argv_lens[i] = payload_len;
252     new->argv_types[i] = arg_type;
253
254     if (payload_len > buffer->len)
255       break;
256     
257     /* Get argument data */
258     silc_buffer_pull(buffer, 3);
259     silc_buffer_unformat(buffer,
260                          SILC_STR_UI_XNSTRING_ALLOC(&new->argv[i], 
261                                                     payload_len),
262                          SILC_STR_END);
263
264     silc_buffer_pull(buffer, payload_len);
265     pull_len += 3 + payload_len;
266   }
267
268   if (buffer->len != 0)
269     goto err;
270
271   new->argc = argc;
272   new->pos = 0;
273
274   silc_buffer_push(buffer, pull_len);
275
276   return new;
277
278  err:
279   if (i) {
280     int k;
281
282     for (k = 0; k < i; k++)
283       silc_free(new->argv[k]);
284   }
285
286   silc_free(new->argv);
287   silc_free(new->argv_lens);
288   silc_free(new->argv_types);
289
290   if (new)
291     silc_free(new);
292
293   return NULL;
294 }
295
296 /* Encodes arguments in to Argument Paylods returning them to SilcBuffer. */
297
298 SilcBuffer silc_argument_payload_encode(unsigned int argc,
299                                         unsigned char **argv,
300                                         unsigned int *argv_lens,
301                                         unsigned int *argv_types)
302 {
303   SilcBuffer buffer;
304   unsigned int len;
305   int i;
306
307   SILC_LOG_DEBUG(("Encoding Argument payload"));
308
309   len = 0;
310   for (i = 0; i < argc; i++)
311     len += 3 + argv_lens[i];
312
313   buffer = silc_buffer_alloc(len);
314   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
315
316   /* Put arguments */
317   for (i = 0; i < argc; i++) {
318     silc_buffer_format(buffer,
319                        SILC_STR_UI_SHORT(argv_lens[i]),
320                        SILC_STR_UI_CHAR(argv_types[i]),
321                        SILC_STR_UI_XNSTRING(argv[i], argv_lens[i]),
322                        SILC_STR_END);
323     silc_buffer_pull(buffer, 3 + argv_lens[i]);
324   }
325
326   silc_buffer_push(buffer, len);
327
328   return buffer;
329 }
330
331 #if 0
332 /* Encodes Argument payload with variable argument list. The arguments
333    must be: unsigned int, unsigned char *, unsigned int, ... One 
334    {unsigned int, unsigned char * and unsigned int} forms one argument, 
335    thus `argc' in case when sending one {unsigned int, unsigned char * 
336    and unsigned int} equals one (1) and when sending two of those it
337    equals two (2), and so on. This has to be preserved or bad things
338    will happen. The variable arguments is: {type, data, data_len}. */
339
340 SilcBuffer silc_command_encode_payload_va(unsigned int argc, ...)
341 {
342   va_list ap;
343   unsigned char **argv;
344   unsigned int *argv_lens = NULL, *argv_types = NULL;
345   unsigned char *x;
346   unsigned int x_len;
347   unsigned int x_type;
348   SilcBuffer buffer;
349   int i;
350
351   va_start(ap, argc);
352
353   argv = silc_calloc(argc, sizeof(unsigned char *));
354   argv_lens = silc_calloc(argc, sizeof(unsigned int));
355   argv_types = silc_calloc(argc, sizeof(unsigned int));
356
357   for (i = 0; i < argc; i++) {
358     x_type = va_arg(ap, unsigned int);
359     x = va_arg(ap, unsigned char *);
360     x_len = va_arg(ap, unsigned int);
361
362     argv[i] = silc_calloc(x_len + 1, sizeof(unsigned char));
363     memcpy(argv[i], x, x_len);
364     argv_lens[i] = x_len;
365     argv_types[i] = x_type;
366   }
367
368   buffer = silc_argument_payload_encode(argc, argv, 
369                                         argv_lens, argv_types);
370
371   for (i = 0; i < argc; i++)
372     silc_free(argv[i]);
373   silc_free(argv);
374   silc_free(argv_lens);
375   silc_free(argv_types);
376
377   return buffer;
378 }
379 #endif
380
381 /* Free's Command Payload */
382
383 void silc_argument_payload_free(SilcArgumentPayload payload)
384 {
385   int i;
386
387   if (payload) {
388     for (i = 0; i < payload->argc; i++)
389       silc_free(payload->argv[i]);
390
391     silc_free(payload->argv);
392     silc_free(payload);
393   }
394 }
395
396 /* Returns number of arguments in payload */
397
398 unsigned int silc_argument_get_arg_num(SilcArgumentPayload payload)
399 {
400   return payload->argc;
401 }
402
403 /* Returns first argument from payload. */
404
405 unsigned char *silc_argument_get_first_arg(SilcArgumentPayload payload,
406                                            unsigned int *ret_len)
407 {
408   payload->pos = 0;
409
410   if (ret_len)
411     *ret_len = payload->argv_lens[payload->pos];
412
413   return payload->argv[payload->pos++];
414 }
415
416 /* Returns next argument from payload or NULL if no more arguments. */
417
418 unsigned char *silc_argument_get_next_arg(SilcArgumentPayload payload,
419                                           unsigned int *ret_len)
420 {
421   if (payload->pos >= payload->argc)
422     return NULL;
423
424   if (ret_len)
425     *ret_len = payload->argv_lens[payload->pos];
426
427   return payload->argv[payload->pos++];
428 }
429
430 /* Returns argument which type is `type'. */
431
432 unsigned char *silc_argument_get_arg_type(SilcArgumentPayload payload,
433                                           unsigned int type,
434                                           unsigned int *ret_len)
435 {
436   int i;
437
438   for (i = 0; i < payload->argc; i++)
439     if (payload->argv_types[i] == type)
440       break;
441
442   if (i >= payload->argc)
443     return NULL;
444
445   if (ret_len)
446     *ret_len = payload->argv_lens[i];
447
448   return payload->argv[i];
449 }