/*
Copyright (c) 2008-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/


/**	@file prqd.h	Main header file for the prqd project.
*/



#ifndef PRQD_H_INCLUDED
#define PRQD_H_INCLUDED

#include <dk.h>


#if DK_HAVE_DB_H
/**	Flag: Use Berkeley DB. */
#define USE_DB_H	1
#endif
#if DK_HAVE_NDBM_H
/**	Flag: use NDBM. */
#define USE_NDBM_H	1
#if defined(USE_GDBM_H)
#undef USE_GDBM_H
#endif
#else
#if DK_HAVE_GDBM_H
/**	Flag: Use GDBM. */
#define USE_GDBM_H	1
#if defined(USE_NDBM_H)
/**	Flag: Use NDBM. */
#undef USE_NDBM_H
#endif
#endif
#endif



/**	Log priority: None. */
#define PRQD_PRIO_NONE			0

/**	Log priority: Fatal error. */
#define PRQD_PRIO_FATAL			1

/**	Log priority: Error. */
#define PRQD_PRIO_ERROR			2

/**	Log priority: Warning. */
#define PRQD_PRIO_WARNING		3

/**	Log priority: Informational message. */
#define PRQD_PRIO_INFO			4

/**	Log priority: Progress indicator message. */
#define PRQD_PRIO_PROGRESS		5

/**	Log priority: Debug message. */
#define PRQD_PRIO_DEBUG			6



/**	State: start of configuration file. */
#define PRQD_RDCONF_STATE_START		0

/**	State: Reading options. */
#define PRQD_RDCONF_STATE_OPTIONS	1

/**	State: Reading printer class information. */
#define PRQD_RDCONF_STATE_CLASS		2

/**	State: Reading printer information. */
#define PRQD_RDCONF_STATE_PRINTER	3


#include <stdio.h>
#if DK_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if DK_HAVE_UNISTD_H
#include <unistd.h>
#endif
#if DK_HAVE_SIGNAL_H
#include <signal.h>
#endif
#if DK_TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if DK_HAVE_TIME_H
#include <time.h>
#else
#if DK_HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#endif
#endif
#if DK_HAVE_SYSLOG
#include <syslog.h>
#endif
#if DK_HAVE_STDARG_H
#include <stdarg.h>
#endif
#if DK_HAVE_STRING_H
#include <string.h>
#endif
#if DK_HAVE_STRINGS_H
#include <strings.h>
#endif
#if DK_HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#else
#error "No sys/socket.h available!"
#endif
#if DK_HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if DK_HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#if DK_HAVE_NETDB_H
#include <netdb.h>
#endif
#if DK_HAVE_SELECT_H
#include <select.h>
#endif
#if DK_HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#if DK_HAVE_ERRNO_H
#include <errno.h>
#endif
#if DK_HAVE_PWD_H
#include <pwd.h>
#endif
#if DK_HAVE_GRP_H
#include <grp.h>
#endif
#if DK_HAVE_MATH_H
#include <math.h>
#endif
#if USE_DB_H
#include <db.h>
#endif
#if USE_GDBM_H
#include <gdbm.h>
#endif
#if USE_NDBM_H
#include <ndbm.h>
#endif

#include <dkmem.h>
#include <dksto.h>
#include <dkstr.h>
#include <dksf.h>
#include <dksignal.h>
#include <dkma.h>



/**	Default line buffer size. */
#define PRQD_BUFFER_SIZE		4096

/**	Default database entry size. */
#define PRQD_DBENTRY_SIZE		128

/**	Limit entry type: General default. */
#define LIMIT_ENTRY_TYPE_DEFAULT	0

/**	Limit entry type: Group limit. */
#define LIMIT_ENTRY_TYPE_GROUP		1

/**	Limit entry type: User limit. */
#define LIMIT_ENTRY_TYPE_USER		2



/**	Limit value type: Access to printer denied. */
#define LIMIT_VALUE_DENIED		0

/**	Limit value type: Number of pages restricted. */
#define LIMIT_VALUE_PAGES		1

