6722761a40f206213a2063a9d8d35175fcbbbc07
[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   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   va_end(ap);
231
232   return buffer;
233 }
234
235 /* Same as above but with va_list. */
236
237 SilcBuffer silc_command_payload_encode_vap(SilcCommand cmd, 
238                                            uint16 ident, 
239                                            uint32 argc, va_list ap)
240 {
241   unsigned char **argv;
242   uint32 *argv_lens = NULL, *argv_types = NULL;
243   unsigned char *x;
244   uint32 x_len;
245   uint32 x_type;
246   SilcBuffer buffer;
247   int i, k;
248
249   argv = silc_calloc(argc, sizeof(unsigned char *));
250   argv_lens = silc_calloc(argc, sizeof(uint32));
251   argv_types = silc_calloc(argc, sizeof(uint32));
252
253   for (i = 0, k = 0; i < argc; i++) {
254     x_type = va_arg(ap, uint32);
255     x = va_arg(ap, unsigned char *);
256     x_len = va_arg(ap, uint32);
257
258     if (!x_type || !x || !x_len)
259       continue;
260
261     argv[k] = silc_calloc(x_len + 1, sizeof(unsigned char));
262     memcpy(argv[k], x, x_len);
263     argv_lens[k] = x_len;
264     argv_types[k] = x_type;
265     k++;
266   }
267
268   buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
269                                        argv_types, ident);
270
271   for (i = 0; i < k; i++)
272     silc_free(argv[i]);
273   silc_free(argv);
274   silc_free(argv_lens);
275   silc_free(argv_types);
276
277   return buffer;
278 }
279
280 /* Same as above except that this is used to encode strictly command
281    reply packets. The command status message to be returned is sent as
282    extra argument to this function. The `argc' must not count `status'
283    as on argument. */
284
285 SilcBuffer 
286 silc_command_reply_payload_encode_va(SilcCommand cmd, 
287                                      SilcCommandStatus status,
288                                      uint16 ident,
289                                      uint32 argc, ...)
290 {
291   va_list ap;
292   unsigned char **argv;
293   uint32 *argv_lens = NULL, *argv_types = NULL;
294   unsigned char status_data[2];
295   unsigned char *x;
296   uint32 x_len;
297   uint32 x_type;
298   SilcBuffer buffer;
299   int i, k;
300
301   va_start(ap, argc);
302
303   argc++;
304   argv = silc_calloc(argc, sizeof(unsigned char *));
305   argv_lens = silc_calloc(argc, sizeof(uint32));
306   argv_types = silc_calloc(argc, sizeof(uint32));
307
308   SILC_PUT16_MSB(status, status_data);
309   argv[0] = silc_calloc(sizeof(status_data) + 1, sizeof(unsigned char));
310   memcpy(argv[0], status_data, sizeof(status_data));
311   argv_lens[0] = sizeof(status_data);
312   argv_types[0] = 1;
313
314   for (i = 1, k = 1; i < argc; i++) {
315     x_type = va_arg(ap, uint32);
316     x = va_arg(ap, unsigned char *);
317     x_len = va_arg(ap, uint32);
318
319     if (!x_type || !x || !x_len)
320       continue;
321
322     argv[k] = silc_calloc(x_len + 1, sizeof(unsigned char));
323     memcpy(argv[k], x, x_len);
324     argv_lens[k] = x_len;
325     argv_types[k] = x_type;
326     k++;
327   }
328
329   buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
330                                        argv_types, ident);
331
332   for (i = 0; i < k; i++)
333     silc_free(argv[i]);
334   silc_free(argv);
335   silc_free(argv_lens);
336   silc_free(argv_types);
337
338   va_end(ap);
339
340   return buffer;
341 }
342
343 /* Frees Command Payload */
344
345 void silc_command_payload_free(SilcCommandPayload payload)
346 {
347   if (payload) {
348     silc_argument_payload_free(payload->args);
349     silc_free(payload);
350   }
351 }
352
353 /* Returns command */
354
355 SilcCommand silc_command_get(SilcCommandPayload payload)
356 {
357   return payload->cmd;
358 }
359
360 /* Retuns arguments payload */
361
362 SilcArgumentPayload silc_command_get_args(SilcCommandPayload payload)
363 {
364   return payload->args;
365 }
366
367 /* Returns identifier */
368
369 uint16 silc_command_get_ident(SilcCommandPayload payload)
370 {
371   return payload->ident;
372 }
373
374 /* Function to set identifier to already allocated Command Payload. Command
375    payloads are frequentlly resent in SILC and thusly this makes it easy
376    to set the identifier. */
377
378 void silc_command_set_ident(SilcCommandPayload payload, uint16 ident)
379 {
380   payload->ident = ident;
381 }
382
383 /* Function to set the command to already allocated Command Payload. */
384
385 void silc_command_set_command(SilcCommandPayload payload, SilcCommand command)
386 {
387   payload->cmd = command;
388 }