/* Copyright (c) 2020-2023 The Creators of Simphone

   See the file COPYING.LESSER.txt for copying permission.
*/

#ifndef _CONTACT_H_
#define _CONTACT_H_

#include "simtypes.h"

#define SIM_MAX_NICK_LENGTH 33 /* maximal number of UTF-8 characters allowed in a nickname */
#define SIM_SIZE_NICK ((SIM_MAX_NICK_LENGTH + 1) * 4)
#define SIM_MAX_LINE_SIZE 400   /* maximal number of bytes in info line */
#define SIM_MAX_REVOKE_SIZE 200 /* maximal number of bytes in key revocation reason */

#define CONTACT_ADDRESS_SIZE 38   /* number of bytes in ASCII address (including terminating zero) */
#define CONTACT_ADDRESS_LENGTH 20 /* number of bytes in a binary address */

struct _messages {
  unsigned len;           /* number of messages in queue */
  unsigned size;          /* current queue/index capacity (number of messages, including unused indexes) */
  unsigned count;         /* number of indexed messages */
  int flags;              /* CONTACT_MSG_xxx */
  unsigned hashsalt;      /* random salt for hash table hash function */
  unsigned hashsize;      /* number of entries in handles hash table head (power of two) */
  simnumber sizes;        /* total allocated size */
  simnumber msgnum;       /* handle of last received message */
  struct _message *queue; /* messages array (index zero is unused) */
  unsigned *indexes;      /* indexes of visible messages (index zero is unused) */
  unsigned *handles;      /* head of handles hash table (zero means end of chain) */
  simtype nick;           /* pointer to last nick of contact (inside message queue) */
  simtype own;            /* pointer to last own nick (inside message queue) */
  simnumber pinging;      /* ping number to send or MSG_NUMBER_INVALID if no ping needs to be sent */
  simnumber calltime;     /* time when pending call was made or zero if not pending */
  int callsample;         /* outgoing call sample rate (negative if fixed) or zero for default */
  int fd;                 /* file descriptor of history file or -1 if closed */
  simnumber msgack;       /* handle of last message acknowledged by contact */
  simnumber resent;       /* handle of last unsent message pushed on handshake (zero if none) */
  simnumber rcvtick;      /* time when message was last received */
  simnumber sndtick;      /* time when last sent message is expected to be acknowledged */
  simnumber savetick;     /* time when pending messages were last saved */
  simnumber savetimeout;  /* time from last save to next save */
  simtype pending;        /* pending messages file (empty table) */
  unsigned maxsize;       /* maximal size allowed to allocate or zero */
};

#define CONTACT_MSG_APPEND 1     /* the history file is append-only */
#define CONTACT_MSG_UNSENT 2     /* there are new pending messages (not acknowledged) */
#define CONTACT_MSG_SAVED 4      /* there are saved (to file) pending messages */
#define CONTACT_MSG_UNSAVED 8    /* there are pending messages to save (or delete) */
#define CONTACT_MSG_NOTSENT 16   /* NOTSENT event has been sent */
#define CONTACT_MSG_NOTPUT 32    /* pending chat messages have not been pushed to Pth queue */
#define CONTACT_MSG_REMOVED 64   /* chat messages have been removed */
#define CONTACT_MSG_OVERFLOW 128 /* the memory limit has been exceeded */

#define CONTACT_MSG_NO_FILES 8192     /* file transfer has been refused (too many files) */
#define CONTACT_MSG_NO_SPACE 16384    /* file transfer has been refused (not enough disk space) */
#define CONTACT_MSG_NO_TRAVERSE 32768 /* NAT traversal has been disabled automatically */

struct _xfer_param {
  simtype hash;        /* keyed by handle, value is pointer to struct _xfer. can be nil if not in use */
  simnumber cid;       /* permanent 61-bit computer identifier sent to contact or zero if never sent */
  simnumber pause;     /* handle of file transfer to start or SIM_CMD_XFER_INIT_PAUSE_xxx (zero if none) */
  struct _xfer **last; /* pointer to pointer to last item of struct _xfer. pointer to NULL if list is empty */
  int name;            /* value of xfer.name last sent to contact */
  int names;           /* value of xfer.names last sent to contact */
};

