Merge commit 'origin/silc.1.1.branch'
[silc.git] / lib / silcsftp / sftp_client.c
1 /*
2
3   sftp_client.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2007 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 "silc.h"
22 #include "silcsftp.h"
23 #include "sftp_util.h"
24
25 /* Request context. Every request will allocate this context and set
26    the correct callback function according the `type' field. */
27 typedef struct SilcSFTPRequestStruct {
28   struct SilcSFTPRequestStruct *next;
29   SilcSFTPStatusCallback status;
30   SilcSFTPHandleCallback handle;
31   SilcSFTPDataCallback data;
32   SilcSFTPNameCallback name;
33   SilcSFTPAttrCallback attr;
34   SilcSFTPExtendedCallback extended;
35   void *context;
36   SilcUInt32 id;
37   SilcSFTPPacket type;
38 } *SilcSFTPRequest;
39
40 /* SFTP client context */
41 typedef struct {
42   SilcStream stream;
43   SilcSchedule schedule;
44   SilcSFTPVersionCallback version;
45   SilcSFTPErrorCallback error;
46   void *context;
47   SilcList requests;
48   SilcBuffer packet;
49   SilcUInt32 id;
50 } *SilcSFTPClient;
51
52 /* File handle */
53 struct SilcSFTPHandleStruct {
54   unsigned char *data;
55   SilcUInt32 data_len;
56 };
57
58 static void silc_sftp_client_receive_process(SilcSFTP context,
59                                              SilcBuffer buffer);
60
61 /* Creates SilcSFTPHandle and returns pointer to it. The caller must free
62    the context. */
63
64 static SilcSFTPHandle silc_sftp_handle_create(unsigned char *data,
65                                               SilcUInt32 data_len)
66 {
67   SilcSFTPHandle handle;
68
69   handle = silc_calloc(1, sizeof(*handle));
70   if (!handle)
71     return NULL;
72   handle->data = silc_calloc(data_len, sizeof(*handle->data));
73   if (!handle->data)
74     return NULL;
75   memcpy(handle->data, data, data_len);
76   handle->data_len = data_len;
77
78   return handle;
79 }
80
81 /* Deletes the handle indicated by the `handle'. */
82
83 static void silc_sftp_handle_delete(SilcSFTPHandle handle)
84 {
85   silc_free(handle->data);
86   silc_free(handle);
87 }
88
89 /* Returns the handle data of the `handle' to the `data' pointer. */
90
91 static void silc_sftp_handle_get(SilcSFTPHandle handle,
92                                  const unsigned char **data,
93                                  SilcUInt32 *data_len)
94 {
95   *data = (const unsigned char *)handle->data;
96   *data_len = handle->data_len;
97 }
98
99 /* Generic routine to send SFTP packet to the SFTP server. */
100
101 static void silc_sftp_send_packet(SilcSFTPClient sftp,
102                                   SilcSFTPPacket type,
103                                   SilcUInt32 len, ...)
104 {
105   SilcBuffer tmp;
106   va_list vp;
107   int ret;
108
109   va_start(vp, len);
110   tmp = silc_sftp_packet_encode_vp(type, sftp->packet, len, vp);
111   va_end(vp);
112   if (!tmp)
113     return;
114   sftp->packet = tmp;
115
116   SILC_LOG_HEXDUMP(("SFTP packet to server"), sftp->packet->data,
117                    silc_buffer_len(sftp->packet));
118
119   /* Send the packet */
120   while (silc_buffer_len(sftp->packet) > 0) {
121     ret = silc_stream_write(sftp->stream, silc_buffer_data(sftp->packet),
122                             silc_buffer_len(sftp->packet));
123     if (ret == -2) {
124       SILC_LOG_ERROR(("Error sending SFTP packet type %d", type));
125       sftp->error((SilcSFTP)sftp, SILC_SFTP_STATUS_NO_CONNECTION,
126                   sftp->context);
127       silc_buffer_reset(sftp->packet);
128       return;
129     }
130     if (ret == 0) {
131       sftp->error((SilcSFTP)sftp, SILC_SFTP_STATUS_EOF, sftp->context);
132       silc_buffer_reset(sftp->packet);
133       return;
134     }
135     if (ret == -1)
136       return;
137
138     silc_buffer_pull(sftp->packet, ret);
139   }
140
141   /* Clear packet */
142   silc_buffer_reset(sftp->packet);
143 }
144
145 /* Finds request by request ID. */
146
147 static SilcSFTPRequest silc_sftp_find_request(SilcSFTPClient sftp,
148                                               SilcUInt32 id)
149 {
150   SilcSFTPRequest req;
151
152   SILC_LOG_DEBUG(("Finding request ID: %d", id));
153
154   silc_list_start(sftp->requests);
155   while ((req = silc_list_get(sftp->requests)) != SILC_LIST_END) {
156     if (req->id == id)
157       return req;
158   }
159
160   SILC_LOG_DEBUG(("Unknown request ID %d", id));
161
162   return NULL;
163 }
164
165 /* Function used to call the request callback indicated by the `req'. The
166    `status' will be sent to the callback function as the status of the
167    operation. The variable argument list includes the status and req->type
168    specific data. */
169
170 static void silc_sftp_call_request(SilcSFTPClient sftp,
171                                    SilcSFTPRequest req,
172                                    SilcSFTPPacket type,
173                                    SilcSFTPStatus status, ...)
174 {
175   va_list vp;
176
177   SILC_LOG_DEBUG(("Start"));
178
179   va_start(vp, status);
180
181   switch (req->type) {
182   case SILC_SFTP_READ:
183     {
184       /* Data returned */
185       unsigned char *data;
186       SilcUInt32 data_len;
187
188       if (status != SILC_SFTP_STATUS_OK) {
189         if (req->data)
190           (*req->data)((SilcSFTP)sftp, status, NULL, 0, req->context);
191         break;
192       }
193
194       data = (unsigned char *)va_arg(vp, unsigned char *);
195       data_len = (SilcUInt32)va_arg(vp, SilcUInt32);
196
197       if (req->data)
198         (*req->data)((SilcSFTP)sftp, status, (const unsigned char *)data,
199                      data_len, req->context);
200     }
201     break;
202
203   case SILC_SFTP_OPEN:
204   case SILC_SFTP_OPENDIR:
205     {
206       /* Handle returned */
207       SilcSFTPHandle handle;
208       unsigned char *hdata;
209       SilcUInt32 hdata_len;
210
211       if (status != SILC_SFTP_STATUS_OK) {
212         if (req->handle)
213           (*req->handle)((SilcSFTP)sftp, status, NULL, req->context);
214         break;
215       }
216
217       hdata = (unsigned char *)va_arg(vp, unsigned char *);
218       hdata_len = (SilcUInt32)va_arg(vp, SilcUInt32);
219       handle = silc_sftp_handle_create(hdata, hdata_len);
220       if (!handle) {
221         if (req->handle)
222           (*req->handle)((SilcSFTP)sftp, status, NULL, req->context);
223         break;
224       }
225
226       if (req->handle)
227         (*req->handle)((SilcSFTP)sftp, status, handle, req->context);
228     }
229     break;
230
231   case SILC_SFTP_CLOSE:
232   case SILC_SFTP_WRITE:
233   case SILC_SFTP_REMOVE:
234   case SILC_SFTP_RENAME:
235   case SILC_SFTP_MKDIR:
236   case SILC_SFTP_RMDIR:
237   case SILC_SFTP_SETSTAT:
238   case SILC_SFTP_FSETSTAT:
239   case SILC_SFTP_SYMLINK:
240     {
241       /* Status returned */
242       char *message, *language_tag;
243
244       message = (char *)va_arg(vp, char *);
245       language_tag = (char *)va_arg(vp, char *);
246
247       if (req->status)
248         (*req->status)((SilcSFTP)sftp, status, (const char *)message,
249                        (const char *)language_tag, req->context);
250     }
251     break;
252
253   case SILC_SFTP_STAT:
254   case SILC_SFTP_LSTAT:
255   case SILC_SFTP_FSTAT:
256     {
257       /* Attributes returned */
258       SilcSFTPAttributes attr;
259
260       if (status != SILC_SFTP_STATUS_OK) {
261         if (req->attr)
262           (*req->attr)((SilcSFTP)sftp, status, NULL, req->context);
263         break;
264       }
265
266       attr = (SilcSFTPAttributes)va_arg(vp, SilcSFTPAttributes);
267
268       if (req->attr)
269         (*req->attr)((SilcSFTP)sftp, status, (const SilcSFTPAttributes)attr,
270                      req->context);
271     }
272     break;
273
274   case SILC_SFTP_READDIR:
275   case SILC_SFTP_REALPATH:
276   case SILC_SFTP_READLINK:
277     {
278       /* Name(s) returned */
279       SilcSFTPName name;
280
281       if (status != SILC_SFTP_STATUS_OK) {
282         if (req->name)
283           (*req->name)((SilcSFTP)sftp, status, NULL, req->context);
284         break;
285       }
286
287       name = (SilcSFTPName)va_arg(vp, SilcSFTPName);
288
289       if (req->name)
290         (*req->name)((SilcSFTP)sftp, status, name, req->context);
291     }
292     break;
293
294   case SILC_SFTP_EXTENDED:
295     {
296       /* Extended reply returned */
297       unsigned char *data;
298       SilcUInt32 data_len;
299
300       if (status != SILC_SFTP_STATUS_OK) {
301         if (req->extended)
302           (*req->extended)((SilcSFTP)sftp, status, NULL, 0, req->context);
303         break;
304       }
305
306       data = (unsigned char *)va_arg(vp, unsigned char *);
307       data_len = (SilcUInt32)va_arg(vp, SilcUInt32);
308
309       if (req->extended)
310         (*req->extended)((SilcSFTP)sftp, status, (const unsigned char *)data,
311                          data_len, req->context);
312     }
313     break;
314
315   default:
316     SILC_LOG_DEBUG(("Unknown request type %d", req->type));
317     break;
318   }
319
320   /* Remove this request */
321   silc_list_del(sftp->requests, req);
322   silc_free(req);
323
324   va_end(vp);
325 }
326
327 /* Handles stream I/O */
328
329 static void silc_sftp_client_io(SilcStream stream, SilcStreamStatus status,
330                                 void *context)
331 {
332   SilcSFTPClient sftp = context;
333   unsigned char inbuf[65536];
334   SilcBufferStruct packet;
335   int ret;
336
337   switch (status) {
338   case SILC_STREAM_CAN_READ:
339     SILC_LOG_DEBUG(("Reading data from stream"));
340
341     /* Read data from stream */
342     ret = silc_stream_read(stream, inbuf, sizeof(inbuf));
343     if (ret <= 0) {
344       if (ret == 0)
345         sftp->error(context, SILC_SFTP_STATUS_EOF, sftp->context);
346       if (ret == -2)
347         sftp->error(context, SILC_SFTP_STATUS_NO_CONNECTION, sftp->context);
348       return;
349     }
350
351     SILC_LOG_DEBUG(("Read %d bytes", ret));
352
353     /* Now process the SFTP packet */
354     silc_buffer_set(&packet, inbuf, ret);
355     silc_sftp_client_receive_process(context, &packet);
356     break;
357
358   case SILC_STREAM_CAN_WRITE:
359     if (!silc_buffer_headlen(sftp->packet))
360       return;
361
362     SILC_LOG_DEBUG(("Writing pending data to stream"));
363
364     /* Write pending data to stream */
365     silc_buffer_push(sftp->packet, silc_buffer_headlen(sftp->packet));
366     while (silc_buffer_len(sftp->packet) > 0) {
367       ret = silc_stream_write(stream, sftp->packet->data,
368                               silc_buffer_len(sftp->packet));
369       if (ret == 0) {
370         sftp->error(context, SILC_SFTP_STATUS_EOF, sftp->context);
371         silc_buffer_reset(sftp->packet);
372         return;
373       }
374
375       if (ret == -2) {
376         sftp->error(context, SILC_SFTP_STATUS_NO_CONNECTION, sftp->context);
377         silc_buffer_reset(sftp->packet);
378         return;
379       }
380
381       if (ret == -1)
382         return;
383
384       /* Wrote data */
385       silc_buffer_pull(sftp->packet, ret);
386     }
387     break;
388
389   default:
390     break;
391   }
392 }
393
394 /* Starts SFTP client and returns context for it. */
395
396 SilcSFTP silc_sftp_client_start(SilcStream stream,
397                                 SilcSchedule schedule,
398                                 SilcSFTPVersionCallback version_cb,
399                                 SilcSFTPErrorCallback error_cb,
400                                 void *context)
401 {
402   SilcSFTPClient sftp;
403
404   SILC_LOG_DEBUG(("Starting SFTP client"));
405
406   if (!schedule)
407     schedule = silc_schedule_get_global();
408
409   if (!stream)
410     return NULL;
411
412   sftp = silc_calloc(1, sizeof(*sftp));
413   if (!sftp)
414     return NULL;
415   sftp->stream = stream;
416   sftp->version = version_cb;
417   sftp->error = error_cb;
418   sftp->context = context;
419   sftp->schedule = schedule;
420   silc_list_init(sftp->requests, struct SilcSFTPRequestStruct, next);
421
422   /* We handle the stream now */
423   silc_stream_set_notifier(stream, schedule, silc_sftp_client_io, sftp);
424
425   /* Send the SFTP session initialization to the server */
426   silc_sftp_send_packet(sftp, SILC_SFTP_INIT, 4,
427                         SILC_STR_UI_INT(SILC_SFTP_PROTOCOL_VERSION),
428                         SILC_STR_END);
429
430   return (SilcSFTP)sftp;
431 }
432
433 /* Shutdown's the SFTP client.  The caller is responsible of closing
434    the associated socket connection.  The SFTP context is freed and is
435    invalid after this function returns. */
436
437 void silc_sftp_client_shutdown(SilcSFTP context)
438 {
439   SilcSFTPClient sftp = (SilcSFTPClient)context;
440
441   silc_stream_set_notifier(sftp->stream, sftp->schedule, NULL, NULL);
442   if (sftp->packet)
443     silc_buffer_free(sftp->packet);
444   silc_free(sftp);
445 }
446
447 /* Function that is called to process the incmoing SFTP packet. */
448
449 void silc_sftp_client_receive_process(SilcSFTP context, SilcBuffer buffer)
450 {
451   SilcSFTPClient sftp = (SilcSFTPClient)context;
452   SilcSFTPRequest req;
453   SilcSFTPPacket type;
454   unsigned char *payload = NULL;
455   SilcUInt32 payload_len;
456   int ret;
457   SilcBufferStruct buf;
458   SilcUInt32 id;
459
460   SILC_LOG_DEBUG(("Process SFTP packet"));
461
462   /* Parse the packet */
463   type = silc_sftp_packet_decode(buffer, &payload, &payload_len);
464   if (type <= 0)
465     return;
466
467   silc_buffer_set(&buf, payload, payload_len);
468
469   switch (type) {
470   case SILC_SFTP_DATA:
471     {
472       unsigned char *data = NULL;
473       SilcUInt32 data_len = 0;
474
475       SILC_LOG_DEBUG(("Data packet"));
476
477       ret = silc_buffer_unformat(&buf,
478                                  SILC_STR_UI_INT(&id),
479                                  SILC_STR_UI32_NSTRING(&data, &data_len),
480                                  SILC_STR_END);
481       if (ret < 0)
482         break;
483
484       /* Get request */
485       req = silc_sftp_find_request(sftp, id);
486       if (!req)
487         break;
488
489       /* Call the callback */
490       silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK,
491                              data, data_len);
492     }
493     break;
494
495   case SILC_SFTP_VERSION:
496     {
497       SilcSFTPVersion version;
498
499       SILC_LOG_DEBUG(("Version packet"));
500
501       ret = silc_buffer_unformat(&buf,
502                                  SILC_STR_UI_INT(&version),
503                                  SILC_STR_END);
504       if (ret < 0) {
505         (*sftp->version)((SilcSFTP)sftp, SILC_SFTP_STATUS_FAILURE, 0,
506                          sftp->context);
507         break;
508       }
509
510       /* Call the callback */
511       (*sftp->version)((SilcSFTP)sftp, SILC_SFTP_STATUS_OK, version,
512                        sftp->context);
513     }
514     break;
515
516   case SILC_SFTP_STATUS:
517     {
518       SilcUInt32 status;
519       char *message = NULL, *language_tag = NULL;
520
521       SILC_LOG_DEBUG(("Status packet"));
522
523       ret = silc_buffer_unformat(&buf,
524                                  SILC_STR_UI_INT(&id),
525                                  SILC_STR_UI_INT(&status),
526                                  SILC_STR_END);
527       if (ret < 0)
528         break;
529
530       if (status != SILC_SFTP_STATUS_OK) {
531         silc_buffer_pull(&buf, 8);
532         ret = silc_buffer_unformat(&buf,
533                                    SILC_STR_UI32_STRING_ALLOC(&message),
534                                    SILC_STR_UI32_STRING_ALLOC(&language_tag),
535                                    SILC_STR_END);
536         if (ret < 0)
537           break;
538
539         silc_buffer_push(&buf, 8);
540       }
541
542       /* Get request */
543       req = silc_sftp_find_request(sftp, id);
544       if (!req) {
545         silc_free(message);
546         silc_free(language_tag);
547         break;
548       }
549
550       /* Call the callback */
551       silc_sftp_call_request(sftp, req, type, status, message, language_tag);
552
553       silc_free(message);
554       silc_free(language_tag);
555     }
556     break;
557
558   case SILC_SFTP_HANDLE:
559     {
560       unsigned char *handle = NULL;
561       SilcUInt32 handle_len;
562
563       SILC_LOG_DEBUG(("Handle packet"));
564
565       ret = silc_buffer_unformat(&buf,
566                                  SILC_STR_UI_INT(&id),
567                                  SILC_STR_UI32_NSTRING(&handle,
568                                                        &handle_len),
569                                  SILC_STR_END);
570       if (ret < 0)
571         break;
572
573       /* Get request */
574       req = silc_sftp_find_request(sftp, id);
575       if (!req)
576         break;
577
578       /* Call the callback */
579       silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK,
580                              handle, handle_len);
581     }
582     break;
583
584   case SILC_SFTP_NAME:
585     {
586       SilcUInt32 count;
587       SilcSFTPName name = NULL;
588
589       SILC_LOG_DEBUG(("Name packet"));
590
591       ret = silc_buffer_unformat(&buf,
592                                  SILC_STR_UI_INT(&id),
593                                  SILC_STR_UI_INT(&count),
594                                  SILC_STR_END);
595       if (ret < 0)
596         break;
597
598       /* Get request */
599       req = silc_sftp_find_request(sftp, id);
600       if (!req)
601         break;
602
603       silc_buffer_pull(&buf, 8);
604       name = silc_sftp_name_decode(count, &buf);
605       if (!name)
606         break;
607       silc_buffer_push(&buf, 8);
608
609       /* Call the callback */
610       silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, name);
611       silc_sftp_name_free(name);
612     }
613     break;
614
615   case SILC_SFTP_ATTRS:
616     {
617       SilcSFTPAttributes attr = NULL;
618       unsigned char *data;
619       SilcBufferStruct tmpbuf;
620
621       SILC_LOG_DEBUG(("Attributes packet"));
622
623       ret =
624         silc_buffer_unformat(&buf,
625                              SILC_STR_UI_INT(&id),
626                              SILC_STR_DATA(&data, silc_buffer_len(&buf) - 4),
627                              SILC_STR_END);
628       if (ret < 0)
629         break;
630
631       /* Get request */
632       req = silc_sftp_find_request(sftp, id);
633       if (!req)
634         break;
635
636       silc_buffer_set(&tmpbuf, data, silc_buffer_len(&buf) - 4);
637       attr = silc_sftp_attr_decode(&tmpbuf);
638       if (!attr)
639         break;
640
641       /* Call the callback */
642       silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, attr);
643     }
644     break;
645
646   case SILC_SFTP_EXTENDED_REPLY:
647     {
648       unsigned char *data = NULL;
649
650       SILC_LOG_DEBUG(("Extended reply packet"));
651
652       ret = silc_buffer_unformat(&buf,
653                                  SILC_STR_UI_INT(&id),
654                                  SILC_STR_DATA(&data,
655                                                silc_buffer_len(&buf) - 4),
656                                  SILC_STR_END);
657       if (ret < 0)
658         break;
659
660       /* Get request */
661       req = silc_sftp_find_request(sftp, id);
662       if (!req)
663         break;
664
665       /* Call the callback */
666       silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK,
667                              data, silc_buffer_len(&buf) - 4);
668     }
669     break;
670
671   default:
672     break;
673   }
674 }
675
676 void silc_sftp_open(SilcSFTP sftp,
677                     const char *filename,
678                     SilcSFTPFileOperation pflags,
679                     SilcSFTPAttributes attrs,
680                     SilcSFTPHandleCallback callback,
681                     void *context)
682 {
683   SilcSFTPClient client = (SilcSFTPClient)sftp;
684   SilcSFTPRequest req;
685   SilcBuffer attrs_buf;
686   SilcUInt32 len = 0;
687
688   SILC_LOG_DEBUG(("Open request"));
689
690   req = silc_calloc(1, sizeof(*req));
691   if (!req)
692     return;
693   req->id = client->id++;
694   req->type = SILC_SFTP_OPEN;
695   req->handle = callback;
696   req->context = context;
697   silc_list_add(client->requests, req);
698
699   attrs_buf = silc_sftp_attr_encode(attrs);
700   if (!attrs_buf)
701     return;
702   len = 4 + 4 + strlen(filename) + 4 + silc_buffer_len(attrs_buf);
703
704   silc_sftp_send_packet(client, req->type, len,
705                         SILC_STR_UI_INT(req->id),
706                         SILC_STR_UI_INT(strlen(filename)),
707                         SILC_STR_UI32_STRING(filename),
708                         SILC_STR_UI_INT(pflags),
709                         SILC_STR_UI_XNSTRING(attrs_buf->data,
710                                              silc_buffer_len(attrs_buf)),
711                         SILC_STR_END);
712
713   silc_buffer_free(attrs_buf);
714 }
715
716 void silc_sftp_close(SilcSFTP sftp,
717                      SilcSFTPHandle handle,
718                      SilcSFTPStatusCallback callback,
719                      void *context)
720 {
721   SilcSFTPClient client = (SilcSFTPClient)sftp;
722   SilcSFTPRequest req;
723   SilcUInt32 len = 0;
724   const unsigned char *hdata;
725   SilcUInt32 hdata_len;
726
727   SILC_LOG_DEBUG(("Close request"));
728
729   req = silc_calloc(1, sizeof(*req));
730   if (!req)
731     return;
732   req->id = client->id++;
733   req->type = SILC_SFTP_CLOSE;
734   req->status = callback;
735   req->context = context;
736   silc_list_add(client->requests, req);
737
738   silc_sftp_handle_get(handle, &hdata, &hdata_len);
739   len = 4 + 4 + hdata_len;
740
741   silc_sftp_send_packet(client, req->type, len,
742                         SILC_STR_UI_INT(req->id),
743                         SILC_STR_UI_INT(hdata_len),
744                         SILC_STR_UI_XNSTRING(hdata, hdata_len),
745                         SILC_STR_END);
746   silc_sftp_handle_delete(handle);
747 }
748
749 void silc_sftp_read(SilcSFTP sftp,
750                     SilcSFTPHandle handle,
751                     SilcUInt64 offset,
752                     SilcUInt32 len,
753                     SilcSFTPDataCallback callback,
754                     void *context)
755 {
756   SilcSFTPClient client = (SilcSFTPClient)sftp;
757   SilcSFTPRequest req;
758   SilcUInt32 len2 = 0;
759   const unsigned char *hdata;
760   SilcUInt32 hdata_len;
761
762   SILC_LOG_DEBUG(("Read request"));
763
764   req = silc_calloc(1, sizeof(*req));
765   if (!req)
766     return;
767   req->id = client->id++;
768   req->type = SILC_SFTP_READ;
769   req->data = callback;
770   req->context = context;
771   silc_list_add(client->requests, req);
772
773   silc_sftp_handle_get(handle, &hdata, &hdata_len);
774   len2 = 4 + 4 + hdata_len + 8 + 4;
775
776   silc_sftp_send_packet(client, req->type, len2,
777                         SILC_STR_UI_INT(req->id),
778                         SILC_STR_UI_INT(hdata_len),
779                         SILC_STR_UI_XNSTRING(hdata, hdata_len),
780                         SILC_STR_UI_INT64(offset),
781                         SILC_STR_UI_INT(len),
782                         SILC_STR_END);
783 }
784
785 void silc_sftp_write(SilcSFTP sftp,
786                      SilcSFTPHandle handle,
787                      SilcUInt64 offset,
788                      const unsigned char *data,
789                      SilcUInt32 data_len,
790                      SilcSFTPStatusCallback callback,
791                      void *context)
792 {
793   SilcSFTPClient client = (SilcSFTPClient)sftp;
794   SilcSFTPRequest req;
795   SilcUInt32 len = 0;
796   const unsigned char *hdata;
797   SilcUInt32 hdata_len;
798
799   SILC_LOG_DEBUG(("Write request"));
800
801   req = silc_calloc(1, sizeof(*req));
802   if (!req)
803     return;
804   req->id = client->id++;
805   req->type = SILC_SFTP_WRITE;
806   req->status = callback;
807   req->context = context;
808   silc_list_add(client->requests, req);
809
810   silc_sftp_handle_get(handle, &hdata, &hdata_len);
811   len = 4 + 4 + hdata_len + 8 + 4 + data_len;
812
813   silc_sftp_send_packet(client, req->type, len,
814                         SILC_STR_UI_INT(req->id),
815                         SILC_STR_UI_INT(hdata_len),
816                         SILC_STR_UI_XNSTRING(hdata, hdata_len),
817                         SILC_STR_UI_INT64(offset),
818                         SILC_STR_UI_INT(data_len),
819                         SILC_STR_UI_XNSTRING(data, data_len),
820                         SILC_STR_END);
821 }
822
823 void silc_sftp_remove(SilcSFTP sftp,
824                       const char *filename,
825                       SilcSFTPStatusCallback callback,
826                       void *context)
827 {
828   SilcSFTPClient client = (SilcSFTPClient)sftp;
829   SilcSFTPRequest req;
830   SilcUInt32 len = 0;
831
832   SILC_LOG_DEBUG(("Remove request"));
833
834   req = silc_calloc(1, sizeof(*req));
835   if (!req)
836     return;
837   req->id = client->id++;
838   req->type = SILC_SFTP_REMOVE;
839   req->status = callback;
840   req->context = context;
841   silc_list_add(client->requests, req);
842
843   len = 4 + 4 + strlen(filename);
844
845   silc_sftp_send_packet(client, req->type, len,
846                         SILC_STR_UI_INT(req->id),
847                         SILC_STR_UI_INT(strlen(filename)),
848                         SILC_STR_UI32_STRING(filename),
849                         SILC_STR_END);
850 }
851
852 void silc_sftp_rename(SilcSFTP sftp,
853                       const char *oldname,
854                       const char *newname,
855                       SilcSFTPStatusCallback callback,
856                       void *context)
857 {
858   SilcSFTPClient client = (SilcSFTPClient)sftp;
859   SilcSFTPRequest req;
860   SilcUInt32 len = 0;
861
862   SILC_LOG_DEBUG(("Rename request"));
863
864   req = silc_calloc(1, sizeof(*req));
865   if (!req)
866     return;
867   req->id = client->id++;
868   req->type = SILC_SFTP_RENAME;
869   req->status = callback;
870   req->context = context;
871   silc_list_add(client->requests, req);
872
873   len = 4 + 4 + strlen(oldname) + 4 + strlen(newname);
874
875   silc_sftp_send_packet(client, req->type, len,
876                         SILC_STR_UI_INT(req->id),
877                         SILC_STR_UI_INT(strlen(oldname)),
878                         SILC_STR_UI32_STRING(oldname),
879                         SILC_STR_UI_INT(strlen(newname)),
880                         SILC_STR_UI32_STRING(newname),
881                         SILC_STR_END);
882 }
883
884 void silc_sftp_mkdir(SilcSFTP sftp,
885                      const char *path,
886                      SilcSFTPAttributes attrs,
887                      SilcSFTPStatusCallback callback,
888                      void *context)
889 {
890   SilcSFTPClient client = (SilcSFTPClient)sftp;
891   SilcSFTPRequest req;
892   SilcUInt32 len = 0;
893   SilcBuffer attrs_buf;
894
895   SILC_LOG_DEBUG(("Mkdir request"));
896
897   req = silc_calloc(1, sizeof(*req));
898   if (!req)
899     return;
900   req->id = client->id++;
901   req->type = SILC_SFTP_MKDIR;
902   req->status = callback;
903   req->context = context;
904   silc_list_add(client->requests, req);
905
906   attrs_buf = silc_sftp_attr_encode(attrs);
907   if (!attrs_buf)
908     return;
909   len = 4 + 4 + strlen(path) + silc_buffer_len(attrs_buf);
910
911   silc_sftp_send_packet(client, req->type, len,
912                         SILC_STR_UI_INT(req->id),
913                         SILC_STR_UI_INT(strlen(path)),
914                         SILC_STR_UI32_STRING(path),
915                         SILC_STR_UI_XNSTRING(attrs_buf->data,
916                                              silc_buffer_len(attrs_buf)),
917                         SILC_STR_END);
918
919   silc_buffer_free(attrs_buf);
920 }
921
922 void silc_sftp_rmdir(SilcSFTP sftp,
923                      const char *path,
924                      SilcSFTPStatusCallback callback,
925                      void *context)
926 {
927   SilcSFTPClient client = (SilcSFTPClient)sftp;
928   SilcSFTPRequest req;
929   SilcUInt32 len = 0;
930
931   SILC_LOG_DEBUG(("Rmdir request"));
932
933   req = silc_calloc(1, sizeof(*req));
934   if (!req)
935     return;
936   req->id = client->id++;
937   req->type = SILC_SFTP_RMDIR;
938   req->status = callback;
939   req->context = context;
940   silc_list_add(client->requests, req);
941
942   len = 4 + 4 + strlen(path);
943
944   silc_sftp_send_packet(client, req->type, len,
945                         SILC_STR_UI_INT(req->id),
946                         SILC_STR_UI_INT(strlen(path)),
947                         SILC_STR_UI32_STRING(path),
948                         SILC_STR_END);
949 }
950
951 void silc_sftp_opendir(SilcSFTP sftp,
952                        const char *path,
953                        SilcSFTPHandleCallback callback,
954                        void *context)
955 {
956   SilcSFTPClient client = (SilcSFTPClient)sftp;
957   SilcSFTPRequest req;
958   SilcUInt32 len = 0;
959
960   SILC_LOG_DEBUG(("Opendir request"));
961
962   req = silc_calloc(1, sizeof(*req));
963   if (!req)
964     return;
965   req->id = client->id++;
966   req->type = SILC_SFTP_OPENDIR;
967   req->handle = callback;
968   req->context = context;
969   silc_list_add(client->requests, req);
970
971   len = 4 + 4 + strlen(path);
972
973   silc_sftp_send_packet(client, req->type, len,
974                         SILC_STR_UI_INT(req->id),
975                         SILC_STR_UI_INT(strlen(path)),
976                         SILC_STR_UI32_STRING(path),
977                         SILC_STR_END);
978 }
979
980 void silc_sftp_readdir(SilcSFTP sftp,
981                        SilcSFTPHandle handle,
982                        SilcSFTPNameCallback callback,
983                        void *context)
984 {
985   SilcSFTPClient client = (SilcSFTPClient)sftp;
986   SilcSFTPRequest req;
987   SilcUInt32 len = 0;
988   const unsigned char *hdata;
989   SilcUInt32 hdata_len;
990
991   SILC_LOG_DEBUG(("Readdir request"));
992
993   req = silc_calloc(1, sizeof(*req));
994   if (!req)
995     return;
996   req->id = client->id++;
997   req->type = SILC_SFTP_READDIR;
998   req->name = callback;
999   req->context = context;
1000   silc_list_add(client->requests, req);
1001
1002   silc_sftp_handle_get(handle, &hdata, &hdata_len);
1003   len = 4 + 4 + hdata_len;
1004
1005   silc_sftp_send_packet(client, req->type, len,
1006                         SILC_STR_UI_INT(req->id),
1007                         SILC_STR_UI_INT(hdata_len),
1008                         SILC_STR_UI_XNSTRING(hdata, hdata_len),
1009                         SILC_STR_END);
1010 }
1011
1012 void silc_sftp_stat(SilcSFTP sftp,
1013                     const char *path,
1014                     SilcSFTPAttrCallback callback,
1015                     void *context)
1016 {
1017   SilcSFTPClient client = (SilcSFTPClient)sftp;
1018   SilcSFTPRequest req;
1019   SilcUInt32 len = 0;
1020
1021   SILC_LOG_DEBUG(("Stat request"));
1022
1023   req = silc_calloc(1, sizeof(*req));
1024   if (!req)
1025     return;
1026   req->id = client->id++;
1027   req->type = SILC_SFTP_STAT;
1028   req->attr = callback;
1029   req->context = context;
1030   silc_list_add(client->requests, req);
1031
1032   len = 4 + 4 + strlen(path);
1033
1034   silc_sftp_send_packet(client, req->type, len,
1035                         SILC_STR_UI_INT(req->id),
1036                         SILC_STR_UI_INT(strlen(path)),
1037                         SILC_STR_UI32_STRING(path),
1038                         SILC_STR_END);
1039 }
1040
1041 void silc_sftp_lstat(SilcSFTP sftp,
1042                      const char *path,
1043                      SilcSFTPAttrCallback callback,
1044                      void *context)
1045 {
1046   SilcSFTPClient client = (SilcSFTPClient)sftp;
1047   SilcSFTPRequest req;
1048   SilcUInt32 len = 0;
1049
1050   SILC_LOG_DEBUG(("Lstat request"));
1051
1052   req = silc_calloc(1, sizeof(*req));
1053   if (!req)
1054     return;
1055   req->id = client->id++;
1056   req->type = SILC_SFTP_LSTAT;
1057   req->attr = callback;
1058   req->context = context;
1059   silc_list_add(client->requests, req);
1060
1061   len = 4 + 4 + strlen(path);
1062
1063   silc_sftp_send_packet(client, req->type, len,
1064                         SILC_STR_UI_INT(req->id),
1065                         SILC_STR_UI_INT(strlen(path)),
1066                         SILC_STR_UI32_STRING(path),
1067                         SILC_STR_END);
1068 }
1069
1070 void silc_sftp_fstat(SilcSFTP sftp,
1071                      SilcSFTPHandle handle,
1072                      SilcSFTPAttrCallback callback,
1073                      void *context)
1074 {
1075   SilcSFTPClient client = (SilcSFTPClient)sftp;
1076   SilcSFTPRequest req;
1077   SilcUInt32 len = 0;
1078   const unsigned char *hdata;
1079   SilcUInt32 hdata_len;
1080
1081   SILC_LOG_DEBUG(("Fstat request"));
1082
1083   req = silc_calloc(1, sizeof(*req));
1084   if (!req)
1085     return;
1086   req->id = client->id++;
1087   req->type = SILC_SFTP_FSTAT;
1088   req->attr = callback;
1089   req->context = context;
1090   silc_list_add(client->requests, req);
1091
1092   silc_sftp_handle_get(handle, &hdata, &hdata_len);
1093   len = 4 + 4 + hdata_len;
1094
1095   silc_sftp_send_packet(client, req->type, len,
1096                         SILC_STR_UI_INT(req->id),
1097                         SILC_STR_UI_INT(hdata_len),
1098                         SILC_STR_UI_XNSTRING(hdata, hdata_len),
1099                         SILC_STR_END);
1100 }
1101
1102 void silc_sftp_setstat(SilcSFTP sftp,
1103                        const char *path,
1104                        SilcSFTPAttributes attrs,
1105                        SilcSFTPStatusCallback callback,
1106                        void *context)
1107 {
1108   SilcSFTPClient client = (SilcSFTPClient)sftp;
1109   SilcSFTPRequest req;
1110   SilcUInt32 len = 0;
1111   SilcBuffer attrs_buf;
1112
1113   SILC_LOG_DEBUG(("Setstat request"));
1114
1115   req = silc_calloc(1, sizeof(*req));
1116   if (!req)
1117     return;
1118   req->id = client->id++;
1119   req->type = SILC_SFTP_SETSTAT;
1120   req->status = callback;
1121   req->context = context;
1122   silc_list_add(client->requests, req);
1123
1124   attrs_buf = silc_sftp_attr_encode(attrs);
1125   if (!attrs_buf)
1126     return;
1127   len = 4 + 4 + strlen(path) + silc_buffer_len(attrs_buf);
1128
1129   silc_sftp_send_packet(client, req->type, len,
1130                         SILC_STR_UI_INT(req->id),
1131                         SILC_STR_UI_INT(strlen(path)),
1132                         SILC_STR_UI32_STRING(path),
1133                         SILC_STR_UI_XNSTRING(attrs_buf->data,
1134                                              silc_buffer_len(attrs_buf)),
1135                         SILC_STR_END);
1136
1137   silc_buffer_free(attrs_buf);
1138 }
1139
1140 void silc_sftp_fsetstat(SilcSFTP sftp,
1141                         SilcSFTPHandle handle,
1142                         SilcSFTPAttributes attrs,
1143                         SilcSFTPStatusCallback callback,
1144                         void *context)
1145 {
1146   SilcSFTPClient client = (SilcSFTPClient)sftp;
1147   SilcSFTPRequest req;
1148   SilcUInt32 len = 0;
1149   SilcBuffer attrs_buf;
1150   const unsigned char *hdata;
1151   SilcUInt32 hdata_len;
1152
1153   SILC_LOG_DEBUG(("Fsetstat request"));
1154
1155   req = silc_calloc(1, sizeof(*req));
1156   if (!req)
1157     return;
1158   req->id = client->id++;
1159   req->type = SILC_SFTP_FSETSTAT;
1160   req->status = callback;
1161   req->context = context;
1162   silc_list_add(client->requests, req);
1163
1164   silc_sftp_handle_get(handle, &hdata, &hdata_len);
1165   attrs_buf = silc_sftp_attr_encode(attrs);
1166   if (!attrs_buf)
1167     return;
1168   len = 4 + 4 + hdata_len + silc_buffer_len(attrs_buf);
1169
1170   silc_sftp_send_packet(client, req->type, len,
1171                         SILC_STR_UI_INT(req->id),
1172                         SILC_STR_UI_INT(hdata_len),
1173                         SILC_STR_UI_XNSTRING(hdata, hdata_len),
1174                         SILC_STR_UI_XNSTRING(attrs_buf->data,
1175                                              silc_buffer_len(attrs_buf)),
1176                         SILC_STR_END);
1177
1178   silc_buffer_free(attrs_buf);
1179 }
1180
1181 void silc_sftp_readlink(SilcSFTP sftp,
1182                         const char *path,
1183                         SilcSFTPNameCallback callback,
1184                         void *context)
1185 {
1186   SilcSFTPClient client = (SilcSFTPClient)sftp;
1187   SilcSFTPRequest req;
1188   SilcUInt32 len = 0;
1189
1190   SILC_LOG_DEBUG(("Readlink request"));
1191
1192   req = silc_calloc(1, sizeof(*req));
1193   if (!req)
1194     return;
1195   req->id = client->id++;
1196   req->type = SILC_SFTP_READLINK;
1197   req->name = callback;
1198   req->context = context;
1199   silc_list_add(client->requests, req);
1200
1201   len = 4 + 4 + strlen(path);
1202
1203   silc_sftp_send_packet(client, req->type, len,
1204                         SILC_STR_UI_INT(req->id),
1205                         SILC_STR_UI_INT(strlen(path)),
1206                         SILC_STR_UI32_STRING(path),
1207                         SILC_STR_END);
1208 }
1209
1210 void silc_sftp_symlink(SilcSFTP sftp,
1211                        const char *linkpath,
1212                        const char *targetpath,
1213                        SilcSFTPStatusCallback callback,
1214                        void *context)
1215 {
1216   SilcSFTPClient client = (SilcSFTPClient)sftp;
1217   SilcSFTPRequest req;
1218   SilcUInt32 len = 0;
1219
1220   SILC_LOG_DEBUG(("Symlink request"));
1221
1222   req = silc_calloc(1, sizeof(*req));
1223   if (!req)
1224     return;
1225   req->id = client->id++;
1226   req->type = SILC_SFTP_SYMLINK;
1227   req->status = callback;
1228   req->context = context;
1229   silc_list_add(client->requests, req);
1230
1231   len = 4 + 4 + strlen(linkpath) + 4 + strlen(targetpath);
1232
1233   silc_sftp_send_packet(client, req->type, len,
1234                         SILC_STR_UI_INT(req->id),
1235                         SILC_STR_UI_INT(strlen(linkpath)),
1236                         SILC_STR_UI32_STRING(linkpath),
1237                         SILC_STR_UI_INT(strlen(targetpath)),
1238                         SILC_STR_UI32_STRING(targetpath),
1239                         SILC_STR_END);
1240 }
1241
1242 void silc_sftp_realpath(SilcSFTP sftp,
1243                         const char *path,
1244                         SilcSFTPNameCallback callback,
1245                         void *context)
1246 {
1247   SilcSFTPClient client = (SilcSFTPClient)sftp;
1248   SilcSFTPRequest req;
1249   SilcUInt32 len = 0;
1250
1251   SILC_LOG_DEBUG(("Realpath request"));
1252
1253   req = silc_calloc(1, sizeof(*req));
1254   if (!req)
1255     return;
1256   req->id = client->id++;
1257   req->type = SILC_SFTP_REALPATH;
1258   req->name = callback;
1259   req->context = context;
1260   silc_list_add(client->requests, req);
1261
1262   len = 4 + 4 + strlen(path);
1263
1264   silc_sftp_send_packet(client, req->type, len,
1265                         SILC_STR_UI_INT(req->id),
1266                         SILC_STR_UI_INT(strlen(path)),
1267                         SILC_STR_UI32_STRING(path),
1268                         SILC_STR_END);
1269 }
1270
1271 void silc_sftp_extended(SilcSFTP sftp,
1272                         const char *request,
1273                         const unsigned char *data,
1274                         SilcUInt32 data_len,
1275                         SilcSFTPExtendedCallback callback,
1276                         void *context)
1277 {
1278   SilcSFTPClient client = (SilcSFTPClient)sftp;
1279   SilcSFTPRequest req;
1280   SilcUInt32 len = 0;
1281
1282   SILC_LOG_DEBUG(("Extended request"));
1283
1284   req = silc_calloc(1, sizeof(*req));
1285   if (!req)
1286     return;
1287   req->id = client->id++;
1288   req->type = SILC_SFTP_WRITE;
1289   req->extended = callback;
1290   req->context = context;
1291   silc_list_add(client->requests, req);
1292
1293   len = 4 + 4 + strlen(request) + data_len;
1294
1295   silc_sftp_send_packet(client, req->type, len,
1296                         SILC_STR_UI_INT(req->id),
1297                         SILC_STR_UI_INT(strlen(request)),
1298                         SILC_STR_UI32_STRING(request),
1299                         SILC_STR_UI_XNSTRING(data, data_len),
1300                         SILC_STR_END);
1301 }