/*
 * 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.
 */

/*
 * $Id: mailimap_parser.c,v 1.74 2011/07/13 09:28:36 hoa Exp $
 */

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

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

#include "mailstream.h"
#include "mailimap_keywords.h"
#include "mailimap_parser.h"
#include "mailimap_extension.h"
#include "mmapstring.h"
#include "mail.h"
#include "timeutils.h"

#ifdef _WIN32
#   ifdef _MSC_VER
#   include "win_etpan.h"
#   endif
#endif

#ifndef UNSTRICT_SYNTAX
#define UNSTRICT_SYNTAX
#endif

/*
  Document: internet-drafts/draft-crispin-imapv-15.txt
  RFC 2060 (IMAP but rather used draft)
  RFC 2234 for all token that are not defined such as ALPHA
*/



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


typedef int mailimap_struct_parser_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                            size_t * indx, void * result,
                                            size_t progr_rate,
                                            progress_function * progr_fun,
                                            mailprogress_function * body_progr_fun,
                                            mailprogress_function * items_progr_fun,
                                            void * context,
                                            mailimap_msg_att_handler * msg_att_handler,
                                            void * msg_att_context);

static int mailimap_address_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				  size_t * indx,
				  struct mailimap_address ** result,
				  size_t progr_rate,
				  progress_function * progr_fun);

static int mailimap_addr_adl_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx, char ** result,
				   size_t progr_rate,
				   progress_function * progr_fun);

static int mailimap_addr_host_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx, char ** result,
				    size_t progr_rate,
				    progress_function * progr_fun);

static int mailimap_addr_mailbox_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				       size_t * indx, char ** result,
				       size_t progr_rate,
				       progress_function * progr_fun);

static int mailimap_addr_name_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx, char ** result,
				    size_t progr_rate,
				    progress_function * progr_fun);

static int mailimap_auth_type_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx, char ** result,
				    size_t progr_rate,
				    progress_function * progr_fun);

static int mailimap_base64_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				 size_t * indx, char ** result,
				 size_t progr_rate,
				 progress_function * progr_fun);

static int mailimap_body_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_body ** result,
			       size_t progr_rate,
			       progress_function * progr_fun);


static int
mailimap_body_extension_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_body_extension ** result,
			      size_t progr_rate,
			      progress_function * progr_fun);


static int
mailimap_body_ext_1part_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_body_ext_1part ** result,
			      size_t progr_rate,
			      progress_function * progr_fun);



static int
mailimap_body_ext_mpart_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_body_ext_mpart ** result,
			      size_t progr_rate,
			      progress_function * progr_fun);


static int
mailimap_body_fields_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			   size_t * indx,
			   struct mailimap_body_fields ** result,
			   size_t progr_rate,
			   progress_function * progr_fun);

static int mailimap_body_fld_desc_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					size_t * indx, char ** result,
					size_t progr_rate,
					progress_function * progr_fun);


static int
mailimap_body_fld_dsp_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_body_fld_dsp ** result,
			    size_t progr_rate,
			    progress_function * progr_fun);



static int
mailimap_body_fld_enc_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_body_fld_enc ** result,
			    size_t progr_rate,
			    progress_function * progr_fun);



static int mailimap_body_fld_id_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				      size_t * indx, char ** result,
				      size_t progr_rate,
				      progress_function * progr_fun);


static int
mailimap_body_fld_lang_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx,
			     struct mailimap_body_fld_lang ** result,
			     size_t progr_rate,
			     progress_function * progr_fun);

static int mailimap_body_fld_lines_parse(mailstream * fd,
					 MMAPString * buffer, struct mailimap_parser_context * parser_ctx, size_t * indx,
					 uint32_t * result);

static int mailimap_body_fld_loc_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                       size_t * indx, char ** result,
                                       size_t progr_rate,
                                       progress_function * progr_fun);

static int mailimap_body_fld_md5_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				       size_t * indx, char ** result,
				       size_t progr_rate,
				       progress_function * progr_fun);

static int mailimap_body_fld_octets_parse(mailstream * fd,
					  MMAPString * buffer, struct mailimap_parser_context * parser_ctx, size_t * indx,
					  uint32_t * result);

static int
mailimap_body_fld_param_parse(mailstream * fd,
			      MMAPString * buffer, struct mailimap_parser_context * parser_ctx, size_t * indx,
			      struct mailimap_body_fld_param ** result,
			      size_t progr_rate,
			      progress_function * progr_fun);



static int
mailimap_body_type_1part_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_body_type_1part ** result,
			       size_t progr_rate,
			       progress_function * progr_fun);



static int
mailimap_body_type_basic_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_body_type_basic ** result,
			       size_t progr_rate,
			       progress_function * progr_fun);



static int
mailimap_body_type_mpart_parse(mailstream * fd,
			       MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_body_type_mpart ** result,
			       size_t progr_rate,
			       progress_function * progr_fun);



static int
mailimap_body_type_msg_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx,
			     struct mailimap_body_type_msg ** result,
			     size_t progr_rate,
			     progress_function * progr_fun);



static int
mailimap_body_type_text_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_body_type_text **
			      result,
			      size_t progr_rate,
			      progress_function * progr_fun);



static int
mailimap_capability_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			  size_t * indx,
			  struct mailimap_capability ** result,
			  size_t progr_rate,
			  progress_function * progr_fun);



/*
static gboolean mailimap_date_day_parse(mailstream * fd,
					MMAPString * buffer,
					guint32 * indx,
					gint * result);
*/
static int mailimap_date_day_fixed_parse(mailstream * fd,
					 MMAPString * buffer,
					 size_t * indx,
					 int * result);

static int mailimap_date_month_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				     size_t * indx, int * result);

/*
struct mailimap_date_text {
  gint day;
  gint month;
  gint year;
};

static gboolean
mailimap_date_text_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			 guint32 * indx, struct mailimap_date_text ** result);
static void mailimap_date_text_free(struct mailimap_date_text * date_text);
*/

static int mailimap_date_year_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx, int * result);

static int mailimap_date_time_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx,
				    struct mailimap_date_time ** t,
				    size_t progr_rate,
				    progress_function * progr_fun);

#ifndef UNSTRICT_SYNTAX
static int mailimap_digit_nz_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx, int * result);
#endif


static int mailimap_envelope_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx,
				   struct mailimap_envelope ** result,
				   size_t progr_rate,
				   progress_function * progr_fun);


static int
mailimap_env_bcc_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
		       size_t * indx, struct mailimap_env_bcc ** result,
		       size_t progr_rate,
		       progress_function * progr_fun);


static int
mailimap_env_cc_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
		      size_t * indx, struct mailimap_env_cc ** result,
		      size_t progr_rate,
		      progress_function * progr_fun);

static int mailimap_env_date_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx, char ** result,
				   size_t progr_rate,
				   progress_function * progr_fun);


static int
mailimap_env_from_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			size_t * indx, struct mailimap_env_from ** result,
			size_t progr_rate,
			progress_function * progr_fun);


static int mailimap_env_in_reply_to_parse(mailstream * fd,
					  MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					  size_t * indx, char ** result,
					  size_t progr_rate,
					  progress_function * progr_fun);

static int mailimap_env_message_id_parse(mailstream * fd,
					 MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					 size_t * indx, char ** result,
					 size_t progr_rate,
					 progress_function * progr_fun);

static int
mailimap_env_reply_to_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_env_reply_to ** result,
			    size_t progr_rate,
			    progress_function * progr_fun);



static int
mailimap_env_sender_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			  size_t * indx, struct mailimap_env_sender ** result,
			  size_t progr_rate,
			  progress_function * progr_fun);

static int mailimap_env_subject_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				      size_t * indx, char ** result,
				      size_t progr_rate,
				      progress_function * progr_fun);


static int
mailimap_env_to_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
		      size_t * indx,
		      struct mailimap_env_to ** result,
		      size_t progr_rate,
		      progress_function * progr_fun);


static int mailimap_flag_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_flag ** result,
			       size_t progr_rate,
			       progress_function * progr_fun);

static int mailimap_flag_extension_parse(mailstream * fd,
					 MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					 size_t * indx,
					 char ** result,
					 size_t progr_rate,
					 progress_function * progr_fun);




static int
mailimap_flag_fetch_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			  size_t * indx,
			  struct mailimap_flag_fetch ** result,
			  size_t progr_rate,
			  progress_function * progr_fun);



static int
mailimap_flag_perm_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			 size_t * indx,
			 struct mailimap_flag_perm ** result,
			 size_t progr_rate,
			 progress_function * progr_fun);


static int mailimap_flag_keyword_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				       size_t * indx,
				       char ** result,
				       size_t progr_rate,
				       progress_function * progr_fun);


static int mailimap_flag_list_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx,
				    struct mailimap_flag_list ** result,
				    size_t progr_rate,
				    progress_function * progr_fun);


static int
mailimap_header_fld_name_parse(mailstream * fd,
			       MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       char ** result,
			       size_t progr_rate,
			       progress_function * progr_fun);




static int
mailimap_header_list_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			   size_t * indx,
			   struct mailimap_header_list ** result,
			   size_t progr_rate,
			   progress_function * progr_fun);

static int mailimap_literal_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                           size_t * indx, char ** result,
                                           size_t * result_len,
                                           size_t progr_rate,
                                           progress_function * progr_fun,
                                           mailprogress_function * body_progr_fun,
                                           mailprogress_function * items_progr_fun,
                                           void * context,
                                           mailimap_msg_att_handler * msg_att_handler,
                                           void * msg_att_context);


static int
mailimap_mailbox_data_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_mailbox_data ** result,
			    size_t progr_rate,
			    progress_function * progr_fun);


static int
mailimap_mbx_list_flags_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_mbx_list_flags ** result,
			      size_t progr_rate,
			      progress_function * progr_fun);


static int
mailimap_mbx_list_oflag_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_mbx_list_oflag ** result,
			      size_t progr_rate,
			      progress_function * progr_fun);

static int
mailimap_mbx_list_oflag_no_sflag_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
    size_t * indx,
    struct mailimap_mbx_list_oflag ** result,
    size_t progr_rate,
    progress_function * progr_fun);

static int
mailimap_mbx_list_sflag_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      int * result);


static int
mailimap_media_basic_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			   size_t * indx,
			   struct mailimap_media_basic ** result,
			   size_t progr_rate,
			   progress_function * progr_fun);

static int
mailimap_media_message_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx);

static int
mailimap_media_subtype_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx,
			     char ** result,
			     size_t progr_rate,
			     progress_function * progr_fun);

static int mailimap_media_text_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				     size_t * indx,
				     char ** result,
				     size_t progr_rate,
				     progress_function * progr_fun);

static int
mailimap_msg_att_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                size_t * indx, struct mailimap_msg_att ** result,
                                size_t progr_rate,
                                progress_function * progr_fun,
                                mailprogress_function * body_progr_fun,
                                mailprogress_function * items_progr_fun,
                                void * context,
                                mailimap_msg_att_handler * msg_att_handler,
                                void * msg_att_context);


static int
mailimap_msg_att_dynamic_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_msg_att_dynamic ** result,
			       size_t progr_rate,
			       progress_function * progr_fun);

static int
mailimap_msg_att_static_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                       size_t * indx,
                                       struct mailimap_msg_att_static ** result,
                                       size_t progr_rate,
                                       progress_function * progr_fun,
                                       mailprogress_function * body_progr_fun,
                                       mailprogress_function * items_progr_fun,
                                       void * context,
                                       mailimap_msg_att_handler * msg_att_handler,
                                       void * msg_att_context);

static int
mailimap_quoted_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
		      size_t * indx, char ** result,
		      size_t progr_rate,
		      progress_function * progr_fun);

static int
mailimap_quoted_specials_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx, char * result);




static int
mailimap_response_done_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx,
			     struct mailimap_response_done ** result,
			     size_t progr_rate,
			     progress_function * progr_fun);

static int
mailimap_response_fatal_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_response_fatal ** result,
			      size_t progr_rate,
			      progress_function * progr_fun);


static int
mailimap_response_tagged_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_response_tagged ** result,
			       size_t progr_rate,
			       progress_function * progr_fun);


static int
mailimap_resp_cond_auth_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_resp_cond_auth ** result,
			      size_t progr_rate,
			      progress_function * progr_fun);

static int
mailimap_resp_cond_bye_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx,
			     struct mailimap_resp_cond_bye ** result,
			     size_t progr_rate,
			     progress_function * progr_fun);


static int
mailimap_resp_cond_state_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_resp_cond_state ** result,
			       size_t progr_rate,
			       progress_function * progr_fun);


static int
mailimap_resp_text_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			 size_t * indx,
			 struct mailimap_resp_text ** result,
			 size_t progr_rate,
			 progress_function * progr_fun);


static int
mailimap_resp_text_code_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_resp_text_code ** result,
			      size_t progr_rate,
			      progress_function * progr_fun);


static int
mailimap_section_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
		       size_t * indx,
		       struct mailimap_section ** result,
		       size_t progr_rate,
		       progress_function * progr_fun);


static int
mailimap_section_msgtext_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_section_msgtext ** result,
			       size_t progr_rate,
			       progress_function * progr_fun);


static int
mailimap_section_part_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_section_part ** result,
			    size_t progr_rate,
			    progress_function * progr_fun);




static int
mailimap_section_spec_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_section_spec ** result,
			    size_t progr_rate,
			    progress_function * progr_fun);


static int
mailimap_section_text_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_section_text ** result,
			    size_t progr_rate,
			    progress_function * progr_fun);


static int mailimap_tag_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx, char ** result,
			      size_t progr_rate,
			      progress_function * progr_fun);

static int mailimap_text_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx, char ** result,
			       size_t progr_rate,
			       progress_function * progr_fun);

static int mailimap_time_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       int * phour, int * pmin, int * psec);

static int mailimap_zone_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx, int * result);

static int
mailimap_mailbox_gmail_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
    size_t * indx, char ** result,
    size_t progr_rate,
    progress_function * progr_fun);

static int
mailimap_response_data_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                      size_t * indx,
                                      struct mailimap_response_data ** result,
                                      size_t progr_rate,
                                      progress_function * progr_fun,
                                      mailprogress_function * body_progr_fun,
                                      mailprogress_function * items_progr_fun,
                                      void * context,
                                      mailimap_msg_att_handler * msg_att_handler,
                                      void * msg_att_context);

static int mailimap_nstring_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                           size_t * indx, char ** result,
                                           size_t * result_len,
                                           size_t progr_rate,
                                           progress_function * progr_fun,
                                           mailprogress_function * body_progr_fun,
                                           mailprogress_function * items_progr_fun,
                                           void * context,
                                           mailimap_msg_att_handler * msg_att_handler,
                                           void * msg_att_context);

static int
mailimap_string_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                               size_t * indx, char ** result,
                               size_t * result_len,
                               size_t progr_rate,
                               progress_function * progr_fun,
                               mailprogress_function * body_progr_fun,
                               mailprogress_function * items_progr_fun,
                               void * context,
                               mailimap_msg_att_handler * msg_att_handler,
                               void * msg_att_context);

static int has_crlf(MMAPString * buffer, size_t index);

static int mailimap_address_list_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                       size_t * indx,
                                       clist ** result,
                                       size_t progr_rate,
                                       progress_function * progr_fun);

static int mailimap_envelope_parse_full(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                        size_t * indx,
                                        struct mailimap_envelope ** result,
                                        size_t progr_rate,
                                        progress_function * progr_fun);

static int mailimap_envelope_parse_workaround_qq_mail(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                                      size_t * indx,
                                                      struct mailimap_envelope ** result,
                                                      size_t progr_rate,
                                                      progress_function * progr_fun);

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





/* ******************** TOOLS **************************** */


static int mailimap_unstrict_char_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					size_t * indx, char token)
{
  size_t cur_token;
  int r;

  cur_token = * indx;
  
#ifdef UNSTRICT_SYNTAX
  /* can accept unstrict syntax */

  mailimap_space_parse(fd, buffer, &cur_token);
  if (token == ' ') {
    * indx = cur_token;
    return MAILIMAP_NO_ERROR;
  }
#endif

  r = mailimap_char_parse(fd, buffer, &cur_token, token);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

int mailimap_oparenth_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, '(');
}

int mailimap_cparenth_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, ')');
}

static int mailimap_oaccolade_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, '{');
}

static int mailimap_caccolade_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, '}');
}

static int mailimap_plus_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, '+');
}

static int mailimap_minus_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, '-');
}

static int mailimap_star_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, '*');
}

static int mailimap_dot_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, '.');
}

int mailimap_colon_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
    size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, ':');
}

static int mailimap_lower_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, '<');
}

static int mailimap_greater_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				  size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, '>');
}

static int mailimap_obracket_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, '[');
}

static int mailimap_cbracket_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx)
{
  return mailimap_unstrict_char_parse(fd, buffer, parser_ctx, indx, ']');
}

int mailimap_dquote_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				 size_t * indx)
{
  return mailimap_char_parse(fd, buffer, indx, '\"');
}

static int mailimap_crlf_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx)
{
  size_t cur_token = * indx;
  int r;
  
#ifdef UNSTRICT_SYNTAX
  mailimap_space_parse(fd, buffer, &cur_token);
#endif

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "\r\n");
  if (r == MAILIMAP_NO_ERROR) {
    * indx = cur_token;
    return MAILIMAP_NO_ERROR;
  }

#ifdef UNSTRICT_SYNTAX
  r = mailimap_unstrict_char_parse(fd, buffer, parser_ctx, &cur_token, '\n');
  if (r == MAILIMAP_NO_ERROR) {
    * indx = cur_token;
    return MAILIMAP_NO_ERROR;
  }
#endif
  
  return MAILIMAP_ERROR_PARSE;
}

static int
mailimap_struct_multiple_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                        size_t * indx, clist ** result,
                                        mailimap_struct_parser_progress * parser,
                                        mailimap_struct_destructor * destructor,
                                        size_t progr_rate,
                                        progress_function * progr_fun,
                                        mailprogress_function * body_progr_fun,
                                        mailprogress_function * items_progr_fun,
                                        void * context,
                                        mailimap_msg_att_handler * msg_att_handler,
                                        void * msg_att_context)
{
  clist * struct_list;
  size_t cur_token;
  void * value;
  int r;
  int res;
  
  cur_token = * indx;
  
  r = parser(fd, buffer, parser_ctx, &cur_token, &value, progr_rate, progr_fun,
             body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  struct_list = clist_new();
  if (struct_list == NULL) {
    destructor(value);
    res = MAILIMAP_ERROR_MEMORY;
    goto err;
  }
  
  if (value != NULL) {
    r = clist_append(struct_list, value);
    if (r < 0) {
      destructor(value);
      res = MAILIMAP_ERROR_MEMORY;
      goto free_list;
    }
  }
  
  while (1) {
    r = parser(fd, buffer, parser_ctx, &cur_token, &value, progr_rate, progr_fun,
               body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);
    if (r == MAILIMAP_ERROR_PARSE)
      break;
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto free_list;
    }
    
    if (value != NULL) {
      r = clist_append(struct_list, value);
      if (r < 0) {
        destructor(value);
        res = MAILIMAP_ERROR_MEMORY;
        goto free_list;
      }
    }
  }
  
  * result = struct_list;
  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;
  
free_list:
  clist_foreach(struct_list, (clist_func) destructor, NULL);
  clist_free(struct_list);
err:
  return res;
}

int
mailimap_struct_multiple_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx, clist ** result,
			       mailimap_struct_parser * parser,
			       mailimap_struct_destructor * destructor,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  clist * struct_list;
  size_t cur_token;
  void * value;
  int r;
  int res;

  cur_token = * indx;

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

  struct_list = clist_new();
  if (struct_list == NULL) {
    destructor(value);
    res = MAILIMAP_ERROR_MEMORY;
    goto err;
  }

  r = clist_append(struct_list, value);
  if (r < 0) {
    destructor(value);
    res = MAILIMAP_ERROR_MEMORY;
    goto free_list;
  }

  while (1) {
    r = parser(fd, buffer, parser_ctx, &cur_token, &value, progr_rate, progr_fun);
    if (r == MAILIMAP_ERROR_PARSE)
      break;
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto free_list;
    }
    
    r = clist_append(struct_list, value);
    if (r < 0) {
      destructor(value);
      res = MAILIMAP_ERROR_MEMORY;
      goto free_list;
    }
  }

  * result = struct_list;
  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;

 free_list:
  clist_foreach(struct_list, (clist_func) destructor, NULL);
  clist_free(struct_list);
 err:
  return res;
}

int
mailimap_struct_list_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                    size_t * indx, clist ** result,
                                    char symbol,
                                    mailimap_struct_parser_progress * parser,
                                    mailimap_struct_destructor * destructor,
                                    size_t progr_rate,
                                    progress_function * progr_fun,
                                    mailprogress_function * body_progr_fun,
                                    mailprogress_function * items_progr_fun,
                                    void * context,
                                    mailimap_msg_att_handler * msg_att_handler,
                                    void * msg_att_context)
{
  clist * struct_list;
  size_t cur_token;
  void * value;
  size_t final_token;
  int r;
  int res;
  
  cur_token = * indx;
  struct_list = NULL;
  
  r = parser(fd, buffer, parser_ctx, &cur_token, &value, progr_rate, progr_fun,
             body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  struct_list = clist_new();
  if (struct_list == NULL) {
    destructor(value);
    res = MAILIMAP_ERROR_MEMORY;
    goto err;
  }
  
  r = clist_append(struct_list, value);
  if (r < 0) {
    destructor(value);
    res = MAILIMAP_ERROR_MEMORY;
    goto free_list;
  }
  
  final_token = cur_token;
  
  while (1) {
    r = mailimap_unstrict_char_parse(fd, buffer, parser_ctx, &cur_token, symbol);
    if (r == MAILIMAP_ERROR_PARSE)
      break;
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto free_list;
    }
    
    r = parser(fd, buffer, parser_ctx, &cur_token, &value, progr_rate, progr_fun,
               body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);
    if (r == MAILIMAP_ERROR_PARSE)
      break;
    
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto free_list;
    }
    
    r = clist_append(struct_list, value);
    if (r < 0) {
      destructor(value);
      res = MAILIMAP_ERROR_MEMORY;
      goto free_list;
    }
    
    final_token = cur_token;
  }
  
  * result = struct_list;
  * indx = final_token;
  
  return MAILIMAP_NO_ERROR;
  
free_list:
  clist_foreach(struct_list, (clist_func) destructor, NULL);
  clist_free(struct_list);
err:
  return res;
}

int
mailimap_struct_list_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			   size_t * indx, clist ** result,
			   char symbol,
			   mailimap_struct_parser * parser,
			   mailimap_struct_destructor * destructor,
			   size_t progr_rate,
			   progress_function * progr_fun)
{
  clist * struct_list;
  size_t cur_token;
  void * value;
  size_t final_token;
  int r;
  int res;
  
  cur_token = * indx;
  struct_list = NULL;

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

  struct_list = clist_new();
  if (struct_list == NULL) {
    destructor(value);
    res = MAILIMAP_ERROR_MEMORY;
    goto err;
  }

  r = clist_append(struct_list, value);
  if (r < 0) {
    destructor(value);
    res = MAILIMAP_ERROR_MEMORY;
    goto free_list;
  }

  final_token = cur_token;

  while (1) {
    r = mailimap_unstrict_char_parse(fd, buffer, parser_ctx, &cur_token, symbol);
    if (r == MAILIMAP_ERROR_PARSE)
      break;
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto free_list;
    }

    r = parser(fd, buffer, parser_ctx, &cur_token, &value, progr_rate, progr_fun);
    if (r == MAILIMAP_ERROR_PARSE)
      break;

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

    r = clist_append(struct_list, value);
    if (r < 0) {
      destructor(value);
      res = MAILIMAP_ERROR_MEMORY;
      goto free_list;
    }
      
    final_token = cur_token;
  }

  * result = struct_list;
  * indx = final_token;

  return MAILIMAP_NO_ERROR;

 free_list:
  clist_foreach(struct_list, (clist_func) destructor, NULL);
  clist_free(struct_list);
 err:
  return res;
}

int
mailimap_struct_spaced_list_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				  size_t * indx, clist ** result,
				  mailimap_struct_parser * parser,
				  mailimap_struct_destructor * destructor,
				  size_t progr_rate,
				  progress_function * progr_fun)
{
  return mailimap_struct_list_parse(fd, buffer, parser_ctx, indx, result,
				    ' ', parser, destructor,
				    progr_rate, progr_fun);
}

static int
mailimap_struct_spaced_list_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                           size_t * indx, clist ** result,
                                           mailimap_struct_parser_progress * parser,
                                           mailimap_struct_destructor * destructor,
                                           size_t progr_rate,
                                           progress_function * progr_fun,
                                           mailprogress_function * body_progr_fun,
                                           mailprogress_function * items_progr_fun,
                                           void * context,
                                           mailimap_msg_att_handler * msg_att_handler,
                                           void * msg_att_context)
{
  return mailimap_struct_list_parse_progress(fd, buffer, parser_ctx, indx, result,
                                             ' ', parser, destructor,
                                             progr_rate, progr_fun,
                                             body_progr_fun, items_progr_fun, context,
                                             msg_att_handler, msg_att_context);
}


static int
mailimap_custom_string_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx, char ** result,
			     int (* is_custom_char)(char))
{
  size_t begin;
  size_t end;
  char * gstr;

  begin = * indx;

#ifdef UNSTRICT_SYNTAX
  mailimap_space_parse(fd, buffer, &begin);
#endif

  end = begin;

  while (is_custom_char(buffer->str[end]))
    end ++;

  if (end != begin) {
    gstr = malloc(end - begin + 1);
    if (gstr == NULL)
      return MAILIMAP_ERROR_MEMORY;

    strncpy(gstr, buffer->str + begin, end - begin);
    gstr[end - begin] = '\0';
    
    * indx = end;
    * result = gstr;
    return MAILIMAP_NO_ERROR;
  }
  else
    return MAILIMAP_ERROR_PARSE;
}



int
mailimap_nz_number_alloc_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       uint32_t ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  uint32_t number;
  uint32_t * number_alloc;
  size_t cur_token;
  int r;

  cur_token = * indx;

  r = mailimap_nz_number_parse(fd, buffer, parser_ctx, &cur_token, &number);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  number_alloc = mailimap_number_alloc_new(number);
  if (number_alloc == NULL)
    return MAILIMAP_ERROR_MEMORY;

  * indx = cur_token;
  * result = number_alloc;

  return MAILIMAP_NO_ERROR;
}


static int is_ctl(char ch)
{
  unsigned char uch = (unsigned char) ch;

  return (uch <= 0x1F);
}

static int is_char(char ch)
{
#ifdef UNSTRICT_SYNTAX
  return (ch != 0);
#else
  unsigned char uch = ch;

  return (uch >= 0x01) && (uch <= 0x7f);
#endif
}

static int is_alpha(char ch)
{
  return ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && (ch <= 'z')));
}

static int is_digit(char ch)
{
  return (ch >= '0') && (ch <= '9');
}

static int mailimap_digit_parse(mailstream * fd, MMAPString * buffer,
				size_t * indx, int * result)
{
  size_t cur_token;

  cur_token = * indx;

  if (is_digit(buffer->str[cur_token])) {
    * result = buffer->str[cur_token] - '0';
    cur_token ++;
    * indx = cur_token;
    return MAILIMAP_NO_ERROR;
  }
  else
    return MAILIMAP_ERROR_PARSE;
}


/* ******************** parser **************************** */

/*
   address         = "(" addr-name SP addr-adl SP addr-mailbox SP
                     addr-host ")"
*/

static int mailimap_address_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				  size_t * indx,
				  struct mailimap_address ** result,
				  size_t progr_rate,
				  progress_function * progr_fun)
{
  size_t cur_token;
  char * addr_name;
  char * addr_adl;
  char * addr_mailbox;
  char * addr_host;
  struct mailimap_address * addr;
  int r;
  int res;
  
  cur_token = * indx;

  addr_name = NULL;
  addr_adl = NULL;
  addr_mailbox = NULL;
  addr_host = NULL;

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

  r = mailimap_addr_name_parse(fd, buffer, parser_ctx, &cur_token, &addr_name,
			       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 addr_name_free;
  }

  r = mailimap_addr_adl_parse(fd, buffer, parser_ctx, &cur_token, &addr_adl,
			      progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto addr_name_free;
  }

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

  r = mailimap_addr_mailbox_parse(fd, buffer, parser_ctx, &cur_token, &addr_mailbox,
				  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto addr_adl_free;
  }

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

  r = mailimap_addr_host_parse(fd, buffer, parser_ctx, &cur_token, &addr_host,
			       progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto addr_mailbox_free;
  }

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto addr_host_free;
  }

  addr = mailimap_address_new(addr_name, addr_adl, addr_mailbox, addr_host);

  if (addr == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto addr_host_free;
  }

  * result = addr;
  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;

 addr_host_free:
  mailimap_addr_host_free(addr_host);
 addr_mailbox_free:
  mailimap_addr_mailbox_free(addr_mailbox);
 addr_adl_free:
  mailimap_addr_adl_free(addr_adl);
 addr_name_free:
  mailimap_addr_name_free(addr_name);
 err:
  return res;
}

