/*
 * libEtPan! -- a mail stuff library
 *
 * Copyright (C) 2001, 2002 - DINH Viet Hoa
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the libEtPan! project nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * $Id$
 */

#include "imfcache.h"

#include <stdlib.h>
#include <string.h>

static int mailimf_cache_field_write(MMAPString * mmapstr, size_t * index,
				     struct mailimf_field * field);
static int mailimf_cache_orig_date_write(MMAPString * mmapstr, size_t * index,
					 struct mailimf_orig_date * date);
static int mailimf_cache_date_time_write(MMAPString * mmapstr, size_t * index,
					 struct mailimf_date_time * date_time);
static int mailimf_cache_from_write(MMAPString * mmapstr, size_t * index,
				    struct mailimf_from * from);
static int mailimf_cache_sender_write(MMAPString * mmapstr, size_t * index,
				      struct mailimf_sender * sender);
static int mailimf_cache_reply_to_write(MMAPString * mmapstr, size_t * index,
					struct mailimf_reply_to * reply_to);
static int mailimf_cache_to_write(MMAPString * mmapstr, size_t * index,
				  struct mailimf_to * to);
static int mailimf_cache_cc_write(MMAPString * mmapstr, size_t * index,
				  struct mailimf_cc * to);
static int mailimf_cache_bcc_write(MMAPString * mmapstr, size_t * index,
				   struct mailimf_bcc * to);
static int mailimf_cache_message_id_write(MMAPString * mmapstr, size_t * index,
					  struct mailimf_message_id * message_id);
static int mailimf_cache_msg_id_list_write(MMAPString * mmapstr, size_t * index,
					   clist * list);
static int mailimf_cache_in_reply_to_write(MMAPString * mmapstr, size_t * index,
					   struct mailimf_in_reply_to *
					   in_reply_to);
static int mailimf_cache_references_write(MMAPString * mmapstr, size_t * index,
					  struct mailimf_references * references);
static int mailimf_cache_subject_write(MMAPString * mmapstr, size_t * index,
				       struct mailimf_subject * subject);
static int mailimf_cache_address_list_write(MMAPString * mmapstr,
					    size_t * index,
					    struct mailimf_address_list *
					    addr_list);
static int mailimf_cache_address_write(MMAPString * mmapstr, size_t * index,
				       struct mailimf_address * addr);
static int mailimf_cache_group_write(MMAPString * mmapstr, size_t * index,
				     struct mailimf_group * group);
static int mailimf_cache_mailbox_list_write(MMAPString * mmapstr,
					    size_t * index,
					    struct mailimf_mailbox_list * mb_list);
static int mailimf_cache_mailbox_write(MMAPString * mmapstr, size_t * index,
				       struct mailimf_mailbox * mb);


static int mailimf_cache_field_read(MMAPString * mmapstr, size_t * index,
				    struct mailimf_field ** result);
static int mailimf_cache_orig_date_read(MMAPString * mmapstr, size_t * index,
					struct mailimf_orig_date ** result);
static int mailimf_cache_date_time_read(MMAPString * mmapstr, size_t * index,
					struct mailimf_date_time ** result);
static int mailimf_cache_from_read(MMAPString * mmapstr, size_t * index,
				   struct mailimf_from ** result);
static int mailimf_cache_sender_read(MMAPString * mmapstr, size_t * index,
				     struct mailimf_sender ** result);
static int mailimf_cache_reply_to_read(MMAPString * mmapstr, size_t * index,
				       struct mailimf_reply_to ** result);
static int mailimf_cache_to_read(MMAPString * mmapstr, size_t * index,
				 struct mailimf_to ** result);
static int mailimf_cache_cc_read(MMAPString * mmapstr, size_t * index,
				 struct mailimf_cc ** result);
static int mailimf_cache_bcc_read(MMAPString * mmapstr, size_t * index,
				  struct mailimf_bcc ** result);
static int mailimf_cache_message_id_read(MMAPString * mmapstr, size_t * index,
					 struct mailimf_message_id ** result);
static int mailimf_cache_msg_id_list_read(MMAPString * mmapstr, size_t * index,
					  clist ** result);
static int
mailimf_cache_in_reply_to_read(MMAPString * mmapstr, size_t * index,
			       struct mailimf_in_reply_to ** result);

static int mailimf_cache_references_read(MMAPString * mmapstr, size_t * index,
					 struct mailimf_references ** result);
static int mailimf_cache_subject_read(MMAPString * mmapstr, size_t * index,
				      struct mailimf_subject ** result);
static int mailimf_cache_address_list_read(MMAPString * mmapstr, size_t * index,
					   struct mailimf_address_list ** result);
static int mailimf_cache_address_read(MMAPString * mmapstr, size_t * index,
				      struct mailimf_address ** result);
static int mailimf_cache_group_read(MMAPString * mmapstr, size_t * index,
				    struct mailimf_group ** result);
static int
mailimf_cache_mailbox_list_read(MMAPString * mmapstr, size_t * index,
				struct mailimf_mailbox_list ** result);
static int mailimf_cache_mailbox_read(MMAPString * mmapstr, size_t * index,
				      struct mailimf_mailbox ** result);

