/*
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.c	The prqd daemon. */


#include "prqd.h"

#line 44 "prqd.ctr"




/** Program name. */
static char appname[] = { "prqd" };



/** Texts for responses to jobstart requests. */
static char *response_keywords[]= {
  /*  0 */ "ACCEPT\n",
  /*  1 */ "FAIL\n",
  /*  2 */ "HOLD\n",
  /*  3 */ "REMOVE\n"
};



/** Log messages for syslog logging. */
static char *logmsgs[] = {
"prqd-acct job u=%s c=%s pr=%s pa=%ld h=%s j=%s",
"prqd-acct summary u=%s c=%s pa=%ld l=%s",
"prqd-acct summary u=%s c=%s ac=%s",
"prqd-acct balance-account u=%s c=%s",
"prqd-acct limit-reached u=%s c=%s",
};



/** Texts in user information files. */
static char *uift[] = {
/*  0 */ "Date:                                ",
/*  1 */ "Printed pages / General limit:       ",
/*  2 */ "Additional pages in personal account:",
/*  3 */ "Allowed to print:                    ",
/*  4 */ "yes",
/*  5 */ "no",
/*  6 */ "unlimited",
NULL
};

/** Database entry format for current job. */
static char current_job_format[] = { "%s:%s" };

/** Database key pattern for current job on a printer. */
static char key_current_job_format[] = { "j:%s" };

/** Database key pattern for pages printed by users. */
static char key_user_pages[] = { "p:%s:%s" };

/** Database key pattern for print accounts. */
static char key_user_account[]= { "a:%s:%s" };

/** Empty string. */
static char empty_string[] = { "" };



/** Replace NULL string pointers by pointer to empty string. */
#define XSTR(s) ((s) ? (s) : empty_string)



/** Flag: SIGHUP received. */
static
#if DK_HAVE_VOLATILE
volatile
#endif
int sighup_r = 0;



/**	SIGHUP handler.
	@param	signo	Signal number (SIGHUP).
*/
dk_signal_ret_t
sighup_handler  DK_P1(int, signo)
{
  dksignal_refresh(signo,sighup_handler);
  sighup_r = 1;
  dksignal_return(0);
}



/** Flag: SIGINT received. */
static
#if DK_HAVE_VOLATILE
volatile
#endif
int sigint_r = 0;



/**	SIGINT handler.
	@param	signo	Signal number (SIGINT).
*/
dk_signal_ret_t
sigint_handler  DK_P1(int, signo)
{
  dksignal_refresh(signo,sigint_handler);
  sigint_r = 1;
  dksignal_return(0);
}



/** Flag: SIGPIPE received. */
static
#if DK_HAVE_VOLATILE
volatile
#endif
int sigpipe_r = 0;



/**	SIGPIPE handler.
	@param	signo	Signal number (SIGPIPE).
*/
dk_signal_ret_t
sigpipe_handler  DK_P1(int, signo)
{
  dksignal_refresh(signo,sigpipe_handler);
  sigpipe_r = 1;
  dksignal_return(0);
}



/** Flag: SIGTERM was received. */
static
#if DK_HAVE_VOLATILE
volatile
#endif
int sigterm_r = 0;



/**	SIGTERM handler.
	@param	signo	Signal number (SIGTERM).
*/
dk_signal_ret_t
sigterm_handler  DK_P1(int, signo)
{
  dksignal_refresh(signo,sigterm_handler);
  sigterm_r = 1;
  dksignal_return(0);
}



/**	Exit outer loop (main program) only on SIGTERM, SIGINT
	or serious error.
	@param	pj	Pointer to prqd job structure.
	@return	1 if we can continue, 0 if we have to leave the loop.
*/
int
prqd_cc1 DK_P1(PJ *,pj)
{
  int back = 1;
  
  if(sigterm_r) back = 0;
  if(sigint_r) back = 0;
  if(pj->e1) back = 0;
  if(!back) {
    prqdlog(pj, PRQD_PRIO_INFO, prqd_get_kw(55));
  }
  
  return back;
}



/**	Exit inner loop on SIGTERM, SIGINT, serious errors
	or reconfiguration.
	@param	pj	Pointer to prqd job structure.
	@return	1 if we can continue, 0 if we have to leave the loop.
*/
int
prqd_cc2 DK_P1(PJ *,pj)
{
  int back = 0;
  
  back = prqd_cc1(pj);
  if(back) {
    if(sighup_r) back = 0;	/* reconfiguration */
    if(pj->e2) back = 0;
  }
  if(!back) {
    prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(54));
  }
  
  return back;
}



/**	Check inner ``can continue'' condition.
	@param	pj	Pointer to prqd job structure.
	@return 1 if we can continue, 0 if we need to leave the loop.
*/
int
prqd_cc3 DK_P1(PJ *,pj)
{
  int back = 0;
  
  back = prqd_cc1(pj);
  if(back) {
    if(sigpipe_r) back = 0;	/* broken pipe, exit session */
    /* on sighup reconfigure _after_ the session */
    if(pj->e2) back = 0;	/* but on errors exit loop */
    if(pj->e3) back = 0;
  }
  if(!back) {
    prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(53));
  }
  
  return back;
}



