/*
 * IMC2 - an inter-mud communications protocol
 *
 * imc.h: the core protocol definitions
 *
 * Copyright (C) 1996,1997 Oliver Jowett <oliver@jowett.manawatu.planet.co.nz>
 *
 * 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 (see the file COPYING); if not, write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#ifndef IMC_H
#define IMC_H

#include <sys/time.h>
#include "imc-config.h"

#define REVISION "Revision 8 5/4/99"
/* activation states */

#define IA_NONE        0
#define IA_CONFIG1     1
#define IA_CONFIG2     2
#define IA_UP          3
#define IA_LISTENING   4

/* connection states */

#define IMC_CLOSED     0
#define IMC_CONNECTING 1
#define IMC_WAIT1      2
#define IMC_WAIT2      3
#define IMC_CONNECTED  4

/* connection flags */

#define IMC_NOAUTO 1
#define IMC_CLIENT 2
#define IMC_RECONNECT 4
#define IMC_BROADCAST 8
#define IMC_DENY 16
#define IMC_QUIET 32
#define IMC_HUB 64      /* SPAM fix - shogar */
#define IMC_MAIN_HUB 128 /* Scion */
#define IMC_OLD_HUB 256 /* Scion */
#define IMC_DEAD_HUB 512 /* Scion */
#define IMC_NEW_HUB 1024 /* shogar */
#define IMC_NOSWITCH 2048 /* shogar */


/* ignore types */
#define IMC_IGNORE  1          /* ignore sender */
#define IMC_NOTRUST 2          /* don't trust sender's levels */
#define IMC_TRUST   3          /* do trust sender's levels */

/* ignore match flags */
#define IMC_IGNORE_EXACT  0    /* match exactly */
#define IMC_IGNORE_PREFIX 1    /* ignore prefixes when matching */
#define IMC_IGNORE_SUFFIX 2    /* ignore suffixes when matching */

/* imc_char_data invisibility state */
#define IMC_INVIS 1
#define IMC_HIDDEN 2




/* typedefs */

/* flag/state tables */
typedef struct
{
  char *name;			/* flag name */
  int value;			/* bit value */
} imc_flag_type;

/* data in an IMC packet */
typedef struct
{
  char *key[IMC_MAX_KEYS];
  char *value[IMC_MAX_KEYS];
} imc_data;

/* an IMC packet, as seen by the high-level code */
typedef struct
{
  char to[IMC_NAME_LENGTH];	/* destination of packet */
  char from[IMC_NAME_LENGTH];	/* source of packet      */
  char type[IMC_TYPE_LENGTH];	/* type of packet        */
  imc_data data;		/* data of packet        */

  /* internal things which only the low-level code needs to know about */
  struct {
    char to[IMC_NAME_LENGTH];
    char from[IMC_NAME_LENGTH];
    char path[IMC_PATH_LENGTH];
    unsigned long sequence;
    int stamp;
  } i;
} imc_packet;

/* an actual IMC connection */
typedef struct _imc_connect
{
  struct _imc_info *info;       /* imc_info struct for this connection */

  int desc;			/* descriptor */
  unsigned short state;	      	/* IMC_xxxx state */
  unsigned short version;      	/* version of remote site */

  short newoutput;              /* try to write at end of cycle regardless
				 * of fd_set state?
				 */
  
  char *inbuf;		        /* input buffer */
  int insize;

  char *outbuf;		        /* output buffer */
  int outsize;

  int spamcounter1;             /* packet counters since last update */
  int spamcounter2;
  int spamtime1;                /* +ve = time exceeding limit */
  int spamtime2;                /* -ve = time remaining with enforced limit */

  struct _imc_connect *next;
} imc_connect;

/* a configured IMC connection */
typedef struct _imc_info
{
  char *name;			/* name of remote mud */
  char *host;			/* hostname */

  struct _imc_connect *connection;

  unsigned short port;		/* remote port */
  char *serverpw;		/* server password */
  char *clientpw;		/* client password */
  int flags;			/* connection flags */
  time_t timer_duration;        /* delay after next reconnect failure */

  int rcvstamp;                 /* packets get this stamp on arrival */
  int noforward;                /* packets with these bits set don't get
                                 * forwarded here */

  time_t last_connected;        /* last connected when? */
  int connect_attempts;       /* try for 3 times - shogar */
  
  struct _imc_info *next;
} imc_info;

/* IMC statistics */
typedef struct
{
  time_t start;		        /* when statistics started */
  
  long rx_pkts;		        /* received packets */
  long tx_pkts;		        /* transmitted packets */
  long rx_bytes;		/* received bytes */
  long tx_bytes;		/* transmitted bytes */

  int max_pkt;                  /* max. size packet processed */
  int sequence_drops;           /* dropped packets due to age */
} imc_statistics;

