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