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