/**	Change the UID and GID of the process.
	To minimize the risk of abuse the prqd process should not run
	in the root account.
	Use ``run as user ='' and ``run as group ='' in the
	prqd.conf file to choose a user account and group.
	@param	pj	Pointer to prqd job structure.
*/
static
void
change_user_and_group DK_P1(PJ *,pj)
{
  struct passwd *ppwd = NULL;
  struct group *pgrp = NULL;
  uid_t	tuid;
  gid_t tgid;
  
  tuid = geteuid();
  tgid = getegid();
  if((pj->prqdc)->rag) {	
    pgrp = getgrnam((pj->prqdc)->rag);
    if(pgrp) {	/* gr_gid */
      tgid = pgrp->gr_gid;	
    } else {
      pj->e1 = 1;
      
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(47), (pj->prqdc)->rag);
    }
  } else {			
    prqdlog(pj, PRQD_PRIO_WARNING, prqd_get_kw(49));
  }
  if((pj->prqdc)->rau) {	
    ppwd = getpwnam((pj->prqdc)->rau);
    if(ppwd) {	/* pw_uid */
      tuid = ppwd->pw_uid;	
    } else {
      pj->e1 = 1;
      
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(48), (pj->prqdc)->rau);
    }
  } else {			
    prqdlog(pj, PRQD_PRIO_WARNING, prqd_get_kw(50));
  }
  (void)chown((pj->prqdc)->sockname, tuid, tgid);
  (void)chown(prqdconf_get_logfile(), tuid, tgid);
  prqdbe_change_ownership(pj, tuid, tgid);
  if(pgrp) {			
    setgid(tgid);
    prqdlog(pj, PRQD_PRIO_INFO, prqd_get_kw(52), (pj->prqdc)->rag);
  }
  if(ppwd) {			
    setuid(tuid);
    prqdlog(pj, PRQD_PRIO_INFO, prqd_get_kw(51), (pj->prqdc)->rau);
  }
  
}



/**	For each printer retrieve information about
	the currently open job from the database.
	@param	pj	Pointer to prqd job structure.
*/
static
void
initialize_open_jobs DK_P1(PJ *,pj)
{
  PR *pr;
  
  dksto_it_reset((pj->prqdc)->pri);
  while((pr = (PR *)dksto_it_next((pj->prqdc)->pri)) != NULL) {
    if(!(pr->alt)) {
    
      (pr->cj)[0] = '\0';
      if((2 + strlen(pr->n)) < pj->sz_dbkey) {	
        sprintf(pj->dbkey, "j:%s", pr->n);
	if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) {
	  
	  if(strlen(pj->dbval) < (pr->sz_cj)) {
	    strcpy(pr->cj, pj->dbval);
	    
	  } else {
	    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(46), pj->dbval);
	  }
	} else {
	  prqdbe_store(pj, pj->dbkey, pr->cj);
	}
      } else {
        prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(36), pr->n);
      }
    }
  }
  
}



/**	Read a request from standard input to buffer.
	@param	pj	Pointer to prqd job structure.
	@return	1 on success, 0 on error.
*/
static
int
read_request DK_P1(PJ *,pj)
{
  int back = 0;
  size_t sz = 0;
  fd_set rfds;
  struct timeval  tv;
  
  if(((pj->prqdc)->to_seconds > 0L) ||((pj->prqdc)->to_microseconds > 0L)) {
    prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(17));
    FD_ZERO(&rfds);
    FD_SET(pj->ss, &rfds);
    tv.tv_sec = (pj->prqdc)->to_seconds;
    tv.tv_usec = (pj->prqdc)->to_microseconds;
    if(select((1 + pj->ss), &rfds, NULL, NULL, &tv) > 0) {
      if(FD_ISSET(pj->ss,&rfds)) {
        sz = recv(pj->ss, pj->b1, pj->sz_b1, 0);
      }
    }
  } else {
    prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(18));
    sz = recv(pj->ss, pj->b1, pj->sz_b1, 0);
  }
  if(sz > 0) {
    prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(19), (unsigned long)sz);
    if(sz >= pj->sz_b1) {
      (pj->b1)[(pj->sz_b1) - 1] = '\0';
    } else {
      (pj->b1)[sz] = '\0';
    }
    back = 1;
  } else {
    prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(20));
  }
  
  return back;
}