struct _network_ip_port { /* keys in MAIN_NODES and values in CONTACT_KEY_IP4 / PROXY_KEY_IP4 */
  unsigned ip;
  unsigned short port;
}; /* ip and port in network byte-order (host byte-order in struct _contact) */

#define CONTACT_SIZE_IP_PORT (sizeof (unsigned) + sizeof (unsigned short))

struct _main_param {
  simunsigned search; /* search DHT when current tick not less than this (zero to not search) */
  simunsigned tick;   /* tick of last successfully finished blacklist search */
  unsigned timeout;   /* timeout for next blacklist search */
  int count;          /* number of non-started blacklist searches */
  simbyte rnd[4];     /* last four (random) bytes of blacklist DHT search id */
  int id;             /* first two bytes of DHT id + 1 (map announced DHT port to real port number) or zero */
  simbool active;     /* next DHT search is MAIN_MODE_ACTIVE (if true) or MAIN_MODE_PASSIVE (if false) */
};

#define CONTACT_FLAG_VERIFY 1 /* i am verified by contact */
#define CONTACT_FLAG_UTF 2    /* contact allows UTF-8 chat messages */
#define CONTACT_FLAG_AUDIO 4  /* contact allows audio calls */
#define CONTACT_FLAG_XFER 8   /* contact allows file transfers */

#define CONTACT_FLAG_TYPING 0x10        /* contact wishes to receive typing notification */
#define CONTACT_FLAG_TYPE 0x20          /* contact is now typing */
#define CONTACT_FLAG_ECHO_CANCEL_Y 0x40 /* contact wishes echo cancellation */
#define CONTACT_FLAG_ECHO_CANCEL_N 0x80 /* contact wishes no echo cancellation */
#define CONTACT_FLAG_XFER_CANCEL 0x100  /* contact will NOT delete cancelled files */

#define CONTACT_FLAG_VERIFIED 0x10000 /* contact verified manually */
#define CONTACT_FLAG_RENAMED 0x20000  /* nickname set locally */
#define CONTACT_FLAG_INFO 0x40000     /* new information for contact available */
#define CONTACT_FLAG_NEW 0x80000      /* contact has just been added */

#define CONTACT_FLAG_EDIT_Y 0x1000000     /* allow editing sent messages */
#define CONTACT_FLAG_EDIT_N 0x2000000     /* disallow editing sent messages */
#define CONTACT_FLAG_ECHO_Y 0x4000000     /* request echo cancellation from contact */
#define CONTACT_FLAG_ECHO_N 0x8000000     /* request no echo cancellation from contact */
#define CONTACT_FLAG_UTF_Y 0x10000000     /* allow UTF-8 chat messages from contact */
#define CONTACT_FLAG_UTF_N 0x20000000     /* disallow UTF-8 chat messages from contact */
#define CONTACT_FLAG_AUDIO_Y 0x40000000   /* allow audio calls from contact */
#define CONTACT_FLAG_AUDIO_N 0x80000000   /* disallow audio calls from contact */
#define CONTACT_FLAG_XFER_Y 0x100000000LL /* allow file transfers from contact */
#define CONTACT_FLAG_XFER_N 0x200000000LL /* disallow file transfers from contact */

#define CONTACT_FLAG_TYPING_Y 0x10000000000LL  /* send typing notification to contact */
#define CONTACT_FLAG_TYPING_N 0x20000000000LL  /* do not send typing notification to contact */
#define CONTACT_FLAG_HISTORY_Y 0x40000000000LL /* log chat history for contact */
#define CONTACT_FLAG_HISTORY_N 0x80000000000LL /* do not log chat history for contact */

#define CONTACT_FLAG_SOUND_ON_Y 0x1000000000000LL  /* always play logon sound for contact */
#define CONTACT_FLAG_SOUND_ON_N 0x2000000000000LL  /* never play logon sound for contact */
#define CONTACT_FLAG_SOUND_OFF_Y 0x4000000000000LL /* always play logoff sound for contact */
#define CONTACT_FLAG_SOUND_OFF_N 0x8000000000000LL /* never play logoff sound for contact */

