/* --------------------------------------------------------------------------
 *
 * License
 *
 * The contents of this file are subject to the Jabber Open Source License
 * Version 1.0 (the "License").  You may not copy or use this file, in either
 * source code or executable form, except in compliance with the License.  You
 * may obtain a copy of the License at http://www.jabber.com/license/ or at
 * http://www.opensource.org/.  
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Copyrights
 * 
 * Portions created by or assigned to Jabber.com, Inc. are 
 * Copyright (c) 1999-2000 Jabber.com, Inc.  All Rights Reserved.  Contact
 * information for Jabber.com, Inc. is available at http://www.jabber.com/.
 *
 * Portions Copyright (c) 1998-1999 Jeremie Miller.
 * 
 * Acknowledgements
 * 
 * Special thanks to the Jabber Open Source Contributors for their
 * suggestions and support of Jabber.
 * 
 * --------------------------------------------------------------------------*/

#ifdef SUNOS
#include <time.h>
#include <limits.h>
#else
#include <bits/time.h>
#include <linux/limits.h>
#endif


#include <pwd.h>
#include <grp.h>

#include <sys/types.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/timeb.h>
#include <sys/ioctl.h>
#include <pthread.h>

#include "util.h"

#ifdef HAVE_SSL
#include <ssl.h>
#endif /* HAVE_SSL */


#include "lib/lib.h"
#include "wphash.h"