enum {
  CACHE_NULL_POINTER = 0,
  CACHE_NOT_NULL     = 1,
};

int mail_serialize_clear(MMAPString * mmapstr, size_t * index)
{
  if (mmap_string_set_size(mmapstr, 0) == NULL)
    return MAIL_ERROR_MEMORY;

  * index = 0;

  return MAIL_NO_ERROR;
}

int mail_serialize_write(MMAPString * mmapstr, size_t * index,
			 char * buf, size_t size)
{
  if (mmap_string_append_len(mmapstr, buf, size) == NULL)
    return MAIL_ERROR_MEMORY;

  * index = * index + size;

  return MAIL_NO_ERROR;
}

int mail_serialize_read(MMAPString * mmapstr, size_t * index,
			char * buf, size_t size)
{
  size_t cur_token;
  
  cur_token = * index;

  if (cur_token + size > mmapstr->len)
    return MAIL_ERROR_STREAM;

  memcpy(buf, mmapstr->str + cur_token, size);
  * index = cur_token + size;

  return MAIL_NO_ERROR;
}

int mailimf_cache_int_write(MMAPString * mmapstr, size_t * index,
			    uint32_t value)
{
  unsigned char ch;
  int r;
  int i;

  for(i = 0 ; i < 4 ; i ++) {
    ch = value % 256;

    r = mail_serialize_write(mmapstr, index, &ch, 1);
    if (r != MAIL_NO_ERROR)
      return r;
    value /= 256;
  }

  return MAIL_NO_ERROR;
}

int mailimf_cache_int_read(MMAPString * mmapstr, size_t * index,
			   uint32_t * result)
{
  unsigned char ch;
  uint32_t value;
  int i;
  int r;
  
  value = 0;
  for(i = 0 ; i < 4 ; i ++) {
    r = mail_serialize_read(mmapstr, index, &ch, 1);
    if (r != MAIL_NO_ERROR)
      return r;
    value = value | ch << (i << 3);
  }
  
  * result = value;

  return MAIL_NO_ERROR;
}


int mailimf_cache_string_write(MMAPString * mmapstr, size_t * index,
			       char * str, size_t length)
{
  int r;

  if (str == NULL) {
    r = mailimf_cache_int_write(mmapstr, index, CACHE_NULL_POINTER);
    if (r != MAIL_NO_ERROR)
      return r;
  }
  else {
    r = mailimf_cache_int_write(mmapstr, index, CACHE_NOT_NULL);
    if (r != MAIL_NO_ERROR)
      return r;
    
    r = mailimf_cache_int_write(mmapstr, index, length);
    if (r != MAIL_NO_ERROR)
      return r;
    
    if (length != 0) {
      r = mail_serialize_write(mmapstr, index, str, length);
      if (r != MAIL_NO_ERROR)
        return MAIL_ERROR_FILE;
    }
  }

  return MAIL_NO_ERROR;
}

int mailimf_cache_string_read(MMAPString * mmapstr, size_t * index,
			      char ** result)
{
  int r;
  uint32_t length;
  char * str;
  uint32_t type;
  
  r = mailimf_cache_int_read(mmapstr, index, &type);
  if (r != MAIL_NO_ERROR)
    return r;

  if (type == CACHE_NULL_POINTER) {
    str = NULL;
  }
  else {
    r = mailimf_cache_int_read(mmapstr, index, &length);
    if (r != MAIL_NO_ERROR)
      return r;
    
    str = malloc(length + 1);
    if (str == NULL)
      return MAIL_ERROR_MEMORY;
    
    r = mail_serialize_read(mmapstr, index, str, length);
    if (r != MAIL_NO_ERROR)
      return MAIL_ERROR_FILE;
    
    str[length] = 0;
  }

  * result = str;

  return MAIL_NO_ERROR;
}

int mailimf_cache_fields_write(MMAPString * mmapstr, size_t * index,
			       struct mailimf_fields * fields)
{
  clistiter * cur;
  int r;

  r = mailimf_cache_int_write(mmapstr, index,
      clist_count(fields->fld_list));
  if (r != MAIL_NO_ERROR)
    return r;
  
  for(cur = clist_begin(fields->fld_list) ; cur != NULL ;
      cur = clist_next(cur)) {
    r = mailimf_cache_field_write(mmapstr, index, clist_content(cur));
    if (r != MAIL_NO_ERROR)
      return r;
  }

  return MAIL_NO_ERROR;
}

int mailimf_cache_fields_read(MMAPString * mmapstr, size_t * index,
			      struct mailimf_fields ** result)
{
  clist * list;
  int r;
  uint32_t count;
  uint32_t i;
  struct mailimf_fields * fields;
  int res;

  r = mailimf_cache_int_read(mmapstr, index, &count);
  if (r != MAIL_NO_ERROR) {
    res = r;
    goto err;
  }

  list = clist_new();
  if (list == NULL) {
    res = MAIL_ERROR_MEMORY;
    goto err;
  }

