Merged from silc_1_0_branch.
[silc.git] / lib / silccore / silccommand.c
1 /*
2
3   silccommand.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2002 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 /* $Id$ */
20
21 #include "silcincludes.h"
22 #include "silccommand.h"
23
24 /******************************************************************************
25
26                               Command Payload
27
28 ******************************************************************************/
29
30 /* Command Payload structure. Contents of this structure is parsed
31    from SILC packets. */
32 struct SilcCommandPayloadStruct {
33   SilcCommand cmd;
34   SilcUInt16 ident;
35   SilcArgumentPayload args;
36 };
37
38 /* Length of the command payload */
39 #define SILC_COMMAND_PAYLOAD_LEN 6
40
41 /* Parses command payload returning new command payload structure */
42
43 SilcCommandPayload silc_command_payload_parse(const unsigned char *payload,
44                                               SilcUInt32 payload_len)
45 {
46   SilcBufferStruct buffer;
47   SilcCommandPayload newp;
48   unsigned char args_num;
49   SilcUInt16 p_len;
50   int ret;
51
52   SILC_LOG_DEBUG(("Parsing command payload"));
53
54   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
55   newp = silc_calloc(1, sizeof(*newp));
56   if (!newp)
57     return NULL;
58
59   /* Parse the Command Payload */
60   ret = silc_buffer_unformat(&buffer, 
61                              SILC_STR_UI_SHORT(&p_len),
62                              SILC_STR_UI_CHAR(&newp->cmd),
63                              SILC_STR_UI_CHAR(&args_num),
64                              SILC_STR_UI_SHORT(&newp->ident),
65                              SILC_STR_END);
66   if (ret == -1) {
67     silc_free(newp);
68     return NULL;
69   }
70
71   if (p_len != buffer.len) {
72     SILC_LOG_ERROR(("Incorrect command payload in packet, packet dropped"));
73     silc_free(newp);
74     return NULL;
75   }
76
77   if (newp->cmd == 0) {
78     SILC_LOG_ERROR(("Incorrect command type in command payload"));
79     silc_free(newp);
80     return NULL;
81   }
82
83   silc_buffer_pull(&buffer, SILC_COMMAND_PAYLOAD_LEN);
84   if (args_num) {
85     newp->args = silc_argument_payload_parse(buffer.data, buffer.len, 
86                                              args_num);
87     if (!newp->args) {
88       silc_free(newp);
89       return NULL;
90     }
91   }
92   silc_buffer_push(&buffer, SILC_COMMAND_PAYLOAD_LEN);
93
94   return newp;
95 }
96
97 /* Encodes Command Payload returning it to SilcBuffer. */
98
99 SilcBuffer silc_command_payload_encode(SilcCommand cmd,
100                                        SilcUInt32 argc,
101                                        unsigned char **argv,
102                                        SilcUInt32 *argv_lens,
103                                        SilcUInt32 *argv_types,
104                                        SilcUInt16 ident)
105 {
106   SilcBuffer buffer;
107   SilcBuffer args = NULL;
108   SilcUInt32 len = 0;
109
110   SILC_LOG_DEBUG(("Encoding command payload"));
111
112   if (argc) {
113     args = silc_argument_payload_encode(argc, argv, argv_lens, argv_types);
114     if (!args)
115       return NULL;
116     len = args->len;
117   }
118
119   len += SILC_COMMAND_PAYLOAD_LEN;
120   buffer = silc_buffer_alloc_size(len);
121   if (!buffer)
122     return NULL;
123
124   /* Create Command payload */
125   silc_buffer_format(buffer,
126                      SILC_STR_UI_SHORT(len),
127                      SILC_STR_UI_CHAR(cmd),
128                      SILC_STR_UI_CHAR(argc),
129                      SILC_STR_UI_SHORT(ident),
130                      SILC_STR_END);
131
132   /* Add arguments */
133   if (argc) {
134     silc_buffer_pull(buffer, SILC_COMMAND_PAYLOAD_LEN);
135     silc_buffer_format(buffer,
136                        SILC_STR_UI_XNSTRING(args->data, args->len),
137                        SILC_STR_END);
138     silc_buffer_push(buffer, SILC_COMMAND_PAYLOAD_LEN);
139     silc_buffer_free(args);
140   }
141
142   return buffer;
143 }
144
145 /* Same as above but encode the buffer from SilcCommandPayload structure
146    instead of raw data. */
147
148 SilcBuffer silc_command_payload_encode_payload(SilcCommandPayload payload)
149 {
150   SilcBuffer buffer;
151   SilcBuffer args = NULL;
152   SilcUInt32 len = 0;
153   SilcUInt32 argc = 0;
154
155   SILC_LOG_DEBUG(("Encoding command payload"));
156
157   if (payload->args) {
158     args = silc_argument_payload_encode_payload(payload->args);
159     if (args)
160       len = args->len;
161     argc = silc_argument_get_arg_num(payload->args);
162   }
163
164   len += SILC_COMMAND_PAYLOAD_LEN;
165   buffer = silc_buffer_alloc_size(len);
166   if (!buffer) {
167     if (args)
168       silc_buffer_free(args);
169     return NULL;
170   }
171
172   /* Create Command payload */
173   silc_buffer_format(buffer,
174                      SILC_STR_UI_SHORT(len),
175                      SILC_STR_UI_CHAR(payload->cmd),
176                      SILC_STR_UI_CHAR(argc),
177                      SILC_STR_UI_SHORT(payload->ident),
178                      SILC_STR_END);
179
180   /* Add arguments */
181   if (args) {
182     silc_buffer_pull(buffer, SILC_COMMAND_PAYLOAD_LEN);
183     silc_buffer_format(buffer,
184                        SILC_STR_UI_XNSTRING(args->data, args->len),
185                        SILC_STR_END);
186     silc_buffer_push(buffer, SILC_COMMAND_PAYLOAD_LEN);
187     silc_buffer_free(args);
188   }
189
190   return buffer;
191 }
192
193 /* Encodes Command payload with variable argument list. The arguments
194    must be: SilcUInt32, unsigned char *, unsigned int, ... One 
195    {SilcUInt32, unsigned char * and unsigned int} forms one argument, 
196    thus `argc' in case when sending one {SilcUInt32, unsigned char * 
197    and SilcUInt32} equals one (1) and when sending two of those it
198    equals two (2), and so on. This has to be preserved or bad things
199    will happen. The variable arguments is: {type, data, data_len}. */
200
201 SilcBuffer silc_command_payload_encode_va(SilcCommand cmd, 
202                                           SilcUInt16 ident, 
203                                           SilcUInt32 argc, ...)
204 {
205   va_list ap;
206   SilcBuffer buffer;
207
208   va_start(ap, argc);
209   buffer = silc_command_payload_encode_vap(cmd, ident, argc, ap);
210   va_end(ap);
211
212   return buffer;
213 }
214
215 /* Same as above but with va_list. */
216
217 SilcBuffer silc_command_payload_encode_vap(SilcCommand cmd, 
218                                            SilcUInt16 ident, 
219                                            SilcUInt32 argc, va_list ap)
220 {
221   unsigned char **argv = NULL;
222   SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
223   unsigned char *x;
224   SilcUInt32 x_len;
225   SilcUInt32 x_type;
226   SilcBuffer buffer = NULL;
227   int i, k = 0;
228
229   if (argc) {
230     argv = silc_calloc(argc, sizeof(unsigned char *));
231     if (!argv)
232       return NULL;
233     argv_lens = silc_calloc(argc, sizeof(SilcUInt32));
234     if (!argv_lens)
235       return NULL;
236     argv_types = silc_calloc(argc, sizeof(SilcUInt32));
237     if (!argv_types)
238       return NULL;
239
240     for (i = 0, k = 0; i < argc; i++) {
241       x_type = va_arg(ap, SilcUInt32);
242       x = va_arg(ap, unsigned char *);
243       x_len = va_arg(ap, SilcUInt32);
244       
245       if (!x_type || !x || !x_len)
246         continue;
247       
248       argv[k] = silc_memdup(x, x_len);
249       if (!argv[k])
250         goto out;
251       argv_lens[k] = x_len;
252       argv_types[k] = x_type;
253       k++;
254     }
255   }
256
257   buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
258                                        argv_types, ident);
259
260  out:
261   for (i = 0; i < k; i++)
262     silc_free(argv[i]);
263   silc_free(argv);
264   silc_free(argv_lens);
265   silc_free(argv_types);
266
267   return buffer;
268 }
269
270 /* Same as above except that this is used to encode strictly command
271    reply packets. The command status message to be returned is sent as
272    extra argument to this function. The `argc' must not count `status'
273    as on argument. */
274
275 SilcBuffer 
276 silc_command_reply_payload_encode_va(SilcCommand cmd, 
277                                      SilcStatus status,
278                                      SilcStatus error,
279                                      SilcUInt16 ident,
280                                      SilcUInt32 argc, ...)
281 {
282   va_list ap;
283   SilcBuffer buffer;
284
285   va_start(ap, argc);
286   buffer = silc_command_reply_payload_encode_vap(cmd, status, error,
287                                                  ident, argc, ap);
288   va_end(ap);
289
290   return buffer;
291 }
292
293 SilcBuffer 
294 silc_command_reply_payload_encode_vap(SilcCommand cmd, 
295                                       SilcStatus status,
296                                       SilcStatus error,
297                                       SilcUInt16 ident, SilcUInt32 argc, 
298                                       va_list ap)
299 {
300   unsigned char **argv;
301   SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
302   unsigned char status_data[2];
303   unsigned char *x;
304   SilcUInt32 x_len;
305   SilcUInt32 x_type;
306   SilcBuffer buffer = NULL;
307   int i, k;
308
309   argc++;
310   argv = silc_calloc(argc, sizeof(unsigned char *));
311   if (!argv)
312     return NULL;
313   argv_lens = silc_calloc(argc, sizeof(SilcUInt32));
314   if (!argv_lens) {
315     silc_free(argv);
316     return NULL;
317   }
318   argv_types = silc_calloc(argc, sizeof(SilcUInt32));
319   if (!argv_types) {
320     silc_free(argv_lens);
321     silc_free(argv);
322     return NULL;
323   }
324
325   status_data[0] = status;
326   status_data[1] = error;
327   argv[0] = silc_memdup(status_data, sizeof(status_data));
328   if (!argv[0]) {
329     silc_free(argv_types);
330     silc_free(argv_lens);
331     silc_free(argv);
332     return NULL;
333   }
334   argv_lens[0] = sizeof(status_data);
335   argv_types[0] = 1;
336
337   for (i = 1, k = 1; i < argc; i++) {
338     x_type = va_arg(ap, SilcUInt32);
339     x = va_arg(ap, unsigned char *);
340     x_len = va_arg(ap, SilcUInt32);
341
342     if (!x_type || !x || !x_len)
343       continue;
344
345     argv[k] = silc_memdup(x, x_len);
346     if (!argv[k])
347       goto out;
348     argv_lens[k] = x_len;
349     argv_types[k] = x_type;
350     k++;
351   }
352
353   buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
354                                        argv_types, ident);
355
356  out:
357   for (i = 0; i < k; i++)
358     silc_free(argv[i]);
359   silc_free(argv);
360   silc_free(argv_lens);
361   silc_free(argv_types);
362
363   return buffer;
364 }
365
366 /* Frees Command Payload */
367
368 void silc_command_payload_free(SilcCommandPayload payload)
369 {
370   if (payload) {
371     silc_argument_payload_free(payload->args);
372     silc_free(payload);
373   }
374 }
375
376 /* Returns command */
377
378 SilcCommand silc_command_get(SilcCommandPayload payload)
379 {
380   return payload->cmd;
381 }
382
383 /* Retuns arguments payload */
384
385 SilcArgumentPayload silc_command_get_args(SilcCommandPayload payload)
386 {
387   return payload->args;
388 }
389
390 /* Returns identifier */
391
392 SilcUInt16 silc_command_get_ident(SilcCommandPayload payload)
393 {
394   return payload->ident;
395 }
396
397 /* Return command status */
398
399 bool silc_command_get_status(SilcCommandPayload payload, 
400                              SilcStatus *status,
401                              SilcStatus *error)
402 {
403   unsigned char *tmp;
404   SilcUInt32 tmp_len;
405
406   if (!payload->args)
407     return 0;
408   tmp = silc_argument_get_arg_type(payload->args, 1, &tmp_len);
409   if (!tmp || tmp_len != 2)
410     return 0;
411
412   /* Check for 1.0 protocol version which didn't have `error' */
413   if (tmp[0] == 0 && tmp[1] != 0) {
414     /* Protocol 1.0 version */
415     SilcStatus s;
416     SILC_GET16_MSB(s, tmp);
417     if (status)
418       *status = s;
419     if (error)
420       *error = 0;
421     if (s >= SILC_STATUS_ERR_NO_SUCH_NICK && error)
422       *error = s;
423     return (s < SILC_STATUS_ERR_NO_SUCH_NICK);
424   }
425
426   /* Take both status and possible error */
427   if (status)
428     *status = (SilcStatus)tmp[0];
429   if (error)
430     *error = (SilcStatus)tmp[1];
431
432   /* If single error occurred have the both `status' and `error' indicate
433      the error value for convenience. */
434   if (tmp[0] >= SILC_STATUS_ERR_NO_SUCH_NICK && error)
435     *error = tmp[0];
436
437   return (tmp[0] < SILC_STATUS_ERR_NO_SUCH_NICK && tmp[1] == SILC_STATUS_OK);
438 }
439
440 /* Function to set identifier to already allocated Command Payload. Command
441    payloads are frequentlly resent in SILC and thusly this makes it easy
442    to set the identifier. */
443
444 void silc_command_set_ident(SilcCommandPayload payload, SilcUInt16 ident)
445 {
446   payload->ident = ident;
447 }
448
449 /* Function to set the command to already allocated Command Payload. */
450
451 void silc_command_set_command(SilcCommandPayload payload, SilcCommand command)
452 {
453   payload->cmd = command;
454 }