/**	Limit value type: Unrestricted access to printer. */
#define LIMIT_VALUE_UNLIMITED		2



/**	Deny action: Remove job. */
#define DENY_ACTION_REMOVE		0

/**	Deny action: Hold job. */
#define DENY_ACTION_HOLD		1

/**	Deny action: Report failure. */
#define DENY_ACTION_FAIL		2

/**	UNIX socket address type. */
typedef struct sockaddr_un	SOUN;

/**	Size of UNIX socket address type. */
#define SZSOUN sizeof(SOUN)



/**	Limit entry. */
typedef struct {
  int	tp;		/**< Limit entry type,  LIMIT_ENTRY_TYPE_xxx. */
  char *who;            /**< User or group name. */
  int	val;		/**< Limit value type, LIMIT_VALUE_xxx. */
  unsigned long p;	/**< Number of pages. */
} LE;



/** Printer class. */
typedef struct {
  char *n;			/**< Class name. */
  dk_storage_t		*l;	/**< Limit entries. */
  dk_storage_iterator_t	*li;	/**< Iterator for limit entries. */
  int			da;	/**< Deny action,  DENY_ACTION_xxx */
  int			wi;	/**< Flag: Write info files */
} PC;



/** Printer. */
typedef struct _printer_ {
  char *n;			/**< Printer name. */
  PC   *pc;			/**< Printer class. */
  struct _printer_	*alt;	/**< Alias target. */
  char *cj;			/**< Current job "user:pages_at_start" */
  size_t sz_cj;			/**< Size of cj buffer in bytes. */
} PR;



/** Complete configuration file. */
typedef struct {
  dk_storage_t	*cl;		/**< Printer classes storage. */
  dk_storage_iterator_t	*cli;	/**< Printer classes storage iterator. */
  dk_storage_t	*pr;		/**< Printers storage. */
  dk_storage_iterator_t *pri;	/**< Printers storage iterator. */
  char *dbname;			/**< File name of database file. */
  char *sockname;		/**< File name of socket. */
  char *rau;			/**< User name to run as. */
  char *rag;			/**< Group name to run as. */
  unsigned long to_seconds;	/**< Socket timeout (seconds). */
  unsigned long to_microseconds;	/**< Socket timeout (microseconds). */
  char *userinfdir;		/**< User information directory */
  int   do_balance;		/**< Balance limit overflow to account. */
} PRQDC;



/**	Database type:	Unknown. */
#define DATABASE_TYPE_UNKNOWN		0

/**	Database type:	Berkeley DB. */
#define DATABASE_TYPE_BDB		1

/**	Database type:	GDBM. */
#define DATABASE_TYPE_GDBM		2

/**	Database type:	NDBM. */
#define DATABASE_TYPE_NDBM		3




/**	Request type:	Start of job. */
#define PRQD_RQ_T_JOBSTART		0

/**	Request type:	Start of file. */
#define PRQD_RQ_T_FILESTART		1

/**	Request type:	Start. */
#define PRQD_RQ_T_START			2

/**	Request type:	End. */
#define PRQD_RQ_T_END			3

/**	Request type:	End of file. */
#define PRQD_RQ_T_FILEEND		4

/**	Request type:	End of job. */
#define PRQD_RQ_T_JOBEND		5

/**	Request type:	Info. */
#define PRQD_RQ_T_INFO			6

/**	Request type:	Control. */
#define PRQD_RQ_T_CONTROL		7