  for(i = 0 ; i < count ; i++) {
    struct mailimf_field * field;

    r = mailimf_cache_field_read(mmapstr, index, &field);
    if (r != MAIL_NO_ERROR) {
      res = r;
      goto free_list;
    }

    r = clist_append(list, field);
    if (r < 0) {
      mailimf_field_free(field);
      res = MAIL_ERROR_MEMORY;
      goto free_list;
    }
  }

  fields = mailimf_fields_new(list);
  if (fields == NULL) {
    res = MAIL_ERROR_MEMORY;
    goto free_list;
  }
  
  * result = fields;

  return MAIL_NO_ERROR;

 free_list:
  clist_foreach(list, (clist_func) mailimf_field_free, NULL);
  clist_free(list);
 err:
  return res;
}


static int mailimf_cache_field_write(MMAPString * mmapstr, size_t * index,
				     struct mailimf_field * field)
{
  int r;
  
  r = mailimf_cache_int_write(mmapstr, index, field->fld_type);
  if (r != MAIL_NO_ERROR)
    return r;
  
  switch (field->fld_type) {
  case MAILIMF_FIELD_ORIG_DATE:
    r = mailimf_cache_orig_date_write(mmapstr, index,
        field->fld_data.fld_orig_date);
    break;
  case MAILIMF_FIELD_FROM:
    r = mailimf_cache_from_write(mmapstr, index,
        field->fld_data.fld_from);
    break;
  case MAILIMF_FIELD_SENDER:
    r = mailimf_cache_sender_write(mmapstr, index,
        field->fld_data.fld_sender);
    break;
  case MAILIMF_FIELD_REPLY_TO:
    r = mailimf_cache_reply_to_write(mmapstr, index,
        field->fld_data.fld_reply_to);
    break;
  case MAILIMF_FIELD_TO:
    r = mailimf_cache_to_write(mmapstr, index,
        field->fld_data.fld_to);
    break;
  case MAILIMF_FIELD_CC:
    r = mailimf_cache_cc_write(mmapstr, index,
        field->fld_data.fld_cc);
    break;
  case MAILIMF_FIELD_BCC:
    r = mailimf_cache_bcc_write(mmapstr, index,
        field->fld_data.fld_bcc);
    break;
  case MAILIMF_FIELD_MESSAGE_ID:
    r = mailimf_cache_message_id_write(mmapstr, index,
        field->fld_data.fld_message_id);
    break;
  case MAILIMF_FIELD_IN_REPLY_TO:
    r = mailimf_cache_in_reply_to_write(mmapstr, index,
        field->fld_data.fld_in_reply_to);
    break;
  case MAILIMF_FIELD_REFERENCES:
    r = mailimf_cache_references_write(mmapstr, index,
        field->fld_data.fld_references);
    break;
  case MAILIMF_FIELD_SUBJECT:
    r = mailimf_cache_subject_write(mmapstr, index,
        field->fld_data.fld_subject);
    break;
  default:
    r = 0;
    break;
  }

  if (r != MAIL_NO_ERROR)
    return r;

  return MAIL_NO_ERROR;
}


static int mailimf_cache_field_read(MMAPString * mmapstr, size_t * index,
				    struct mailimf_field ** result)
{
  int r;
  uint32_t type;
  struct mailimf_orig_date * orig_date;
  struct mailimf_from * from;
  struct mailimf_sender * sender;
  struct mailimf_to * to;
  struct mailimf_reply_to * reply_to;
  struct mailimf_cc * cc;
  struct mailimf_bcc * bcc;
  struct mailimf_message_id * message_id;
  struct mailimf_in_reply_to * in_reply_to;
  struct mailimf_references * references;
  struct mailimf_subject * subject;
  struct mailimf_field * field;
  int res;

  orig_date = NULL;
  from = NULL;
  sender = NULL;
  to = NULL;
  reply_to = NULL;
  cc = NULL;
  bcc = NULL;
  message_id = NULL;
  in_reply_to = NULL;
  references = NULL;
  subject = NULL;
  field = NULL;

  r = mailimf_cache_int_read(mmapstr, index, &type);
  if (r != MAIL_NO_ERROR) {
    res = r;
    goto err;
  }

  switch (type) {
  case MAILIMF_FIELD_ORIG_DATE:
    r = mailimf_cache_orig_date_read(mmapstr, index, &orig_date);
    break;
  case MAILIMF_FIELD_FROM:
    r = mailimf_cache_from_read(mmapstr, index, &from);
    break;
  case MAILIMF_FIELD_SENDER:
    r = mailimf_cache_sender_read(mmapstr, index, &sender);
    break;
  case MAILIMF_FIELD_REPLY_TO:
    r = mailimf_cache_reply_to_read(mmapstr, index, &reply_to);
    break;
  case MAILIMF_FIELD_TO:
    r = mailimf_cache_to_read(mmapstr, index, &to);
    break;
  case MAILIMF_FIELD_CC:
    r = mailimf_cache_cc_read(mmapstr, index, &cc);
    break;
  case MAILIMF_FIELD_BCC:
    r = mailimf_cache_bcc_read(mmapstr, index, &bcc);
    break;
  case MAILIMF_FIELD_MESSAGE_ID:
    r = mailimf_cache_message_id_read(mmapstr, index, &message_id);
    break;
  case MAILIMF_FIELD_IN_REPLY_TO:
    r = mailimf_cache_in_reply_to_read(mmapstr, index, &in_reply_to);
    break;
  case MAILIMF_FIELD_REFERENCES:
    r = mailimf_cache_references_read(mmapstr, index, &references);
    break;
  case MAILIMF_FIELD_SUBJECT:
    r = mailimf_cache_subject_read(mmapstr, index, &subject);
    break;
  default:
    r = MAIL_ERROR_INVAL;
    break;
  }