#ifdef __cplusplus
extern "C" {
#endif

#define VERSION "Jabberd 1.1.1  stable / with real threads / by Lukas Karwacki" 

/* packet types */
typedef enum { p_NONE, p_NORM, p_XDB, p_LOG, p_ROUTE } ptype;

/* ordering types, me first me first, managerial, engineer, grunt */
typedef enum { o_PRECOND, o_COND, o_PREDELIVER, o_DELIVER } order;

/* result types, unregister me, I pass, I should be last, I suck, I rock */
typedef enum { r_UNREG, r_NONE, r_PASS, r_LAST, r_ERR, r_DONE } result;

typedef struct instance_struct *instance, _instance;

/* packet wrapper, d as in delivery or daemon, whichever pleases you */
typedef struct dpacket_struct
{
    char *host;
    jid id;
    ptype type;
    pool p;
    xmlnode x;
} *dpacket, _dpacket;

/* main deliver hashes */
typedef struct {
  volatile int deliver__flag;
  SEM_VAR deliver__sem; /* sem for registering and unregistering instances */
  
  volatile wpxht deliver__hnorm; /* hosts for normal packets, important and most frequently used one */
  volatile wpxht deliver__hxdb; /* host filters for xdb requests */
  volatile wpxht deliver__hlog; /* host filters for logging */
  volatile wpxht deliver__ns; /* namespace filters for xdb */
  volatile wpxht deliver__logtype; /* log types, fixed set, but it's easier (wussier) to just be consistent and use a hashtable */  
  volatile instance deliver__uplink; /* uplink instance, only one */
} * jabber_deliver, _jabber_deliver;

/* delivery handler function callback definition */
typedef result (*phandler)(instance id, dpacket p, void *arg);

/* delivery handler list */
typedef struct handel_struct
{
    pool p;
    phandler f;
    void *arg;
    order o; /* for sorting new handlers as they're inserted */
    struct handel_struct *next;
} *handel, _handel;

/* wrapper around top-level config file sections */
struct instance_struct
{
    char *id;
    pool p;
    xmlnode x;
    ptype type;
    handel hds;
};

/* config file handler function callback definition */
typedef result (*cfhandler)(instance id, xmlnode x, void *arg);

/* heartbeat function callback definition */
typedef result (*beathandler)(void *arg);

/*** public functions for base modules ***/
void register_config(char *node, cfhandler f, void *arg); /* register a function to handle that node in the config file */
void register_instance(instance i, char *host); /* associate an id with a hostname for that packet type */
void unregister_instance(instance i, char *host); /* disassociate an id with a hostname for that packet type */
void register_phandler(instance id, order o, phandler f, void *arg); /* register a function to handle delivery for this instance */
void register_beat(int freq, beathandler f, void *arg); /* register the function to be called from the heartbeat, freq is how often, <= 0 is ignored */
void register_beat_safe(int freq, beathandler f, void *arg); /* for heartbeat thread */

typedef void(*shutdown_func)(void*arg);
void register_shutdown(shutdown_func f,void *arg); /* register to be notified when the server is shutting down */
void register_shutdown_first(shutdown_func f,void *arg);

dpacket dpacket_new(xmlnode x); /* create a new delivery packet from source xml */
dpacket dpacket_copy(dpacket p); /* copy a packet (and it's flags) */
void deliver(dpacket p, instance i); /* deliver packet from sending instance */
void deliver_fail(dpacket p, char *err); /* bounce a packet intelligently */
void deliver_instance(instance i, dpacket p); /* deliver packet TO the instance, if the result != r_DONE, you have to handle the packet! */


#define MAX_LOG_SIZE 4096 /* 4096 */
extern int debug_flag;

void debug_log(char *zone, const char *msgfmt, ...);
int get_debug_flag();

#ifdef NODEBUG
#define log_debug(ala...)
#else
#ifdef CYGWIN
#define log_debug if(get_debug_flag()) debug_log
#else
#define log_debug if(debug_flag) debug_log
#endif
#endif
void log_notice(char *host, const char *msgfmt, ...);
void log_warn(char *host, const char *msgfmt, ...);
void log_alert(char *host, const char *msgfmt, ...);
#define log_error log_alert
void logger(char *type, char *host, char *message); /* actually creates and delivers the log message */
void log_record(char *id, char *type, char *action, const char *msgfmt, ...); /* for generic logging support, like log_record("jer@jabber.org","session","end","...") */

/*** xdb utilities ***/

/* ring for handling cached structures */
typedef struct xdbcache_struct
{
    instance i;
    unsigned int id;
    char *ns;
    char *act; /* for set */
    char *match; /* for set */
    volatile xmlnode data; /* for set */
    pthread_mutex_t sem;
    jid owner;
    int sent;
    volatile int preblock;
    int set;
    struct xdbcache_struct *prev;
    struct xdbcache_struct *next;
} *xdbcache, _xdbcache;

xdbcache xdb_cache(instance i); /* create a new xdb cache for this instance */
xmlnode xdb_get(xdbcache xc,  jid owner, char *ns); /* blocks until namespace is retrieved, returns xmlnode or NULL if failed */
int xdb_act(xdbcache xc, jid owner, char *ns, char *act, char *match, xmlnode data); /* sends new xml action, returns non-zero if failure */
int xdb_set(xdbcache xc, jid owner, char *ns, xmlnode data); /* sends new xml to replace old, returns non-zero if failure */

/* Error messages */
#define SERROR_NAMESPACE "<stream:error>Invalid namespace specified.</stream:error>"
#define SERROR_INVALIDHOST "<stream:error>Invalid hostname used.</stream:error>"

/* ------------------------------------
 * Managed Thread Queue (MTQ) utilities 
 * ------------------------------------*/

/* default waiting threads */
#ifndef MTQ_THREADS
# define MTQ_THREADS 10
#endif
#ifndef MTQ_QUEUE_LONG
# define MTQ_QUEUE_LONG 5000
#endif
/* mtq callback simple function definition */
typedef void (*mtq_callback)(void *arg);

typedef struct mtqqueue_struct
{
    mtq_callback f; /* function to run within the thread */
    void *arg; /* the data for this call */
    struct mtqqueue_struct *prev;
    char memory;
} _mtqqueue, *mtqqueue;

/* has a pointer to the currently assigned thread for this queue */
typedef struct mtq_struct
{
    struct mtqqueue_struct *first;
    struct mtqqueue_struct *last;
    struct mtqqueue_struct *free_first;
    struct mtqqueue_struct *free_last;
    _mtqqueue queue[MTQ_QUEUE_LONG];
    volatile int dl;     /* number of actions to do */
    int length; /* length of the queue */
    SEM_VAR   sem;
    COND_VAR  cond;    
    volatile int users_count; /* number of users in this queue */
} *mtq, _mtq;

/* has the message port for the running thread, and the current queue it's processing */
typedef struct mth_struct
{
    struct mtq_struct *mtq;
    pool p;
    pthread_t thread;
} *mth, _mth;


typedef struct mtqmaster_struct
{
  mth all[MTQ_THREADS];
  int random;
} *mtqmaster, _mtqmaster;

mtq mtq_new(pool p);
mtq mtq_new_que(pool p,  mtq que); 
void mtq_stop();
void mtq_send(mtq q, pool p, mtq_callback f, void *arg); /* appends the arg to the queue to be run on a thread */

/* MIO */

/* struct to handle the write queue */
typedef enum { queue_XMLNODE, queue_CDATA } mio_queue_type;
typedef struct mio_wb_q_st
{
    pool p;
    mio_queue_type type;
    xmlnode x;
    void *data;
    void *cur;
    int len;
    struct mio_wb_q_st *next;
} _mio_wbq,*mio_wbq;

struct mio_handlers_st; /* grr.. stupid fsking chicken and egg C definitions */

/* the mio data type */
typedef enum { state_ACTIVE, state_CLOSE } mio_state;
typedef enum { type_LISTEN, type_NORMAL, type_NUL, type_HTTP } mio_type;
typedef struct mio_st
{
    pool p;
    int fd;
    mio_type type; /* listen (server) socket or normal (client) socket */
    mio_state state;

    mio_wbq queue; /* write buffer queue */
    mio_wbq tail;  /* the last buffer queue item */

    struct mio_st *prev,*next;

    pthread_mutex_t sem;
  
    void *cb_arg;    /* do not modify directly */
    void *cb;     /* do not modify directly */
    struct mio_handlers_st *mh;

    xstream    xs;   /* XXX kill me, I suck */
    XML_Parser parser;
    int        root;
    xmlnode    stacknode;
    void       *ssl;

    struct karma k;
    int rated;   /* is this socket rate limted? */
    jlimit rate; /* if so, what is the rate?    */
    char *ip;
} *mio, _mio;

/* MIO SOCKET HANDLERS */
typedef ssize_t (*mio_read_func)    (mio m, void*            buf,       size_t     count);
typedef ssize_t (*mio_write_func)   (mio m, const void*      buf,       size_t     count); 
typedef void    (*mio_parser_func)  (mio m,  const void*      buf,       size_t     bufsz);
typedef int     (*mio_accept_func)  (mio m, struct sockaddr* serv_addr, socklen_t* addrlen);
typedef int     (*mio_connect_func) (mio m, struct sockaddr* serv_addr, socklen_t  addrlen);

/* the MIO handlers data type */
typedef struct mio_handlers_st
{
    pool  p;
    mio_read_func   read;
    mio_write_func  write;
    mio_accept_func accept;
    mio_parser_func parser;
} _mio_handlers, *mio_handlers; 

/* standard read/write/accept/connect functions */
#define MIO_READ_FUNC    read 
#define MIO_WRITE_FUNC   write
#define MIO_ACCEPT_FUNC  accept
#define MIO_CONNECT_FUNC connect

ssize_t _mio_raw_read(mio m, void *buf, size_t count);
ssize_t _mio_raw_write(mio m, void *buf, size_t count);
int _mio_raw_accept(mio m, struct sockaddr* serv_addr, socklen_t* addrlen);
void _mio_raw_parser(mio m, const void *buf, size_t bufsz);
int _mio_raw_connect(mio m, struct sockaddr* serv_addr, socklen_t  addrlen);
#define MIO_RAW_READ    (mio_read_func)&_mio_raw_read
#define MIO_RAW_WRITE   (mio_write_func)&_mio_raw_write
#define MIO_RAW_ACCEPT  (mio_accept_func)&_mio_raw_accept
#define MIO_RAW_CONNECT (mio_connect_func)&_mio_raw_connect
#define MIO_RAW_PARSER  (mio_parser_func)&_mio_raw_parser

void _mio_xml_parser(mio m, const void *buf, size_t bufsz);
#define MIO_XML_PARSER  (mio_parser_func)&_mio_xml_parser

/* function helpers */
#define MIO_LISTEN_RAW NULL, mio_handlers_new(NULL, NULL, NULL)
#define MIO_CONNECT_RAW  NULL, mio_handlers_new(NULL, NULL, NULL)
#define MIO_LISTEN_XML NULL, mio_handlers_new(NULL, NULL, MIO_XML_PARSER)
#define MIO_CONNECT_XML  NULL, mio_handlers_new(NULL, NULL, MIO_XML_PARSER)

/* SSL functions */
void    mio_ssl_init     (xmlnode x);
void    _mio_ssl_cleanup (void *arg);
ssize_t _mio_ssl_read    (mio m, void *buf, size_t count);
ssize_t _mio_ssl_write   (mio m, const void*      buf,       size_t     count);
int     _mio_ssl_accept  (mio m, struct sockaddr* serv_addr, socklen_t* addrlen);
int     _mio_ssl_connect (mio m, struct sockaddr* serv_addr, socklen_t  addrlen);
#define MIO_SSL_READ    _mio_ssl_read
#define MIO_SSL_WRITE   _mio_ssl_write
#define MIO_SSL_ACCEPT  _mio_ssl_accept
#define MIO_SSL_CONNECT _mio_ssl_connect

/* MIO handlers helper functions */
mio_handlers mio_handlers_new(mio_read_func rf, mio_write_func wf, mio_parser_func pf);
void         mio_handlers_free(mio_handlers mh);
void         mio_set_handlers(mio m, mio_handlers mh);

/* callback state flags */
#define MIO_NEW       0
#define MIO_BUFFER    1
#define MIO_XML_ROOT  2
#define MIO_XML_NODE  3 
#define MIO_CLOSED    4
#define MIO_ERROR     5

/* standard i/o callback function definition */
typedef void (*mio_std_cb)(mio m, int state, void *arg);
typedef void (*mio_xml_cb)(mio m, int state, void *arg, xmlnode x);
typedef void (*mio_raw_cb)(mio m, int state, void *arg, char *buffer,int bufsz);
typedef void (*mio_ssl_cb)(mio m, int state, void *arg, char *buffer,int bufsz);

/* initializes the MIO subsystem */
void mio_init(void);

/* stops the MIO system */
void mio_stop(void);

/* create a new mio object from a file descriptor */
mio mio_new(int fd, void *cb, void *cb_arg, mio_handlers mh);

/* reset the callback and argument for an mio object */
mio mio_reset(mio m, void *cb, void *arg);

/* request the mio socket be closed */
void mio_close(mio m);

/* writes an xmlnode to the socket */
void mio_write(mio m,xmlnode x, char *buffer, int len);

/* sets the karma values for a socket */
void mio_karma(mio m, int val, int max, int inc, int dec, int penalty, int restore);
void mio_karma2(mio m, struct karma *k);

/* sets connection based rate limits */
void mio_rate(mio m, int rate_time, int max_points);

/* pops the next xmlnode from the queue, or NULL if no more nodes */
xmlnode mio_cleanup(mio m);

/* connects to an ip */
void mio_connect(char *host, int port, void *cb, void *cb_arg, int timeout, mio_connect_func f, mio_handlers mh);

/* starts listening on a port/ip, returns NULL if failed to listen */
mio mio_listen(int port, char *sourceip, void *cb, void *cb_arg, mio_accept_func f, mio_handlers mh);

/* reads and process everything in sockets */
void  mio_clean();


/* some nice api utilities */
#define mio_pool(m) (m->p)
#define mio_ip(m) (m->ip)

typedef unsigned long DWORD;

DWORD timeGetTime();
void Sleep(DWORD time);


/* defines of other functions */
void xdb_file(instance i, xmlnode x);

/* flag shutdown jabber */
extern volatile int jab_shutdown;

/* WPLOG */
#define MAX_WPLOGS 10

typedef struct wplog_packet_struct
{
  unsigned int messages;
  unsigned int presences;
  unsigned int queries;
  unsigned int files;
  unsigned int last_messages;
  unsigned int last_presences;
  unsigned int last_queries;
  unsigned int last_files;
  unsigned int active;
  int time;
  char * file;
} _wplog_packet, *wplog_packet;

typedef struct wplog_struct
{
  int logs; /* number of logs */
  pool p;
  wplog_packet tab[MAX_WPLOGS];
  char *server_start;
} *wplog, _wplog;

extern wplog __wplog;
void wplog_init(void);
char *jutil_timestamplocal(void);


#ifdef __cplusplus
}
#endif
