d0030a0cc2f1ffe7799f8ca670b1940f68d1dbd9
[silc.git] / lib / silcsftp / sftp_server.c
1 /*
2
3   sftp_server.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 /* SFTP Server context */
26 typedef struct {
27   SilcSocketConnection sock;
28   SilcSFTPSendPacketCallback send_packet;
29   void *send_context;
30   SilcSFTPFilesystem fs;
31   void *fs_context;
32 } *SilcSFTPServer;
33
34 /* General routine to send SFTP packet to the SFTP client. */
35
36 static void silc_sftp_send_packet(SilcSFTPServer sftp,
37                                   SilcSFTPPacket type, 
38                                   uint32 len, ...)
39 {
40   SilcBuffer packet;
41   va_list vp;
42
43   va_start(vp, len);
44   packet = silc_sftp_packet_encode_vp(type, len, vp);
45   va_end(vp);
46
47   if (!packet)
48     return;
49
50   SILC_LOG_HEXDUMP(("SFTP packet to client"), packet->data, packet->len);
51
52   /* Send the packet */
53   (*sftp->send_packet)(sftp->sock, packet, sftp->send_context);
54
55   silc_buffer_free(packet);
56 }
57
58 /* Sends error to the client */
59
60 static void silc_sftp_send_error(SilcSFTPServer sftp,
61                                  SilcSFTPStatus status,
62                                  uint32 id)
63 {
64   SILC_LOG_DEBUG(("Send error %d", status));
65
66   silc_sftp_send_packet(sftp, SILC_SFTP_STATUS, 16,
67                         SILC_STR_UI_INT(id),
68                         SILC_STR_UI_INT(status),
69                         SILC_STR_UI_INT(0),      /* Error */
70                         SILC_STR_UI_INT(0),      /* Language tag */
71                         SILC_STR_END);
72 }
73
74 /* Status callback */
75
76 static void silc_sftp_server_status(SilcSFTP sftp,
77                                     SilcSFTPStatus status,
78                                     const char *message,
79                                     const char *language_tag,
80                                     void *context)
81 {
82   SilcSFTPServer server = (SilcSFTPServer)sftp;
83   uint32 id = (uint32)context;
84   int mlen, llen;
85
86   SILC_LOG_DEBUG(("Status callback"));
87   SILC_LOG_DEBUG(("Request ID: %d", id));
88   
89   if (!message)
90     message = "";
91   if (!language_tag)
92     language_tag = "";
93   mlen = strlen(message);
94   llen = strlen(language_tag);
95
96   silc_sftp_send_packet(server, SILC_SFTP_STATUS, 16 + mlen + llen,
97                         SILC_STR_UI_INT(id),
98                         SILC_STR_UI_INT(status),
99                         SILC_STR_UI_INT(mlen),
100                         SILC_STR_UI32_STRING(message),
101                         SILC_STR_UI_INT(llen),
102                         SILC_STR_UI32_STRING(language_tag),
103                         SILC_STR_END);
104 }
105
106 /* Handle callback */
107
108 static void silc_sftp_server_handle(SilcSFTP sftp,
109                                     SilcSFTPStatus status,
110                                     SilcSFTPHandle handle,
111                                     void *context)
112 {
113   SilcSFTPServer server = (SilcSFTPServer)sftp;
114   uint32 id = (uint32)context;
115   unsigned char *hdata;
116   uint32 hdata_len;
117
118   SILC_LOG_DEBUG(("Handle callback"));
119   SILC_LOG_DEBUG(("Request ID: %d", id));
120
121   if (status != SILC_SFTP_STATUS_OK) {
122     silc_sftp_send_error(server, status, id);
123     return;
124   }
125
126   hdata = server->fs->sftp_encode_handle(server->fs_context, sftp,
127                                          handle, &hdata_len);
128   if (!hdata) {
129     silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
130     return;
131   }
132
133   silc_sftp_send_packet(server, SILC_SFTP_HANDLE, 8 + hdata_len,
134                         SILC_STR_UI_INT(id),
135                         SILC_STR_UI_INT(hdata_len),
136                         SILC_STR_UI_XNSTRING(hdata, hdata_len),
137                         SILC_STR_END);
138 }
139
140 /* Data callback */
141
142 static void silc_sftp_server_data(SilcSFTP sftp,
143                                   SilcSFTPStatus status,
144                                   const unsigned char *data,
145                                   uint32 data_len,
146                                   void *context)
147 {
148   SilcSFTPServer server = (SilcSFTPServer)sftp;
149   uint32 id = (uint32)context;
150
151   SILC_LOG_DEBUG(("Data callback"));
152   SILC_LOG_DEBUG(("Request ID: %d", id));
153
154   if (status != SILC_SFTP_STATUS_OK) {
155     silc_sftp_send_error(server, status, id);
156     return;
157   }
158
159   silc_sftp_send_packet(server, SILC_SFTP_DATA, 8 + data_len,
160                         SILC_STR_UI_INT(id),
161                         SILC_STR_UI_INT(data_len),
162                         SILC_STR_UI_XNSTRING(data, data_len),
163                         SILC_STR_END);
164 }
165
166 /* Name callback */
167
168 static void silc_sftp_server_name(SilcSFTP sftp,
169                                   SilcSFTPStatus status,
170                                   const SilcSFTPName name,
171                                   void *context)
172 {
173   SilcSFTPServer server = (SilcSFTPServer)sftp;
174   uint32 id = (uint32)context;
175   SilcBuffer namebuf;
176
177   SILC_LOG_DEBUG(("Name callback"));
178   SILC_LOG_DEBUG(("Request ID: %d", id));
179
180   if (status != SILC_SFTP_STATUS_OK) {
181     silc_sftp_send_error(server, status, id);
182     return;
183   }
184
185   namebuf = silc_sftp_name_encode(name);
186   if (!namebuf) {
187     silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
188     return;
189   }
190
191   silc_sftp_send_packet(server, SILC_SFTP_NAME, 4 + namebuf->len,
192                         SILC_STR_UI_INT(id),
193                         SILC_STR_UI_XNSTRING(namebuf->data, namebuf->len),
194                         SILC_STR_END);
195 }
196
197 /* Attributes callback */
198
199 static void silc_sftp_server_attr(SilcSFTP sftp,
200                                   SilcSFTPStatus status,
201                                   const SilcSFTPAttributes attrs,
202                                   void *context)
203 {
204   SilcSFTPServer server = (SilcSFTPServer)sftp;
205   uint32 id = (uint32)context;
206   SilcBuffer attr_buf;
207
208   SILC_LOG_DEBUG(("Attr callback"));
209   SILC_LOG_DEBUG(("Request ID: %d", id));
210
211   if (status != SILC_SFTP_STATUS_OK) {
212     silc_sftp_send_error(server, status, id);
213     return;
214   }
215
216   attr_buf = silc_sftp_attr_encode(attrs);
217
218   silc_sftp_send_packet(server, SILC_SFTP_ATTRS, 4 + attr_buf->len,
219                         SILC_STR_UI_INT(id),
220                         SILC_STR_UI_XNSTRING(attr_buf->data, attr_buf->len),
221                         SILC_STR_END);
222
223   silc_buffer_free(attr_buf);
224 }
225
226 /* Extended callback */
227
228 static void silc_sftp_server_extended(SilcSFTP sftp,
229                                       SilcSFTPStatus status,
230                                       const unsigned char *data,
231                                       uint32 data_len,
232                                       void *context)
233 {
234   SilcSFTPServer server = (SilcSFTPServer)sftp;
235   uint32 id = (uint32)context;
236
237   SILC_LOG_DEBUG(("Extended callback"));
238   SILC_LOG_DEBUG(("Request ID: %d", id));
239
240   if (status != SILC_SFTP_STATUS_OK) {
241     silc_sftp_send_error(server, status, id);
242     return;
243   }
244
245   silc_sftp_send_packet(server, SILC_SFTP_EXTENDED, 4 + data_len,
246                         SILC_STR_UI_INT(id),
247                         SILC_STR_UI_XNSTRING(data, data_len),
248                         SILC_STR_END);
249 }
250
251 /* Starts SFTP server by associating the socket connection `sock' to the
252    created SFTP server context.  This function returns the allocated
253    SFTP client context or NULL on error. The `send_packet' is called
254    by the library when it needs to send a packet. The `fs' is the
255    structure containing filesystem access callbacks. */
256
257 SilcSFTP silc_sftp_server_start(SilcSocketConnection sock,
258                                 SilcSFTPSendPacketCallback send_packet,
259                                 void *send_context,
260                                 SilcSFTPFilesystem fs,
261                                 void *fs_context)
262 {
263   SilcSFTPServer server;
264
265   server = silc_calloc(1, sizeof(*server));
266   server->sock = sock;
267   server->send_packet = send_packet;
268   server->send_context = send_context;
269   server->fs = fs;
270   server->fs_context = fs_context;
271
272   SILC_LOG_DEBUG(("Starting SFTP server %p", server));
273
274   return (SilcSFTP)server;
275 }
276
277 /* Shutdown's the SFTP server.  The caller is responsible of closing
278    the associated socket connection.  The SFTP context is freed and is
279    invalid after this function returns. */
280
281 void silc_sftp_server_shutdown(SilcSFTP sftp)
282 {
283   SilcSFTPServer server = (SilcSFTPServer)sftp;
284
285   SILC_LOG_DEBUG(("Stopping SFTP server %p", server));
286
287   silc_free(server);
288 }
289
290 /* Function that is called to process the incmoing SFTP packet. */
291 /* XXX Some day this will go away and we have automatic receive callbacks
292    for SilcSocketConnection API or SilcPacketContext API. */
293
294 void silc_sftp_server_receive_process(SilcSFTP sftp,
295                                       SilcSocketConnection sock,
296                                       SilcPacketContext *packet)
297 {
298   SilcSFTPServer server = (SilcSFTPServer)sftp;
299   SilcSFTPPacket type;
300   char *filename = NULL, *path = NULL;
301   const unsigned char *payload = NULL;
302   uint32 payload_len;
303   int ret;
304   SilcBufferStruct buf;
305   uint32 id;
306   SilcSFTPAttributes attrs;
307   SilcSFTPHandle handle;
308
309   SILC_LOG_DEBUG(("Start"));
310
311   /* Parse the packet */
312   type = silc_sftp_packet_decode(packet->buffer, (unsigned char **)&payload, 
313                                  &payload_len);
314   if (!type)
315     return;
316
317   silc_buffer_set(&buf, (unsigned char *)payload, payload_len);
318
319   switch (type) {
320   case SILC_SFTP_INIT:
321     {
322       SilcSFTPVersion version;
323
324       SILC_LOG_DEBUG(("Init request"));
325
326       ret = silc_buffer_unformat(&buf,
327                                  SILC_STR_UI_INT(&version),
328                                  SILC_STR_END);
329       if (ret < 0)
330         break;
331
332       silc_sftp_send_packet(server, SILC_SFTP_VERSION, 4,
333                             SILC_STR_UI_INT(SILC_SFTP_PROTOCOL_VERSION),
334                             SILC_STR_END);
335     }
336     break;
337
338   case SILC_SFTP_OPEN:
339     {
340       SilcSFTPFileOperation pflags;
341       unsigned char *attr_buf;
342       uint32 attr_len = 0;
343       SilcBufferStruct tmpbuf;
344
345       SILC_LOG_DEBUG(("Open request"));
346
347       ret = silc_buffer_unformat(&buf,
348                                  SILC_STR_UI_INT(&id),
349                                  SILC_STR_UI32_STRING_ALLOC(&filename),
350                                  SILC_STR_UI_INT(&pflags),
351                                  SILC_STR_UI32_NSTRING(&attr_buf, 
352                                                        &attr_len),
353                                  SILC_STR_END);
354       if (ret < 0)
355         goto failure;
356
357       if (attr_len) {
358         silc_buffer_set(&tmpbuf, attr_buf, attr_len);
359         attrs = silc_sftp_attr_decode(&tmpbuf);
360       } else {
361         attrs = silc_calloc(1, sizeof(*attrs));
362       }
363
364       /* Open operation */
365       server->fs->sftp_open(server->fs_context, sftp, filename, pflags,
366                             attrs, silc_sftp_server_handle, (void *)id);
367
368       silc_free(filename);
369       silc_sftp_attr_free(attrs);
370     }
371     break;
372
373   case SILC_SFTP_CLOSE:
374     {
375       unsigned char *hdata;
376       uint32 hdata_len;
377
378       SILC_LOG_DEBUG(("Close request"));
379
380       ret = silc_buffer_unformat(&buf,
381                                  SILC_STR_UI_INT(&id),
382                                  SILC_STR_UI32_NSTRING(&hdata, 
383                                                        &hdata_len),
384                                  SILC_STR_END);
385       if (ret < 0)
386         goto failure;
387
388       /* Get the handle */
389       handle = server->fs->sftp_get_handle(server->fs_context, sftp,
390                                            (const unsigned char *)hdata,
391                                            hdata_len);
392       if (!handle) {
393         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
394         break;
395       }
396
397       /* Close operation */
398       server->fs->sftp_close(server->fs_context, sftp, handle,
399                              silc_sftp_server_status, (void *)id);
400     }
401     break;
402
403   case SILC_SFTP_READ:
404     {
405       unsigned char *hdata;
406       uint32 hdata_len;
407       uint64 offset;
408       uint32 len;
409
410       SILC_LOG_DEBUG(("Read request"));
411
412       ret = silc_buffer_unformat(&buf,
413                                  SILC_STR_UI_INT(&id),
414                                  SILC_STR_UI32_NSTRING(&hdata, 
415                                                        &hdata_len),
416                                  SILC_STR_UI_INT64(&offset),
417                                  SILC_STR_UI_INT(&len),
418                                  SILC_STR_END);
419       if (ret < 0)
420         goto failure;
421
422       /* Get the handle */
423       handle = server->fs->sftp_get_handle(server->fs_context, sftp,
424                                            (const unsigned char *)hdata,
425                                            hdata_len);
426       if (!handle) {
427         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
428         break;
429       }
430
431       /* Read operation */
432       server->fs->sftp_read(server->fs_context, sftp, handle, offset, len,
433                             silc_sftp_server_data, (void *)id);
434     }
435     break;
436
437   case SILC_SFTP_WRITE:
438     {
439       unsigned char *hdata;
440       uint32 hdata_len;
441       uint64 offset;
442       unsigned char *data;
443       uint32 data_len;
444
445       SILC_LOG_DEBUG(("Read request"));
446
447       ret = silc_buffer_unformat(&buf,
448                                  SILC_STR_UI_INT(&id),
449                                  SILC_STR_UI32_NSTRING(&hdata, 
450                                                        &hdata_len),
451                                  SILC_STR_UI_INT64(&offset),
452                                  SILC_STR_UI32_NSTRING(&data, 
453                                                        &data_len),
454                                  SILC_STR_END);
455       if (ret < 0)
456         goto failure;
457
458       /* Get the handle */
459       handle = server->fs->sftp_get_handle(server->fs_context, sftp,
460                                            (const unsigned char *)hdata,
461                                            hdata_len);
462       if (!handle) {
463         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
464         break;
465       }
466
467       /* Write operation */
468       server->fs->sftp_write(server->fs_context, sftp, handle, offset, 
469                              (const unsigned char *)data, data_len,
470                              silc_sftp_server_status, (void *)id);
471     }
472     break;
473
474   case SILC_SFTP_REMOVE:
475     {
476       SILC_LOG_DEBUG(("Remove request"));
477
478       ret = silc_buffer_unformat(&buf,
479                                  SILC_STR_UI_INT(&id),
480                                  SILC_STR_UI32_STRING_ALLOC(&filename),
481                                  SILC_STR_END);
482       if (ret < 0)
483         goto failure;
484
485       /* Remove operation */
486       server->fs->sftp_remove(server->fs_context, sftp, filename,
487                               silc_sftp_server_status, (void *)id);
488
489       silc_free(filename);
490     }
491     break;
492
493   case SILC_SFTP_RENAME:
494     {
495       char *newname = NULL;
496
497       SILC_LOG_DEBUG(("Rename request"));
498
499       ret = silc_buffer_unformat(&buf,
500                                  SILC_STR_UI_INT(&id),
501                                  SILC_STR_UI32_STRING_ALLOC(&filename),
502                                  SILC_STR_UI32_STRING_ALLOC(&newname),
503                                  SILC_STR_END);
504       if (ret < 0)
505         goto failure;
506
507       /* Rename operation */
508       server->fs->sftp_rename(server->fs_context, sftp, filename, newname,
509                               silc_sftp_server_status, (void *)id);
510
511       silc_free(filename);
512       silc_free(newname);
513     }
514     break;
515
516   case SILC_SFTP_MKDIR:
517     {
518       unsigned char *attr_buf;
519       uint32 attr_len = 0;
520       SilcBufferStruct tmpbuf;
521
522       SILC_LOG_DEBUG(("Mkdir request"));
523
524       ret = silc_buffer_unformat(&buf,
525                                  SILC_STR_UI_INT(&id),
526                                  SILC_STR_UI32_STRING_ALLOC(&path),
527                                  SILC_STR_UI32_NSTRING(&attr_buf,
528                                                        &attr_len),
529                                  SILC_STR_END);
530       if (ret < 0)
531         goto failure;
532
533       if (attr_len) {
534         silc_buffer_set(&tmpbuf, attr_buf, attr_len);
535         attrs = silc_sftp_attr_decode(&tmpbuf);
536       } else {
537         attrs = silc_calloc(1, sizeof(*attrs));
538       }
539
540       /* Mkdir operation */
541       server->fs->sftp_mkdir(server->fs_context, sftp, path, attrs,
542                              silc_sftp_server_status, (void *)id);
543
544       silc_sftp_attr_free(attrs);
545       silc_free(path);
546     }
547     break;
548
549   case SILC_SFTP_RMDIR:
550     {
551       SILC_LOG_DEBUG(("Rmdir request"));
552
553       ret = silc_buffer_unformat(&buf,
554                                  SILC_STR_UI_INT(&id),
555                                  SILC_STR_UI32_STRING_ALLOC(&path),
556                                  SILC_STR_END);
557       if (ret < 0)
558         goto failure;
559
560       /* Rmdir operation */
561       server->fs->sftp_rmdir(server->fs_context, sftp, path,
562                              silc_sftp_server_status, (void *)id);
563
564       silc_free(path);
565     }
566     break;
567
568   case SILC_SFTP_OPENDIR:
569     {
570       SILC_LOG_DEBUG(("Opendir request"));
571
572       ret = silc_buffer_unformat(&buf,
573                                  SILC_STR_UI_INT(&id),
574                                  SILC_STR_UI32_STRING_ALLOC(&path),
575                                  SILC_STR_END);
576       if (ret < 0)
577         goto failure;
578
579       /* Opendir operation */
580       server->fs->sftp_opendir(server->fs_context, sftp, path,
581                                silc_sftp_server_handle, (void *)id);
582
583       silc_free(path);
584     }
585     break;
586
587   case SILC_SFTP_READDIR:
588     {
589       unsigned char *hdata;
590       uint32 hdata_len;
591
592       SILC_LOG_DEBUG(("Readdir request"));
593
594       ret = silc_buffer_unformat(&buf,
595                                  SILC_STR_UI_INT(&id),
596                                  SILC_STR_UI32_NSTRING(&hdata, 
597                                                        &hdata_len),
598                                  SILC_STR_END);
599       if (ret < 0)
600         goto failure;
601
602       /* Get the handle */
603       handle = server->fs->sftp_get_handle(server->fs_context, sftp,
604                                            (const unsigned char *)hdata,
605                                            hdata_len);
606       if (!handle) {
607         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
608         break;
609       }
610
611       /* Readdir operation */
612       server->fs->sftp_readdir(server->fs_context, sftp, handle,
613                                silc_sftp_server_name, (void *)id);
614     }
615     break;
616
617   case SILC_SFTP_STAT:
618     {
619       SILC_LOG_DEBUG(("Stat request"));
620
621       ret = silc_buffer_unformat(&buf,
622                                  SILC_STR_UI_INT(&id),
623                                  SILC_STR_UI32_STRING_ALLOC(&path),
624                                  SILC_STR_END);
625       if (ret < 0)
626         goto failure;
627
628       /* Stat operation */
629       server->fs->sftp_stat(server->fs_context, sftp, path,
630                             silc_sftp_server_attr, (void *)id);
631
632       silc_free(path);
633     }
634     break;
635
636   case SILC_SFTP_LSTAT:
637     {
638       SILC_LOG_DEBUG(("Lstat request"));
639
640       ret = silc_buffer_unformat(&buf,
641                                  SILC_STR_UI_INT(&id),
642                                  SILC_STR_UI32_STRING_ALLOC(&path),
643                                  SILC_STR_END);
644       if (ret < 0)
645         goto failure;
646
647       /* Lstat operation */
648       server->fs->sftp_lstat(server->fs_context, sftp, path,
649                              silc_sftp_server_attr, (void *)id);
650
651       silc_free(path);
652     }
653     break;
654
655   case SILC_SFTP_FSTAT:
656     {
657       unsigned char *hdata;
658       uint32 hdata_len;
659
660       SILC_LOG_DEBUG(("Fstat request"));
661
662       ret = silc_buffer_unformat(&buf,
663                                  SILC_STR_UI_INT(&id),
664                                  SILC_STR_UI32_NSTRING(&hdata, 
665                                                        &hdata_len),
666                                  SILC_STR_END);
667       if (ret < 0)
668         goto failure;
669
670       /* Get the handle */
671       handle = server->fs->sftp_get_handle(server->fs_context, sftp,
672                                            (const unsigned char *)hdata,
673                                            hdata_len);
674       if (!handle) {
675         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
676         break;
677       }
678
679       /* Fstat operation */
680       server->fs->sftp_fstat(server->fs_context, sftp, handle,
681                              silc_sftp_server_attr, (void *)id);
682     }
683     break;
684
685   case SILC_SFTP_SETSTAT:
686     {
687       unsigned char *attr_buf;
688       uint32 attr_len = 0;
689       SilcBufferStruct tmpbuf;
690
691       SILC_LOG_DEBUG(("Setstat request"));
692
693       ret = silc_buffer_unformat(&buf,
694                                  SILC_STR_UI_INT(&id),
695                                  SILC_STR_UI32_STRING_ALLOC(&path),
696                                  SILC_STR_UI32_NSTRING(&attr_buf,
697                                                        &attr_len),
698                                  SILC_STR_END);
699       if (ret < 0)
700         goto failure;
701
702       if (attr_len) {
703         silc_buffer_set(&tmpbuf, attr_buf, attr_len);
704         attrs = silc_sftp_attr_decode(&tmpbuf);
705       } else {
706         attrs = silc_calloc(1, sizeof(*attrs));
707       }
708
709       /* Setstat operation */
710       server->fs->sftp_setstat(server->fs_context, sftp, path, attrs,
711                                silc_sftp_server_status, (void *)id);
712
713       silc_sftp_attr_free(attrs);
714       silc_free(path);
715     }
716     break;
717
718   case SILC_SFTP_FSETSTAT:
719     {
720       unsigned char *hdata, *attr_buf;
721       uint32 hdata_len, attr_len = 0;
722       SilcBufferStruct tmpbuf;
723
724       SILC_LOG_DEBUG(("Fsetstat request"));
725
726       ret = silc_buffer_unformat(&buf,
727                                  SILC_STR_UI_INT(&id),
728                                  SILC_STR_UI32_NSTRING(&hdata, 
729                                                        &hdata_len),
730                                  SILC_STR_UI32_NSTRING(&attr_buf,
731                                                        &attr_len),
732                                  SILC_STR_END);
733       if (ret < 0)
734         goto failure;
735
736       if (attr_len) {
737         silc_buffer_set(&tmpbuf, attr_buf, attr_len);
738         attrs = silc_sftp_attr_decode(&tmpbuf);
739       } else {
740         attrs = silc_calloc(1, sizeof(*attrs));
741       }
742
743       /* Get the handle */
744       handle = server->fs->sftp_get_handle(server->fs_context, sftp,
745                                            (const unsigned char *)hdata,
746                                            hdata_len);
747       if (!handle) {
748         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
749         break;
750       }
751
752       /* Fsetstat operation */
753       server->fs->sftp_fsetstat(server->fs_context, sftp, handle, attrs,
754                                 silc_sftp_server_status, (void *)id);
755
756       silc_sftp_attr_free(attrs);
757     }
758     break;
759
760   case SILC_SFTP_READLINK:
761     {
762       SILC_LOG_DEBUG(("Readlink request"));
763
764       ret = silc_buffer_unformat(&buf,
765                                  SILC_STR_UI_INT(&id),
766                                  SILC_STR_UI32_STRING_ALLOC(&path),
767                                  SILC_STR_END);
768       if (ret < 0)
769         goto failure;
770
771       /* Readlink operation */
772       server->fs->sftp_readlink(server->fs_context, sftp, path,
773                                 silc_sftp_server_name, (void *)id);
774
775       silc_free(path);
776     }
777     break;
778
779   case SILC_SFTP_SYMLINK:
780     {
781       char *target = NULL;
782
783       SILC_LOG_DEBUG(("Symlink request"));
784
785       ret = silc_buffer_unformat(&buf,
786                                  SILC_STR_UI_INT(&id),
787                                  SILC_STR_UI32_STRING_ALLOC(&path),
788                                  SILC_STR_UI32_STRING_ALLOC(&target),
789                                  SILC_STR_END);
790       if (ret < 0)
791         goto failure;
792
793       /* Symlink operation */
794       server->fs->sftp_symlink(server->fs_context, sftp, path, target,
795                                silc_sftp_server_status, (void *)id);
796
797       silc_free(path);
798       silc_free(target);
799     }
800     break;
801
802   case SILC_SFTP_REALPATH:
803     {
804       SILC_LOG_DEBUG(("Realpath request"));
805
806       ret = silc_buffer_unformat(&buf,
807                                  SILC_STR_UI_INT(&id),
808                                  SILC_STR_UI32_STRING_ALLOC(&path),
809                                  SILC_STR_END);
810       if (ret < 0)
811         goto failure;
812
813       /* Realpath operation */
814       server->fs->sftp_realpath(server->fs_context, sftp, path,
815                                 silc_sftp_server_name, (void *)id);
816
817       silc_free(path);
818     }
819     break;
820
821   case SILC_SFTP_EXTENDED:
822     {
823       char *request = NULL;
824       unsigned char *data;
825       uint32 data_len;
826
827       SILC_LOG_DEBUG(("Extended request"));
828
829       ret = silc_buffer_unformat(&buf,
830                                  SILC_STR_UI_INT(&id),
831                                  SILC_STR_UI32_STRING_ALLOC(&request),
832                                  SILC_STR_END);
833       if (ret < 0)
834         goto failure;
835
836       data_len = 8 + strlen(request);
837       silc_buffer_pull(&buf, data_len);
838       ret = silc_buffer_unformat(&buf,
839                                  SILC_STR_UI_XNSTRING(&data, buf.len),
840                                  SILC_STR_END);
841       if (ret < 0)
842         goto failure;
843       data_len = buf.len;
844
845       /* Extended operation */
846       server->fs->sftp_extended(server->fs_context, sftp, 
847                                 request, data, data_len,
848                                 silc_sftp_server_extended, (void *)id);
849
850       silc_free(request);
851     }
852     break;
853
854   default:
855     break;
856   }
857
858   return;
859
860  failure:
861   silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
862 }