Ported SFTP library to new util library.
[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 - 2007 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. */
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 > (silc_buffer_len(packet) - 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 += silc_buffer_len(attr->extended_type[i]);
152       len += silc_buffer_len(attr->extended_data[i]);
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(
204                    buffer,
205                    SILC_STR_UI_INT(silc_buffer_len(attr->extended_type[i])),
206                    SILC_STR_DATA(silc_buffer_data(attr->extended_type[i]),
207                                  silc_buffer_len(attr->extended_type[i])),
208                    SILC_STR_UI_INT(silc_buffer_len(attr->extended_data[i])),
209                    SILC_STR_DATA(silc_buffer_data(attr->extended_data[i]),
210                                  silc_buffer_len(attr->extended_data[i])),
211                    SILC_STR_END);
212       silc_buffer_pull(buffer, ret);
213     }
214   }
215
216   silc_buffer_push(buffer, buffer->data - buffer->head);
217
218   return buffer;
219 }
220
221 /* Decodes SilcSFTPAttributes from the buffer `buffer'. Returns the allocated
222    attributes that the caller must free or NULL on error. */
223
224 SilcSFTPAttributes silc_sftp_attr_decode(SilcBuffer buffer)
225 {
226   SilcSFTPAttributes attr;
227
228   attr = silc_calloc(1, sizeof(*attr));
229   if (!attr)
230     return NULL;
231
232   if (silc_buffer_unformat(buffer,
233                            SILC_STR_UI_INT(&attr->flags),
234                            SILC_STR_END) < 0)
235     goto out;
236
237   silc_buffer_pull(buffer, 4);
238
239   if (attr->flags & SILC_SFTP_ATTR_SIZE) {
240     if (silc_buffer_unformat(buffer,
241                              SILC_STR_UI_INT64(&attr->size),
242                              SILC_STR_END) < 0)
243       goto out;
244
245     silc_buffer_pull(buffer, 8);
246   }
247
248   if (attr->flags & SILC_SFTP_ATTR_UIDGID) {
249     if (silc_buffer_unformat(buffer,
250                              SILC_STR_UI_INT(&attr->uid),
251                              SILC_STR_UI_INT(&attr->gid),
252                              SILC_STR_END) < 0)
253       goto out;
254
255     silc_buffer_pull(buffer, 8);
256   }
257
258   if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS) {
259     if (silc_buffer_unformat(buffer,
260                              SILC_STR_UI_INT(&attr->permissions),
261                              SILC_STR_END) < 0)
262       goto out;
263
264     silc_buffer_pull(buffer, 4);
265   }
266
267   if (attr->flags & SILC_SFTP_ATTR_ACMODTIME) {
268     if (silc_buffer_unformat(buffer,
269                              SILC_STR_UI_INT(&attr->atime),
270                              SILC_STR_UI_INT(&attr->mtime),
271                              SILC_STR_END) < 0)
272       goto out;
273
274     silc_buffer_pull(buffer, 8);
275   }
276
277   if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
278     int i;
279
280     if (silc_buffer_unformat(buffer,
281                              SILC_STR_UI_INT(&attr->extended_count),
282                              SILC_STR_END) < 0)
283       goto out;
284
285     silc_buffer_pull(buffer, 4);
286
287     attr->extended_type = silc_calloc(attr->extended_count,
288                                       sizeof(*attr->extended_type));
289     attr->extended_data = silc_calloc(attr->extended_count,
290                                       sizeof(*attr->extended_data));
291     if (!attr->extended_type || !attr->extended_data)
292       return NULL;
293
294     for (i = 0; i < attr->extended_count; i++) {
295       unsigned char *tmp, *tmp2;
296       SilcUInt32 tmp_len, tmp2_len;
297
298       if (silc_buffer_unformat(buffer,
299                                SILC_STR_UI32_NSTRING(&tmp, &tmp_len),
300                                SILC_STR_UI32_NSTRING(&tmp2, &tmp2_len),
301                                SILC_STR_END) < 0)
302         goto out;
303
304       attr->extended_type[i] = silc_buffer_alloc(tmp_len);
305       attr->extended_data[i] = silc_buffer_alloc(tmp2_len);
306       if (!attr->extended_type[i] || !attr->extended_data[i])
307         return NULL;
308       silc_buffer_put(attr->extended_type[i], tmp, tmp_len);
309       silc_buffer_put(attr->extended_data[i], tmp2, tmp2_len);
310
311       silc_buffer_pull(buffer, tmp_len + 4 + tmp2_len + 4);
312     }
313   }
314
315   return attr;
316
317  out:
318   silc_sftp_attr_free(attr);
319   return NULL;
320 }
321
322 /* Frees the attributes context and its internals. */
323
324 void silc_sftp_attr_free(SilcSFTPAttributes attr)
325 {
326   int i;
327
328   for (i = 0; i < attr->extended_count; i++) {
329     silc_buffer_free(attr->extended_type[i]);
330     silc_buffer_free(attr->extended_data[i]);
331   }
332   silc_free(attr->extended_type);
333   silc_free(attr->extended_data);
334   silc_free(attr);
335 }
336
337 /* Adds an entry to the `name' context. */
338
339 void silc_sftp_name_add(SilcSFTPName name, const char *short_name,
340                         const char *long_name, SilcSFTPAttributes attrs)
341 {
342   name->filename = silc_realloc(name->filename, sizeof(*name->filename) *
343                                 (name->count + 1));
344   name->long_filename = silc_realloc(name->long_filename,
345                                      sizeof(*name->long_filename) *
346                                      (name->count + 1));
347   name->attrs = silc_realloc(name->attrs, sizeof(*name->attrs) *
348                              (name->count + 1));
349   if (!name->filename || !name->long_filename || !name->attrs)
350     return;
351
352   name->filename[name->count] = strdup(short_name);
353   name->long_filename[name->count] = strdup(long_name);
354   name->attrs[name->count] = attrs;
355   name->count++;
356 }
357
358 /* Encodes the SilcSFTPName to a buffer and returns the allocated buffer.
359    The caller must free the buffer. */
360
361 SilcBuffer silc_sftp_name_encode(SilcSFTPName name)
362 {
363   SilcBuffer buffer;
364   int i, len = 4;
365   SilcBuffer *attr_buf;
366
367   attr_buf = silc_calloc(name->count, sizeof(*attr_buf));
368   if (!attr_buf)
369     return NULL;
370
371   for (i = 0; i < name->count; i++) {
372     len += (8 + strlen(name->filename[i]) + strlen(name->long_filename[i]));
373     attr_buf[i] = silc_sftp_attr_encode(name->attrs[i]);
374     if (!attr_buf[i])
375       return NULL;
376     len += silc_buffer_len(attr_buf[i]);
377   }
378
379   buffer = silc_buffer_alloc(len);
380   if (!buffer)
381     return NULL;
382   silc_buffer_end(buffer);
383
384   silc_buffer_format(buffer,
385                      SILC_STR_UI_INT(name->count),
386                      SILC_STR_END);
387   silc_buffer_pull(buffer, 4);
388
389   for (i = 0; i < name->count; i++) {
390     len =
391       silc_buffer_format(buffer,
392                          SILC_STR_UI_INT(strlen(name->filename[i])),
393                          SILC_STR_UI32_STRING(name->filename[i]),
394                          SILC_STR_UI_INT(strlen(name->long_filename[i])),
395                          SILC_STR_UI32_STRING(name->long_filename[i]),
396                          SILC_STR_DATA(silc_buffer_data(attr_buf[i]),
397                                        silc_buffer_len(attr_buf[i])),
398                          SILC_STR_END);
399
400     silc_buffer_pull(buffer, len);
401     silc_free(attr_buf[i]);
402   }
403   silc_free(attr_buf);
404
405   silc_buffer_push(buffer, buffer->data - buffer->head);
406
407   return buffer;
408 }
409
410 /* Decodes a SilcSFTPName structure from the `buffer' that must include
411    `count' many name, longname and attribute values. Returns the allocated
412    structure or NULL on error. */
413
414 SilcSFTPName silc_sftp_name_decode(SilcUInt32 count, SilcBuffer buffer)
415 {
416   SilcSFTPName name;
417   int i;
418   int ret;
419
420   name = silc_calloc(1, sizeof(*name));
421   if (!name)
422     return NULL;
423   name->filename = silc_calloc(count, sizeof(*name->filename));
424   name->long_filename = silc_calloc(count, sizeof(*name->filename));
425   name->attrs = silc_calloc(count, sizeof(*name->attrs));
426   if (!name->filename || !name->long_filename || !name->attrs) {
427     silc_sftp_name_free(name);
428     return NULL;
429   }
430   name->count = count;
431
432   for (i = 0; i < count; i++) {
433     ret =
434       silc_buffer_unformat(buffer,
435                            SILC_STR_UI32_STRING_ALLOC(&name->filename[i]),
436                            SILC_STR_UI32_STRING_ALLOC(&name->long_filename[i]),
437                            SILC_STR_END);
438     if (ret < 0) {
439       silc_sftp_name_free(name);
440       return NULL;
441     }
442
443     silc_buffer_pull(buffer, ret);
444
445     /* Decode attributes, this will pull the `buffer' to correct place
446        for next round automatically. */
447     name->attrs[i] = silc_sftp_attr_decode(buffer);
448     if (!name->attrs[i]) {
449       silc_sftp_name_free(name);
450       return NULL;
451     }
452   }
453
454   return name;
455 }
456
457 /* Frees the name context and its internals. */
458
459 void silc_sftp_name_free(SilcSFTPName name)
460 {
461   int i;
462
463   for (i = 0; i < name->count; i++) {
464     silc_free(name->filename[i]);
465     silc_free(name->long_filename[i]);
466     silc_sftp_attr_free(name->attrs[i]);
467   }
468
469   silc_free(name->filename);
470   silc_free(name->long_filename);
471   silc_free(name->attrs);
472   silc_free(name);
473 }
474
475 /* Maps errno to SFTP status message. */
476
477 SilcSFTPStatus silc_sftp_map_errno(int err)
478 {
479   SilcSFTPStatus ret;
480
481   switch (err) {
482   case 0:
483     ret = SILC_SFTP_STATUS_OK;
484     break;
485   case ENOENT:
486   case ENOTDIR:
487   case EBADF:
488     ret = SILC_SFTP_STATUS_NO_SUCH_FILE;
489     break;
490   case EPERM:
491   case EACCES:
492   case EFAULT:
493     ret = SILC_SFTP_STATUS_PERMISSION_DENIED;
494     break;
495   case ENAMETOOLONG:
496   case EINVAL:
497     ret = SILC_SFTP_STATUS_BAD_MESSAGE;
498     break;
499   default:
500     ret = SILC_SFTP_STATUS_FAILURE;
501     break;
502   }
503
504   return ret;
505 }