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   SilcBuffer buffer;
259
260   va_start(ap, argc);
261   buffer = silc_command_reply_payload_encode_vap(cmd, status, ident, argc, ap);
262   va_end(ap);
263
264   return buffer;
265 }
266
267 SilcBuffer 
268 silc_command_reply_payload_encode_vap(SilcCommand cmd, 
269                                       SilcCommandStatus status,
270                                       uint16 ident, uint32 argc, 
271                                       va_list ap)
272 {
273   unsigned char **argv;
274   uint32 *argv_lens = NULL, *argv_types = NULL;
275   unsigned char status_data[2];
276   unsigned char *x;
277   uint32 x_len;
278   uint32 x_type;
279   SilcBuffer buffer;
280   int i, k;
281
282   argc++;
283   argv = silc_calloc(argc, sizeof(unsigned char *));
284   argv_lens = silc_calloc(argc, sizeof(uint32));
285   argv_types = silc_calloc(argc, sizeof(uint32));
286
287   SILC_PUT16_MSB(status, status_data);
288   argv[0] = silc_calloc(sizeof(status_data) + 1, sizeof(unsigned char));
289   memcpy(argv[0], status_data, sizeof(status_data));
290   argv_lens[0] = sizeof(status_data);
291   argv_types[0] = 1;
292
293   for (i = 1, k = 1; i < argc; i++) {
294     x_type = va_arg(ap, uint32);
295     x = va_arg(ap, unsigned char *);
296     x_len = va_arg(ap, uint32);
297
298     if (!x_type || !x || !x_len)
299       continue;
300
301     argv[k] = silc_calloc(x_len + 1, sizeof(unsigned char));
302     memcpy(argv[k], x, x_len);
303     argv_lens[k] = x_len;
304     argv_types[k] = x_type;
305     k++;
306   }
307
308   buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
309                                        argv_types, ident);
310
311   for (i = 0; i < k; i++)
312     silc_free(argv[i]);
313   silc_free(argv);
314   silc_free(argv_lens);
315   silc_free(argv_types);
316
317   return buffer;
318 }
319
320 /* Frees Command Payload */
321
322 void silc_command_payload_free(SilcCommandPayload payload)
323 {
324   if (payload) {
325     silc_argument_payload_free(payload->args);
326     silc_free(payload);
327   }
328 }
329
330 /* Returns command */
331
332 SilcCommand silc_command_get(SilcCommandPayload payload)
333 {
334   return payload->cmd;
335 }
336
337 /* Retuns arguments payload */
338
339 SilcArgumentPayload silc_command_get_args(SilcCommandPayload payload)
340 {
341   return payload->args;
342 }
343
344 /* Returns identifier */
345
346 uint16 silc_command_get_ident(SilcCommandPayload payload)
347 {
348   return payload->ident;
349 }
350
351 /* Function to set identifier to already allocated Command Payload. Command
352    payloads are frequentlly resent in SILC and thusly this makes it easy
353    to set the identifier. */
354
355 void silc_command_set_ident(SilcCommandPayload payload, uint16 ident)
356 {
357   payload->ident = ident;
358 }
359
360 /* Function to set the command to already allocated Command Payload. */
361
362 void silc_command_set_command(SilcCommandPayload payload, SilcCommand command)
363 {
364   payload->cmd = command;
365 }