/**	Write user information file for a user on a printer class.
	@param	pj	Pointer to prqd job structure.
	@param	uname	User name.
	@param	pclass	Printer class name.
*/
void
prqd_write_userinfo_file DK_P3(PJ *,pj, char *,uname, char *,pclass)
{
  PC *pc; LE *le;
  long uap = 0L;
  long upp = 0L;
  long l;
  char keybuffer[PRQD_DBENTRY_SIZE], valbuffer[PRQD_DBENTRY_SIZE], pagebuffer[32];
  int limit_value;
  unsigned long limit_pages;
  int is_bigger = 0, apply_limit = 0, is_allowed = 0;
  struct group *gr;
  struct passwd *pw;
  uid_t	uid_uname;
  gid_t gid_uname;
  char *fn, *p1, **lfdptr;
  time_t timer;
  struct tm *tm;
  FILE *fipo;
  size_t sz;
  int do_print = 0;
  
  if((pj) && (uname) && (pclass)) {		
    if((pj->prqdc)->userinfdir) {
      if((3 + strlen(uname) + strlen(pclass)) < sizeof(keybuffer)) {
        /* find printed pages */
        sprintf(keybuffer, "p:%s:%s", pclass, uname);
	if(prqdbe_fetch(pj, keybuffer, valbuffer, sizeof(valbuffer))) {
	  if(sscanf(valbuffer, "%ld", &l) == 1) {
	    upp = l;
	  }
	}
	/* find pages in personal account */
	sprintf(keybuffer, "a:%s:%s", pclass, uname);
	if(prqdbe_fetch(pj, keybuffer, valbuffer, sizeof(valbuffer))) {
	  if(sscanf(valbuffer, "%ld", &l) == 1) {
	    uap = l;
	  }
	}
	/* find limit */
	limit_value = LIMIT_VALUE_DENIED; limit_pages = 0UL; is_allowed = 0;
	pc = (PC *)dksto_it_find_like((pj->prqdc)->cli, pclass, 1);
	if(pc) {
	  if(pc->wi) {
	    do_print = 1;
	    uid_uname = 0; gid_uname = 0;
	    pw = getpwnam(uname);
	    if(pw) {
	      uid_uname = pw->pw_uid;
	      gid_uname = pw->pw_gid;
	    } else {
	      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(45), uname);
	    }
	    dksto_it_reset(pc->li);
	    while((le = dksto_it_next(pc->li)) != NULL) {
	      is_bigger = 0;
	      if(le->val > limit_value) {
	        is_bigger = 1;
	      } else {
	        if((le->val == LIMIT_VALUE_PAGES) && (limit_value == LIMIT_VALUE_PAGES)) {
	          if(le->p > limit_pages) {
		    is_bigger = 1;
		  }
	        }
	      }
	      if(is_bigger) {
	        apply_limit = 0;
	        switch(le->tp) {
	          case LIMIT_ENTRY_TYPE_DEFAULT: {
	            apply_limit = 1;
	          } break;
	          case LIMIT_ENTRY_TYPE_GROUP: {
	            if(le->who) {
	              gr = getgrnam(le->who);
	              if(gr) {
	                if(pw) {
	      	          if(gid_uname == gr->gr_gid) {
	      	            apply_limit = 1;
	      	          }
	      	        }
	      	        if(!apply_limit) {
	      	          lfdptr = gr->gr_mem;
	      	          while((*lfdptr) && (apply_limit == 0)) {
	      	            if(strcmp(*lfdptr, uname) == 0) {
	      	              apply_limit = 1;
	      	            }
	      	            lfdptr++;
	      	          }
	      	        }
	              }
	            }
	          } break;
	          case LIMIT_ENTRY_TYPE_USER: {
	            if(le->who) {
	              if(strcmp(le->who, uname) == 0) {
	                apply_limit = 1;
	              }
	            }
	          } break;
	        }
	        if(apply_limit) {
	          limit_value = le->val;
		  limit_pages = le->p;
	        }
	      }
	    }
	  }
	} else {		
	  prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(44), pclass);
	}
	/* decide */
	switch(limit_value) {
	  case LIMIT_VALUE_PAGES: {
	    if(limit_pages > upp) {
	      is_allowed = 1;
	    } else {
	      if(uap > 0L) {
	        is_allowed = 1;
	      }
	    }
	  } break;
	  case LIMIT_VALUE_UNLIMITED: {
	    is_allowed = 1;
	  } break;
	}
	/* write output */
	if(do_print) {
	  sz = 7 + strlen((pj->prqdc)->userinfdir) + strlen(uname) + strlen(pclass);
	  fn = dk_new(char,sz);
	  if(fn) {				
	    time(&timer);
	    tm = localtime(&timer);
	    sprintf(fn, "%s/%s-%s.txt", (pj->prqdc)->userinfdir, pclass, uname);
	    
            fipo = dksf_fopen(fn, "w");
	    if(fipo) {				
	      fchmod(fileno(fipo), (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH));
	      fprintf(fipo, "%s   %04d-%02d-%02d %02d:%02d:%02d\r\n",
	        uift[0], (1900 + tm->tm_year), (1 + tm->tm_mon), tm->tm_mday,
	        tm->tm_hour, tm->tm_min, tm->tm_sec
	      );
              switch(limit_value) {
	        case LIMIT_VALUE_UNLIMITED: {
	          p1 = uift[6];
	        } break;
	        case LIMIT_VALUE_PAGES: {
	          sprintf(pagebuffer, "%lu", limit_pages);
		  p1 = pagebuffer;
	        } break;
	        default: {
	          sprintf(pagebuffer, "%lu", 0UL);
		  p1 = pagebuffer;
	        } break;
	      }
	      fprintf(fipo, "%s   %ld / %s\r\n", uift[1], upp, p1);
	      fprintf(fipo, "%s   %ld\r\n", uift[2], uap);
	      fprintf(fipo, "%s   %s\r\n", uift[3], uift[is_allowed ? 4 : 5]);
	      fclose(fipo); fipo = NULL;
	    } else {				
	      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(43), fn);
	    }
	    chmod(fn, 0644);
	    dk_delete(fn);
	  } else {				
	    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42));
	  }
	}
      } else {					
        prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(41), uname, pclass);
      }
    }
  } else {					
    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(40));
  }
  
}



/**	Process jobstart request.
	Check whether the user is allowed to print.
	@param	pj	Pointer to prqd job structure.
	@return	1 on success, 0 on error.
*/
static
int
jobstart_request DK_P1(PJ *,pj)
{
  int back = 0;
  long upp = 0L, uap = 0L;
  
  prqd_pj_get_names(pj);
  prqd_pj_find_limit(pj);
  if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
    sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname);
    if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) {
      if(sscanf(pj->dbval, "%ld", &upp) != 1) {
        upp = 0L; back = 0;
	
	prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(37), pj->dbval);
      }
    }
  } else {		
    back = 0;
  }
  if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
    sprintf(pj->dbkey, key_user_account, pj->cname, pj->uname);
    if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) {
      if(sscanf(pj->dbval, "%ld", &uap) != 1) {
        uap = 0L; back = 0;
	
	prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(38), pj->dbval);
      }
    }
  } else {		
    back = 0;
  }
  
  
  switch(pj->limit_type) {
    case LIMIT_VALUE_UNLIMITED: {
      
      strcpy(pj->b2, response_keywords[0]);
      back = 1;
    } break;
    case LIMIT_VALUE_PAGES: {
      
      if((unsigned long)upp < pj->limit_pages) {
        
        strcpy(pj->b2, response_keywords[0]);
	back = 1;
      } else {
        if(uap > 0L) {
	  
	  strcpy(pj->b2, response_keywords[0]);
	  back = 1;
	} else {	
	}
      }
    } break;
    default: {
      
    } break;
  }
  prqdlog(pj, PRQD_PRIO_INFO, prqd_get_kw(39), back);
  
  return back;
}



