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