/*
   addr-adl        = nstring
                       ; Holds route from [RFC-822] route-addr if
                       ; non-NIL
*/

static int mailimap_addr_adl_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx, char ** result,
				   size_t progr_rate,
				   progress_function * progr_fun)
{
  return mailimap_nstring_parse(fd, buffer, parser_ctx, indx, result, NULL,
				progr_rate, progr_fun);
}

/*
   addr-host       = nstring
                       ; NIL indicates [RFC-822] group syntax.
                       ; Otherwise, holds [RFC-822] domain name
*/

static int mailimap_addr_host_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx, char ** result,
				    size_t progr_rate,
				    progress_function * progr_fun)
{
  int r;

  /* workaround for qq.com IMAP Server. */
  r = mailimap_token_case_insensitive_parse(fd, buffer, indx, "\"qq.com\\\"");
  if (r == MAILIMAP_NO_ERROR) {
    return r;
  }

  return mailimap_nstring_parse(fd, buffer, parser_ctx, indx, result, NULL,
				progr_rate, progr_fun);
}

/*
   addr-mailbox    = nstring
                       ; NIL indicates end of [RFC-822] group; if
                       ; non-NIL and addr-host is NIL, holds
                       ; [RFC-822] group name.
                       ; Otherwise, holds [RFC-822] local-part
                       ; after removing [RFC-822] quoting
 */

static int mailimap_addr_mailbox_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				       size_t * indx, char ** result,
				       size_t progr_rate,
				       progress_function * progr_fun)
{
  return mailimap_nstring_parse(fd, buffer, parser_ctx, indx, result, NULL,
				progr_rate, progr_fun);
}


/*
   addr-name       = nstring
                       ; If non-NIL, holds phrase from [RFC-822]
                       ; mailbox after removing [RFC-822] quoting
*/

static int mailimap_addr_name_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx, char ** result,
				    size_t progr_rate,
				    progress_function * progr_fun)
{
  return mailimap_nstring_parse(fd, buffer, parser_ctx, indx, result, NULL,
				progr_rate, progr_fun);
}


/*
  NOT IMPLEMENTED
   append          = "APPEND" SP mailbox [SP flag-list] [SP date-time] SP
                     literal
*/

/*
   astring         = 1*ASTRING-CHAR / string
*/

static int is_astring_char(char ch);

static int
mailimap_atom_astring_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx, char ** result,
			    size_t progr_rate,
			    progress_function * progr_fun)
{
  return mailimap_custom_string_parse(fd, buffer, parser_ctx, indx, result,
				      is_astring_char);
}

int
mailimap_astring_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
		       size_t * indx,
		       char ** result,
		       size_t progr_rate,
		       progress_function * progr_fun)
{
  size_t cur_token;
  char * astring;
  int r;

  cur_token = * indx;

  r = mailimap_atom_astring_parse(fd, buffer, parser_ctx, &cur_token, &astring,
				  progr_rate, progr_fun);
  switch (r) {
  case MAILIMAP_NO_ERROR:
    break;

  case MAILIMAP_ERROR_PARSE:
    r = mailimap_string_parse(fd, buffer, parser_ctx, &cur_token, &astring, NULL,
			      progr_rate, progr_fun);
    if (r != MAILIMAP_NO_ERROR)
      return r;
    break;

  default:
    return r;
  }

  * result = astring;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
   ASTRING-CHAR   = ATOM-CHAR / resp-specials
*/

static int is_atom_char(char ch);
static int is_resp_specials(char ch);

static int is_astring_char(char ch)
{
  if (is_atom_char(ch))
    return TRUE;
  if (is_resp_specials(ch))
    return TRUE;
  return FALSE;
}

/*
   atom            = 1*ATOM-CHAR
*/

int mailimap_atom_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			size_t * indx, char ** result,
			size_t progr_rate,
			progress_function * progr_fun)
{
  return mailimap_custom_string_parse(fd, buffer, parser_ctx, indx, result,
				      is_atom_char);
}

/*
   ATOM-CHAR       = <any CHAR except atom-specials>
*/

static int is_atom_specials(char ch);

static int is_atom_char(char ch)
{
  if (is_atom_specials(ch))
    return FALSE;

  return is_char(ch);
}

/*
   atom-specials   = "(" / ")" / "{" / SP / CTL / list-wildcards /
                     quoted-specials / resp-specials

no "}" because there is no need (Mark Crispin)
*/

static int is_quoted_specials(char ch);
static int is_list_wildcards(char ch);

static int is_atom_specials(char ch)
{
  switch (ch) {
  case '(':
  case ')':
  case '{':
  case ' ':
    return TRUE;
  };
  if (is_ctl(ch))
    return TRUE;
  if (is_list_wildcards(ch))
    return TRUE;
  if (is_resp_specials(ch))
    return TRUE;

  return is_quoted_specials(ch);
}

/*
  NOT IMPLEMENTED
   authenticate    = "AUTHENTICATE" SP auth-type *(CRLF base64)
*/

/*
   auth-type       = atom
                       ; Defined by [SASL]
*/

static int mailimap_auth_type_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx, char ** result,
				    size_t progr_rate,
				    progress_function * progr_fun)
{
  return mailimap_atom_parse(fd, buffer, parser_ctx, indx, result,
			     progr_rate, progr_fun);
}

/*
   base64          = *(4base64-char) [base64-terminal]
*/

static int is_base64_4char(char * str);
static int is_base64_terminal(char * str);

static int mailimap_base64_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				 size_t * indx, char ** result,
				 size_t progr_rate,
				 progress_function * progr_fun)
{
  size_t begin;
  size_t end;
  char * gstr;

  begin = * indx;
  end = begin;

  while (is_base64_4char(buffer->str + end))
    end += 4;
  if (is_base64_terminal(buffer->str + end))
    end += 4;

  if (begin == end)
    return MAILIMAP_ERROR_PARSE;
  
  gstr = malloc(end - begin + 1);
  if (gstr == NULL)
    return MAILIMAP_ERROR_MEMORY;
  strncpy(gstr, buffer->str + begin, end - begin);
  gstr[end - begin] = '\0';

  * result = gstr;
  * indx = end;

  return MAILIMAP_NO_ERROR;
}

/*
   base64-char     = ALPHA / DIGIT / "+" / "/"
                       ; Case-sensitive
*/

static int is_base64_char(char ch)
{
  return (is_alpha(ch) || is_digit(ch) || ch == '+' || ch == '/');
}

static int is_base64_4char(char * str)
{
  size_t i;

  for (i = 0 ; i < 4 ; i++)
    if (!is_base64_char(str[i]))
      return FALSE;
  return TRUE;
}

/*
   base64-terminal = (2base64-char "==") / (3base64-char "=")
*/

static int is_base64_terminal(char * str)
{
  if (str[0] == 0)
    return FALSE;
  if (str[1] == 0)
    return FALSE;
  if (str[2] == 0)
    return FALSE;
  if (str[3] == 0)
    return FALSE;

  if (is_base64_char(str[0]) && is_base64_char(str[1])
      && str[2] == '=' && str[3] == '=')
    return TRUE;
  if (is_base64_char(str[0]) && is_base64_char(str[1])
      && is_base64_char(str[2]) && str[3] == '=')
    return TRUE;
  return FALSE;
}


/*
   body            = "(" (body-type-1part / body-type-mpart) ")"
*/

static int mailimap_body_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_body ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  struct mailimap_body_type_1part * body_type_1part;
  struct mailimap_body_type_mpart * body_type_mpart;
  struct mailimap_body * body;
  size_t cur_token;
  int type;
  int r;
  int res;

  cur_token = * indx;

  body_type_1part = NULL;
  body_type_mpart = NULL;

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

  type = MAILIMAP_BODY_ERROR; /* XXX - removes a gcc warning */

  r = mailimap_body_type_1part_parse(fd, buffer, parser_ctx, &cur_token, &body_type_1part,
				     progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_BODY_1PART;

  if (r == MAILIMAP_ERROR_PARSE) {
   r = mailimap_body_type_mpart_parse(fd, buffer, parser_ctx, &cur_token,
				      &body_type_mpart,
				      progr_rate, progr_fun);
   
   if (r == MAILIMAP_NO_ERROR)
     type = MAILIMAP_BODY_MPART;
  }

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

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free;
  }

  body = mailimap_body_new(type, body_type_1part, body_type_mpart);
  if (body == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = body;
  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;

 free:
  if (body_type_1part)
    mailimap_body_type_1part_free(body_type_1part);
  if (body_type_mpart)
    mailimap_body_type_mpart_free(body_type_mpart);
 err:
  return res;
}

/*
   body-extension  = nstring / number /
                      "(" body-extension *(SP body-extension) ")"
                       ; Future expansion.  Client implementations
                       ; MUST accept body-extension fields.  Server
                       ; implementations MUST NOT generate
                       ; body-extension fields except as defined by
                       ; future standard or standards-track
                       ; revisions of this specification.
*/

/*
  "(" body-extension *(SP body-extension) ")"
*/

static int
mailimap_body_ext_list_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx,
			     clist ** result,
			     size_t progr_rate,
			     progress_function * progr_fun)
{
  size_t cur_token;
  clist * list;
  int r;
  int res;

  cur_token = * indx;

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

  r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx,
					&cur_token, &list,
					(mailimap_struct_parser * )
					mailimap_body_extension_parse,
					(mailimap_struct_destructor * )
					mailimap_body_extension_free,
					progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free_list;
  }

  * indx = cur_token;
  * result = list;

  return MAILIMAP_NO_ERROR;

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

/*
   body-extension  = nstring / number /
                      "(" body-extension *(SP body-extension) ")"
                       ; Future expansion.  Client implementations
                       ; MUST accept body-extension fields.  Server
                       ; implementations MUST NOT generate
                       ; body-extension fields except as defined by
                       ; future standard or standards-track
                       ; revisions of this specification.
*/

static int
mailimap_body_extension_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_body_extension ** result,
			      size_t progr_rate,
			      progress_function * progr_fun)
{
  size_t cur_token;
  uint32_t number;
  char * nstring;
  clist * body_extension_list;
  struct mailimap_body_extension * body_extension;
  int type;
  int r;
  int res;

  cur_token = * indx;

  nstring = NULL;
  number = 0;
  body_extension_list = NULL;
  type = MAILIMAP_BODY_EXTENSION_ERROR; /* XXX - removes a gcc warning */

  r = mailimap_nstring_parse(fd, buffer, parser_ctx, &cur_token, &nstring, NULL,
			     progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_BODY_EXTENSION_NSTRING;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_number_parse(fd, buffer, &cur_token, &number);

    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_BODY_EXTENSION_NUMBER;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_body_ext_list_parse(fd, buffer, parser_ctx, &cur_token,
				     &body_extension_list,
				     progr_rate, progr_fun);

    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_BODY_EXTENSION_LIST;
  }

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

  body_extension = mailimap_body_extension_new(type, nstring, number,
					       body_extension_list);
  
  if (body_extension == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = body_extension;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (nstring != NULL)
    mailimap_nstring_free(nstring);
  if (body_extension_list) {
    clist_foreach(body_extension_list,
		  (clist_func) mailimap_body_extension_free,
		  NULL);
    clist_free(body_extension_list);
  }
 err:
  return res;
}

/*
   body-ext-1part  = body-fld-md5 [SP body-fld-dsp [SP body-fld-lang
                     *(SP body-extension)]]
                       ; MUST NOT be returned on non-extensible
                       ; "BODY" fetch
*/

/*
 *(SP body-extension)
*/

static int
mailimap_body_ext_1part_4_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				size_t * indx,
				clist ** body_ext_list,
				size_t progr_rate,
				progress_function * progr_fun)
{
  size_t cur_token;
  int r;
  
  cur_token = * indx;
  * body_ext_list = NULL;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token,
					body_ext_list,
					(mailimap_struct_parser *)
					mailimap_body_extension_parse,
					(mailimap_struct_destructor *)
					mailimap_body_extension_free,
					progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
[SP body-fld-loc *(SP body-extension)]]]
*/

static int
mailimap_body_ext_1part_3_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                size_t * indx,
                                char ** fld_loc,
                                clist ** body_ext_list,
                                size_t progr_rate,
                                progress_function * progr_fun)
{
  size_t cur_token;
  int r;
  
  cur_token = * indx;
  * fld_loc = NULL;
  * body_ext_list = NULL;
  
  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_body_fld_loc_parse(fd, buffer, parser_ctx, &cur_token, fld_loc,
                                  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_body_ext_1part_4_parse(fd, buffer, parser_ctx, &cur_token,
                                      body_ext_list, progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE))
    return r;
  
  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;
}

/*
  [SP body-fld-lang
  *(SP body-extension)]]
*/

static int
mailimap_body_ext_1part_2_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				size_t * indx,
				struct mailimap_body_fld_lang ** fld_lang,
        char ** fld_loc,
				clist ** body_ext_list,
				size_t progr_rate,
				progress_function * progr_fun)
{
  size_t cur_token;
  int r;
  
  cur_token = * indx;
  * fld_lang = NULL;
  * fld_loc = NULL;
  * body_ext_list = NULL;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_body_fld_lang_parse(fd, buffer, parser_ctx, &cur_token, fld_lang,
				   progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_body_ext_1part_3_parse(fd, buffer, parser_ctx, &cur_token,
				      fld_loc, body_ext_list, progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE))
    return r;

  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;
}


/*
  SP body-fld-dsp [SP body-fld-lang
  *(SP body-extension)]]
*/

static int
mailimap_body_ext_1part_1_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				size_t * indx,
				struct mailimap_body_fld_dsp ** fld_dsp,
				struct mailimap_body_fld_lang ** fld_lang,
        char ** fld_loc,
				clist ** body_ext_list,
				size_t progr_rate,
				progress_function * progr_fun)
{
  size_t cur_token;
  int r;
  
  cur_token = * indx;
  * fld_dsp = NULL;
  * fld_lang = NULL;
  * fld_loc = NULL;
  * body_ext_list = NULL;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_body_fld_dsp_parse(fd, buffer, parser_ctx, &cur_token, fld_dsp,
				  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_body_ext_1part_2_parse(fd, buffer, parser_ctx, &cur_token,
				      fld_lang, fld_loc, body_ext_list,
				      progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE))
    return r;

  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
   body-ext-1part  = body-fld-md5 [SP body-fld-dsp [SP body-fld-lang
                     *(SP body-extension)]]
                       ; MUST NOT be returned on non-extensible
                       ; "BODY" fetch
*/

static int
mailimap_body_ext_1part_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_body_ext_1part ** result,
			      size_t progr_rate,
			      progress_function * progr_fun)
{
  size_t cur_token;

  char * fld_md5;
  struct mailimap_body_fld_dsp * fld_dsp;
  struct mailimap_body_fld_lang * fld_lang;
  char * fld_loc;
  clist * body_ext_list;
  int r;
  int res;

  struct mailimap_body_ext_1part * ext_1part;

  cur_token = * indx;

  fld_md5 = NULL;
  fld_dsp = NULL;
  fld_lang = NULL;
  fld_loc = NULL;
  body_ext_list = NULL;

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

  r = mailimap_body_ext_1part_1_parse(fd, buffer, parser_ctx, &cur_token,
				      &fld_dsp,
				      &fld_lang,
              &fld_loc,
				      &body_ext_list,
				      progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
    res = r;
    goto free;
  }

  ext_1part = mailimap_body_ext_1part_new(fld_md5, fld_dsp, fld_lang, fld_loc,
					  body_ext_list);
  
  if (ext_1part == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }
  
  * result = ext_1part;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
  
 free:
  if (body_ext_list) {
    clist_foreach(body_ext_list, (clist_func) mailimap_body_extension_free,
		  NULL);
    clist_free(body_ext_list);
  }
  mailimap_body_fld_loc_free(fld_loc);
  if (fld_lang)
    mailimap_body_fld_lang_free(fld_lang);
  if (fld_dsp)
    mailimap_body_fld_dsp_free(fld_dsp);
  mailimap_body_fld_md5_free(fld_md5);
 err:
  return res;
}
					      

/*
   body-ext-mpart  = body-fld-param [SP body-fld-dsp [SP body-fld-lang
                     *(SP body-extension)]]
                       ; MUST NOT be returned on non-extensible
                       ; "BODY" fetch
*/

static int
mailimap_body_ext_mpart_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_body_ext_mpart ** result,
			      size_t progr_rate,
			      progress_function * progr_fun)
{
  size_t cur_token;

  struct mailimap_body_fld_dsp * fld_dsp;
  struct mailimap_body_fld_lang * fld_lang;
  char * fld_loc;
  struct mailimap_body_fld_param * fld_param;
  clist * body_ext_list;

  struct mailimap_body_ext_mpart * ext_mpart;
  int r;
  int res;

  cur_token = * indx;

  fld_param = NULL;
  fld_dsp = NULL;
  fld_lang = NULL;
  fld_loc = NULL;
  body_ext_list = NULL;

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

  r = mailimap_body_ext_1part_1_parse(fd, buffer, parser_ctx, &cur_token,
				      &fld_dsp,
				      &fld_lang,
              &fld_loc,
				      &body_ext_list,
				      progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
    res = r;
    goto free;
  }

  ext_mpart = mailimap_body_ext_mpart_new(fld_param, fld_dsp, fld_lang, fld_loc,
					  body_ext_list);
  if (ext_mpart == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }
  
  * result = ext_mpart;
  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;

 free:
  if (body_ext_list) {
    clist_foreach(body_ext_list, (clist_func) mailimap_body_extension_free,
		   NULL);
    clist_free(body_ext_list);
  }
  mailimap_body_fld_loc_free(fld_loc);
  if (fld_lang)
    mailimap_body_fld_lang_free(fld_lang);
  if (fld_dsp)
    mailimap_body_fld_dsp_free(fld_dsp);
  if (fld_param != NULL)
    mailimap_body_fld_param_free(fld_param);
 err:
  return res;
}

/*
   body-fields     = body-fld-param SP body-fld-id SP body-fld-desc SP
                     body-fld-enc SP body-fld-octets
*/

static int
mailimap_body_fields_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			   size_t * indx,
			   struct mailimap_body_fields ** result,
			   size_t progr_rate,
			   progress_function * progr_fun)
{
  struct mailimap_body_fields * body_fields;
  size_t cur_token;
  struct mailimap_body_fld_param * body_fld_param;
  char * body_fld_id;
  char * body_fld_desc;
  struct mailimap_body_fld_enc * body_fld_enc;
  uint32_t body_fld_octets;
  int r;
  int res;
  
  body_fld_param = NULL;
  body_fld_id = NULL;
  body_fld_desc = NULL;
  body_fld_enc = NULL;
  body_fld_octets = 0;

  cur_token = * indx;

  r = mailimap_body_fld_param_parse(fd, buffer, parser_ctx, &cur_token, &body_fld_param,
				    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 fld_param_free;
  }

  r = mailimap_body_fld_id_parse(fd, buffer, parser_ctx, &cur_token, &body_fld_id,
				 progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto fld_param_free;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto fld_id_free;
  }
  
  r = mailimap_body_fld_desc_parse(fd, buffer, parser_ctx, &cur_token, &body_fld_desc,
				   progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto fld_id_free;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto fld_desc_free;
  }
  
  r = mailimap_body_fld_enc_parse(fd, buffer, parser_ctx, &cur_token, &body_fld_enc,
				  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto fld_desc_free;
  }

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

  r = mailimap_body_fld_octets_parse(fd, buffer, parser_ctx, &cur_token,
				     &body_fld_octets);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto fld_enc_free;
  }

  body_fields = mailimap_body_fields_new(body_fld_param,
					 body_fld_id,
					 body_fld_desc,
					 body_fld_enc,
					 body_fld_octets);
  if (body_fields == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto fld_enc_free;
  }

  * result = body_fields;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 fld_enc_free:
  mailimap_body_fld_enc_free(body_fld_enc);
 fld_desc_free:
  mailimap_body_fld_desc_free(body_fld_desc);
 fld_id_free:
  mailimap_body_fld_id_free(body_fld_id);
 fld_param_free:
  if (body_fld_param != NULL)
    mailimap_body_fld_param_free(body_fld_param);
 err:  
  return res;
}
  
/*
   body-fld-desc   = nstring
*/

static int mailimap_body_fld_desc_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					size_t * indx, char ** result,
					size_t progr_rate,
					progress_function * progr_fun)
{
  return mailimap_nstring_parse(fd, buffer, parser_ctx, indx, result, NULL,
				progr_rate, progr_fun);
}

/*
   body-fld-dsp    = "(" string SP body-fld-param ")" / nil
*/

static int
mailimap_body_fld_dsp_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_body_fld_dsp ** result,
			    size_t progr_rate,
			    progress_function * progr_fun)
{
  size_t cur_token;
  char * name;
  struct mailimap_body_fld_param * body_fld_param;
  struct mailimap_body_fld_dsp * body_fld_dsp;
  int res;
  int r;

  cur_token = * indx;
  name = NULL;
  body_fld_param = NULL;

  r = mailimap_nil_parse(fd, buffer, parser_ctx, &cur_token);
  if (r == MAILIMAP_NO_ERROR) {
    * result = NULL;
    * indx = cur_token;
    return MAILIMAP_NO_ERROR;
  }

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

  r = mailimap_oparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    // workaround for exchange
    r = mailimap_string_parse(fd, buffer, parser_ctx, &cur_token, &name, NULL,
                              +                                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 string_free;
    }
    
    r = mailimap_body_fld_param_parse(fd, buffer, parser_ctx, &cur_token,
                                      &body_fld_param,
                                      progr_rate, progr_fun);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto string_free;
    }
    
    goto create_body_fld_dsp;
  }

  r = mailimap_string_parse(fd, buffer, parser_ctx, &cur_token, &name, NULL,
			    progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  /* workaround for IMAPrev1 Citadel */
  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto string_free;
    }
    
    body_fld_param = NULL;
    
    goto create_body_fld_dsp;
  }
  
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto string_free;
  }

  r = mailimap_body_fld_param_parse(fd, buffer, parser_ctx, &cur_token,
				    &body_fld_param,
				    progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto string_free;
  }

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto string_free;
  }

create_body_fld_dsp:
  body_fld_dsp = mailimap_body_fld_dsp_new(name, body_fld_param);
  if (body_fld_dsp == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto fld_param_free;
  }

  * indx = cur_token;
  * result = body_fld_dsp;

  return MAILIMAP_NO_ERROR;

 fld_param_free:
  if (body_fld_param != NULL)
    mailimap_body_fld_param_free(body_fld_param);
 string_free:
    mailimap_string_free(name);
 err:
  return res;
}

/*
   body-fld-enc    = (DQUOTE ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
                     "QUOTED-PRINTABLE") DQUOTE) / string
*/

static inline int 
mailimap_body_fld_known_enc_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
    size_t * indx,
    int * result,
    size_t progr_rate,
    progress_function * progr_fun)
{
  size_t cur_token;
  int type;
  int r;
  int res;

  cur_token = * indx;

  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r == MAILIMAP_ERROR_PARSE) {
    /* workaround for exchange */
    type = mailimap_encoding_get_token_value(fd, buffer, &cur_token);
    
    if (type == -1) {
      res = MAILIMAP_ERROR_PARSE;
      goto err;
    }
    
    * result = type;
    * indx = cur_token;
    
    return MAILIMAP_NO_ERROR;
  }
  else if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  type = mailimap_encoding_get_token_value(fd, buffer, &cur_token);

  if (type == -1) {
    res = MAILIMAP_ERROR_PARSE;
    goto err;
  }
  
  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  * result = type;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
  
 err:
  return res;
}     

static int
mailimap_body_fld_enc_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_body_fld_enc ** result,
			    size_t progr_rate,
			    progress_function * progr_fun)
{
  size_t cur_token;
  int type;
  char * value;
  struct mailimap_body_fld_enc * body_fld_enc;
  int r;
  int res;

  cur_token = * indx;

  r = mailimap_body_fld_known_enc_parse(fd, buffer, parser_ctx, &cur_token,
      &type, progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR) {
    value = NULL;
  }
  else if (r == MAILIMAP_ERROR_PARSE) {
    type = MAILIMAP_BODY_FLD_ENC_OTHER;

    r = mailimap_nstring_parse(fd, buffer, parser_ctx, &cur_token, &value, NULL,
			      progr_rate, progr_fun);
    if (r == MAILIMAP_ERROR_PARSE) {
      /* workaround for exchange */
      r = mailimap_astring_parse(fd, buffer, parser_ctx, &cur_token, &value,
                                 progr_rate, progr_fun);
    }
    
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto err;
    }

	if (value == NULL) {
	  type = MAILIMAP_BODY_FLD_ENC_8BIT;
	}
  }
  else {
    res = r;
    goto err;
  }
  
  body_fld_enc = mailimap_body_fld_enc_new(type, value);
  if (body_fld_enc == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto value_free;
  }
  
  * result = body_fld_enc;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 value_free:
  if (value)
    mailimap_string_free(value);
 err:
  return res;
}

/*
   body-fld-id     = nstring
*/

static int mailimap_body_fld_id_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				      size_t * indx, char ** result,
				      size_t progr_rate,
				      progress_function * progr_fun)
{
  int r;
  
  r = mailimap_nstring_parse(fd, buffer, parser_ctx, indx, result, NULL,
                             progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    return MAILIMAP_NO_ERROR;
  
  r = mailimap_atom_parse(fd, buffer, parser_ctx, indx, result,
                          progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    return MAILIMAP_NO_ERROR;
  
  return r;
}


/*
   body-fld-lang   = nstring / "(" string *(SP string) ")"
*/

/*
"(" string *(SP string) ")"
*/

static int
mailimap_body_fld_lang_list_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				  size_t * indx, clist ** result,
				  size_t progr_rate,
				  progress_function * progr_fun)
{
  size_t cur_token;
  clist * list;
  int r;
  int res;

  cur_token = * indx;

  r = mailimap_oparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  list = clist_new();
  if (list == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto err;
  }
  
  while (1) {
    char * elt;

    mailimap_space_parse(fd, buffer, &cur_token);
    
    r = mailimap_string_parse(fd, buffer, parser_ctx, &cur_token, &elt, NULL,
			      progr_rate, progr_fun);
    if (r == MAILIMAP_ERROR_PARSE)
      break;
    else if (r == MAILIMAP_NO_ERROR) {
      r = clist_append(list, elt);
      if (r < 0) {
	mailimap_string_free(elt);
	res = r;
	goto list_free;
      }
    }
    else {
      res = r;
      goto list_free;
    }
  }
  
  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto list_free;
  }

  * indx = cur_token;
  * result = list;

  return MAILIMAP_NO_ERROR;

 list_free:
  clist_foreach(list, (clist_func) mailimap_string_free, NULL);
  clist_free(list);
 err:
  return res;
}

/*
   body-fld-lang   = nstring / "(" string *(SP string) ")"
*/

