Initial revision
[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 /*
21  * $Id$
22  * $Log$
23  * Revision 1.1  2000/06/27 11:36:55  priikone
24  * Initial revision
25  *
26  *
27  */
28
29 #include "silcincludes.h"
30 #include "silccommand.h"
31
32 /* Command Payload structure. Contents of this structure is parsed
33    from SILC packets. */
34 struct SilcCommandPayloadStruct {
35   SilcCommand cmd;
36   unsigned int argc;
37   unsigned char **argv;
38   unsigned int *argv_lens;
39   unsigned int *argv_types;
40   unsigned int pos;
41 };
42
43 /* Length of the command payload */
44 #define SILC_COMMAND_PAYLOAD_LEN 4
45
46 /* Parses command payload returning new command payload structure */
47
48 SilcCommandPayload silc_command_parse_payload(SilcBuffer buffer)
49 {
50   SilcCommandPayload new;
51   unsigned short payload_len = 0;
52   unsigned char args_num = 0;
53   unsigned char arg_num = 0;
54   unsigned int arg_type = 0;
55   unsigned int pull_len = 0;
56   int i = 0;
57
58   SILC_LOG_DEBUG(("Parsing command payload"));
59
60   new = silc_calloc(1, sizeof(*new));
61   if (!new) {
62     SILC_LOG_ERROR(("Could not allocate new command payload"));
63     return NULL;
64   }
65
66   /* Parse the Command Payload */
67   silc_buffer_unformat(buffer, 
68                        SILC_STR_UI_CHAR(&new->cmd),
69                        SILC_STR_UI_CHAR(&args_num),
70                        SILC_STR_UI_SHORT(&payload_len),
71                        SILC_STR_END);
72
73   if (payload_len != buffer->len) {
74     SILC_LOG_ERROR(("Incorrect command payload in packet, packet dropped"));
75     return NULL;
76   }
77
78   if (new->cmd == 0)
79     return NULL;
80
81   if (args_num && payload_len) {
82
83     new->argv = silc_calloc(args_num, sizeof(unsigned char *));
84     new->argv_lens = silc_calloc(args_num, sizeof(unsigned int));
85     new->argv_types = silc_calloc(args_num, sizeof(unsigned int));
86
87     silc_buffer_pull(buffer, SILC_COMMAND_PAYLOAD_LEN);
88     pull_len += SILC_COMMAND_PAYLOAD_LEN;
89
90     /* Parse Command Argument Payloads */
91     arg_num = 1;
92     while(arg_num) {
93       silc_buffer_unformat(buffer,
94                            SILC_STR_UI_CHAR(&arg_num),
95                            SILC_STR_UI_CHAR(&arg_type),
96                            SILC_STR_UI_SHORT(&payload_len),
97                            SILC_STR_END);
98
99       /* Check that argument number is correct */
100       if (arg_num != i + 1)
101         goto err;
102
103       new->argv_lens[i] = payload_len;
104       new->argv_types[i] = arg_type;
105
106       /* Get argument data */
107       silc_buffer_pull(buffer, 4);
108       silc_buffer_unformat(buffer,
109                            SILC_STR_UI_XNSTRING_ALLOC(&new->argv[i], 
110                                                       payload_len),
111                            SILC_STR_END);
112       silc_buffer_pull(buffer, payload_len);
113       pull_len += 4 + payload_len;
114
115       i++;
116
117       if (i == args_num)
118         break;
119     }
120
121     /* Check the number of arguments */
122     if (arg_num != args_num)
123       goto err;
124   }
125
126   new->argc = i;
127   new->pos = 0;
128
129   silc_buffer_push(buffer, pull_len);
130
131   return new;
132
133  err:
134   if (i) {
135     int k;
136
137     for (k = 0; k < i; k++)
138       silc_free(new->argv[k]);
139   }
140
141   silc_free(new->argv);
142   silc_free(new->argv_lens);
143   silc_free(new->argv_types);
144
145   if (new)
146     silc_free(new);
147
148   return NULL;
149 }
150
151 /* Encodes Command Payload returning it to SilcBuffer. */
152
153 SilcBuffer silc_command_encode_payload(SilcCommand cmd,
154                                        unsigned int argc,
155                                        unsigned char **argv,
156                                        unsigned int *argv_lens,
157                                        unsigned int *argv_types)
158 {
159   SilcBuffer buffer;
160   unsigned int len;
161   int i;
162
163   SILC_LOG_DEBUG(("Encoding command payload"));
164
165   len = 1 + 1 + 2;
166   for (i = 0; i < argc; i++)
167     len += 1 + 1 + 2 + argv_lens[i];
168
169   buffer = silc_buffer_alloc(len);
170   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
171
172   /* Create Command payload */
173   silc_buffer_format(buffer,
174                      SILC_STR_UI_CHAR(cmd),
175                      SILC_STR_UI_CHAR(argc),
176                      SILC_STR_UI_SHORT(len),
177                      SILC_STR_END);
178
179   /* Put arguments */
180   if (argc) {
181     silc_buffer_pull(buffer, 4);
182    
183     for (i = 0; i < argc; i++) {
184       silc_buffer_format(buffer,
185                          SILC_STR_UI_CHAR(i + 1),
186                          SILC_STR_UI_CHAR(argv_types[i]),
187                          SILC_STR_UI_SHORT(argv_lens[i]),
188                          SILC_STR_UI_XNSTRING(argv[i], argv_lens[i]),
189                          SILC_STR_END);
190       silc_buffer_pull(buffer, 4 + argv_lens[i]);
191     }
192
193     silc_buffer_push(buffer, len);
194   }
195
196   return buffer;
197 }
198
199 /* Encodes Command payload with variable argument list. The arguments
200    must be: unsigned char *, unsigned int, ... One unsigned char *
201    and unsigned int forms one argument, hence `argc' in case when
202    sending one unsigned char * and unsigned int equals one (1) and
203    when sending two of those it equals two (2), and so on. This has
204    to be preserved or bad things will happen. */
205
206 SilcBuffer silc_command_encode_payload_va(SilcCommand cmd, 
207                                           unsigned int argc, ...)
208 {
209   va_list ap;
210   unsigned char **argv;
211   unsigned int *argv_lens = NULL, *argv_types = NULL;
212   unsigned char *x;
213   unsigned int x_len;
214   SilcBuffer buffer;
215   int i;
216
217   va_start(ap, argc);
218
219   argv = silc_calloc(argc, sizeof(unsigned char *));
220   argv_lens = silc_calloc(argc, sizeof(unsigned int));
221   argv_types = silc_calloc(argc, sizeof(unsigned int));
222
223   for (i = 0; i < argc; i++) {
224     x = va_arg(ap, unsigned char *);
225     x_len = va_arg(ap, unsigned int);
226
227     argv[i] = silc_calloc(x_len + 1, sizeof(unsigned char));
228     memcpy(argv[i], x, x_len);
229     argv_lens[i] = x_len;
230     argv_types[i] = i + 1;
231   }
232
233   buffer = silc_command_encode_payload(cmd, argc, argv, 
234                                        argv_lens, argv_types);
235
236   for (i = 0; i < argc; i++)
237     silc_free(argv[i]);
238   silc_free(argv);
239   silc_free(argv_lens);
240   silc_free(argv_types);
241
242   return buffer;
243 }
244
245 /* Free's Command Payload */
246
247 void silc_command_free_payload(SilcCommandPayload payload)
248 {
249   int i;
250
251   if (payload) {
252     for (i = 0; i < payload->argc; i++)
253       silc_free(payload->argv[i]);
254
255     silc_free(payload->argv);
256     silc_free(payload);
257   }
258 }
259
260 /* Returns the command type in payload */
261
262 SilcCommand silc_command_get(SilcCommandPayload payload)
263 {
264   return payload->cmd;
265 }
266
267 /* Returns number of arguments in payload */
268
269 unsigned int silc_command_get_arg_num(SilcCommandPayload payload)
270 {
271   return payload->argc;
272 }
273
274 /* Returns first argument from payload. */
275
276 unsigned char *silc_command_get_first_arg(SilcCommandPayload payload,
277                                           unsigned int *ret_len)
278 {
279   payload->pos = 0;
280
281   if (ret_len)
282     *ret_len = payload->argv_lens[payload->pos];
283
284   return payload->argv[payload->pos++];
285 }
286
287 /* Returns next argument from payload or NULL if no more arguments. */
288
289 unsigned char *silc_command_get_next_arg(SilcCommandPayload payload,
290                                          unsigned int *ret_len)
291 {
292   if (payload->pos >= payload->argc)
293     return NULL;
294
295   if (ret_len)
296     *ret_len = payload->argv_lens[payload->pos];
297
298   return payload->argv[payload->pos++];
299 }
300
301 /* Returns argument which type is `type'. */
302
303 unsigned char *silc_command_get_arg_type(SilcCommandPayload payload,
304                                          unsigned int type,
305                                          unsigned int *ret_len)
306 {
307   int i;
308
309   for (i = 0; i < payload->argc; i++)
310     if (payload->argv_types[i] == type)
311       break;
312
313   if (i >= payload->argc)
314     return NULL;
315
316   if (ret_len)
317     *ret_len = payload->argv_lens[i];
318
319   return payload->argv[i];
320 }
321
322 /* Encodes command status payload. Status payload is sent as one reply
323    argument. The returned payload still has to be saved into the 
324    Command Argument payload. */
325
326 SilcBuffer silc_command_encode_status_payload(SilcCommandStatus status,
327                                               unsigned char *data,
328                                               unsigned int len)
329 {
330   SilcBuffer sp;
331
332   sp = silc_buffer_alloc(len + 2);
333   silc_buffer_pull_tail(sp, SILC_BUFFER_END(sp));
334   silc_buffer_format(sp,
335                      SILC_STR_UI_SHORT(status),
336                      SILC_STR_UI_XNSTRING(data, len),
337                      SILC_STR_END);
338
339   return sp;
340 }