/*
 * Program:	Mailbox Access routines
 *
 * Author:	Mark Crispin
 *		Networks and Distributed Computing
 *		Computing & Communications
 *		University of Washington
 *		Administration Building, AG-44
 *		Seattle, WA  98195
 *		Internet: MRC@CAC.Washington.EDU
 *
 * Date:	22 November 1989
 * Last Edited:	14 September 1993
 *
 * Copyright 1993 by the University of Washington
 *
 *  Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appears in all copies and that both the
 * above copyright notice and this permission notice appear in supporting
 * documentation, and that the name of the University of Washington not be
 * used in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  This software is made
 * available "as is", and
 * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
 * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
 * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
 * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

/* Build parameters */

#define CACHEINCREMENT 100	/* cache growth increments */
#define MAILTMPLEN 1024		/* size of a temporary buffer */
#define MAXMESSAGESIZE 65000	/* MS-DOS: maximum text buffer size
				 * other:  initial text buffer size */
#define NUSERFLAGS 30		/* # of user flags (current servers 30 max) */
#define BASEYEAR 1969		/* the year time began */


/* Constants */

#define NIL 0			/* convenient name */
#define T 1			/* opposite of NIL */
#define LONGT (long) 1		/* long T */

#define WARN (long) 1		/* mm_log warning type */
#define ERROR (long) 2		/* mm_log error type */
#define PARSE (long) 3		/* mm_log parse error type */


/* Open options */

#define OP_DEBUG (long) 1	/* debug protocol negotiations */
#define OP_READONLY (long) 2	/* read-only open */
#define OP_ANONYMOUS (long) 4	/* anonymous open of newsgroup */
#define OP_SHORTCACHE (long) 8	/* short (elt-only) caching */
#define OP_SILENT (long) 16	/* don't pass up events (internal use) */
#define OP_PROTOTYPE (long) 32	/* return driver prototype */
#define OP_HALFOPEN (long) 64	/* half-open (IMAP connect but no select) */


/* Cache management function codes */

#define CH_INIT (long) 10	/* initialize cache */
#define CH_SIZE (long) 11	/* (re-)size the cache */
#define CH_MAKELELT (long) 20	/* return long elt, make if needed */
#define CH_LELT (long) 21	/* return long elt if exists */
#define CH_MAKEELT (long) 30	/* return short elt, make if needed */
#define CH_ELT (long) 31	/* return short elt if exists */
#define CH_FREE (long) 40	/* free space used by elt */
#define CH_EXPUNGE (long) 45	/* delete elt pointer from list */


/* Garbage collection flags */

#define GC_ELT (long) 1		/* message cache elements */
#define GC_ENV (long) 2		/* envelopes and bodies */
#define GC_TEXTS (long) 4	/* cached texts */

/* Message structures */


/* Item in an address list */

#define ADDRESS struct mail_address
ADDRESS {
  char *personal;		/* personal name phrase */
  char *adl;			/* at-domain-list source route */
  char *mailbox;		/* mailbox name */
  char *host;			/* domain name of mailbox's host */
  char *error;			/* error in address from SMTP module */
  ADDRESS *next;		/* pointer to next address in list */
};


/* Message envelope */

typedef struct mail_envelope {
  char *remail;			/* remail header if any */
  ADDRESS *return_path;		/* error return address */
  char *date;			/* message composition date string */
  ADDRESS *from;		/* originator address list */
  ADDRESS *sender;		/* sender address list */
  ADDRESS *reply_to;		/* reply address list */
  char *subject;		/* message subject string */
  ADDRESS *to;			/* primary recipient list */
  ADDRESS *cc;			/* secondary recipient list */
  ADDRESS *bcc;			/* blind secondary recipient list */
  char *in_reply_to;		/* replied message ID */
  char *message_id;		/* message ID */
  char *newsgroups;		/* USENET newsgroups */
} ENVELOPE;

/* Primary body types */
/* If you change any of these you must also change body_types in rfc822.c */

extern const char *body_types[];/* defined body type strings */

#define TYPETEXT 0		/* unformatted text */
#define TYPEMULTIPART 1		/* multiple part */
#define TYPEMESSAGE 2		/* encapsulated message */
#define TYPEAPPLICATION 3	/* application data */
#define TYPEAUDIO 4		/* audio */
#define TYPEIMAGE 5		/* static image */
#define TYPEVIDEO 6		/* video */
#define TYPEOTHER 7		/* unknown */