static int
mailimap_body_fld_lang_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx,
			     struct mailimap_body_fld_lang ** result,
			     size_t progr_rate,
			     progress_function * progr_fun)
{
  char * value;
  clist * list;
  struct mailimap_body_fld_lang * fld_lang;
  int type;
  int r;
  int res;

  size_t cur_token;

  cur_token = * indx;

  value = NULL;
  list = NULL;
  type = MAILIMAP_BODY_FLD_LANG_ERROR; /* XXX - removes a gcc warning */
  
  r = mailimap_nstring_parse(fd, buffer, parser_ctx, &cur_token, &value, NULL,
			     progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_BODY_FLD_LANG_SINGLE;
  
  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_body_fld_lang_list_parse(fd, buffer, parser_ctx, &cur_token, &list,
					  progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_BODY_FLD_LANG_LIST;
  }

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

  fld_lang = mailimap_body_fld_lang_new(type, value, list);
  if (fld_lang == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * indx = cur_token;
  * result = fld_lang;
  
  return MAILIMAP_NO_ERROR;

 free:
  if (value)
    mailimap_nstring_free(value);
  if (list) {
    clist_foreach(list, (clist_func) mailimap_string_free, NULL);
    clist_free(list);
  }
 err:
  return res;
}

/*
   body-fld-lines  = number
*/

static int mailimap_body_fld_lines_parse(mailstream * fd,
					 MMAPString * buffer, struct mailimap_parser_context * parser_ctx, size_t * indx,
					 uint32_t * result)
{
  return mailimap_number_parse(fd, buffer, indx, result);
}

/*
   body-fld-md5    = nstring
*/

static int mailimap_body_fld_md5_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				       size_t * indx, char ** result,
				       size_t progr_rate,
				       progress_function * progr_fun)
{
  int r;
  uint32_t value;
  size_t cur_token;
  char * md5_value;
  
  r = mailimap_nstring_parse(fd, buffer, parser_ctx, indx, result, NULL,
      progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    return r;
  
  cur_token = * indx;
  /* workaround for google IMAP */
  r = mailimap_number_parse(fd, buffer, &cur_token, &value);
  if (r != MAILIMAP_NO_ERROR) {
    return r;
  }
  
  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    return r;
  }

  r = mailimap_nstring_parse(fd, buffer, parser_ctx, &cur_token, &md5_value, NULL,
      progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    return r;
  }
  
  * indx = cur_token;
  * result = md5_value;
  
  return MAILIMAP_NO_ERROR;
}

static int mailimap_body_fld_loc_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                       size_t * indx, char ** result,
                                       size_t progr_rate,
                                       progress_function * progr_fun)
{
  return mailimap_nstring_parse(fd, buffer, parser_ctx, indx, result, NULL,
                                progr_rate, progr_fun);
}

/*
   body-fld-octets = number
*/

static int mailimap_body_fld_octets_parse(mailstream * fd,
					  MMAPString * buffer, struct mailimap_parser_context * parser_ctx, size_t * indx,
					  uint32_t * result)
{
  int r;
  
  r = mailimap_number_parse(fd, buffer, indx, result);
  if (r == MAILIMAP_NO_ERROR)
    return r;
  
  // workaround for Microsoft Exchange: it can provides -1 as octets
  r = mailimap_token_case_insensitive_parse(fd, buffer, indx, "-1");
  if (r == MAILIMAP_NO_ERROR) {
    * result = 0;
    return MAILIMAP_NO_ERROR;
  }
  
  // workaround for mbox mail for mac
  r = mailimap_nil_parse(fd, buffer, parser_ctx, indx);
  if (r == MAILIMAP_NO_ERROR) {
    * result = 0;
    return MAILIMAP_NO_ERROR;
  }
  
  return MAILIMAP_ERROR_PARSE;
}

/*
   body-fld-param  = "(" string SP string *(SP string SP string) ")" / nil
*/

/*
  string SP string
*/

static int
mailimap_single_body_fld_param_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				     size_t * indx,
				     struct mailimap_single_body_fld_param **
				     result,
				     size_t progr_rate,
				     progress_function * progr_fun)
{
  struct mailimap_single_body_fld_param * param;
  char * name;
  char * value;
  size_t cur_token;
  int r;
  int res;
  int workaround_used;

  cur_token = * indx;
  
  name = NULL;
  value = NULL;

  r = mailimap_string_parse(fd, buffer, parser_ctx, &cur_token, &name, NULL,
			    progr_rate, progr_fun);
  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_astring_parse(fd, buffer, parser_ctx, &cur_token, &name,
                               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 free_name;
  }
  /* workaround for Lotus Domino - parse "3D"Windows-1252"" */
  workaround_used = 0;
  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "\"3D\"Windows-1252\"\"");
  if (r == MAILIMAP_NO_ERROR) {
    workaround_used = 1;
    value = strdup("\"3D\"Windows-1252\"\"");
    if (value == NULL) {
      res = MAILIMAP_ERROR_MEMORY;
      goto free_name;
    }
  }
  
  if (!workaround_used) {
	  // also parse NIL to workaround Exchange issue
    r = mailimap_nstring_parse(fd, buffer, parser_ctx, &cur_token, &value, NULL,
                              progr_rate, progr_fun);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto free_name;
    }
    
    if (value == NULL) {
      value = strdup("");
      if (value == NULL) {
        res = MAILIMAP_ERROR_MEMORY;
        goto free_name;
      }
    }
  }
  
  param = mailimap_single_body_fld_param_new(name, value);
  if (param == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_value;
  }

  * result = param;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free_value:
  mailimap_string_free(name);
 free_name:
  mailimap_string_free(value);
 err:
  return res;
}

static void mailimap_broken_single_body_fld_param_string_free(char * value)
{
  // do nothing
}

static int
mailimap_broken_single_body_fld_param_string_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                                   size_t * indx,
                                                   char ** result,
                                                   size_t progr_rate,
                                                   progress_function * progr_fun)
{
  char * value;
  int r;
  size_t cur_token;
  
  cur_token = * indx;
  
  r = mailimap_string_parse(fd, buffer, parser_ctx, &cur_token, &value, NULL,
                            progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR) {
    mailimap_string_free(value);
    * result = "";
    * indx = cur_token;
    return r;
  }
  
  r = mailimap_astring_parse(fd, buffer, parser_ctx, &cur_token, &value,
                             progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR) {
    mailimap_astring_free(value);
    * result = "";
    * indx = cur_token;
    return r;
  }
  
  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "\"3D\"Windows-1252\"\"");
  if (r == MAILIMAP_NO_ERROR) {
    * result = "";
    * indx = cur_token;
    return r;
  }
  
  r = mailimap_nstring_parse(fd, buffer, parser_ctx, &cur_token, &value, NULL,
                             progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR) {
    mailimap_nstring_free(value);
    * result = "";
    * indx = cur_token;
    return r;
  }
  
  return MAILIMAP_ERROR_PARSE;
}

/*
   body-fld-param  = "(" string SP string *(SP string SP string) ")" / nil
*/

static int
mailimap_body_fld_param_parse(mailstream * fd,
			      MMAPString * buffer, struct mailimap_parser_context * parser_ctx, size_t * indx,
			      struct mailimap_body_fld_param ** result,
			      size_t progr_rate,
			      progress_function * progr_fun)
{
  size_t cur_token;
  clist * param_list;
  struct mailimap_body_fld_param * fld_param;
  int r;
  int res;

  param_list = NULL;
  cur_token = * indx;

  r = mailimap_nil_parse(fd, buffer, parser_ctx, &cur_token);
  if (r == MAILIMAP_NO_ERROR) {
    * result = NULL;
    * indx = cur_token;
    return MAILIMAP_NO_ERROR;
  }

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

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

  r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token, &param_list,
					(mailimap_struct_parser *)
					mailimap_single_body_fld_param_parse,
					(mailimap_struct_destructor *)
					mailimap_single_body_fld_param_free,
					progr_rate, progr_fun);
  if (r == MAILIMAP_ERROR_PARSE) {
    // case of empty ()
    // do nothing, workaround for mbox mail parser
  }
  else if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r == MAILIMAP_ERROR_PARSE) {
    // workaround for broken param list
    if (param_list != NULL) {
      clist_foreach(param_list,
                    (clist_func) mailimap_single_body_fld_param_free,
                    NULL);
      clist_free(param_list);
      param_list = NULL;
    }
    r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token, &param_list,
                                          (mailimap_struct_parser *)
                                          mailimap_broken_single_body_fld_param_string_parse,
                                          (mailimap_struct_destructor *)
                                          mailimap_broken_single_body_fld_param_string_free,
                                          progr_rate, progr_fun);
    if (param_list != NULL) {
      clist_free(param_list);
      param_list = NULL;
    }
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto free;
    }
    
    r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto free;
    }
  }
  else if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free;
  }
  
  if (param_list == NULL) {
    * result = NULL;
    * indx = cur_token;
    
    return MAILIMAP_NO_ERROR;
  }
  
  fld_param = mailimap_body_fld_param_new(param_list);
  if (fld_param == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * indx = cur_token;
  * result = fld_param;

  return MAILIMAP_NO_ERROR;

 free:
  if (param_list != NULL) {
    clist_foreach(param_list,
                  (clist_func) mailimap_single_body_fld_param_free,
                  NULL);
    clist_free(param_list);
  }
 err:
  return res;
}

/*
   body-type-1part = (body-type-basic / body-type-msg / body-type-text)
                     [SP body-ext-1part]
*/

static int
mailimap_body_type_1part_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_body_type_1part ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_body_type_1part * body_type_1part;
  struct mailimap_body_type_basic * body_type_basic;
  struct mailimap_body_type_msg * body_type_msg;
  struct mailimap_body_type_text * body_type_text;
  struct mailimap_body_ext_1part * body_ext_1part;
  int type;
  size_t final_token;
  int r;
  int res;
  
  cur_token = * indx;

  body_type_basic = NULL;
  body_type_msg = NULL;
  body_type_text = NULL;
  body_ext_1part = NULL;
  
  type = MAILIMAP_BODY_TYPE_1PART_ERROR; /* XXX - removes a gcc warning */
  
  r = mailimap_body_type_msg_parse(fd, buffer, parser_ctx, &cur_token,
				   &body_type_msg,
				   progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_BODY_TYPE_1PART_MSG;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_body_type_text_parse(fd, buffer, parser_ctx, &cur_token,
				      &body_type_text,
				      progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_BODY_TYPE_1PART_TEXT;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_body_type_basic_parse(fd, buffer, parser_ctx, &cur_token,
				       &body_type_basic,
				       progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_BODY_TYPE_1PART_BASIC;
  }

  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  final_token = cur_token;
  body_ext_1part = NULL;

  r = mailimap_space_parse(fd, buffer, &cur_token);

  if (r == MAILIMAP_NO_ERROR) {
    r = mailimap_body_ext_1part_parse(fd, buffer, parser_ctx, &cur_token, &body_ext_1part,
				      progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      final_token = cur_token;
    else if (r == MAILIMAP_ERROR_PARSE) {
      /* do nothing */
    }
    else {
      res = r;
      goto free;
    }
  }
  else if (r == MAILIMAP_ERROR_PARSE) {
    /* do nothing */
  }
  else {
    res = r;
    goto free;
  }

  body_type_1part = mailimap_body_type_1part_new(type, body_type_basic,
						 body_type_msg, body_type_text,
						 body_ext_1part);
  if (body_type_1part == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * indx = final_token;
  * result = body_type_1part;

  return MAILIMAP_NO_ERROR;

 free:
  if (body_type_basic)
    mailimap_body_type_basic_free(body_type_basic);
  if (body_type_msg)
    mailimap_body_type_msg_free(body_type_msg);
  if (body_type_text)
    mailimap_body_type_text_free(body_type_text);
  if (body_ext_1part)
    mailimap_body_ext_1part_free(body_ext_1part);
 err:
  return res;
}

/*
   body-type-basic = media-basic SP body-fields
                       ; MESSAGE subtype MUST NOT be "RFC822"
*/

static int
mailimap_body_type_basic_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_body_type_basic ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  size_t cur_token;
  size_t saved_token;
  struct mailimap_body_type_basic * body_type_basic;
  struct mailimap_media_basic * media_basic;
  struct mailimap_body_fields * body_fields;
  int r;
  int res;
  uint32_t body_fld_lines;

  cur_token = * indx;

  media_basic = NULL;
  body_fields = NULL;

  r = mailimap_media_basic_parse(fd, buffer, parser_ctx, &cur_token, &media_basic,
				 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 free_media_basic;
  }

  r = mailimap_body_fields_parse(fd, buffer, parser_ctx, &cur_token, &body_fields,
				 progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free_media_basic;
  }
  
  /* workaround for a bug on lavabit.com IMAP server - begin */
  saved_token = cur_token;
  
  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r == MAILIMAP_NO_ERROR) {
    r = mailimap_body_fld_lines_parse(fd, buffer, parser_ctx, &cur_token, &body_fld_lines);
    if (r != MAILIMAP_NO_ERROR) {
      cur_token = saved_token;
    }
  }
  else {
    cur_token = saved_token;
  }
  /* workaround for a bug on lavabit.com IMAP server - end */
  
  body_type_basic = mailimap_body_type_basic_new(media_basic, body_fields);
  if (body_type_basic == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_body_fields;
  }

  * indx = cur_token;
  * result = body_type_basic;

  return MAILIMAP_NO_ERROR;

 free_body_fields:
  mailimap_body_fields_free(body_fields);
 free_media_basic:
  mailimap_media_basic_free(media_basic);
 err:
  return res;
}

/*
   body-type-mpart = 1*body SP media-subtype
                     [SP body-ext-mpart]
*/

static int
mailimap_body_type_mpart_parse(mailstream * fd,
			       MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_body_type_mpart ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  struct mailimap_body_type_mpart * body_type_mpart;
  clist * body_list;
  size_t cur_token;
  size_t final_token;
  char * media_subtype;
  struct mailimap_body_ext_mpart * body_ext_mpart;
  int r;
  int res;
  
  cur_token = * indx;

  body_list = NULL;
  media_subtype = NULL;
  body_ext_mpart = NULL;

  r = mailimap_struct_multiple_parse(fd, buffer, parser_ctx, &cur_token,
				     &body_list,
				     (mailimap_struct_parser *)
				     mailimap_body_parse,
				     (mailimap_struct_destructor *)
				     mailimap_body_free,
				     progr_rate, progr_fun);
  if (r == MAILIMAP_ERROR_PARSE) {
	body_list =  clist_new();
	if (body_list == NULL) {
		res = MAILIMAP_ERROR_MEMORY;
		goto err;
	}
  }
  else if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  else {
    r = mailimap_space_parse(fd, buffer, &cur_token);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto free_body_list;
    }
  }

  r = mailimap_media_subtype_parse(fd, buffer, parser_ctx, &cur_token, &media_subtype,
				   progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free_body_list;
  }

  final_token = cur_token;

  body_ext_mpart = NULL;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r == MAILIMAP_NO_ERROR) {
    r = mailimap_body_ext_mpart_parse(fd, buffer, parser_ctx, &cur_token, &body_ext_mpart,
				      progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      final_token = cur_token;
    else if (r == MAILIMAP_ERROR_PARSE) {
      /* do nothing */
    }
    else {
      res = r;
      goto free_body_list;
    }
  }
  else if (r == MAILIMAP_ERROR_PARSE) {
    /* do nothing */
  }
  else {
    res = r;
    goto free_body_list;
  }

  body_type_mpart = mailimap_body_type_mpart_new(body_list, media_subtype,
						 body_ext_mpart);
  if (body_type_mpart == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_body_ext_mpart;
  }
  
  * result = body_type_mpart;
  * indx = final_token;

  return MAILIMAP_NO_ERROR;
  
 free_body_ext_mpart:
  if (body_ext_mpart)
    mailimap_body_ext_mpart_free(body_ext_mpart);
  mailimap_media_subtype_free(media_subtype);
 free_body_list:
  clist_foreach(body_list, (clist_func) mailimap_body_free, NULL);
  clist_free(body_list);
 err:
  return res;
}

/*
   body-type-msg   = media-message SP body-fields SP envelope
                     SP body SP body-fld-lines
*/

static struct mailimap_body * body_text_new_empty(void)
{
  struct mailimap_body_type_1part * body_1part;
  struct mailimap_body_type_text * body_type_text;
  struct mailimap_body_fields * body_fields;
  struct mailimap_body_fld_enc * enc;
  struct mailimap_body * body;
  char * media;
  
  enc = mailimap_body_fld_enc_new(MAILIMAP_BODY_FLD_ENC_7BIT, NULL);
  if (enc == NULL) {
    return NULL;
  }
  
  body_fields = mailimap_body_fields_new(NULL, NULL, NULL, enc, 0);
  if (body_fields == NULL) {
    mailimap_body_fld_enc_free(enc);
    return NULL;
  }
  
  media = strdup("plain");
  if (media == NULL) {
    mailimap_body_fields_free(body_fields);
    return NULL;
  }
  
  body_type_text = mailimap_body_type_text_new(media, body_fields, 0);
  if (body_type_text == NULL) {
    free(media);
    mailimap_body_fields_free(body_fields);
    return NULL;
  }
  
  body_1part =  mailimap_body_type_1part_new(MAILIMAP_BODY_TYPE_1PART_TEXT,
                                             NULL, NULL, body_type_text,
                                             NULL);
  if (body_1part == NULL) {
    mailimap_body_type_text_free(body_type_text);
    return NULL;
  }
  
  body = mailimap_body_new(MAILIMAP_BODY_1PART, body_1part, NULL);
  if (body == NULL) {
    mailimap_body_type_1part_free(body_1part);
    return NULL;
  }
  
  return body;
}

static int
mailimap_body_type_msg_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx,
			     struct mailimap_body_type_msg ** result,
			     size_t progr_rate,
			     progress_function * progr_fun)
{
  struct mailimap_body_fields * body_fields;
  struct mailimap_envelope * envelope;
  struct mailimap_body * body;
  uint32_t body_fld_lines;
  struct mailimap_body_type_msg * body_type_msg;
  size_t cur_token;
  int r;
  int res;

  cur_token = * indx;

  body_fields = NULL;
  envelope = NULL;
  body = NULL;
  body_fld_lines = 0;

  r = mailimap_media_message_parse(fd, buffer, parser_ctx, &cur_token);
  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_body_fields_parse(fd, buffer, parser_ctx, &cur_token, &body_fields,
				 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 body_fields;
  }

  r = mailimap_envelope_parse(fd, buffer, parser_ctx, &cur_token, &envelope,
			      progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto body_fields;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r == MAILIMAP_ERROR_PARSE) {
    // do nothing
  }
  else if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto envelope;
  }

  r = mailimap_body_parse(fd, buffer, parser_ctx, &cur_token, &body,
			  progr_rate, progr_fun);
  if (r == MAILIMAP_ERROR_PARSE) {
	  /* workaround for Lotus Domino - body malformed */
    r = mailimap_oparenth_parse(fd, buffer, parser_ctx, &cur_token);
    if (r != MAILIMAP_NO_ERROR) {
      res = MAILIMAP_ERROR_PARSE;
      goto envelope;
    }
    r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
    if (r != MAILIMAP_NO_ERROR) {
      res = MAILIMAP_ERROR_PARSE;
      goto envelope;
    }
    
    body = body_text_new_empty();
    if (body == NULL) {
      res = MAILIMAP_ERROR_MEMORY;
      goto envelope;
    }
  }
  else if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto envelope;
  }

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

  r = mailimap_body_fld_lines_parse(fd, buffer, parser_ctx, &cur_token,
				    &body_fld_lines);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto body;
  }

  body_type_msg = mailimap_body_type_msg_new(body_fields, envelope,
					     body, body_fld_lines);
  if (body_type_msg == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto body;
  }

  * result = body_type_msg;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 body:
  mailimap_body_free(body);
 envelope:
  mailimap_envelope_free(envelope);
 body_fields:
  mailimap_body_fields_free(body_fields);
 err:
  return res;
}

/*
   body-type-text  = media-text SP body-fields SP body-fld-lines
*/

static int
mailimap_body_type_text_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_body_type_text **
			      result,
			      size_t progr_rate,
			      progress_function * progr_fun)
{
  char * media_text;
  struct mailimap_body_fields * body_fields;
  uint32_t body_fld_lines;
  struct mailimap_body_type_text * body_type_text;
  size_t cur_token;
  int r;
  int res;

  media_text = NULL;
  body_fields = NULL;
  body_fld_lines = 0;
  
  cur_token = * indx;

  r = mailimap_media_text_parse(fd, buffer, parser_ctx, &cur_token, &media_text,
				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 free_media_text;
  }

  r = mailimap_body_fields_parse(fd, buffer, parser_ctx, &cur_token, &body_fields,
				 progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free_media_text;
  }

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

  r = mailimap_body_fld_lines_parse(fd, buffer, parser_ctx, &cur_token, &body_fld_lines);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free_body_fields;
  }

  body_type_text = mailimap_body_type_text_new(media_text, body_fields,
					       body_fld_lines);
  if (body_type_text == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_body_fields;
  }

  * result = body_type_text;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free_body_fields:
  mailimap_body_fields_free(body_fields);
 free_media_text:
  mailimap_media_text_free(media_text);
 err:
  return res;
}


/*
   capability      = ("AUTH=" auth-type) / atom
                       ; New capabilities MUST begin with "X" or be
                       ; registered with IANA as standard or
                       ; standards-track
*/

static int
mailimap_capability_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			  size_t * indx,
			  struct mailimap_capability ** result,
			  size_t progr_rate,
			  progress_function * progr_fun)
{
  size_t cur_token;
  int type;
  char * auth_type;
  char * atom;
  struct mailimap_capability * cap;
  int r;
  int res;

  cur_token = * indx;

  auth_type = NULL;
  atom = NULL;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "AUTH=");
  switch (r) {
  case MAILIMAP_NO_ERROR:
    type = MAILIMAP_CAPABILITY_AUTH_TYPE;

    r = mailimap_auth_type_parse(fd, buffer, parser_ctx, &cur_token, &auth_type,
				 progr_rate, progr_fun);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto err;
    }
    break;

  case MAILIMAP_ERROR_PARSE:
    r = mailimap_atom_parse(fd, buffer, parser_ctx, &cur_token, &atom,
			    progr_rate, progr_fun);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto err;
    }

    type = MAILIMAP_CAPABILITY_NAME;
    break;

  default:
    res = r;
    goto err;
  }

  cap = mailimap_capability_new(type, auth_type, atom);
  if (cap == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = cap;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (auth_type)
    mailimap_auth_type_free(auth_type);
  if (atom)
    mailimap_atom_free(atom);
 err:
  return res;
}

/*
   capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev1"
                     *(SP capability)
                       ; IMAP4rev1 servers which offer RFC 1730
                       ; compatibility MUST list "IMAP4" as the first
                       ; capability.
*/

/*
 SP capability *(SP capability)
*/

int mailimap_capability_list_parse(mailstream * fd,
					  MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					  size_t * indx,
					  clist ** result,
					  size_t progr_rate,
					  progress_function * progr_fun)
{
  size_t cur_token;
  clist * list;
  int r;
  
  cur_token = * indx;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token, &list,
					(mailimap_struct_parser *)
					mailimap_capability_parse,
					(mailimap_struct_destructor *)
					mailimap_capability_free,
					progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * indx = cur_token;
  * result = list;

  return MAILIMAP_NO_ERROR;
}

int
mailimap_capability_data_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_capability_data ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  size_t cur_token;
  clist * cap_list;
  struct mailimap_capability_data * cap_data;
  int r;
  int res;

  cur_token = * indx;
  cap_list = NULL;

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

  r = mailimap_capability_list_parse(fd, buffer, parser_ctx, &cur_token,
				     &cap_list,
				     progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
    res = r;
    goto err;
  }

  cap_data = mailimap_capability_data_new(cap_list);
  if (cap_data == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_list;
  }

  * result = cap_data;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free_list:
  if (cap_list) {
    clist_foreach(cap_list, (clist_func) mailimap_capability_free, NULL);
    clist_free(cap_list);
  }
 err:
  return res;
}     

/*
  UNIMPLEMENTED BECAUSE UNUSED (only in literal)
   CHAR8           = %x01-ff
                       ; any OCTET except NUL, %x00
*/

/*
static gboolean is_char8(gchar ch)
{
  return (ch != 0x00);
}
*/


/*
UNIMPLEMENTED
   command         = tag SP (command-any / command-auth / command-nonauth /
                     command-select) CRLF
                       ; Modal based on state
*/

/*
UNIMPLEMENTED
   command-any     = "CAPABILITY" / "LOGOUT" / "NOOP" / x-command
                       ; Valid in all states
*/

/*
UNIMPLEMENTED
   command-auth    = append / create / delete / examine / list / lsub /
                     rename / select / status / subscribe / unsubscribe
                       ; Valid only in Authenticated or Selected state
*/

/*
UNIMPLEMENTED
   command-nonauth = login / authenticate
                       ; Valid only when in Not Authenticated state
*/

/*
UNIMPLEMENTED
   command-select  = "CHECK" / "CLOSE" / "EXPUNGE" / copy / fetch / store /
                     uid / search
                       ; Valid only when in Selected state
*/

/*
   continue-req    = "+" SP (resp-text / base64) CRLF
*/

int
mailimap_continue_req_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_continue_req ** result,
			    size_t progr_rate,
			    progress_function * progr_fun)
{
  struct mailimap_resp_text * resp_text;
  size_t cur_token;
  struct mailimap_continue_req * cont_req;
  char * base64;
  int type;
  int r;
  int res;
  size_t saved_token;

  cur_token = * indx;
  
  r = mailimap_plus_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r == MAILIMAP_ERROR_PARSE) {
    /* do nothing */
  }
  else if (r != MAILIMAP_NO_ERROR) {
    return r;
  }
  
  resp_text = NULL;
  base64 = NULL;

  type = MAILIMAP_CONTINUE_REQ_ERROR; /* XXX - removes a gcc warning */
  
  saved_token = cur_token;
  r = mailimap_base64_parse(fd, buffer, parser_ctx, &cur_token, &base64,
      progr_rate, progr_fun);

  if (r == MAILIMAP_NO_ERROR) {
    type = MAILIMAP_CONTINUE_REQ_BASE64;
    
    r = mailimap_crlf_parse(fd, buffer, parser_ctx, &cur_token);
    if (r == MAILIMAP_NO_ERROR) {
      /* do nothing */
    }
    else if (r == MAILIMAP_ERROR_PARSE) {
      mailimap_base64_free(base64);
      base64 = NULL;
      cur_token = saved_token;
    }
    else {
      res = r;
      goto err;
    }
  }
  
  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_resp_text_parse(fd, buffer, parser_ctx, &cur_token, &resp_text,
        progr_rate, progr_fun);
    
    if (r == MAILIMAP_NO_ERROR) {
      type = MAILIMAP_CONTINUE_REQ_TEXT;
      
      r = mailimap_crlf_parse(fd, buffer, parser_ctx, &cur_token);
      if (r == MAILIMAP_NO_ERROR) {
        /* do nothing */
      }
      else if (r == MAILIMAP_ERROR_PARSE) {
        mailimap_resp_text_free(resp_text);
        resp_text = NULL;
        cur_token = saved_token;
      }
      else {
        res = r;
        goto err;
      }
    }
  }
  
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  cont_req = mailimap_continue_req_new(type, resp_text, base64);
  if (cont_req == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }
  
  * result = cont_req;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (base64 != NULL)
    mailimap_base64_free(base64);
  if (resp_text != NULL)
    mailimap_resp_text_free(resp_text);
 err:
  return res;
}

/*
  UNIMPLEMENTED
   copy            = "COPY" SP set SP mailbox
*/

/*
  UNIMPLEMENTED
   create          = "CREATE" SP mailbox
                       ; Use of INBOX gives a NO error
*/

/*
  UNIMPLEMENTED
   date            = date-text / DQUOTE date-text DQUOTE
*/

/*
  UNIMPLEMENTED
   date-day        = 1*2DIGIT
                       ; Day of month
*/

/*
static gboolean mailimap_date_day_parse(mailstream * fd,
                                        MMAPString * buffer,
					guint32 * indx,
					gint * result)
{
  guint32 cur_token;
  gint digit;
  gint number;

  cur_token = * indx;
  
  if (!mailimap_digit_parse(fd, buffer, &cur_token, &digit))
    return FALSE;

  number = digit;

  if (mailimap_digit_parse(fd, buffer, &cur_token, &digit))
    number = number * 10 + digit;
  
  * result = number;
  * indx = cur_token;

  return TRUE;
}
*/

/*
   date-day-fixed  = (SP DIGIT) / 2DIGIT
                       ; Fixed-format version of date-day
*/

static int mailimap_date_day_fixed_parse(mailstream * fd,
					 MMAPString * buffer,
					 size_t * indx,
					 int * result)
{
#ifdef UNSTRICT_SYNTAX
  size_t cur_token;
  uint32_t day;
  int r;

  cur_token = * indx;

  r = mailimap_number_parse(fd, buffer, &cur_token, &day);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * indx = cur_token;
  * result = day;

  return MAILIMAP_NO_ERROR;

#else
  size_t cur_token;
  int r;

  cur_token = * indx;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r == MAILIMAP_NO_ERROR) {
    int digit;

    r = mailimap_digit_parse(fd, buffer, &cur_token, &digit);
    if (r != MAILIMAP_NO_ERROR)
      return r;

    * result = digit;
    * indx = cur_token;

    return MAILIMAP_NO_ERROR;
  }
  else {
    int digit1;
    int digit2;

    r = mailimap_digit_parse(fd, buffer, &cur_token, &digit1);
    if (r != MAILIMAP_NO_ERROR)
      return r;
    r = mailimap_digit_parse(fd, buffer, &cur_token, &digit2);
    if (r != MAILIMAP_NO_ERROR)
      return r;

    * result = digit1 * 10 + digit2;
    * indx = cur_token;

    return MAILIMAP_NO_ERROR;
  }