#define IMC_REMINFO_NORMAL 0
#define IMC_REMINFO_EXPIRED 1

/* info about another mud on IMC */
typedef struct _imc_reminfo
{
  char *name;
  char *version;
  time_t alive;
  int ping;
  int type;
  int hide;
  char *route;
  char *path;
  unsigned long top_sequence;
  struct _imc_reminfo *next;
} imc_reminfo;

/* an event */
typedef struct _imc_event
{
  time_t when;
  void (*callback)(void *data);
  void *data;
  int timed;
  struct _imc_event *next;
} imc_event;

/* for the versioning table */
typedef struct
{
  int version;
  const char *(*generate) (const imc_packet *);
  imc_packet *(*interpret) (const char *);
} _imc_vinfo;

/* an entry in the memory table */
typedef struct
{
  char *from;
  unsigned long sequence;
} _imc_memory;

/* an ignore/notrust/etc entry */
typedef struct _imc_ignore_data
{
  char *name;
  int match;                      /* exact, prefix, suffix */
  int type;                       /* ignore, notrust */
  struct _imc_ignore_data *next;
} imc_ignore_data;

/* a player on IMC */
typedef struct
{
  char name[IMC_NAME_LENGTH];	/* name of character */
  int invis;			/* invisible to IMC? */
  int level;			/* trust level */
  int wizi;			/* wizi level */
} imc_char_data;

/* one piece of mail */
typedef struct _imc_mail
{
  char *from;			/* 'from' line */
  char *to;			/* 'to' line */
  char *text;			/* text of the mail */
  char *date;			/* 'date' line */
  char *subject;		/* 'subject' line */
  char *id;			/* mail ID (should be unique among mails) */
  time_t received;		/* when it was received (into the queue) */
  int usage;			/* number of references to this mail */
  struct _imc_mail *next;
} imc_mail;

/* a queue entry pointing at a piece of mail */
typedef struct _imc_qnode
{
  imc_mail *data;
  char *tomud;
  struct _imc_qnode *next;
} imc_qnode;

/* a mail ID that has been received */
typedef struct _imc_mailid
{
  char *id;			/* message-id */
  time_t received;		/* when received */
  struct _imc_mailid *next;
} imc_mailid;

/* site information */
typedef struct
{
  char *name;    /* FULL name of mud */
  char *host;    /* host AND port */
  char *email;   /* contact address (email) */
  char *imail;   /* contact address (IMC mail) */
  char *www;     /* homepage */
  char *details; /* BRIEF description of mud */
  char *flags;   /* special flags - currently only "hide" is supported */
  int maxguests; /* max number of guest connections - shogar */
} imc_siteinfo_struct;

/* data structures */

extern imc_siteinfo_struct imc_siteinfo;

/* the packet memory table */
extern _imc_memory imc_memory[IMC_MEMORY];
/* the version lookup table */
extern _imc_vinfo imc_vinfo[];

/* global stats struct */
extern imc_statistics imc_stats;

/* flag and state tables */
extern const imc_flag_type imc_connection_flags[];
extern const imc_flag_type imc_ignore_types[];
extern const imc_flag_type imc_state_names[];
extern const imc_flag_type imc_active_names[];

/* global recursion lock */
extern int imc_lock;
/* the local IMC name */
extern char *imc_name;
/* the local IMC port */
extern unsigned short imc_port;
/* IP to bind to */
extern unsigned long imc_bind;
/* the configuration prefix */
extern char *imc_prefix;
/* run as a router? */
extern int imc_is_router;

/* the ignore list */
extern imc_ignore_data *imc_ignore_list;
/* the reminfo list */
extern imc_reminfo *imc_reminfo_list;
/* the connection list */
extern imc_connect *imc_connect_list;
/* the configured connection list */
extern imc_info *imc_info_list;
/* the event list, and recycle list */
extern imc_event *imc_event_list, *imc_event_free;

/* the current time */
extern time_t imc_now;
/* next sequence number to use */
extern unsigned long imc_sequencenumber;

/* IMC state */
extern int imc_active;

/* -1 if prefix is in use (when network is up), >=0 otherwise */
extern int imc_lock_file;

/* mail list */
extern imc_mail *imc_ml_head;
/* mail queue */
extern imc_qnode *imc_mq_head, *imc_mq_tail;
/* ID list */
extern imc_mailid *imc_idlist;

/* last reported error */
extern char imc_lasterror[IMC_DATA_LENGTH];

/* packet hook */
int (*imc_recv_hook)(const imc_packet *p, int bcast);

