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