/* Body encodings */
/* If you change any of these you must also change body_encodings in rfc822.c
 */

				/* defined body encoding strings */
extern const char *body_encodings[];

#define ENC7BIT 0		/* 7 bit SMTP semantic data */
#define ENC8BIT 1		/* 8 bit SMTP semantic data */
#define ENCBINARY 2		/* 8 bit binary data */
#define ENCBASE64 3		/* base-64 encoded data */
#define ENCQUOTEDPRINTABLE 4	/* human-readable 8-as-7 bit data */
#define ENCOTHER 5		/* unknown */


/* Body contents */

#define BINARY void
#define BODY struct mail_body
#define MESSAGE struct mail_body_message
#define PARAMETER struct mail_body_parameter
#define PART struct mail_body_part

/* Message content (ONLY for parsed messages) */

MESSAGE {
  ENVELOPE *env;		/* message envelope */
  BODY *body;			/* message body */
  char *text;			/* message in RFC-822 form */
  unsigned long offset;		/* offset of text from header */
};


/* Parameter list */

PARAMETER {
  char *attribute;		/* parameter attribute name */
  char *value;			/* parameter value */
  PARAMETER *next;		/* next parameter in list */
};


/* Message body structure */

BODY {
  unsigned short type;		/* body primary type */
  unsigned short encoding;	/* body transfer encoding */
  char *subtype;		/* subtype string */
  PARAMETER *parameter;		/* parameter list */
  char *id;			/* body identifier */
  char *description;		/* body description */
  union {			/* different ways of accessing contents */
    unsigned char *text;	/* body text (+ enc. message in composing) */
    BINARY *binary;		/* body binary */
    PART *part;			/* body part list */
    MESSAGE msg;		/* body encapsulated message (PARSE ONLY) */
  } contents;
  struct {
    unsigned long lines;	/* size in lines */
    unsigned long bytes;	/* size in bytes */
    unsigned long ibytes;	/* internal size in bytes (drivers ONLY!!) */
  } size;
};


/* Multipart content list */

PART {
  BODY body;			/* body information for this part */
  unsigned long offset;		/* offset from body origin */
  PART *next;			/* next body part */
};

/* Entry in the message cache array */

typedef struct message_cache {
  unsigned long msgno;		/* message number */
  /* The next 8 bytes is ordered in this way so that it will be reasonable even
   * on a 36-bit machine.  Maybe someday I'll port this to TOPS-20.  ;-)
   */
			/* internal time/zone, system flags (4 bytes) */
  unsigned int hours: 5;	/* hours (0-23) */
  unsigned int minutes: 6;	/* minutes (0-59) */
  unsigned int seconds: 6;	/* seconds (0-59) */
  /* It may seem easier to have zhours be signed.  Unfortunately, a certain
   * cretinous C compiler from a well-known software vendor in Redmond, WA
   * does not allow signed bit fields.
   */
  unsigned int zoccident : 1;	/* non-zero if west of UTC */
  unsigned int zhours : 4;	/* hours from UTC (0-12) */
  unsigned int zminutes: 6;	/* minutes (0-59) */
  unsigned int seen : 1;	/* system Seen flag */
  unsigned int deleted : 1;	/* system Deleted flag */
  unsigned int flagged : 1; 	/* system Flagged flag */
  unsigned int answered : 1;	/* system Answered flag */
			/* flags, lock count (2 bytes) */
  unsigned int xxxx : 1;	/* system flag reserved for future */
  unsigned int yyyy : 1;	/* system flag reserved for future */
  unsigned int recent : 1;	/* message is new as of this mailbox open */
  unsigned int searched : 1;	/* message was searched */
  unsigned int sequence : 1;	/* (driver use) message is in sequence */
  unsigned int spare : 1;	/* reserved for use by main program */
  unsigned int zzzz : 2;	/* reserved for future assignment */
  unsigned int lockcount : 8;	/* non-zero if multiple references */
			/* internal date (2 bytes) */
  unsigned int day : 5;		/* day of month (1-31) */
  unsigned int month : 4;	/* month of year (1-12) */
  unsigned int year : 7;	/* year since 1969 (expires 2097) */
  unsigned long user_flags;	/* user-assignable flags */
  unsigned long rfc822_size;	/* # of bytes of message as raw RFC822 */
  unsigned long data1;		/* (driver use) first data item */
  unsigned long data2;		/* (driver use) second data item */
} MESSAGECACHE;