/* imc-util.c exported functions */

/* static buffer handling */
char *imc_getsbuf(int len);
void imc_shrinksbuf(char *buf);

/* imc data handlers */
const char *imc_getkey(const imc_data *p, const char *key, const char *def);
int imc_getkeyi(const imc_data *p, const char *key, int def);
void imc_addkey(imc_data *p, const char *key, const char *value);
void imc_addkeyi(imc_data *p, const char *key, int value);
void imc_initdata(imc_data *p);
void imc_freedata(imc_data *p);
void imc_clonedata(const imc_data *p, imc_data *n);

/* reminfo handling */
imc_reminfo *imc_find_reminfo(const char *name, int type);
imc_reminfo *imc_new_reminfo(void);
void imc_delete_reminfo(imc_reminfo *p);

/* info handling */
imc_info *imc_new_info(void);
void imc_delete_info(imc_info *);
imc_info *imc_getinfo(const char *name);

/* state/flag handling */
const char *imc_flagname(int value, const imc_flag_type *table);
int imc_flagvalue(const char *name, const imc_flag_type *table);
const char *imc_statename(int value, const imc_flag_type *table);
int imc_statevalue(const char *name, const imc_flag_type *table);

/* string manipulations */
const char *imc_nameof(const char *name);
const char *imc_mudof(const char *name);
const char *imc_makename(const char *name, const char *mud);
const char *imc_firstinpath(const char *path);
const char *imc_lastinpath(const char *path);
const char *imc_getarg(const char *arg, char *buf, int length);
int imc_hasname(const char *list, const char *name);
void imc_addname(char **list, const char *name);
void imc_removename(char **list, const char *name);
void imc_slower(char *what);
void imc_sncpy(char *dest, const char *src, int count);
const char *imc_getconnectname(const imc_connect *c);

/* logging */

void imc_logstring(const char *format,...); /*__attribute__((format(printf,1,2))); Uncomment these */
void imc_logerror(const char *format,...); /*__attribute__((format(printf,1,2))); if your system can*/
void imc_qerror(const char *format,...); /*__attribute__((format(printf,1,2)));   use them. */
void imc_lerror(const char *format,...); /*__attribute__((format(printf,1,2)));*/
const char *imc_error(void);

/* external log interfaces */
void imc_log(const char *string);
void imc_debug(const imc_connect *, int out, const char *packet);



/* imc-events.c exported functions */

/* event handling */
imc_event *imc_new_event(void);
void imc_free_event(imc_event *p);
void imc_add_event(int when, void (*callback)(void *), void *data, int timed);
void imc_cancel_event(void (*callback)(void *), void *data);
void imc_run_events(time_t);
int imc_next_event(void (*callback)(void *), void *data);
void imc_recv_keepalive(const char *from, const char *version, const char *flags); /* shogar */
void imc_request_keepalive(void); /* shogar */
void imc_recv_info_reply(const char *from, const char *hub, int direct); /* Scion */
void imc_send_autoconnect(const char *to); /* Scion */
void imc_recv_autoconnect(const char *to, const char *rhost, int rport, const char *rclientpw, const char *rserverpw, 
						  int rrcvstamp, int rnoforward, const char *rflags, const char *remotename); /* Scion */
void imc_autoconnect_reply_accept(const char *from, const char *rhost, int rport, const char *rclientpw, const char *rserverpw, 
						  int rrcvstamp, int rnoforward, const char *rflags, const char *remotename, const char *confirm); /* Scion */
imc_info *imc_insert_info(); /* shogar */
void imc_cancel_info(imc_info *i); /* shogar */


/* all events (in imc-events.c unless otherwise specified) */
void ev_expire_reminfo(void *data);
void ev_drop_reminfo(void *data);
void ev_login_timeout(void *data);       /* imc.c */
void ev_reconnect(void *data);
void ev_shrink_input(void *data);
void ev_shrink_output(void *data);
void ev_keepalive(void *data);
void ev_request_keepalive(void *data);
void ev_spam1(void *data);
void ev_spam2(void *data);
void ev_qnode_expire(void *data);        /* imc-mail.c */
void ev_mailid_expire(void *data);       /* imc-mail.c */
void ev_qnode_send(void *data);          /* imc-mail.c */
void ev_imc_optimize(void *data);	/* Scion */
void ev_imc_pollforhub(void *data);	/* shogar */

/* imc.c exported functions */

void imc_setup_reconnect(imc_info *i);

const char *imc_make_password(void); /* Scion */

void imc_send(imc_packet *p);
void imc_recv(const imc_packet *p);