/**	Seed pseudo random number generator for faked limits.
	For users which do not exist we return a faked entry
	indicating ``over limit'' to prevent prqd for being
	abused to guess user names.
*/
static
void
set_random_for_name DK_P1(char *,uname)
{
  char *p1, *p2;
  size_t szrs, i;
  unsigned rs;
  rs = (unsigned)getpid;
  p1 = (char *)(&rs);
  szrs = sizeof(unsigned);
  p2 = uname; i = 0;
  while(*p2) {
    p1[i] = p1[i] ^ (*p2);
    p2++; i++;
    if(i >= szrs) { i = 0; }
  }
  srand(rs);
}



/**	Process info request.
	@param	pj	Pointer to prqd job structure.
	@param	p	Additional arguments for the request.
	@return	1 on success, 0 on error.
*/
static
int
info_request DK_P2(PJ *,pj, char *,p)
{
  int back = 1;
  char *p1, *p2;
  long upp = 0L, uap = 0L, page_limit = 0L;
  struct passwd *pw;
  /*
  	uppt: bereits gedruckte Seiten
	uapt: Seiten in persoenlichem Account
	lt:   Limit (Seitenzahl)
	ft:   Flag (0 oder 1)
  */
  char uppt[32], uapt[32], lt[32], ft[32];
  
  sprintf(uppt, "%ld", 0L);
  sprintf(uapt, "%ld", 0L);
  sprintf(lt, "%ld", 0L);
  sprintf(ft, "%ld", 0L);
  if(p) {				
    p1 = dkstr_start(p, NULL);
    if(p1) {				
      p2 = dkstr_next(p1, NULL);
      if(p2) {				
        pw = getpwnam(p2);
	if(pw) {
          prqd_pj_set_arg(pj, 'n', p2);
	  prqd_pj_set_arg(pj, 'P', p1);
	  prqd_pj_get_names(pj);
	  prqd_pj_find_limit(pj);
	  if(pj->cname) {
            if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
              sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname);
              if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) {
                if(sscanf(pj->dbval, "%ld", &upp) != 1) {
                  upp = 0L; back = 0;
	          
	          prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(37), pj->dbval);
                } else {
	          sprintf(uppt, "%ld", upp);
	        }
              }
            } else {		
              back = 0;
            }
            if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
              sprintf(pj->dbkey, key_user_account, pj->cname, pj->uname);
              if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) {
                if(sscanf(pj->dbval, "%ld", &uap) != 1) {
                  uap = 0L; back = 0;
	          
	          prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(38), pj->dbval);
                } else {
	          sprintf(uapt, "%ld", uap);
	        }
              }
            } else {		
              back = 0;
            }
            
            
            switch(pj->limit_type) {
              case LIMIT_VALUE_UNLIMITED: {
                
                /* strcpy(pj->b2, response_keywords[0]); */
	        sprintf(ft, "%ld", 1L);
	        sprintf(lt, "%ld", -1L);
                back = 1;
              } break;
              case LIMIT_VALUE_PAGES: {
                
	        sprintf(lt, "%ld", pj->limit_pages);
                if((unsigned long)upp < pj->limit_pages) {
                  
                  /* strcpy(pj->b2, response_keywords[0]); */
		  sprintf(ft, "%ld", 1L);
	          back = 1;
                } else {
                  if(uap > 0L) {
	            
	            /* strcpy(pj->b2, response_keywords[0]); */
		    sprintf(ft, "%ld", 1L);
	            back = 1;
	          } else {	
	          }
                }
              } break;
              default: {
                
              } break;
            }
	  } else {
	    /* ##### ERROR: Missing class name */
	    
	  }
	} else {
	  /* ##### Invalid user name. Provide faked data */
	  prqd_pj_set_arg(pj, 'n', p2);
	  prqd_pj_set_arg(pj, 'P', p1);
	  prqd_pj_get_names(pj);
	  prqd_pj_find_limit(pj);
	  if(pj->cname) {
	    page_limit = 200L;
	    set_random_for_name(pj->uname);
	    upp = page_limit + (rand() % page_limit);
	    uap = 0 - (rand() % (2 * page_limit));
            sprintf(uppt, "%ld", upp);
	    sprintf(uapt, "%ld", uap);
	    sprintf(lt, "%ld", page_limit);
	  } else {
	    /* ##### ERROR: Missing class name */
	  }
	}
      } else {
	/* ##### ERROR: Missing user name */
	
      }
    } else {
      /* ##### ERROR: Missing printer name */
      
    }
  }
  sprintf(pj->b2, "%s %s %s %s\n", lt, uppt, uapt, ft);
  
  return back;
}



/**	Process file start request.
	Keep track of the current print user and the old pagecount
	value of a printer.
	@param	pj	Pointer to prqd job structure.
	@return	1 on success, 0 on error.
*/
static
int
filestart_request DK_P1(PJ *,pj)
{
  int back = 1;
  PR *pr;
  
#if OLD_VERSION
  if((pj->uname) && (pj->pname) && (pj->pages)) {
    
    pc = NULL; pr = NULL;
    pr = dksto_it_find_like((pj->prqdc)->pri, pj->pname, 1);
    if(pr) {				
      while(pr->alt) { pr = pr->alt; }
      if(pr->pc) {			
        pc = pr->pc;
	  if((1 + strlen(pj->pages) + strlen(pj->uname)) < pr->sz_cj) {
	    
	    sprintf(pr->cj, "%s:%s", pj->pages, pj->uname);
	    if((2 + strlen(pr->n)) < pj->sz_dbkey) {
	      
	      sprintf(pj->dbkey, "j:%s", pr->n);
	      prqdbe_store(pj, pj->dbkey, pr->cj);
	    }
	  } else {
	  }
      } else {				
      }
    } else {				
    }
  }
#else
  prqd_pj_get_names(pj);
  if((pj->uname) && (pj->pname) && (pj->pages) && (pj->rname) && (pj->cname)) {
    
    pr = dksto_it_find_like((pj->prqdc)->pri, pj->rname, 1);
    if(pr) { 
      if((1 + strlen(pj->pages) + strlen(pj->uname)) < pr->sz_cj) {
        sprintf(pr->cj, current_job_format, pj->pages, pj->uname);
	if((2 + strlen(pr->n)) < pj->sz_dbkey) {
	  sprintf(pj->dbkey, key_current_job_format, pr->n);
	  if(!prqdbe_store(pj, pj->dbkey, pr->cj)) {
	    back = 0;	
	  }
	} else {	
	  prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(36), pr->n);
	  back = 0;
	}
      } else {		
        prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(36), pj->uname);
        back = 0;
      }
    } else {		
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(26), pj->rname);
      back = 0;
    }
  } else {	
    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(25));
  }
