updates.
[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 "silcsftp_fs.h"
24 #include "sftp_util.h"
25
26 /* SFTP Server context */
27 typedef struct {
28   SilcSocketConnection sock;
29   SilcSFTPSendPacketCallback send_packet;
30   void *send_context;
31   SilcSFTPFilesystem fs;
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->fs->sftp_encode_handle(server->fs->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 {
262   SilcSFTPServer server;
263
264   server = silc_calloc(1, sizeof(*server));
265   server->sock = sock;
266   server->send_packet = send_packet;
267   server->send_context = send_context;
268   server->fs = fs;
269
270   SILC_LOG_DEBUG(("Starting SFTP server %p", server));
271
272   return (SilcSFTP)server;
273 }
274
275 /* Shutdown's the SFTP server.  The caller is responsible of closing
276    the associated socket connection.  The SFTP context is freed and is
277    invalid after this function returns. */
278
279 void silc_sftp_server_shutdown(SilcSFTP sftp)
280 {
281   SilcSFTPServer server = (SilcSFTPServer)sftp;
282
283   SILC_LOG_DEBUG(("Stopping SFTP server %p", server));
284
285   silc_free(server);
286 }
287
288 /* Function that is called to process the incmoing SFTP packet. */
289 /* XXX Some day this will go away and we have automatic receive callbacks
290    for SilcSocketConnection API or SilcPacketContext API. */
291
292 void silc_sftp_server_receive_process(SilcSFTP sftp,
293                                       SilcSocketConnection sock,
294                                       SilcPacketContext *packet)
295 {
296   SilcSFTPServer server = (SilcSFTPServer)sftp;
297   SilcSFTPPacket type;
298   char *filename = NULL, *path = NULL;
299   const unsigned char *payload = NULL;
300   uint32 payload_len;
301   int ret;
302   SilcBufferStruct buf;
303   uint32 id;
304   SilcSFTPAttributes attrs;
305   SilcSFTPHandle handle;
306
307   SILC_LOG_DEBUG(("Start"));
308
309   /* Parse the packet */
310   type = silc_sftp_packet_decode(packet->buffer, (unsigned char **)&payload, 
311                                  &payload_len);
312   if (!type)
313     return;
314
315   silc_buffer_set(&buf, (unsigned char *)payload, payload_len);
316
317   switch (type) {
318   case SILC_SFTP_INIT:
319     {
320       SilcSFTPVersion version;
321
322       SILC_LOG_DEBUG(("Init request"));
323
324       ret = silc_buffer_unformat(&buf,
325                                  SILC_STR_UI_INT(&version),
326                                  SILC_STR_END);
327       if (ret < 0)
328         break;
329
330       silc_sftp_send_packet(server, SILC_SFTP_VERSION, 4,
331                             SILC_STR_UI_INT(SILC_SFTP_PROTOCOL_VERSION),
332                             SILC_STR_END);
333     }
334     break;
335
336   case SILC_SFTP_OPEN:
337     {
338       SilcSFTPFileOperation pflags;
339       unsigned char *attr_buf;
340       uint32 attr_len = 0;
341       SilcBufferStruct tmpbuf;
342
343       SILC_LOG_DEBUG(("Open request"));
344
345       ret = silc_buffer_unformat(&buf,
346                                  SILC_STR_UI_INT(&id),
347                                  SILC_STR_UI32_STRING_ALLOC(&filename),
348                                  SILC_STR_UI_INT(&pflags),
349                                  SILC_STR_UI32_NSTRING(&attr_buf, 
350                                                        &attr_len),
351                                  SILC_STR_END);
352       if (ret < 0)
353         goto failure;
354
355       if (attr_len) {
356         silc_buffer_set(&tmpbuf, attr_buf, attr_len);
357         attrs = silc_sftp_attr_decode(&tmpbuf);
358       } else {
359         attrs = silc_calloc(1, sizeof(*attrs));
360       }
361
362       /* Open operation */
363       server->fs->fs->sftp_open(server->fs->fs_context, sftp, filename, pflags,
364                                 attrs, silc_sftp_server_handle, (void *)id);
365
366       silc_free(filename);
367       silc_sftp_attr_free(attrs);
368     }
369     break;
370
371   case SILC_SFTP_CLOSE:
372     {
373       unsigned char *hdata;
374       uint32 hdata_len;
375
376       SILC_LOG_DEBUG(("Close request"));
377
378       ret = silc_buffer_unformat(&buf,
379                                  SILC_STR_UI_INT(&id),
380                                  SILC_STR_UI32_NSTRING(&hdata, 
381                                                        &hdata_len),
382                                  SILC_STR_END);
383       if (ret < 0)
384         goto failure;
385
386       /* Get the handle */
387       handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
388                                                (const unsigned char *)hdata,
389                                                hdata_len);
390       if (!handle) {
391         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
392         break;
393       }
394
395       /* Close operation */
396       server->fs->fs->sftp_close(server->fs->fs_context, sftp, handle,
397                                  silc_sftp_server_status, (void *)id);
398     }
399     break;
400
401   case SILC_SFTP_READ:
402     {
403       unsigned char *hdata;
404       uint32 hdata_len;
405       uint64 offset;
406       uint32 len;
407
408       SILC_LOG_DEBUG(("Read request"));
409
410       ret = silc_buffer_unformat(&buf,
411                                  SILC_STR_UI_INT(&id),
412                                  SILC_STR_UI32_NSTRING(&hdata, 
413                                                        &hdata_len),
414                                  SILC_STR_UI_INT64(&offset),
415                                  SILC_STR_UI_INT(&len),
416                                  SILC_STR_END);
417       if (ret < 0)
418         goto failure;
419
420       /* Get the handle */
421       handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
422                                                (const unsigned char *)hdata,
423                                                hdata_len);
424       if (!handle) {
425         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
426         break;
427       }
428
429       /* Read operation */
430       server->fs->fs->sftp_read(server->fs->fs_context, sftp, 
431                                 handle, offset, len,
432                                 silc_sftp_server_data, (void *)id);
433     }
434     break;
435
436   case SILC_SFTP_WRITE:
437     {
438       unsigned char *hdata;
439       uint32 hdata_len;
440       uint64 offset;
441       unsigned char *data;
442       uint32 data_len;
443
444       SILC_LOG_DEBUG(("Read request"));
445
446       ret = silc_buffer_unformat(&buf,
447                                  SILC_STR_UI_INT(&id),
448                                  SILC_STR_UI32_NSTRING(&hdata, 
449                                                        &hdata_len),
450                                  SILC_STR_UI_INT64(&offset),
451                                  SILC_STR_UI32_NSTRING(&data, 
452                                                        &data_len),
453                                  SILC_STR_END);
454       if (ret < 0)
455         goto failure;
456
457       /* Get the handle */
458       handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
459                                                (const unsigned char *)hdata,
460                                                hdata_len);
461       if (!handle) {
462         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
463         break;
464       }
465
466       /* Write operation */
467       server->fs->fs->sftp_write(server->fs->fs_context, sftp, handle, offset, 
468                                  (const unsigned char *)data, data_len,
469                                  silc_sftp_server_status, (void *)id);
470     }
471     break;
472
473   case SILC_SFTP_REMOVE:
474     {
475       SILC_LOG_DEBUG(("Remove request"));
476
477       ret = silc_buffer_unformat(&buf,
478                                  SILC_STR_UI_INT(&id),
479                                  SILC_STR_UI32_STRING_ALLOC(&filename),
480                                  SILC_STR_END);
481       if (ret < 0)
482         goto failure;
483
484       /* Remove operation */
485       server->fs->fs->sftp_remove(server->fs->fs_context, sftp, filename,
486                                   silc_sftp_server_status, (void *)id);
487
488       silc_free(filename);
489     }
490     break;
491
492   case SILC_SFTP_RENAME:
493     {
494       char *newname = NULL;
495
496       SILC_LOG_DEBUG(("Rename request"));
497
498       ret = silc_buffer_unformat(&buf,
499                                  SILC_STR_UI_INT(&id),
500                                  SILC_STR_UI32_STRING_ALLOC(&filename),
501                                  SILC_STR_UI32_STRING_ALLOC(&newname),
502                                  SILC_STR_END);
503       if (ret < 0)
504         goto failure;
505
506       /* Rename operation */
507       server->fs->fs->sftp_rename(server->fs->fs_context, sftp, filename, newname,
508                                   silc_sftp_server_status, (void *)id);
509
510       silc_free(filename);
511       silc_free(newname);
512     }
513     break;
514
515   case SILC_SFTP_MKDIR:
516     {
517       unsigned char *attr_buf;
518       uint32 attr_len = 0;
519       SilcBufferStruct tmpbuf;
520
521       SILC_LOG_DEBUG(("Mkdir request"));
522
523       ret = silc_buffer_unformat(&buf,
524                                  SILC_STR_UI_INT(&id),
525                                  SILC_STR_UI32_STRING_ALLOC(&path),
526                                  SILC_STR_UI32_NSTRING(&attr_buf,
527                                                        &attr_len),
528                                  SILC_STR_END);
529       if (ret < 0)
530         goto failure;
531
532       if (attr_len) {
533         silc_buffer_set(&tmpbuf, attr_buf, attr_len);
534         attrs = silc_sftp_attr_decode(&tmpbuf);
535       } else {
536         attrs = silc_calloc(1, sizeof(*attrs));
537       }
538
539       /* Mkdir operation */
540       server->fs->fs->sftp_mkdir(server->fs->fs_context, sftp, path, attrs,
541                                  silc_sftp_server_status, (void *)id);
542
543       silc_sftp_attr_free(attrs);
544       silc_free(path);
545     }
546     break;
547
548   case SILC_SFTP_RMDIR:
549     {
550       SILC_LOG_DEBUG(("Rmdir request"));
551
552       ret = silc_buffer_unformat(&buf,
553                                  SILC_STR_UI_INT(&id),
554                                  SILC_STR_UI32_STRING_ALLOC(&path),
555                                  SILC_STR_END);
556       if (ret < 0)
557         goto failure;
558
559       /* Rmdir operation */
560       server->fs->fs->sftp_rmdir(server->fs->fs_context, sftp, path,
561                                  silc_sftp_server_status, (void *)id);
562
563       silc_free(path);
564     }
565     break;
566
567   case SILC_SFTP_OPENDIR:
568     {
569       SILC_LOG_DEBUG(("Opendir request"));
570
571       ret = silc_buffer_unformat(&buf,
572                                  SILC_STR_UI_INT(&id),
573                                  SILC_STR_UI32_STRING_ALLOC(&path),
574                                  SILC_STR_END);
575       if (ret < 0)
576         goto failure;
577
578       /* Opendir operation */
579       server->fs->fs->sftp_opendir(server->fs->fs_context, sftp, path,
580                                    silc_sftp_server_handle, (void *)id);
581
582       silc_free(path);
583     }
584     break;
585
586   case SILC_SFTP_READDIR:
587     {
588       unsigned char *hdata;
589       uint32 hdata_len;
590
591       SILC_LOG_DEBUG(("Readdir request"));
592
593       ret = silc_buffer_unformat(&buf,
594                                  SILC_STR_UI_INT(&id),
595                                  SILC_STR_UI32_NSTRING(&hdata, 
596                                                        &hdata_len),
597                                  SILC_STR_END);
598       if (ret < 0)
599         goto failure;
600
601       /* Get the handle */
602       handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
603                                                (const unsigned char *)hdata,
604                                                hdata_len);
605       if (!handle) {
606         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
607         break;
608       }
609
610       /* Readdir operation */
611       server->fs->fs->sftp_readdir(server->fs->fs_context, sftp, handle,
612                                    silc_sftp_server_name, (void *)id);
613     }
614     break;
615
616   case SILC_SFTP_STAT:
617     {
618       SILC_LOG_DEBUG(("Stat request"));
619
620       ret = silc_buffer_unformat(&buf,
621                                  SILC_STR_UI_INT(&id),
622                                  SILC_STR_UI32_STRING_ALLOC(&path),
623                                  SILC_STR_END);
624       if (ret < 0)
625         goto failure;
626
627       /* Stat operation */
628       server->fs->fs->sftp_stat(server->fs->fs_context, sftp, path,
629                                 silc_sftp_server_attr, (void *)id);
630
631       silc_free(path);
632     }
633     break;
634
635   case SILC_SFTP_LSTAT:
636     {
637       SILC_LOG_DEBUG(("Lstat request"));
638
639       ret = silc_buffer_unformat(&buf,
640                                  SILC_STR_UI_INT(&id),
641                                  SILC_STR_UI32_STRING_ALLOC(&path),
642                                  SILC_STR_END);
643       if (ret < 0)
644         goto failure;
645
646       /* Lstat operation */
647       server->fs->fs->sftp_lstat(server->fs->fs_context, sftp, path,
648                                  silc_sftp_server_attr, (void *)id);
649
650       silc_free(path);
651     }
652     break;
653
654   case SILC_SFTP_FSTAT:
655     {
656       unsigned char *hdata;
657       uint32 hdata_len;
658
659       SILC_LOG_DEBUG(("Fstat request"));
660
661       ret = silc_buffer_unformat(&buf,
662                                  SILC_STR_UI_INT(&id),
663                                  SILC_STR_UI32_NSTRING(&hdata, 
664                                                        &hdata_len),
665                                  SILC_STR_END);
666       if (ret < 0)
667         goto failure;
668
669       /* Get the handle */
670       handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
671                                                (const unsigned char *)hdata,
672                                                hdata_len);
673       if (!handle) {
674         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
675         break;
676       }
677
678       /* Fstat operation */
679       server->fs->fs->sftp_fstat(server->fs->fs_context, sftp, handle,
680                                  silc_sftp_server_attr, (void *)id);
681     }
682     break;
683
684   case SILC_SFTP_SETSTAT:
685     {
686       unsigned char *attr_buf;
687       uint32 attr_len = 0;
688       SilcBufferStruct tmpbuf;
689
690       SILC_LOG_DEBUG(("Setstat request"));
691
692       ret = silc_buffer_unformat(&buf,
693                                  SILC_STR_UI_INT(&id),
694                                  SILC_STR_UI32_STRING_ALLOC(&path),
695                                  SILC_STR_UI32_NSTRING(&attr_buf,
696                                                        &attr_len),
697                                  SILC_STR_END);
698       if (ret < 0)
699         goto failure;
700
701       if (attr_len) {
702         silc_buffer_set(&tmpbuf, attr_buf, attr_len);
703         attrs = silc_sftp_attr_decode(&tmpbuf);
704       } else {
705         attrs = silc_calloc(1, sizeof(*attrs));
706       }
707
708       /* Setstat operation */
709       server->fs->fs->sftp_setstat(server->fs->fs_context, sftp, path, attrs,
710                                    silc_sftp_server_status, (void *)id);
711
712       silc_sftp_attr_free(attrs);
713       silc_free(path);
714     }
715     break;
716
717   case SILC_SFTP_FSETSTAT:
718     {
719       unsigned char *hdata, *attr_buf;
720       uint32 hdata_len, attr_len = 0;
721       SilcBufferStruct tmpbuf;
722
723       SILC_LOG_DEBUG(("Fsetstat request"));
724
725       ret = silc_buffer_unformat(&buf,
726                                  SILC_STR_UI_INT(&id),
727                                  SILC_STR_UI32_NSTRING(&hdata, 
728                                                        &hdata_len),
729                                  SILC_STR_UI32_NSTRING(&attr_buf,
730                                                        &attr_len),
731                                  SILC_STR_END);
732       if (ret < 0)
733         goto failure;
734
735       if (attr_len) {
736         silc_buffer_set(&tmpbuf, attr_buf, attr_len);
737         attrs = silc_sftp_attr_decode(&tmpbuf);
738       } else {
739         attrs = silc_calloc(1, sizeof(*attrs));
740       }
741
742       /* Get the handle */
743       handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
744                                                (const unsigned char *)hdata,
745                                                hdata_len);
746       if (!handle) {
747         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
748         break;
749       }
750
751       /* Fsetstat operation */
752       server->fs->fs->sftp_fsetstat(server->fs->fs_context, sftp, handle, attrs,
753                                     silc_sftp_server_status, (void *)id);
754
755       silc_sftp_attr_free(attrs);
756     }
757     break;
758
759   case SILC_SFTP_READLINK:
760     {
761       SILC_LOG_DEBUG(("Readlink request"));
762
763       ret = silc_buffer_unformat(&buf,
764                                  SILC_STR_UI_INT(&id),
765                                  SILC_STR_UI32_STRING_ALLOC(&path),
766                                  SILC_STR_END);
767       if (ret < 0)
768         goto failure;
769
770       /* Readlink operation */
771       server->fs->fs->sftp_readlink(server->fs->fs_context, sftp, path,
772                                     silc_sftp_server_name, (void *)id);
773
774       silc_free(path);
775     }
776     break;
777
778   case SILC_SFTP_SYMLINK:
779     {
780       char *target = NULL;
781
782       SILC_LOG_DEBUG(("Symlink request"));
783
784       ret = silc_buffer_unformat(&buf,
785                                  SILC_STR_UI_INT(&id),
786                                  SILC_STR_UI32_STRING_ALLOC(&path),
787                                  SILC_STR_UI32_STRING_ALLOC(&target),
788                                  SILC_STR_END);
789       if (ret < 0)
790         goto failure;
791
792       /* Symlink operation */
793       server->fs->fs->sftp_symlink(server->fs->fs_context, sftp, path, target,
794                                    silc_sftp_server_status, (void *)id);
795
796       silc_free(path);
797       silc_free(target);
798     }
799     break;
800
801   case SILC_SFTP_REALPATH:
802     {
803       SILC_LOG_DEBUG(("Realpath request"));
804
805       ret = silc_buffer_unformat(&buf,
806                                  SILC_STR_UI_INT(&id),
807                                  SILC_STR_UI32_STRING_ALLOC(&path),
808                                  SILC_STR_END);
809       if (ret < 0)
810         goto failure;
811
812       /* Realpath operation */
813       server->fs->fs->sftp_realpath(server->fs->fs_context, sftp, path,
814                                     silc_sftp_server_name, (void *)id);
815
816       silc_free(path);
817     }
818     break;
819
820   case SILC_SFTP_EXTENDED:
821     {
822       char *request = NULL;
823       unsigned char *data;
824       uint32 data_len;
825
826       SILC_LOG_DEBUG(("Extended request"));
827
828       ret = silc_buffer_unformat(&buf,
829                                  SILC_STR_UI_INT(&id),
830                                  SILC_STR_UI32_STRING_ALLOC(&request),
831                                  SILC_STR_END);
832       if (ret < 0)
833         goto failure;
834
835       data_len = 8 + strlen(request);
836       silc_buffer_pull(&buf, data_len);
837       ret = silc_buffer_unformat(&buf,
838                                  SILC_STR_UI_XNSTRING(&data, buf.len),
839                                  SILC_STR_END);
840       if (ret < 0)
841         goto failure;
842       data_len = buf.len;
843
844       /* Extended operation */
845       server->fs->fs->sftp_extended(server->fs->fs_context, sftp, 
846                                     request, data, data_len,
847                                     silc_sftp_server_extended, (void *)id);
848
849       silc_free(request);
850     }
851     break;
852
853   default:
854     break;
855   }
856
857   return;
858
859  failure:
860   silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
861 }