#define CONTACT_FLAG_TYPE_Y 0x1000000000000000LL /* notify contact that i am now typing */
#define CONTACT_FLAG_TYPE_N 0x2000000000000000LL /* notify contact that i am not typing any more */

#define CONTACT_FLAG_NO_STATS 0x4000000000000000LL /* deleted contact has no statistics */

#define CONTACT_FLAGS_OVERFLOW (CONTACT_FLAG_UTF | CONTACT_FLAG_XFER | CONTACT_FLAG_AUDIO)
#define CONTACT_FLAGS_DEFAULT (CONTACT_FLAG_UTF | CONTACT_FLAG_XFER | CONTACT_FLAG_AUDIO | CONTACT_FLAG_TYPING)
#define CONTACT_FLAGS_EVENT (CONTACT_FLAG_VERIFY | CONTACT_FLAGS_DEFAULT | CONTACT_FLAG_TYPE)
#define CONTACT_FLAGS_NO_EVENT (CONTACT_FLAG_ECHO_CANCEL_Y | CONTACT_FLAG_ECHO_CANCEL_N | CONTACT_FLAG_XFER_CANCEL)

/* check contact's access rights */
#define CONTACT_CHECK_RIGHT_DEFAULT(contact, def, yes, no) \
  (((contact)->flags & (yes)) || (! ((contact)->flags & (no)) && (def)))
#define CONTACT_CHECK_RIGHT(contact, param, yes, no) \
  CONTACT_CHECK_RIGHT_DEFAULT (contact, param_get_number (param) > 0, yes, no)

#define CONTACT_CHECK_RIGHT_EDIT(contact) \
  CONTACT_CHECK_RIGHT (contact, "msg.edit", CONTACT_FLAG_EDIT_Y, CONTACT_FLAG_EDIT_N)
#define CONTACT_CHECK_RIGHT_TYPE(contact) \
  CONTACT_CHECK_RIGHT (contact, "rights.type", CONTACT_FLAG_TYPING_Y, CONTACT_FLAG_TYPING_N)
#define CONTACT_CHECK_RIGHT_ECHO(contact) ((contact)->flags & (CONTACT_FLAG_ECHO_Y | CONTACT_FLAG_ECHO_N))

#if HAVE_LIBSPEEX
#define CONTACT_CHECK_RIGHT_AUDIO(contact) \
  CONTACT_CHECK_RIGHT (contact, "rights.audio", CONTACT_FLAG_AUDIO_Y, CONTACT_FLAG_AUDIO_N)
#else
#define CONTACT_CHECK_RIGHT_AUDIO(contact) false
#endif

#define CONTACT_CHECK_RIGHT_HISTORY(contact) \
  CONTACT_CHECK_RIGHT (contact, "rights.history", CONTACT_FLAG_HISTORY_Y, CONTACT_FLAG_HISTORY_N)
#define CONTACT_CHECK_RIGHT_UTF(contact) \
  CONTACT_CHECK_RIGHT (contact, "rights.utf", CONTACT_FLAG_UTF_Y, CONTACT_FLAG_UTF_N)
#define CONTACT_CHECK_RIGHT_XFER(contact) \
  CONTACT_CHECK_RIGHT (contact, "rights.xfer", CONTACT_FLAG_XFER_Y, CONTACT_FLAG_XFER_N)

extern const char *contact_right_names[63]; /* names of contact rights flags */
extern const char *contact_auth_names[5];   /* names of contact authorization levels */
extern const char *contact_status_names[7]; /* names of contact statuses */

#define CONTACT_LOOKUP_STATUS_NAME(status) contact_status_names[(status) - (SIM_STATUS_MIN)]

#define CONTACT_AUTH_DROP (-3)    /* disconnect if connected */
#define CONTACT_AUTH_FORGET (-2)  /* scheduled for removal */
#define CONTACT_AUTH_REVOKED (-1) /* contact has revoked his key */
#define CONTACT_AUTH_DELETED 0    /* rejected contact */
#define CONTACT_AUTH_BLOCKED 1    /* suppressed contact */
#define CONTACT_AUTH_NEW 2        /* non-accepted contact */
#define CONTACT_AUTH_ACCEPTED 3   /* authorized contact */