#endif
}


/*
   date-month      = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
                     "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
*/

static int mailimap_date_month_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				     size_t * indx, int * result)
{
  size_t cur_token;
  int month;

  cur_token = * indx;

  month = mailimap_month_get_token_value(fd, buffer, &cur_token);
  if (month == -1)
    return MAILIMAP_ERROR_PARSE;

  * result = month;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  UNIMPLEMENTED
   date-text       = date-day "-" date-month "-" date-year
*/

/*
static struct mailimap_date_text *
mailimap_date_text_new(gint day, gint month, gint year)
{
  struct mailimap_date_text * date_text;
  
  date_text = g_new(struct mailimap_date_text, 1);
  if (date_text == NULL)
    return NULL;

  date_text->day = day;
  date_text->month = month;
  date_text->year = year;

  return date_text;
}

static void mailimap_date_text_free(struct mailimap_date_text * date_text)
{
  g_free(date_text);
}

static gboolean
mailimap_date_text_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			 guint32 * indx, struct mailimap_date_text ** result)
{
  struct mailimap_date_text * date_text;
  gint day;
  gint month;
  gint year;
  guint32 cur_token;

  cur_token = * indx;

  if (!mailimap_date_day_parse(fd, buffer, parser_ctx, &cur_token, &day))
    return FALSE;

  if (!mailimap_minus_parse(fd, buffer, parser_ctx, &cur_token))
    return FALSE;

  if (!mailimap_date_month_parse(fd, buffer, parser_ctx, &cur_token, &month))
    return FALSE;

  if (!mailimap_minus_parse(fd, buffer, parser_ctx, &cur_token))
    return FALSE;

  if (!mailimap_date_year_parse(fd, buffer, parser_ctx, &cur_token, &year))
    return FALSE;

  date_text = mailimap_date_text_new(day, month, year);
  if (date_text == NULL)
    return FALSE;

  * result = date_text;
  * indx = cur_token;

  return TRUE;
}
*/

/*
   date-year       = 4DIGIT
*/

static int mailimap_date_year_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx, int * result)
{
#ifdef UNSTRICT_SYNTAX
  uint32_t year;
  int r;
  size_t cur_token;
  
  cur_token = * indx;
  
  r = mailimap_number_parse(fd, buffer, &cur_token, &year);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = year;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
#else
  int i;
  size_t cur_token;
  int year;
  int digit;
  int r;

  cur_token = * indx;
  year = 0;

  for(i = 0 ; i < 4 ; i ++) {
    r = mailimap_digit_parse(fd, buffer, &cur_token, &digit);
    if (r != MAILIMAP_NO_ERROR)
      return r;
    year = year * 10 + digit;
  }

  * result = year;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
#endif
}

/*
   date-time       = DQUOTE date-day-fixed "-" date-month "-" date-year
                     SP time SP zone DQUOTE
*/

static int get_current_timezone_offset(void)
{
  struct tm gmt;
  struct tm lt;
  int off;
  time_t t;
  
  t = time(NULL);
  
  if (gmtime_r(&t, &gmt) == NULL)
    return 0;
  
  if (localtime_r(&t, &lt) == NULL)
    return 0;
  
  off = (int) ((mail_mkgmtime(&lt) - mail_mkgmtime(&gmt)) * 100 / (60 * 60));
  
  return off;
}

static int mailimap_date_time_no_quote_space_timezone_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                                            size_t * indx, int * p_timezone)
{
  int r;
  size_t cur_token;
  int zone;
  
  cur_token = * indx;
  
  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_zone_parse(fd, buffer, parser_ctx, &cur_token, &zone);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  * indx = cur_token;
  * p_timezone = zone;
  
  return MAILIMAP_NO_ERROR;
}

static int mailimap_date_time_no_quote_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                             size_t * indx,
                                             struct mailimap_date_time ** result,
                                             size_t progr_rate,
                                             progress_function * progr_fun)
{
  int day;
  int month;
  int year;
  int hour;
  int min;
  int sec;
  int zone;
  struct mailimap_date_time * date_time;
  size_t cur_token;
  int r;
  
  cur_token = * indx;
  
  r = mailimap_date_day_fixed_parse(fd, buffer, &cur_token, &day);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_minus_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_date_month_parse(fd, buffer, parser_ctx, &cur_token, &month);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_minus_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_date_year_parse(fd, buffer, parser_ctx, &cur_token, &year);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_time_parse(fd, buffer, parser_ctx, &cur_token, &hour, &min, &sec);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_date_time_no_quote_space_timezone_parse(fd, buffer, parser_ctx, &cur_token, &zone);
  if (r == MAILIMAP_ERROR_PARSE) {
    zone = get_current_timezone_offset();
  }
  else if (r != MAILIMAP_NO_ERROR) {
    return r;
  }
  
  date_time = mailimap_date_time_new(day, month, year, hour, min, sec, zone);
  if (date_time == NULL)
    return MAILIMAP_ERROR_MEMORY;
  
  * result = date_time;
  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;
}

LIBETPAN_EXPORT
int mailimap_hack_date_time_parse(char * str,
                                  struct mailimap_date_time ** result,
                                  size_t progr_rate,
                                  progress_function * progr_fun)
{
  mailstream * fd;
  MMAPString * buffer;
  struct mailimap_date_time * date_time;
  size_t cur_token;
  int r;
  
  fd = NULL;
  buffer = mmap_string_new(str);
  if (buffer == NULL) {
    return MAILIMAP_ERROR_MEMORY;
  }
  
  cur_token = 0;
  
  r = mailimap_date_time_no_quote_parse(fd, buffer, NULL, &cur_token, &date_time,
                                        progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    return r;
  }
  
  * result = date_time;
  
  return MAILIMAP_NO_ERROR;
}

static int mailimap_date_time_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                    size_t * indx,
                                    struct mailimap_date_time ** result,
                                    size_t progr_rate,
                                    progress_function * progr_fun)
{
  struct mailimap_date_time * date_time;
  size_t cur_token;
  int r;
  int res;
  
  cur_token = * indx;
  
  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  r = mailimap_date_time_no_quote_parse(fd, buffer, parser_ctx, &cur_token,
                                        &date_time, progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free_date_time;
  }
  
  * result = date_time;
  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;
  
free_date_time:
  mailimap_date_time_free(date_time);
err:
  return res;
}

/*
  UNIMPLEMENTED
   delete          = "DELETE" SP mailbox
                       ; Use of INBOX gives a NO error
*/

/*
   digit-nz        = %x31-39
                       ; 1-9
*/

#ifndef UNSTRICT_SYNTAX
static int is_digit_nz(char ch)
{
  return (ch >= '1') && (ch <= '9');
}

static int mailimap_digit_nz_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx, int * result)
{
  size_t cur_token;

  cur_token = * indx;

  if (is_digit_nz(buffer->str[cur_token])) {
    * result = buffer->str[cur_token] - '0';
    cur_token ++;
    * indx = cur_token;
    return MAILIMAP_NO_ERROR;
  }
  else
    return MAILIMAP_ERROR_PARSE;
}
#endif

/*
   envelope        = "(" env-date SP env-subject SP env-from SP env-sender SP
                     env-reply-to SP env-to SP env-cc SP env-bcc SP
                     env-in-reply-to SP env-message-id ")"
*/

static int mailimap_envelope_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx,
				   struct mailimap_envelope ** result,
				   size_t progr_rate,
				   progress_function * progr_fun)
{
  int r;
  r = mailimap_envelope_parse_full(fd, buffer, parser_ctx, indx, result, progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR) {
    return MAILIMAP_NO_ERROR;
  }
  else if (r != MAILIMAP_ERROR_PARSE) {
    return r;
  }

  /* workaround for qq.com IMAP Server. */
  r = mailimap_envelope_parse_workaround_qq_mail(fd, buffer, parser_ctx, indx, result, progr_rate, progr_fun);
  return r;
}

static int mailimap_envelope_parse_full(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                        size_t * indx,
                                        struct mailimap_envelope ** result,
                                        size_t progr_rate,
                                        progress_function * progr_fun)
{
  size_t cur_token;
  char * date;
  char * subject;
  struct mailimap_env_from * from;
  struct mailimap_env_sender * sender;
  struct mailimap_env_reply_to * reply_to;
  struct mailimap_env_to * to;
  struct mailimap_env_cc * cc;
  struct mailimap_env_bcc * bcc;
  char * in_reply_to;
  char * message_id;
  struct mailimap_envelope * envelope;
  int r;
  int res;
  
  date = NULL;
  subject = NULL;
  from = NULL;
  sender = NULL;
  reply_to = NULL;
  to = NULL;
  cc = NULL;
  bcc = NULL;
  in_reply_to = NULL;
  message_id = NULL;

  cur_token = * indx;

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

  r = mailimap_env_date_parse(fd, buffer, parser_ctx, &cur_token, &date,
			      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 date;
  }

  r = mailimap_env_subject_parse(fd, buffer, parser_ctx, &cur_token, &subject,
				 progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto date;
  }

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

  r = mailimap_env_from_parse(fd, buffer, parser_ctx, &cur_token, &from,
			      progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto subject;
  }

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

  r = mailimap_env_sender_parse(fd, buffer, parser_ctx, &cur_token, &sender,
				progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto from;
  }

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

  r = mailimap_env_reply_to_parse(fd, buffer, parser_ctx, &cur_token, &reply_to,
				  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto sender;
  }

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

  r = mailimap_env_to_parse(fd, buffer, parser_ctx, &cur_token, &to,
			    progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto reply_to;
  }

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

  r = mailimap_env_cc_parse(fd, buffer, parser_ctx, &cur_token, &cc,
			    progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto to;
  }

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

  r = mailimap_env_bcc_parse(fd, buffer, parser_ctx, &cur_token, &bcc,
			     progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto cc;
  }

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

  r = mailimap_env_in_reply_to_parse(fd, buffer, parser_ctx, &cur_token, &in_reply_to,
				     progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto bcc;
  }

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

  r = mailimap_env_message_id_parse(fd, buffer, parser_ctx, &cur_token, &message_id,
				    progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto in_reply_to;
  }

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto message_id;
  }

  envelope = mailimap_envelope_new(date, subject, from, sender, reply_to, to,
				   cc, bcc, in_reply_to, message_id);
  if (envelope == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto message_id;
  }

  * result = envelope;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 message_id:
  mailimap_env_message_id_free(message_id);
 in_reply_to:
  mailimap_env_in_reply_to_free(in_reply_to);
 bcc:
  mailimap_env_bcc_free(bcc);
 cc:
  mailimap_env_cc_free(cc);
 to:
  mailimap_env_to_free(to);
 reply_to:
  mailimap_env_reply_to_free(reply_to);
 sender:
  mailimap_env_sender_free(sender);
 from:
  mailimap_env_from_free(from);
 subject:
  mailimap_env_subject_free(date);
 date:
  mailimap_env_date_free(date);
 err:
  return res;
}

static int mailimap_envelope_parse_workaround_qq_mail(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                                      size_t * indx,
                                                      struct mailimap_envelope ** result,
                                                      size_t progr_rate,
                                                      progress_function * progr_fun)
{
  size_t cur_token;
  char * date;
  char * subject;
  struct mailimap_env_from * from;
  char * in_reply_to;
  char * message_id;
  struct mailimap_envelope * envelope;
  int r;
  int res;
  char * first_string;
  char * second_string;
  int has_first_string;
  int has_second_string;

  date = NULL;
  subject = NULL;
  from = NULL;
  in_reply_to = NULL;
  message_id = NULL;
  first_string = NULL;
  second_string = NULL;
  res = MAILIMAP_ERROR_PARSE;

  cur_token = * indx;

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

  r = mailimap_env_date_parse(fd, buffer, parser_ctx, &cur_token, &date,
                              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 date;
  }

  r = mailimap_env_subject_parse(fd, buffer, parser_ctx, &cur_token, &subject,
                                 progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto date;
  }

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

  r = mailimap_env_from_parse(fd, buffer, parser_ctx, &cur_token, &from,
                              progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
    res = r;
    goto subject;
  }

  if (from == NULL) {
    clist * list;
    struct mailimap_address * addr;

    addr = mailimap_address_new("", NULL, "", "");
    if (addr == NULL) {
      goto subject;
    }

    list = clist_new();
    if (list == NULL) {
      mailimap_address_free(addr);
      goto subject;
    }

    r = clist_append(list, addr);
    if (r < 0) {
      clist_free(list);
      mailimap_address_free(addr);
      goto subject;
    }

    from = mailimap_env_from_new(list);
    if (from == NULL) {
      clist_free(list);
      mailimap_address_free(addr);
      goto subject;
    }
  }

  while (1) {
    clist * list;

    r = mailimap_space_parse(fd, buffer, &cur_token);
    if (r == MAILIMAP_ERROR_PARSE) {
      break;
    }
    else if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto from;
    }

    r = mailimap_address_list_parse(fd, buffer, parser_ctx, &cur_token, &list,
                                    progr_rate, progr_fun);
    if (r == MAILIMAP_ERROR_PARSE) {
      break;
    }
    else if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto from;
    }
    if (list != NULL) {
      clist_foreach(list, (clist_func) mailimap_address_free, NULL);
      clist_free(list);
    }
  }

  has_first_string = 0;
  r = mailimap_nstring_parse(fd, buffer, parser_ctx, &cur_token, &first_string, NULL,
                                progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR) {
    has_first_string = 1;
  }
  else if (r == MAILIMAP_ERROR_PARSE) {
    // Do nothing.
  }
  else {
    res = r;
    goto from;
  }

  r = mailimap_space_parse(fd, buffer, &cur_token);
  // ignore errors.

  has_second_string = 0;
  r = mailimap_nstring_parse(fd, buffer, parser_ctx, &cur_token, &second_string, NULL,
                             progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR) {
    has_second_string = 1;
  }
  else if (r == MAILIMAP_ERROR_PARSE) {
    // Do nothing.
  }
  else {
    res = r;
    goto first_string;
  }

  if (has_first_string && has_second_string) {
    in_reply_to = first_string;
    message_id = second_string;
  }
  else if (has_first_string) {
    message_id = first_string;
  }

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto second_string;
  }

  envelope = mailimap_envelope_new(date, subject, from, NULL, NULL, NULL,
                                   NULL, NULL, in_reply_to, message_id);
  if (envelope == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto second_string;
  }

  * result = envelope;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

second_string:
  free(second_string);
first_string:
  free(first_string);
from:
  mailimap_env_from_free(from);
subject:
  mailimap_env_subject_free(date);
date:
  mailimap_env_date_free(date);
err:
  return res;
}

/*
  "(" 1*address ")" / nil
*/

static int mailimap_address_list_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				       size_t * indx,
				       clist ** result,
				       size_t progr_rate,
				       progress_function * progr_fun)
{
  size_t cur_token;
  clist * address_list;
  int r;
  int res;
  
  cur_token = * indx;

  address_list = NULL;

  r = mailimap_nil_parse(fd, buffer, parser_ctx, &cur_token);
  switch (r) {
  case MAILIMAP_NO_ERROR:
    address_list = NULL;
    break;
 
  case MAILIMAP_ERROR_PARSE:
    r = mailimap_oparenth_parse(fd, buffer, parser_ctx, &cur_token);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto err;
    }
				   
    r = mailimap_struct_multiple_parse(fd, buffer, parser_ctx, &cur_token, &address_list,
				       (mailimap_struct_parser *)
				       mailimap_address_parse,
				       (mailimap_struct_destructor *)
				       mailimap_address_free,
				       progr_rate, progr_fun);
    if (r == MAILIMAP_ERROR_PARSE) {
      address_list = NULL;
    }
    else if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto err;
    }
    
    r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto address_list;
    }

    break;
    
  default:
    res = r;
    goto err;
  }

  * result = address_list;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 address_list:
  if (address_list) {
    clist_foreach(address_list, (clist_func) mailimap_address_free, NULL);
    clist_free(address_list);
  }
 err:
  return res;
}

/*
   env-bcc         = "(" 1*address ")" / nil
*/

