Beispiel #1
0
SilcSFTPPacket silc_sftp_packet_decode(SilcBuffer packet,
				       unsigned char **payload,
				       SilcUInt32 *payload_len)
{
  SilcUInt32 len;
  SilcUInt8 type;
  int ret;

  ret = silc_buffer_unformat(packet,
			     SILC_STR_UI_INT(&len),
			     SILC_STR_UI_CHAR(&type),
			     SILC_STR_END);
  if (ret < 0)
    return 0;

  if (type < SILC_SFTP_INIT || type > SILC_SFTP_EXTENDED_REPLY)
    return 0;

  if (len > (silc_buffer_len(packet) - 5))
    return -1;

  silc_buffer_pull(packet, 5);
  ret = silc_buffer_unformat(packet,
			     SILC_STR_UI_XNSTRING(payload, len),
			     SILC_STR_END);
  if (ret < 0)
    return 0;

  silc_buffer_push(packet, 5);

  *payload_len = len;

  return (SilcSFTPPacket)type;
}
Beispiel #2
0
SilcBool silc_public_key_payload_decode(unsigned char *data,
					SilcUInt32 data_len,
					SilcPublicKey *public_key)
{
  SilcBufferStruct buf;
  SilcUInt16 pk_len, pk_type;
  unsigned char *pk;
  int ret;

  if (!public_key)
    return FALSE;

  silc_buffer_set(&buf, data, data_len);
  ret = silc_buffer_unformat(&buf,
			     SILC_STR_ADVANCE,
			     SILC_STR_UI_SHORT(&pk_len),
			     SILC_STR_UI_SHORT(&pk_type),
			     SILC_STR_END);
  if (ret < 0 || pk_len > data_len - 4)
    return FALSE;

  if (pk_type < SILC_PKCS_SILC || pk_type > SILC_PKCS_SPKI)
    return FALSE;

  ret = silc_buffer_unformat(&buf,
			     SILC_STR_DATA(&pk, pk_len),
			     SILC_STR_END);
  if (ret < 0)
    return FALSE;

  return silc_pkcs_public_key_alloc((SilcPKCSType)pk_type,
				    pk, pk_len, public_key);
}
Beispiel #3
0
static void
silcpurple_wb_parse(SilcPurpleWb wbs, PurpleWhiteboard *wb,
		  unsigned char *message, SilcUInt32 message_len)
{
	SilcUInt8 command;
	SilcUInt16 width, height, brush_size;
	SilcUInt32 brush_color, x, y, dx, dy;
	SilcBufferStruct buf;
	int ret;

	/* Parse the packet */
	silc_buffer_set(&buf, message, message_len);
	ret = silc_buffer_unformat(&buf,
				   SILC_STR_UI_CHAR(&command),
				   SILC_STR_UI_SHORT(&width),
				   SILC_STR_UI_SHORT(&height),
				   SILC_STR_UI_INT(&brush_color),
				   SILC_STR_UI_SHORT(&brush_size),
				   SILC_STR_END);
	if (ret < 0)
		return;
	silc_buffer_pull(&buf, ret);

	/* Update whiteboard if its dimensions changed */
	if (width != wbs->width || height != wbs->height)
		silcpurple_wb_set_dimensions(wb, height, width);

	if (command == SILCPURPLE_WB_DRAW) {
		/* Parse data and draw it */
		ret = silc_buffer_unformat(&buf,
					   SILC_STR_UI_INT(&dx),
					   SILC_STR_UI_INT(&dy),
					   SILC_STR_END);
		if (ret < 0)
			return;
		silc_buffer_pull(&buf, 8);
		x = dx;
		y = dy;
		while (silc_buffer_len(&buf) > 0) {
			ret = silc_buffer_unformat(&buf,
						   SILC_STR_UI_INT(&dx),
						   SILC_STR_UI_INT(&dy),
						   SILC_STR_END);
			if (ret < 0)
				return;
			silc_buffer_pull(&buf, 8);

			purple_whiteboard_draw_line(wb, x, y, x + dx, y + dy,
						    brush_color, brush_size);
			x += dx;
			y += dy;
		}
	}

	if (command == SILCPURPLE_WB_CLEAR)
		purple_whiteboard_clear(wb);
}
Beispiel #4
0
SilcCommandPayload silc_command_payload_parse(const unsigned char *payload,
					      SilcUInt32 payload_len)
{
  SilcBufferStruct buffer;
  SilcCommandPayload newp;
  unsigned char args_num;
  SilcUInt16 p_len;
  int ret;

  SILC_LOG_DEBUG(("Parsing command payload"));

  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
  newp = silc_calloc(1, sizeof(*newp));
  if (!newp)
    return NULL;

  /* Parse the Command Payload */
  ret = silc_buffer_unformat(&buffer,
			     SILC_STR_UI_SHORT(&p_len),
			     SILC_STR_UI_CHAR(&newp->cmd),
			     SILC_STR_UI_CHAR(&args_num),
			     SILC_STR_UI_SHORT(&newp->ident),
			     SILC_STR_END);
  if (ret == -1) {
    SILC_LOG_ERROR(("Incorrect command payload in packet"));
    silc_free(newp);
    return NULL;
  }

  if (p_len != silc_buffer_len(&buffer)) {
    SILC_LOG_ERROR(("Incorrect command payload in packet"));
    silc_free(newp);
    return NULL;
  }

  if (newp->cmd == 0) {
    SILC_LOG_ERROR(("Incorrect command type in command payload"));
    silc_free(newp);
    return NULL;
  }

  silc_buffer_pull(&buffer, SILC_COMMAND_PAYLOAD_LEN);
  if (args_num) {
    newp->args = silc_argument_payload_parse(buffer.data,
					     silc_buffer_len(&buffer),
					     args_num);
    if (!newp->args) {
      silc_free(newp);
      return NULL;
    }
  }
  silc_buffer_push(&buffer, SILC_COMMAND_PAYLOAD_LEN);

  return newp;
}
Beispiel #5
0
SilcDList silc_attribute_payload_parse(const unsigned char *payload,
				       SilcUInt32 payload_len)
{
  SilcBufferStruct buffer;
  SilcDList list;
  SilcAttributePayload newp;
  SilcUInt32 len;
  int ret;

  SILC_LOG_DEBUG(("Parsing Attribute Payload list"));

  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
  list = silc_dlist_init();

  while (silc_buffer_len(&buffer)) {
    newp = silc_calloc(1, sizeof(*newp));
    if (!newp)
      goto err;
    ret = silc_buffer_unformat(&buffer,
			       SILC_STR_UI_CHAR(&newp->attribute),
			       SILC_STR_UI_CHAR(&newp->flags),
			       SILC_STR_UI16_NSTRING_ALLOC(&newp->data,
							   &newp->data_len),
			       SILC_STR_END);
    if (ret == -1)
      goto err;

    if (newp->data_len > silc_buffer_len(&buffer) - 4) {
      SILC_LOG_ERROR(("Incorrect attribute payload in list"));
      goto err;
    }

    len = 4 + newp->data_len;
    if (silc_buffer_len(&buffer) < len)
      break;
    silc_buffer_pull(&buffer, len);

    silc_dlist_add(list, newp);
  }

  return list;

 err:
  silc_attribute_payload_list_free(list);
  return NULL;
}
Beispiel #6
0
SilcNotifyPayload silc_notify_payload_parse(const unsigned char *payload,
					    SilcUInt32 payload_len)
{
  SilcBufferStruct buffer;
  SilcNotifyPayload newp;
  SilcUInt16 len;
  int ret;

  SILC_LOG_DEBUG(("Parsing Notify payload"));

  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
  newp = silc_calloc(1, sizeof(*newp));
  if (!newp)
    return NULL;

  ret = silc_buffer_unformat(&buffer,
			     SILC_STR_UI_SHORT(&newp->type),
			     SILC_STR_UI_SHORT(&len),
			     SILC_STR_UI_CHAR(&newp->argc),
			     SILC_STR_END);
  if (ret == -1)
    goto err;

  if (len > silc_buffer_len(&buffer))
    goto err;

  if (newp->argc) {
    silc_buffer_pull(&buffer, 5);
    newp->args = silc_argument_payload_parse(buffer.data,
					     silc_buffer_len(&buffer),
					     newp->argc);
    if (!newp->args)
      goto err;
    silc_buffer_push(&buffer, 5);
  }

  return newp;

 err:
  silc_free(newp);
  return NULL;
}
Beispiel #7
0
SilcSFTPName silc_sftp_name_decode(SilcUInt32 count, SilcBuffer buffer)
{
  SilcSFTPName name;
  int i;
  int ret;

  name = silc_calloc(1, sizeof(*name));
  if (!name)
    return NULL;
  name->filename = silc_calloc(count, sizeof(*name->filename));
  name->long_filename = silc_calloc(count, sizeof(*name->filename));
  name->attrs = silc_calloc(count, sizeof(*name->attrs));
  if (!name->filename || !name->long_filename || !name->attrs) {
    silc_sftp_name_free(name);
    return NULL;
  }
  name->count = count;

  for (i = 0; i < count; i++) {
    ret =
      silc_buffer_unformat(buffer,
			   SILC_STR_UI32_STRING_ALLOC(&name->filename[i]),
			   SILC_STR_UI32_STRING_ALLOC(&name->long_filename[i]),
			   SILC_STR_END);
    if (ret < 0) {
      silc_sftp_name_free(name);
      return NULL;
    }

    silc_buffer_pull(buffer, ret);

    /* Decode attributes, this will pull the `buffer' to correct place
       for next round automatically. */
    name->attrs[i] = silc_sftp_attr_decode(buffer);
    if (!name->attrs[i]) {
      silc_sftp_name_free(name);
      return NULL;
    }
  }

  return name;
}
Beispiel #8
0
static void silc_client_private_message_key_cb(SilcClient client,
					       SilcClientConnection conn,
					       SilcStatus status,
					       SilcDList clients,
					       void *context)
{
  SilcFSMThread thread = context;
  SilcPacket packet = silc_fsm_get_state_context(thread);
  unsigned char *cipher = NULL, *hmac = NULL;
  SilcClientEntry client_entry;
  int ret;

  if (!clients) {
    silc_packet_free(packet);
    silc_fsm_finish(thread);
    return;
  }

  /* Parse the private message key payload */
  ret = silc_buffer_unformat(&packet->buffer,
			     SILC_STR_UI16_STRING_ALLOC(&cipher),
			     SILC_STR_UI16_STRING_ALLOC(&hmac),
			     SILC_STR_END);
  if (!ret)
    goto out;

  /* Mark that we are responder */
  client_entry = silc_dlist_get(clients);
  client_entry->internal.prv_resp = TRUE;

  /* XXX we should notify application that remote wants to set up the
     static key.  And we should tell if we already have key with remote.
     Application should return status telling whether to delete the key
     or not. */

 out:
  silc_free(cipher);
  silc_free(hmac);
  silc_packet_free(packet);
  silc_fsm_finish(thread);
}
Beispiel #9
0
SilcBool silc_attribute_get_object(SilcAttributePayload payload,
				   void *object, SilcUInt32 object_size)
{
  SilcUInt16 len;
  SilcBool ret = FALSE;

  if (!object || payload->flags & SILC_ATTRIBUTE_FLAG_INVALID)
    return FALSE;

  switch (payload->attribute) {
  case SILC_ATTRIBUTE_USER_INFO:
    {
      SilcVCard vcard = object;
      if (object_size != sizeof(*vcard))
	break;
      if (!silc_vcard_decode(payload->data, payload->data_len, vcard))
	break;
      ret = TRUE;
    }
    break;

  case SILC_ATTRIBUTE_SERVICE:
    {
      SilcAttributeObjService *service = object;
      SilcBufferStruct buf;
      SilcUInt16 addr_len, signon_len;
      char *addr, *signon;
      int res;
      if (object_size != sizeof(*service))
	break;
      if (payload->data_len < 13)
	break;
      silc_buffer_set(&buf, payload->data, payload->data_len);
      res = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&service->port),
				 SILC_STR_UI16_NSTRING(&addr, &addr_len),
				 SILC_STR_UI_CHAR(&service->status),
				 SILC_STR_UI16_NSTRING(&signon, &signon_len),
				 SILC_STR_UI_INT(&service->idle),
				 SILC_STR_END);
      if (res == -1)
	break;
      memset(service->address, 0, sizeof(service->address));
      memset(service->signon, 0, sizeof(service->signon));
      memcpy(service->address, addr,
	     (addr_len < sizeof(service->address) - 1 ? addr_len :
	      sizeof(service->address) - 1));
      memcpy(service->signon, signon,
	     (signon_len < sizeof(service->signon) - 1 ? signon_len :
	      sizeof(service->signon) - 1));
      ret = TRUE;
    }
    break;

  case SILC_ATTRIBUTE_STATUS_MOOD:
  case SILC_ATTRIBUTE_PREFERRED_CONTACT:
    {
      SilcUInt32 *mask = (SilcUInt32 *)object;
      if (object_size != sizeof(SilcUInt32))
	break;
      if (payload->data_len < 4)
	break;
      SILC_GET32_MSB(*mask, payload->data);
      ret = TRUE;
    }
    break;

  case SILC_ATTRIBUTE_STATUS_FREETEXT:
  case SILC_ATTRIBUTE_PREFERRED_LANGUAGE:
  case SILC_ATTRIBUTE_TIMEZONE:
    {
      char *string = object;
      if (payload->data_len < 2)
	break;
      SILC_GET16_MSB(len, payload->data);
      if (payload->data_len < 2 + len)
	break;
      if (object_size < len)
	break;
      memcpy(string, payload->data + 2, len);
      ret = TRUE;
    }
    break;

  case SILC_ATTRIBUTE_STATUS_MESSAGE:
  case SILC_ATTRIBUTE_EXTENSION:
  case SILC_ATTRIBUTE_USER_ICON:
    {
      SilcMime mime = object;
      if (object_size != sizeof(*mime))
	break;
      if (!silc_mime_decode(mime, payload->data, payload->data_len))
	break;
      ret = TRUE;
    }
    break;

  case SILC_ATTRIBUTE_GEOLOCATION:
    {
      SilcAttributeObjGeo *geo = object;
      SilcBufferStruct buffer;
      int res;
      if (object_size != sizeof(*geo))
	break;
      silc_buffer_set(&buffer, (unsigned char *)payload->data,
		      payload->data_len);
      res = silc_buffer_unformat(&buffer,
				 SILC_STR_UI16_STRING_ALLOC(&geo->longitude),
				 SILC_STR_UI16_STRING_ALLOC(&geo->latitude),
				 SILC_STR_UI16_STRING_ALLOC(&geo->altitude),
				 SILC_STR_UI16_STRING_ALLOC(&geo->accuracy),
				 SILC_STR_END);
      if (res == -1)
	break;
      ret = TRUE;
    }
    break;

  case SILC_ATTRIBUTE_DEVICE_INFO:
    {
      SilcAttributeObjDevice *dev = object;
      SilcBufferStruct buffer;
      SilcUInt32 type;
      int res;
      if (object_size != sizeof(*dev))
	break;
      silc_buffer_set(&buffer, (unsigned char *)payload->data,
		      payload->data_len);
      res =
	silc_buffer_unformat(&buffer,
			     SILC_STR_UI_INT(&type),
			     SILC_STR_UI16_STRING_ALLOC(&dev->manufacturer),
			     SILC_STR_UI16_STRING_ALLOC(&dev->version),
			     SILC_STR_UI16_STRING_ALLOC(&dev->model),
			     SILC_STR_UI16_STRING_ALLOC(&dev->language),
			     SILC_STR_END);
      if (res == -1)
	break;
      dev->type = type;
      ret = TRUE;
    }
    break;

  case SILC_ATTRIBUTE_PHONE_NUMBER:
    {
      SilcAttributeObjPN *pn = object;
      SilcBufferStruct buffer;
      SilcUInt32 pn_format;
      int res;
      if (object_size != sizeof(*pn))
	break;
      silc_buffer_set(&buffer, (unsigned char *)payload->data,
		      payload->data_len);
      res =
	silc_buffer_unformat(&buffer,
			     SILC_STR_UI_INT(&pn_format),
			     SILC_STR_UI16_STRING_ALLOC(&pn->number),
			     SILC_STR_END);
      if (res == -1)
	break;
      pn->format = pn_format;
      ret = TRUE;
    }
    break;

  case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
  case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
    {
      SilcAttributeObjPk *pk = object;
      SilcBufferStruct buffer;
      int res;
      if (object_size != sizeof(*pk))
	break;
      silc_buffer_set(&buffer, (unsigned char *)payload->data,
		      payload->data_len);
      res =
	silc_buffer_unformat(&buffer,
			     SILC_STR_UI16_NSTRING_ALLOC(&pk->type, &len),
			     SILC_STR_END);
      if (res == -1 || len > silc_buffer_len(&buffer) - 2)
	break;
      pk->data = silc_memdup(payload->data + 2 + len,
			     payload->data_len - 2 - len);
      pk->data_len = payload->data_len - 2 - len;
      ret = TRUE;
    }
    break;

  case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
  case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
    {
      SilcAttributeObjPk *pk = object;
      if (object_size != sizeof(*pk))
	break;
      pk->type = NULL;
      pk->data = silc_memdup(payload->data, payload->data_len);
      pk->data_len = payload->data_len;
      ret = TRUE;
    }
    break;

  default:
    break;
  }

  return ret;
}
Beispiel #10
0
SilcSFTPAttributes silc_sftp_attr_decode(SilcBuffer buffer)
{
  SilcSFTPAttributes attr;

  attr = silc_calloc(1, sizeof(*attr));
  if (!attr)
    return NULL;

  if (silc_buffer_unformat(buffer,
			   SILC_STR_UI_INT(&attr->flags),
			   SILC_STR_END) < 0)
    goto out;

  silc_buffer_pull(buffer, 4);

  if (attr->flags & SILC_SFTP_ATTR_SIZE) {
    if (silc_buffer_unformat(buffer,
			     SILC_STR_UI_INT64(&attr->size),
			     SILC_STR_END) < 0)
      goto out;

    silc_buffer_pull(buffer, 8);
  }

  if (attr->flags & SILC_SFTP_ATTR_UIDGID) {
    if (silc_buffer_unformat(buffer,
			     SILC_STR_UI_INT(&attr->uid),
			     SILC_STR_UI_INT(&attr->gid),
			     SILC_STR_END) < 0)
      goto out;

    silc_buffer_pull(buffer, 8);
  }

  if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS) {
    if (silc_buffer_unformat(buffer,
			     SILC_STR_UI_INT(&attr->permissions),
			     SILC_STR_END) < 0)
      goto out;

    silc_buffer_pull(buffer, 4);
  }

  if (attr->flags & SILC_SFTP_ATTR_ACMODTIME) {
    if (silc_buffer_unformat(buffer,
			     SILC_STR_UI_INT(&attr->atime),
			     SILC_STR_UI_INT(&attr->mtime),
			     SILC_STR_END) < 0)
      goto out;

    silc_buffer_pull(buffer, 8);
  }

  if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
    int i;

    if (silc_buffer_unformat(buffer,
			     SILC_STR_UI_INT(&attr->extended_count),
			     SILC_STR_END) < 0)
      goto out;

    silc_buffer_pull(buffer, 4);

    attr->extended_type = silc_calloc(attr->extended_count,
				      sizeof(*attr->extended_type));
    attr->extended_data = silc_calloc(attr->extended_count,
				      sizeof(*attr->extended_data));
    if (!attr->extended_type || !attr->extended_data)
      return NULL;

    for (i = 0; i < attr->extended_count; i++) {
      unsigned char *tmp, *tmp2;
      SilcUInt32 tmp_len, tmp2_len;

      if (silc_buffer_unformat(buffer,
			       SILC_STR_UI32_NSTRING(&tmp, &tmp_len),
			       SILC_STR_UI32_NSTRING(&tmp2, &tmp2_len),
			       SILC_STR_END) < 0)
	goto out;

      attr->extended_type[i] = silc_buffer_alloc(tmp_len);
      attr->extended_data[i] = silc_buffer_alloc(tmp2_len);
      if (!attr->extended_type[i] || !attr->extended_data[i])
	return NULL;
      silc_buffer_put(attr->extended_type[i], tmp, tmp_len);
      silc_buffer_put(attr->extended_data[i], tmp2, tmp2_len);

      silc_buffer_pull(buffer, tmp_len + 4 + tmp2_len + 4);
    }
  }

  return attr;

 out:
  silc_sftp_attr_free(attr);
  return NULL;
}
Beispiel #11
0
static void silc_sftp_server_receive_process(SilcSFTP sftp, SilcBuffer buffer)
{
  SilcSFTPServer server = (SilcSFTPServer)sftp;
  SilcSFTPPacket type;
  char *filename = NULL, *path = NULL;
  unsigned char *payload = NULL;
  SilcUInt32 payload_len;
  int ret;
  SilcBufferStruct buf;
  SilcUInt32 id;
  SilcSFTPAttributes attrs;
  SilcSFTPHandle handle;
  SilcSFTPMonitorDataStruct mdata;

  SILC_LOG_DEBUG(("Start"));

  /* Parse the packet */
  type = silc_sftp_packet_decode(buffer, &payload, &payload_len);
  if (type <= 0)
    return;

  silc_buffer_set(&buf, payload, payload_len);

  memset(&mdata, 0, sizeof(mdata));

  switch (type) {
  case SILC_SFTP_READ:
    {
      unsigned char *hdata;
      SilcUInt32 hdata_len;
      SilcUInt64 offset;
      SilcUInt32 len;

      SILC_LOG_DEBUG(("Read request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_NSTRING(&hdata,
						       &hdata_len),
				 SILC_STR_UI_INT64(&offset),
				 SILC_STR_UI_INT(&len),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Get the handle */
      handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
					       (const unsigned char *)hdata,
					       hdata_len);
      if (!handle) {
	silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
	break;
      }

      /* Read operation */
      server->fs->fs->sftp_read(server->fs->fs_context, sftp,
				handle, offset, len,
				silc_sftp_server_data, SILC_32_TO_PTR(id));

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_READ && server->monitor) {
	mdata.offset = offset;
	mdata.data_len = len;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_READ, &mdata,
			   server->monitor_context);
      }
    }
    break;

  case SILC_SFTP_WRITE:
    {
      unsigned char *hdata;
      SilcUInt32 hdata_len;
      SilcUInt64 offset;
      unsigned char *data;
      SilcUInt32 data_len;

      SILC_LOG_DEBUG(("Read request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_NSTRING(&hdata,
						       &hdata_len),
				 SILC_STR_UI_INT64(&offset),
				 SILC_STR_UI32_NSTRING(&data,
						       &data_len),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Get the handle */
      handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
					       (const unsigned char *)hdata,
					       hdata_len);
      if (!handle) {
	silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
	break;
      }

      /* Write operation */
      server->fs->fs->sftp_write(server->fs->fs_context, sftp, handle, offset,
				 (const unsigned char *)data, data_len,
				 silc_sftp_server_status, SILC_32_TO_PTR(id));

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_WRITE && server->monitor) {
	mdata.offset = offset;
	mdata.data_len = data_len;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_WRITE, &mdata,
			   server->monitor_context);
      }
    }
    break;

  case SILC_SFTP_INIT:
    {
      SilcSFTPVersion version;

      SILC_LOG_DEBUG(("Init request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&version),
				 SILC_STR_END);
      if (ret < 0)
	break;

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_INIT && server->monitor) {
	mdata.version = version;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_INIT, &mdata,
			   server->monitor_context);
      }

      silc_sftp_send_packet(server, SILC_SFTP_VERSION, 4,
			    SILC_STR_UI_INT(SILC_SFTP_PROTOCOL_VERSION),
			    SILC_STR_END);
    }
    break;

  case SILC_SFTP_OPEN:
    {
      SilcSFTPFileOperation pflags;
      unsigned char *attr_buf;
      SilcUInt32 attr_len = 0;
      SilcBufferStruct tmpbuf;

      SILC_LOG_DEBUG(("Open request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&filename),
				 SILC_STR_UI_INT(&pflags),
				 SILC_STR_UI32_NSTRING(&attr_buf,
						       &attr_len),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      if (attr_len) {
	silc_buffer_set(&tmpbuf, attr_buf, attr_len);
	attrs = silc_sftp_attr_decode(&tmpbuf);
	if (!attrs)
	  goto failure;
      } else {
	attrs = silc_calloc(1, sizeof(*attrs));
	if (!attrs)
	  goto failure;
      }

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_OPEN && server->monitor) {
	mdata.name = filename;
	mdata.pflags = pflags;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_OPEN, &mdata,
			   server->monitor_context);
      }

      /* Open operation */
      server->fs->fs->sftp_open(server->fs->fs_context, sftp, filename, pflags,
				attrs, silc_sftp_server_handle,
				SILC_32_TO_PTR(id));

      silc_free(filename);
      silc_sftp_attr_free(attrs);
    }
    break;

  case SILC_SFTP_CLOSE:
    {
      unsigned char *hdata;
      SilcUInt32 hdata_len;

      SILC_LOG_DEBUG(("Close request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_NSTRING(&hdata,
						       &hdata_len),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Get the handle */
      handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
					       (const unsigned char *)hdata,
					       hdata_len);
      if (!handle) {
	silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
	break;
      }

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_CLOSE && server->monitor) {
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_CLOSE, &mdata,
			   server->monitor_context);
      }

      /* Close operation */
      server->fs->fs->sftp_close(server->fs->fs_context, sftp, handle,
				 silc_sftp_server_status, SILC_32_TO_PTR(id));

    }
    break;

  case SILC_SFTP_REMOVE:
    {
      SILC_LOG_DEBUG(("Remove request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&filename),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_REMOVE && server->monitor) {
	mdata.name = filename;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_REMOVE, &mdata,
			   server->monitor_context);
      }

      /* Remove operation */
      server->fs->fs->sftp_remove(server->fs->fs_context, sftp, filename,
				  silc_sftp_server_status, SILC_32_TO_PTR(id));

      silc_free(filename);
    }
    break;

  case SILC_SFTP_RENAME:
    {
      char *newname = NULL;

      SILC_LOG_DEBUG(("Rename request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&filename),
				 SILC_STR_UI32_STRING_ALLOC(&newname),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_RENAME && server->monitor) {
	mdata.name = filename;
	mdata.name2 = newname;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_RENAME, &mdata,
			   server->monitor_context);
      }

      /* Rename operation */
      server->fs->fs->sftp_rename(server->fs->fs_context, sftp,
				  filename, newname,
				  silc_sftp_server_status, SILC_32_TO_PTR(id));

      silc_free(filename);
      silc_free(newname);
    }
    break;

  case SILC_SFTP_MKDIR:
    {
      unsigned char *attr_buf;
      SilcUInt32 attr_len = 0;
      SilcBufferStruct tmpbuf;

      SILC_LOG_DEBUG(("Mkdir request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&path),
				 SILC_STR_UI32_NSTRING(&attr_buf,
						       &attr_len),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      if (attr_len) {
	silc_buffer_set(&tmpbuf, attr_buf, attr_len);
	attrs = silc_sftp_attr_decode(&tmpbuf);
	if (!attrs)
	  goto failure;
      } else {
	attrs = silc_calloc(1, sizeof(*attrs));
	if (!attrs)
	  goto failure;
      }

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_MKDIR && server->monitor) {
	mdata.name = path;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_MKDIR, &mdata,
			   server->monitor_context);
      }

      /* Mkdir operation */
      server->fs->fs->sftp_mkdir(server->fs->fs_context, sftp, path, attrs,
				 silc_sftp_server_status, SILC_32_TO_PTR(id));

      silc_sftp_attr_free(attrs);
      silc_free(path);
    }
    break;

  case SILC_SFTP_RMDIR:
    {
      SILC_LOG_DEBUG(("Rmdir request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&path),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_RMDIR && server->monitor) {
	mdata.name = path;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_RMDIR, &mdata,
			   server->monitor_context);
      }

      /* Rmdir operation */
      server->fs->fs->sftp_rmdir(server->fs->fs_context, sftp, path,
				 silc_sftp_server_status, SILC_32_TO_PTR(id));

      silc_free(path);
    }
    break;

  case SILC_SFTP_OPENDIR:
    {
      SILC_LOG_DEBUG(("Opendir request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&path),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_OPENDIR && server->monitor) {
	mdata.name = path;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_OPENDIR, &mdata,
			   server->monitor_context);
      }

      /* Opendir operation */
      server->fs->fs->sftp_opendir(server->fs->fs_context, sftp, path,
				   silc_sftp_server_handle, SILC_32_TO_PTR(id));

      silc_free(path);
    }
    break;

  case SILC_SFTP_READDIR:
    {
      unsigned char *hdata;
      SilcUInt32 hdata_len;

      SILC_LOG_DEBUG(("Readdir request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_NSTRING(&hdata,
						       &hdata_len),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Get the handle */
      handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
					       (const unsigned char *)hdata,
					       hdata_len);
      if (!handle) {
	silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
	break;
      }

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_READDIR && server->monitor) {
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_READDIR, &mdata,
			   server->monitor_context);
      }

      /* Readdir operation */
      server->fs->fs->sftp_readdir(server->fs->fs_context, sftp, handle,
				   silc_sftp_server_name, SILC_32_TO_PTR(id));
    }
    break;

  case SILC_SFTP_STAT:
    {
      SILC_LOG_DEBUG(("Stat request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&path),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_STAT && server->monitor) {
	mdata.name = path;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_STAT, &mdata,
			   server->monitor_context);
      }

      /* Stat operation */
      server->fs->fs->sftp_stat(server->fs->fs_context, sftp, path,
				silc_sftp_server_attr, SILC_32_TO_PTR(id));

      silc_free(path);
    }
    break;

  case SILC_SFTP_LSTAT:
    {
      SILC_LOG_DEBUG(("Lstat request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&path),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_LSTAT && server->monitor) {
	mdata.name = path;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_LSTAT, &mdata,
			   server->monitor_context);
      }

      /* Lstat operation */
      server->fs->fs->sftp_lstat(server->fs->fs_context, sftp, path,
				 silc_sftp_server_attr, SILC_32_TO_PTR(id));

      silc_free(path);
    }
    break;

  case SILC_SFTP_FSTAT:
    {
      unsigned char *hdata;
      SilcUInt32 hdata_len;

      SILC_LOG_DEBUG(("Fstat request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_NSTRING(&hdata,
						       &hdata_len),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Get the handle */
      handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
					       (const unsigned char *)hdata,
					       hdata_len);
      if (!handle) {
	silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
	break;
      }

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_FSTAT && server->monitor) {
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_FSTAT, &mdata,
			   server->monitor_context);
      }

      /* Fstat operation */
      server->fs->fs->sftp_fstat(server->fs->fs_context, sftp, handle,
				 silc_sftp_server_attr, SILC_32_TO_PTR(id));
    }
    break;

  case SILC_SFTP_SETSTAT:
    {
      unsigned char *attr_buf;
      SilcUInt32 attr_len = 0;
      SilcBufferStruct tmpbuf;

      SILC_LOG_DEBUG(("Setstat request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&path),
				 SILC_STR_UI32_NSTRING(&attr_buf,
						       &attr_len),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      if (attr_len) {
	silc_buffer_set(&tmpbuf, attr_buf, attr_len);
	attrs = silc_sftp_attr_decode(&tmpbuf);
	if (!attrs)
	  goto failure;
      } else {
	attrs = silc_calloc(1, sizeof(*attrs));
	if (!attrs)
	  goto failure;
      }

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_SETSTAT && server->monitor) {
	mdata.name = path;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_SETSTAT, &mdata,
			   server->monitor_context);
      }

      /* Setstat operation */
      server->fs->fs->sftp_setstat(server->fs->fs_context, sftp, path, attrs,
				   silc_sftp_server_status,
				   SILC_32_TO_PTR(id));

      silc_sftp_attr_free(attrs);
      silc_free(path);
    }
    break;

  case SILC_SFTP_FSETSTAT:
    {
      unsigned char *hdata, *attr_buf;
      SilcUInt32 hdata_len, attr_len = 0;
      SilcBufferStruct tmpbuf;

      SILC_LOG_DEBUG(("Fsetstat request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_NSTRING(&hdata,
						       &hdata_len),
				 SILC_STR_UI32_NSTRING(&attr_buf,
						       &attr_len),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      if (attr_len) {
	silc_buffer_set(&tmpbuf, attr_buf, attr_len);
	attrs = silc_sftp_attr_decode(&tmpbuf);
	if (!attrs)
	  goto failure;
      } else {
	attrs = silc_calloc(1, sizeof(*attrs));
	if (!attrs)
	  goto failure;
      }

      /* Get the handle */
      handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
					       (const unsigned char *)hdata,
					       hdata_len);
      if (!handle) {
	silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
	break;
      }

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_FSETSTAT && server->monitor) {
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_FSETSTAT, &mdata,
			   server->monitor_context);
      }

      /* Fsetstat operation */
      server->fs->fs->sftp_fsetstat(server->fs->fs_context, sftp,
				    handle, attrs,
				    silc_sftp_server_status,
				    SILC_32_TO_PTR(id));

      silc_sftp_attr_free(attrs);
    }
    break;

  case SILC_SFTP_READLINK:
    {
      SILC_LOG_DEBUG(("Readlink request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&path),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_READLINK && server->monitor) {
	mdata.name = path;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_READLINK, &mdata,
			   server->monitor_context);
      }

      /* Readlink operation */
      server->fs->fs->sftp_readlink(server->fs->fs_context, sftp, path,
				    silc_sftp_server_name, SILC_32_TO_PTR(id));

      silc_free(path);
    }
    break;

  case SILC_SFTP_SYMLINK:
    {
      char *target = NULL;

      SILC_LOG_DEBUG(("Symlink request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&path),
				 SILC_STR_UI32_STRING_ALLOC(&target),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_SYMLINK && server->monitor) {
	mdata.name = path;
	mdata.name2 = target;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_SYMLINK, &mdata,
			   server->monitor_context);
      }

      /* Symlink operation */
      server->fs->fs->sftp_symlink(server->fs->fs_context, sftp, path, target,
				   silc_sftp_server_status,
				   SILC_32_TO_PTR(id));

      silc_free(path);
      silc_free(target);
    }
    break;

  case SILC_SFTP_REALPATH:
    {
      SILC_LOG_DEBUG(("Realpath request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&path),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_REALPATH && server->monitor) {
	mdata.name = path;
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_REALPATH, &mdata,
			   server->monitor_context);
      }

      /* Realpath operation */
      server->fs->fs->sftp_realpath(server->fs->fs_context, sftp, path,
				    silc_sftp_server_name, SILC_32_TO_PTR(id));

      silc_free(path);
    }
    break;

  case SILC_SFTP_EXTENDED:
    {
      char *request = NULL;
      unsigned char *data;
      SilcUInt32 data_len;

      SILC_LOG_DEBUG(("Extended request"));

      ret = silc_buffer_unformat(&buf,
				 SILC_STR_UI_INT(&id),
				 SILC_STR_UI32_STRING_ALLOC(&request),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;

      data_len = 8 + strlen(request);
      silc_buffer_pull(&buf, data_len);
      ret = silc_buffer_unformat(&buf,
				 SILC_STR_DATA(&data, silc_buffer_len(&buf)),
				 SILC_STR_END);
      if (ret < 0)
	goto failure;
      data_len = silc_buffer_len(&buf);

      /* Call monitor */
      if (server->monitors & SILC_SFTP_MONITOR_EXTENDED && server->monitor) {
	(*server->monitor)(sftp, SILC_SFTP_MONITOR_EXTENDED, &mdata,
			   server->monitor_context);
      }

      /* Extended operation */
      server->fs->fs->sftp_extended(server->fs->fs_context, sftp,
				    request, data, data_len,
				    silc_sftp_server_extended,
				    SILC_32_TO_PTR(id));

      silc_free(request);
    }
    break;

  default:
    break;
  }

  return;

 failure:
  silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
}
static void silc_buffer_stream_io(SilcStream stream,
				  SilcStreamStatus status,
				  void *context)
{
  SilcBufferStream bs = context;
  SilcBuffer buffer = NULL;
  SilcUInt32 buf_len;
  int ret, len;

  if (bs->closed)
    return;

  if (status == SILC_STREAM_CAN_READ) {
    /* Read data */
    SILC_LOG_DEBUG(("Read data from buffer stream %p", bs));

    while ((ret = silc_stream_read(bs->stream, bs->inbuf->tail,
				   silc_buffer_taillen(bs->inbuf))) > 0) {
      if (!buffer) {
	buffer = silc_buffer_alloc(0);
	if (!buffer)
	  return;
      }

      silc_buffer_pull_tail(bs->inbuf, ret);

      /* Parse the buffer */
      while ((len = silc_buffer_unformat(bs->inbuf,
					 SILC_STR_BUFFER_ALLOC(buffer),
					 SILC_STR_END)) > 0) {
	/* Deliver the buffer */
	SILC_LOG_HEXDUMP(("Received buffer, size %d",
			  silc_buffer_len(buffer)),
			 silc_buffer_data(buffer), silc_buffer_len(buffer));
	bs->receiver(SILC_OK, (SilcStream)bs, buffer, bs->context);
	silc_buffer_pull(bs->inbuf, len);

	buffer = silc_buffer_alloc(0);
	if (!buffer)
	  return;
      }

      if (silc_buffer_len(bs->inbuf) > 0) {
	/* Not complete buffer, read more data */
	buf_len = 4;
	if (silc_buffer_len(bs->inbuf) >= 4) {
	  SILC_GET32_MSB(buf_len, bs->inbuf->data);
	  SILC_LOG_DEBUG(("Incomplete buffer, wait for rest, buffer size %d",
			  buf_len));
	}

	/* Enlarge inbuf if needed */
	if (silc_buffer_taillen(bs->inbuf) < buf_len)
	  silc_buffer_realloc(bs->inbuf, silc_buffer_truelen(bs->inbuf) +
			      buf_len);
	continue;
      }

      /* All data read, read more */
      silc_buffer_reset(bs->inbuf);
    }

    silc_buffer_free(buffer);

    if (ret == 0 || ret == -2) {
      bs->receiver(silc_errno, (SilcStream)bs, NULL, bs->context);
      return;
    }
  } else {
    /* Write any pending data */
    SILC_LOG_DEBUG(("Write pending data to buffer stream %p", bs));

    while (silc_buffer_len(&bs->queue) > 0) {
      ret = silc_stream_write(bs->stream, silc_buffer_data(&bs->queue),
			      silc_buffer_len(&bs->queue));
      if (silc_unlikely(ret == 0))
	return;

      if (silc_unlikely(ret == -2))
	return;

      if (silc_unlikely(ret == -1)) {
	SILC_LOG_DEBUG(("Buffer stream %p would block, send later", bs));
	return;
      }

      /* Wrote data */
      silc_buffer_pull(&bs->queue, ret);
    }

    memset(&bs->queue, 0, sizeof(bs->queue));
    silc_buffer_reset(bs->outbuf);
  }
}