#define CONTACT_AUTH_AUTHORIZE 9 /* used internally (manually authorized) */
#define CONTACT_AUTH_USER 98     /* used internally (added from txt file) */
#define CONTACT_AUTH_SYSTEM 99   /* used internally (system contact) */
#define CONTACT_AUTH_SELF 100    /* used internally (self contact) */

#define CONTACT_SEEN_RECEIVE 0  /* last time when a network packet (other than status) was received */
#define CONTACT_SEEN_STATUS 1   /* last time when contact changed status, or when contact got deleted */
#define CONTACT_SEEN_ACTIVITY 2 /* last time of communication or communication attempt */

struct _contact {
  struct _messages msgs;            /* message queue */
  int auth;                         /* contact authorization level (CONTACT_AUTH_xxx) */
  int status;                       /* SIM_STATUS_xxx except for SIM_STATUS_IDLE */
  simnumber flags;                  /* CONTACT_FLAG_xxx */
  simnumber id;                     /* contact identifier */
  struct _xfer_param xfer;          /* file transfer configuration */
  struct _xfer *xfers;              /* current file transfers (list of struct _xfer) */
  const char *addr;                 /* ASCII address */
  simtype key;                      /* binary address */
  simtype nick;                     /* nickname */
  simtype own;                      /* own nickname */
  simtype line;                     /* info line or nil if none */
  simtype revocation;               /* key revocation reason (CONTACT_AUTH_REVOKED) or nil if not revoked */
  simtype hosts;                    /* array of hostnames or nil if none */
  simtype versions;                 /* table of versions or nil if unknown */
  struct _main_param dht;           /* mainline DHT search state */
  int edit;                         /* number of last sent messages contact wishes to allow you to edit */
  int ips;                          /* number of ip/port pairs */
  struct _network_ip_port ip[8];    /* array of ip/port */
  simnumber seen[3];                /* last seen time (indexed by CONTACT_SEEN_xxx) or zero if never seen */
  simnumber tick;                   /* tick of last successful outgoing connection or -1 if one needs to be made soon */
  simunsigned verify;               /* ip verification deadline or zero if no ip verification in progress */
  int clients;                      /* number of currently connected clients */
  int connected;                    /* number of pending sim_contact_connect_ calls */
  int logon;                        /* logon period (minimal number of seconds between incoming probes) */
  unsigned location;                /* ip address of last connection (zero if never connected) */
  char *insecure;                   /* last insecure OS version name or NULL if none */
  const char *rcvcipher;            /* last used decrypt cipher */
  const char *sndcipher;            /* last used encrypt cipher */
  struct _network_ip_port probe[6]; /* ip and port number of probes to send (indexed by CONTACT_PROBE_xxx) */
  simnumber probetick[6];           /* time when probes were last sent (indexed by CONTACT_PROBE_xxx) */
  simtype probehost;                /* hostname (for CONTACT_PROBE_RESOLVE) or nil */
  struct client_probe *probes;      /* pointer to first pending probe to contact or NULL if none */
  simclient client;                 /* main loop running with this client or NULL if it does not */
  simtype ec, rsa;                  /* public keys (or nil if unknown) */
  simtype file;                     /* the rest of the data (empty table) */
  int handshake;                    /* maximal successful handshake duration for this session (seconds). not saved */
  unsigned next;                    /* index of next contact in logon (random) order (zero is end of list) */
  simnumber stats[10][4];           /* statistics (first index is CONTACT_STATS_xxx, second index is CONTACT_STAT_xxx) */
};