/**	Prqd program data. */
typedef struct {
  int ec;			/**< Exit code. */
  int deb;			/**< Flag: Debugging mode. */
  int e1;			/**< Flag: Serious error, exit program. */
  int e2;			/**< Flag: Recovery attempt, reconfiguration. */
  int e3;			/**< Flag: Error in a session. */
  time_t	last_log_time;	/**< Timestamp of last log message. */
  PRQDC *prqdc;			/**< Current printer class to deal with. */
  int ss;			/**< Session socket. */
  int sock;			/**< Listen socket. */
  int backlog;			/**< Listen backlog. */
  int allow_file_logging;	/**< Flag: Allow logging to file (prqd only). */
  struct {
    int tp;			/**< Database type. */
    union {
      struct {
        char *fn;		/**< Database file name. */
#if USE_DB_H
	DB   *dbp;		/**< Pointer to database structure. */
#endif
      } bdb;			/**< Details for Berkeley DB backend. */
      struct {
        char *fn;		/**< Database file name. */
	char *myfn;		/**< Private copy of database file name. */
      } gdbm;			/**< Details for GDBM backend. */
      struct {
        char *fn;		/**< Database file name. */
#if USE_NDBM_H
        DBM *db;		/**< Pointer to database structure. */
#endif
      } ndbm;			/**< Details for NDBM backend. */
    } c;
  } dbbe;			/**< Details about database backend. */
  char		*b1;		/**< Input line buffer. */
  size_t	sz_b1;		/**< Size of b1 in bytes. */
  char		*b2;		/**< Output buffer. */
  size_t	sz_b2;		/**< Size of b2 in bytes. */
  char		*b3;		/**< Buffer for the user's primary group name */
  size_t	sz_b3;		/**< Size of b3 in bytes. */
  char		*dbkey;		/**< Database key buffer. */
  size_t	sz_dbkey;	/**< Size of dbkey in bytes. */
  char		*dbval;		/**< Database value buffer. */
  size_t	sz_dbval;	/**< Size of dbval in bytes. */
  unsigned char	must_respond;	/**< Flag: Must send response. */
  char		**args;		/**< 53 elements. */
  int		rqt;		/**< Request type PRQD_RQ_T_xxx. */
  /*
  	Data following here is possibly destroyed by later operations
  */
  char		*rqtstr;	/**< Pointer to request type. */
  char		*uname;		/**< Pointer to user name. */
  char		*pname;		/**< Pointer to printer name. */
  char		*rname;		/**< Real printer name (aliases resolved) */
  char		*tname;		/**< ? */
  char		*pages;		/**< ? */
  char		*cname;		/**< Pointer to printer class name. */
  char		*hname;		/**< Pointer to host name. */
  int		limit_type;	/**< Limit type. */
  unsigned long limit_pages;	/**< Limit pages. */
  /*	
  	Set in control requests.
  */
  char		*cr_cname;	/**< Pointer to control request subtype. */
  char		*cr_uname;	/**< Pointer to user name. */
  char		*cr_pages;	/**< Pointer to number of pages. */
  char		*cr_pname;	/**< Pointer to printer name. */
  long		cr_deltap;	/**< Number of pages to add. */
  dk_storage_t	*cr_st;			/**< Storage for request args. */
  dk_storage_iterator_t	*cr_sti;	/**< Iterator for storage. */
} PJ;



/**	Key/value pair.  */
typedef struct {
  char *k;	/**< Key. */
  char *v;	/**< Value. */
} KEYVALUE;




#if DK_HAVE_PROTOTYPES
/**	Database traversal function.
  	@param	pj	Print job description.
	@param	k	Buffer for key.
	@param	v	Buffer for value.
	@return	1 on success, 0 on non-critical error (continue traversal),
	<0 on critical error (stop traversal).
*/
typedef
int
PRQD_TRAVERSE_FCT(PJ *pj, char *k, char *v);
#else
typedef
int
PRQD_TRAVERSE_FCT();
#endif

#if defined(EXTERN)
#undef EXTERN
#endif
#if PRQDLOG_C
#define EXTERN /* nix */
#else
#if DK_HAVE_PROTOTYPES
#define EXTERN /* nix */
#else
#define EXTERN extern
#endif
#endif
#if defined(__cplusplus)
extern "C" {
#endif
EXTERN int prqdlog DK_PR((PJ *pj, int prio, char *f, ...));
#if defined(__cplusplus)
}
#endif