static int
mailimap_env_bcc_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
		       size_t * indx, struct mailimap_env_bcc ** result,
		       size_t progr_rate,
		       progress_function * progr_fun)
{
  clist * list;
  size_t cur_token;
  struct mailimap_env_bcc * env_bcc;
  int r;
  int res;

  cur_token = * indx;
  list = NULL;

  r = mailimap_address_list_parse(fd, buffer, parser_ctx, &cur_token, &list,
				  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  env_bcc = mailimap_env_bcc_new(list);
  if (env_bcc == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * indx = cur_token;
  * result = env_bcc;

  return MAILIMAP_NO_ERROR;

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

/*
   env-cc          = "(" 1*address ")" / nil
*/

static int
mailimap_env_cc_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
		      size_t * indx, struct mailimap_env_cc ** result,
		      size_t progr_rate,
		      progress_function * progr_fun)
{
  clist * list;
  size_t cur_token;
  struct mailimap_env_cc * env_cc;
  int r;
  int res;

  cur_token = * indx;
  list = NULL;

  r = mailimap_address_list_parse(fd, buffer, parser_ctx, &cur_token, &list,
				  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  env_cc = mailimap_env_cc_new(list);
  if (env_cc == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * indx = cur_token;
  * result = env_cc;

  return MAILIMAP_NO_ERROR;

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

/*
   env-date        = nstring
*/

static int mailimap_env_date_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx, char ** result,
				   size_t progr_rate,
				   progress_function * progr_fun)
{
  return mailimap_nstring_parse(fd, buffer, parser_ctx, indx, result, NULL,
				progr_rate, progr_fun);
}

/*
   env-from        = "(" 1*address ")" / nil
*/

static int
mailimap_env_from_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			size_t * indx, struct mailimap_env_from ** result,
			size_t progr_rate,
			progress_function * progr_fun)
{
  clist * list;
  size_t cur_token;
  struct mailimap_env_from * env_from;
  int r;
  int res;

  cur_token = * indx;
  list = NULL;
  
  r = mailimap_address_list_parse(fd, buffer, parser_ctx, &cur_token, &list,
				  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  env_from = mailimap_env_from_new(list);
  if (env_from == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * indx = cur_token;
  * result = env_from;

  return MAILIMAP_NO_ERROR;

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

/*
   env-in-reply-to = nstring
*/

static int mailimap_env_in_reply_to_parse(mailstream * fd,
					  MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					  size_t * indx, char ** result,
					  size_t progr_rate,
					  progress_function * progr_fun)
{
  return mailimap_nstring_parse(fd, buffer, parser_ctx, indx, result, NULL,
				progr_rate, progr_fun);
}

/*
   env-message-id  = nstring
*/

static int mailimap_env_message_id_parse(mailstream * fd,
					 MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					 size_t * indx, char ** result,
					 size_t progr_rate,
					 progress_function * progr_fun)
{
  return mailimap_nstring_parse(fd, buffer, parser_ctx, indx, result, NULL,
				progr_rate, progr_fun);
}

/*
   env-reply-to    = "(" 1*address ")" / nil
*/

static int
mailimap_env_reply_to_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_env_reply_to ** result,
			    size_t progr_rate,
			    progress_function * progr_fun)
{
  clist * list;
  size_t cur_token;
  struct mailimap_env_reply_to * env_reply_to;
  int r;
  int res;

  cur_token = * indx;
  list = NULL;
  
  r = mailimap_address_list_parse(fd, buffer, parser_ctx, &cur_token, &list,
				  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  env_reply_to = mailimap_env_reply_to_new(list);
  if (env_reply_to == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * indx = cur_token;
  * result = env_reply_to;

  return MAILIMAP_NO_ERROR;

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

/*
   env-sender      = "(" 1*address ")" / nil
*/


static int
mailimap_env_sender_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			  size_t * indx, struct mailimap_env_sender ** result,
			  size_t progr_rate,
			  progress_function * progr_fun)
{
  clist * list;
  size_t cur_token;
  struct mailimap_env_sender * env_sender;
  int r;
  int res;

  cur_token = * indx;
  list = NULL;
  
  r = mailimap_address_list_parse(fd, buffer, parser_ctx, &cur_token, &list,
				  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  env_sender = mailimap_env_sender_new(list);
  if (env_sender == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * indx = cur_token;
  * result = env_sender;

  return MAILIMAP_NO_ERROR;

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


/*
   env-subject     = nstring
*/

static int mailimap_env_subject_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				      size_t * indx, char ** result,
				      size_t progr_rate,
				      progress_function * progr_fun)
{
  return mailimap_nstring_parse(fd, buffer, parser_ctx, indx, result, NULL,
				progr_rate, progr_fun);
}


/*
   env-to          = "(" 1*address ")" / nil
*/

static int mailimap_env_to_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				 size_t * indx,
				 struct mailimap_env_to ** result,
				 size_t progr_rate,
				 progress_function * progr_fun)
{
  clist * list;
  size_t cur_token;
  struct mailimap_env_to * env_to;
  int r;
  int res;

  cur_token = * indx;
  list = NULL;
  
  r = mailimap_address_list_parse(fd, buffer, parser_ctx, &cur_token, &list,
				  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  env_to = mailimap_env_to_new(list);
  if (env_to == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * indx = cur_token;
  * result = env_to;

  return MAILIMAP_NO_ERROR;

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


/*
  UNIMPLEMENTED
   examine         = "EXAMINE" SP mailbox
*/

/*
  UNIMPLEMENTED
   fetch           = "FETCH" SP set SP ("ALL" / "FULL" / "FAST" / fetch-att /
                     "(" fetch-att *(SP fetch-att) ")")
*/

/*
  UNIMPLEMENTED
   fetch-att       = "ENVELOPE" / "FLAGS" / "INTERNALDATE" /
                     "RFC822" [".HEADER" / ".SIZE" / ".TEXT"] /
                     "BODY" ["STRUCTURE"] / "UID" /
                     "BODY" [".PEEK"] section ["<" number "." nz-number ">"]
*/

/*
   flag            = "\Answered" / "\Flagged" / "\Deleted" /
                     "\Seen" / "\Draft" / flag-keyword / flag-extension
                       ; Does not include "\Recent"
*/

static int mailimap_flag_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_flag ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  struct mailimap_flag * flag;
  size_t cur_token;
  char * flag_keyword;
  char * flag_extension;
  int type;
  int r;
  int res;
  
  cur_token = * indx;

  flag_keyword = NULL;
  flag_extension = NULL;

  type = mailimap_flag_get_token_value(fd, buffer, &cur_token);
  if (type == -1) {
    r = mailimap_flag_keyword_parse(fd, buffer, parser_ctx, &cur_token, &flag_keyword,
				    progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_FLAG_KEYWORD;

    if (r == MAILIMAP_ERROR_PARSE) {
      r = mailimap_flag_extension_parse(fd, buffer, parser_ctx, &cur_token, 
					&flag_extension,
					progr_rate, progr_fun);
      type = MAILIMAP_FLAG_EXTENSION;
    }

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

  flag = mailimap_flag_new(type, flag_keyword, flag_extension);
  if (flag == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = flag;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (flag_keyword != NULL)
    mailimap_flag_keyword_free(flag_keyword);
  if (flag_extension != NULL)
    mailimap_flag_extension_free(flag_extension);
 err:
  return res;
}

/*
   flag-extension  = "\" atom
                       ; Future expansion.  Client implementations
                       ; MUST accept flag-extension flags.  Server
                       ; implementations MUST NOT generate
                       ; flag-extension flags except as defined by
                       ; future standard or standards-track
                       ; revisions of this specification.
*/

static int mailimap_flag_extension_parse(mailstream * fd,
					 MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					 size_t * indx,
					 char ** result,
					 size_t progr_rate,
					 progress_function * progr_fun)
{
  size_t cur_token;
  char * atom;
  int r;
  
  cur_token = * indx;

  r = mailimap_char_parse(fd, buffer, &cur_token, '\\');
  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_char_parse(fd, buffer, &cur_token, '/');
  }
  if (r != MAILIMAP_NO_ERROR)
    return r;

  atom = NULL;
  r = mailimap_atom_parse(fd, buffer, parser_ctx, &cur_token, &atom,
			  progr_rate, progr_fun);
  if (r == MAILIMAP_ERROR_PARSE) {
    /* workaround for binc IMAP */
    r = mailimap_char_parse(fd, buffer, &cur_token, '*');
    if (r == MAILIMAP_NO_ERROR) {
      atom = malloc(2);
      if (atom == NULL)
        return MAILIMAP_ERROR_MEMORY;
      
      atom[0] = '*';
      atom[1] = '\0';
    }
  }
  if (r != MAILIMAP_NO_ERROR) {
	free(atom);
    return r;
  }
  
  * result = atom;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
   flag-fetch      = flag / "\Recent"
*/

static int
mailimap_flag_fetch_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			  size_t * indx,
			  struct mailimap_flag_fetch ** result,
			  size_t progr_rate,
			  progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_flag * flag;
  struct mailimap_flag_fetch * flag_fetch;
  int type;
  int r;
  int res;

  cur_token = * indx;

  flag = NULL;

  type = MAILIMAP_FLAG_FETCH_ERROR; /* XXX - removes a gcc warning */
  
  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "\\Recent");
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_FLAG_FETCH_RECENT;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_flag_parse(fd, buffer, parser_ctx, &cur_token, &flag,
			    progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_FLAG_FETCH_OTHER;
  }

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

  flag_fetch = mailimap_flag_fetch_new(type, flag);
  if (flag_fetch == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * indx = cur_token;
  * result = flag_fetch;

  return MAILIMAP_NO_ERROR;

 free:
  if (flag != NULL)
    mailimap_flag_free(flag);
 err:
  return res;
}

/*
   flag-keyword    = atom
   WARNING : parse astring instead of atom to workaround RockLiffe server :
   http://www.rockliffe.com/
*/

static int mailimap_flag_keyword_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				       size_t * indx,
				       char ** result,
				       size_t progr_rate,
				       progress_function * progr_fun)
{
#if 0
  return mailimap_atom_parse(fd, buffer, parser_ctx, indx, result,
      progr_rate, progr_fun);
#endif
  return mailimap_astring_parse(fd, buffer, parser_ctx, indx, result,
      progr_rate, progr_fun);
}

/*
   flag-list       = "(" [flag *(SP flag)] ")"
*/

static int mailimap_flag_list_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx,
				    struct mailimap_flag_list ** result,
				    size_t progr_rate,
				    progress_function * progr_fun)
{
  size_t cur_token;
  clist * list;
  struct mailimap_flag_list * flag_list;
  int r;
  int res;

  list = NULL;
  cur_token = * indx;

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

  r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token, &list,
					(mailimap_struct_parser *)
					mailimap_flag_parse,
					(mailimap_struct_destructor *)
					mailimap_flag_free,
					progr_rate, progr_fun);

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

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free;
  }

  flag_list = mailimap_flag_list_new(list);
  if (flag_list == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = flag_list;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (list != NULL) {
    clist_foreach(list, (clist_func) mailimap_flag_free, NULL);
    clist_free(list);
  }
 err:
  return res;
}

/*
   flag-perm       = flag / "\*"
*/

static int
mailimap_flag_perm_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			 size_t * indx,
			 struct mailimap_flag_perm ** result,
			 size_t progr_rate,
			 progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_flag_perm * flag_perm;
  struct mailimap_flag * flag;
  int type;
  int r;
  int res;

  flag = NULL;
  cur_token = * indx;
  type = MAILIMAP_FLAG_PERM_ERROR; /* XXX - removes a gcc warning */
  
  r = mailimap_token_case_insensitive_parse(fd, buffer,
                                            &cur_token, "\\*");
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_FLAG_PERM_ALL;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_flag_parse(fd, buffer, parser_ctx, &cur_token, &flag,
                            progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_FLAG_PERM_FLAG;

  }

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

  flag_perm = mailimap_flag_perm_new(type, flag);
  if (flag_perm == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = flag_perm;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (flag != NULL)
    mailimap_flag_free(flag);
 err:
  return res;
}

/*
   greeting        = "*" SP (resp-cond-auth / resp-cond-bye) CRLF
*/

int mailimap_greeting_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_greeting ** result,
			    size_t progr_rate,
			    progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_resp_cond_auth * resp_cond_auth;
  struct mailimap_resp_cond_bye * resp_cond_bye;
  struct mailimap_greeting * greeting;
  int type;
  int r;
  int res;

  cur_token = * indx;
  resp_cond_bye = NULL;
  resp_cond_auth = NULL;

  r = mailimap_star_parse(fd, buffer, parser_ctx, &cur_token);
  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;
  }

  type = MAILIMAP_GREETING_RESP_COND_ERROR; /* XXX - removes a gcc warning */
  
  r = mailimap_resp_cond_auth_parse(fd, buffer, parser_ctx, &cur_token, &resp_cond_auth,
				    progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_GREETING_RESP_COND_AUTH;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_resp_cond_bye_parse(fd, buffer, parser_ctx, &cur_token,
				     &resp_cond_bye,
				     progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_GREETING_RESP_COND_BYE;
  }

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

  r = mailimap_crlf_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free;
  }

  greeting = mailimap_greeting_new(type, resp_cond_auth, resp_cond_bye);
  if (greeting == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = greeting;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (resp_cond_auth)
    mailimap_resp_cond_auth_free(resp_cond_auth);
  if (resp_cond_bye)
    mailimap_resp_cond_bye_free(resp_cond_bye);
 err:
  return res;
}

/*
   header-fld-name = astring
*/

static int
mailimap_header_fld_name_parse(mailstream * fd,
			       MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       char ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  return mailimap_astring_parse(fd, buffer, parser_ctx, indx, result,
				progr_rate, progr_fun);
}

/*
   header-list     = "(" header-fld-name *(SP header-fld-name) ")"
*/

static int
mailimap_header_list_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			   size_t * indx,
			   struct mailimap_header_list ** result,
			   size_t progr_rate,
			   progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_header_list * header_list;
  clist * list;
  int r;
  int res;

  cur_token = * indx;

  list = NULL;

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

  r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token, &list,
					(mailimap_struct_parser *)
					mailimap_header_fld_name_parse,
					(mailimap_struct_destructor *)
					mailimap_header_fld_name_free,
					progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free;
  }

  header_list = mailimap_header_list_new(list);
  if (header_list == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = header_list;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

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

/*
UNIMPLEMENTED
   list            = "LIST" SP mailbox SP list-mailbox

UNIMPLEMENTED
   list-mailbox    = 1*list-char / string

UNIMPLEMENTED
   list-char       = ATOM-CHAR / list-wildcards / resp-specials
*/

/*
   list-wildcards  = "%" / "*"
*/

static int is_list_wildcards(char ch)
{
  switch (ch) {
  case '%':
  case '*':
    return TRUE;
  }
  return FALSE;
}


/*
   literal         = "{" number "}" CRLF *CHAR8
                       ; Number represents the number of CHAR8s
*/

#define MAX_READ_PROGRESS 65536

static int mailimap_literal_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                           size_t * indx, char ** result,
                                           size_t * result_len,
                                           size_t progr_rate,
                                           progress_function * progr_fun,
                                           mailprogress_function * body_progr_fun,
                                           mailprogress_function * items_progr_fun,
                                           void * context,
                                           mailimap_msg_att_handler * msg_att_handler,
                                           void * msg_att_context)
{
  size_t cur_token;
  uint32_t number;
  MMAPString * literal;
  uint32_t left;
  int r;
  int res;
  size_t number_token;
  bool use_msg_body_handler;
  
  cur_token = * indx;
  use_msg_body_handler = (parser_ctx->msg_body_handler != NULL
                          && parser_ctx->msg_body_parse_in_progress);
  
  r = mailimap_oaccolade_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  number_token = cur_token;
  
  r = mailimap_number_parse(fd, buffer, &cur_token, &number);
  if (r == MAILIMAP_ERROR_PARSE) {
    // workaround issue with Free servers
    r = mailimap_minus_parse(fd, buffer, parser_ctx, &cur_token);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto err;
    }
    r = mailimap_number_parse(fd, buffer, &cur_token, &number);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto err;
    }
    number = 0;
  }
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  r = mailimap_caccolade_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  r = mailimap_crlf_parse(fd, buffer, parser_ctx, &cur_token);
	if (r == MAILIMAP_ERROR_PARSE) {
		/* workaround for Lotus Domino IMAP server */
		mailimap_space_parse(fd, buffer, &cur_token);
		mailimap_space_parse(fd, buffer, &cur_token);
	}
	else if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  if (use_msg_body_handler) {
    literal = mmap_string_new("");
  }
  else {
    literal = mmap_string_sized_new(number);
  }
  if (literal == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto err;
  }
  
  left = (uint32_t) (buffer->len - cur_token);
  
  if (left >= number) {
    if (number > 0) {
      if (use_msg_body_handler) {
        if (!parser_ctx->msg_body_handler(parser_ctx->msg_body_att_type, parser_ctx->msg_body_section,
                                          buffer->str + cur_token, number,
                                          parser_ctx->msg_body_handler_context)) {
          res = MAILIMAP_ERROR_MEMORY;
          goto free_literal;
        }
      }
      else {
        if (mmap_string_append_len(literal, buffer->str + cur_token,
                                   number) == NULL) {
          res = MAILIMAP_ERROR_MEMORY;
          goto free_literal;
        }
      }
    }
    if (progr_rate != 0) {
      if (progr_fun != NULL) {
        progr_fun(number, number);
      }
      if (body_progr_fun != NULL) {
        body_progr_fun(number, number, context);
      }
    }
    cur_token = cur_token + number;
  }
  else {
    uint32_t needed;
    uint32_t current_prog = 0;
    uint32_t last_prog = 0;
    
    needed = number - left;
    if (left > 0) {
      if (use_msg_body_handler) {
        if (!parser_ctx->msg_body_handler(parser_ctx->msg_body_att_type, parser_ctx->msg_body_section,
                                          buffer->str + cur_token, left,
                                          parser_ctx->msg_body_handler_context)) {
          res = MAILIMAP_ERROR_MEMORY;
          goto free_literal;
        }
      }
      else {
        if (mmap_string_append_len(literal, buffer->str + cur_token,
                                   left) == NULL) {
          res = MAILIMAP_ERROR_MEMORY;
          goto free_literal;
        }
      }
    }
    current_prog = left;
    
    while (needed > 0) {
      ssize_t read_bytes;
      size_t bytes_to_read;
      
      bytes_to_read = needed;
      if (bytes_to_read > MAX_READ_PROGRESS) {
        bytes_to_read = MAX_READ_PROGRESS;
      }
      if (fd == NULL) {
        res = MAILIMAP_ERROR_NEEDS_MORE_DATA;
        goto free_literal;
      }

      char read_buffer[MAX_READ_PROGRESS];
      if (use_msg_body_handler) {
        read_bytes = mailstream_read(fd, read_buffer, bytes_to_read);
        if (read_bytes > 0) {
          if (!parser_ctx->msg_body_handler(parser_ctx->msg_body_att_type, parser_ctx->msg_body_section,
                                            read_buffer, read_bytes,
                                            parser_ctx->msg_body_handler_context)) {
            res = MAILIMAP_ERROR_MEMORY;
            goto free_literal;
          }
        }
      }
      else {
        read_bytes = mailstream_read(fd, literal->str + literal->len, bytes_to_read);
        if (read_bytes > 0) {
          literal->len += read_bytes;
        }
      }

      if (read_bytes == -1) {
        res = MAILIMAP_ERROR_STREAM;
        goto free_literal;
      }
      needed -= read_bytes;

      if (needed > 0 && mailimap_parser_context_is_rambler_workaround_enabled(parser_ctx)) {
        /* workaround issue with Rambler IMAP server */
        char const * search_beg = use_msg_body_handler ? read_buffer : literal->str;
        char const * search_end = use_msg_body_handler ? read_buffer + read_bytes : literal->str + literal->len;
        char tag_response_end[] = " OK completed\r\n";
        size_t tag_response_end_len = sizeof(tag_response_end) - 1;
        /* Looking backward for pattern " UID \d+)\r\n\d+ OK completed\r\n$" */
        if (search_end - search_beg >= tag_response_end_len &&
            memcmp(search_end - tag_response_end_len, tag_response_end, tag_response_end_len) == 0) {
          search_end -= tag_response_end_len;
          /* match tag */
          while (search_end > search_beg && is_digit(*(search_end - 1))) search_end--;
          /* match closing parenthesis and CRLF */
          if (search_end > search_beg + 3) search_end -= 3;
          if (memcmp(search_end, ")\r\n", 3) == 0) {
            /* match uid */
            while (search_end > search_beg && is_digit(*(search_end - 1))) search_end--;
            if (search_end > search_beg + 5) search_end -= 5;
            if (memcmp(search_end, " UID ", 5) == 0) {
              /* got invalid server response, no more data will received */
              res = MAILIMAP_ERROR_PARSE;
              goto free_literal;
            }
          }
        }
      }

      current_prog += read_bytes;
      if (current_prog - last_prog > progr_rate) {
        if (progr_fun != NULL) {
          progr_fun(current_prog, number);
        }
        if (body_progr_fun != NULL) {
          body_progr_fun(current_prog, number, context);
        }
        last_prog = current_prog;
      }
    }
    
    if (!use_msg_body_handler) {
      literal->str[number] = 0;
    }
    
    if (mmap_string_truncate(buffer, number_token) == NULL) {
      res = MAILIMAP_ERROR_MEMORY;
      goto free_literal;
    }
    
    if (mmap_string_append(buffer, "0}\r\n") == NULL) {
      res = MAILIMAP_ERROR_MEMORY;
      goto free_literal;
    }
    
    cur_token = number_token + 4;
  }
  if (progr_rate != 0) {
    if (progr_fun != NULL) {
      progr_fun(number, number);
    }
    if (body_progr_fun != NULL) {
      body_progr_fun(number, number, context);
    }
  }
  
  if (fd == NULL) {
    if (!has_crlf(buffer, cur_token)) {
      res = MAILIMAP_ERROR_NEEDS_MORE_DATA;
      goto free_literal;
    }
  }
  else {
    if (mailstream_read_line_append(fd, buffer) == NULL) {
      res = MAILIMAP_ERROR_STREAM;
      goto free_literal;
    }
  }
  
  if (mmap_string_ref(literal) < 0) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_literal;
  }
  
  * result = literal->str;
  if (result_len != NULL)
    * result_len = literal->len;
  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;
  
free_literal:
  mmap_string_free(literal);
err:
  return res;
}

/*
  UNIMPLEMENTED
   login           = "LOGIN" SP userid SP password

   UNIMPLEMENTED
   lsub            = "LSUB" SP mailbox SP list-mailbox
*/

/*
   mailbox         = "INBOX" / astring
                       ; INBOX is case-insensitive.  All case variants of
                       ; INBOX (e.g. "iNbOx") MUST be interpreted as INBOX
                       ; not as an astring.  An astring which consists of
                       ; the case-insensitive sequence "I" "N" "B" "O" "X"
                       ; is considered to be INBOX and not an astring.
                       ;  Refer to section 5.1 for further
                       ; semantic details of mailbox names.
*/

int
mailimap_mailbox_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
		       size_t * indx, char ** result,
		       size_t progr_rate,
		       progress_function * progr_fun)
{
  size_t cur_token;
  char * name;
  int r;

  cur_token = * indx;

  r = mailimap_astring_parse(fd, buffer, parser_ctx, &cur_token, &name,
			     progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = name;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

static int
mailimap_mailbox_gmail_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
    size_t * indx, char ** result,
    size_t progr_rate,
    progress_function * progr_fun)
{
  size_t cur_token;
  char * name;
  int r;
  clist * astring_list;
  size_t parsed_length;
  clistiter * cur;
  
  cur_token = * indx;

  r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token, &astring_list,
					(mailimap_struct_parser *)
					mailimap_astring_parse,
					(mailimap_struct_destructor *)
					mailimap_astring_free,
					progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  for(cur = clist_begin(astring_list) ; cur != NULL ; cur = clist_next(cur)) {
    char * str;
    
    str = clist_content(cur);
    mailimap_astring_free(str);
  }
  clist_free(astring_list);
  
  parsed_length = cur_token - * indx;
  name = malloc(parsed_length + 1);
  if (name == NULL) {
    return MAILIMAP_ERROR_MEMORY;
  }
  memcpy(name, buffer->str + * indx, parsed_length);
  name[parsed_length] = 0;
  
  * result = name;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}


/*
   mailbox-data    =  "FLAGS" SP flag-list / "LIST" SP mailbox-list /
                      "LSUB" SP mailbox-list / "SEARCH" *(SP nz-number) /
                      "STATUS" SP mailbox SP "("
                      [status-att SP number *(SP status-att SP number)] ")" /
                      number SP "EXISTS" / number SP "RECENT"
*/

/*
  "FLAGS" SP flag-list
*/

static int
mailimap_mailbox_data_flags_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				  size_t * indx,
				  struct mailimap_flag_list ** result,
				  size_t progr_rate,
				  progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_flag_list * flag_list;
  int r;

  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "FLAGS");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  flag_list = NULL;
  r = mailimap_flag_list_parse(fd, buffer, parser_ctx, &cur_token, &flag_list,
			       progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = flag_list;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}


/*
  "LIST" SP mailbox-list
*/

static int
mailimap_mailbox_data_list_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				 size_t * indx,
				 struct mailimap_mailbox_list ** result,
				 size_t progr_rate,
				 progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_mailbox_list * mb_list;
  int r;

  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "LIST");
  if (r != MAILIMAP_NO_ERROR) {
    return r;
  }

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

  r = mailimap_mailbox_list_parse(fd, buffer, parser_ctx, &cur_token, &mb_list,
				  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    return r;
  }

  * result = mb_list;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  "LSUB" SP mailbox-list
*/

static int
mailimap_mailbox_data_lsub_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				 size_t * indx,
				 struct mailimap_mailbox_list ** result,
				 size_t progr_rate,
				 progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_mailbox_list * mb_list;
  int r;

  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "LSUB");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_mailbox_list_parse(fd, buffer, parser_ctx, &cur_token, &mb_list,
				  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = mb_list;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  "SEARCH" *(SP nz-number)
*/


static int
mailimap_mailbox_data_search_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx,
				   clist ** result,
				   size_t progr_rate,
				   progress_function * progr_fun)
{
  size_t cur_token;
  size_t final_token;
  clist * number_list;
  int r;
  
  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer,
					    &cur_token, "SEARCH");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  final_token = cur_token;
  number_list = NULL;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r == MAILIMAP_NO_ERROR) {
    r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token, &number_list,
					  (mailimap_struct_parser *)
					  mailimap_nz_number_alloc_parse,
					  (mailimap_struct_destructor *)
					  mailimap_number_alloc_free,
					  progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      final_token = cur_token;
  }
  
  * result = number_list;
  * indx = final_token;

  return MAILIMAP_NO_ERROR;
}

/*
  "STATUS" SP mailbox SP "("
  [status-att SP number *(SP status-att SP number)] ")"
*/

/*
  status-att SP number
*/

static int
mailimap_status_info_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			   size_t * indx,
			   struct mailimap_status_info **
			   result,
			   size_t progr_rate,
			   progress_function * progr_fun)
{
  size_t cur_token;
  int status_att;
  uint32_t value;
  struct mailimap_status_info * info;
  int r;
  
  cur_token = * indx;
  value = 0;

  r = mailimap_status_att_parse(fd, buffer, parser_ctx, &cur_token, &status_att);
  if (r == MAILIMAP_NO_ERROR) {
    /* Force parse error when HIGHESTMODSEQ is parsed */
    if (status_att == MAILIMAP_STATUS_ATT_HIGHESTMODSEQ) {
      r = MAILIMAP_ERROR_PARSE;
    }
  }
  if (r == MAILIMAP_ERROR_PARSE) {
    struct mailimap_extension_data * ext_data;
    
    cur_token = * indx;
    r = mailimap_extension_data_parse(MAILIMAP_EXTENDED_PARSER_STATUS_ATT,
        fd, buffer, parser_ctx, &cur_token, &ext_data,
        progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR) {
      info = mailimap_status_info_new(MAILIMAP_STATUS_ATT_EXTENSION, 0, ext_data);
      if (info == NULL)
        return MAILIMAP_ERROR_MEMORY;

      * result = info;
      * indx = cur_token;
      
      return MAILIMAP_NO_ERROR;
    }
  }
  
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_number_parse(fd, buffer, &cur_token, &value);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  info = mailimap_status_info_new(status_att, value, NULL);
  if (info == NULL)
    return MAILIMAP_ERROR_MEMORY;

  * result = info;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  "STATUS" SP mailbox SP "("
  [status-att SP number *(SP status-att SP number)] ")"
*/

static int
mailimap_mailbox_data_status_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx, struct
				   mailimap_mailbox_data_status ** result,
				   size_t progr_rate,
				   progress_function * progr_fun)
{
  size_t cur_token;
  char * mb;
  clist * status_info_list;
  struct mailimap_mailbox_data_status * data_status;
  int r;
  int res;

  cur_token = * indx;
  mb = NULL;
  status_info_list = NULL;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "STATUS");
  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_gmail_parse(fd, buffer, parser_ctx, &cur_token, &mb,
      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;
  }

  r = mailimap_oparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto mailbox;
  }
  
  r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token,
					&status_info_list,
					(mailimap_struct_parser *)
					mailimap_status_info_parse,
					(mailimap_struct_destructor *)
					mailimap_status_info_free,
					progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
    res = r;
    goto mailbox;
  }

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto status_info_list;
  }

  data_status = mailimap_mailbox_data_status_new(mb, status_info_list);
  if (data_status == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto status_info_list;
  }

  * result = data_status;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 status_info_list:
  if (status_info_list != NULL) {
    clist_foreach(status_info_list, (clist_func) mailimap_status_info_free,
		  NULL);
    clist_free(status_info_list);
  }
 mailbox:
  mailimap_mailbox_free(mb);
 err:
  return res;
}

/*
  number SP "EXISTS"
*/

static int
mailimap_mailbox_data_exists_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx,
				   uint32_t * result)
{
  size_t cur_token;
  uint32_t number;
  int r;
  
  cur_token = * indx;

  r = mailimap_number_parse(fd, buffer, &cur_token, &number);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "EXISTS");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = number;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  number SP "RECENT"
*/

static int
mailimap_mailbox_data_recent_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx,
				   uint32_t * result)
{
  size_t cur_token;
  uint32_t number;
  int r;
  
  cur_token = * indx;

  r = mailimap_number_parse(fd, buffer, &cur_token, &number);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_token_case_insensitive_parse(fd, buffer,
					    &cur_token, "RECENT");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = number;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
   mailbox-data    =  "FLAGS" SP flag-list / "LIST" SP mailbox-list /
                      "LSUB" SP mailbox-list / "SEARCH" *(SP nz-number) /
                      "STATUS" SP mailbox SP "("
                      [status-att SP number *(SP status-att SP number)] ")" /
                      number SP "EXISTS" / number SP "RECENT"
*/

static int
mailimap_mailbox_data_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_mailbox_data ** result,
			    size_t progr_rate,
			    progress_function * progr_fun)
{
  int type;
  struct mailimap_flag_list * data_flags;
  struct mailimap_mailbox_list * data_list;
  struct mailimap_mailbox_list * data_lsub;
  clist * data_search; 
  struct mailimap_mailbox_data_status * data_status;
  uint32_t data_exists;
  uint32_t data_recent;
  struct mailimap_extension_data * data_extension;

  struct mailimap_mailbox_data * mailbox_data;
  size_t cur_token;
  int r;
  int res;

  cur_token = * indx;

  data_flags = NULL;
  data_list = NULL;
  data_lsub = NULL;
  data_search = NULL;
  data_status = NULL;
  data_exists = 0;
  data_recent = 0;
  data_extension = NULL;

  type = MAILIMAP_MAILBOX_DATA_ERROR; /* XXX - removes a gcc warning */
  
  r = mailimap_mailbox_data_flags_parse(fd, buffer, parser_ctx, &cur_token,
					&data_flags,
					progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_MAILBOX_DATA_FLAGS;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_mailbox_data_list_parse(fd, buffer, parser_ctx, &cur_token,
					 &data_list,
					 progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MAILBOX_DATA_LIST;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_mailbox_data_lsub_parse(fd, buffer, parser_ctx, &cur_token,
					 &data_lsub,
					 progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MAILBOX_DATA_LSUB;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_mailbox_data_search_parse(fd, buffer, parser_ctx, &cur_token,
					   &data_search,
					   progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MAILBOX_DATA_SEARCH;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_mailbox_data_status_parse(fd, buffer, parser_ctx, &cur_token,
					   &data_status,
					   progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MAILBOX_DATA_STATUS;
  }
  
  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_mailbox_data_exists_parse(fd, buffer, parser_ctx, &cur_token,
					   &data_exists);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MAILBOX_DATA_EXISTS;
  }
  
  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_mailbox_data_recent_parse(fd, buffer, parser_ctx, &cur_token,
					   &data_recent);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MAILBOX_DATA_RECENT;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_extension_data_parse(MAILIMAP_EXTENDED_PARSER_MAILBOX_DATA,
              fd, buffer, parser_ctx, &cur_token, &data_extension, progr_rate,
              progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MAILBOX_DATA_EXTENSION_DATA;
  }

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

  mailbox_data = mailimap_mailbox_data_new(type, data_flags, data_list,
      data_lsub, data_search,
      data_status,
      data_exists, data_recent,
      data_extension);
  
  if (mailbox_data == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = mailbox_data;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (data_flags != NULL)
    mailimap_flag_list_free(data_flags);
  if (data_list != NULL)
    mailimap_mailbox_list_free(data_list);
  if (data_lsub != NULL)
    mailimap_mailbox_list_free(data_lsub);
  if (data_search != NULL)
    mailimap_mailbox_data_search_free(data_search);
  if (data_status != NULL)
    mailimap_mailbox_data_status_free(data_status);
  if (data_extension != NULL)
    mailimap_extension_data_free(data_extension);
 err:
  return res;
}

/*
   mailbox-list    = "(" [mbx-list-flags] ")" SP
                      (DQUOTE QUOTED-CHAR DQUOTE / nil) SP mailbox
*/

/*
  DQUOTE QUOTED-CHAR DQUOTE
*/

static int
mailimap_mailbox_list_quoted_char_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					size_t * indx,
					char * result)
{
  size_t cur_token;
  char ch;
  int r;

  cur_token = * indx;

  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_quoted_char_parse(fd, buffer, parser_ctx, &cur_token, &ch);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * indx = cur_token;
  * result = ch;

  return MAILIMAP_NO_ERROR;
}

int
mailimap_mailbox_list_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_mailbox_list ** result,
			    size_t progr_rate,
			    progress_function * progr_fun)
{
  struct mailimap_mailbox_list * mb_list;
  struct mailimap_mbx_list_flags * mb_flag_list;
  char ch; 
  char * mb;
  size_t cur_token;
  int r;
  int res;

  cur_token = * indx;

  r = mailimap_oparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  mb_flag_list = NULL;
  ch = 0;
  mb = NULL;

  r = mailimap_mbx_list_flags_parse(fd, buffer, parser_ctx, &cur_token,
				    &mb_flag_list, progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
    res = r;
    goto err;
  }

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free_list_flags;
  }

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

  r = mailimap_mailbox_list_quoted_char_parse(fd, buffer, parser_ctx, &cur_token, &ch);
  if (r == MAILIMAP_ERROR_PARSE)
    r = mailimap_nil_parse(fd, buffer, parser_ctx, &cur_token);

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

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

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

  mb_list = mailimap_mailbox_list_new(mb_flag_list, ch, mb);
  if (mb_list == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_mailbox;
  }
  
  * result = mb_list;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free_mailbox:
  mailimap_mailbox_free(mb);
 free_list_flags:
  if (mb_flag_list != NULL)
    mailimap_mbx_list_flags_free(mb_flag_list);
 err:
  return res;
}

/*
   mbx-list-flags  = *(mbx-list-oflag SP) mbx-list-sflag
                     *(SP mbx-list-oflag) /
                     mbx-list-oflag *(SP mbx-list-oflag)
*/

static int
mailimap_mbx_list_flags_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_mbx_list_flags ** result,
			      size_t progr_rate,
			      progress_function * progr_fun)
{
  struct mailimap_mbx_list_flags * mbx_list_flag;
  size_t cur_token;
  clist * oflags;
  clist * oflags_2;
  int sflag;
  int type;
  int r;
  int res;
  size_t final_token;
  int try_sflag;
  
  cur_token = * indx;
  final_token = cur_token;
  
  oflags = clist_new();
  if (oflags == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto err;
  }

  sflag = MAILIMAP_MBX_LIST_SFLAG_ERROR;
  oflags_2 = NULL;

  r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token,
					&oflags_2,
					(mailimap_struct_parser *)
					mailimap_mbx_list_oflag_no_sflag_parse,
					(mailimap_struct_destructor *)
					mailimap_mbx_list_oflag_free,
					progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
    res = r;
    goto free;
  }
  
  try_sflag = 1;
  if (r == MAILIMAP_NO_ERROR) {
    clist_concat(oflags, oflags_2);
    clist_free(oflags_2);
    
    final_token = cur_token;
    try_sflag = 0;
    r = mailimap_space_parse(fd, buffer, &cur_token);
    if (r == MAILIMAP_NO_ERROR)
      try_sflag = 1;
  }
  
  type = MAILIMAP_MBX_LIST_FLAGS_NO_SFLAG;
  if (try_sflag) {
    r = mailimap_mbx_list_sflag_parse(fd, buffer, parser_ctx, &cur_token, &sflag);
    switch (r) {
    case MAILIMAP_ERROR_PARSE:
      type = MAILIMAP_MBX_LIST_FLAGS_NO_SFLAG;
      break;
    
    case MAILIMAP_NO_ERROR:
      type = MAILIMAP_MBX_LIST_FLAGS_SFLAG;
    
      final_token = cur_token;
      r = mailimap_space_parse(fd, buffer, &cur_token);
      if (r == MAILIMAP_NO_ERROR) {
        r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token,
            &oflags_2,
            (mailimap_struct_parser *) mailimap_mbx_list_oflag_parse,
            (mailimap_struct_destructor *) mailimap_mbx_list_oflag_free,
            progr_rate, progr_fun);
        if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
          res = r;
          goto err;
        }
      
        if (r == MAILIMAP_NO_ERROR) {
          clist_concat(oflags, oflags_2);
          clist_free(oflags_2);
          
          final_token = cur_token;
        }
      }
      
      break;

    default:
      res = r;
      goto free;
    }
  }
  
  if ((clist_count(oflags) == 0) && (type == MAILIMAP_MBX_LIST_FLAGS_NO_SFLAG)) {
    res = MAILIMAP_ERROR_PARSE;
    goto free;
  }
  
  cur_token = final_token;
  mbx_list_flag = mailimap_mbx_list_flags_new(type, oflags, sflag);
  if (mbx_list_flag == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = mbx_list_flag;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

free:
  clist_foreach(oflags, (clist_func) mailimap_mbx_list_oflag_free, NULL);
  clist_free(oflags);
err:
  return res;
}

/*
   mbx-list-oflag  = "\Noinferiors" / flag-extension
                       ; Other flags; multiple possible per LIST response
*/

static int
mailimap_mbx_list_oflag_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_mbx_list_oflag ** result,
			      size_t progr_rate,
			      progress_function * progr_fun)
{
  int type;
  size_t cur_token;
  struct mailimap_mbx_list_oflag * oflag;
  char * flag_ext;
  int r;
  int res;
  
  cur_token = * indx;
  flag_ext = NULL;
  type = MAILIMAP_MBX_LIST_OFLAG_ERROR; /* XXX - removes a gcc warning */

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "\\Noinferiors");
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS;
  
  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
                                              "/Noinferiors");
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS;
  }
  
  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_flag_extension_parse(fd, buffer, parser_ctx, &cur_token,
				      &flag_ext, progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MBX_LIST_OFLAG_FLAG_EXT;
  }

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

  oflag = mailimap_mbx_list_oflag_new(type, flag_ext);
  if (oflag == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = oflag;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (flag_ext != NULL)
    mailimap_flag_extension_free(flag_ext);
 err:
  return res;
}