#endif
  
  return back;
}



/** Buffer for print limit text. */
static char limit_buffer[32];



/** Keywords used in limitations. */
static char *limit_keywords[] = { "denied", "unlimited" };



/**	Convert limit value back to text form.
	@param	pj	Pointer to prqd job structure.
	@return	Pointer to text form of limit.
*/
static
char *limit_str DK_P1(PJ *,pj)
{
  char *back;
  back = limit_keywords[0];
  switch(pj->limit_type) {
    case LIMIT_VALUE_UNLIMITED: {
      back = limit_keywords[1];
    } break;
    default: {
      sprintf(limit_buffer, "%lu", pj->limit_pages);
      back = limit_buffer;
    } break;
  }
  return back;
}



/**	Handle fileend request.
	Calculate number of pages in the job and process and
	change database entries.
	@param	pj	Pointer to print job data.
	@return	1 on success, 0 on error.
*/

#if VERSION_BEFORE_2010_10_26

static
int
fileend_request DK_P1(PJ *,pj)
{
  int back = 1;
  PR *pr;
  unsigned long job_start_pages, job_end_pages;
  long job_pages;
  long upp;		/* user pages printed */
  long uap;		/* user account of pages */
  char *ou;		/* old user name (start of job) */
  int must_balance;
  
#if OLD_VERSION
  if((pj->uname) && (pj->pname) && (pj->pages)) {
    prqd_pj_find_limit(pj);
    
    pc = NULL; pr = NULL;
    pr = dksto_it_find_like((pj->prqdc)->pri, pj->pname, 1);
    if(pr) {
      while(pr->alt) { pr = pr->alt; }
      if(pr->pc) {
        pc = pr->pc;
	if(pc) {

	}
      }
    }
  }
#else
  job_start_pages = job_end_pages = 0UL;
  job_pages = 0L;
  upp = uap = 0L;
  ou = NULL;
  prqd_pj_get_names(pj);
  prqd_pj_find_limit(pj);
  if((pj->uname) && (pj->pname) && (pj->pages) && (pj->rname) && (pj->cname)) {
    
    pr = dksto_it_find_like((pj->prqdc)->pri, pj->rname, 1);
    if(pr) {					
      ou = dkstr_chr(pr->cj, ':');
      if(ou) {					
        *(ou++) = '\0';
	if(strcmp(ou, pj->uname) == 0) {	
	  if(sscanf(pr->cj, "%lu", &job_start_pages) == 1) {
	    if(sscanf(pj->pages, "%lu", &job_end_pages) == 1) {
	      if(job_end_pages > job_start_pages) {
	        job_pages = (long)(job_end_pages - job_start_pages);
		if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
		  sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname);
		  if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) {
		    if(sscanf(pj->dbval, "%ld", &upp) != 1) {
		      upp = 0L; back = 0;	
		    }
		  }
		} else {		
		  back = 0;
		}
		if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
		  sprintf(pj->dbkey, key_user_account, pj->cname, pj->uname);
		  if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) {
		    if(sscanf(pj->dbval, "%ld", &uap) != 1) {
		      uap = 0L; back = 0;	
		    }
		  }
		} else {		
		  back = 0;
		}
		
		upp = upp + job_pages;
		if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
		  sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname);
		  sprintf(pj->dbval, "%ld", upp);
		  if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) {
		    back = 0;		
		  }
		} 
		/* ##### LOG job summary */
prqdlog(pj,PRQD_PRIO_INFO,logmsgs[0],pj->uname,pj->cname,pj->pname,job_pages,XSTR(pj->hname),XSTR(pj->tname));
prqdlog(pj,PRQD_PRIO_INFO,logmsgs[1],pj->uname,pj->cname,upp,limit_str(pj));
		must_balance = 0;
		if(pj->limit_type == LIMIT_VALUE_PAGES) {
		  if(upp > pj->limit_pages) {
		    must_balance = 1;
		  }
		}
		if(must_balance) {	
		  uap = uap + (long)(pj->limit_pages) - upp;
		  upp = pj->limit_pages;
		  if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
		    sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname);
		    sprintf(pj->dbval, "%ld", upp);
		    if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) {
		      back = 0;	
		    }
		  }
		  if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
		    sprintf(pj->dbkey, key_user_account, pj->cname, pj->uname);
		    sprintf(pj->dbval, "%ld", uap);
		    if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) {
		      back = 0;	
		    }
		  }
prqdlog(pj, PRQD_PRIO_INFO, logmsgs[3],pj->uname,pj->cname);
prqdlog(pj,PRQD_PRIO_INFO,logmsgs[1],pj->uname,pj->cname,upp,limit_str(pj));
prqdlog(pj,PRQD_PRIO_INFO,logmsgs[2],pj->uname,pj->cname,pj->dbval);
		}
	      } else {	
	        prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(34));
	        back = 0;
	      }
	    } else {	
	      prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(33), pj->pages);
	      back = 0;
	    }
	  } else {	
	    prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(32), pr->cj);
	    back = 0;
	  }
	} else {	
	  prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(31), ou, pj->uname);
	}
      } else {		
        prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(30));
      }
      (pr->cj)[0] = '\0';
      if((2 + strlen(pr->n)) < pj->sz_dbkey) {
        sprintf(pj->dbkey, key_current_job_format, pr->n);
	prqdbe_store(pj, pj->dbkey, pr->cj);
      }
    } else {		
      prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(26), pj->rname);
      back = 0;
    }
    prqd_write_userinfo_file(pj, pj->uname, pj->cname);
  } else {		
    prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(25));
    back = 0;
  }
