/*
 * Copyright (C) 1996-1999 Brandon Long <blong@fiction.net>
 * Copyright (C) 1999-2009 Brendan Cully <brendan@kublai.com>
 *
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#ifndef _IMAP_PRIVATE_H
#define _IMAP_PRIVATE_H 1

#include "imap.h"
#include "mutt_curses.h"
#include "mutt_socket.h"
#include "bcache.h"
#ifdef USE_HCACHE
#include "hcache.h"
#endif
#include "message.h"  /* for IMAP_HEADER_DATA */

/* -- symbols -- */
#define IMAP_PORT 143
#define IMAP_SSL_PORT 993

/* logging levels */
#define IMAP_LOG_CMD  2
#define IMAP_LOG_LTRL 4
#define IMAP_LOG_PASS 5

/* IMAP command responses. Used in IMAP_COMMAND.state too */
/* <tag> OK ... */
#define IMAP_CMD_OK       (0)
/* <tag> BAD ... */
#define IMAP_CMD_BAD      (-1)
/* <tag> NO ... */
#define IMAP_CMD_NO       (-2)
/* * ... */
#define IMAP_CMD_CONTINUE (1)
/* + */
#define IMAP_CMD_RESPOND  (2)
/* IMAP_COMMAND.state additions */
#define IMAP_CMD_NEW    (3)

/* number of entries in the hash table */
#define IMAP_CACHE_LEN 10

#define SEQLEN 5
/* maximum length of command lines before they must be split (for
 * lazy servers) */
#define IMAP_MAX_CMDLEN 1024

#define IMAP_REOPEN_ALLOW     (1<<0)
#define IMAP_EXPUNGE_EXPECTED (1<<1)
#define IMAP_EXPUNGE_PENDING  (1<<2)
#define IMAP_NEWMAIL_PENDING  (1<<3)
#define IMAP_FLAGS_PENDING    (1<<4)

/* imap_exec flags (see imap_exec) */
#define IMAP_CMD_FAIL_OK (1<<0)
#define IMAP_CMD_PASS    (1<<1)
#define IMAP_CMD_QUEUE   (1<<2)
#define IMAP_CMD_POLL    (1<<3)

enum
{
  IMAP_FATAL = 1,
  IMAP_BYE
};

enum
{
  /* States */
  IMAP_DISCONNECTED = 0,
  IMAP_CONNECTED,
  IMAP_AUTHENTICATED,
  IMAP_SELECTED,

  /* and pseudo-states */
  IMAP_IDLE
};

enum
{
  /* Namespace types */
  IMAP_NS_PERSONAL = 0,
  IMAP_NS_OTHER,
  IMAP_NS_SHARED
};

/* Capabilities we are interested in */
enum
{
  IMAP4 = 0,
  IMAP4REV1,
  STATUS,
  ACL,                          /* RFC 2086: IMAP4 ACL extension */
  NAMESPACE,                    /* RFC 2342: IMAP4 Namespace */
  ACRAM_MD5,                    /* RFC 2195: CRAM-MD5 authentication */
  AGSSAPI,                      /* RFC 1731: GSSAPI authentication */
  AUTH_ANON,                    /* AUTH=ANONYMOUS */
  AUTH_OAUTHBEARER,             /* RFC 7628: AUTH=OAUTHBEARER */
  AUTH_XOAUTH2,                 /* Deprecated precursor to OAUTHBEARER */
  STARTTLS,                     /* RFC 2595: STARTTLS */
  LOGINDISABLED,                /*           LOGINDISABLED */
  IDLE,                         /* RFC 2177: IDLE */
  SASL_IR,                      /* SASL initial response draft */
  ENABLE,                       /* RFC 5161 */
  CONDSTORE,                    /* RFC 7162 */
  QRESYNC,                      /* RFC 7162 */
  LIST_EXTENDED,                /* RFC 5258: IMAP4 - LIST Command Extensions */
  COMPRESS_DEFLATE,             /* RFC 4978: COMPRESS=DEFLATE */

  CAPMAX
};

/* imap_conn_find flags */
#define MUTT_IMAP_CONN_NONEW    (1<<0)
#define MUTT_IMAP_CONN_NOSELECT (1<<1)

/* -- data structures -- */
typedef struct
{
  unsigned int uid;
  char *path;
} IMAP_CACHE;