#define CONTACT_STATS_CLIENTS 0  /* CONTACT_KEY_ALL_CLIENT_xxx (CONTACT_STAT_xxx - client: previous runs) */
#define CONTACT_STATS_CLIENT 1   /* CONTACT_KEY_THIS_CLIENT_xxx (CONTACT_STAT_xxx - client: this run) */
#define CONTACT_STATS_SOCKETS 2  /* CONTACT_KEY_ALL_SOCKET_xxx (CONTACT_STAT_xxx - probe: previous runs) */
#define CONTACT_STATS_SOCKET 3   /* CONTACT_KEY_THIS_SOCKET_xxx (CONTACT_STAT_xxx - probe: this run) */
#define CONTACT_STATS_OUTPUTS 4  /* CONTACT_KEY_ALL_OUTPUT_xxx (CONTACT_STAT_xxx - proxy output: previous runs) */
#define CONTACT_STATS_OUTPUT 5   /* CONTACT_KEY_THIS_OUTPUT_xxx (CONTACT_STAT_xxx - proxy output: this run) */
#define CONTACT_STATS_INPUTS 6   /* CONTACT_KEY_ALL_INPUT_xxx (CONTACT_STAT_xxx - proxy input: previous runs) */
#define CONTACT_STATS_INPUT 7    /* CONTACT_KEY_THIS_INPUT_xxx (CONTACT_STAT_xxx - proxy input: this run) */
#define CONTACT_STATS_MINE 8     /* CONTACT_KEY_FRAMES_MINE_xxx (CONTACT_FRAMES_xxx - my audio frames) */
#define CONTACT_STATS_PEER 9     /* CONTACT_KEY_FRAMES_PEER_xxx (CONTACT_FRAMES_xxx - peer's audio frames) */
#define CONTACT_STATS_LATENCY 10 /* CONTACT_KEY_MS_xxx */

#define CONTACT_STAT_RECEIVED 0 /* number of bytes received (CONTACT_KEY_xxx_RECEIVED) */
#define CONTACT_STAT_SENT 1     /* number of bytes sent (CONTACT_KEY_xxx_SENT) */
#define CONTACT_STAT_DURATION 2 /* number of milliseconds for all sessions (CONTACT_KEY_xxx_TIME) */
#define CONTACT_STAT_COUNT 3    /* number of (all) sessions (CONTACT_KEY_xxx_COUNT) */

#define CONTACT_FRAMES_READ 0  /* number of audio read errors (CONTACT_KEY_xxx_FRAMES_READ) */
#define CONTACT_FRAMES_WRITE 1 /* number of audio write errors (CONTACT_KEY_xxx_FRAMES_WRITE) */
#define CONTACT_FRAMES_ALL 2   /* number of audio frames (CONTACT_KEY_xxx_FRAMES_ALL) */
#define CONTACT_FRAMES_LOST 3  /* number of lost (not output) audio frames (CONTACT_KEY_xxx_FRAMES_LOST) */

extern const char *contact_stat_names[15][4];  /* names of statistics keys */
extern const unsigned contact_stat_indexes[4]; /* indexes of statistics keys (CONTACT_STAT_xxx) */
extern const unsigned contact_stat_deleted[4]; /* indexes of deleted keys (PROXY_STATS_DELETED_xxx) */

struct _contacts {
  simtype array;     /* array of simcontact */
  simtype table;     /* simcontact indexed by id */
  simtype file;      /* the rest of the data (empty table) */
  simcontact me;     /* this is me myself or NULL if i don't exist */
  simcontact system; /* system contact or NULL if none */
  unsigned test;     /* preloaded value of nat.test */
  int auth;          /* CONTACT_AUTH_xxx (for contact_list.me) */
  unsigned first;    /* index of first contact in logon (random) order */
};

extern struct _contacts contact_list;

/* initialize and deinitialize contacts */
void contact_init (void);
void contact_uninit (void);

/* convert binary address to ASCII. type should be 'S' */
simtype sim_contact_convert_to_address (const simtype key, char type);

/* convert ASCII address to binary. type should be 'S' */
simtype sim_contact_convert_address (const char *address, char type);

/* convert ASCII address to id */
simnumber sim_contact_convert_to_id (const char *address, const simtype key);
#define CONTACT_CONVERT_TO_ID(address) sim_contact_convert_to_id (address, contact_list.me->key)

/* find contact id by address. return id or zero if not available */
simnumber sim_contact_convert_to_id_ (const char *address);

/* check ASCII address and return corrected address or nil if invalid. type should be "S" */
simtype sim_contact_test_address (const char *address, const char *type);

/* randomly shuffle contact list for client logon order */
void contact_list_init (void);

/* check whether contact request is allowed now */
simbool contact_list_check_request (simnumber tick);

/* new status info needs to be sent to all contacts but not immediately */
void contact_list_set_info (int bit);

/* return number of (authorized) contacts */
int contact_list_count (simbool authorized);