typedef struct long_cache {
  MESSAGECACHE elt;
  ENVELOPE *env;		/* pointer to message envelope */
  BODY *body;			/* pointer to message body */
} LONGCACHE;

/* String structure */

#define STRINGDRIVER struct string_driver

typedef struct mailstring {
  void *data;			/* driver-dependent data */
  unsigned long data1;		/* driver-dependent data */
  unsigned long size;		/* total length of string */
  char *chunk;			/* base address of chunk */
  unsigned long chunksize;	/* size of chunk */
  unsigned long offset;		/* offset of this chunk in base */
  char *curpos;			/* current position in chunk */
  unsigned long cursize;	/* number of bytes remaining in chunk */
  STRINGDRIVER *dtb;		/* driver that handles this type of string */
} STRING;


/* Dispatch table for string driver */

STRINGDRIVER {
				/* initialize string driver */
  void (*init) ();
				/* get next character in string */
  char (*next) ();
				/* set position in string */
  void (*setpos) ();
};


/* Stringstruct access routines */

#define INIT(s,d,data,size) ((*((s)->dtb = &d)->init) (s,data,size))
#define SIZE(s) ((s)->size - GETPOS (s))
#define CHR(s) (*(s)->curpos)
#define SNX(s) (--(s)->cursize ? *(s)->curpos++ : (*(s)->dtb->next) (s))
#define GETPOS(s) ((s)->offset + ((s)->curpos - (s)->chunk))
#define SETPOS(s,i) (*(s)->dtb->setpos) (s,i)

/* Mail Access I/O stream */


/* Structure for mail driver dispatch */

#define DRIVER struct driver	


/* Mail I/O stream */

typedef struct mail_stream {
  DRIVER *dtb;			/* dispatch table for this driver */
  void *local;			/* pointer to driver local data */
  char *mailbox;		/* mailbox name */
  unsigned int lock : 1;	/* stream lock flag */
  unsigned int debug : 1;	/* stream debug flag */
  unsigned int silent : 1;	/* silent stream from Tenex */
  unsigned int readonly : 1;	/* stream read-only flag */
  unsigned int anonymous : 1;	/* stream anonymous access flag */
  unsigned int scache : 1;	/* stream short cache flag */
  unsigned int halfopen : 1;	/* stream half-open flag */
  unsigned short use;		/* stream use count */
  unsigned short sequence;	/* stream sequence */
  unsigned long gensym;		/* generated tag */
  unsigned long nmsgs;		/* # of associated msgs */
  unsigned long recent;		/* # of recent msgs */
  char *flagstring;		/* buffer of user keyflags */
  char *user_flags[NUSERFLAGS];	/* pointers to user flags in bit order */
  unsigned long cachesize;	/* size of message cache */
  union {
    void **c;			/* to get at the cache in general */
    MESSAGECACHE **s;		/* message cache array */
    LONGCACHE **l;		/* long cache array */
  } cache;
  unsigned long msgno;		/* message number of `current' message */
  ENVELOPE *env;		/* pointer to `current' message envelope */
  BODY *body;			/* pointer to `current' message body */
  char *text;			/* pointer to `current' text */
} MAILSTREAM;


/* Mail I/O stream handle */

typedef struct mail_stream_handle {
  MAILSTREAM *stream;		/* pointer to mail stream */
  unsigned short sequence;	/* sequence of what we expect stream to be */
} MAILHANDLE;

/* Mail driver dispatch */

