updates. New data types.
[silc.git] / lib / silccore / silccommand.c
1 /*
2
3   silccommand.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 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   unsigned char **argv;
193   uint32 *argv_lens = NULL, *argv_types = NULL;
194   unsigned char *x;
195   uint32 x_len;
196   uint32 x_type;
197   SilcBuffer buffer;
198   int i, k;
199
200   va_start(ap, argc);
201
202   argv = silc_calloc(argc, sizeof(unsigned char *));
203   argv_lens = silc_calloc(argc, sizeof(uint32));
204   argv_types = silc_calloc(argc, sizeof(uint32));
205
206   for (i = 0, k = 0; i < argc; i++) {
207     x_type = va_arg(ap, uint32);
208     x = va_arg(ap, unsigned char *);
209     x_len = va_arg(ap, uint32);
210
211     if (!x_type || !x || !x_len)
212       continue;
213
214     argv[k] = silc_calloc(x_len + 1, sizeof(unsigned char));
215     memcpy(argv[k], x, x_len);
216     argv_lens[k] = x_len;
217     argv_types[k] = x_type;
218     k++;
219   }
220
221   buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
222                                        argv_types, ident);
223
224   for (i = 0; i < k; i++)
225     silc_free(argv[i]);
226   silc_free(argv);
227   silc_free(argv_lens);
228   silc_free(argv_types);
229
230   return buffer;
231 }
232
233 /* Same as above but with va_list. */
234
235 SilcBuffer silc_command_payload_encode_vap(SilcCommand cmd, 
236                                            uint16 ident, 
237                                            uint32 argc, va_list ap)
238 {
239   unsigned char **argv;
240   uint32 *argv_lens = NULL, *argv_types = NULL;
241   unsigned char *x;
242   uint32 x_len;
243   uint32 x_type;
244   SilcBuffer buffer;
245   int i, k;
246
247   argv = silc_calloc(argc, sizeof(unsigned char *));
248   argv_lens = silc_calloc(argc, sizeof(uint32));
249   argv_types = silc_calloc(argc, sizeof(uint32));
250
251   for (i = 0, k = 0; i < argc; i++) {
252     x_type = va_arg(ap, uint32);
253     x = va_arg(ap, unsigned char *);
254     x_len = va_arg(ap, uint32);
255
256     if (!x_type || !x || !x_len)
257       continue;
258
259     argv[k] = silc_calloc(x_len + 1, sizeof(unsigned char));
260     memcpy(argv[k], x, x_len);
261     argv_lens[k] = x_len;
262     argv_types[k] = x_type;
263     k++;
264   }
265
266   buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
267                                        argv_types, ident);
268
269   for (i = 0; i < k; i++)
270     silc_free(argv[i]);
271   silc_free(argv);
272   silc_free(argv_lens);
273   silc_free(argv_types);
274
275   return buffer;
276 }
277
278 /* Same as above except that this is used to encode strictly command
279    reply packets. The command status message to be returned is sent as
280    extra argument to this function. The `argc' must not count `status'
281    as on argument. */
282
283 SilcBuffer 
284 silc_command_reply_payload_encode_va(SilcCommand cmd, 
285                                      SilcCommandStatus status,
286                                      uint16 ident,
287                                      uint32 argc, ...)
288 {
289   va_list ap;
290   unsigned char **argv;
291   uint32 *argv_lens = NULL, *argv_types = NULL;
292   unsigned char status_data[2];
293   unsigned char *x;
294   uint32 x_len;
295   uint32 x_type;
296   SilcBuffer buffer;
297   int i, k;
298
299   va_start(ap, argc);
300
301   argc++;
302   argv = silc_calloc(argc, sizeof(unsigned char *));
303   argv_lens = silc_calloc(argc, sizeof(uint32));
304   argv_types = silc_calloc(argc, sizeof(uint32));
305
306   SILC_PUT16_MSB(status, status_data);
307   argv[0] = silc_calloc(sizeof(status_data) + 1, sizeof(unsigned char));
308   memcpy(argv[0], status_data, sizeof(status_data));
309   argv_lens[0] = sizeof(status_data);
310   argv_types[0] = 1;
311
312   for (i = 1, k = 1; i < argc; i++) {
313     x_type = va_arg(ap, uint32);
314     x = va_arg(ap, unsigned char *);
315     x_len = va_arg(ap, uint32);
316
317     if (!x_type || !x || !x_len)
318       continue;
319
320     argv[k] = silc_calloc(x_len + 1, sizeof(unsigned char));
321     memcpy(argv[k], x, x_len);
322     argv_lens[k] = x_len;
323     argv_types[k] = x_type;
324     k++;
325   }
326
327   buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
328                                        argv_types, ident);
329
330   for (i = 0; i < k; i++)
331     silc_free(argv[i]);
332   silc_free(argv);
333   silc_free(argv_lens);
334   silc_free(argv_types);
335
336   return buffer;
337 }
338
339 /* Free's Command Payload */
340
341 void silc_command_free_payload(SilcCommandPayload payload)
342 {
343   if (payload) {
344     silc_argument_payload_free(payload->args);
345     silc_free(payload);
346   }
347 }
348
349 /* Returns command */
350
351 SilcCommand silc_command_get(SilcCommandPayload payload)
352 {
353   return payload->cmd;
354 }
355
356 /* Retuns arguments payload */
357
358 SilcArgumentPayload silc_command_get_args(SilcCommandPayload payload)
359 {
360   return payload->args;
361 }
362
363 /* Returns identifier */
364
365 uint16 silc_command_get_ident(SilcCommandPayload payload)
366 {
367   return payload->ident;
368 }
369
370 /* Function to set identifier to already allocated Command Payload. Command
371    payloads are frequentlly resent in SILC and thusly this makes it easy
372    to set the identifier. */
373
374 void silc_command_set_ident(SilcCommandPayload payload, uint16 ident)
375 {
376   payload->ident = ident;
377 }
378
379 /* Function to set the command to already allocated Command Payload. */
380
381 void silc_command_set_command(SilcCommandPayload payload, SilcCommand command)
382 {
383   payload->cmd = command;
384 }