void imc_startup_port(void);
void imc_startup_network(void);
void imc_startup(const char *prefix);
void imc_shutdown(void);
void imc_shutdown_network(void);
void imc_shutdown_port(void);
void imc_idle(int s, int us);

int imc_get_max_timeout(void);
int imc_fill_fdsets(int maxfd, fd_set *read, fd_set *write, fd_set *exc);
void imc_idle_select(fd_set *read, fd_set *write, fd_set *exc, time_t now);

int imc_connect_to(const char *mud);
int imc_disconnect(const char *mud);

/* memory allocation hooks
 * these aren't #defines so we don't have to know about the actual mud
 * function declarations in imc.c/imc-comm.c
 */
#define imc_free(b,s) {IMC_free(b,s);b=NULL;}
#define imc_strfree(s) {IMC_strfree(s);s=NULL;}
void *imc_malloc(int size);
void IMC_free(void *block, int size);
char *imc_strdup(const char *src);
void IMC_strfree(char *str);



/* imc-interp.c exported functions */

void imc_sendignore(const char *to);

void imc_send_chat(const imc_char_data *from, int channel,
		   const char *argument, const char *to);
void imc_send_emote(const imc_char_data *from, int channel,
		    const char *argument, const char *to);
void imc_send_tell(const imc_char_data *from, const char *to,
		   const char *argument, int isreply);
void imc_send_who(const imc_char_data *from, const char *to,
		  const char *type);
void imc_send_whoreply(const char *to, const char *data, int sequence);
void imc_send_whois(const imc_char_data *from, const char *to);
void imc_send_whoisreply(const char *to, const char *data);
void imc_send_beep(const imc_char_data *from, const char *to);
void imc_send_keepalive(void);
void imc_send_ping(const char *to, int time_s, int time_u);
void imc_send_pingreply(const char *to, int time_s, int time_u, const char *path);
void imc_send_whois(const imc_char_data *from, const char *to);
void imc_send_whoisreply(const char *to, const char *text);
void imc_send_info_request(void);


void imc_whoreply_start(const char *to);
void imc_whoreply_add(const char *text);
void imc_whoreply_end(void);

/* callbacks that need to be provided by the interface layer */

void imc_recv_chat(const imc_char_data *from, int channel,
		   const char *argument);
void imc_recv_emote(const imc_char_data *from, int channel,
		    const char *argument);
void imc_recv_tell(const imc_char_data *from, const char *to,
		   const char *argument, int isreply);
void imc_recv_whoreply(const char *to, const char *data, int sequence);
void imc_recv_who(const imc_char_data *from, const char *type);
void imc_recv_beep(const imc_char_data *from, const char *to);
void imc_recv_keepalive(const char *from, const char *version, const char *flags);
void imc_recv_ping(const char *from, int time_s, int time_u, const char *path);
void imc_recv_pingreply(const char *from, int time_s, int time_u, const char *pathto, const char *pathfrom);
void imc_recv_whois(const imc_char_data *from, const char *to);
void imc_recv_whoisreply(const char *to, const char *text);
void imc_recv_inforequest(const char *from);

void imc_traceroute(int ping, const char *pathto, const char *pathfrom);

/* other functions */
const char *imc_sockets(void);
const char *imc_getstats(void);


/* imc-config.c exported functions */

/* ignore handling */
imc_ignore_data *imc_findignore(const char *who, int type);
int imc_isignored(const char *who);
void imc_addignore(const char *who, int flags);
int imc_delignore(const char *who);
imc_ignore_data *imc_newignore(void);
void imc_freeignore(imc_ignore_data *ign);

const char *imc_ignore(const char *what);
const char *imc_list(int level);
int imc_command(const char *argument);
int imc_saveconfig(void);
int imc_readconfig(void);
int imc_saveignores(void);
int imc_readignores(void);

int imc_readconfighook(const char *type, const char *value);
void imc_saveconfighook(FILE *fp);

/* Globals for optimization routines. I'm sure there's a better way to handle this,
but I'll save that for version 1.1, thank you very much. 1/6/99 -- Scion */
extern char global_hubname[IMC_MNAME_LENGTH];
extern int global_directnum;





/* imc-mail.c exported functions */

void imc_recv_mailok(const char *from, const char *id);
void imc_recv_mailrej(const char *from, const char *id, const char *reason);
void imc_recv_mail(const char *from, const char *to, const char *date,
		   const char *subject, const char *id, const char *text);
void imc_send_mail(const char *from, const char *to, const char *date,
		   const char *subject, const char *text);

char *imc_mail_arrived(const char *from, const char *to, const char *date,
		       const char *subject, const char *text);

void imc_mail_startup(void);
void imc_mail_shutdown(void);

char *imc_mail_showqueue(void);

#endif
