/*
 * libEtPan! -- a mail stuff library
 *
 * Copyright (C) 2001, 2005 - 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 AUTHORS 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 AUTHORS 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.
 */

#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "acl_parser.h"
#include "mailimap_keywords.h"
#include "mailimap_extension.h"
#include "acl.h"

#include <stdlib.h>

int
mailimap_acl_acl_data_parse(mailstream * fd, MMAPString *buffer,
    size_t * indx,
    struct mailimap_acl_acl_data ** result,
    size_t progr_rate,
    progress_function * progr_fun)
{
  size_t cur_token;
  char * mailbox;
  clist * ir_list;
  struct mailimap_acl_acl_data * acl_data;
  int r;
  int res;

  cur_token = * indx;

  mailbox = NULL; /* XXX - removes a gcc warning */

  r = mailimap_token_case_insensitive_parse(fd, buffer,
					    &cur_token, "ACL");
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_mailbox_parse(fd, buffer, &cur_token, &mailbox,
          progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto mailbox_free;
  }

  r = mailimap_struct_spaced_list_parse(fd, buffer,
        &cur_token, &ir_list,
        (mailimap_struct_parser * )
        mailimap_acl_identifier_rights_parse,
        (mailimap_struct_destructor * )
        mailimap_acl_identifier_rights_free,
        progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto mailbox_free;
  }

  acl_data = mailimap_acl_acl_data_new(mailbox,
      ir_list);
  if (acl_data == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto ir_list_free;
  }

  * result = acl_data;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 ir_list_free:
  if (ir_list != NULL) {
    clist_foreach(ir_list,
      (clist_func) mailimap_acl_identifier_rights_free, NULL);
    clist_free(ir_list);
  }
 mailbox_free:
  mailimap_mailbox_free(mailbox);
 err:
  return res;
}

int
mailimap_acl_listrights_data_parse(mailstream * fd, MMAPString *buffer,
    size_t * indx,
    struct mailimap_acl_listrights_data ** result,
    size_t progr_rate,
    progress_function * progr_fun)
{
  size_t cur_token;
  char * mailbox;
  char * identifier;
  clist * rights_list;
  struct mailimap_acl_listrights_data * lr_data;
  int r;
  int res;

  cur_token = * indx;

  mailbox = NULL; /* XXX - removes a gcc warning */
  identifier = NULL;

  r = mailimap_token_case_insensitive_parse(fd, buffer,
					    &cur_token, "LISTRIGHTS");
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_mailbox_parse(fd, buffer, &cur_token, &mailbox,
          progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto mailbox_free;
  }

  r = mailimap_acl_identifier_parse(fd, buffer, &cur_token, &identifier,
          progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto mailbox_free;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto identifier_free;
  }

  r = mailimap_struct_spaced_list_parse(fd, buffer,
        &cur_token, &rights_list,
        (mailimap_struct_parser * )
        mailimap_acl_rights_parse,
        (mailimap_struct_destructor * )
        mailimap_acl_rights_free,
        progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto identifier_free;
  }

  lr_data = mailimap_acl_listrights_data_new(mailbox, identifier,
      rights_list);
  if (lr_data == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto rights_list_free;
  }

  * result = lr_data;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 rights_list_free:
  if (rights_list != NULL) {
    clist_foreach(rights_list,
      (clist_func) mailimap_acl_rights_free, NULL);
    clist_free(rights_list);
  }
 identifier_free:
  mailimap_acl_identifier_free(identifier);
 mailbox_free:
  mailimap_mailbox_free(mailbox);
 err:
  return res;
}

int
mailimap_acl_myrights_data_parse(mailstream * fd, MMAPString *buffer,
    size_t * indx,
    struct mailimap_acl_myrights_data ** result,
    size_t progr_rate,
    progress_function * progr_fun)
{
  size_t cur_token;
  char * mailbox;
  char * rights;
  struct mailimap_acl_myrights_data * mr_data;
  int r;
  int res;

  cur_token = * indx;

  mailbox = NULL; /* XXX - removes a gcc warning */
  rights = NULL;

  r = mailimap_token_case_insensitive_parse(fd, buffer,
					    &cur_token, "MYRIGHTS");
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_mailbox_parse(fd, buffer, &cur_token, &mailbox,
          progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto mailbox_free;
  }

  r = mailimap_acl_rights_parse(fd, buffer, &cur_token, &rights,
          progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto mailbox_free;
  }

  mr_data = mailimap_acl_myrights_data_new(mailbox, rights);
  if (mr_data == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto rights_free;
  }

  * result = mr_data;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 rights_free:
  mailimap_acl_rights_free(rights);
 mailbox_free:
  mailimap_mailbox_free(mailbox);
 err:
  return res;
}