typedef struct
{
  const char *name;

  unsigned int messages;
  unsigned int recent;
  unsigned int uidnext;
  unsigned int uidvalidity;
  unsigned int unseen;
  unsigned long long modseq;  /* Used by CONDSTORE. 1 <= modseq < 2^63 */
} IMAP_STATUS;

typedef struct
{
  char *name;

  char delim;
  /* if we end up storing a lot of these we could turn this into a bitfield */
  unsigned char noselect;
  unsigned char noinferiors;
} IMAP_LIST;

/* IMAP command structure */
typedef struct
{
  char seq[SEQLEN+1];
  int state;
} IMAP_COMMAND;

typedef enum
{
  IMAP_CT_NONE = 0,
  IMAP_CT_LIST,
  IMAP_CT_STATUS
} IMAP_COMMAND_TYPE;

typedef struct
{
  /* This data is specific to a CONNECTION to an IMAP server */
  CONNECTION *conn;
  unsigned char state;
  unsigned char status;
  /* let me explain capstr: SASL needs the capability string (not bits).
   * we have 3 options:
   *   1. rerun CAPABILITY inside SASL function.
   *   2. build appropriate CAPABILITY string by reverse-engineering from bits.
   *   3. keep a copy until after authentication.
   * I've chosen (3) for now. (2) might not be too bad, but it involves
   * tracking all possible capabilities. bah. (1) I don't like because
   * it's just no fun to get the same information twice */
  char *capstr;
  unsigned char capabilities[(CAPMAX + 7)/8];
  unsigned int seqno;
  time_t lastread; /* last time we read a command for the server */
  char *buf;
  size_t blen;

  /* If nonzero, we can send UTF-8, and the server will use UTF8 rather
   * than mUTF7 */
  int unicode;

  int qresync;  /* Set to 1 if QRESYNC is successfully ENABLE'd */

  /* if set, the response parser will store results for complicated commands
   * here. */
  IMAP_COMMAND_TYPE cmdtype;
  void *cmddata;

  /* command queue */
  IMAP_COMMAND *cmds;
  int cmdslots;
  int nextcmd;
  int lastcmd;
  BUFFER *cmdbuf;

  /* cache IMAP_STATUS of visited mailboxes */
  LIST *mboxcache;

  /* The following data is all specific to the currently SELECTED mbox */
  char delim;
  CONTEXT *ctx;
  char *mailbox;
  unsigned short check_status;
  unsigned char reopen;
  unsigned int newMailCount;   /* Set when EXISTS notifies of new mail */
  IMAP_CACHE cache[IMAP_CACHE_LEN];
  HASH *uid_hash;
  unsigned int uid_validity;
  unsigned int uidnext;
  unsigned long long modseq;
  HEADER **msn_index;          /* look up headers by (MSN-1) */
  unsigned int msn_index_size; /* allocation size */
  unsigned int max_msn;        /* the largest MSN fetched so far */
  body_cache_t *bcache;

  /* all folder flags - system flags AND keywords */
  LIST *flags;
#ifdef USE_HCACHE
  header_cache_t *hcache;
#endif
} IMAP_DATA;
/* I wish that were called IMAP_CONTEXT :( */

typedef struct
{
  char *full_seqset;
  char *eostr;
  int in_range;
  int down;
  unsigned int range_cur, range_end;
  char *substr_cur, *substr_end;
} SEQSET_ITERATOR;

/* -- macros -- */
#define CTX_DATA ((IMAP_DATA *) ctx->data)

/* -- private IMAP functions -- */
/* imap.c */
int imap_create_mailbox(IMAP_DATA *idata, char *mailbox);
int imap_rename_mailbox(IMAP_DATA *idata, IMAP_MBOX *mx, const char *newname);
IMAP_STATUS *imap_mboxcache_get(IMAP_DATA *idata, const char *mbox,
                                int create);
void imap_mboxcache_free(IMAP_DATA *idata);
int imap_exec_msgset(IMAP_DATA *idata, const char *pre, const char *post,
                     int flag, int changed, int invert);
int imap_open_connection(IMAP_DATA *idata);
void imap_close_connection(IMAP_DATA *idata);
IMAP_DATA *imap_conn_find(const ACCOUNT *account, int flags);
int imap_read_literal(FILE *fp, IMAP_DATA *idata, unsigned int bytes, progress_t*);
void imap_expunge_mailbox(IMAP_DATA *idata);
void imap_logout(IMAP_DATA **idata);
int imap_sync_message_for_copy(IMAP_DATA *idata, HEADER *hdr, BUFFER *cmd,
                               int *err_continue);