#define CONTACT_TXT_DEFAULT 0 /* save according to contact.txt parameter */
#define CONTACT_TXT_ADDRESS 1 /* save address */
#define CONTACT_TXT_NICK 2    /* save address and nickname */

/* load and save users (contacts.txt) file. use external file if pathname is not NULL */
int contact_list_load_txt_ (const char *pathname);
/* if pathname is NULL, contacts file is also saved and mode is ignored */
int contact_list_save_txt (const char *pathname, int mode);

/* load contacts file */
int contact_list_load (const simtype address);

/* save contacts file */
int contact_list_save (void);

/* release loaded contacts file from memory */
void contact_list_free (void);

/* find any contact (including blocked, deleted or forgotten) by id and return contact structure or NULL if not found */
simcontact contact_list_find_id (simnumber id);

/* find any contact by address. return contact structure or NULL if not found */
simcontact contact_list_find_address (const char *address);

/* search for contact on mainline DHT; do not start blacklist search if restart = true. return MAIN_SEARCH_xxx */
int sim_contact_search_ (simnumber id, simbool restart);

/* find contact nick by id (helper for logging). return NULL if not found */
const char *sim_contact_get_nick_ (simnumber id, char nick[SIM_SIZE_NICK]);

/* use id if non-zero, else use address. return id if contact not found, else return contact nick */
const char *contact_get_nick (simnumber id, const char *address);
#define CONTACT_GET_NICK(address) contact_get_nick (CONTACT_CONVERT_TO_ID (address), address)

/* return a copy of contact's info line */
#define CONTACT_GET_INFO(contact) ((contact)->line.len ? string_copy_string ((contact)->line) : pointer_new (""))

/* return RSA key bit size */
#define CONTACT_GET_KEY_SIZE(contact) ((contact)->rsa.len > 14 ? ((contact)->rsa.len - 14) * 8 : 0)

/* find contact id by nick. return id or zero if not found */
simnumber sim_contact_find_ (const char *nick);

/* find DynDNS hostname and return its index or zero if not found. zero port matches any port */
int contact_find_host (simcontact contact, const char *host, int port);

/* add DynDNS hostname or clear all hostnames if NULL */
void contact_add_host (simcontact contact, const char *host, int port);

/* set contact flags (SIMNUMBER). return SIM_NO_ERROR or SIM_KEY_NO_SAVE if there's a change */
int contact_set_rights (simcontact contact, const simtype info);

/* set verification code (SIMNIL to unverify). return SIM_NO_ERROR if there's a change */
int contact_set_verified (simcontact contact, const simtype verify, const simtype key);

/* compare public keys to stored ones; if no stored keys, store them and save the contacts file */
int contact_set_keys (simcontact contact, const simtype ec, const simtype rsa);

/* set status and flags and send SIM_EVENT_STATUS if necessary */
void contact_set_status (simcontact contact, int status, simnumber flags);

#define CONTACT_IP_FIND (-1) /* check if ip:port already present */
#define CONTACT_IP_ADD 0     /* make ip:port most recent; add them if not present */
#define CONTACT_IP_UPDATE 1  /* if ip:port added make them most recent */

/* set ip address and port number (which should be valid). mode is CONTACT_IP_xxx. return true if ip:port now present */
simbool contact_set_ip (simcontact contact, unsigned ip, int port, int mode);

/* set info line of contact. return SIM_NO_ERROR if there's a change */
int contact_set_line (simcontact contact, const simtype line);

/* set edit number, info line and own nickname and send SIM_EVENT_STATUS if necessary */
void contact_set_nick (simcontact contact, const simtype nick, const simtype line, simnumber edit);

/* contact has revoked his key. return old authorization level */
int contact_set_revocation (simcontact contact, const simtype reason);

/* try to remove soft link for old nick and try to create a soft link for newnick. pathname should be NULL */
void contact_new_link (simcontact contact, char *pathname, const simtype oldnick, const simtype newnick);

/* authorize a (new) contact (CONTACT_AUTH_SELF/CONTACT_AUTH_SYSTEM/CONTACT_AUTH_USER,
   CONTACT_AUTH_ACCEPTED or CONTACT_AUTH_NEW). return *contact */
int contact_new_ (const char *address, int auth, simcontact *contact);