  if (r != MAIL_NO_ERROR) {
    res = r;
    goto free;
  }

  field = mailimf_field_new(type, NULL, NULL, NULL, NULL, NULL,
      NULL, NULL, NULL, orig_date, from, sender, reply_to,
      to, cc, bcc, message_id,
      in_reply_to, references,
      subject, NULL, NULL, NULL);
  if (field == NULL) {
    res = MAIL_ERROR_MEMORY;
    goto free;
  }

  * result = field;

  return MAIL_NO_ERROR;

 free:
  if (orig_date != NULL)
    mailimf_orig_date_free(orig_date);
  if (from != NULL)
    mailimf_from_free(from);
  if (sender != NULL)
    mailimf_sender_free(sender);
  if (reply_to != NULL)
    mailimf_reply_to_free(reply_to);
  if (to != NULL)
    mailimf_to_free(to);
  if (cc != NULL)
    mailimf_cc_free(cc);
  if (bcc != NULL)
    mailimf_bcc_free(bcc);
  if (message_id != NULL)
    mailimf_message_id_free(message_id);
  if (in_reply_to != NULL)
    mailimf_in_reply_to_free(in_reply_to);
  if (references != NULL)
    mailimf_references_free(references);
  if (subject != NULL)
    mailimf_subject_free(subject);
 err:
  return res;
}

static int mailimf_cache_orig_date_write(MMAPString * mmapstr, size_t * index,
					 struct mailimf_orig_date * date)
{
  return mailimf_cache_date_time_write(mmapstr, index, date->dt_date_time);
}

static int mailimf_cache_orig_date_read(MMAPString * mmapstr, size_t * index,
					struct mailimf_orig_date ** result)
{
  int r;
  struct mailimf_date_time * date_time;
  struct mailimf_orig_date * orig_date;

  r = mailimf_cache_date_time_read(mmapstr, index, &date_time);
  if (r != MAIL_NO_ERROR)
    return r;

  orig_date = mailimf_orig_date_new(date_time);
  if (orig_date == NULL) {
    mailimf_date_time_free(date_time);
    return MAIL_ERROR_MEMORY;
  }

  * result = orig_date;

  return MAIL_NO_ERROR;
}

static int mailimf_cache_date_time_write(MMAPString * mmapstr, size_t * index,
					 struct mailimf_date_time * date_time)
{
  int r;

  r = mailimf_cache_int_write(mmapstr, index, date_time->dt_day);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_int_write(mmapstr, index, date_time->dt_month);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_int_write(mmapstr, index, date_time->dt_year);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_int_write(mmapstr, index, date_time->dt_hour);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_int_write(mmapstr, index, date_time->dt_min);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_int_write(mmapstr, index, date_time->dt_sec);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_int_write(mmapstr, index, date_time->dt_zone);
  if (r != MAIL_NO_ERROR)
    return r;

  return MAIL_NO_ERROR;
}

static int mailimf_cache_date_time_read(MMAPString * mmapstr, size_t * index,
					struct mailimf_date_time ** result)
{
  int r;
  uint32_t day;
  uint32_t month;
  uint32_t year;
  uint32_t hour;
  uint32_t min;
  uint32_t sec;
  uint32_t zone;
  struct mailimf_date_time * date_time;

  r = mailimf_cache_int_read(mmapstr, index, &day);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_int_read(mmapstr, index, &month);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_int_read(mmapstr, index, &year);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_int_read(mmapstr, index, &hour);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_int_read(mmapstr, index, &min);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_int_read(mmapstr, index, &sec);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_int_read(mmapstr, index, &zone);
  if (r != MAIL_NO_ERROR)
    return r;

  date_time = mailimf_date_time_new(day, month, year, hour, min, sec, zone);
  if (date_time == NULL)
    return MAIL_ERROR_MEMORY;

  * result = date_time;

  return MAIL_NO_ERROR;
  
}


static int mailimf_cache_from_write(MMAPString * mmapstr, size_t * index,
				    struct mailimf_from * from)
{
  return mailimf_cache_mailbox_list_write(mmapstr, index, from->frm_mb_list);
}

static int mailimf_cache_from_read(MMAPString * mmapstr, size_t * index,
				   struct mailimf_from ** result)
{
  struct mailimf_mailbox_list * mb_list;
  struct mailimf_from * from;
  int r;
  
  r = mailimf_cache_mailbox_list_read(mmapstr, index, &mb_list);
  if (r != MAIL_NO_ERROR)
    return r;

  from = mailimf_from_new(mb_list);
  if (from == NULL) {
    mailimf_mailbox_list_free(mb_list);
    return MAIL_ERROR_MEMORY;
  }