#endif
  
  return back;
}

#else

static
int
fileend_request DK_P1(PJ *,pj)
{
  int back = 1;
  PR *pr;
  unsigned long job_start_pages, job_end_pages;
  long job_pages;
  long upp;		/* user pages printed */
  long uap;		/* user account of pages */
  char *ou;		/* old user name (start of job) */
  int must_balance;
  int have_account;
  
#if OLD_VERSION
  if((pj->uname) && (pj->pname) && (pj->pages)) {
    prqd_pj_find_limit(pj);
    
    pc = NULL; pr = NULL;
    pr = dksto_it_find_like((pj->prqdc)->pri, pj->pname, 1);
    if(pr) {
      while(pr->alt) { pr = pr->alt; }
      if(pr->pc) {
        pc = pr->pc;
	if(pc) {

	}
      }
    }
  }
#else
  job_start_pages = job_end_pages = 0UL;
  job_pages = 0L;
  upp = uap = 0L;
  ou = NULL; have_account = 0;
  prqd_pj_get_names(pj);
  prqd_pj_find_limit(pj);
  if((pj->uname) && (pj->pname) && (pj->pages) && (pj->rname) && (pj->cname)) {
    
    pr = dksto_it_find_like((pj->prqdc)->pri, pj->rname, 1);
    if(pr) {					
      ou = dkstr_chr(pr->cj, ':');
      if(ou) {					
        *(ou++) = '\0';
	if(strcmp(ou, pj->uname) == 0) {	
	  if(sscanf(pr->cj, "%lu", &job_start_pages) == 1) {
	    if(sscanf(pj->pages, "%lu", &job_end_pages) == 1) {
	      if(job_end_pages > job_start_pages) {
	        job_pages = (long)(job_end_pages - job_start_pages);
		if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
		  sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname);
		  if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) {
		    if(sscanf(pj->dbval, "%ld", &upp) != 1) {
		      upp = 0L; back = 0;	
		    }
		  }
		} else {		
		  back = 0;
		}
		if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
		  sprintf(pj->dbkey, key_user_account, pj->cname, pj->uname);
		  if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) {
		    have_account = 1;
		    if(sscanf(pj->dbval, "%ld", &uap) != 1) {
		      uap = 0L; back = 0;	
		      have_account = 0;
		    }
		  }
		} else {		
		  back = 0;
		}
		
		upp = upp + job_pages;
		/* ##### LOG job summary */
prqdlog(pj,PRQD_PRIO_INFO,logmsgs[0],pj->uname,pj->cname,pj->pname,job_pages,XSTR(pj->hname),XSTR(pj->tname));
prqdlog(pj,PRQD_PRIO_INFO,logmsgs[1],pj->uname,pj->cname,upp,limit_str(pj));
		must_balance = 0;
		if(pj->limit_type == LIMIT_VALUE_PAGES) {
		  if(upp > pj->limit_pages) {
		    must_balance = 1;
		  }
		}
		if(must_balance) {	
		  if(have_account || (pj->prqdc)->do_balance) {
		    uap = uap + (long)(pj->limit_pages) - upp;
		    upp = pj->limit_pages;
		    if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
		      sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname);
		      sprintf(pj->dbval, "%ld", upp);
		      if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) {
		        back = 0;	
		      }
		    }
		    if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
		      sprintf(pj->dbkey, key_user_account, pj->cname, pj->uname);
		      sprintf(pj->dbval, "%ld", uap);
		      if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) {
		        back = 0;	
		      }
		    }
prqdlog(pj, PRQD_PRIO_INFO, logmsgs[3],pj->uname,pj->cname);
prqdlog(pj,PRQD_PRIO_INFO,logmsgs[1],pj->uname,pj->cname,upp,limit_str(pj));
prqdlog(pj,PRQD_PRIO_INFO,logmsgs[2],pj->uname,pj->cname,pj->dbval);
		  } else {
		    upp = pj->limit_pages;
		    if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
		      sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname);
		      sprintf(pj->dbval, "%ld", upp);
		      if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) {
		        back = 0;		
		      }
		    } 
prqdlog(pj, PRQD_PRIO_INFO, logmsgs[4],pj->uname,pj->cname);
prqdlog(pj,PRQD_PRIO_INFO,logmsgs[1],pj->uname,pj->cname,upp,limit_str(pj));
		  }
		} else {		
		  if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) {
		    sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname);
		    sprintf(pj->dbval, "%ld", upp);
		    if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) {
		      back = 0;		
		    }
		  } 
		}
	      } else {	
	        prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(34));
	        back = 0;
	      }
	    } else {	
	      prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(33), pj->pages);
	      back = 0;
	    }
	  } else {	
	    prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(32), pr->cj);
	    back = 0;
	  }
	} else {	
	  prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(31), ou, pj->uname);
	}
      } else {		
        prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(30));
      }
      (pr->cj)[0] = '\0';
      if((2 + strlen(pr->n)) < pj->sz_dbkey) {
        sprintf(pj->dbkey, key_current_job_format, pr->n);
	prqdbe_store(pj, pj->dbkey, pr->cj);
      }
    } else {		
      prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(26), pj->rname);
      back = 0;
    }
    prqd_write_userinfo_file(pj, pj->uname, pj->cname);
  } else {		
    prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(25));
    back = 0;
  }
#endif
  
  return back;
}


#endif



