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