DRIVER {
  char *name;			/* driver name */
  DRIVER *next;			/* next driver */
				/* mailbox is valid for us */
  DRIVER *(*valid) ();
				/* manipulate driver parameters */
  void *(*parameters) ();
				/* find mailboxes */
  void (*find) ();
				/* find bboards */
  void (*find_bboard) ();
				/* find all mailboxes */
  void (*find_all) ();
				/* find all bboards */
  void (*find_all_bboard) ();
				/* subscribe to mailbox */
  long (*subscribe) ();
				/* unsubscribe from mailbox */
  long (*unsubscribe) ();
				/* subscribe to bboard */
  long (*subscribe_bboard) ();
				/* unsubscribe to bboard */
  long (*unsubscribe_bboard) ();
				/* create mailbox */
  long (*create) ();
				/* delete mailbox */
  long (*delete) ();
				/* rename mailbox */
  long (*rename) ();

				/* open mailbox */
  MAILSTREAM *(*open) ();
				/* close mailbox */
  void (*close) ();
				/* fetch message "fast" attributes */
  void (*fetchfast) ();
				/* fetch message flags */
  void (*fetchflags) ();
				/* fetch message envelopes */
  ENVELOPE *(*fetchstructure) ();
				/* fetch message header only */
  char *(*fetchheader) ();
				/* fetch message body only */
  char *(*fetchtext) ();
				/* fetch message body section */
  char *(*fetchbody) ();
				/* set message flag */
  void (*setflag) ();
				/* clear message flag */
  void (*clearflag) ();
				/* search for message based on criteria */
  void (*search) ();
				/* ping mailbox to see if still alive */
  long (*ping) ();
				/* check for new messages */
  void (*check) ();
				/* expunge deleted messages */
  void (*expunge) ();
				/* copy messages to another mailbox */
  long (*copy) ();
				/* move messages to another mailbox */
  long (*move) ();
				/* append string message to mailbox */
  long (*append) ();
				/* garbage collect stream */
  void (*gc) ();
};


/* Parse results from mail_valid_net_parse */

#define MAXSRV 20
typedef struct net_mailbox {
  char host[MAILTMPLEN];	/* host name */
  char mailbox[MAILTMPLEN];	/* mailbox name */
  char service[MAXSRV+1];	/* service name */
  int port;			/* TCP port number */
  unsigned int anoflag : 1;	/* anonymous */
  unsigned int bbdflag : 1;	/* bboard flag */
} NETMBX;

/* Other symbols */

extern const char *months[];	/* month name strings */


/* Jacket into external interfaces */

typedef long (*readfn_t) ();
typedef char *(*mailgets_t) ();
typedef void *(*mailcache_t) ();

extern MAILSTREAM *mailstd_proto;
extern char *lhostn;
extern mailgets_t mailgets;
extern mailcache_t mailcache;

/* Coddle certain compilers' 6-character symbol limitation */

#ifdef __COMPILER_KCC__
#define mm_gets mmgets
#define mm_cache mmcach
#define mm_searched mmsrhd
#define mm_exists mmexst
#define mm_expunged mmexpn
#define mm_flags mmflag
#define mm_notify mmntfy
#define mm_mailbox mmmlbx
#define mm_bboard mmbbrd
#define mm_log mmlog
#define mm_dlog mmdlog
#define mm_login mmlogn
#define mm_critical mmcrit
#define mm_nocritical mmncrt
#define mm_diskerror mmderr
#define mm_fatal mmfatl
#define mail_string mstr
#define mail_string_init mstrin
#define mail_string_next mstrnx
#define mail_string_setpos mstrsp
#define mail_link mllink
#define mail_parameters mlparm
#define mail_find mlfind
#define mail_find_bboards mlfndb
#define mail_find_all mlfnam
#define mail_find_all_bboard mlfalb
#define mail_valid mlvali
#define mail_valid_net mlvaln
#define mail_valid_net_parse mlvlnp
#define mail_subscribe mlsub
#define mail_unsubscribe mlusub
#define mail_subscribe_bboard mlsubb
#define mail_unsubscribe_bboard mlusbb
#define mail_create mlcret
#define mail_delete mldele
#define mail_rename mlrena
#define mail_open mlopen
#define mail_close mlclse
#define mail_makehandle mlmkha
#define mail_free_handle mlfrha
#define mail_stream mlstrm
#define mail_fetchfast mlffst
#define mail_fetchflags mlfflg
#define mail_fetchstructure mlfstr
#define mail_fetchheader mlfhdr
#define mail_fetchtext mlftxt
#define mail_fetchbody mlfbdy
#define mail_fetchfrom mlffrm
#define mail_fetchsubject mlfsub