/**	Handle jobend request. No action necessary.
	@param	pj	Pointer to prqd job structure.
	@return	1 on success, 0 on error.
*/
static
int
jobend_request DK_P1(PJ *,pj)
{
  int back = 1;
  return back;
}



/**	Find request type and start dedicated handler function.
	@param	pj	Pointer to print job data.
	@return	1 on success, 0 on error.
*/
static
int
process_request DK_P1(PJ *,pj)
{
  int back = 0;
  
  char *p1, *p2;
  pj->rqtstr = p1 = dkstr_start(pj->b1, NULL);
  if(p1) {
    dkstr_chomp(p1, NULL); 
    prqdlog(pj,PRQD_PRIO_INFO,"%s",p1);
    p2 = dkstr_next(p1, NULL);
    prqd_pj_get_rq_type(pj, p1);
    pj->uname = NULL;
    pj->pname = NULL;
    pj->tname = NULL;
    pj->pages = NULL;
    pj->cname = NULL;
    pj->rname = NULL;
    pj->hname = NULL;
    switch(pj->rqt) {
      case PRQD_RQ_T_JOBSTART: {
        pj->must_respond = 0x01;
	prqd_pj_set_all_args(pj, p2);
	if(!jobstart_request(pj)) {
	  prqd_pj_use_deny_action(pj);
	}
	back = 1;
      } break;
      case PRQD_RQ_T_FILESTART: case PRQD_RQ_T_START: {
        pj->must_respond = 0x00;
	prqd_pj_set_all_args(pj, p2);
	back = filestart_request(pj);
      } break;
      case PRQD_RQ_T_END: case PRQD_RQ_T_FILEEND: {
        pj->must_respond = 0x00;
	prqd_pj_set_all_args(pj, p2);
	back = fileend_request(pj);
      } break;
      case PRQD_RQ_T_JOBEND: {
        pj->must_respond = 0x00;
	prqd_pj_set_all_args(pj, p2);
	back = jobend_request(pj);
      } break;
      case PRQD_RQ_T_CONTROL: {
        back = prqdctrl_request(pj, p2);
      } break;
      case PRQD_RQ_T_INFO: {
        pj->must_respond = 0x01;
	back = info_request(pj, p2);
      } break;
      default: {
      } break;
    }
  } else {
  }
  
  return back;
}



/**	Send response text to peer if necessary.
	@param	pj	Pointer to print job data.
	@return	1 on success, 0 on error.
*/
static
int
write_response DK_P1(PJ *,pj)
{
  int back = 0; size_t sz, osz;
  char logbuffer[64];
  fd_set wfds; struct timeval tv;
  
  if(pj->must_respond) {
    
    if(strlen(pj->b2) < sizeof(logbuffer)) {
      
      strcpy(logbuffer, pj->b2);
      dkstr_chomp(logbuffer, NULL);
      prqdlog(pj,PRQD_PRIO_INFO,"Response: %s",logbuffer);
    }
    sz = 1 + strlen(pj->b2);
    if(((pj->prqdc)->to_seconds > 0L) ||((pj->prqdc)->to_microseconds > 0L)) {
      FD_ZERO(&wfds);
      FD_SET(pj->ss,&wfds);
      tv.tv_sec = (pj->prqdc)->to_seconds;
      tv.tv_usec = (pj->prqdc)->to_microseconds;
      if(select((1 + pj->ss), NULL, &wfds, NULL, &tv) > 0) {
        if(FD_ISSET(pj->ss,&wfds)) {
	  osz = send(pj->ss, pj->b2, sz, 0);
	  if(osz >= sz) {
	    back = 1;
	  }
	}
      }
    } else {
      osz = send(pj->ss, pj->b2, sz, 0);
      if(osz >= sz) {
        back = 1;
      }
    }
  } else {		
    back = 1;
  }
  
  return  back;
}



/**	Run a service session.
	As long as the peer sends request data process
	the requests and send the response.
	@param	pj	Pointer to print job data.
*/
static
void
run_session DK_P1(PJ *,pj)
{
  int cc;
  
  cc = 1; sigpipe_r = 0; pj->e3 = 0;
  while((cc) && prqd_cc3(pj)) {
    /* (pj->b2)[0] = '\0'; */
    strcpy(pj->b2, response_keywords[3]);
    pj->must_respond = 0x00;
    prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(15));
    if(read_request(pj)) {
      prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(16));
      if(!process_request(pj)) {
        cc = 0;
      }
      prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(21));
      write_response(pj);
    } else {
      cc = 0;
    }
  }
  
}



/**	Accept connection requests and start a session
	as long as we can continue.
	@param	pj	Pointer to print job structure.
*/
static
void
inner_loop DK_P1(PJ *,pj)
{
  sighup_r = 0; pj->e2 = 0;
  while(prqd_cc2(pj)) {
    prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(10));
    pj->ss = accept(pj->sock, NULL, 0);
    if(pj->ss > -1) {		
      prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(11));
      run_session(pj);
      prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(12));
      close(pj->ss); pj->ss = -1;
      prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(13));
    } else {			
      if(errno != EINTR) {
	prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(14));
        pj->e1 = 1;
      }
    }
  }
}



