Merged from silc_1_0_branch.
[silc.git] / lib / silcsftp / sftp_util.c
1 /*
2
3   sftp_util.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2002 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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silcincludes.h"
22 #include "silcsftp.h"
23 #include "sftp_util.h"
24
25 /* Encodes a SFTP packet of type `packet' of length `len'. The variable
26    argument list is encoded as data payload to the buffer. Returns the
27    encoded packet or NULL on error. The caller must free the returned
28    buffer. If `packet_buf' is non-NULL then the new packet data is put
29    to that buffer instead of allocating new one.  If the new data cannot
30    fit to `packet_buf' will be reallocated. */
31
32 SilcBuffer silc_sftp_packet_encode(SilcSFTPPacket packet, 
33                                    SilcBuffer packet_buf, SilcUInt32 len, ...)
34 {
35   SilcBuffer buffer;
36   va_list vp;
37
38   va_start(vp, len);
39   buffer = silc_sftp_packet_encode_vp(packet, packet_buf, len, vp);
40   va_end(vp);
41
42   return buffer;
43 }
44
45 /* Same as silc_sftp_packet_encode but takes the variable argument list
46    pointer as argument. */
47
48 SilcBuffer silc_sftp_packet_encode_vp(SilcSFTPPacket packet, 
49                                       SilcBuffer packet_buf, SilcUInt32 len, 
50                                       va_list vp)
51 {
52   SilcBuffer buffer;
53   bool dyn;
54   int ret;
55
56   if (packet_buf) {
57     if (packet_buf->truelen < 4 + 1 + len) {
58       packet_buf = silc_buffer_realloc(packet_buf, 4 + 1 + len);
59       if (!packet_buf)
60         return NULL;
61     }
62
63     buffer = packet_buf;
64     dyn = FALSE;
65   } else {
66     buffer = silc_buffer_alloc(4 + 1 + len);
67     if (!buffer)
68       return NULL;
69     dyn = TRUE;
70   }
71
72   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
73   silc_buffer_format(buffer, 
74                      SILC_STR_UI_INT(len),
75                      SILC_STR_UI_CHAR(packet),
76                      SILC_STR_END);
77   silc_buffer_pull(buffer, 5);
78
79   ret = silc_buffer_format_vp(buffer, vp);
80   if (ret < 0) {
81     if (dyn)
82       silc_buffer_free(buffer);
83     return NULL;
84   }
85
86   silc_buffer_push(buffer, 5);
87
88   return buffer;
89 }
90
91 /* Decodes the SFTP packet data `packet' and return the SFTP packet type.
92    The payload of the packet is returned to the `payload' pointer. Returns
93    0 if error occurred during decoding. */
94
95 SilcSFTPPacket silc_sftp_packet_decode(SilcBuffer packet,
96                                        unsigned char **payload,
97                                        SilcUInt32 *payload_len)
98 {
99   SilcUInt32 len;
100   SilcUInt8 type;
101   int ret;
102
103   ret = silc_buffer_unformat(packet,
104                              SILC_STR_UI_INT(&len),
105                              SILC_STR_UI_CHAR(&type),
106                              SILC_STR_END);
107   if (ret < 0)
108     return 0;
109
110   if (type < SILC_SFTP_INIT || type > SILC_SFTP_EXTENDED_REPLY)
111     return 0;
112
113   if (len > (packet->len - 5))
114     return 0;
115
116   silc_buffer_pull(packet, 5);
117   ret = silc_buffer_unformat(packet, 
118                              SILC_STR_UI_XNSTRING(payload, len),
119                              SILC_STR_END);
120   if (ret < 0)
121     return 0;
122
123   silc_buffer_push(packet, 5);
124
125   *payload_len = len;
126
127   return (SilcSFTPPacket)type;
128 }
129
130 /* Encodes the SFTP attributes to a buffer and returns the allocated buffer.
131    The caller must free the buffer. */
132
133 SilcBuffer silc_sftp_attr_encode(SilcSFTPAttributes attr)
134 {
135   SilcBuffer buffer;
136   int i, ret;
137   SilcUInt32 len = 4;
138
139   if (attr->flags & SILC_SFTP_ATTR_SIZE)
140     len += 8;
141   if (attr->flags & SILC_SFTP_ATTR_UIDGID)
142     len += 8;
143   if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS)
144     len += 4;
145   if (attr->flags & SILC_SFTP_ATTR_ACMODTIME)
146     len += 8;
147   if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
148     len += 4;
149     for (i = 0; i < attr->extended_count; i++) {
150       len += 8;
151       len += attr->extended_type[i]->len;
152       len += attr->extended_data[i]->len;
153     }
154   }
155
156   buffer = silc_buffer_alloc_size(len);
157   if (!buffer)
158     return NULL;
159
160   silc_buffer_format(buffer, 
161                      SILC_STR_UI_INT(attr->flags), 
162                      SILC_STR_END);
163   silc_buffer_pull(buffer, 4);
164
165   if (attr->flags & SILC_SFTP_ATTR_SIZE) {
166     silc_buffer_format(buffer, 
167                        SILC_STR_UI_INT64(attr->size), 
168                        SILC_STR_END);
169     silc_buffer_pull(buffer, 8);
170   }
171
172   if (attr->flags & SILC_SFTP_ATTR_UIDGID) {
173     silc_buffer_format(buffer, 
174                        SILC_STR_UI_INT(attr->uid), 
175                        SILC_STR_UI_INT(attr->gid), 
176                        SILC_STR_END);
177     silc_buffer_pull(buffer, 8);
178   }
179
180   if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS) {
181     silc_buffer_format(buffer, 
182                        SILC_STR_UI_INT(attr->permissions), 
183                        SILC_STR_END);
184     silc_buffer_pull(buffer, 4);
185   }
186
187   if (attr->flags & SILC_SFTP_ATTR_ACMODTIME) {
188     silc_buffer_format(buffer, 
189                        SILC_STR_UI_INT(attr->atime), 
190                        SILC_STR_UI_INT(attr->mtime), 
191                        SILC_STR_END);
192     silc_buffer_pull(buffer, 8);
193   }
194
195   if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
196     silc_buffer_format(buffer, 
197                        SILC_STR_UI_INT(attr->extended_count), 
198                        SILC_STR_END);
199     silc_buffer_pull(buffer, 4);
200
201     for (i = 0; i < attr->extended_count; i++) {
202       ret = 
203         silc_buffer_format(buffer, 
204                            SILC_STR_UI_INT(attr->extended_type[i]->len),
205                            SILC_STR_UI_XNSTRING(attr->extended_type[i]->data,
206                                                 attr->extended_type[i]->len),
207                            SILC_STR_UI_INT(attr->extended_data[i]->len),
208                            SILC_STR_UI_XNSTRING(attr->extended_data[i]->data,
209                                                 attr->extended_data[i]->len),
210                            SILC_STR_END);
211       silc_buffer_pull(buffer, ret);
212     }
213   }
214
215   silc_buffer_push(buffer, buffer->data - buffer->head);
216
217   return buffer;
218 }
219
220 /* Decodes SilcSFTPAttributes from the buffer `buffer'. Returns the allocated
221    attributes that the caller must free or NULL on error. */
222
223 SilcSFTPAttributes silc_sftp_attr_decode(SilcBuffer buffer)
224 {
225   SilcSFTPAttributes attr;
226
227   attr = silc_calloc(1, sizeof(*attr));
228   if (!attr)
229     return NULL;
230
231   if (silc_buffer_unformat(buffer, 
232                            SILC_STR_UI_INT(&attr->flags), 
233                            SILC_STR_END) < 0)
234     goto out;
235
236   silc_buffer_pull(buffer, 4);
237
238   if (attr->flags & SILC_SFTP_ATTR_SIZE) {
239     if (silc_buffer_unformat(buffer, 
240                              SILC_STR_UI_INT64(&attr->size), 
241                              SILC_STR_END) < 0)
242       goto out;
243
244     silc_buffer_pull(buffer, 8);
245   }
246
247   if (attr->flags & SILC_SFTP_ATTR_UIDGID) {
248     if (silc_buffer_unformat(buffer, 
249                              SILC_STR_UI_INT(&attr->uid), 
250                              SILC_STR_UI_INT(&attr->gid), 
251                              SILC_STR_END) < 0)
252       goto out;
253
254     silc_buffer_pull(buffer, 8);
255   }
256
257   if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS) {
258     if (silc_buffer_unformat(buffer, 
259                              SILC_STR_UI_INT(&attr->permissions), 
260                              SILC_STR_END) < 0)
261       goto out;
262
263     silc_buffer_pull(buffer, 4);
264   }
265
266   if (attr->flags & SILC_SFTP_ATTR_ACMODTIME) {
267     if (silc_buffer_unformat(buffer, 
268                              SILC_STR_UI_INT(&attr->atime), 
269                              SILC_STR_UI_INT(&attr->mtime), 
270                              SILC_STR_END) < 0)
271       goto out;
272
273     silc_buffer_pull(buffer, 8);
274   }
275
276   if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
277     int i;
278
279     if (silc_buffer_unformat(buffer, 
280                              SILC_STR_UI_INT(&attr->extended_count), 
281                              SILC_STR_END) < 0)
282       goto out;
283
284     silc_buffer_pull(buffer, 4);
285
286     attr->extended_type = silc_calloc(attr->extended_count, 
287                                       sizeof(*attr->extended_type));
288     attr->extended_data = silc_calloc(attr->extended_count, 
289                                       sizeof(*attr->extended_data));
290     if (!attr->extended_type || !attr->extended_data)
291       return NULL;
292
293     for (i = 0; i < attr->extended_count; i++) {
294       unsigned char *tmp, *tmp2;
295       SilcUInt32 tmp_len, tmp2_len;
296
297       if (silc_buffer_unformat(buffer, 
298                                SILC_STR_UI32_NSTRING(&tmp, &tmp_len),
299                                SILC_STR_UI32_NSTRING(&tmp2, &tmp2_len),
300                                SILC_STR_END) < 0)
301         goto out;
302
303       attr->extended_type[i] = silc_buffer_alloc(tmp_len);
304       attr->extended_data[i] = silc_buffer_alloc(tmp2_len);
305       if (!attr->extended_type[i] || !attr->extended_data[i])
306         return NULL;
307       silc_buffer_put(attr->extended_type[i], tmp, tmp_len);
308       silc_buffer_put(attr->extended_data[i], tmp2, tmp2_len);
309
310       silc_buffer_pull(buffer, tmp_len + 4 + tmp2_len + 4);
311     }
312   }
313
314   return attr;
315
316  out:
317   silc_sftp_attr_free(attr);
318   return NULL;
319 }
320
321 /* Frees the attributes context and its internals. */
322
323 void silc_sftp_attr_free(SilcSFTPAttributes attr)
324 {
325   int i;
326
327   for (i = 0; i < attr->extended_count; i++) {
328     silc_buffer_free(attr->extended_type[i]);
329     silc_buffer_free(attr->extended_data[i]);
330   }
331   silc_free(attr->extended_type);
332   silc_free(attr->extended_data);
333   silc_free(attr);
334 }
335
336 /* Adds an entry to the `name' context. */
337
338 void silc_sftp_name_add(SilcSFTPName name, const char *short_name,
339                         const char *long_name, SilcSFTPAttributes attrs)
340 {
341   name->filename = silc_realloc(name->filename, sizeof(*name->filename) *
342                                 (name->count + 1));
343   name->long_filename = silc_realloc(name->long_filename, 
344                                      sizeof(*name->long_filename) *
345                                      (name->count + 1));
346   name->attrs = silc_realloc(name->attrs, sizeof(*name->attrs) *
347                              (name->count + 1));
348   if (!name->filename || !name->long_filename || !name->attrs)
349     return;
350
351   name->filename[name->count] = strdup(short_name);
352   name->long_filename[name->count] = strdup(long_name);
353   name->attrs[name->count] = attrs;
354   name->count++;
355 }
356
357 /* Encodes the SilcSFTPName to a buffer and returns the allocated buffer. 
358    The caller must free the buffer. */
359
360 SilcBuffer silc_sftp_name_encode(SilcSFTPName name)
361 {
362   SilcBuffer buffer;
363   int i, len = 4;
364   SilcBuffer *attr_buf;
365
366   attr_buf = silc_calloc(name->count, sizeof(*attr_buf));
367   if (!attr_buf)
368     return NULL;
369
370   for (i = 0; i < name->count; i++) {
371     len += (8 + strlen(name->filename[i]) + strlen(name->long_filename[i]));
372     attr_buf[i] = silc_sftp_attr_encode(name->attrs[i]);
373     if (!attr_buf[i])
374       return NULL;
375     len += attr_buf[i]->len;
376   }
377
378   buffer = silc_buffer_alloc(len);
379   if (!buffer)
380     return NULL;
381   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
382
383   silc_buffer_format(buffer,
384                      SILC_STR_UI_INT(name->count),
385                      SILC_STR_END);
386   silc_buffer_pull(buffer, 4);
387
388   for (i = 0; i < name->count; i++) {
389     len =
390       silc_buffer_format(buffer,
391                          SILC_STR_UI_INT(strlen(name->filename[i])),
392                          SILC_STR_UI32_STRING(name->filename[i]),
393                          SILC_STR_UI_INT(strlen(name->long_filename[i])),
394                          SILC_STR_UI32_STRING(name->long_filename[i]),
395                          SILC_STR_UI_XNSTRING(attr_buf[i]->data,
396                                               attr_buf[i]->len),
397                          SILC_STR_END);
398
399     silc_buffer_pull(buffer, len);
400     silc_free(attr_buf[i]);
401   }
402   silc_free(attr_buf);
403
404   silc_buffer_push(buffer, buffer->data - buffer->head);
405
406   return buffer;
407 }
408
409 /* Decodes a SilcSFTPName structure from the `buffer' that must include
410    `count' many name, longname and attribute values. Returns the allocated
411    structure or NULL on error. */
412
413 SilcSFTPName silc_sftp_name_decode(SilcUInt32 count, SilcBuffer buffer)
414 {
415   SilcSFTPName name;
416   int i;
417   int ret;
418
419   name = silc_calloc(1, sizeof(*name));
420   if (!name)
421     return NULL;
422   name->filename = silc_calloc(count, sizeof(*name->filename));
423   name->long_filename = silc_calloc(count, sizeof(*name->filename));
424   name->attrs = silc_calloc(count, sizeof(*name->attrs));
425   if (!name->filename || !name->long_filename || !name->attrs) {
426     silc_sftp_name_free(name);
427     return NULL;
428   }
429   name->count = count;
430
431   for (i = 0; i < count; i++) {
432     ret = 
433       silc_buffer_unformat(buffer,
434                            SILC_STR_UI32_STRING_ALLOC(&name->filename[i]),
435                            SILC_STR_UI32_STRING_ALLOC(&name->long_filename[i]),
436                            SILC_STR_END);
437     if (ret < 0) {
438       silc_sftp_name_free(name);
439       return NULL;
440     }
441
442     silc_buffer_pull(buffer, ret);
443
444     /* Decode attributes, this will pull the `buffer' to correct place
445        for next round automatically. */
446     name->attrs[i] = silc_sftp_attr_decode(buffer);
447     if (!name->attrs[i]) {
448       silc_sftp_name_free(name);
449       return NULL;
450     }
451   }
452
453   return name;
454 }
455
456 /* Frees the name context and its internals. */
457
458 void silc_sftp_name_free(SilcSFTPName name)
459 {
460   int i;
461
462   for (i = 0; i < name->count; i++) {
463     silc_free(name->filename[i]);
464     silc_free(name->long_filename[i]);
465     silc_sftp_attr_free(name->attrs[i]);
466   }
467
468   silc_free(name->filename);
469   silc_free(name->long_filename);
470   silc_free(name->attrs);
471   silc_free(name);
472 }
473
474 /* Maps errno to SFTP status message. */
475
476 SilcSFTPStatus silc_sftp_map_errno(int err)
477 {
478   SilcSFTPStatus ret;
479
480   switch (err) {
481   case 0:
482     ret = SILC_SFTP_STATUS_OK;
483     break;
484   case ENOENT:
485   case ENOTDIR:
486   case EBADF:
487     ret = SILC_SFTP_STATUS_NO_SUCH_FILE;
488     break;
489   case EPERM:
490   case EACCES:
491   case EFAULT:
492     ret = SILC_SFTP_STATUS_PERMISSION_DENIED;
493     break;
494   case ENAMETOOLONG:
495   case EINVAL:
496     ret = SILC_SFTP_STATUS_BAD_MESSAGE;
497     break;
498   default:
499     ret = SILC_SFTP_STATUS_FAILURE;
500     break;
501   }
502
503   return ret;
504 }