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