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