static int
mailimap_mbx_list_oflag_no_sflag_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
    size_t * indx,
    struct mailimap_mbx_list_oflag ** result,
    size_t progr_rate,
    progress_function * progr_fun)
{
  size_t cur_token;
  int sflag_type;
  int r;

  cur_token = * indx;
  
  r = mailimap_mbx_list_sflag_parse(fd, buffer, parser_ctx, &cur_token, &sflag_type);
  if (r == MAILIMAP_NO_ERROR)
    return MAILIMAP_ERROR_PARSE;
  
  return mailimap_mbx_list_oflag_parse(fd, buffer, parser_ctx, indx, result,
      progr_rate, progr_fun);
}


/*
   mbx-list-sflag  = "\Noselect" / "\Marked" / "\Unmarked"
                       ; Selectability flags; only one per LIST response
*/

static int
mailimap_mbx_list_sflag_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      int * result)
{
  int type;
  size_t cur_token;

  cur_token = * indx;

  type = mailimap_mbx_list_sflag_get_token_value(fd, buffer, &cur_token);
  if (type == -1)
    return MAILIMAP_ERROR_PARSE;

  * result = type;
  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;
}


/*
   media-basic     = ((DQUOTE ("APPLICATION" / "AUDIO" / "IMAGE" / "MESSAGE" /
                     "VIDEO") DQUOTE) / string) SP media-subtype
                       ; Defined in [MIME-IMT]
*/

/*
  DQUOTE ("APPLICATION" / "AUDIO" / "IMAGE" / "MESSAGE" /
  "VIDEO") DQUOTE
*/

static int
mailimap_media_basic_standard_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx,
				    int * result)
{
  size_t cur_token;
  int type;
  int r;
  
  cur_token = * indx;

  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  type = mailimap_media_basic_get_token_value(fd, buffer, &cur_token);
  if (type == -1)
    return MAILIMAP_ERROR_PARSE;
    
  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * indx = cur_token;
  * result = type;
  
  return MAILIMAP_NO_ERROR;
}

/*
   media-basic     = ((DQUOTE ("APPLICATION" / "AUDIO" / "IMAGE" / "MESSAGE" /
                     "VIDEO") DQUOTE) / string) SP media-subtype
                       ; Defined in [MIME-IMT]
*/

static int
mailimap_media_basic_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			   size_t * indx,
			   struct mailimap_media_basic ** result,
			   size_t progr_rate,
			   progress_function * progr_fun)
{
  size_t cur_token;
  int type;
  char * basic_type;
  char * subtype;
  struct mailimap_media_basic * media_basic;
  int r;
  int res;

  cur_token = * indx;

  basic_type = NULL;
  subtype = NULL;

  type = MAILIMAP_MEDIA_BASIC_OTHER;
  r = mailimap_media_basic_standard_parse(fd, buffer, parser_ctx, &cur_token,
					  &type);

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_string_parse(fd, buffer, parser_ctx, &cur_token, &basic_type, NULL,
			      progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MEDIA_BASIC_OTHER;
  }
  
  // workaround for mbox mail for mac
  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_nil_parse(fd, buffer, parser_ctx, &cur_token);
    if (r == MAILIMAP_NO_ERROR) {
      type = MAILIMAP_MEDIA_BASIC_APPLICATION;
    }
  }

  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 free_basic_type;
  }

  r = mailimap_media_subtype_parse(fd, buffer, parser_ctx, &cur_token, &subtype,
				   progr_rate, progr_fun);
  if (r == MAILIMAP_ERROR_PARSE) {
    // workaround for mbox mail for mac
  }
  else if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free_basic_type;
  }
  
  if (r == MAILIMAP_ERROR_PARSE) {
    // workaround for mbox mail for mac
    r = mailimap_nil_parse(fd, buffer, parser_ctx, &cur_token);
    if (r == MAILIMAP_NO_ERROR) {
      subtype = strdup("DATA"); // application data
      if (subtype == NULL) {
        res = MAILIMAP_ERROR_MEMORY;
        goto free_basic_type;
      }
    }
  }
  
  media_basic = mailimap_media_basic_new(type, basic_type, subtype);
  if (media_basic == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_subtype;
  }

  * result = media_basic;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free_subtype:
  mailimap_media_subtype_free(subtype);
 free_basic_type:
  if (basic_type != NULL)
    mailimap_string_free(basic_type);
 err:
  return res;
}


/*
   media-message   = DQUOTE "MESSAGE" DQUOTE SP DQUOTE "RFC822" DQUOTE
                       ; Defined in [MIME-IMT]
*/

static int
mailimap_media_message_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx)
{
  size_t cur_token;
  int r;

  cur_token = * indx;

  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "MESSAGE");
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "RFC822");
  if (r == MAILIMAP_ERROR_PARSE) {
    // workaround for IMAP server "Bigfoot"
    // * OK [CAPABILITY IMAP4REV1 LITERAL+ SASL-IR LOGIN-REFERRALS AUTH=LOGIN ID] mail16c25 IMAP4rev1 Bigfoot
    // 01 id
    // * ID ("name", "Bigfoot", "version", "1.0", "os", "Linux", "os-version", "2.6", "vendor", "Megamailservers.com", "author", "Derek Snider")
    // * OK ID completed
    r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
                                              "DELIVERY-STATUS");
  }
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
   media-subtype   = string
                       ; Defined in [MIME-IMT]
*/

static int
mailimap_media_subtype_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx,
			     char ** result,
			     size_t progr_rate,
			     progress_function * progr_fun)
{
  return mailimap_string_parse(fd, buffer, parser_ctx, indx, result, NULL,
			       progr_rate, progr_fun);
}

/*
   media-text      = DQUOTE "TEXT" DQUOTE SP media-subtype
                       ; Defined in [MIME-IMT]
*/

static int mailimap_media_text_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				     size_t * indx,
				     char ** result,
				     size_t progr_rate,
				     progress_function * progr_fun)
{
  size_t cur_token;
  char * media_subtype;
  int r;

  cur_token = * indx;

  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "TEXT");
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_media_subtype_parse(fd, buffer, parser_ctx, &cur_token, &media_subtype,
				   progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = media_subtype;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}


/*
   message-data    = nz-number SP ("EXPUNGE" / ("FETCH" SP msg-att))
*/


static int
mailimap_message_data_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                     size_t * indx,
                                     struct mailimap_message_data ** result,
                                     size_t progr_rate,
                                     progress_function * progr_fun,
                                     mailprogress_function * body_progr_fun,
                                     mailprogress_function * items_progr_fun,
                                     void * context,
                                     mailimap_msg_att_handler * msg_att_handler,
                                     void * msg_att_context)
{
  size_t cur_token;
  uint32_t number;
  int type;
  struct mailimap_msg_att * msg_att;
  struct mailimap_message_data * msg_data;
  int r;
  int res;

  cur_token = * indx;
  msg_att = NULL;

	r = mailimap_nz_number_parse(fd, buffer, parser_ctx, &cur_token, &number);
	if (r == MAILIMAP_ERROR_PARSE) {
		// workaround for Zoho Mail IMAP server. sometimes returns negative message numbers

	  r = mailimap_minus_parse(fd, buffer, parser_ctx, &cur_token);
		if (r != MAILIMAP_NO_ERROR) {
			res = r;
			goto err;
		}
    r = mailimap_number_parse(fd, buffer, &cur_token, &number);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto err;
    }
		
		number = 0;
	}
	else 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;
  }

  type = MAILIMAP_MESSAGE_DATA_ERROR; /* XXX - removes a gcc warning */

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "EXPUNGE");
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_MESSAGE_DATA_EXPUNGE;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					      "FETCH");
    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_msg_att_parse_progress(fd, buffer, parser_ctx, &cur_token, &msg_att,
			       progr_rate, progr_fun, body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto err;
    }

    if (items_progr_fun != NULL) {
      (* items_progr_fun)(0, 0, context);
    }
    
    type = MAILIMAP_MESSAGE_DATA_FETCH;
  }

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

  msg_data = mailimap_message_data_new(number, type, msg_att);
  if (msg_data == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_msg_att;
  }

  * result = msg_data;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free_msg_att:
  if (msg_att != NULL)
    mailimap_msg_att_free(msg_att);
 err:
  return res;
}

/*
   msg-att         = "(" (msg-att-dynamic / msg-att-static)
                      *(SP (msg-att-dynamic / msg-att-static)) ")"
*/

/*
  msg-att-dynamic / msg-att-static
*/

static int
mailimap_msg_att_item_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                     size_t * indx,
                                     struct mailimap_msg_att_item ** result,
                                     size_t progr_rate,
                                     progress_function * progr_fun,
                                     mailprogress_function * body_progr_fun,
                                     mailprogress_function * items_progr_fun,
                                     void * context,
                                     mailimap_msg_att_handler * msg_att_handler,
                                     void * msg_att_context)
{
  int type;
  struct mailimap_msg_att_dynamic * msg_att_dynamic;
  struct mailimap_msg_att_static * msg_att_static;
  struct mailimap_extension_data * msg_att_extension;
  size_t cur_token;
  struct mailimap_msg_att_item * item;
  int r;
  int res;

  cur_token = * indx;

  msg_att_dynamic = NULL;
  msg_att_static = NULL;
  msg_att_extension = NULL;

  type = MAILIMAP_MSG_ATT_ITEM_ERROR; /* XXX - removes a gcc warning */

  r = mailimap_msg_att_dynamic_parse(fd, buffer, parser_ctx, &cur_token,
				     &msg_att_dynamic,
				     progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_MSG_ATT_ITEM_DYNAMIC;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_msg_att_static_parse_progress(fd, buffer, parser_ctx, &cur_token,
                                               &msg_att_static,
                                               progr_rate, progr_fun,
                                               body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MSG_ATT_ITEM_STATIC;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_extension_data_parse(MAILIMAP_EXTENDED_PARSER_FETCH_DATA,
                                      fd, buffer, parser_ctx, &cur_token, &msg_att_extension, progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MSG_ATT_ITEM_EXTENSION;
  }
  
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  item = mailimap_msg_att_item_new(type, msg_att_dynamic, msg_att_static, msg_att_extension);
  if (item == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = item;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (msg_att_extension != NULL)
    mailimap_extension_data_free(msg_att_extension);
  if (msg_att_dynamic != NULL)
    mailimap_msg_att_dynamic_free(msg_att_dynamic);
  if (msg_att_static != NULL)
    mailimap_msg_att_static_free(msg_att_static);
 err:
  return res;
}

/*
   msg-att         = "(" (msg-att-dynamic / msg-att-static)
                      *(SP (msg-att-dynamic / msg-att-static)) ")"
*/

static int
mailimap_msg_att_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                size_t * indx, struct mailimap_msg_att ** result,
                                size_t progr_rate,
                                progress_function * progr_fun,
                                mailprogress_function * body_progr_fun,
                                mailprogress_function * items_progr_fun,
                                void * context,
                                mailimap_msg_att_handler * msg_att_handler,
                                void * msg_att_context)
{
  size_t cur_token;
  clist * list;
  struct mailimap_msg_att * msg_att;
  int r;
  int res;

  cur_token = * indx;
  list = NULL;

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

  r = mailimap_struct_spaced_list_parse_progress(fd, buffer, parser_ctx, &cur_token, &list,
                                                 (mailimap_struct_parser_progress *)
                                                 mailimap_msg_att_item_parse_progress,
                                                 (mailimap_struct_destructor *)
                                                 mailimap_msg_att_item_free,
                                                 progr_rate, progr_fun,
                                                 body_progr_fun, items_progr_fun,
                                                 context, msg_att_handler, msg_att_context);
  if (r != MAILIMAP_NO_ERROR) {
    if (mailimap_parser_context_is_qip_workaround_enabled(parser_ctx)) {
      r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
      if (r == MAILIMAP_NO_ERROR) {
        // QIP returns "* num FETCH ()" response for storeFlags operations.

        list = clist_new();
        if (list == NULL) {
          res = MAILIMAP_ERROR_MEMORY;
          goto free;
        }
        goto ok;
      }
    }

    res = r;
    goto err;
  }

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free;
  }

 ok:
  msg_att = mailimap_msg_att_new(list);
  if (msg_att == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * indx = cur_token;
  * result = msg_att;

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

/*
   msg-att-dynamic = "FLAGS" SP "(" [flag-fetch *(SP flag-fetch)] ")"
                       ; MAY change for a message
*/


static int
mailimap_msg_att_dynamic_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_msg_att_dynamic ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  clist * list;
  struct mailimap_msg_att_dynamic * msg_att_dyn;
  size_t cur_token;
  int r;
  int res;
  
  cur_token = * indx;

  list = NULL;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "FLAGS");
  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_oparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token,
					&list,
					(mailimap_struct_parser *)
					mailimap_flag_fetch_parse,
					(mailimap_struct_destructor *)
					mailimap_flag_fetch_free,
					progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
    res = r;
    goto err;
  }

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free;
  }
  
  msg_att_dyn = mailimap_msg_att_dynamic_new(list);
  if (msg_att_dyn == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = msg_att_dyn;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (list != NULL) {
    clist_foreach(list, (clist_func) mailimap_flag_fetch_free, NULL);
    clist_free(list);
  }
 err:
  return res;
}

/*
   msg-att-static  = "ENVELOPE" SP envelope / "INTERNALDATE" SP date-time /
                     "RFC822" [".HEADER" / ".TEXT"] SP nstring /
                     "RFC822.SIZE" SP number / "BODY" ["STRUCTURE"] SP body /
                     "BODY" section ["<" number ">"] SP nstring /
                     "UID" SP uniqueid
                       ; MUST NOT change for a message
*/

/*
  "ENVELOPE" SP envelope
*/


static int
mailimap_msg_att_envelope_parse(mailstream * fd,
				MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				size_t * indx,
				struct mailimap_envelope ** result,
				size_t progr_rate,
				progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_envelope * env;
  int r;
  
  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer,
					    &cur_token, "ENVELOPE");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_envelope_parse(fd, buffer, parser_ctx, &cur_token, &env,
			      progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * indx = cur_token;
  * result = env;

  return MAILIMAP_NO_ERROR;
}


/*
  "INTERNALDATE" SP date-time
*/


static int
mailimap_msg_att_internaldate_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx,
				    struct mailimap_date_time ** result,
				    size_t progr_rate,
				    progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_date_time * date_time;
  int r;
  
  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "INTERNALDATE");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_date_time_parse(fd, buffer, parser_ctx, &cur_token, &date_time,
			       progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = date_time;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  "RFC822" SP nstring
*/

static int
mailimap_msg_att_rfc822_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                       size_t * indx, char ** result,
                                       size_t * result_len,
                                       size_t progr_rate,
                                       progress_function * progr_fun,
                                       mailprogress_function * body_progr_fun,
                                       mailprogress_function * items_progr_fun,
                                       void * context,
                                       mailimap_msg_att_handler * msg_att_handler,
                                       void * msg_att_context)
{
  size_t cur_token;
  char * rfc822_message;
  int r;
  size_t length;
  
  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "RFC822");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  parser_ctx->msg_body_parse_in_progress = true;
  parser_ctx->msg_body_att_type = MAILIMAP_MSG_ATT_RFC822;

  r = mailimap_nstring_parse_progress(fd, buffer, parser_ctx, &cur_token, &rfc822_message, &length,
         progr_rate, progr_fun, body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);

  parser_ctx->msg_body_parse_in_progress = false;
  parser_ctx->msg_body_att_type = 0;

  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = rfc822_message;
  if (result_len != NULL)
    * result_len = length;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  "RFC822" ".HEADER" SP nstring
*/

static int
mailimap_msg_att_rfc822_header_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				     size_t * indx, char ** result,
				     size_t * result_len,
				     size_t progr_rate,
				     progress_function * progr_fun)
{
  size_t cur_token;
  char * rfc822_header;
  int r;
  size_t length;
  
  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "RFC822");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    ".HEADER");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_nstring_parse(fd, buffer, parser_ctx, &cur_token, &rfc822_header, &length,
			     progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = rfc822_header;
  if (result_len != NULL)
    * result_len = length;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  "RFC822" ".TEXT" SP nstring
*/

static int
mailimap_msg_att_rfc822_text_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                            size_t * indx, char ** result,
                                            size_t * result_len,
                                            size_t progr_rate,
                                            progress_function * progr_fun,
                                            mailprogress_function * body_progr_fun,
                                            mailprogress_function * items_progr_fun,
                                            void * context,
                                            mailimap_msg_att_handler * msg_att_handler,
                                            void * msg_att_context)
{
  size_t cur_token;
  char * rfc822_text;
  int r;
  size_t length;
  
  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "RFC822");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    ".TEXT");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  parser_ctx->msg_body_parse_in_progress = true;
  parser_ctx->msg_body_att_type = MAILIMAP_MSG_ATT_RFC822_TEXT;

  r = mailimap_nstring_parse_progress(fd, buffer, parser_ctx, &cur_token, &rfc822_text, &length,
                                      progr_rate, progr_fun, body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);

  parser_ctx->msg_body_parse_in_progress = false;
  parser_ctx->msg_body_att_type = 0;

  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = rfc822_text;
  if (result_len != NULL)
    * result_len = length;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  "RFC822.SIZE" SP number
*/

static int
mailimap_msg_att_rfc822_size_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				   size_t * indx, uint32_t * result)
{
  size_t cur_token;
  uint32_t number;
  int r;
  
  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "RFC822.SIZE");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_number_parse(fd, buffer, &cur_token, &number);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = number;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  "BODY" SP body
*/


static int
mailimap_msg_att_body_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx, struct mailimap_body ** result,
			    size_t progr_rate,
			    progress_function * progr_fun)
{
  struct mailimap_body * body;
  size_t cur_token;
  int r;
  
  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "BODY");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_body_parse(fd, buffer, parser_ctx, &cur_token, &body,
			  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = body;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  "BODY" "STRUCTURE" SP body
*/


static int
mailimap_msg_att_bodystructure_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				     size_t * indx,
				     struct mailimap_body ** result,
				     size_t progr_rate,
				     progress_function * progr_fun)
{
  struct mailimap_body * body;
  size_t cur_token;
  int r;
  
  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "BODY");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "STRUCTURE");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_body_parse(fd, buffer, parser_ctx, &cur_token, &body,
			  progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = body;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  "BODY" section ["<" number ">"] SP nstring
*/

static int
mailimap_msg_att_body_section_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                             size_t * indx,
                                             struct mailimap_msg_att_body_section **
                                             result,
                                             size_t progr_rate,
                                             progress_function * progr_fun,
                                             mailprogress_function * body_progr_fun,
                                             mailprogress_function * items_progr_fun,
                                             void * context,
                                             mailimap_msg_att_handler * msg_att_handler,
                                             void * msg_att_context)
{
  size_t cur_token;
  uint32_t number;
  struct mailimap_section * section;
  char * body_part;
  struct mailimap_msg_att_body_section * msg_att_body_section;
  int r;
  int res;
  size_t length;

  cur_token = * indx;

  section = NULL;
  number = 0;
  body_part = NULL;

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

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

  r = mailimap_lower_parse(fd, buffer, parser_ctx, &cur_token);
  switch (r) {
  case MAILIMAP_NO_ERROR:
    r = mailimap_number_parse(fd, buffer, &cur_token, &number);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto free_section;
    }
    
    r = mailimap_greater_parse(fd, buffer, parser_ctx, &cur_token);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto free_section;
    }
    break;

  case MAILIMAP_ERROR_PARSE:
    break;
    
  default:
    return r;
  }

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

  msg_att_body_section =
    mailimap_msg_att_body_section_new(section, number, NULL, 0);
  if (msg_att_body_section == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_section;
  }

  parser_ctx->msg_body_parse_in_progress = true;
  parser_ctx->msg_body_att_type = MAILIMAP_MSG_ATT_BODY_SECTION;
  parser_ctx->msg_body_section = msg_att_body_section;

  r = mailimap_nstring_parse_progress(fd, buffer, parser_ctx, &cur_token, &body_part, &length,
                                      progr_rate, progr_fun, body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);

  parser_ctx->msg_body_parse_in_progress = false;
  parser_ctx->msg_body_att_type = 0;
  parser_ctx->msg_body_section = NULL;

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

  msg_att_body_section->sec_body_part = body_part;
  msg_att_body_section->sec_length = length;

  * result = msg_att_body_section;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free_att_body_section:
  msg_att_body_section->sec_section = NULL;
  mailimap_msg_att_body_section_free(msg_att_body_section);
 free_section:
  if (section != NULL)
    mailimap_section_free(section);
 err:
  return res;
}

/*
  "UID" SP uniqueid
*/

static int
mailimap_msg_att_uid_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			   size_t * indx,
			   uint32_t * result)
{
  size_t cur_token;
  uint32_t uid;
  int r;

  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "UID");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_uniqueid_parse(fd, buffer, parser_ctx, &cur_token, &uid);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  * indx = cur_token;
  * result = uid;

  return MAILIMAP_NO_ERROR;
}

/*
   msg-att-static  = "ENVELOPE" SP envelope / "INTERNALDATE" SP date-time /
                     "RFC822" [".HEADER" / ".TEXT"] SP nstring /
                     "RFC822.SIZE" SP number / "BODY" ["STRUCTURE"] SP body /
                     "BODY" section ["<" number ">"] SP nstring /
                     "UID" SP uniqueid
                       ; MUST NOT change for a message
*/

static int
mailimap_msg_att_static_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                       size_t * indx,
                                       struct mailimap_msg_att_static ** result,
                                       size_t progr_rate,
                                       progress_function * progr_fun,
                                       mailprogress_function * body_progr_fun,
                                       mailprogress_function * items_progr_fun,
                                       void * context,
                                       mailimap_msg_att_handler * msg_att_handler,
                                       void * msg_att_context)
{
  size_t cur_token;
  struct mailimap_envelope * env;
  struct mailimap_date_time * internal_date;
  char * rfc822;
  char * rfc822_header;
  char * rfc822_text;
  uint32_t rfc822_size;
  struct mailimap_body * bodystructure;
  struct mailimap_body * body;
  struct mailimap_msg_att_body_section * body_section;
  uint32_t uid;
  struct mailimap_msg_att_static * msg_att_static;
  int type;
  int r;
  int res;
  size_t length;

  cur_token = * indx;

  env = NULL;
  internal_date = NULL;
  rfc822 = NULL;
  rfc822_header = NULL;
  rfc822_text = NULL;
  rfc822_size = 0;
  length = 0;
  bodystructure = NULL;
  body = NULL;
  body_section = NULL;
  uid = 0;

  type = MAILIMAP_MSG_ATT_ERROR; /* XXX - removes a gcc warning */

  r = mailimap_msg_att_envelope_parse(fd, buffer, parser_ctx, &cur_token, &env,
				      progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_MSG_ATT_ENVELOPE;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_msg_att_internaldate_parse(fd, buffer, parser_ctx, &cur_token,
					    &internal_date,
					    progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MSG_ATT_INTERNALDATE;
  }
  
  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_msg_att_rfc822_parse_progress(fd, buffer, parser_ctx, &cur_token,
                                               &rfc822, &length,
                                               progr_rate, progr_fun,
                                               body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MSG_ATT_RFC822;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_msg_att_rfc822_header_parse(fd, buffer, parser_ctx, &cur_token,
					     &rfc822_header, &length,
					     progr_rate, progr_fun);
    type = MAILIMAP_MSG_ATT_RFC822_HEADER;
  }
  
  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_msg_att_rfc822_text_parse_progress(fd, buffer, parser_ctx, &cur_token,
                                                    &rfc822_text, &length,
                                                    progr_rate, progr_fun,
                                                    body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MSG_ATT_RFC822_TEXT;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_msg_att_rfc822_size_parse(fd, buffer, parser_ctx, &cur_token,
					   &rfc822_size);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MSG_ATT_RFC822_SIZE;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_msg_att_body_parse(fd, buffer, parser_ctx, &cur_token,
				    &body, progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MSG_ATT_BODY;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_msg_att_bodystructure_parse(fd, buffer, parser_ctx, &cur_token,
					     &bodystructure,
					     progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MSG_ATT_BODYSTRUCTURE;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_msg_att_body_section_parse_progress(fd, buffer, parser_ctx, &cur_token,
                                                     &body_section,
                                                     progr_rate, progr_fun,
                                                     body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MSG_ATT_BODY_SECTION;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_msg_att_uid_parse(fd, buffer, parser_ctx, &cur_token,
				   &uid);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_MSG_ATT_UID;
  }

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

  msg_att_static = mailimap_msg_att_static_new(type, env, internal_date,
					       rfc822, rfc822_header,
					       rfc822_text, length,
					       rfc822_size, bodystructure,
					       body, body_section, uid);
  if (msg_att_static == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = msg_att_static;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (env)
    mailimap_msg_att_envelope_free(env);
  if (internal_date)
    mailimap_msg_att_internaldate_free(internal_date);
  if (rfc822)
    mailimap_msg_att_rfc822_free(rfc822);
  if (rfc822_header)
    mailimap_msg_att_rfc822_header_free(rfc822_header);
  if (rfc822_text)
    mailimap_msg_att_rfc822_text_free(rfc822_text);
  if (bodystructure)
    mailimap_msg_att_bodystructure_free(bodystructure);
  if (body)
    mailimap_msg_att_body_free(body);
  if (body_section)
    mailimap_msg_att_body_section_free(body_section);
 err:
  return res;
}

/*
   nil             = "NIL"
*/

int mailimap_nil_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                       size_t * indx)
{
  return mailimap_token_case_insensitive_parse(fd, buffer, indx, "NIL");
}

/*
   nstring         = string / nil
*/

static int mailimap_nstring_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                           size_t * indx, char ** result,
                                           size_t * result_len,
                                           size_t progr_rate,
                                           progress_function * progr_fun,
                                           mailprogress_function * body_progr_fun,
                                           mailprogress_function * items_progr_fun,
                                           void * context,
                                           mailimap_msg_att_handler * msg_att_handler,
                                           void * msg_att_context)
{
  int r;
  
  r = mailimap_string_parse_progress(fd, buffer, parser_ctx, indx, result, result_len,
                                     progr_rate, progr_fun,
                                     body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);
  switch (r) {
    case MAILIMAP_NO_ERROR:
      return MAILIMAP_NO_ERROR;
      
    case MAILIMAP_ERROR_PARSE:
      r = mailimap_nil_parse(fd, buffer, parser_ctx, indx);
      if (r != MAILIMAP_NO_ERROR) {
        return r;
      }
      
      * result = NULL;
      if (result_len != NULL)
        * result_len = 0;
      return MAILIMAP_NO_ERROR;
      
    default:
      return r;
  }
}

int mailimap_nstring_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				  size_t * indx, char ** result,
				  size_t * result_len,
				  size_t progr_rate,
				  progress_function * progr_fun)
{
  return mailimap_nstring_parse_progress(fd, buffer, parser_ctx, indx, result, result_len, progr_rate, progr_fun,
                                         NULL, NULL, NULL, NULL, NULL);
}

/*
   number          = 1*DIGIT
                       ; Unsigned 32-bit integer
                       ; (0 <= n < 4,294,967,296)
*/

int
mailimap_number_parse(mailstream * fd, MMAPString * buffer,
		      size_t * indx, uint32_t * result)
{
  size_t cur_token;
  int digit;
  uint32_t number;
  int parsed;
  int r;
	int negative;
  
  cur_token = * indx;
  parsed = FALSE;

#ifdef UNSTRICT_SYNTAX
  mailimap_space_parse(fd, buffer, &cur_token);
#endif

	negative = 0;
	r = mailimap_char_parse(fd, buffer, &cur_token, '-');
	if (r == MAILIMAP_NO_ERROR) {
	  negative = 1;
	}
	
  number = 0;
  while (1) {
    r = mailimap_digit_parse(fd, buffer, &cur_token, &digit);
    if (r == MAILIMAP_ERROR_PARSE)
      break;
    else if (r == MAILIMAP_NO_ERROR) {
      number *= 10;
      number += digit;
      parsed = TRUE;
    }
    else
      return r;
  }

	if (negative) {
	  number = 0;
	}
	
  if (!parsed)
    return MAILIMAP_ERROR_PARSE;

  * result = number;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
   nz-number       = digit-nz *DIGIT
                       ; Non-zero unsigned 32-bit integer
                       ; (0 < n < 4,294,967,296)
*/

int
mailimap_nz_number_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			 size_t * indx, uint32_t * result)
{
#ifdef UNSTRICT_SYNTAX
  size_t cur_token;
  uint32_t number;
  int r;
  
  cur_token = * indx;

  r = mailimap_number_parse(fd, buffer, &cur_token, &number);
  if (r != MAILIMAP_NO_ERROR)
    return r;

#if 0 // don't fail if zero
  if (number == 0)
    return MAILIMAP_ERROR_PARSE;
#endif

#else
  size_t cur_token;
  int digit;
  uint32_t number;
  int r;
  
  cur_token = * indx;

  r = mailimap_digit_nz_parse(fd, buffer, parser_ctx, &cur_token, &digit);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  number = digit;

  while (1) {
    r = mailimap_digit_parse(fd, buffer, &cur_token, &digit);
    if (r == MAILIMAP_ERROR_PARSE)
      break;
    else if (r == MAILIMAP_NO_ERROR) {
      number *= 10;
      number += (guint32) digit;
    }
    else
      return r;
  }
#endif

  * result = number;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
   password        = astring
*/

/*
   quoted          = DQUOTE *QUOTED-CHAR DQUOTE
*/

static int
mailimap_quoted_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
		      size_t * indx, char ** result,
		      size_t progr_rate,
		      progress_function * progr_fun)
{
  char ch;
  size_t cur_token;
  MMAPString * gstr_quoted;
  int r;
  int res;
  bool use_msg_body_handler;

  cur_token = * indx;

  use_msg_body_handler = (parser_ctx->msg_body_handler != NULL
                          && parser_ctx->msg_body_parse_in_progress);

#ifdef UNSTRICT_SYNTAX
  r = mailimap_space_parse(fd, buffer, &cur_token);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE))
    return r;
#endif

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

  gstr_quoted = mmap_string_new("");
  if (gstr_quoted == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto err;
  }

  while (1) {
    if (cur_token >= buffer->len) {
      if (fd == NULL) {
        if (!has_crlf(buffer, cur_token)) {
          res = MAILIMAP_ERROR_NEEDS_MORE_DATA;
          goto free;
        }
      }
      else if (mailstream_read_line_append(fd, buffer) == NULL) {
        res = MAILIMAP_ERROR_PARSE;
        goto free;
      }
    }
    
    r = mailimap_quoted_char_parse(fd, buffer, parser_ctx, &cur_token, &ch);
    if (r == MAILIMAP_ERROR_PARSE)
      break;
    else if (r == MAILIMAP_NO_ERROR) {
      if (use_msg_body_handler) {
        if (!parser_ctx->msg_body_handler(parser_ctx->msg_body_att_type, parser_ctx->msg_body_section,
                                          &ch, 1,
                                          parser_ctx->msg_body_handler_context)) {
          res = MAILIMAP_ERROR_MEMORY;
          goto free;
        }
      }
      else {
        if (mmap_string_append_c(gstr_quoted, ch) == NULL) {
          res = MAILIMAP_ERROR_MEMORY;
          goto free;
        }
      }
    }
    else {
      res = r;
      goto free;
    }
  }

  r = mailimap_dquote_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free;
  }

  if (mmap_string_ref(gstr_quoted) < 0) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * indx = cur_token;
  * result = gstr_quoted->str;

  return MAILIMAP_NO_ERROR;

 free:
  mmap_string_free(gstr_quoted);
 err:
  return res;
}

