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
20 #include "silcincludes.h"
21 #include "silcsftp.h"
22 #include "sftp_util.h"
23
24 /* SFTP Server context */
25 typedef struct {
26   SilcSocketConnection sock;
27   SilcSFTPSendPacketCallback send_packet;
28   void *send_context;
29   SilcSFTPFilesystem fs;
30   void *fs_context;
31 } *SilcSFTPServer;
32
33 /* General routine to send SFTP packet to the SFTP client. */
34
35 static void silc_sftp_send_packet(SilcSFTPServer sftp,
36                                   SilcSFTPPacket type, 
37                                   uint32 len, ...)
38 {
39   SilcBuffer packet;
40   va_list vp;
41
42   va_start(vp, len);
43   packet = silc_sftp_packet_encode_vp(type, len, vp);
44   va_end(vp);
45
46   if (!packet)
47     return;
48
49   SILC_LOG_HEXDUMP(("SFTP packet to client"), packet->data, packet->len);
50
51   /* Send the packet */
52   (*sftp->send_packet)(sftp->sock, packet, sftp->send_context);
53
54   silc_buffer_free(packet);
55 }
56
57 /* Sends error to the client */
58
59 static void silc_sftp_send_error(SilcSFTPServer sftp,
60                                  SilcSFTPStatus status,
61                                  uint32 id)
62 {
63   SILC_LOG_DEBUG(("Send error %d", status));
64
65   silc_sftp_send_packet(sftp, SILC_SFTP_STATUS, 16,
66                         SILC_STR_UI_INT(id),
67                         SILC_STR_UI_INT(status),
68                         SILC_STR_UI_INT(0),      /* Error */
69                         SILC_STR_UI_INT(0),      /* Language tag */
70                         SILC_STR_END);
71 }
72
73 /* Status callback */
74
75 static void silc_sftp_server_status(SilcSFTP sftp,
76                                     SilcSFTPStatus status,
77                                     const char *message,
78                                     const char *language_tag,
79                                     void *context)
80 {
81   SilcSFTPServer server = (SilcSFTPServer)sftp;
82   uint32 id = (uint32)context;
83   int mlen, llen;
84
85   SILC_LOG_DEBUG(("Status callback"));
86   SILC_LOG_DEBUG(("Request ID: %d", id));
87   
88   if (!message)
89     message = "";
90   if (!language_tag)
91     language_tag = "";
92   mlen = strlen(message);
93   llen = strlen(language_tag);
94
95   silc_sftp_send_packet(server, SILC_SFTP_STATUS, 16 + mlen + llen,
96                         SILC_STR_UI_INT(id),
97                         SILC_STR_UI_INT(status),
98                         SILC_STR_UI_INT(mlen),
99                         SILC_STR_UI32_STRING(message),
100                         SILC_STR_UI_INT(llen),
101                         SILC_STR_UI32_STRING(language_tag),
102                         SILC_STR_END);
103 }
104
105 /* Handle callback */
106
107 static void silc_sftp_server_handle(SilcSFTP sftp,
108                                     SilcSFTPStatus status,
109                                     SilcSFTPHandle handle,
110                                     void *context)
111 {
112   SilcSFTPServer server = (SilcSFTPServer)sftp;
113   uint32 id = (uint32)context;
114   unsigned char *hdata;
115   uint32 hdata_len;
116
117   SILC_LOG_DEBUG(("Handle callback"));
118   SILC_LOG_DEBUG(("Request ID: %d", id));
119
120   if (status != SILC_SFTP_STATUS_OK) {
121     silc_sftp_send_error(server, status, id);
122     return;
123   }
124
125   hdata = server->fs->sftp_encode_handle(server->fs_context, sftp,
126                                          handle, &hdata_len);
127   if (!hdata) {
128     silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
129     return;
130   }
131
132   silc_sftp_send_packet(server, SILC_SFTP_HANDLE, 8 + hdata_len,
133                         SILC_STR_UI_INT(id),
134                         SILC_STR_UI_INT(hdata_len),
135                         SILC_STR_UI_XNSTRING(hdata, hdata_len),
136                         SILC_STR_END);
137 }
138
139 /* Data callback */
140
141 static void silc_sftp_server_data(SilcSFTP sftp,
142                                   SilcSFTPStatus status,
143                                   const unsigned char *data,
144                                   uint32 data_len,
145                                   void *context)
146 {
147   SilcSFTPServer server = (SilcSFTPServer)sftp;
148   uint32 id = (uint32)context;
149
150   SILC_LOG_DEBUG(("Data callback"));
151   SILC_LOG_DEBUG(("Request ID: %d", id));
152
153   if (status != SILC_SFTP_STATUS_OK) {
154     silc_sftp_send_error(server, status, id);
155     return;
156   }
157
158   silc_sftp_send_packet(server, SILC_SFTP_DATA, 8 + data_len,
159                         SILC_STR_UI_INT(id),
160                         SILC_STR_UI_INT(data_len),
161                         SILC_STR_UI_XNSTRING(data, data_len),
162                         SILC_STR_END);
163 }
164
165 /* Name callback */
166
167 static void silc_sftp_server_name(SilcSFTP sftp,
168                                   SilcSFTPStatus status,
169                                   const SilcSFTPName name,
170                                   void *context)
171 {
172   SilcSFTPServer server = (SilcSFTPServer)sftp;
173   uint32 id = (uint32)context;
174   SilcBuffer namebuf;
175
176   SILC_LOG_DEBUG(("Name callback"));
177   SILC_LOG_DEBUG(("Request ID: %d", id));
178
179   if (status != SILC_SFTP_STATUS_OK) {
180     silc_sftp_send_error(server, status, id);
181     return;
182   }
183
184   namebuf = silc_sftp_name_encode(name);
185   if (!namebuf) {
186     silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
187     return;
188   }
189
190   silc_sftp_send_packet(server, SILC_SFTP_NAME, 4 + namebuf->len,
191                         SILC_STR_UI_INT(id),
192                         SILC_STR_UI_XNSTRING(namebuf->data, namebuf->len),
193                         SILC_STR_END);
194 }
195
196 /* Attributes callback */
197
198 static void silc_sftp_server_attr(SilcSFTP sftp,
199                                   SilcSFTPStatus status,
200                                   const SilcSFTPAttributes attrs,
201                                   void *context)
202 {
203   SilcSFTPServer server = (SilcSFTPServer)sftp;
204   uint32 id = (uint32)context;
205   SilcBuffer attr_buf;
206
207   SILC_LOG_DEBUG(("Attr callback"));
208   SILC_LOG_DEBUG(("Request ID: %d", id));
209
210   if (status != SILC_SFTP_STATUS_OK) {
211     silc_sftp_send_error(server, status, id);
212     return;
213   }
214
215   attr_buf = silc_sftp_attr_encode(attrs);
216
217   silc_sftp_send_packet(server, SILC_SFTP_ATTRS, 4 + attr_buf->len,
218                         SILC_STR_UI_INT(id),
219                         SILC_STR_UI_XNSTRING(attr_buf->data, attr_buf->len),
220                         SILC_STR_END);
221
222   silc_buffer_free(attr_buf);
223 }
224
225 /* Extended callback */
226
227 static void silc_sftp_server_extended(SilcSFTP sftp,
228                                       SilcSFTPStatus status,
229                                       const unsigned char *data,
230                                       uint32 data_len,
231                                       void *context)
232 {
233   SilcSFTPServer server = (SilcSFTPServer)sftp;
234   uint32 id = (uint32)context;
235
236   SILC_LOG_DEBUG(("Extended callback"));
237   SILC_LOG_DEBUG(("Request ID: %d", id));
238
239   if (status != SILC_SFTP_STATUS_OK) {
240     silc_sftp_send_error(server, status, id);
241     return;
242   }
243
244   silc_sftp_send_packet(server, SILC_SFTP_EXTENDED, 4 + data_len,
245                         SILC_STR_UI_INT(id),
246                         SILC_STR_UI_XNSTRING(data, data_len),
247                         SILC_STR_END);
248 }
249
250 /* Starts SFTP server by associating the socket connection `sock' to the
251    created SFTP server context.  This function returns the allocated
252    SFTP client context or NULL on error. The `send_packet' is called
253    by the library when it needs to send a packet. The `fs' is the
254    structure containing filesystem access callbacks. */
255
256 SilcSFTP silc_sftp_server_start(SilcSocketConnection sock,
257                                 SilcSFTPSendPacketCallback send_packet,
258                                 void *send_context,
259                                 SilcSFTPFilesystem fs,
260                                 void *fs_context)
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   server->fs_context = fs_context;
270
271   SILC_LOG_DEBUG(("Starting SFTP server %p", server));
272
273   return (SilcSFTP)server;
274 }
275
276 /* Shutdown's the SFTP server.  The caller is responsible of closing
277    the associated socket connection.  The SFTP context is freed and is
278    invalid after this function returns. */
279
280 void silc_sftp_server_shutdown(SilcSFTP sftp)
281 {
282   SilcSFTPServer server = (SilcSFTPServer)sftp;
283
284   SILC_LOG_DEBUG(("Stopping SFTP server %p", server));
285
286   silc_free(server);
287 }
288
289 /* Function that is called to process the incmoing SFTP packet. */
290 /* XXX Some day this will go away and we have automatic receive callbacks
291    for SilcSocketConnection API or SilcPacketContext API. */
292
293 void silc_sftp_server_receive_process(SilcSFTP sftp,
294                                       SilcSocketConnection sock,
295                                       SilcPacketContext *packet)
296 {
297   SilcSFTPServer server = (SilcSFTPServer)sftp;
298   SilcSFTPPacket type;
299   char *filename = NULL, *path = NULL;
300   const unsigned char *payload = NULL;
301   uint32 payload_len;
302   int ret;
303   SilcBufferStruct buf;
304   uint32 id;
305   SilcSFTPAttributes attrs;
306   SilcSFTPHandle handle;
307
308   SILC_LOG_DEBUG(("Start"));
309
310   /* Parse the packet */
311   type = silc_sftp_packet_decode(packet->buffer, (unsigned char **)&payload, 
312                                  &payload_len);
313   if (!type)
314     return;
315
316   silc_buffer_set(&buf, (unsigned char *)payload, payload_len);
317
318   switch (type) {
319   case SILC_SFTP_INIT:
320     {
321       SilcSFTPVersion version;
322
323       SILC_LOG_DEBUG(("Init request"));
324
325       ret = silc_buffer_unformat(&buf,
326                                  SILC_STR_UI_INT(&version),
327                                  SILC_STR_END);
328       if (ret < 0)
329         break;
330
331       silc_sftp_send_packet(server, SILC_SFTP_VERSION, 4,
332                             SILC_STR_UI_INT(SILC_SFTP_PROTOCOL_VERSION),
333                             SILC_STR_END);
334     }
335     break;
336
337   case SILC_SFTP_OPEN:
338     {
339       SilcSFTPFileOperation pflags;
340       unsigned char *attr_buf;
341       uint32 attr_len = 0;
342       SilcBufferStruct tmpbuf;
343
344       SILC_LOG_DEBUG(("Open request"));
345
346       ret = silc_buffer_unformat(&buf,
347                                  SILC_STR_UI_INT(&id),
348                                  SILC_STR_UI32_STRING_ALLOC(&filename),
349                                  SILC_STR_UI_INT(&pflags),
350                                  SILC_STR_UI32_NSTRING(&attr_buf, 
351                                                        &attr_len),
352                                  SILC_STR_END);
353       if (ret < 0)
354         goto failure;
355
356       if (attr_len) {
357         silc_buffer_set(&tmpbuf, attr_buf, attr_len);
358         attrs = silc_sftp_attr_decode(&tmpbuf);
359       } else {
360         attrs = silc_calloc(1, sizeof(*attrs));
361       }
362
363       /* Open operation */
364       server->fs->sftp_open(server->fs_context, sftp, filename, pflags,
365                             attrs, silc_sftp_server_handle, (void *)id);
366
367       silc_free(filename);
368       silc_sftp_attr_free(attrs);
369     }
370     break;
371
372   case SILC_SFTP_CLOSE:
373     {
374       unsigned char *hdata;
375       uint32 hdata_len;
376
377       SILC_LOG_DEBUG(("Close request"));
378
379       ret = silc_buffer_unformat(&buf,
380                                  SILC_STR_UI_INT(&id),
381                                  SILC_STR_UI32_NSTRING(&hdata, 
382                                                        &hdata_len),
383                                  SILC_STR_END);
384       if (ret < 0)
385         goto failure;
386
387       /* Get the handle */
388       handle = server->fs->sftp_get_handle(server->fs_context, sftp,
389                                            (const unsigned char *)hdata,
390                                            hdata_len);
391       if (!handle) {
392         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
393         break;
394       }
395
396       /* Close operation */
397       server->fs->sftp_close(server->fs_context, sftp, handle,
398                              silc_sftp_server_status, (void *)id);
399     }
400     break;
401
402   case SILC_SFTP_READ:
403     {
404       unsigned char *hdata;
405       uint32 hdata_len;
406       uint64 offset;
407       uint32 len;
408
409       SILC_LOG_DEBUG(("Read request"));
410
411       ret = silc_buffer_unformat(&buf,
412                                  SILC_STR_UI_INT(&id),
413                                  SILC_STR_UI32_NSTRING(&hdata, 
414                                                        &hdata_len),
415                                  SILC_STR_UI_INT64(&offset),
416                                  SILC_STR_UI_INT(&len),
417                                  SILC_STR_END);
418       if (ret < 0)
419         goto failure;
420
421       /* Get the handle */
422       handle = server->fs->sftp_get_handle(server->fs_context, sftp,
423                                            (const unsigned char *)hdata,
424                                            hdata_len);
425       if (!handle) {
426         silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
427         break;
428       }
429
430       /* Read operation */
431       server->fs->sftp_read(server->fs_context, sftp, 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->sftp_get_handle(server->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->sftp_write(server->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->sftp_remove(server->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->sftp_rename(server->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->sftp_mkdir(server->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->sftp_rmdir(server->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->sftp_opendir(server->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->sftp_get_handle(server->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->sftp_readdir(server->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->sftp_stat(server->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->sftp_lstat(server->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->sftp_get_handle(server->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->sftp_fstat(server->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->sftp_setstat(server->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->sftp_get_handle(server->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->sftp_fsetstat(server->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->sftp_readlink(server->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->sftp_symlink(server->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->sftp_realpath(server->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->sftp_extended(server->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 }