#if defined(EXTERN)
#undef EXTERN
#endif
#if PRQDPJ_C
#define EXTERN /* nix */
#else
#if DK_HAVE_PROTOTYPES
#define EXTERN /* nix */
#else
#define EXTERN extern
#endif
#endif
#if defined(__cplusplus)
extern "C" {
#endif
EXTERN void prqd_pj_init DK_PR((PJ *pj));
EXTERN void prqd_pj_set_arg DK_PR((PJ *pj, char c, char *s));
EXTERN char *prqd_pj_get_arg DK_PR((PJ *pj, char c));
EXTERN void prqd_pj_set_all_args DK_PR((PJ *pj, char *il));
EXTERN int prqd_pj_is_acceptable_name DK_PR((char *s));
EXTERN void prqd_pj_get_rq_type DK_PR((PJ *pj, char *s));
EXTERN void prqd_pj_get_names DK_PR((PJ *pj));
EXTERN void prqd_pj_find_limit DK_PR((PJ *pj));
EXTERN void prqd_pj_use_deny_action DK_PR((PJ *pj));
#if defined(__cplusplus)
}
#endif



#if defined(EXTERN)
#undef EXTERN
#endif
#if PRQD_C
#define EXTERN /* nix */
#else
#if DK_HAVE_PROTOTYPES
#define EXTERN /* nix */
#else
#define EXTERN extern
#endif
#endif
#if defined(__cplusplus)
extern "C" {
#endif
EXTERN void prqd_write_userinfo_file DK_PR((PJ *pj, char *u, char *c));
#if defined(__cplusplus)
}
#endif




#if defined(EXTERN)
#undef EXTERN
#endif
#if PRQDCONF_C
#define EXTERN /* nix */
#else
#if DK_HAVE_PROTOTYPES
#define EXTERN /* nix */
#else
#define EXTERN extern
#endif
#endif
#if defined(__cplusplus)
extern "C" {
#endif
EXTERN char *prqdconf_get_progname DK_PR((void));
EXTERN int prqdconf_get_debug DK_PR((PJ *pj));
EXTERN char *prqdconf_get_logfile DK_PR((void));
EXTERN char *prqdconf_get_cfgfile DK_PR((void));
EXTERN PRQDC *prqdconf_new_prqdc DK_PR((PJ *pj, char *n));
EXTERN void prqdconf_delete_prqdc DK_PR((PRQDC *prqdc));
EXTERN char *prqd_get_kw DK_PR((size_t s));
#if defined(__cplusplus)
}
#endif


#if defined(EXTERN)
#undef EXTERN
#endif
#if PRQDBE_C
#define EXTERN /* nix */
#else
#if DK_HAVE_PROTOTYPES
#define EXTERN /* nix */
#else
#define EXTERN extern
#endif
#endif
#if defined(__cplusplus)
extern "C" {
#endif
EXTERN void prqdbe_change_ownership DK_PR((PJ *pj, uid_t u, gid_t g));
EXTERN int prqdbe_open DK_PR((PJ *pj));
EXTERN void prqdbe_close DK_PR((PJ *pj));
EXTERN int prqdbe_store DK_PR((PJ *pj, char *k, char *v));
EXTERN size_t prqdbe_fetch DK_PR((PJ *pj, char *k, char *v, size_t sz));
EXTERN int prqdbe_traverse DK_PR((PJ *pj, PRQD_TRAVERSE_FCT *f));
EXTERN int prqdbe_delete DK_PR((PJ *pj, char *k));
#if defined(__cplusplus)
}
#endif


#if defined(EXTERN)
#undef EXTERN
#endif
#if PRQDCTRL_C
#define EXTERN /* nix */
#else
#if DK_HAVE_PROTOTYPES
#define EXTERN /* nix */
#else
#define EXTERN extern
#endif
#endif
#if defined(__cplusplus)
extern "C" {
#endif
EXTERN int
prqdctrl_request DK_PR((PJ *pj, char *args));
#if defined(__cplusplus)
}
#endif

/* ##### delete this for real quota enforcement */
/* define ALWAYS_PERMIT	1 */



#endif
/* PRQD_H_INCLUDED */
