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