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