  * result = from;

  return MAIL_NO_ERROR;
}

static int mailimf_cache_sender_write(MMAPString * mmapstr, size_t * index,
				      struct mailimf_sender * sender)
{
  return mailimf_cache_mailbox_write(mmapstr, index, sender->snd_mb);
}

static int mailimf_cache_sender_read(MMAPString * mmapstr, size_t * index,
				     struct mailimf_sender ** result)
{
  int r;
  struct mailimf_mailbox * mb;
  struct mailimf_sender * sender;

  r = mailimf_cache_mailbox_read(mmapstr, index, &mb);
  if (r != MAIL_NO_ERROR)
    return r;

  sender = mailimf_sender_new(mb);
  if (sender == NULL) {
    mailimf_mailbox_free(mb);
    return MAIL_ERROR_MEMORY;
  }

  * result = sender;

  return MAIL_NO_ERROR;
}

static int mailimf_cache_reply_to_write(MMAPString * mmapstr, size_t * index,
					struct mailimf_reply_to * reply_to)
{
  return mailimf_cache_address_list_write(mmapstr, index,
      reply_to->rt_addr_list);
}

static int mailimf_cache_reply_to_read(MMAPString * mmapstr, size_t * index,
				       struct mailimf_reply_to ** result)
{
  int r;
  struct mailimf_address_list * addr_list;
  struct mailimf_reply_to * reply_to;

  r = mailimf_cache_address_list_read(mmapstr, index, &addr_list);
  if (r != MAIL_NO_ERROR)
    return r;

  reply_to = mailimf_reply_to_new(addr_list);
  if (reply_to == NULL) {
    mailimf_address_list_free(addr_list);
    return MAIL_ERROR_MEMORY;
  }

  * result = reply_to;

  return MAIL_NO_ERROR;
}

static int mailimf_cache_to_write(MMAPString * mmapstr, size_t * index,
				  struct mailimf_to * to)
{
  return mailimf_cache_address_list_write(mmapstr, index, to->to_addr_list);
}

static int mailimf_cache_to_read(MMAPString * mmapstr, size_t * index,
				 struct mailimf_to ** result)
{
  int r;
  struct mailimf_address_list * addr_list;
  struct mailimf_to * to;

  r = mailimf_cache_address_list_read(mmapstr, index, &addr_list);
  if (r != MAIL_NO_ERROR)
    return r;

  to = mailimf_to_new(addr_list);
  if (to == NULL) {
    mailimf_address_list_free(addr_list);
    return MAIL_ERROR_MEMORY;
  }

  * result = to;

  return MAIL_NO_ERROR;
}

static int mailimf_cache_cc_write(MMAPString * mmapstr, size_t * index,
				  struct mailimf_cc * cc)
{
  return mailimf_cache_address_list_write(mmapstr, index, cc->cc_addr_list);
}

static int mailimf_cache_cc_read(MMAPString * mmapstr, size_t * index,
    struct mailimf_cc ** result)
{
  int r;
  struct mailimf_address_list * addr_list;
  struct mailimf_cc * cc;

  r = mailimf_cache_address_list_read(mmapstr, index, &addr_list);
  if (r != MAIL_NO_ERROR)
    return r;

  cc = mailimf_cc_new(addr_list);
  if (cc == NULL) {
    mailimf_address_list_free(addr_list);
    return MAIL_ERROR_MEMORY;
  }

  * result = cc;

  return MAIL_NO_ERROR;
}

static int mailimf_cache_bcc_write(MMAPString * mmapstr, size_t * index,
				   struct mailimf_bcc * bcc)
{
  return mailimf_cache_address_list_write(mmapstr, index, bcc->bcc_addr_list);
}

static int mailimf_cache_bcc_read(MMAPString * mmapstr, size_t * index,
				  struct mailimf_bcc ** result)
{
  int r;
  struct mailimf_address_list * addr_list;
  struct mailimf_bcc * bcc;

  r = mailimf_cache_address_list_read(mmapstr, index, &addr_list);
  if (r != MAIL_NO_ERROR)
    return r;

  bcc = mailimf_bcc_new(addr_list);
  if (bcc == NULL) {
    mailimf_address_list_free(addr_list);
    return MAIL_ERROR_MEMORY;
  }

  * result = bcc;

  return MAIL_NO_ERROR;
}

static int
mailimf_cache_message_id_write(MMAPString * mmapstr, size_t * index,
			       struct mailimf_message_id * message_id)
{
  return mailimf_cache_string_write(mmapstr, index,
      message_id->mid_value, strlen(message_id->mid_value));
}

static int mailimf_cache_message_id_read(MMAPString * mmapstr, size_t * index,
					 struct mailimf_message_id ** result)
{
  struct mailimf_message_id * message_id;
  char * str;
  int r;

  r = mailimf_cache_string_read(mmapstr, index, &str);
  if (r != MAIL_NO_ERROR)
    return r;

  message_id = mailimf_message_id_new(str);
  if (message_id == NULL) {
    free(str);
    return MAIL_ERROR_MEMORY;
  }

  * result = message_id;
  
  return MAIL_NO_ERROR;
}

