updates.
[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   uint16 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(SilcBuffer buffer)
45 {
46   SilcCommandPayload new;
47   unsigned char args_num;
48   uint16 payload_len;
49   int ret;
50
51   SILC_LOG_DEBUG(("Parsing command payload"));
52
53   new = silc_calloc(1, sizeof(*new));
54
55   /* Parse the Command Payload */
56   ret = silc_buffer_unformat(buffer, 
57                              SILC_STR_UI_SHORT(&payload_len),
58                              SILC_STR_UI_CHAR(&new->cmd),
59                              SILC_STR_UI_CHAR(&args_num),
60                              SILC_STR_UI_SHORT(&new->ident),
61                              SILC_STR_END);
62   if (ret == -1) {
63     silc_free(new);
64     return NULL;
65   }
66
67   if (payload_len != buffer->len) {
68     SILC_LOG_ERROR(("Incorrect command payload in packet, packet dropped"));
69     silc_free(new);
70     return NULL;
71   }
72
73   if (new->cmd == 0) {
74     silc_free(new);
75     return NULL;
76   }
77
78   silc_buffer_pull(buffer, SILC_COMMAND_PAYLOAD_LEN);
79   if (args_num) {
80     new->args = silc_argument_payload_parse(buffer, args_num);
81     if (!new->args) {
82       silc_free(new);
83       return NULL;
84     }
85   }
86   silc_buffer_push(buffer, SILC_COMMAND_PAYLOAD_LEN);
87
88   return new;
89 }
90
91 /* Encodes Command Payload returning it to SilcBuffer. */
92
93 SilcBuffer silc_command_payload_encode(SilcCommand cmd,
94                                        uint32 argc,
95                                        unsigned char **argv,
96                                        uint32 *argv_lens,
97                                        uint32 *argv_types,
98                                        uint16 ident)
99 {
100   SilcBuffer buffer;
101   SilcBuffer args = NULL;
102   uint32 len = 0;
103
104   SILC_LOG_DEBUG(("Encoding command payload"));
105
106   if (argc) {
107     args = silc_argument_payload_encode(argc, argv, argv_lens, argv_types);
108     len = args->len;
109   }
110
111   len += SILC_COMMAND_PAYLOAD_LEN;
112   buffer = silc_buffer_alloc(len);
113   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
114
115   /* Create Command payload */
116   silc_buffer_format(buffer,
117                      SILC_STR_UI_SHORT(len),
118                      SILC_STR_UI_CHAR(cmd),
119                      SILC_STR_UI_CHAR(argc),
120                      SILC_STR_UI_SHORT(ident),
121                      SILC_STR_END);
122
123   /* Add arguments */
124   if (argc) {
125     silc_buffer_pull(buffer, SILC_COMMAND_PAYLOAD_LEN);
126     silc_buffer_format(buffer,
127                        SILC_STR_UI_XNSTRING(args->data, args->len),
128                        SILC_STR_END);
129     silc_buffer_push(buffer, SILC_COMMAND_PAYLOAD_LEN);
130     silc_free(args);
131   }
132
133   return buffer;
134 }
135
136 /* Same as above but encode the buffer from SilcCommandPayload structure
137    instead of raw data. */
138
139 SilcBuffer silc_command_payload_encode_payload(SilcCommandPayload payload)
140 {
141   SilcBuffer buffer;
142   SilcBuffer args = NULL;
143   uint32 len = 0;
144   uint32 argc = 0;
145
146   SILC_LOG_DEBUG(("Encoding command payload"));
147
148   if (payload->args) {
149     args = silc_argument_payload_encode_payload(payload->args);
150     len = args->len;
151     argc = silc_argument_get_arg_num(payload->args);
152   }
153
154   len += SILC_COMMAND_PAYLOAD_LEN;
155   buffer = silc_buffer_alloc(len);
156   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
157
158   /* Create Command payload */
159   silc_buffer_format(buffer,
160                      SILC_STR_UI_SHORT(len),
161                      SILC_STR_UI_CHAR(payload->cmd),
162                      SILC_STR_UI_CHAR(argc),
163                      SILC_STR_UI_SHORT(payload->ident),
164                      SILC_STR_END);
165
166   /* Add arguments */
167   if (args) {
168     silc_buffer_pull(buffer, SILC_COMMAND_PAYLOAD_LEN);
169     silc_buffer_format(buffer,
170                        SILC_STR_UI_XNSTRING(args->data, args->len),
171                        SILC_STR_END);
172     silc_buffer_push(buffer, SILC_COMMAND_PAYLOAD_LEN);
173     silc_free(args);
174   }
175
176   return buffer;
177 }
178
179 /* Encodes Command payload with variable argument list. The arguments
180    must be: uint32, unsigned char *, unsigned int, ... One 
181    {uint32, unsigned char * and unsigned int} forms one argument, 
182    thus `argc' in case when sending one {uint32, unsigned char * 
183    and uint32} equals one (1) and when sending two of those it
184    equals two (2), and so on. This has to be preserved or bad things
185    will happen. The variable arguments is: {type, data, data_len}. */
186
187 SilcBuffer silc_command_payload_encode_va(SilcCommand cmd, 
188                                           uint16 ident, 
189                                           uint32 argc, ...)
190 {
191   va_list ap;
192   SilcBuffer buffer;
193
194   va_start(ap, argc);
195   buffer = silc_command_payload_encode_vap(cmd, ident, argc, ap);
196   va_end(ap);
197
198   return buffer;
199 }
200
201 /* Same as above but with va_list. */
202
203 SilcBuffer silc_command_payload_encode_vap(SilcCommand cmd, 
204                                            uint16 ident, 
205                                            uint32 argc, va_list ap)
206 {
207   unsigned char **argv;
208   uint32 *argv_lens = NULL, *argv_types = NULL;
209   unsigned char *x;
210   uint32 x_len;
211   uint32 x_type;
212   SilcBuffer buffer;
213   int i, k;
214
215   argv = silc_calloc(argc, sizeof(unsigned char *));
216   argv_lens = silc_calloc(argc, sizeof(uint32));
217   argv_types = silc_calloc(argc, sizeof(uint32));
218
219   for (i = 0, k = 0; i < argc; i++) {
220     x_type = va_arg(ap, uint32);
221     x = va_arg(ap, unsigned char *);
222     x_len = va_arg(ap, uint32);
223
224     if (!x_type || !x || !x_len)
225       continue;
226
227     argv[k] = silc_calloc(x_len + 1, sizeof(unsigned char));
228     memcpy(argv[k], x, x_len);
229     argv_lens[k] = x_len;
230     argv_types[k] = x_type;
231     k++;
232   }
233
234   buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
235                                        argv_types, ident);
236
237   for (i = 0; i < k; i++)
238     silc_free(argv[i]);
239   silc_free(argv);
240   silc_free(argv_lens);
241   silc_free(argv_types);
242
243   return buffer;
244 }
245
246 /* Same as above except that this is used to encode strictly command
247    reply packets. The command status message to be returned is sent as
248    extra argument to this function. The `argc' must not count `status'
249    as on argument. */
250
251 SilcBuffer 
252 silc_command_reply_payload_encode_va(SilcCommand cmd, 
253                                      SilcCommandStatus status,
254                                      uint16 ident,
255                                      uint32 argc, ...)
256 {
257   va_list ap;
258   unsigned char **argv;
259   uint32 *argv_lens = NULL, *argv_types = NULL;
260   unsigned char status_data[2];
261   unsigned char *x;
262   uint32 x_len;
263   uint32 x_type;
264   SilcBuffer buffer;
265   int i, k;
266
267   va_start(ap, argc);
268
269   argc++;
270   argv = silc_calloc(argc, sizeof(unsigned char *));
271   argv_lens = silc_calloc(argc, sizeof(uint32));
272   argv_types = silc_calloc(argc, sizeof(uint32));
273
274   SILC_PUT16_MSB(status, status_data);
275   argv[0] = silc_calloc(sizeof(status_data) + 1, sizeof(unsigned char));
276   memcpy(argv[0], status_data, sizeof(status_data));
277   argv_lens[0] = sizeof(status_data);
278   argv_types[0] = 1;
279
280   for (i = 1, k = 1; i < argc; i++) {
281     x_type = va_arg(ap, uint32);
282     x = va_arg(ap, unsigned char *);
283     x_len = va_arg(ap, uint32);
284
285     if (!x_type || !x || !x_len)
286       continue;
287
288     argv[k] = silc_calloc(x_len + 1, sizeof(unsigned char));
289     memcpy(argv[k], x, x_len);
290     argv_lens[k] = x_len;
291     argv_types[k] = x_type;
292     k++;
293   }
294
295   buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
296                                        argv_types, ident);
297
298   for (i = 0; i < k; i++)
299     silc_free(argv[i]);
300   silc_free(argv);
301   silc_free(argv_lens);
302   silc_free(argv_types);
303
304   va_end(ap);
305
306   return buffer;
307 }
308
309 /* Frees Command Payload */
310
311 void silc_command_payload_free(SilcCommandPayload payload)
312 {
313   if (payload) {
314     silc_argument_payload_free(payload->args);
315     silc_free(payload);
316   }
317 }
318
319 /* Returns command */
320
321 SilcCommand silc_command_get(SilcCommandPayload payload)
322 {
323   return payload->cmd;
324 }
325
326 /* Retuns arguments payload */
327
328 SilcArgumentPayload silc_command_get_args(SilcCommandPayload payload)
329 {
330   return payload->args;
331 }
332
333 /* Returns identifier */
334
335 uint16 silc_command_get_ident(SilcCommandPayload payload)
336 {
337   return payload->ident;
338 }
339
340 /* Function to set identifier to already allocated Command Payload. Command
341    payloads are frequentlly resent in SILC and thusly this makes it easy
342    to set the identifier. */
343
344 void silc_command_set_ident(SilcCommandPayload payload, uint16 ident)
345 {
346   payload->ident = ident;
347 }
348
349 /* Function to set the command to already allocated Command Payload. */
350
351 void silc_command_set_command(SilcCommandPayload payload, SilcCommand command)
352 {
353   payload->cmd = command;
354 }