#define mail_lelt mllelt
#define mail_elt mlelt
#define mail_setflag mlsflg
#define mail_clearflag mlcflg
#define mail_search mlsrch
#define mail_ping mlping
#define mail_check mlchck
#define mail_expunge mlexpn
#define mail_copy mlcopy
#define mail_move mlmove
#define mail_append mlappd
#define mail_gc mailgc
#define mail_date mldate
#define mail_cdate mlcdat
#define mail_parse_date mlpdat
#define mail_searched mlsrch
#define mail_exists mlexist
#define mail_recent mlrcnt
#define mail_expunged mlexst
#define mail_lock mllock
#define mail_unlock mlulck
#define mail_debug mldbug
#define mail_nodebug mlndbg
#define mail_sequence mlsequ
#define mail_newenvelope mlnenv
#define mail_newaddr mlnadr
#define mail_newbody mlnbod
#define mail_initbody mlibod
#define mail_newbody_parameter mlnbpr
#define mail_newbody_part mlnbpt
#define mail_free_body mlfrbd
#define mail_free_body_data mlfrbt
#define mail_free_body_parameter mlfrbr
#define mail_free_body_part mlfrbp
#define mail_free_cache mlfrch
#define mail_free_elt mlfrel
#define mail_free_envelope mlfren
#define mail_free_address mlfrad
#endif

/* Function prototypes */

void mm_searched  ();
void mm_exists  ();
void mm_expunged  ();
void mm_flags  ();
void mm_notify  ();
void mm_mailbox  ();
void mm_bboard  ();
void mm_log  ();
void mm_dlog  ();
void mm_login  ();
void mm_critical  ();
void mm_nocritical  ();
long mm_diskerror  ();
void mm_fatal  ();
char *mm_gets  ();
void *mm_cache  ();

extern STRINGDRIVER mail_string;
void mail_string_init  ();
char mail_string_next  ();
void mail_string_setpos  ();
void mail_link  ();
void *mail_parameters  ();
void mail_find  ();
void mail_find_bboards  ();
void mail_find_all  ();
void mail_find_all_bboard  ();
DRIVER *mail_valid  ();
DRIVER *mail_valid_net  ();
long mail_valid_net_parse  ();
long mail_subscribe  ();
long mail_unsubscribe  ();
long mail_subscribe_bboard  ();
long mail_unsubscribe_bboard  ();
long mail_create  ();
long mail_delete  ();
long mail_rename  ();
MAILSTREAM *mail_open  ();
MAILSTREAM *mail_close  ();
MAILHANDLE *mail_makehandle  ();
void mail_free_handle  ();
MAILSTREAM *mail_stream  ();
void mail_fetchfast  ();
void mail_fetchflags  ();
ENVELOPE *mail_fetchstructure  ();
char *mail_fetchheader  ();
char *mail_fetchtext  ();
char *mail_fetchbody  ();
void mail_fetchfrom  ();
void mail_fetchsubject  ();
LONGCACHE *mail_lelt  ();
MESSAGECACHE *mail_elt  ();

void mail_setflag  ();
void mail_clearflag  ();
void mail_search  ();
long mail_ping  ();
void mail_check  ();
void mail_expunge  ();
long mail_copy  ();
long mail_move  ();
long mail_append  ();
void mail_gc  ();
char *mail_date  ();
char *mail_cdate  ();
long mail_parse_date  ();
void mail_searched  ();
void mail_exists  ();
void mail_recent  ();
void mail_expunged  ();
void mail_lock  ();
void mail_unlock  ();
void mail_debug  ();
void mail_nodebug  ();
long mail_sequence  ();
ENVELOPE *mail_newenvelope  ();
ADDRESS *mail_newaddr  ();
BODY *mail_newbody  ();
BODY *mail_initbody  ();
PARAMETER *mail_newbody_parameter  ();
PART *mail_newbody_part  ();
void mail_free_body  ();
void mail_free_body_data  ();
void mail_free_body_parameter  ();
void mail_free_body_part  ();
void mail_free_cache  ();
void mail_free_elt  ();
void mail_free_lelt  ();
void mail_free_envelope  ();
void mail_free_address  ();

long sm_subscribe  ();
long sm_unsubscribe  ();
char *sm_read  ();
