/*

  silcprivate.c

  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>

  Copyright (C) 1997 - 2001 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; either version 2 of the License, or
  (at your option) any later version.
  
  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.

*/
/* Includes the Private Message Payload implementation */
/* $Id: silcprivate.c,v 1.10 2001/11/26 19:05:58 priikone Exp $ */

#include "silcincludes.h"
#include "silcprivate.h"

/******************************************************************************

                           Private Message Payload

******************************************************************************/

#define SILC_PRIVATE_MESSAGE_PAD(__payloadlen) (16 - (__payloadlen) % 16)

/* Private Message Payload structure. Contents of this structure is parsed
   from SILC packets. */
struct SilcPrivateMessagePayloadStruct {
  uint16 flags;
  uint16 message_len;
  unsigned char *message;
};

/* Parses private message payload returning new private mesage payload 
   structure. This also decrypts the message if the `cipher' is provided. */

SilcPrivateMessagePayload 
silc_private_message_payload_parse(unsigned char *payload,
				   uint32 payload_len,
				   SilcCipher cipher)
{
  SilcBufferStruct buffer;
  SilcPrivateMessagePayload new;
  int ret;

  SILC_LOG_DEBUG(("Parsing private message payload"));

  silc_buffer_set(&buffer, payload, payload_len);

  /* Decrypt the payload */
  if (cipher)
    silc_cipher_decrypt(cipher, buffer.data, buffer.data, 
			buffer.len, cipher->iv);

  new = silc_calloc(1, sizeof(*new));

  /* Parse the Private Message Payload. Ignore the padding. */
  ret = silc_buffer_unformat(&buffer,
			     SILC_STR_UI_SHORT(&new->flags),
			     SILC_STR_UI16_NSTRING_ALLOC(&new->message, 
							 &new->message_len),
			     SILC_STR_END);
  if (ret == -1) {
    SILC_LOG_DEBUG(("Incorrect private message payload"));
    goto err;
  }

  if ((new->message_len < 1 || new->message_len > buffer.len)) {
    SILC_LOG_DEBUG(("Incorrect private message payload in packet, "
		    "packet dropped"));
    goto err;
  }

  return new;

 err:
  silc_private_message_payload_free(new);
  return NULL;
}

/* Encodes private message payload into a buffer and returns it.  If
   the cipher is provided the packet is also encrypted here.  It is provided
   if the private message private keys are used. */

SilcBuffer silc_private_message_payload_encode(uint16 flags,
					       uint16 data_len,
					       const unsigned char *data,
					       SilcCipher cipher)
{
  int i;
  SilcBuffer buffer;
  uint32 len, pad_len = 0;
  unsigned char pad[16];

  SILC_LOG_DEBUG(("Encoding private message payload"));

  len = 4 + data_len;

  if (cipher) {
    /* Calculate length of padding. */
    pad_len = SILC_PRIVATE_MESSAGE_PAD(len);
    len += pad_len;

    /* Generate padding */
    for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte();
  }

  /* Allocate private message payload buffer */
  buffer = silc_buffer_alloc(len);

  /* Encode the Channel Message Payload */
  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
  silc_buffer_format(buffer, 
		     SILC_STR_UI_SHORT(flags),
		     SILC_STR_UI_SHORT(data_len),
		     SILC_STR_UI_XNSTRING(data, data_len),
		     SILC_STR_UI_XNSTRING(pad, pad_len),
		     SILC_STR_END);

  if (cipher) {
    /* Encrypt payload of the packet. */
    silc_cipher_encrypt(cipher, buffer->data, buffer->data, 
			buffer->len, cipher->iv);
    memset(pad, 0, sizeof(pad));
  }

  return buffer;
}

/* Frees Private Message Payload */

void silc_private_message_payload_free(SilcPrivateMessagePayload payload)
{
  if (payload->message) {
    memset(payload->message, 0, payload->message_len);
    silc_free(payload->message);
  }
  silc_free(payload);
}

/* Return flags */

uint16 
silc_private_message_get_flags(SilcPrivateMessagePayload payload)
{
  return payload->flags;
}

/* Return message */

unsigned char *
silc_private_message_get_message(SilcPrivateMessagePayload payload,
				 uint32 *message_len)
{
  if (message_len)
    *message_len = payload->message_len;

  return payload->message;
}