int
mailimap_acl_identifier_rights_parse(mailstream * fd,
    MMAPString *buffer, size_t * indx,
    struct mailimap_acl_identifier_rights ** result,
    size_t progr_rate,
    progress_function * progr_fun)
{
  size_t cur_token;
  char * identifier;
  char * rights;
  struct mailimap_acl_identifier_rights * id_rights;
  int r;
  int res;

  cur_token = * indx;

  identifier = NULL; /* XXX - removes a gcc warning */
  rights = NULL;

  r = mailimap_acl_identifier_parse(fd, buffer, &cur_token, &identifier,
          progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto identifier_free;
  }

  r = mailimap_acl_rights_parse(fd, buffer, &cur_token, &rights,
          progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto identifier_free;
  }

  id_rights = mailimap_acl_identifier_rights_new(identifier, rights);
  if (id_rights == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto rights_free;
  }

  * result = id_rights;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 rights_free:
  mailimap_acl_rights_free(rights);
 identifier_free:
  mailimap_acl_identifier_free(identifier);
 err:
  return res;
}

int
mailimap_acl_identifier_parse(mailstream * fd,
    MMAPString *buffer, size_t * indx,
    char ** result, size_t progr_rate,
    progress_function * progr_fun)
{
  return mailimap_astring_parse(fd, buffer, indx, result,
      progr_rate, progr_fun);
}

int mailimap_acl_rights_parse(mailstream * fd,
    MMAPString *buffer, size_t * indx,
    char ** result, size_t progr_rate,
    progress_function * progr_fun)
{
  return mailimap_astring_parse(fd, buffer, indx, result,
      progr_rate, progr_fun);
}

/*
  this is the extension's initial parser. it switches on calling_parser
  and calls the corresponding actual parser. acl extends
  imap as follows:
  mailbox-data    =/ acl-data / listrights-data / myrights-data
                      ;;mailbox-data is defined in [IMAP4]
  capability      =/ rights-capa
                      ;;capability is defined in [IMAP4]

  the extension to capability is omitted so far.
*/

int mailimap_acl_parse(int calling_parser, mailstream * fd,
    MMAPString * buffer, size_t * indx,
    struct mailimap_extension_data ** result,
    size_t progr_rate,
    progress_function * progr_fun)
{
  int r;
  int res;

  struct mailimap_acl_acl_data * acl_data;
  struct mailimap_acl_listrights_data * lr_data;
  struct mailimap_acl_myrights_data * mr_data;

  void * data;

  int type;

	acl_data = NULL;
	lr_data = NULL;
	mr_data = NULL;

  switch (calling_parser)
  {
    case MAILIMAP_EXTENDED_PARSER_MAILBOX_DATA:
      r = mailimap_acl_acl_data_parse(fd, buffer, indx,
        &acl_data, progr_rate, progr_fun);
      if (r == MAILIMAP_NO_ERROR) {
        type = MAILIMAP_ACL_TYPE_ACL_DATA;
        data = acl_data;
      }

      if (r == MAILIMAP_ERROR_PARSE) {
        r = mailimap_acl_listrights_data_parse(fd, buffer, indx,
          &lr_data, progr_rate, progr_fun);
        if (r == MAILIMAP_NO_ERROR) {
          type = MAILIMAP_ACL_TYPE_LISTRIGHTS_DATA;
          data = lr_data;
        }
      }

      if (r == MAILIMAP_ERROR_PARSE) {
        r = mailimap_acl_myrights_data_parse(fd, buffer, indx,
          &mr_data, progr_rate, progr_fun);
        if (r == MAILIMAP_NO_ERROR) {
          type = MAILIMAP_ACL_TYPE_MYRIGHTS_DATA;
          data = mr_data;
        }
      }

      if (r != MAILIMAP_NO_ERROR) {
        res = r;
        goto err;
      }

      * result = mailimap_extension_data_new(&mailimap_extension_acl,
                type, data);
      if (*result == NULL) {
        res = MAILIMAP_ERROR_MEMORY;
        goto data_free;
      }
      break;
    default:
      /* return a MAILIMAP_ERROR_PARSE if the extension
         doesn't extend calling_parser. */
      return MAILIMAP_ERROR_PARSE;
      break;
  }

  return MAILIMAP_NO_ERROR;

 data_free:
  if (acl_data != NULL)
    mailimap_acl_acl_data_free(acl_data);
  if (lr_data != NULL)
    mailimap_acl_listrights_data_free(lr_data);
  if (mr_data != NULL)
    mailimap_acl_myrights_data_free(mr_data);
 err:
  return res;
}