/*
   QUOTED-CHAR     = <any TEXT-CHAR except quoted-specials> /
                     "\" quoted-specials
*/

static int is_quoted_specials(char ch);

int
mailimap_quoted_char_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			   size_t * indx, char * result)
{
  size_t cur_token;
  int r;
  
  cur_token = * indx;

  if (cur_token >= buffer->len)
    return MAILIMAP_ERROR_PARSE;
  
  if (!is_quoted_specials(buffer->str[cur_token])) {
    * result = buffer->str[cur_token];
    cur_token ++;
    * indx = cur_token;
    return MAILIMAP_NO_ERROR;
  }
  else if (buffer->str[cur_token] == '\\') {
    char quoted_special;

    r = mailimap_char_parse(fd, buffer, &cur_token, '\\');
    if (r != MAILIMAP_NO_ERROR) {
      return r;
    }

    r = mailimap_quoted_specials_parse(fd, buffer, parser_ctx, &cur_token,
				       &quoted_special);
    if (r != MAILIMAP_NO_ERROR) {
      * result = '\\';
      * indx = cur_token;
      return MAILIMAP_NO_ERROR;
    }

    * result = quoted_special;
    * indx = cur_token;

    return MAILIMAP_NO_ERROR;
  }
  else {
    return MAILIMAP_ERROR_PARSE;
  }
}

/*
   quoted-specials = DQUOTE / "\"
*/

static int is_quoted_specials(char ch)
{
  return (ch == '\"') || (ch == '\\');
}

static int
mailimap_quoted_specials_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx, char * result)
{
  size_t cur_token;
  
  cur_token = * indx;

  if (cur_token >= buffer->len)
    return MAILIMAP_ERROR_PARSE;

  if (is_quoted_specials(buffer->str[cur_token])) {
    * result = buffer->str[cur_token];
    cur_token ++;
    * indx = cur_token;
    return MAILIMAP_NO_ERROR;
  }
  else
    return MAILIMAP_ERROR_PARSE;
}

/*
  UNIMPLEMENTED
   rename          = "RENAME" SP mailbox SP mailbox
                       ; Use of INBOX as a destination gives a NO error
*/

/*
   response        = *(continue-req / response-data) response-done
*/

/*
  continue-req / response-data
*/

static int
mailimap_cont_req_or_resp_data_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                              size_t * indx,
                                              struct mailimap_cont_req_or_resp_data **
                                              result,
                                              size_t progr_rate,
                                              progress_function * progr_fun,
                                              mailprogress_function * body_progr_fun,
                                              mailprogress_function * items_progr_fun,
                                              void * context,
                                              mailimap_msg_att_handler * msg_att_handler,
                                              void * msg_att_context)
{
  size_t cur_token;
  struct mailimap_cont_req_or_resp_data * cont_req_or_resp_data;
  struct mailimap_continue_req * cont_req;
  struct mailimap_response_data * resp_data;
  int type;
  int r;
  int res;

  cur_token = * indx;

  cont_req = NULL;
  resp_data = NULL;
  type = MAILIMAP_RESP_ERROR; /* XXX - removes a gcc warning */

  // skip blank lines (workaround for exchange)
  while (1) {
    r = mailimap_crlf_parse(fd, buffer, parser_ctx, &cur_token);
    if (r == MAILIMAP_NO_ERROR) {
      if (fd == NULL) {
        if (!has_crlf(buffer, cur_token)) {
          res = MAILIMAP_ERROR_NEEDS_MORE_DATA;
          goto free;
        }
      }
      else if (mailstream_read_line_append(fd, buffer) == NULL) {
        res = MAILIMAP_ERROR_STREAM;
        goto free;
      }
      continue;
    }
    break;
  }
  
  r = mailimap_continue_req_parse(fd, buffer, parser_ctx, &cur_token, &cont_req,
				  progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_RESP_CONT_REQ;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_response_data_parse_progress(fd, buffer, parser_ctx, &cur_token, &resp_data,
                                              progr_rate, progr_fun,
                                              body_progr_fun, items_progr_fun,
                                              context, msg_att_handler, msg_att_context);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_RESP_RESP_DATA;
  }

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

  /*
    multi-lines response
    read another response line because after that token,
    there must have something (continue-req, response-data or response-done)
  */

  if (fd == NULL) {
    if (!has_crlf(buffer, cur_token)) {
      res = MAILIMAP_ERROR_NEEDS_MORE_DATA;
      goto free;
    }
  }
  else if (mailstream_read_line_append(fd, buffer) == NULL) {
    res = MAILIMAP_ERROR_STREAM;
    goto free;
  }
  
  if ((cont_req == NULL) && (resp_data == NULL)) {
    cont_req_or_resp_data = NULL;
  }
  else {
    cont_req_or_resp_data =
    mailimap_cont_req_or_resp_data_new(type, cont_req, resp_data);
    if (cont_req_or_resp_data == NULL) {
      res = MAILIMAP_ERROR_MEMORY;
      goto free;
    }
  }

  * result = cont_req_or_resp_data;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (cont_req != NULL)
    mailimap_continue_req_free(cont_req);
  if (resp_data != NULL)
    mailimap_response_data_free(resp_data);
 err:
  return res;
}

/* static */ int
mailimap_cont_req_or_resp_data_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                     size_t * indx,
                                     struct mailimap_cont_req_or_resp_data **
                                     result,
                                     size_t progr_rate,
                                     progress_function * progr_fun)
{
  return mailimap_cont_req_or_resp_data_parse_progress(fd, buffer, parser_ctx, indx, result,
                                                       progr_rate,
                                                       progr_fun,
                                                       NULL, NULL, NULL, NULL, NULL);
}

/*
   response        = *(continue-req / response-data) response-done
*/

static int
mailimap_response_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                 size_t * indx, struct mailimap_response ** result,
                                 size_t progr_rate,
                                 progress_function * progr_fun,
                                 mailprogress_function * body_progr_fun,
                                 mailprogress_function * items_progr_fun,
                                 void * context,
                                 mailimap_msg_att_handler * msg_att_handler,
                                 void * msg_att_context)
{
  size_t cur_token;
  clist * cont_req_or_resp_data_list;
  struct mailimap_response * resp;
  struct mailimap_response_done * resp_done;
  int r;
  int res;
  clistiter * iter;
  
  cur_token = * indx;
  cont_req_or_resp_data_list = NULL;
  resp_done = NULL;
  
  r = mailimap_struct_multiple_parse_progress(fd, buffer, parser_ctx,
                                              &cur_token, &cont_req_or_resp_data_list,
                                              (mailimap_struct_parser_progress *)
                                              mailimap_cont_req_or_resp_data_parse_progress,
                                              (mailimap_struct_destructor *)
                                              mailimap_cont_req_or_resp_data_free,
                                              progr_rate, progr_fun,
                                              body_progr_fun,
                                              items_progr_fun,
                                              context,
                                              msg_att_handler,
                                              msg_att_context);
  
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE))
    return r;
  
  if (r == MAILIMAP_NO_ERROR) {
    for(iter = clist_begin(cont_req_or_resp_data_list) ; iter != NULL ; iter = clist_next(iter)) {
      struct mailimap_cont_req_or_resp_data * cont_req_or_resp_data = clist_content(iter);
      if (cont_req_or_resp_data->rsp_type != MAILIMAP_RESP_RESP_DATA) {
        continue;
      }
      if (cont_req_or_resp_data->rsp_data.rsp_resp_data->rsp_type == MAILIMAP_RESP_DATA_TYPE_COND_BYE) {
        res = MAILIMAP_ERROR_STREAM;
        goto free_list;
      }
    }
  }
  
  r = mailimap_response_done_parse(fd, buffer, parser_ctx, &cur_token, &resp_done,
                                   progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free_list;
  }
  
  resp = mailimap_response_new(cont_req_or_resp_data_list, resp_done);
  if (resp == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_resp_done;
  }
  
  * result = resp;
  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;
  
free_resp_done:
  mailimap_response_done_free(resp_done);
free_list:
  if (cont_req_or_resp_data_list != NULL) {
    clist_foreach(cont_req_or_resp_data_list,
                  (clist_func) mailimap_cont_req_or_resp_data_free, NULL);
    clist_free(cont_req_or_resp_data_list);
  }
  return res;
}

int
mailimap_response_parse_with_context(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                     size_t * indx, struct mailimap_response ** result,
                                     mailprogress_function * body_progr_fun,
                                     mailprogress_function * items_progr_fun,
                                     void * context,
                                     mailimap_msg_att_handler * msg_att_handler,
                                     void * msg_att_context)
{
  return mailimap_response_parse_progress(fd, buffer, parser_ctx, indx, result,
                                          4096, NULL,
                                          body_progr_fun, items_progr_fun, context,
                                          msg_att_handler, msg_att_context);
}

int
mailimap_response_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			size_t * indx, struct mailimap_response ** result,
			size_t progr_rate,
			progress_function * progr_fun)
{
  return mailimap_response_parse_progress(fd, buffer, parser_ctx, indx, result,
                                          progr_rate, progr_fun,
                                          NULL, NULL, NULL, NULL, NULL);
}

/*
   response-data   = "*" SP (resp-cond-state / resp-cond-bye /
                     mailbox-data / message-data / capability-data) CRLF
*/

static int
mailimap_response_data_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                                      size_t * indx,
                                      struct mailimap_response_data ** result,
                                      size_t progr_rate,
                                      progress_function * progr_fun,
                                      mailprogress_function * body_progr_fun,
                                      mailprogress_function * items_progr_fun,
                                      void * context,
                                      mailimap_msg_att_handler * msg_att_handler,
                                      void * msg_att_context)
{
  struct mailimap_response_data * resp_data;
  size_t cur_token;
  int type;
  struct mailimap_resp_cond_state * cond_state;
  struct mailimap_resp_cond_bye * cond_bye;
  struct mailimap_mailbox_data * mb_data;
  struct mailimap_message_data * msg_data;
  struct mailimap_capability_data * cap_data;
  struct mailimap_extension_data * ext_data;
  int r;
  int res;
  int msg_att_handled;

  cond_state = NULL;
  cond_bye = NULL;
  mb_data = NULL;
  msg_data = NULL;
  cap_data = NULL;
  ext_data = NULL;

  cur_token = * indx;

  r = mailimap_star_parse(fd, buffer, parser_ctx, &cur_token);
  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;
  }

  type = MAILIMAP_RESP_DATA_TYPE_ERROR; /* XXX - removes a gcc warning */

  r = mailimap_resp_cond_state_parse(fd, buffer, parser_ctx, &cur_token, &cond_state,
				     progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_RESP_DATA_TYPE_COND_STATE;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_resp_cond_bye_parse(fd, buffer, parser_ctx, &cur_token, &cond_bye,
				     progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_RESP_DATA_TYPE_COND_BYE;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_mailbox_data_parse(fd, buffer, parser_ctx, &cur_token, &mb_data,
				    progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_RESP_DATA_TYPE_MAILBOX_DATA;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_message_data_parse_progress(fd, buffer, parser_ctx, &cur_token, &msg_data,
				    progr_rate, progr_fun, body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_RESP_DATA_TYPE_MESSAGE_DATA;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_capability_data_parse(fd, buffer, parser_ctx, &cur_token, &cap_data,
				       progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_RESP_DATA_TYPE_CAPABILITY_DATA;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_extension_data_parse(MAILIMAP_EXTENDED_PARSER_RESPONSE_DATA,
                fd, buffer, parser_ctx, &cur_token, &ext_data, progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_RESP_DATA_TYPE_EXTENSION_DATA;
  }

  if (r != MAILIMAP_NO_ERROR) {
    if (fd != NULL) {
      mailstream_log_error(fd, buffer->str + cur_token, buffer->len - cur_token);
    }
    res = r;
    goto err;
  }

  r = mailimap_crlf_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free;
  }

  msg_att_handled = 0;
  if (msg_data != NULL) {
    if (msg_data->mdt_type == MAILIMAP_MESSAGE_DATA_FETCH) {
      if (msg_att_handler != NULL) {
	      msg_data->mdt_msg_att->att_number = msg_data->mdt_number;
        msg_att_handler(msg_data->mdt_msg_att, msg_att_context);
        mailimap_message_data_free(msg_data);
        msg_data = NULL;
        msg_att_handled = 1;
      }
    }
  }
  
  if (msg_att_handled) {
    resp_data = NULL;
  }
  else {
    resp_data = mailimap_response_data_new(type, cond_state,
                                           cond_bye, mb_data,
                                           msg_data, cap_data, ext_data);
    if (resp_data == NULL) {
      res = MAILIMAP_ERROR_MEMORY;
      goto free;
    }
  }

  * result = resp_data;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (cond_state)
    mailimap_resp_cond_state_free(cond_state);
  if (cond_bye)
    mailimap_resp_cond_bye_free(cond_bye);
  if (mb_data)
    mailimap_mailbox_data_free(mb_data);
  if (msg_data)
    mailimap_message_data_free(msg_data);
  if (cap_data)
    mailimap_capability_data_free(cap_data);
  if (ext_data)
    mailimap_extension_data_free(ext_data);
 err:
  return res;
}

int
mailimap_response_data_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                             size_t * indx,
                             struct mailimap_response_data ** result,
                             size_t progr_rate,
                             progress_function * progr_fun)
{
  return mailimap_response_data_parse_progress(fd, buffer, parser_ctx, indx, result,
                                               progr_rate, progr_fun,
                                               NULL, NULL, NULL, NULL, NULL);
}

/*
   response-done   = response-tagged / response-fatal
*/

static int
mailimap_response_done_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx,
			     struct mailimap_response_done ** result,
			     size_t progr_rate,
			     progress_function * progr_fun)
{
  int type;
  struct mailimap_response_done * resp_done;
  size_t cur_token;
  struct mailimap_response_tagged * tagged;
  struct mailimap_response_fatal * fatal;
  int r;
  int res;

  cur_token = * indx;

  tagged = NULL;
  fatal = NULL;

  type = MAILIMAP_RESP_DONE_TYPE_ERROR; /* removes a gcc warning */

  r = mailimap_response_tagged_parse(fd, buffer, parser_ctx, &cur_token, &tagged,
				     progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_RESP_DONE_TYPE_TAGGED;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_response_fatal_parse(fd, buffer, parser_ctx, &cur_token, &fatal,
				      progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_RESP_DONE_TYPE_FATAL;
  }

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

  resp_done = mailimap_response_done_new(type, tagged, fatal);
  if (resp_done == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = resp_done;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
  
 free:
  if (tagged == NULL)
    mailimap_response_tagged_free(tagged);
  if (fatal == NULL)
    mailimap_response_fatal_free(fatal);
 err:
  return res;
}

/*
   response-fatal  = "*" SP resp-cond-bye CRLF
                       ; Server closes connection immediately
*/

static int
mailimap_response_fatal_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_response_fatal ** result,
			      size_t progr_rate,
			      progress_function * progr_fun)
{
  struct mailimap_resp_cond_bye * cond_bye;
  struct mailimap_response_fatal * fatal;
  size_t cur_token;
  int res;
  int r;

  cur_token = * indx;

  r = mailimap_star_parse(fd, buffer, parser_ctx, &cur_token);
  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_resp_cond_bye_parse(fd, buffer, parser_ctx, &cur_token, &cond_bye,
				   progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_crlf_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free;
  }

  fatal = mailimap_response_fatal_new(cond_bye);
  if (fatal == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }
  
  * result = fatal;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  mailimap_resp_cond_bye_free(cond_bye);
 err:
  return res;
}

/*
   response-tagged = tag SP resp-cond-state CRLF
*/

static int
mailimap_response_tagged_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_response_tagged ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  size_t cur_token;
  char * tag;
  struct mailimap_resp_cond_state * cond_state;
  struct mailimap_response_tagged * resp_tagged;
  int r;
  int res;

  cur_token = * indx;
  cond_state = NULL;

  r = mailimap_tag_parse(fd, buffer, parser_ctx, &cur_token, &tag,
			 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 free_tag;
  }

  r = mailimap_resp_cond_state_parse(fd, buffer, parser_ctx, &cur_token, &cond_state,
				     progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free_tag;
  }

  resp_tagged = mailimap_response_tagged_new(tag, cond_state);
  if (resp_tagged == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_cond_state;
  }

  * result = resp_tagged;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free_cond_state:
  mailimap_resp_cond_state_free(cond_state);
 free_tag:
  mailimap_tag_free(tag);
 err:
  return res;
}

/*
   resp-cond-auth  = ("OK" / "PREAUTH") SP resp-text
                       ; Authentication condition
*/

static int
mailimap_resp_cond_auth_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_resp_cond_auth ** result,
			      size_t progr_rate,
			      progress_function * progr_fun)
{
  struct mailimap_resp_cond_auth * cond_auth;
  size_t cur_token;
  struct mailimap_resp_text * text;
  int type;
  int r;
  int res;
  
  cur_token = * indx;
  text = NULL;

  type = MAILIMAP_RESP_COND_AUTH_ERROR; /* XXX - removes a gcc warning */

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "OK");
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_RESP_COND_AUTH_OK;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_token_case_insensitive_parse(fd, buffer,
					      &cur_token, "PREAUTH");
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_RESP_COND_AUTH_PREAUTH;
  }

  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_resp_text_parse(fd, buffer, parser_ctx, &cur_token, &text,
			       progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  cond_auth = mailimap_resp_cond_auth_new(type, text);
  if (cond_auth == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = cond_auth;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  mailimap_resp_text_free(text);
 err:
  return res;
}

/*
   resp-cond-bye   = "BYE" SP resp-text
*/

static int
mailimap_resp_cond_bye_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			     size_t * indx,
			     struct mailimap_resp_cond_bye ** result,
			     size_t progr_rate,
			     progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_resp_cond_bye * cond_bye;
  struct mailimap_resp_text * text;
  int r;
  int res;

  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer,
					    &cur_token, "BYE");
  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_resp_text_parse(fd, buffer, parser_ctx, &cur_token, &text,
			       progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  cond_bye = mailimap_resp_cond_bye_new(text);
  if (cond_bye == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }
    
  * indx = cur_token;
  * result = cond_bye;

  return MAILIMAP_NO_ERROR;

 free:
  mailimap_resp_text_free(text);
 err:
  return res;
}

/*
   resp-cond-state = ("OK" / "NO" / "BAD") SP resp-text
                       ; Status condition
*/

static int
mailimap_resp_cond_state_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_resp_cond_state ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  struct mailimap_resp_cond_state * cond_state;
  size_t cur_token;
  struct mailimap_resp_text * text;
  int type;
  int r;
  int res;
  
  cur_token = * indx;
  text = NULL;

  type = mailimap_resp_cond_state_get_token_value(fd, buffer, &cur_token);
  if (type == -1) {
    res = MAILIMAP_ERROR_PARSE;
    goto err;
  }

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

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

  cond_state = mailimap_resp_cond_state_new(type, text);
  if (cond_state == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = cond_state;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  mailimap_resp_text_free(text);
 err:
  return res;
}


/*
   resp-specials   = "]"
*/

static int is_resp_specials(char ch)
{
  switch (ch) {
  case ']':
    return TRUE;
  };
  return FALSE;
}

/*
   resp-text       = ["[" resp-text-code "]" SP] text
*/

/* "[" resp-text-code "]" */

static int
mailimap_resp_text_resp_text_code_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					size_t * indx,
					struct mailimap_resp_text_code **
					result,
					size_t progr_rate,
					progress_function * progr_fun)
{
  struct mailimap_resp_text_code * text_code;
  size_t cur_token;
  int r;
  int res;
  
  cur_token = * indx;

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

  text_code = NULL;
  r = mailimap_resp_text_code_parse(fd, buffer, parser_ctx, &cur_token, &text_code,
				    progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_cbracket_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free;
  }

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

  * result = text_code;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  mailimap_resp_text_code_free(text_code);
 err:
  return res;
}

/*
   resp-text       = ["[" resp-text-code "]" SP] text
*/

static int
mailimap_resp_text_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			 size_t * indx,
			 struct mailimap_resp_text ** result,
			 size_t progr_rate,
			 progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_resp_text_code * text_code;
  struct mailimap_resp_text * resp_text;
  char * text;
  int r;
  int res;

  cur_token = * indx;
  text = NULL;
  text_code = NULL;

  r = mailimap_resp_text_resp_text_code_parse(fd, buffer, parser_ctx, &cur_token,
					      &text_code,
					      progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE))
    return r;

  r = mailimap_text_parse(fd, buffer, parser_ctx, &cur_token, &text,
			  progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
    res = r;
    goto free_resp_text_code;
  }
  
  resp_text = mailimap_resp_text_new(text_code, text);
  if (resp_text == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_text;
  }

  * result = resp_text;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free_resp_text_code:
  if (text_code != NULL)
    mailimap_resp_text_code_free(text_code);
 free_text:
  mailimap_text_free(text);
  return res;
}

/*
   resp-text-code  = "ALERT" /
                     "BADCHARSET" [SP "(" astring *(SP astring) ")" ] /
                     capability-data / "PARSE" /
                     "PERMANENTFLAGS" SP "(" [flag-perm *(SP flag-perm)] ")" /
                     "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
                     "UIDNEXT" SP nz-number / "UIDVALIDITY" SP nz-number /
                     "UNSEEN" SP nz-number /
                     atom [SP 1*<any TEXT-CHAR except "]">]
*/

/*
  ALERT / PARSE / READ-ONLY / READ-WRITE / TRYCREATE
*/

static int
mailimap_resp_text_code_1_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				size_t * indx,
				int * result)
{
  int id;
  size_t cur_token;

  cur_token = * indx;

  id = mailimap_resp_text_code_1_get_token_value(fd, buffer, &cur_token);
  
  if (id == -1)
    return MAILIMAP_ERROR_PARSE;

  * result = id;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}


/*
  "BADCHARSET" [SP "(" astring *(SP astring) ")" ]
*/

/*
  SP "(" astring *(SP astring) ")"
*/

static int
mailimap_resp_text_code_badcharset_1_parse(mailstream * fd,
					   MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					   size_t * indx,
					   clist ** result,
					   size_t progr_rate,
					   progress_function * progr_fun)
{
  size_t cur_token;
  clist * charset;
  int r;
  int res;

  cur_token = * indx;

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

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

  r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token, &charset,
					(mailimap_struct_parser *)
					mailimap_astring_parse,
					(mailimap_struct_destructor *)
					mailimap_astring_free,
					progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto charset;
  }

  * indx = cur_token;
  * result = charset;

  return MAILIMAP_NO_ERROR;

 charset:
  clist_foreach(charset, (clist_func) mailimap_string_free, NULL);
  clist_free(charset);
 err:
  return res;
}

/*
  "BADCHARSET" [SP "(" astring *(SP astring) ")" ]
*/

static int
mailimap_resp_text_code_badcharset_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					 size_t * indx,
					 clist ** result,
					 size_t progr_rate,
					 progress_function * progr_fun)
{
  size_t cur_token;
  clist * charset;
  int r;

  cur_token = * indx;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "BADCHARSET");
  if (r != MAILIMAP_NO_ERROR)
    return r;

  charset = NULL;

  r = mailimap_resp_text_code_badcharset_1_parse(fd, buffer, parser_ctx, &cur_token,
						 &charset,
						 progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE))
    return r;
  
  * result = charset;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  "PERMANENTFLAGS" SP "(" [flag-perm *(SP flag-perm)] ")"
*/

static int
mailimap_resp_text_code_permanentflags_parse(mailstream * fd,
					     MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
					     size_t * indx,
					     clist ** result,
					     size_t progr_rate,
					     progress_function * progr_fun)
{
  size_t cur_token;
  clist * flaglist;
  int r;
  int res;

  cur_token = * indx;

  flaglist = NULL;

  r = mailimap_token_case_insensitive_parse(fd, buffer, &cur_token,
					    "PERMANENTFLAGS");
  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_oparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }
  
  r = mailimap_struct_spaced_list_parse(fd, buffer, parser_ctx, &cur_token, &flaglist,
					(mailimap_struct_parser *)
					mailimap_flag_perm_parse,
					(mailimap_struct_destructor *)
					mailimap_flag_perm_free,
					progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
    res = r;
    goto err;
  }
  
  r = mailimap_cparenth_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto free;
  }

  * indx = cur_token;
  * result = flaglist;

  return MAILIMAP_NO_ERROR;

 free:
  clist_foreach(flaglist, (clist_func) mailimap_flag_perm_free, NULL);
  clist_free(flaglist);
 err:
  return res;
}


/*
  "UIDNEXT" SP nz-number /
  "UIDVALIDITY" SP nz-number /
  "UNSEEN" SP nz-number 
*/

