Changed SILC code to use new SRT and SCT APIs.
[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 - 2008 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 "silc.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 (silc_buffer_truelen(packet_buf) < 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, 4 + 1 + len);
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 and -1 if partial packet was
94    received. */
95
96 SilcSFTPPacket silc_sftp_packet_decode(SilcBuffer packet,
97                                        unsigned char **payload,
98                                        SilcUInt32 *payload_len)
99 {
100   SilcUInt32 len;
101   SilcUInt8 type;
102   int ret;
103
104   ret = silc_buffer_unformat(packet,
105                              SILC_STR_UI_INT(&len),
106                              SILC_STR_UI_CHAR(&type),
107                              SILC_STR_END);
108   if (ret < 0)
109     return 0;
110
111   if (type < SILC_SFTP_INIT || type > SILC_SFTP_EXTENDED_REPLY)
112     return 0;
113
114   if (len > (silc_buffer_len(packet) - 5))
115     return -1;
116
117   silc_buffer_pull(packet, 5);
118   ret = silc_buffer_unformat(packet,
119                              SILC_STR_UI_XNSTRING(payload, len),
120                              SILC_STR_END);
121   if (ret < 0)
122     return 0;
123
124   silc_buffer_push(packet, 5);
125
126   *payload_len = len;
127
128   return (SilcSFTPPacket)type;
129 }
130
131 /* Encodes the SFTP attributes to a buffer and returns the allocated buffer.
132    The caller must free the buffer. */
133
134 SilcBuffer silc_sftp_attr_encode(SilcSFTPAttributes attr)
135 {
136   SilcBuffer buffer;
137   int i, ret;
138   SilcUInt32 len = 4;
139
140   if (attr->flags & SILC_SFTP_ATTR_SIZE)
141     len += 8;
142   if (attr->flags & SILC_SFTP_ATTR_UIDGID)
143     len += 8;
144   if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS)
145     len += 4;
146   if (attr->flags & SILC_SFTP_ATTR_ACMODTIME)
147     len += 8;
148   if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
149     len += 4;
150     for (i = 0; i < attr->extended_count; i++) {
151       len += 8;
152       len += silc_buffer_len(attr->extended_type[i]);
153       len += silc_buffer_len(attr->extended_data[i]);
154     }
155   }
156
157   buffer = silc_buffer_alloc_size(len);
158   if (!buffer)
159     return NULL;
160
161   silc_buffer_format(buffer,
162                      SILC_STR_UI_INT(attr->flags),
163                      SILC_STR_END);
164   silc_buffer_pull(buffer, 4);
165
166   if (attr->flags & SILC_SFTP_ATTR_SIZE) {
167     silc_buffer_format(buffer,
168                        SILC_STR_UI_INT64(attr->size),
169                        SILC_STR_END);
170     silc_buffer_pull(buffer, 8);
171   }
172
173   if (attr->flags & SILC_SFTP_ATTR_UIDGID) {
174     silc_buffer_format(buffer,
175                        SILC_STR_UI_INT(attr->uid),
176                        SILC_STR_UI_INT(attr->gid),
177                        SILC_STR_END);
178     silc_buffer_pull(buffer, 8);
179   }
180
181   if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS) {
182     silc_buffer_format(buffer,
183                        SILC_STR_UI_INT(attr->permissions),
184                        SILC_STR_END);
185     silc_buffer_pull(buffer, 4);
186   }
187
188   if (attr->flags & SILC_SFTP_ATTR_ACMODTIME) {
189     silc_buffer_format(buffer,
190                        SILC_STR_UI_INT(attr->atime),
191                        SILC_STR_UI_INT(attr->mtime),
192                        SILC_STR_END);
193     silc_buffer_pull(buffer, 8);
194   }
195
196   if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
197     silc_buffer_format(buffer,
198                        SILC_STR_UI_INT(attr->extended_count),
199                        SILC_STR_END);
200     silc_buffer_pull(buffer, 4);
201
202     for (i = 0; i < attr->extended_count; i++) {
203       ret =
204         silc_buffer_format(
205                    buffer,
206                    SILC_STR_UI_INT(silc_buffer_len(attr->extended_type[i])),
207                    SILC_STR_DATA(silc_buffer_data(attr->extended_type[i]),
208                                  silc_buffer_len(attr->extended_type[i])),
209                    SILC_STR_UI_INT(silc_buffer_len(attr->extended_data[i])),
210                    SILC_STR_DATA(silc_buffer_data(attr->extended_data[i]),
211                                  silc_buffer_len(attr->extended_data[i])),
212                    SILC_STR_END);
213       silc_buffer_pull(buffer, ret);
214     }
215   }
216
217   silc_buffer_push(buffer, buffer->data - buffer->head);
218
219   return buffer;
220 }
221
222 /* Decodes SilcSFTPAttributes from the buffer `buffer'. Returns the allocated
223    attributes that the caller must free or NULL on error. */
224
225 SilcSFTPAttributes silc_sftp_attr_decode(SilcBuffer buffer)
226 {
227   SilcSFTPAttributes attr;
228
229   attr = silc_calloc(1, sizeof(*attr));
230   if (!attr)
231     return NULL;
232
233   if (silc_buffer_unformat(buffer,
234                            SILC_STR_UI_INT(&attr->flags),
235                            SILC_STR_END) < 0)
236     goto out;
237
238   silc_buffer_pull(buffer, 4);
239
240   if (attr->flags & SILC_SFTP_ATTR_SIZE) {
241     if (silc_buffer_unformat(buffer,
242                              SILC_STR_UI_INT64(&attr->size),
243                              SILC_STR_END) < 0)
244       goto out;
245
246     silc_buffer_pull(buffer, 8);
247   }
248
249   if (attr->flags & SILC_SFTP_ATTR_UIDGID) {
250     if (silc_buffer_unformat(buffer,
251                              SILC_STR_UI_INT(&attr->uid),
252                              SILC_STR_UI_INT(&attr->gid),
253                              SILC_STR_END) < 0)
254       goto out;
255
256     silc_buffer_pull(buffer, 8);
257   }
258
259   if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS) {
260     if (silc_buffer_unformat(buffer,
261                              SILC_STR_UI_INT(&attr->permissions),
262                              SILC_STR_END) < 0)
263       goto out;
264
265     silc_buffer_pull(buffer, 4);
266   }
267
268   if (attr->flags & SILC_SFTP_ATTR_ACMODTIME) {
269     if (silc_buffer_unformat(buffer,
270                              SILC_STR_UI_INT(&attr->atime),
271                              SILC_STR_UI_INT(&attr->mtime),
272                              SILC_STR_END) < 0)
273       goto out;
274
275     silc_buffer_pull(buffer, 8);
276   }
277
278   if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
279     int i;
280
281     if (silc_buffer_unformat(buffer,
282                              SILC_STR_UI_INT(&attr->extended_count),
283                              SILC_STR_END) < 0)
284       goto out;
285
286     silc_buffer_pull(buffer, 4);
287
288     attr->extended_type = silc_calloc(attr->extended_count,
289                                       sizeof(*attr->extended_type));
290     attr->extended_data = silc_calloc(attr->extended_count,
291                                       sizeof(*attr->extended_data));
292     if (!attr->extended_type || !attr->extended_data)
293       return NULL;
294
295     for (i = 0; i < attr->extended_count; i++) {
296       unsigned char *tmp, *tmp2;
297       SilcUInt32 tmp_len, tmp2_len;
298
299       if (silc_buffer_unformat(buffer,
300                                SILC_STR_UI32_NSTRING(&tmp, &tmp_len),
301                                SILC_STR_UI32_NSTRING(&tmp2, &tmp2_len),
302                                SILC_STR_END) < 0)
303         goto out;
304
305       attr->extended_type[i] = silc_buffer_alloc(tmp_len);
306       attr->extended_data[i] = silc_buffer_alloc(tmp2_len);
307       if (!attr->extended_type[i] || !attr->extended_data[i])
308         return NULL;
309       silc_buffer_put(attr->extended_type[i], tmp, tmp_len);
310       silc_buffer_put(attr->extended_data[i], tmp2, tmp2_len);
311
312       silc_buffer_pull(buffer, tmp_len + 4 + tmp2_len + 4);
313     }
314   }
315
316   return attr;
317
318  out:
319   silc_sftp_attr_free(attr);
320   return NULL;
321 }
322
323 /* Frees the attributes context and its internals. */
324
325 void silc_sftp_attr_free(SilcSFTPAttributes attr)
326 {
327   int i;
328
329   for (i = 0; i < attr->extended_count; i++) {
330     silc_buffer_free(attr->extended_type[i]);
331     silc_buffer_free(attr->extended_data[i]);
332   }
333   silc_free(attr->extended_type);
334   silc_free(attr->extended_data);
335   silc_free(attr);
336 }
337
338 /* Adds an entry to the `name' context. */
339
340 void silc_sftp_name_add(SilcSFTPName name, const char *short_name,
341                         const char *long_name, SilcSFTPAttributes attrs)
342 {
343   name->filename = silc_realloc(name->filename, sizeof(*name->filename) *
344                                 (name->count + 1));
345   name->long_filename = silc_realloc(name->long_filename,
346                                      sizeof(*name->long_filename) *
347                                      (name->count + 1));
348   name->attrs = silc_realloc(name->attrs, sizeof(*name->attrs) *
349                              (name->count + 1));
350   if (!name->filename || !name->long_filename || !name->attrs)
351     return;
352
353   name->filename[name->count] = strdup(short_name);
354   name->long_filename[name->count] = strdup(long_name);
355   name->attrs[name->count] = attrs;
356   name->count++;
357 }
358
359 /* Encodes the SilcSFTPName to a buffer and returns the allocated buffer.
360    The caller must free the buffer. */
361
362 SilcBuffer silc_sftp_name_encode(SilcSFTPName name)
363 {
364   SilcBuffer buffer;
365   int i, len = 4;
366   SilcBuffer *attr_buf;
367
368   attr_buf = silc_calloc(name->count, sizeof(*attr_buf));
369   if (!attr_buf)
370     return NULL;
371
372   for (i = 0; i < name->count; i++) {
373     len += (8 + strlen(name->filename[i]) + strlen(name->long_filename[i]));
374     attr_buf[i] = silc_sftp_attr_encode(name->attrs[i]);
375     if (!attr_buf[i])
376       return NULL;
377     len += silc_buffer_len(attr_buf[i]);
378   }
379
380   buffer = silc_buffer_alloc(len);
381   if (!buffer)
382     return NULL;
383   silc_buffer_end(buffer);
384
385   silc_buffer_format(buffer,
386                      SILC_STR_UI_INT(name->count),
387                      SILC_STR_END);
388   silc_buffer_pull(buffer, 4);
389
390   for (i = 0; i < name->count; i++) {
391     len =
392       silc_buffer_format(buffer,
393                          SILC_STR_UI_INT(strlen(name->filename[i])),
394                          SILC_STR_UI32_STRING(name->filename[i]),
395                          SILC_STR_UI_INT(strlen(name->long_filename[i])),
396                          SILC_STR_UI32_STRING(name->long_filename[i]),
397                          SILC_STR_DATA(silc_buffer_data(attr_buf[i]),
398                                        silc_buffer_len(attr_buf[i])),
399                          SILC_STR_END);
400
401     silc_buffer_pull(buffer, len);
402     silc_free(attr_buf[i]);
403   }
404   silc_free(attr_buf);
405
406   silc_buffer_push(buffer, buffer->data - buffer->head);
407
408   return buffer;
409 }
410
411 /* Decodes a SilcSFTPName structure from the `buffer' that must include
412    `count' many name, longname and attribute values. Returns the allocated
413    structure or NULL on error. */
414
415 SilcSFTPName silc_sftp_name_decode(SilcUInt32 count, SilcBuffer buffer)
416 {
417   SilcSFTPName name;
418   int i;
419   int ret;
420
421   name = silc_calloc(1, sizeof(*name));
422   if (!name)
423     return NULL;
424   name->filename = silc_calloc(count, sizeof(*name->filename));
425   name->long_filename = silc_calloc(count, sizeof(*name->filename));
426   name->attrs = silc_calloc(count, sizeof(*name->attrs));
427   if (!name->filename || !name->long_filename || !name->attrs) {
428     silc_sftp_name_free(name);
429     return NULL;
430   }
431   name->count = count;
432
433   for (i = 0; i < count; i++) {
434     ret =
435       silc_buffer_unformat(buffer,
436                            SILC_STR_UI32_STRING_ALLOC(&name->filename[i]),
437                            SILC_STR_UI32_STRING_ALLOC(&name->long_filename[i]),
438                            SILC_STR_END);
439     if (ret < 0) {
440       silc_sftp_name_free(name);
441       return NULL;
442     }
443
444     silc_buffer_pull(buffer, ret);
445
446     /* Decode attributes, this will pull the `buffer' to correct place
447        for next round automatically. */
448     name->attrs[i] = silc_sftp_attr_decode(buffer);
449     if (!name->attrs[i]) {
450       silc_sftp_name_free(name);
451       return NULL;
452     }
453   }
454
455   return name;
456 }
457
458 /* Frees the name context and its internals. */
459
460 void silc_sftp_name_free(SilcSFTPName name)
461 {
462   int i;
463
464   for (i = 0; i < name->count; i++) {
465     silc_free(name->filename[i]);
466     silc_free(name->long_filename[i]);
467     silc_sftp_attr_free(name->attrs[i]);
468   }
469
470   silc_free(name->filename);
471   silc_free(name->long_filename);
472   silc_free(name->attrs);
473   silc_free(name);
474 }
475
476 /* Maps errno to SFTP status message. */
477
478 SilcSFTPStatus silc_sftp_map_errno(SilcResult err)
479 {
480   SilcSFTPStatus ret;
481
482   switch (err) {
483   case 0:
484     ret = SILC_SFTP_STATUS_OK;
485     break;
486   case SILC_ERR_NOT_DIRECTORY:
487   case SILC_ERR_NO_SUCH_FILE:
488   case SILC_ERR_BAD_FD:
489     ret = SILC_SFTP_STATUS_NO_SUCH_FILE;
490     break;
491   case SILC_ERR_PERMISSION_DENIED:
492     ret = SILC_SFTP_STATUS_PERMISSION_DENIED;
493     break;
494   case SILC_ERR_INVALID_ARGUMENT:
495     ret = SILC_SFTP_STATUS_BAD_MESSAGE;
496     break;
497   default:
498     ret = SILC_SFTP_STATUS_FAILURE;
499     break;
500   }
501
502   return ret;
503 }