int imap_has_flag(LIST *flag_list, const char *flag);
int imap_reconnect(IMAP_DATA **p_idata);

/* auth.c */
int imap_authenticate(IMAP_DATA *idata);

/* command.c */
int imap_cmd_start(IMAP_DATA *idata, const char *cmd);
int imap_cmd_step(IMAP_DATA *idata);
void imap_cmd_finish(IMAP_DATA *idata);
int imap_code(const char *s);
const char *imap_cmd_trailer(IMAP_DATA *idata);
int imap_exec(IMAP_DATA *idata, const char *cmd, int flags);
int imap_cmd_idle(IMAP_DATA *idata);

/* message.c */
void imap_add_keywords(char *s, HEADER *keywords, LIST *mailbox_flags, size_t slen);
void imap_free_header_data(IMAP_HEADER_DATA **data);
int imap_read_headers(IMAP_DATA *idata, unsigned int msn_begin, unsigned int msn_end,
                      int initial_download);
char *imap_set_flags(IMAP_DATA *idata, HEADER *h, char *s, int *server_changes);
int imap_cache_del(IMAP_DATA *idata, HEADER *h);
int imap_cache_clean(IMAP_DATA *idata);

int imap_fetch_message(CONTEXT *ctx, MESSAGE *msg, int msgno, int headers);
int imap_close_message(CONTEXT *ctx, MESSAGE *msg);
int imap_commit_message(CONTEXT *ctx, MESSAGE *msg);

/* util.c */
#ifdef USE_HCACHE
header_cache_t *imap_hcache_open(IMAP_DATA *idata, const char *path);
void imap_hcache_close(IMAP_DATA *idata);
HEADER *imap_hcache_get(IMAP_DATA *idata, unsigned int uid);
int imap_hcache_put(IMAP_DATA *idata, HEADER *h);
int imap_hcache_del(IMAP_DATA *idata, unsigned int uid);
int imap_hcache_store_uid_seqset(IMAP_DATA *idata);
int imap_hcache_clear_uid_seqset(IMAP_DATA *idata);
char *imap_hcache_get_uid_seqset(IMAP_DATA *idata);
#endif

int imap_continue(const char *msg, const char *resp);
void imap_error(const char *where, const char *msg);
IMAP_DATA *imap_new_idata(void);
void imap_free_idata(IMAP_DATA **idata);
char *imap_fix_path(IMAP_DATA *idata, const char *mailbox, char *path,
                    size_t plen);
void imap_cachepath(IMAP_DATA *idata, const char *mailbox, BUFFER *dest);
int imap_get_literal_count(char *buf, unsigned int *bytes);
char *imap_get_qualifier(char *buf);
int imap_mxcmp(const char *mx1, const char *mx2);
char *imap_next_word(char *s);
time_t imap_parse_date(char *s);
void imap_make_date(BUFFER *buf, time_t timestamp);
void imap_qualify_path(char *dest, size_t len, IMAP_MBOX *mx, char *path);
void imap_buffer_qualify_path(BUFFER *dest, IMAP_MBOX *mx, char *path);
void imap_quote_string(char *dest, size_t dlen, const char *src);
void imap_quote_string_and_backquotes(char *dest, size_t dlen, const char *src);
void imap_unquote_string(char *s);
void imap_munge_mbox_name(IMAP_DATA *idata, char *dest, size_t dlen, const char *src);
void imap_unmunge_mbox_name(IMAP_DATA *idata, char *s);
int imap_wordcasecmp(const char *a, const char *b);
SEQSET_ITERATOR *mutt_seqset_iterator_new(const char *seqset);
int mutt_seqset_iterator_next(SEQSET_ITERATOR *iter, unsigned int *next);
void mutt_seqset_iterator_free(SEQSET_ITERATOR **p_iter);

/* utf7.c */
void imap_utf_encode(IMAP_DATA *idata, char **s);
void imap_utf_decode(IMAP_DATA *idata, char **s);

#if USE_HCACHE
/* typedef size_t (*hcache_keylen_t)(const char *fn); */
#define imap_hcache_keylen mutt_strlen
#endif /* USE_HCACHE */

#endif
