/* sftp_client.c Author: Pekka Riikonen Copyright (C) 2001 - 2007 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ /* $Id$ */ #include "silc.h" #include "silcsftp.h" #include "sftp_util.h" /* Request context. Every request will allocate this context and set the correct callback function according the `type' field. */ typedef struct SilcSFTPRequestStruct { struct SilcSFTPRequestStruct *next; SilcSFTPStatusCallback status; SilcSFTPHandleCallback handle; SilcSFTPDataCallback data; SilcSFTPNameCallback name; SilcSFTPAttrCallback attr; SilcSFTPExtendedCallback extended; void *context; SilcUInt32 id; SilcSFTPPacket type; } *SilcSFTPRequest; /* SFTP client context */ typedef struct { SilcStream stream; SilcSchedule schedule; SilcSFTPVersionCallback version; SilcSFTPErrorCallback error; void *context; SilcList requests; SilcBuffer packet; SilcUInt32 id; } *SilcSFTPClient; /* File handle */ struct SilcSFTPHandleStruct { unsigned char *data; SilcUInt32 data_len; }; static void silc_sftp_client_receive_process(SilcSFTP context, SilcBuffer buffer); /* Creates SilcSFTPHandle and returns pointer to it. The caller must free the context. */ static SilcSFTPHandle silc_sftp_handle_create(unsigned char *data, SilcUInt32 data_len) { SilcSFTPHandle handle; handle = silc_calloc(1, sizeof(*handle)); if (!handle) return NULL; handle->data = silc_calloc(data_len, sizeof(*handle->data)); if (!handle->data) return NULL; memcpy(handle->data, data, data_len); handle->data_len = data_len; return handle; } /* Deletes the handle indicated by the `handle'. */ static void silc_sftp_handle_delete(SilcSFTPHandle handle) { silc_free(handle->data); silc_free(handle); } /* Returns the handle data of the `handle' to the `data' pointer. */ static void silc_sftp_handle_get(SilcSFTPHandle handle, const unsigned char **data, SilcUInt32 *data_len) { *data = (const unsigned char *)handle->data; *data_len = handle->data_len; } /* Generic routine to send SFTP packet to the SFTP server. */ static void silc_sftp_send_packet(SilcSFTPClient sftp, SilcSFTPPacket type, SilcUInt32 len, ...) { SilcBuffer tmp; va_list vp; int ret; va_start(vp, len); tmp = silc_sftp_packet_encode_vp(type, sftp->packet, len, vp); va_end(vp); if (!tmp) return; sftp->packet = tmp; SILC_LOG_HEXDUMP(("SFTP packet to server"), sftp->packet->data, silc_buffer_len(sftp->packet)); /* Send the packet */ while (silc_buffer_len(sftp->packet) > 0) { ret = silc_stream_write(sftp->stream, silc_buffer_data(sftp->packet), silc_buffer_len(sftp->packet)); if (ret == -2) { SILC_LOG_ERROR(("Error sending SFTP packet type %d", type)); sftp->error((SilcSFTP)sftp, SILC_SFTP_STATUS_NO_CONNECTION, sftp->context); silc_buffer_reset(sftp->packet); return; } if (ret == 0) { sftp->error((SilcSFTP)sftp, SILC_SFTP_STATUS_EOF, sftp->context); silc_buffer_reset(sftp->packet); return; } if (ret == -1) return; silc_buffer_pull(sftp->packet, ret); } /* Clear packet */ silc_buffer_reset(sftp->packet); } /* Finds request by request ID. */ static SilcSFTPRequest silc_sftp_find_request(SilcSFTPClient sftp, SilcUInt32 id) { SilcSFTPRequest req; SILC_LOG_DEBUG(("Finding request ID: %d", id)); silc_list_start(sftp->requests); while ((req = silc_list_get(sftp->requests)) != SILC_LIST_END) { if (req->id == id) return req; } SILC_LOG_DEBUG(("Unknown request ID %d", id)); return NULL; } /* Function used to call the request callback indicated by the `req'. The `status' will be sent to the callback function as the status of the operation. The variable argument list includes the status and req->type specific data. */ static void silc_sftp_call_request(SilcSFTPClient sftp, SilcSFTPRequest req, SilcSFTPPacket type, SilcSFTPStatus status, ...) { va_list vp; SILC_LOG_DEBUG(("Start")); va_start(vp, status); switch (req->type) { case SILC_SFTP_READ: { /* Data returned */ unsigned char *data; SilcUInt32 data_len; if (status != SILC_SFTP_STATUS_OK) { if (req->data) (*req->data)((SilcSFTP)sftp, status, NULL, 0, req->context); break; } data = (unsigned char *)va_arg(vp, unsigned char *); data_len = (SilcUInt32)va_arg(vp, SilcUInt32); if (req->data) (*req->data)((SilcSFTP)sftp, status, (const unsigned char *)data, data_len, req->context); } break; case SILC_SFTP_OPEN: case SILC_SFTP_OPENDIR: { /* Handle returned */ SilcSFTPHandle handle; unsigned char *hdata; SilcUInt32 hdata_len; if (status != SILC_SFTP_STATUS_OK) { if (req->handle) (*req->handle)((SilcSFTP)sftp, status, NULL, req->context); break; } hdata = (unsigned char *)va_arg(vp, unsigned char *); hdata_len = (SilcUInt32)va_arg(vp, SilcUInt32); handle = silc_sftp_handle_create(hdata, hdata_len); if (!handle) { if (req->handle) (*req->handle)((SilcSFTP)sftp, status, NULL, req->context); break; } if (req->handle) (*req->handle)((SilcSFTP)sftp, status, handle, req->context); } break; case SILC_SFTP_CLOSE: case SILC_SFTP_WRITE: case SILC_SFTP_REMOVE: case SILC_SFTP_RENAME: case SILC_SFTP_MKDIR: case SILC_SFTP_RMDIR: case SILC_SFTP_SETSTAT: case SILC_SFTP_FSETSTAT: case SILC_SFTP_SYMLINK: { /* Status returned */ char *message, *language_tag; message = (char *)va_arg(vp, char *); language_tag = (char *)va_arg(vp, char *); if (req->status) (*req->status)((SilcSFTP)sftp, status, (const char *)message, (const char *)language_tag, req->context); } break; case SILC_SFTP_STAT: case SILC_SFTP_LSTAT: case SILC_SFTP_FSTAT: { /* Attributes returned */ SilcSFTPAttributes attr; if (status != SILC_SFTP_STATUS_OK) { if (req->attr) (*req->attr)((SilcSFTP)sftp, status, NULL, req->context); break; } attr = (SilcSFTPAttributes)va_arg(vp, SilcSFTPAttributes); if (req->attr) (*req->attr)((SilcSFTP)sftp, status, (const SilcSFTPAttributes)attr, req->context); } break; case SILC_SFTP_READDIR: case SILC_SFTP_REALPATH: case SILC_SFTP_READLINK: { /* Name(s) returned */ SilcSFTPName name; if (status != SILC_SFTP_STATUS_OK) { if (req->name) (*req->name)((SilcSFTP)sftp, status, NULL, req->context); break; } name = (SilcSFTPName)va_arg(vp, SilcSFTPName); if (req->name) (*req->name)((SilcSFTP)sftp, status, name, req->context); } break; case SILC_SFTP_EXTENDED: { /* Extended reply returned */ unsigned char *data; SilcUInt32 data_len; if (status != SILC_SFTP_STATUS_OK) { if (req->extended) (*req->extended)((SilcSFTP)sftp, status, NULL, 0, req->context); break; } data = (unsigned char *)va_arg(vp, unsigned char *); data_len = (SilcUInt32)va_arg(vp, SilcUInt32); if (req->extended) (*req->extended)((SilcSFTP)sftp, status, (const unsigned char *)data, data_len, req->context); } break; default: SILC_LOG_DEBUG(("Unknown request type %d", req->type)); break; } /* Remove this request */ silc_list_del(sftp->requests, req); silc_free(req); va_end(vp); } /* Handles stream I/O */ static void silc_sftp_client_io(SilcStream stream, SilcStreamStatus status, void *context) { SilcSFTPClient sftp = context; unsigned char inbuf[65536]; SilcBufferStruct packet; int ret; switch (status) { case SILC_STREAM_CAN_READ: SILC_LOG_DEBUG(("Reading data from stream")); /* Read data from stream */ ret = silc_stream_read(stream, inbuf, sizeof(inbuf)); if (ret <= 0) { if (ret == 0) sftp->error(context, SILC_SFTP_STATUS_EOF, sftp->context); if (ret == -2) sftp->error(context, SILC_SFTP_STATUS_NO_CONNECTION, sftp->context); return; } SILC_LOG_DEBUG(("Read %d bytes", ret)); /* Now process the SFTP packet */ silc_buffer_set(&packet, inbuf, ret); silc_sftp_client_receive_process(context, &packet); break; case SILC_STREAM_CAN_WRITE: if (!silc_buffer_headlen(sftp->packet)) return; SILC_LOG_DEBUG(("Writing pending data to stream")); /* Write pending data to stream */ silc_buffer_push(sftp->packet, silc_buffer_headlen(sftp->packet)); while (silc_buffer_len(sftp->packet) > 0) { ret = silc_stream_write(stream, sftp->packet->data, silc_buffer_len(sftp->packet)); if (ret == 0) { sftp->error(context, SILC_SFTP_STATUS_EOF, sftp->context); silc_buffer_reset(sftp->packet); return; } if (ret == -2) { sftp->error(context, SILC_SFTP_STATUS_NO_CONNECTION, sftp->context); silc_buffer_reset(sftp->packet); return; } if (ret == -1) return; /* Wrote data */ silc_buffer_pull(sftp->packet, ret); } break; default: break; } } /* Starts SFTP client and returns context for it. */ SilcSFTP silc_sftp_client_start(SilcStream stream, SilcSchedule schedule, SilcSFTPVersionCallback version_cb, SilcSFTPErrorCallback error_cb, void *context) { SilcSFTPClient sftp; SILC_LOG_DEBUG(("Starting SFTP client")); if (!schedule) schedule = silc_schedule_get_global(); if (!stream) return NULL; sftp = silc_calloc(1, sizeof(*sftp)); if (!sftp) return NULL; sftp->stream = stream; sftp->version = version_cb; sftp->error = error_cb; sftp->context = context; sftp->schedule = schedule; silc_list_init(sftp->requests, struct SilcSFTPRequestStruct, next); /* We handle the stream now */ silc_stream_set_notifier(stream, schedule, silc_sftp_client_io, sftp); /* Send the SFTP session initialization to the server */ silc_sftp_send_packet(sftp, SILC_SFTP_INIT, 4, SILC_STR_UI_INT(SILC_SFTP_PROTOCOL_VERSION), SILC_STR_END); return (SilcSFTP)sftp; } /* Shutdown's the SFTP client. The caller is responsible of closing the associated socket connection. The SFTP context is freed and is invalid after this function returns. */ void silc_sftp_client_shutdown(SilcSFTP context) { SilcSFTPClient sftp = (SilcSFTPClient)context; silc_stream_set_notifier(sftp->stream, sftp->schedule, NULL, NULL); if (sftp->packet) silc_buffer_free(sftp->packet); silc_free(sftp); } /* Function that is called to process the incmoing SFTP packet. */ void silc_sftp_client_receive_process(SilcSFTP context, SilcBuffer buffer) { SilcSFTPClient sftp = (SilcSFTPClient)context; SilcSFTPRequest req; SilcSFTPPacket type; unsigned char *payload = NULL; SilcUInt32 payload_len; int ret; SilcBufferStruct buf; SilcUInt32 id; SILC_LOG_DEBUG(("Process SFTP packet")); /* Parse the packet */ type = silc_sftp_packet_decode(buffer, &payload, &payload_len); if (type <= 0) return; silc_buffer_set(&buf, payload, payload_len); switch (type) { case SILC_SFTP_DATA: { unsigned char *data = NULL; SilcUInt32 data_len = 0; SILC_LOG_DEBUG(("Data packet")); ret = silc_buffer_unformat(&buf, SILC_STR_UI_INT(&id), SILC_STR_UI32_NSTRING(&data, &data_len), SILC_STR_END); if (ret < 0) break; /* Get request */ req = silc_sftp_find_request(sftp, id); if (!req) break; /* Call the callback */ silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, data, data_len); } break; case SILC_SFTP_VERSION: { SilcSFTPVersion version; SILC_LOG_DEBUG(("Version packet")); ret = silc_buffer_unformat(&buf, SILC_STR_UI_INT(&version), SILC_STR_END); if (ret < 0) { (*sftp->version)((SilcSFTP)sftp, SILC_SFTP_STATUS_FAILURE, 0, sftp->context); break; } /* Call the callback */ (*sftp->version)((SilcSFTP)sftp, SILC_SFTP_STATUS_OK, version, sftp->context); } break; case SILC_SFTP_STATUS: { SilcUInt32 status; char *message = NULL, *language_tag = NULL; SILC_LOG_DEBUG(("Status packet")); ret = silc_buffer_unformat(&buf, SILC_STR_UI_INT(&id), SILC_STR_UI_INT(&status), SILC_STR_END); if (ret < 0) break; if (status != SILC_SFTP_STATUS_OK) { silc_buffer_pull(&buf, 8); ret = silc_buffer_unformat(&buf, SILC_STR_UI32_STRING_ALLOC(&message), SILC_STR_UI32_STRING_ALLOC(&language_tag), SILC_STR_END); if (ret < 0) break; silc_buffer_push(&buf, 8); } /* Get request */ req = silc_sftp_find_request(sftp, id); if (!req) { silc_free(message); silc_free(language_tag); break; } /* Call the callback */ silc_sftp_call_request(sftp, req, type, status, message, language_tag); silc_free(message); silc_free(language_tag); } break; case SILC_SFTP_HANDLE: { unsigned char *handle = NULL; SilcUInt32 handle_len; SILC_LOG_DEBUG(("Handle packet")); ret = silc_buffer_unformat(&buf, SILC_STR_UI_INT(&id), SILC_STR_UI32_NSTRING(&handle, &handle_len), SILC_STR_END); if (ret < 0) break; /* Get request */ req = silc_sftp_find_request(sftp, id); if (!req) break; /* Call the callback */ silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, handle, handle_len); } break; case SILC_SFTP_NAME: { SilcUInt32 count; SilcSFTPName name = NULL; SILC_LOG_DEBUG(("Name packet")); ret = silc_buffer_unformat(&buf, SILC_STR_UI_INT(&id), SILC_STR_UI_INT(&count), SILC_STR_END); if (ret < 0) break; /* Get request */ req = silc_sftp_find_request(sftp, id); if (!req) break; silc_buffer_pull(&buf, 8); name = silc_sftp_name_decode(count, &buf); if (!name) break; silc_buffer_push(&buf, 8); /* Call the callback */ silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, name); silc_sftp_name_free(name); } break; case SILC_SFTP_ATTRS: { SilcSFTPAttributes attr = NULL; unsigned char *data; SilcBufferStruct tmpbuf; SILC_LOG_DEBUG(("Attributes packet")); ret = silc_buffer_unformat(&buf, SILC_STR_UI_INT(&id), SILC_STR_DATA(&data, silc_buffer_len(&buf) - 4), SILC_STR_END); if (ret < 0) break; /* Get request */ req = silc_sftp_find_request(sftp, id); if (!req) break; silc_buffer_set(&tmpbuf, data, silc_buffer_len(&buf) - 4); attr = silc_sftp_attr_decode(&tmpbuf); if (!attr) break; /* Call the callback */ silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, attr); } break; case SILC_SFTP_EXTENDED_REPLY: { unsigned char *data = NULL; SILC_LOG_DEBUG(("Extended reply packet")); ret = silc_buffer_unformat(&buf, SILC_STR_UI_INT(&id), SILC_STR_DATA(&data, silc_buffer_len(&buf) - 4), SILC_STR_END); if (ret < 0) break; /* Get request */ req = silc_sftp_find_request(sftp, id); if (!req) break; /* Call the callback */ silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, data, silc_buffer_len(&buf) - 4); } break; default: break; } } void silc_sftp_open(SilcSFTP sftp, const char *filename, SilcSFTPFileOperation pflags, SilcSFTPAttributes attrs, SilcSFTPHandleCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcBuffer attrs_buf; SilcUInt32 len = 0; SILC_LOG_DEBUG(("Open request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_OPEN; req->handle = callback; req->context = context; silc_list_add(client->requests, req); attrs_buf = silc_sftp_attr_encode(attrs); if (!attrs_buf) return; len = 4 + 4 + strlen(filename) + 4 + silc_buffer_len(attrs_buf); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(filename)), SILC_STR_UI32_STRING(filename), SILC_STR_UI_INT(pflags), SILC_STR_UI_XNSTRING(attrs_buf->data, silc_buffer_len(attrs_buf)), SILC_STR_END); silc_buffer_free(attrs_buf); } void silc_sftp_close(SilcSFTP sftp, SilcSFTPHandle handle, SilcSFTPStatusCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; const unsigned char *hdata; SilcUInt32 hdata_len; SILC_LOG_DEBUG(("Close request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_CLOSE; req->status = callback; req->context = context; silc_list_add(client->requests, req); silc_sftp_handle_get(handle, &hdata, &hdata_len); len = 4 + 4 + hdata_len; silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(hdata_len), SILC_STR_UI_XNSTRING(hdata, hdata_len), SILC_STR_END); silc_sftp_handle_delete(handle); } void silc_sftp_read(SilcSFTP sftp, SilcSFTPHandle handle, SilcUInt64 offset, SilcUInt32 len, SilcSFTPDataCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len2 = 0; const unsigned char *hdata; SilcUInt32 hdata_len; SILC_LOG_DEBUG(("Read request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_READ; req->data = callback; req->context = context; silc_list_add(client->requests, req); silc_sftp_handle_get(handle, &hdata, &hdata_len); len2 = 4 + 4 + hdata_len + 8 + 4; silc_sftp_send_packet(client, req->type, len2, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(hdata_len), SILC_STR_UI_XNSTRING(hdata, hdata_len), SILC_STR_UI_INT64(offset), SILC_STR_UI_INT(len), SILC_STR_END); } void silc_sftp_write(SilcSFTP sftp, SilcSFTPHandle handle, SilcUInt64 offset, const unsigned char *data, SilcUInt32 data_len, SilcSFTPStatusCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; const unsigned char *hdata; SilcUInt32 hdata_len; SILC_LOG_DEBUG(("Write request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_WRITE; req->status = callback; req->context = context; silc_list_add(client->requests, req); silc_sftp_handle_get(handle, &hdata, &hdata_len); len = 4 + 4 + hdata_len + 8 + 4 + data_len; silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(hdata_len), SILC_STR_UI_XNSTRING(hdata, hdata_len), SILC_STR_UI_INT64(offset), SILC_STR_UI_INT(data_len), SILC_STR_UI_XNSTRING(data, data_len), SILC_STR_END); } void silc_sftp_remove(SilcSFTP sftp, const char *filename, SilcSFTPStatusCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SILC_LOG_DEBUG(("Remove request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_REMOVE; req->status = callback; req->context = context; silc_list_add(client->requests, req); len = 4 + 4 + strlen(filename); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(filename)), SILC_STR_UI32_STRING(filename), SILC_STR_END); } void silc_sftp_rename(SilcSFTP sftp, const char *oldname, const char *newname, SilcSFTPStatusCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SILC_LOG_DEBUG(("Rename request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_RENAME; req->status = callback; req->context = context; silc_list_add(client->requests, req); len = 4 + 4 + strlen(oldname) + 4 + strlen(newname); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(oldname)), SILC_STR_UI32_STRING(oldname), SILC_STR_UI_INT(strlen(newname)), SILC_STR_UI32_STRING(newname), SILC_STR_END); } void silc_sftp_mkdir(SilcSFTP sftp, const char *path, SilcSFTPAttributes attrs, SilcSFTPStatusCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SilcBuffer attrs_buf; SILC_LOG_DEBUG(("Mkdir request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_MKDIR; req->status = callback; req->context = context; silc_list_add(client->requests, req); attrs_buf = silc_sftp_attr_encode(attrs); if (!attrs_buf) return; len = 4 + 4 + strlen(path) + silc_buffer_len(attrs_buf); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(path)), SILC_STR_UI32_STRING(path), SILC_STR_UI_XNSTRING(attrs_buf->data, silc_buffer_len(attrs_buf)), SILC_STR_END); silc_buffer_free(attrs_buf); } void silc_sftp_rmdir(SilcSFTP sftp, const char *path, SilcSFTPStatusCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SILC_LOG_DEBUG(("Rmdir request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_RMDIR; req->status = callback; req->context = context; silc_list_add(client->requests, req); len = 4 + 4 + strlen(path); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(path)), SILC_STR_UI32_STRING(path), SILC_STR_END); } void silc_sftp_opendir(SilcSFTP sftp, const char *path, SilcSFTPHandleCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SILC_LOG_DEBUG(("Opendir request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_OPENDIR; req->handle = callback; req->context = context; silc_list_add(client->requests, req); len = 4 + 4 + strlen(path); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(path)), SILC_STR_UI32_STRING(path), SILC_STR_END); } void silc_sftp_readdir(SilcSFTP sftp, SilcSFTPHandle handle, SilcSFTPNameCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; const unsigned char *hdata; SilcUInt32 hdata_len; SILC_LOG_DEBUG(("Readdir request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_READDIR; req->name = callback; req->context = context; silc_list_add(client->requests, req); silc_sftp_handle_get(handle, &hdata, &hdata_len); len = 4 + 4 + hdata_len; silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(hdata_len), SILC_STR_UI_XNSTRING(hdata, hdata_len), SILC_STR_END); } void silc_sftp_stat(SilcSFTP sftp, const char *path, SilcSFTPAttrCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SILC_LOG_DEBUG(("Stat request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_STAT; req->attr = callback; req->context = context; silc_list_add(client->requests, req); len = 4 + 4 + strlen(path); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(path)), SILC_STR_UI32_STRING(path), SILC_STR_END); } void silc_sftp_lstat(SilcSFTP sftp, const char *path, SilcSFTPAttrCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SILC_LOG_DEBUG(("Lstat request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_LSTAT; req->attr = callback; req->context = context; silc_list_add(client->requests, req); len = 4 + 4 + strlen(path); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(path)), SILC_STR_UI32_STRING(path), SILC_STR_END); } void silc_sftp_fstat(SilcSFTP sftp, SilcSFTPHandle handle, SilcSFTPAttrCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; const unsigned char *hdata; SilcUInt32 hdata_len; SILC_LOG_DEBUG(("Fstat request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_FSTAT; req->attr = callback; req->context = context; silc_list_add(client->requests, req); silc_sftp_handle_get(handle, &hdata, &hdata_len); len = 4 + 4 + hdata_len; silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(hdata_len), SILC_STR_UI_XNSTRING(hdata, hdata_len), SILC_STR_END); } void silc_sftp_setstat(SilcSFTP sftp, const char *path, SilcSFTPAttributes attrs, SilcSFTPStatusCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SilcBuffer attrs_buf; SILC_LOG_DEBUG(("Setstat request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_SETSTAT; req->status = callback; req->context = context; silc_list_add(client->requests, req); attrs_buf = silc_sftp_attr_encode(attrs); if (!attrs_buf) return; len = 4 + 4 + strlen(path) + silc_buffer_len(attrs_buf); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(path)), SILC_STR_UI32_STRING(path), SILC_STR_UI_XNSTRING(attrs_buf->data, silc_buffer_len(attrs_buf)), SILC_STR_END); silc_buffer_free(attrs_buf); } void silc_sftp_fsetstat(SilcSFTP sftp, SilcSFTPHandle handle, SilcSFTPAttributes attrs, SilcSFTPStatusCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SilcBuffer attrs_buf; const unsigned char *hdata; SilcUInt32 hdata_len; SILC_LOG_DEBUG(("Fsetstat request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_FSETSTAT; req->status = callback; req->context = context; silc_list_add(client->requests, req); silc_sftp_handle_get(handle, &hdata, &hdata_len); attrs_buf = silc_sftp_attr_encode(attrs); if (!attrs_buf) return; len = 4 + 4 + hdata_len + silc_buffer_len(attrs_buf); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(hdata_len), SILC_STR_UI_XNSTRING(hdata, hdata_len), SILC_STR_UI_XNSTRING(attrs_buf->data, silc_buffer_len(attrs_buf)), SILC_STR_END); silc_buffer_free(attrs_buf); } void silc_sftp_readlink(SilcSFTP sftp, const char *path, SilcSFTPNameCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SILC_LOG_DEBUG(("Readlink request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_READLINK; req->name = callback; req->context = context; silc_list_add(client->requests, req); len = 4 + 4 + strlen(path); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(path)), SILC_STR_UI32_STRING(path), SILC_STR_END); } void silc_sftp_symlink(SilcSFTP sftp, const char *linkpath, const char *targetpath, SilcSFTPStatusCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SILC_LOG_DEBUG(("Symlink request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_SYMLINK; req->status = callback; req->context = context; silc_list_add(client->requests, req); len = 4 + 4 + strlen(linkpath) + 4 + strlen(targetpath); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(linkpath)), SILC_STR_UI32_STRING(linkpath), SILC_STR_UI_INT(strlen(targetpath)), SILC_STR_UI32_STRING(targetpath), SILC_STR_END); } void silc_sftp_realpath(SilcSFTP sftp, const char *path, SilcSFTPNameCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SILC_LOG_DEBUG(("Realpath request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_REALPATH; req->name = callback; req->context = context; silc_list_add(client->requests, req); len = 4 + 4 + strlen(path); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(path)), SILC_STR_UI32_STRING(path), SILC_STR_END); } void silc_sftp_extended(SilcSFTP sftp, const char *request, const unsigned char *data, SilcUInt32 data_len, SilcSFTPExtendedCallback callback, void *context) { SilcSFTPClient client = (SilcSFTPClient)sftp; SilcSFTPRequest req; SilcUInt32 len = 0; SILC_LOG_DEBUG(("Extended request")); req = silc_calloc(1, sizeof(*req)); if (!req) return; req->id = client->id++; req->type = SILC_SFTP_WRITE; req->extended = callback; req->context = context; silc_list_add(client->requests, req); len = 4 + 4 + strlen(request) + data_len; silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(request)), SILC_STR_UI32_STRING(request), SILC_STR_UI_XNSTRING(data, data_len), SILC_STR_END); }