static int
mailimf_cache_msg_id_list_write(MMAPString * mmapstr, size_t * index,
				clist * list)
{
  clistiter * cur;
  int r;

  r = mailimf_cache_int_write(mmapstr, index, clist_count(list));
  if (r != MAIL_NO_ERROR)
    return r;
  
  for(cur = clist_begin(list) ; cur != NULL ; cur = clist_next(cur)) {
    char * msgid;

    msgid = clist_content(cur);

    r = mailimf_cache_string_write(mmapstr, index, msgid, strlen(msgid));
    if (r != MAIL_NO_ERROR)
      return r;
  }

  return MAIL_NO_ERROR;
}

static int mailimf_cache_msg_id_list_read(MMAPString * mmapstr, size_t * index,
					  clist ** result)
{
  clist * list;
  int r;
  uint32_t count;
  uint32_t i;
  int res;

  r = mailimf_cache_int_read(mmapstr, index, &count);
  if (r != MAIL_NO_ERROR) {
    res = r;
    goto err;
  }
  
  list = clist_new();
  if (list == NULL) {
    res = MAIL_ERROR_MEMORY;
    goto err;
  }

  for(i = 0 ; i < count ; i++) {
    char * msgid;
    
    r = mailimf_cache_string_read(mmapstr, index, &msgid);
    if (r != MAIL_NO_ERROR) {
      res = r;
      goto err;
    }

    r = clist_append(list, msgid);
    if (r < 0) {
      free(msgid);
      res = MAIL_ERROR_MEMORY;
      goto free_list;
    }
  }

  * result = list;

  return MAIL_NO_ERROR;

 free_list:
  clist_foreach(list, (clist_func) free, NULL);
  clist_free(list);
 err:
  return res;
}

static int
mailimf_cache_in_reply_to_write(MMAPString * mmapstr, size_t * index,
				struct mailimf_in_reply_to * in_reply_to)
{
  return mailimf_cache_msg_id_list_write(mmapstr, index,
      in_reply_to->mid_list);
}

static int mailimf_cache_in_reply_to_read(MMAPString * mmapstr, size_t * index,
					  struct mailimf_in_reply_to ** result)
{
  int r;
  clist * msg_id_list;
  struct mailimf_in_reply_to * in_reply_to;

  r = mailimf_cache_msg_id_list_read(mmapstr, index, &msg_id_list);
  if (r != MAIL_NO_ERROR)
    return r;

  in_reply_to = mailimf_in_reply_to_new(msg_id_list);
  if (in_reply_to == NULL) {
    clist_foreach(msg_id_list, (clist_func) free, NULL);
    clist_free(msg_id_list);
    return MAIL_ERROR_MEMORY;
  }

  * result = in_reply_to;

  return MAIL_NO_ERROR;
}

static int mailimf_cache_references_write(MMAPString * mmapstr, size_t * index,
					  struct mailimf_references * references)
{
  return mailimf_cache_msg_id_list_write(mmapstr, index,
      references->mid_list);
}

static int mailimf_cache_references_read(MMAPString * mmapstr, size_t * index,
					 struct mailimf_references ** result)
{
  int r;
  clist * msg_id_list;
  struct mailimf_references * references;

  r = mailimf_cache_msg_id_list_read(mmapstr, index, &msg_id_list);
  if (r != MAIL_NO_ERROR)
    return r;

  references = mailimf_references_new(msg_id_list);
  if (references == NULL) {
    clist_foreach(msg_id_list, (clist_func) free, NULL);
    clist_free(msg_id_list);
    return MAIL_ERROR_MEMORY;
  }

  * result = references;

  return MAIL_NO_ERROR;
}


static int mailimf_cache_subject_write(MMAPString * mmapstr, size_t * index,
				       struct mailimf_subject * subject)
{
  return mailimf_cache_string_write(mmapstr, index,
      subject->sbj_value, strlen(subject->sbj_value));
}

static int mailimf_cache_subject_read(MMAPString * mmapstr, size_t * index,
				      struct mailimf_subject ** result)
{
  char * str;
  struct mailimf_subject * subject;
  int r;

  r = mailimf_cache_string_read(mmapstr, index, &str);
  if (r != MAIL_NO_ERROR)
    return r;

  if (str == NULL) {
    str = strdup("");
    if (str == NULL)
      return MAIL_ERROR_MEMORY;
  }

  subject = mailimf_subject_new(str);
  if (subject == NULL) {
    free(str);
    return MAIL_ERROR_MEMORY;
  }

  * result = subject;

  return MAIL_NO_ERROR;
}


static int
mailimf_cache_address_list_write(MMAPString * mmapstr, size_t * index,
				 struct mailimf_address_list * addr_list)
{
  clistiter * cur;
  int r;