static int
mailimap_resp_text_code_number_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				     size_t * indx,
				     struct mailimap_resp_text_code ** result,
				     size_t progr_rate,
				     progress_function * progr_fun)
{
  size_t cur_token;
  int type;
  uint32_t number;
  struct mailimap_resp_text_code * resp_text_code;
  int r;

  cur_token = * indx;

  resp_text_code = NULL;

  type = mailimap_resp_text_code_2_get_token_value(fd, buffer, &cur_token);
  if (type == -1)
    return MAILIMAP_ERROR_PARSE;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_nz_number_parse(fd, buffer, parser_ctx, &cur_token, &number);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  switch (type) {
  case MAILIMAP_RESP_TEXT_CODE_UIDNEXT:
    resp_text_code = mailimap_resp_text_code_new(type, NULL, NULL, NULL,
						 number, 0, 0, NULL , NULL, NULL);
    break;
  case MAILIMAP_RESP_TEXT_CODE_UIDVALIDITY:
    resp_text_code = mailimap_resp_text_code_new(type, NULL, NULL, NULL,
						 0, number, 0, NULL , NULL, NULL);
    break;
  case MAILIMAP_RESP_TEXT_CODE_UNSEEN:
    resp_text_code = mailimap_resp_text_code_new(type, NULL, NULL, NULL,
						 0, 0, number, NULL , NULL, NULL);
    break;
  }

  if (resp_text_code == NULL)
    return MAILIMAP_ERROR_MEMORY;

  * result = resp_text_code;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  atom [SP 1*<any TEXT-CHAR except "]">]
*/

static int is_text_char(char ch);

/*
  any TEXT-CHAR except "]"
*/

static int is_text_char_1(char ch)
{
  if (ch == ']')
    return FALSE;
  return is_text_char(ch);
}

/*
  1*<any TEXT-CHAR except "]"
*/

static int
mailimap_resp_text_code_other_2_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				      size_t * indx, char ** result,
				      size_t progr_rate,
				      progress_function * progr_fun)
{
  return mailimap_custom_string_parse(fd, buffer, parser_ctx, indx, result,
				      is_text_char_1);
}

/*
  SP 1*<any TEXT-CHAR except "]">
*/

static int
mailimap_resp_text_code_other_1_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				      size_t * indx,
				      char ** result,
				      size_t progr_rate,
				      progress_function * progr_fun)
{
  size_t cur_token;
  char * value;
  int r;

  cur_token = * indx;

  r = mailimap_space_parse(fd, buffer, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_resp_text_code_other_2_parse(fd, buffer, parser_ctx, &cur_token,
					    &value,
					    progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = value;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  atom [SP 1*<any TEXT-CHAR except "]">]
*/

static int
mailimap_resp_text_code_other_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				    size_t * indx,
				    struct mailimap_resp_text_code ** result,
				    size_t progr_rate,
				    progress_function * progr_fun)
{
  size_t cur_token;
  char * atom;
  char * value;
  struct mailimap_resp_text_code * resp_text_code;
  int r;
  int res;

  cur_token = * indx;
  atom = NULL;
  value = NULL;

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

  r = mailimap_resp_text_code_other_1_parse(fd, buffer, parser_ctx, &cur_token,
					    &value, progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
    res = r;
    goto err;
  }
  
  resp_text_code = mailimap_resp_text_code_new(MAILIMAP_RESP_TEXT_CODE_OTHER,
					       NULL, NULL, NULL,
					       0, 0, 0, atom, value, NULL);
  if (resp_text_code == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_value;
  }

  * result = resp_text_code;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free_value:
  if (value != NULL)
    free(value);
  mailimap_atom_free(atom);
 err:
  return res;
}



/*
   resp-text-code  = "ALERT" /
                     "BADCHARSET" [SP "(" astring *(SP astring) ")" ] /
                     capability-data / "PARSE" /
                     "PERMANENTFLAGS" SP "(" [flag-perm *(SP flag-perm)] ")" /
                     "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
                     "UIDNEXT" SP nz-number / "UIDVALIDITY" SP nz-number /
                     "UNSEEN" SP nz-number /
                     atom [SP 1*<any TEXT-CHAR except "]">]
*/

static int
mailimap_resp_text_code_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx,
			      struct mailimap_resp_text_code ** result,
			      size_t progr_rate,
			      progress_function * progr_fun)
{
  size_t cur_token;
  struct mailimap_resp_text_code * resp_text_code;
  clist * badcharset;
  clist * permanentflags;
  struct mailimap_capability_data * cap_data;
  struct mailimap_extension_data * ext_data;
  int type;
  int r;
  int res;

  cur_token = * indx;

  resp_text_code = NULL;
  badcharset = NULL;
  cap_data = NULL;
  ext_data = NULL;
  permanentflags = NULL;

  type = MAILIMAP_RESP_TEXT_CODE_OTHER;
  r = mailimap_resp_text_code_1_parse(fd, buffer, parser_ctx, &cur_token, &type);
  if (r == MAILIMAP_NO_ERROR) {
    /* do nothing */
  }

  if (r == MAILIMAP_ERROR_PARSE) {

    r = mailimap_resp_text_code_badcharset_parse(fd, buffer, parser_ctx, &cur_token,
						 &badcharset,
						 progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_RESP_TEXT_CODE_BADCHARSET;
  }
  
  if (r == MAILIMAP_ERROR_PARSE) {

    r = mailimap_capability_data_parse(fd, buffer, parser_ctx, &cur_token,
				       &cap_data,
				       progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_RESP_TEXT_CODE_CAPABILITY_DATA;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_resp_text_code_permanentflags_parse(fd, buffer, parser_ctx, &cur_token,
						     &permanentflags,
						     progr_rate,
						     progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_RESP_TEXT_CODE_PERMANENTFLAGS;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_resp_text_code_number_parse(fd, buffer, parser_ctx, &cur_token,
					     &resp_text_code,
					     progr_rate, progr_fun);
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_extension_data_parse(MAILIMAP_EXTENDED_PARSER_RESP_TEXT_CODE,
        fd, buffer, parser_ctx, &cur_token, &ext_data,
        progr_rate, progr_fun);
    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_RESP_TEXT_CODE_EXTENSION;
  }

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_resp_text_code_other_parse(fd, buffer, parser_ctx, &cur_token,
					    &resp_text_code,
					    progr_rate, progr_fun);
  }

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

  if (resp_text_code == NULL) {
    resp_text_code = mailimap_resp_text_code_new(type,
						 badcharset, cap_data,
						 permanentflags,
						 0, 0, 0, NULL, NULL, ext_data);
    if (resp_text_code == NULL) {
      res = MAILIMAP_ERROR_MEMORY;
      goto free;
    }
  }

  * result = resp_text_code;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

free:
  if (permanentflags) {
    clist_foreach(permanentflags,
		  (clist_func) mailimap_flag_perm_free, NULL);
    clist_free(permanentflags);
  }
  if (cap_data)
    mailimap_capability_data_free(cap_data);
  if (badcharset) {
    clist_foreach(badcharset, (clist_func) mailimap_astring_free, NULL);
    clist_free(badcharset);
  }
  if (ext_data)
    mailimap_extension_data_free(ext_data);
err:
  return res;
}

/*
  UNIMPLEMENTED
   search          = "SEARCH" [SP "CHARSET" SP astring] 1*(SP search-key)
                       ; CHARSET argument to MUST be registered with IANA
*/

/*
  UNIMPLEMENTED
   search-key      = "ALL" / "ANSWERED" / "BCC" SP astring /
                     "BEFORE" SP date / "BODY" SP astring /
                     "CC" SP astring / "DELETED" / "FLAGGED" /
                     "FROM" SP astring / "KEYWORD" SP flag-keyword / "NEW" /
                     "OLD" / "ON" SP date / "RECENT" / "SEEN" /
                     "SINCE" SP date / "SUBJECT" SP astring /
                     "TEXT" SP astring / "TO" SP astring /
                     "UNANSWERED" / "UNDELETED" / "UNFLAGGED" /
                     "UNKEYWORD" SP flag-keyword / "UNSEEN" /
                       ; Above this line were in [IMAP2]
                     "DRAFT" / "HEADER" SP header-fld-name SP astring /
                     "LARGER" SP number / "NOT" SP search-key /
                     "OR" SP search-key SP search-key /
                     "SENTBEFORE" SP date / "SENTON" SP date /
                     "SENTSINCE" SP date / "SMALLER" SP number /
                     "UID" SP set / "UNDRAFT" / set /
                     "(" search-key *(SP search-key) ")"
*/

/*
   section         = "[" [section-spec] "]"
*/

static int
mailimap_section_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
		       size_t * indx,
		       struct mailimap_section ** result,
		       size_t progr_rate,
		       progress_function * progr_fun)
{
  struct mailimap_section_spec * section_spec;
  size_t cur_token;
  struct mailimap_section * section;
  int r;
  int res;

  cur_token = * indx;

  section_spec = NULL;

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

  r = mailimap_section_spec_parse(fd, buffer, parser_ctx, &cur_token, &section_spec,
				  progr_rate, progr_fun);
  if ((r != MAILIMAP_NO_ERROR) && (r != MAILIMAP_ERROR_PARSE)) {
    res = r;
    goto err;
  }

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

  if (section_spec == NULL)
    section = NULL;
  else {
    section = mailimap_section_new(section_spec);
    if (section == NULL) {
      res = MAILIMAP_ERROR_MEMORY;
      goto free;
    }
  }

  * result = section;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  mailimap_section_spec_free(section_spec);
 err:
  return res;
}

/*
   section-msgtext = "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list /
                     "TEXT"
                       ; top-level or MESSAGE/RFC822 part
*/

static int
mailimap_section_msgtext_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       struct mailimap_section_msgtext ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  size_t cur_token;
  int type;
  struct mailimap_header_list * header_list;
  struct mailimap_section_msgtext * msgtext;
  int r;
  int res;

  cur_token = * indx;

  header_list = NULL;

  type = mailimap_section_msgtext_get_token_value(fd, buffer, &cur_token);
  if (type == -1) {
    res = MAILIMAP_ERROR_PARSE;
    goto err;
  }

  if (type == MAILIMAP_SECTION_MSGTEXT_HEADER_FIELDS) {
    r = mailimap_header_list_parse(fd, buffer, parser_ctx, &cur_token, &header_list,
				   progr_rate, progr_fun);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto err;
    }
  }
  else if (type == MAILIMAP_SECTION_MSGTEXT_HEADER_FIELDS_NOT) {
    r = mailimap_header_list_parse(fd, buffer, parser_ctx, &cur_token, &header_list,
				   progr_rate, progr_fun);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto err;
    }
  }

  msgtext = mailimap_section_msgtext_new(type, header_list);
  if (msgtext == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_header_list;
  }

  * result = msgtext;
  * indx = cur_token;
  
  return MAILIMAP_NO_ERROR;

 free_header_list:
  if (header_list)
    mailimap_header_list_free(header_list);
 err:
  return res;
}

/*
   section-part    = nz-number *("." nz-number)
                       ; body part nesting
*/

static int
mailimap_section_part_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_section_part ** result,
			    size_t progr_rate,
			    progress_function * progr_fun)
{
  struct mailimap_section_part * section_part;
  size_t cur_token;
  clist * section_id;
  int r;
  int res;
  
  cur_token = * indx;
  section_id = NULL;

  r = mailimap_struct_list_parse(fd, buffer, parser_ctx, &cur_token, &section_id, '.',
				 (mailimap_struct_parser *)
				 mailimap_nz_number_alloc_parse,
				 (mailimap_struct_destructor *)
				 mailimap_number_alloc_free,
				 progr_rate, progr_fun);
  if (r != MAILIMAP_NO_ERROR) {
    res = r;
    goto err;
  }

  section_part = mailimap_section_part_new(section_id);
  if (section_part == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free_section_id;
  }

  * result = section_part;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
  
 free_section_id:
  clist_foreach(section_id, (clist_func) mailimap_number_alloc_free, NULL);
  clist_free(section_id);
 err:
  return res;
}

/*
   section-spec    = section-msgtext / (section-part ["." section-text])
*/

static int
mailimap_section_spec_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_section_spec ** result,
			    size_t progr_rate,
			    progress_function * progr_fun)
{
  int type;
  struct mailimap_section_msgtext * section_msgtext;
  struct mailimap_section_part * section_part;
  struct mailimap_section_text * section_text;
  struct mailimap_section_spec * section_spec;
  size_t cur_token;
  int r;
  int res;
  size_t final_token;

  cur_token = * indx;

  section_msgtext = NULL;
  section_part = NULL;
  section_text = NULL;

  r = mailimap_section_msgtext_parse(fd, buffer, parser_ctx, &cur_token,
				     &section_msgtext,
				     progr_rate, progr_fun);
  switch (r) {
  case MAILIMAP_NO_ERROR:
    type = MAILIMAP_SECTION_SPEC_SECTION_MSGTEXT;
    break;
    
  case MAILIMAP_ERROR_PARSE:
    
    r = mailimap_section_part_parse(fd, buffer, parser_ctx, &cur_token,
				    &section_part,
				    progr_rate, progr_fun);
    if (r != MAILIMAP_NO_ERROR) {
      res = r;
      goto err;
    }
    
    final_token = cur_token;
    
    type = MAILIMAP_SECTION_SPEC_SECTION_PART;
    
    r = mailimap_dot_parse(fd, buffer, parser_ctx, &cur_token);
    if (r == MAILIMAP_NO_ERROR) {
      r = mailimap_section_text_parse(fd, buffer, parser_ctx, &cur_token, &section_text,
				      progr_rate, progr_fun);
      if (r == MAILIMAP_NO_ERROR) {
	final_token = cur_token;
      }
      else if (r != MAILIMAP_ERROR_PARSE) {
	res = r;
	goto err;
      }
    }
    else if (r != MAILIMAP_ERROR_PARSE) {
      res = r;
      goto err;
    }
    
    cur_token = final_token;
    break;

  default:
    res = r;
    goto err;
  }
  
  section_spec = mailimap_section_spec_new(type, section_msgtext,
					   section_part, section_text);
  if (section_spec == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = section_spec;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (section_msgtext)
    mailimap_section_msgtext_free(section_msgtext);
  if (section_part)
    mailimap_section_part_free(section_part);
  if (section_text)
    mailimap_section_text_free(section_text);
 err:
  return res;
}

/*
   section-text    = section-msgtext / "MIME"
                       ; text other than actual body part (headers, etc.)
*/

static int
mailimap_section_text_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			    size_t * indx,
			    struct mailimap_section_text ** result,
			    size_t progr_rate,
			    progress_function * progr_fun)
{
  struct mailimap_section_msgtext * section_msgtext;
  size_t cur_token;
  struct mailimap_section_text * section_text;
  int type;
  int r;
  int res;
  
  cur_token = * indx;

  section_msgtext = NULL;

  type = MAILIMAP_SECTION_TEXT_ERROR; /* XXX - removes a gcc warning */

  r = mailimap_section_msgtext_parse(fd, buffer, parser_ctx, &cur_token, &section_msgtext,
				     progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    type = MAILIMAP_SECTION_TEXT_SECTION_MSGTEXT;

  if (r == MAILIMAP_ERROR_PARSE) {
    r= mailimap_token_case_insensitive_parse(fd, buffer, &cur_token, "MIME");

    if (r == MAILIMAP_NO_ERROR)
      type = MAILIMAP_SECTION_TEXT_MIME;
  }

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

  section_text = mailimap_section_text_new(type, section_msgtext);
  if (section_text == NULL) {
    res = MAILIMAP_ERROR_MEMORY;
    goto free;
  }

  * result = section_text;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;

 free:
  if (section_msgtext)
    mailimap_section_msgtext_free(section_msgtext);
 err:
  return res;
}

/*
  UNIMPLEMENTED
   select          = "SELECT" SP mailbox
*/

/*
  UNIMPLEMENTED
   sequence-num    = nz-number / "*"
                       ; * is the largest number in use.  For message
                       ; sequence numbers, it is the number of messages
                       ; in the mailbox.  For unique identifiers, it is
                       ; the unique identifier of the last message in
                       ; the mailbox.
*/

/*
  UNIMPLEMENTED
   set             = sequence-num / (sequence-num ":" sequence-num) /
                     (set "," set)
                       ; Identifies a set of messages.  For message
                       ; sequence numbers, these are consecutive
                       ; numbers from 1 to the number of messages in
                       ; the mailbox
                       ; Comma delimits individual numbers, colon
                       ; delimits between two numbers inclusive.
                       ; Example: 2,4:7,9,12:* is 2,4,5,6,7,9,12,13,
                       ; 14,15 for a mailbox with 15 messages.
*/

/*
  UNIMPLEMENTED
   status          = "STATUS" SP mailbox SP "(" status-att *(SP status-att) ")"
*/

/*
   status-att      = "MESSAGES" / "RECENT" / "UIDNEXT" / "UIDVALIDITY" /
                     "UNSEEN"
*/

int mailimap_status_att_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
  size_t * indx, int * result)
{
  int type;
  size_t cur_token;

  cur_token = * indx;

  type = mailimap_status_att_get_token_value(fd, buffer, &cur_token);

  if (type == -1)
    return MAILIMAP_ERROR_PARSE;

  * result = type;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}


/*
  UNIMPLEMENTED
   store           = "STORE" SP set SP store-att-flags
*/

/*
  UNIMPLEMENTED
   store-att-flags = (["+" / "-"] "FLAGS" [".SILENT"]) SP
                     (flag-list / (flag *(SP flag)))
*/

/*
   string          = quoted / literal
*/

static int
mailimap_string_parse_progress(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                               size_t * indx, char ** result,
                               size_t * result_len,
                               size_t progr_rate,
                               progress_function * progr_fun,
                               mailprogress_function * body_progr_fun,
                               mailprogress_function * items_progr_fun,
                               void * context,
                               mailimap_msg_att_handler * msg_att_handler,
                               void * msg_att_context)
{
  size_t cur_token;
  char * string;
  int r;
  size_t len;

  cur_token = * indx;

  string = NULL;
  len = 0;
  
  r = mailimap_quoted_parse(fd, buffer, parser_ctx, &cur_token, &string,
			    progr_rate, progr_fun);
  if (r == MAILIMAP_NO_ERROR)
    len = strlen(string);
  else if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_literal_parse_progress(fd, buffer, parser_ctx, &cur_token, &string, &len,
			       progr_rate, progr_fun, body_progr_fun, items_progr_fun, context, msg_att_handler, msg_att_context);
  }

  if (r != MAILIMAP_NO_ERROR)
    return r;

  * result = string;
  if (result_len != NULL)
    * result_len = len;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

int
mailimap_string_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
                      size_t * indx, char ** result,
                      size_t * result_len,
                      size_t progr_rate,
                      progress_function * progr_fun)
{
  return mailimap_string_parse_progress(fd, buffer, parser_ctx, indx, result,
                                        result_len, progr_rate, progr_fun,
                                        NULL, NULL, NULL, NULL, NULL);
}

/*
  UNIMPLEMENTED
   subscribe       = "SUBSCRIBE" SP mailbox
*/

/*
   tag             = 1*<any ASTRING-CHAR except "+">
*/

/*
  any ASTRING-CHAR except "+"
*/

static int is_tag_char(char ch)
{
  if (ch == '+')
    return FALSE;
  return is_astring_char(ch);
}

/*
   tag             = 1*<any ASTRING-CHAR except "+">
*/

static int mailimap_tag_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			      size_t * indx, char ** result,
			      size_t progr_rate,
			      progress_function * progr_fun)
{
  size_t cur_token;
  char * tag;
  int r;

  cur_token = * indx;

  r = mailimap_custom_string_parse(fd, buffer, parser_ctx, &cur_token, &tag,
				   is_tag_char);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * indx = cur_token;
  * result = tag;

  return MAILIMAP_NO_ERROR;
}

/*
   text            = 1*TEXT-CHAR
*/

static int mailimap_text_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx, char ** result,
			       size_t progr_rate,
			       progress_function * progr_fun)
{
  return mailimap_custom_string_parse(fd, buffer, parser_ctx, indx, result,
				      is_text_char);
}


/*
   TEXT-CHAR       = <any CHAR except CR and LF>
*/

static int is_text_char(char ch)
{
  if ((ch == '\r') || (ch == '\n'))
    return FALSE;

  return is_char(ch);
}

/*
   time            = 2DIGIT ":" 2DIGIT ":" 2DIGIT
                       ; Hours minutes seconds
*/

/*
  2DIGIT
*/

static int mailimap_2digit_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
				 size_t * indx, int * result)
{
#ifndef UNSTRICT_SYNTAX
  int digit;
  int two_digit;
  size_t cur_token;
  int r;

  cur_token = * indx;

  r = mailimap_digit_parse(fd, buffer, &cur_token, &digit);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  two_digit = digit;
  
  r = mailimap_digit_parse(fd, buffer, &cur_token, &digit);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  two_digit = two_digit * 10 + digit;

  * result = two_digit;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
#else
  uint32_t number;
  size_t cur_token;
  int r;
  
  cur_token = * indx;
  
  r = mailimap_number_parse(fd, buffer, &cur_token, &number);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  * indx = cur_token;
  * result = number;

  return MAILIMAP_NO_ERROR;
#endif
}

/*
   time            = 2DIGIT ":" 2DIGIT ":" 2DIGIT
                       ; Hours minutes seconds
*/

static int mailimap_time_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx,
			       int * phour, int * pmin, int * psec)
{
  size_t cur_token;
  int hour;
  int min;
  int sec;
  int r;
  
  cur_token = * indx;

  r = mailimap_2digit_parse(fd, buffer, parser_ctx, &cur_token, &hour);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_colon_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_2digit_parse(fd, buffer, parser_ctx, &cur_token, &min);
  if (r != MAILIMAP_NO_ERROR)
    return r;
  
  r = mailimap_colon_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  r = mailimap_2digit_parse(fd, buffer, parser_ctx, &cur_token, &sec);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  * phour = hour;
  * pmin = min;
  * psec = sec;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

/*
  UNIMPLEMENTED
   uid             = "UID" SP (copy / fetch / search / store)
                       ; Unique identifiers used instead of message
                       ; sequence numbers
*/

/*
   uniqueid        = nz-number
                       ; Strictly ascending
*/

int mailimap_uniqueid_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
    size_t * indx, uint32_t * result)
{
  return mailimap_nz_number_parse(fd, buffer, parser_ctx, indx, result);
}

/*
  UNIMPLEMENTED
   unsubscribe     = "UNSUBSCRIBE" SP mailbox
*/

/*
  UNIMPLEMENTED
   userid          = astring
*/

/*
  UNIMPLEMENTED
   x-command       = "X" atom <experimental command arguments>
*/

/*
   zone            = ("+" / "-") 4DIGIT
                       ; Signed four-digit value of hhmm representing
                       ; hours and minutes east of Greenwich (that is,
                       ; the amount that the given time differs from
                       ; Universal Time).  Subtracting the timezone
                       ; from the given time will give the UT form.
                       ; The Universal Time zone is "+0000".
*/

static int mailimap_zone_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
			       size_t * indx, int * result)
{
  size_t cur_token;
  uint32_t zone;
#ifndef UNSTRICT_SYNTAX
  int i;
  int digit;
#endif
  int sign;
  int r;

  cur_token = * indx;

  sign = 1;
  r = mailimap_plus_parse(fd, buffer, parser_ctx, &cur_token);
  if (r == MAILIMAP_NO_ERROR)
    sign = 1;

  if (r == MAILIMAP_ERROR_PARSE) {
    r = mailimap_minus_parse(fd, buffer, parser_ctx, &cur_token);
    if (r == MAILIMAP_NO_ERROR)
      sign = -1;
  }

  if (r != MAILIMAP_NO_ERROR)
    return r;

#ifdef UNSTRICT_SYNTAX
  r = mailimap_number_parse(fd, buffer, &cur_token, &zone);
  if (r != MAILIMAP_NO_ERROR)
    return r;
#else
  zone = 0;
  for(i = 0 ; i < 4 ; i ++) {
    r = mailimap_digit_parse(fd, buffer, &cur_token, &digit);
    if (r != MAILIMAP_NO_ERROR)
      return r;
    zone = zone * 10 + digit;
  }
#endif

  zone *= sign;

  * result = zone;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

int mailimap_uint64_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
  size_t * indx, uint64_t * result)
{
  size_t cur_token;
  int digit;
  uint64_t number;
  int parsed;
  int r;

  cur_token = * indx;
  parsed = 0;

  mailimap_space_parse(fd, buffer, &cur_token);

  number = 0;
  while (1) {
    r = mailimap_digit_parse(fd, buffer, &cur_token, &digit);
    if (r == MAILIMAP_ERROR_PARSE)
      break;
    else if (r == MAILIMAP_NO_ERROR) {
      number *= 10;
      number += digit;
      parsed = 1;
    }
    else
      return r;
  }

  if (!parsed)
    return MAILIMAP_ERROR_PARSE;

  * result = number;
  * indx = cur_token;

  return MAILIMAP_NO_ERROR;
}

int mailimap_mod_sequence_value_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
  size_t * indx, uint64_t * result)
{
  return mailimap_uint64_parse(fd, buffer, parser_ctx, indx, result);
}

static int mailimap_seq_number_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
  size_t * indx, uint32_t * result)
{
  int r;
  uint32_t value;
  size_t cur_token;  

  cur_token  = * indx;
  
  r = mailimap_nz_number_parse(fd, buffer, parser_ctx, &cur_token, &value);
  if (r == MAILIMAP_NO_ERROR) {
    * indx = cur_token;
    * result = value;
    return MAILIMAP_NO_ERROR;
  }
  r = mailimap_star_parse(fd, buffer, parser_ctx, &cur_token);
  if (r == MAILIMAP_NO_ERROR) {
    * indx = cur_token;
    * result = 0;
    return MAILIMAP_NO_ERROR;
  }
  
  return MAILIMAP_ERROR_PARSE;
}

static int mailimap_seq_range_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
  size_t * indx, uint32_t * result_left, uint32_t * result_right)
{
  size_t cur_token;  
  int r;
  uint32_t left;
  uint32_t right;

  cur_token  = * indx;
  r = mailimap_seq_number_parse(fd, buffer, parser_ctx, &cur_token, &left);
  if (r != MAILIMAP_NO_ERROR) {
    return r;
  }
  
  r = mailimap_colon_parse(fd, buffer, parser_ctx, &cur_token);
  if (r != MAILIMAP_NO_ERROR) {
    return r;
  }
  
  r = mailimap_seq_number_parse(fd, buffer, parser_ctx, &cur_token, &right);
  if (r != MAILIMAP_NO_ERROR) {
    return r;
  }
  
  * indx = cur_token;
  * result_left = left;
  * result_right = right;
  
  return MAILIMAP_NO_ERROR;
}

static int mailimap_set_item_parse(mailstream * fd, MMAPString * buffer, struct mailimap_parser_context * parser_ctx,
  size_t * indx, struct mailimap_set_item ** result,
  size_t progr_rate,
  progress_function * progr_fun)
{
  int r;
  uint32_t value;
  uint32_t left;
  uint32_t right;
  size_t cur_token;
  
  cur_token = * indx;
  
  r = mailimap_seq_range_parse(fd, buffer, parser_ctx, &cur_token, &left, &right);
  if (r == MAILIMAP_NO_ERROR) {
    struct mailimap_set_item * item;
    
    item = mailimap_set_item_new(left, right);
    if (item == NULL) {
      return MAILIMAP_ERROR_MEMORY;
    }
    * result = item;
    * indx = cur_token;
    return MAILIMAP_NO_ERROR;
  }
  
  r = mailimap_seq_number_parse(fd, buffer, parser_ctx, &cur_token, &value);
  if (r == MAILIMAP_NO_ERROR) {
    struct mailimap_set_item * item;
    
    item = mailimap_set_item_new(value, value);
    if (item == NULL) {
      return MAILIMAP_ERROR_MEMORY;
    }
    * result = item;
    * indx = cur_token;
    return MAILIMAP_NO_ERROR;
  }
  
  return MAILIMAP_ERROR_PARSE;
}

int mailimap_set_parse(mailstream * fd,
  MMAPString * buffer, struct mailimap_parser_context * parser_ctx, size_t * indx, struct mailimap_set ** result)
{
  size_t cur_token;
  struct mailimap_set * set;
  clist * item_list;
  int r;
  
  cur_token = * indx;
  
  r = mailimap_struct_list_parse(fd, buffer, parser_ctx, &cur_token, &item_list,
    ',', (mailimap_struct_parser *) mailimap_set_item_parse, (mailimap_struct_destructor *)mailimap_set_item_free,
    0, NULL);
  if (r != MAILIMAP_NO_ERROR)
    return r;

  set = mailimap_set_new(item_list);
  if (set == NULL) {
    clist_foreach(item_list, (clist_func) mailimap_set_item_free, NULL);
    clist_free(item_list);
    return MAILIMAP_ERROR_MEMORY;
  }

  * indx = cur_token;
  * result = set;
  
  return MAILIMAP_NO_ERROR;
}

static int has_crlf(MMAPString * buffer, size_t index)
{
  while (index < buffer->len) {
    if (buffer->str[index] == '\n')
      return 1;
    index ++;
  }
  return 0;
}