/* set nickname of contact. may change nickname if always = true */
int contact_rename (simcontact contact, const simtype nick, simbool always);

/* deauthorize contact (CONTACT_AUTH_FORGET, CONTACT_AUTH_DELETED or CONTACT_AUTH_BLOCKED)
   return old authorization level or CONTACT_AUTH_FORGET if contact didn't exist */
int contact_reject (simcontact contact, int auth);

/* drop contact if connected */
void contact_cancel (simcontact contact, int error);

#define CONTACT_PROBE_CONNECT 0    /* server loop failed */
#define CONTACT_PROBE_DISCONNECT 1 /* proxy changed during server loop */
#define CONTACT_PROBE_RECONNECT 2  /* proxy detected duplicate customer */
#define CONTACT_PROBE_HANDSHAKE 3  /* normal reverse probe */
#define CONTACT_PROBE_RESOLVE 4    /* host (DNS) probe */
#define CONTACT_PROBE_DUPLICATE 5  /* SIM_CMD_DUPLICATE */
#define CONTACT_PROBE_VERIFY 6     /* SIM_CMD_REQUEST_VERIFY */

/* send an out-of-band (status) packet to contact; probe is CONTACT_PROBE_xxx. return SIM_NO_ERROR if skipped */
int contact_probe (simcontact contact, unsigned ip, int port, const char *host, int probe);

/* handle periodic tasks (send delayed status packets) */
void contact_periodic (void);

/* debug logging */
#define CONTACT_LOG_SHORT 4 /* these two in addition to CONTACT_AUTH_xxx */
#define CONTACT_LOG_LONG (-4)

void contact_log_list (const char *module, int level, int auth);
int contact_log_stats (const char *module, int level, const char *key, simnumber id);

/* sub-keys in contacts file */
#define CONTACT_KEY_ID ""
#define CONTACT_KEY_AUTH "auth"
#define CONTACT_KEY_FLAGS "fl"
#define CONTACT_KEY_EDIT "em"
#define CONTACT_KEY_NICK "nick"
#define CONTACT_KEY_OWN_NICK "own"
#define CONTACT_KEY_LINE "line"
#define CONTACT_KEY_REVOCATION "reason"
#define CONTACT_KEY_HOSTS "host"
#define CONTACT_KEY_VERSIONS "ver"
#define CONTACT_KEY_IP4 "ip4"
#define CONTACT_KEY_SEEN_RECEIVE "sr"
#define CONTACT_KEY_SEEN_STATUS "ss"
#define CONTACT_KEY_SEEN_ACTIVITY "sa"
#define CONTACT_KEY_STATUS "on"
#define CONTACT_KEY_LOGON "log"
#define CONTACT_KEY_MSG_HANDLE "mh"
#define CONTACT_KEY_CIPHER "rc"
#define CONTACT_KEY_LOCATION "loc"
#define CONTACT_KEY_RSA "rsa"
#define CONTACT_KEY_EC "ec"

#define CONTACT_KEY_ALL_CLIENT_RECEIVED "SCR"
#define CONTACT_KEY_ALL_CLIENT_SENT "SCS"
#define CONTACT_KEY_ALL_CLIENT_TIME "SCT"
#define CONTACT_KEY_ALL_CLIENT_COUNT "SCC"

#define CONTACT_KEY_ALL_SOCKET_RECEIVED "SSR"
#define CONTACT_KEY_ALL_SOCKET_SENT "SSS"
#define CONTACT_KEY_ALL_SOCKET_TIME "SST"
#define CONTACT_KEY_ALL_SOCKET_COUNT "SSC"

#define CONTACT_KEY_ALL_OUTPUT_RECEIVED "SOR"
#define CONTACT_KEY_ALL_OUTPUT_SENT "SOS"
#define CONTACT_KEY_ALL_OUTPUT_TIME "SOT"
#define CONTACT_KEY_ALL_OUTPUT_COUNT "SOC"

#define CONTACT_KEY_ALL_INPUT_RECEIVED "SIR"
#define CONTACT_KEY_ALL_INPUT_SENT "SIS"
#define CONTACT_KEY_ALL_INPUT_TIME "SIT"
#define CONTACT_KEY_ALL_INPUT_COUNT "SIC"

#endif