  if (addr_list == NULL) {
    r = mailimf_cache_int_write(mmapstr, index, CACHE_NULL_POINTER);
    if (r != MAIL_NO_ERROR)
      return r;
  }
  else {
    r = mailimf_cache_int_write(mmapstr, index, CACHE_NOT_NULL);
    if (r != MAIL_NO_ERROR)
      return r;
    
    r = mailimf_cache_int_write(mmapstr, index,
        clist_count(addr_list->ad_list));
    if (r != MAIL_NO_ERROR)
      return r;
    
    for(cur = clist_begin(addr_list->ad_list) ; cur != NULL ;
        cur = clist_next(cur)) {
      struct mailimf_address * addr;
      
      addr = clist_content(cur);
      
      r = mailimf_cache_address_write(mmapstr, index, addr);
      if (r != MAIL_NO_ERROR)
	return r;
    }
  }

  return MAIL_NO_ERROR;
}

static int
mailimf_cache_address_list_read(MMAPString * mmapstr, size_t * index,
				struct mailimf_address_list ** result)
{
  struct mailimf_address_list * addr_list;
  uint32_t count;
  uint32_t i;
  int r;
  clist * list;
  int res;
  uint32_t type;
  
  r = mailimf_cache_int_read(mmapstr, index, &type);
  if (r != MAIL_NO_ERROR) {
    res = r;
    goto err;
  }
  
  if (type == CACHE_NULL_POINTER) {
    * result = NULL;
    return MAIL_NO_ERROR;
  }
  
  r = mailimf_cache_int_read(mmapstr, index, &count);
  if (r != MAIL_NO_ERROR) {
    res = r;
    goto err;
  }

  list = clist_new();
  if (list == NULL) {
    res = MAIL_ERROR_MEMORY;
    goto err;
  }

  for(i = 0 ; i < count ; i++) {
    struct mailimf_address * addr;
    
    r = mailimf_cache_address_read(mmapstr, index, &addr);
    if (r != MAIL_NO_ERROR) {
      res = r;
      goto free_list;
    }

    r = clist_append(list, addr);
    if (r < 0) {
      mailimf_address_free(addr);
      res = MAIL_ERROR_MEMORY;
      goto free_list;
    }
  }

  addr_list = mailimf_address_list_new(list);
  if (addr_list == NULL) {
    res = MAIL_ERROR_MEMORY;
    goto free_list;
  }

  * result = addr_list;

  return MAIL_NO_ERROR;

 free_list:
  clist_foreach(list, (clist_func) mailimf_address_free, NULL);
  clist_free(list);
 err:
  return res;
}

static int mailimf_cache_address_write(MMAPString * mmapstr, size_t * index,
				       struct mailimf_address * addr)
{
  int r;

  r = mailimf_cache_int_write(mmapstr, index, addr->ad_type);
  if (r != MAIL_NO_ERROR)
    return r;
  
  switch(addr->ad_type) {
  case MAILIMF_ADDRESS_MAILBOX:
    r = mailimf_cache_mailbox_write(mmapstr, index, addr->ad_data.ad_mailbox);
    if (r != MAIL_NO_ERROR)
      return r;

    break;

  case MAILIMF_ADDRESS_GROUP:
    r = mailimf_cache_group_write(mmapstr, index, addr->ad_data.ad_group);
    if (r != MAIL_NO_ERROR)
      return r;
    
    break;
  }

  return MAIL_NO_ERROR;
}

static int mailimf_cache_address_read(MMAPString * mmapstr, size_t * index,
				      struct mailimf_address ** result)
{
  uint32_t type;
  int r;
  struct mailimf_mailbox * mailbox;
  struct mailimf_group * group;
  struct mailimf_address * addr;

  r = mailimf_cache_int_read(mmapstr, index, &type);
  if (r != MAIL_NO_ERROR)
    return r;
  
  mailbox = NULL;
  group = NULL;

  switch (type) {
  case MAILIMF_ADDRESS_MAILBOX:
    r = mailimf_cache_mailbox_read(mmapstr, index, &mailbox);
    if (r != MAIL_NO_ERROR)
      return r;

    break;

  case MAILIMF_ADDRESS_GROUP:
    r = mailimf_cache_group_read(mmapstr, index, &group);
    if (r != MAIL_NO_ERROR)
      return r;
    
    break;
  }

  addr = mailimf_address_new(type, mailbox, group);
  if (addr == NULL)
    goto free;

  * result = addr;

  return MAIL_NO_ERROR;

 free:
  if (mailbox != NULL)
    mailimf_mailbox_free(mailbox);
  if (group != NULL)
    mailimf_group_free(group);
  return MAIL_ERROR_MEMORY;
}

static int mailimf_cache_group_write(MMAPString * mmapstr, size_t * index,
				     struct mailimf_group * group)
{
  int r;

  r = mailimf_cache_string_write(mmapstr, index, group->grp_display_name,
			   strlen(group->grp_display_name));
  if (r != MAIL_NO_ERROR)
    return r;
  
  r = mailimf_cache_mailbox_list_write(mmapstr, index, group->grp_mb_list);
  if (r != MAIL_NO_ERROR)
    return r;
  
  return MAIL_NO_ERROR;
}

static int mailimf_cache_group_read(MMAPString * mmapstr, size_t * index,
				    struct mailimf_group ** result)
{
  int r;
  char * display_name;
  struct mailimf_mailbox_list * mb_list;
  struct mailimf_group * group;
  int res;