/**	Create and set up socket and run service loop.
	@param pj	Pointer to print job structure.
*/
static
void
outer_loop DK_P1(PJ *,pj)
{
  static int first_run = 1;
  struct sockaddr_un soun;
  char *sn;
  
  sn = (pj->prqdc)->sockname;	
  unlink(sn);
  pj->sock = socket(PF_UNIX, SOCK_STREAM, 0);
  if(pj->sock > -1) {
    soun.sun_family = AF_UNIX;
    if(strlen(sn) < 108) {
      strcpy(soun.sun_path, sn);
      if(bind(pj->sock, (struct sockaddr *)(&soun), SZSOUN) == 0) {
        if(listen(pj->sock, pj->backlog) == 0) {
	  chmod(sn, 0660);
	  if(first_run) {
	    first_run = 0;
	    change_user_and_group(pj);
	  }
	  prqdlog(pj, PRQD_PRIO_PROGRESS, prqd_get_kw(8));
	  initialize_open_jobs(pj);
	  prqdlog(pj, PRQD_PRIO_PROGRESS, prqd_get_kw(9));
	  inner_loop(pj);
	} else {
	  prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(7), sn);
	  pj->e1 = 1;
	}
      } else {
        prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(6), sn);
	pj->e1 = 1;
      }
    } else {
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(5), sn);
      pj->e1 = 1;
    }
  } else {
    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(4));
    pj->e1 = 1;
  }
  
}



/**	Run the service in a loop as long as we can continue
	(no SIGTERM received or fatal error occured).
	On SIGHUP reload the configuration file.
	@param pj	Pointer to print job structure.
*/
static
void
run_the_service DK_P1(PJ *,pj)
{
  
#line 1501 "prqd.ctr"

  
  while(prqd_cc1(pj)) {			
    sighup_r = 0; pj->e2 = 0;
    pj->prqdc = prqdconf_new_prqdc(pj, prqdconf_get_cfgfile());
    if(pj->prqdc) {			
      if(prqdbe_open(pj)) {		
        if((pj->prqdc)->sockname) {
          outer_loop(pj);
	} else {
	  prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(24));
	  pj->e1 = 1;
	}
        prqdbe_close(pj);
      } else {				
	prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(23));
	pj->e1 = 1;
      }
      prqdconf_delete_prqdc(pj->prqdc); pj->prqdc = NULL;
    } else {				
      prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(22));
      pj->e1 = 1;
    }
  }
  
  
#line 1526 "prqd.ctr"

}



/**	Run processing.
	Change to root directory, set file creation mask,
	close all files and detach from terminals before
	running the service in background.
	@param	pj	Pointer to print job structure.
*/
static
void
run DK_P1(PJ *,pj)
{
  int maxfiles, i;
  pid_t pid;
  dk_signal_disp_t disp_hup;
  dk_signal_disp_t disp_int;
  dk_signal_disp_t disp_term;
  dk_signal_disp_t disp_pipe;

  disp_hup = disp_int = disp_term = disp_pipe = NULL;
  (void)chdir("/");
  umask(077);
  maxfiles = (int)dksf_get_maxfiles();
  for(i = 0; i < maxfiles; i++) { (void)close(i); }
  pid = fork();
  if(pid == 0) {
#if DK_HAVE_SETSID
  setsid();
#else
#if DK_HAVE_SETPGRP
  setpgrp();
#endif
#endif
    disp_hup = dksignal_set(SIGHUP, sighup_handler);
    pid = fork();
    if(pid == 0) {
      dksf_write_pid_file(appname, 1);
      disp_int  = dksignal_set(SIGINT,sigint_handler);
      disp_term = dksignal_set(SIGTERM,sigterm_handler);
      disp_pipe = dksignal_set(SIGPIPE,sigpipe_handler);
#if DK_HAVE_SYSLOG_H
      openlog(prqdconf_get_progname(),(LOG_CONS|LOG_NDELAY|LOG_PID),LOG_LPR);
#endif
      prqdlog(pj, PRQD_PRIO_INFO, "%s", prqd_get_kw(1));
      prqdlog(pj, PRQD_PRIO_INFO, "%s", prqd_get_kw(3));
      run_the_service(pj);
      prqdlog(pj, PRQD_PRIO_INFO, "%s", prqd_get_kw(2));
#if DK_HAVE_SYSLOG_H
      closelog();
#endif
      if(disp_pipe) { dksignal_set(SIGPIPE,disp_pipe); }
      if(disp_term) { dksignal_set(SIGTERM,disp_term); }
      if(disp_int)  { dksignal_set(SIGINT,disp_int); }
      dksf_write_pid_file(appname, 0);
    } else {
      if(pid == ((pid_t)-1)) {
        pj->ec  = 1;
      }
    }
    dksignal_set(SIGHUP, disp_hup);
  } else {
    if(pid == ((pid_t)-1)) {
      pj->ec = 1;
    }
  }
}



/** Buffer to read input line. */
static char inbuffer[PRQD_BUFFER_SIZE];

/** Buffer to create a response. */
static char outbuffer[PRQD_BUFFER_SIZE];

/** Buffer for use as b1 in static PJ. */
static char dbkey[PRQD_DBENTRY_SIZE];

/** Buffer for use as b2 in static PJ. */
static char dbval[PRQD_DBENTRY_SIZE];

/** Buffer for use as b3 in static PJ. */
static char pgnamebuffer[PRQD_DBENTRY_SIZE];

/** Array of argument pointers for use in static PJ. */
static char *args[53];



/**	Main function.

This function initializes a static PJ structure,
and sets up buffers before running further
processing.
@param	argc	Number of command line arguments.
@param	argv	Command line arguments array.
@return	0 on success, positive values on error.
*/
#if DK_HAVE_PROTOTYPES
int main(int argc, char *argv[])
#else
int main(argc, argv) int argc; char *argv[];
#endif
{
  PJ pj;
  prqd_pj_init(&pj);
  pj.b1 = inbuffer; pj.sz_b1 = sizeof(inbuffer);
  pj.b2 = outbuffer; pj.sz_b2 = sizeof(outbuffer);
  pj.b3 = pgnamebuffer; pj.sz_b3 = sizeof(pgnamebuffer);
  pj.args = args;
  pj.dbkey = dbkey; pj.sz_dbkey = sizeof(dbkey);
  pj.dbval = dbval; pj.sz_dbval = sizeof(dbval);
  pj.allow_file_logging = 1;
  run(&pj);
  exit(pj.ec); return(pj.ec);
}