  r = mailimf_cache_string_read(mmapstr, index, &display_name);
  if (r != MAIL_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimf_cache_mailbox_list_read(mmapstr, index, &mb_list);
  if (r != MAIL_NO_ERROR) {
    res = r;
    goto free_dsp_name;
  }

  group = mailimf_group_new(display_name, mb_list);
  if (group == NULL) {
    res = MAIL_ERROR_MEMORY;
    goto free_mb_list;
  }

  * result = group;

  return MAIL_NO_ERROR;

 free_mb_list:
  mailimf_mailbox_list_free(mb_list);
 free_dsp_name:
  free(display_name);
 err:
  return res;
}

static int
mailimf_cache_mailbox_list_write(MMAPString * mmapstr, size_t * index,
				 struct mailimf_mailbox_list * mb_list)
{
  clistiter * cur;
  int r;

  if (mb_list == NULL) {
    r = mailimf_cache_int_write(mmapstr, index, CACHE_NULL_POINTER);
    if (r != MAIL_NO_ERROR)
      return r;
  }
  else {
    r = mailimf_cache_int_write(mmapstr, index, CACHE_NOT_NULL);
    if (r != MAIL_NO_ERROR)
      return r;
    
    r = mailimf_cache_int_write(mmapstr, index,
        clist_count(mb_list->mb_list));
    if (r != MAIL_NO_ERROR)
      return r;
    
    for(cur = clist_begin(mb_list->mb_list) ; cur != NULL ;
        cur = clist_next(cur)) {
      struct mailimf_mailbox * mb;
      
      mb = clist_content(cur);
      
      r = mailimf_cache_mailbox_write(mmapstr, index, mb);
      if (r != MAIL_NO_ERROR)
        return r;
    }
  }

  return MAIL_NO_ERROR;
}

static int
mailimf_cache_mailbox_list_read(MMAPString * mmapstr, size_t * index,
				struct mailimf_mailbox_list ** result)
{
  clist * list;
  int r;
  uint32_t count;
  uint32_t i;
  struct mailimf_mailbox_list * mb_list;
  int res;
  uint32_t type;
  
  r = mailimf_cache_int_read(mmapstr, index, &type);
  if (r != MAIL_NO_ERROR) {
    res = r;
    goto err;
  }
  
  if (type == CACHE_NULL_POINTER) {
    * result = NULL;
    return MAIL_NO_ERROR;
  }
  
  r = mailimf_cache_int_read(mmapstr, index, &count);
  if (r != MAIL_NO_ERROR) {
    res = r;
    goto err;
  }

  list = clist_new();
  if (list == NULL) {
    res = MAIL_ERROR_MEMORY;
    goto err;
  }

  for(i = 0 ; i < count ; i++) {
    struct mailimf_mailbox * mb;
    
    r = mailimf_cache_mailbox_read(mmapstr, index, &mb);
    if (r != MAIL_NO_ERROR) {
      res = r;
      goto free_list;
    }

    r = clist_append(list, mb);
    if (r < 0) {
      mailimf_mailbox_free(mb);
      res = MAIL_ERROR_MEMORY;
      goto free_list;
    }
  }

  mb_list = mailimf_mailbox_list_new(list);
  if (mb_list == NULL) {
    res = MAIL_ERROR_MEMORY;
    goto free_list;
  }

  * result = mb_list;

  return MAIL_NO_ERROR;

 free_list:
  clist_foreach(list, (clist_func) mailimf_mailbox_free, NULL);
  clist_free(list);
 err:
  return res;
}

static int mailimf_cache_mailbox_write(MMAPString * mmapstr, size_t * index,
				       struct mailimf_mailbox * mb)
{
  int r;

  if (mb->mb_display_name) {
    r = mailimf_cache_string_write(mmapstr, index, 
        mb->mb_display_name, strlen(mb->mb_display_name));
    if (r != MAIL_NO_ERROR)
      return r;
  }
  else {
    r = mailimf_cache_string_write(mmapstr, index, NULL, 0);
    if (r != MAIL_NO_ERROR)
      return r;
  }

  r = mailimf_cache_string_write(mmapstr, index,
      mb->mb_addr_spec, strlen(mb->mb_addr_spec));
  if (r != MAIL_NO_ERROR)
    return r;

  return MAIL_NO_ERROR;
}

static int mailimf_cache_mailbox_read(MMAPString * mmapstr, size_t * index,
				      struct mailimf_mailbox ** result)
{
  int r;
  char * dsp_name;
  char * addr_spec;
  struct mailimf_mailbox * mb;

  dsp_name = NULL;

  r = mailimf_cache_string_read(mmapstr, index, &dsp_name);
  if (r != MAIL_NO_ERROR)
    return r;

  r = mailimf_cache_string_read(mmapstr, index, &addr_spec);
  if (r != MAIL_NO_ERROR)
    goto free_dsp_name;

  mb = mailimf_mailbox_new(dsp_name, addr_spec);
  if (mb == NULL)
    goto free_addr;

  * result = mb;

  return MAIL_NO_ERROR;

 free_addr:
  free(addr_spec);
 free_dsp_name:
  if (dsp_name != NULL)
    free(dsp_name);
  return MAIL_ERROR_MEMORY;
}