/*
 * Copyright (c) 1990,1995 Regents of The University of Michigan.
 * All Rights Reserved.  See COPYRIGHT.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

#include <string.h>

#include <sys/param.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <atalk/logger.h>

/* POSIX.1 sys/wait.h check */
#include <sys/types.h>
#include <sys/wait.h>
#ifndef WEXITSTATUS
#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#endif /* ! WEXITSTATUS */
#ifndef WIFEXITED
#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
#endif /* ! WIFEXITED */

#include <errno.h>
#include <string.h>

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>

#include <netatalk/endian.h>
#include <netatalk/at.h>
#include <atalk/compat.h>
#include <atalk/atp.h>
#include <atalk/pap.h>
#include <atalk/paths.h>
#include <atalk/util.h>
#include <atalk/nbp.h>
#include <atalk/unicode.h>

#include "printer.h"
#include "printcap.h"
#include "session.h"
#include "uam_auth.h"
#include "print_cups.h"

#define MACCHARSET "MAC_ROMAN"

/* This maps to TReq and TResp, per "Inside AppleTalk" 10-13 */

typedef struct __attribute__((__packed__)) {
	uint8_t user_bytes[4];
	uint8_t data_bytes[4];
	uint8_t buf_len;
	char buf[255];
} rbuf_t;

static rbuf_t r_buf;

struct printer	defprinter;
struct printer	*printers = NULL;

int		debug = 0;
static char	*conffile = _PATH_PAPDCONF;
char		*printcap = _PATH_PAPDPRINTCAP;
unsigned char	connid, quantum, sock, oquantum = PAP_MAXQUANTUM;
char		*cannedstatus = "status: print spooler processing job";
struct printer	*printer = NULL;
char		*version = VERSION;
static char	*pidfile = _PATH_PAPDLOCK;

char		*uamlist;
char		*uampath = _PATH_PAPDUAMPATH;

/* Prototypes for locally used functions */
int getstatus( struct printer *pr, rbuf_t *buf );
int rprintcap( struct printer *pr );
static void getprinters( char *cf );


/* this only needs to be used by the server process */
static void papd_exit(const int i)
{
  server_unlock(pidfile);
  auth_unload();
  exit(i);
}

static void
die(int n)
{
    struct printer	*pr;
    struct at_addr	addr;

    memset(&addr, 0, sizeof(addr));

    for ( pr = printers; pr; pr = pr->p_next ) {
	if ( pr->p_flags & P_REGISTERED ) {
	    if ( nbp_unrgstr( pr->p_name, pr->p_type, pr->p_zone, &addr ) < 0 ) {
		LOG(log_error, logtype_papd, "can't unregister %s:%s@%s", pr->p_name,
			pr->p_type, pr->p_zone );
		papd_exit( n + 1 );
	    }
	    LOG(log_info, logtype_papd, "unregister %s:%s@%s", pr->p_name, pr->p_type,
		    pr->p_zone );
	}
#ifdef HAVE_CUPS
	if ( pr->p_flags & P_SPOOLED && pr->p_flags & P_CUPS_PPD ) {
		LOG(log_info, logtype_papd, "Deleting CUPS temp PPD file for %s (%s)", pr->p_name, pr->p_ppdfile);
		unlink (pr->p_ppdfile);
	}
#endif /* HAVE_CUPS */

    }
    papd_exit( n );
}

static void
reap(int sig _U_)
{
    int		status;
    int		pid;

    while (( pid = wait3( &status, WNOHANG, NULL )) > 0 ) {
	if ( WIFEXITED( status )) {
	    if ( WEXITSTATUS( status )) {
		LOG(log_error, logtype_papd, "child %d exited with %d", pid,
			WEXITSTATUS( status ));
	    } else {
		LOG(log_info, logtype_papd, "child %d done", pid );
	    }
	} else {
	    if ( WIFSIGNALED( status )) {
		LOG(log_error, logtype_papd, "child %d killed with %d", pid,
			WTERMSIG( status ));
	    } else {
		LOG(log_error, logtype_papd, "child %d died", pid );
	    }
	}
    }
    return;
}

int main(int ac, char **av)
{
    extern char         *optarg;

    ATP			atp;
    struct atp_block	atpb;
    struct sockaddr_at	sat;
    struct sigaction	sv;
    struct iovec	iov;
    fd_set		fdset;
    struct printer	*pr;
    char		*p, hostname[ MAXHOSTNAMELEN ];
    char		cbuf[ 8 ];
    int			c;
    char 		*atname;
    char* string, * macName = MACCHARSET;

    /* Set codepages here since it is no longer auto-initialzed anymore*/
    set_charset_name(CH_UNIX, "UTF8");
    set_charset_name(CH_MAC, MACCHARSET);

    if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
	perror( "gethostname" );
	exit( 1 );
    }
    if (( p = strchr( hostname, '.' )) != NULL ) {
	*p = '\0';
    }
    if (( defprinter.p_name = (char *)malloc( strlen( hostname ) + 1 ))
	    == NULL ) {
	perror( "malloc" );
	exit( 1 );
    }
    strcpy( defprinter.p_name, hostname );
    defprinter.p_type = "LaserWriter";
    defprinter.p_zone = "*";
    memset(&defprinter.p_addr, 0, sizeof(defprinter.p_addr));
#ifdef __svr4__
    defprinter.p_flags = P_PIPED;
    defprinter.p_printer = "/usr/bin/lp -T PS";
#else /* __svr4__ */
    defprinter.p_flags = P_SPOOLED;
    defprinter.p_printer = "lp";
#endif /* __svr4__ */
    defprinter.p_operator = "operator";
    defprinter.p_spool = _PATH_PAPDSPOOLDIR;
#ifdef ABS_PRINT
    defprinter.p_role = NULL;
    defprinter.p_srvid = 0;
#endif /* ABS_PRINT */
    defprinter.p_pagecost = 200;		/* default cost */
    defprinter.p_pagecost_msg = NULL;
    defprinter.p_lock = "lock";

    while (( c = getopt( ac, av, "adf:p:P:v" )) != EOF ) {
	switch ( c ) {
	case 'a' :		/* for compatibility with old papd */
	    break;

	case 'd' :		/* debug */
	    debug++;
	    break;

	case 'f' :		/* conffile */
	    conffile = optarg;
	    break;

	case 'p' :		/* printcap */
	    printcap = optarg;
	    break;

	case 'P' :
	    pidfile = optarg;
	    break;

	case 'v' :		/* version */
	    printf( "papd (version %s)\n", VERSION );
	    exit ( 1 );
	    break;

	default :
	    fprintf( stderr,
		    "Usage:\t%s [ -d ] [ -f conffile ] [ -p printcap ]\n",
		    *av );
	    exit( 1 );
	}
    }


    switch (server_lock("papd", pidfile, debug)) {
    case 0: /* open a couple things again in the child */
      if (!debug && (c = open("/", O_RDONLY)) >= 0) {
	dup2(c, 1);
	dup2(c, 2);
      }
      break;
    case -1:
      exit(1);
    default:
      exit(0);
    }

    if ( debug )
	fault_setup(NULL);

    /*
     * Start logging.
     */
    if (( p = strrchr( av[ 0 ], '/' )) == NULL ) {
	p = av[ 0 ];
    } else {
	p++;
    }
    set_processname(p);
    syslog_setup(log_debug, logtype_default, logoption_ndelay | logoption_pid |
               debug ? logoption_perror : 0, logfacility_lpr );

    LOG(log_info, logtype_papd, "restart (%s)", version );
#ifdef HAVE_CUPS
    LOG(log_info, logtype_papd, "CUPS support enabled (%s)", CUPS_API_VERSION );
#endif

    getprinters( conffile );

    for ( pr = printers; pr; pr = pr->p_next ) {
	if (( pr->p_flags & P_SPOOLED ) && rprintcap( pr ) < 0 ) {
	    LOG(log_error, logtype_papd, "printcap problem: %s", pr->p_printer );
	}

	if (!(pr->p_flags & P_CUPS)) {
		if ((size_t)-1 != convert_string_allocate(CH_UNIX, CH_MAC, pr->p_name, -1, &atname)) {
			pr->p_u_name = pr->p_name;
			pr->p_name = atname;
		}
	}

	if (( pr->p_atp = atp_open( ATADDR_ANYPORT, &pr->p_addr )) == NULL ) {
	    LOG(log_error, logtype_papd, "atp_open: %s", strerror(errno) );
	    papd_exit( 1 );
	}
	if ( nbp_rgstr( atp_sockaddr( pr->p_atp ), pr->p_name, pr->p_type,
		pr->p_zone ) < 0 ) {
	    LOG(log_error, logtype_papd, "can't register %s:%s@%s", pr->p_u_name, pr->p_type,
		    pr->p_zone );
	    die( 1 );
	}
	if ( pr->p_flags & P_AUTH ) {
		LOG(log_info, logtype_papd, "Authentication enabled: %s", pr->p_u_name );
	}
	else {
		LOG(log_info, logtype_papd, "Authentication disabled: %s", pr->p_u_name );
	}
	LOG(log_info, logtype_papd, "register %s:%s@%s", pr->p_u_name, pr->p_type,
		pr->p_zone );
	pr->p_flags |= P_REGISTERED;
    }

    memset(&sv, 0, sizeof(sv));
    sv.sa_handler = die;
    sigemptyset( &sv.sa_mask );
    sv.sa_flags = SA_RESTART;
    if ( sigaction( SIGTERM, &sv, NULL ) < 0 ) {
	LOG(log_error, logtype_papd, "sigaction: %s", strerror(errno) );
	papd_exit( 1 );
    }

    sv.sa_handler = reap;
    sigemptyset( &sv.sa_mask );
    sv.sa_flags = SA_RESTART;
    if ( sigaction( SIGCHLD, &sv, NULL ) < 0 ) {
	LOG(log_error, logtype_papd, "sigaction: %s", strerror(errno) );
	papd_exit( 1 );
    }

    /*
     * Load UAMS
     */
    auth_load(uampath, uamlist);

    /*
     * Begin accepting connections.
     */
    FD_ZERO( &fdset );
    for (;;) {
	for ( pr = printers; pr; pr = pr->p_next ) {
	    FD_SET( atp_fileno( pr->p_atp ), &fdset );
	}
	if (( c = select( FD_SETSIZE, &fdset, NULL, NULL, NULL )) < 0 ) {
	    if ( errno == EINTR ) {
		continue;
	    }
	    LOG(log_error, logtype_papd, "select: %s", strerror(errno) );
	    papd_exit( 1 );
	}

	for ( pr = printers; pr; pr = pr->p_next ) {
	    if ( FD_ISSET( atp_fileno( pr->p_atp ), &fdset )) {
		int		err = 0;

		memset( &sat, 0, sizeof( struct sockaddr_at ));
#ifdef BSD4_4
		sat.sat_len = sizeof( struct sockaddr_at );
#endif /* BSD4_4 */
		sat.sat_family = AF_APPLETALK;
		sat.sat_addr.s_net = ATADDR_ANYNET;
		sat.sat_addr.s_node = ATADDR_ANYNODE;
		sat.sat_port = ATADDR_ANYPORT;
		/* do an atp_rsel(), to prevent hangs */
		if (( c = atp_rsel( pr->p_atp, &sat, ATP_TREQ )) != ATP_TREQ ) {
		    continue;
		}
		atpb.atp_saddr = &sat;
		atpb.atp_rreqdata = cbuf;
		atpb.atp_rreqdlen = sizeof( cbuf );
		if ( atp_rreq( pr->p_atp, &atpb ) < 0 ) {
		    LOG(log_error, logtype_papd, "atp_rreq: %s", strerror(errno) );
		    continue;
		}

		/* should check length of req buf */

		switch( cbuf[ 1 ] ) {
		case PAP_OPEN :
		    connid = (unsigned char)cbuf[ 0 ];
		    sock = (unsigned char)cbuf[ 4 ];
		    quantum = (unsigned char)cbuf[ 5 ];
		    r_buf.user_bytes[0] = connid;
		    r_buf.user_bytes[1] = PAP_OPENREPLY;
		    r_buf.user_bytes[2] = 0;
		    r_buf.user_bytes[3] = 0;

		    if (( pr->p_flags & P_SPOOLED ) && rprintcap( pr ) != 0 ) {
			LOG(log_error, logtype_papd, "printcap problem: %s",
				pr->p_printer );
			/* "IAT", 10-16 */
			r_buf.user_bytes[2] = 0xff;
			r_buf.user_bytes[3] = 0xff;
			err = 1;
		    }

#ifdef HAVE_CUPS
		   /*
		    * If cups is not accepting jobs, we return
		    * 0xffff to indicate we're busy
		    */
		    LOG(log_debug9, logtype_papd, "CUPS: PAP_OPEN");

		    if ( (pr->p_flags & P_SPOOLED) && (cups_get_printer_status ( pr ) == 0)) {
                        LOG(log_error, logtype_papd, "CUPS_PAP_OPEN: %s is not accepting jobs",
                                pr->p_printer );
			r_buf.user_bytes[2] = 0xff;
			r_buf.user_bytes[3] = 0xff;
                        err = 1;
                    }
#endif /* HAVE_CUPS */

		    /*
		     * If this fails, we've run out of sockets. Rather than
		     * just die(), let's try to continue. Maybe some sockets
		     * will close, and we can continue;
		     */
		    if (( atp = atp_open( ATADDR_ANYPORT,
					  &pr->p_addr)) == NULL ) {
			LOG(log_error, logtype_papd, "atp_open: %s", strerror(errno) );
			r_buf.user_bytes[2] = 0xff;
			r_buf.user_bytes[3] = 0xff;
			/* FIXME is it right? */
			r_buf.data_bytes[0] = 0x00;
			err = 1;
		    }
		    else {
		       r_buf.data_bytes[0] = atp_sockaddr( atp )->sat_port;
                    }
		    r_buf.data_bytes[1] = oquantum;
		    r_buf.data_bytes[2] = 0;
		    r_buf.data_bytes[3] = 0;

		    iov.iov_base = &r_buf;
		    iov.iov_len = 8 + getstatus( pr, &r_buf );
		    atpb.atp_sresiov = &iov;
		    atpb.atp_sresiovcnt = 1;
		    /*
		     * This may error out if we lose a route, so we won't die().
		     */
		    if ( atp_sresp( pr->p_atp, &atpb ) < 0 ) {
			LOG(log_error, logtype_papd, "atp_sresp: %s", strerror(errno) );
			err = 1;
		    }

		    if ( err ) {
			if (atp) {
			   atp_close(atp);
                        }
			continue;
		    }

		    switch ( c = fork()) {
		    case -1 :
			LOG(log_error, logtype_papd, "fork: %s", strerror(errno) );
                        atp_close(atp);
			continue;

		    case 0 : /* child */
			printer = pr;

			#ifndef HAVE_CUPS
			if (( printer->p_flags & P_SPOOLED ) &&
				chdir( printer->p_spool ) < 0 ) {
			    LOG(log_error, logtype_papd, "chdir %s: %s", printer->p_spool, strerror(errno) );
			    exit( 1 );
			}
			#else
			if (( printer->p_flags & P_SPOOLED ) &&
				chdir( SPOOLDIR ) < 0 ) {
			    LOG(log_error, logtype_papd, "chdir %s: %s", SPOOLDIR, strerror(errno) );
			    exit( 1 );
			}

			#endif

			sv.sa_handler = SIG_DFL;
			sigemptyset( &sv.sa_mask );
			sv.sa_flags = SA_RESTART;
			if ( sigaction( SIGTERM, &sv, NULL ) < 0 ) {
			    LOG(log_error, logtype_papd, "sigaction: %s", strerror(errno) );
			    exit( 1 );
			}

			if ( sigaction( SIGCHLD, &sv, NULL ) < 0 ) {
			    LOG(log_error, logtype_papd, "sigaction: %s", strerror(errno) );
			    exit( 1 );
                        }

			for ( pr = printers; pr; pr = pr->p_next ) {
			    atp_close( pr->p_atp );
			}
			sat.sat_port = sock;
			if ( session( atp, &sat ) < 0 ) {
			    LOG(log_error, logtype_papd, "bad session" );
			    exit( 1 );
			}
			exit( 0 );
			break;

		    default : /* parent */
			LOG(log_info, logtype_papd, "child %d for \"%s\" from %u.%u",
				c, pr->p_name, ntohs( sat.sat_addr.s_net ),
				sat.sat_addr.s_node);
			atp_close( atp );
		    }
		    break;

		case PAP_SENDSTATUS :
		    r_buf.user_bytes[0] = 0;
		    r_buf.user_bytes[1] = PAP_STATUS;
		    r_buf.user_bytes[2] = 0;
		    r_buf.user_bytes[3] = 0;
		    r_buf.data_bytes[0] = 0;
		    r_buf.data_bytes[1] = 0;
		    r_buf.data_bytes[2] = 0;
		    r_buf.data_bytes[3] = 0;

		    iov.iov_base = &r_buf;
		    iov.iov_len = 8 + getstatus( pr, &r_buf );
		    atpb.atp_sresiov = &iov;
		    atpb.atp_sresiovcnt = 1;
		    /*
		     * This may error out if we lose a route, so we won't die().
		     */
		    if ( atp_sresp( pr->p_atp, &atpb ) < 0 ) {
			LOG(log_error, logtype_papd, "atp_sresp: %s", strerror(errno) );
		    }
		    break;

		default :
		    LOG(log_error, logtype_papd, "Bad request from %u.%u!",
			    ntohs( sat.sat_addr.s_net ), sat.sat_addr.s_node );
		    continue;
		    break;
		}

#ifdef notdef
		/*
		 * Sometimes the child process will send its first READ
		 * before the parent has sent the OPEN REPLY.  Moving this
		 * code into the OPEN/STATUS switch fixes this problem.
		 */
		iov.iov_base = rbuf;
		iov.iov_len = 8 + getstatus( pr, &rbuf[ 8 ] );
		atpb.atp_sresiov = &iov;
		atpb.atp_sresiovcnt = 1;
		/*
		 * This may error out if we lose a route, so we won't die().
		 */
		if ( atp_sresp( pr->p_atp, &atpb ) < 0 ) {
		    LOG(log_error, logtype_papd, "atp_sresp: %s", strerror(errno) );
		}
#endif /* notdef */
	    }
	}
    }
    return 0;
}

/*
 * We assume buf is big enough for 255 bytes of data and a length byte.
 */

int getstatus(struct printer *pr, rbuf_t *buf)
{
#ifdef HAVE_CUPS
    if ( pr->p_flags & P_PIPED ) {
	buf->buf_len = strlen(cannedstatus);
	snprintf(buf->buf, 255, "%s", cannedstatus);
	return (buf->buf_len + 1);
    } else {
	cups_get_printer_status( pr );
	buf->buf_len = strlen(pr->p_status);
	snprintf(buf->buf, 255, "%s", pr->p_status);
	return (buf->buf_len + 1);
    }
#else
    char path[MAXPATHLEN];
    char getstatus_buffer[255];
    char *temp = NULL;
    FILE *fd = NULL;
    int rc;

    if ( pr->p_flags & P_SPOOLED && ( pr->p_spool != NULL )) {
	snprintf(path, MAXPATHLEN - 1, "%s/status", pr->p_spool);
	fd = fopen(path, O_RDONLY);
    }

    if ((pr->p_flags & P_PIPED) || (fd == NULL)) {
	buf->buf_len = strlen(cannedstatus);
	snprintf(buf->buf, 255, "%s", cannedstatus);
	return (buf->buf_len + 1);
    } else {
	rc = fread(getstatus_buffer, 255, sizeof(unsigned char), fd);
	fclose(fd);

	if (rc > 0) {
		if (temp != NULL) {
			temp[strcspn(temp, "\n")] = '\0';
			snprintf(buf->buf, 255, "%s", temp);
			buf->buf_len = strlen(buf->buf);
		}
	} else {
		snprintf(buf->buf, 255, "%s", "thisisanemptystring");
		buf->buf_len = 0;
	}

	return (buf->buf_len + 1);
    }
#endif /* HAVE_CUPS */
}

char	*pgetstr(char *id, char **area);
char	*getpname(char **area, int bufsize);

#define PF_CONFBUFFER	1024

static void getprinters( char *cf)
{
    char		buf[ PF_CONFBUFFER ], area[ PF_CONFBUFFER ], *a, *p, *name, *type, *zone;
    struct printer	*pr;
    int			c;

    while (( c = getprent( cf, buf, PF_CONFBUFFER )) > 0 ) {
	a = area;
	/*
	 * Get the printer's nbp name.
	 */
	if (( p = getpname( &a, PF_CONFBUFFER )) == NULL ) {
	    fprintf( stderr, "No printer name\n" );
	    exit( 1 );
	}

	if (( pr = (struct printer *)malloc( sizeof( struct printer )))
		== NULL ) {
	    perror( "malloc" );
	    exit( 1 );
	}
	memset( pr, 0, sizeof( struct printer ));

	name = defprinter.p_name;
	type = defprinter.p_type;
	zone = defprinter.p_zone;
	if ( nbp_name( p, &name, &type, &zone )) {
	    fprintf( stderr, "Can't parse \"%s\"\n", name );
	    exit( 1 );
	}
	if ( name != defprinter.p_name ) {
	    if (( pr->p_name = (char *)malloc( strlen( name ) + 1 )) == NULL ) {
		perror( "malloc" );
		exit( 1 );
	    }
	    strcpy( pr->p_name, name );
	} else {
	    pr->p_name = name;
	}
	if ( type != defprinter.p_type ) {
	    if (( pr->p_type = (char *)malloc( strlen( type ) + 1 )) == NULL ) {
		perror( "malloc" );
		exit( 1 );
	    }
	    strcpy( pr->p_type, type );
	} else {
	    pr->p_type = type;
	}
	if ( zone != defprinter.p_zone ) {
	    if (( pr->p_zone = (char *)malloc( strlen( zone ) + 1 )) == NULL ) {
		perror( "malloc" );
		exit( 1 );
	    }
	    strcpy( pr->p_zone, zone );
	} else {
	    pr->p_zone = zone;
	}

	if ( pnchktc( cf ) != 1 ) {
	    fprintf( stderr, "Bad papcap entry\n" );
	    exit( 1 );
	}

	/*
	 * Get PPD file.
	 */
	if (( p = pgetstr( "pd", &a ) )) {
	    if (( pr->p_ppdfile = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
		perror( "malloc" );
		exit( 1 );
	    }
	    strcpy( pr->p_ppdfile, p );
	}

	/*
	 * Get lpd printer name.
	 */
	if (( p = pgetstr( "pr", &a )) == NULL ) {
	    pr->p_printer = defprinter.p_printer;
	    pr->p_flags = defprinter.p_flags;
	} else {
	    if ( *p == '|' ) {
		p++;
		pr->p_flags = P_PIPED;
	    } else {
		pr->p_flags = P_SPOOLED;
	    }
	    if (( pr->p_printer = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
		perror( "malloc" );
		exit( 1 );
	    }
	    strcpy( pr->p_printer, p );
	}

	/*
	 * Do we want authenticated printing?
	 */
	if ((p = pgetstr( "ca", &a )) != NULL ) {
	    if ((pr->p_authprintdir = (char *)malloc(strlen(p)+1)) == NULL) {
		perror( "malloc" );
		exit(1);
	    }
	    strcpy( pr->p_authprintdir, p );
	    pr->p_flags |= P_AUTH;
	    pr->p_flags |= P_AUTH_CAP;
	} else { pr->p_authprintdir = NULL; }

	if ( pgetflag( "sp" ) == 1 ) {
	    pr->p_flags |= P_AUTH;
	    pr->p_flags |= P_AUTH_PSSP;
	}

	if ((p = pgetstr("am", &a)) != NULL ) {
		if ((uamlist = (char *)malloc(strlen(p)+1)) == NULL ) {
			perror("malloc");
			exit(1);
		}
		strcpy(uamlist, p);
	}

	if ( pr->p_flags & P_SPOOLED ) {
	    /*
	     * Get operator name.
	     */
	    if (( p = pgetstr( "op", &a )) == NULL ) {
		pr->p_operator = defprinter.p_operator;
	    } else {
		if (( pr->p_operator = (char *)malloc( strlen( p ) + 1 ))
			== NULL ) {
		    perror( "malloc" );
		    exit( 1 );
		}
		strcpy( pr->p_operator, p );
	    }
	}

	/* get printer's appletalk address. */
	if (( p = pgetstr( "pa", &a )) == NULL )
	    memcpy(&pr->p_addr, &defprinter.p_addr, sizeof(pr->p_addr));
	else
	    atalk_aton(p, &pr->p_addr);

#ifdef HAVE_CUPS
	if ((p = pgetstr("co", &a)) != NULL ) {
            pr->p_cupsoptions = strdup(p);
            LOG (log_error, logtype_papd, "enabling cups-options for %s: %s", pr->p_name, pr->p_cupsoptions);
	}
#endif

	/* convert line endings for setup sections.
           real ugly work around for foomatic deficiencies,
	   need to get rid of this */
	if ( pgetflag("fo") == 1 ) {
            pr->p_flags |= P_FOOMATIC_HACK;
            LOG (log_error, logtype_papd, "enabling foomatic hack for %s", pr->p_name);
	}

	if (strncasecmp (pr->p_name, "cupsautoadd", 11) == 0)
	{
#ifdef HAVE_CUPS
		pr = cups_autoadd_printers (pr, printers);
		printers = pr;
#else
		LOG (log_error, logtype_papd, "cupsautoadd: Cups support not compiled in");
#endif /* HAVE_CUPS */
	}
	else {
#ifdef HAVE_CUPS
		if ( cups_check_printer ( pr, printers, 1) == 0)
		{
			pr->p_next = printers;
			printers = pr;
		}
#else
		pr->p_next = printers;
		printers = pr;
#endif /* HAVE_CUPS */
	}
    }
    if ( c == 0 ) {
	endprent();
    } else {			/* No capability file, do default */
	printers = &defprinter;
    }
}

int rprintcap( struct printer *pr)
{

#ifdef HAVE_CUPS

    char		*p;

    if ( pr->p_flags & P_SPOOLED && !(pr->p_flags & P_CUPS_AUTOADDED) ) { /* Skip check if autoadded */
	if ( cups_printername_ok ( pr->p_printer ) != 1) {
	    LOG(log_error, logtype_papd, "No such CUPS printer: '%s'", pr->p_printer );
	    return( -1 );
	}
    }

    /*
     * Check for ppd file, moved here because of cups_autoadd we cannot check at the usual location
     */

    if ( pr->p_ppdfile == NULL ) {
	if ( (p = (char *) cups_get_printer_ppd ( pr->p_printer )) != NULL ) {
	    if (( pr->p_ppdfile = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
	    	LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) );
		exit( 1 );
	    }
	    strcpy( pr->p_ppdfile, p );
	    pr->p_flags |= P_CUPS_PPD;
	    LOG(log_info, logtype_papd, "PPD File for %s set to %s", pr->p_printer, pr->p_ppdfile );
	}
    }


#else

    char		buf[ 1024 ], area[ 1024 ], *a, *p;
    int			c;

    /*
     * Spool directory from printcap file.
     */
    if ( pr->p_flags & P_SPOOLED ) {
	if ( pgetent( printcap, buf, pr->p_printer ) != 1 ) {
	    LOG(log_error, logtype_papd, "No such printer: %s", pr->p_printer );
	    return( -1 );
	}

	/*
	 * Spool directory.
	 */
	if ( pr->p_spool != NULL && pr->p_spool != defprinter.p_spool ) {
	    free( pr->p_spool );
	}
	a = area;
	if (( p = pgetstr( "sd", &a )) == NULL ) {
	    pr->p_spool = defprinter.p_spool;
	} else {
	    if (( pr->p_spool = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
		LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) );
		exit( 1 );
	    }
	    strcpy( pr->p_spool, p );
	}

	/*
	 * Is accounting on?
	 */
	a = area;
	if ( pgetstr( "af", &a ) == NULL ) {
	    pr->p_flags &= ~P_ACCOUNT;
	} else {
	    pr->p_flags |= P_ACCOUNT;
#ifdef ABS_PRINT
	    if ( pr->p_role != NULL && pr->p_role != defprinter.p_role ) {
		free( pr->p_role );
	    }
	    a = area;
	    if (( p = pgetstr( "ro", &a )) == NULL ) {
		pr->p_role = defprinter.p_role;
	    } else {
		if (( pr->p_role =
			(char *)malloc( strlen( p ) + 1 )) == NULL ) {
		    LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) );
		    exit( 1 );
		}
		strcpy( pr->p_role, p );
	    }

	    if (( c = pgetnum( "si" )) < 0 ) {
		pr->p_srvid = defprinter.p_srvid;
	    } else {
		pr->p_srvid = c;
	    }
#endif /* ABS_PRINT */
	}


	/*
	 * Cost of printer.
	 */
	if ( pr->p_pagecost_msg != NULL &&
		pr->p_pagecost_msg != defprinter.p_pagecost_msg ) {
	    free( pr->p_pagecost_msg );
	}
	a = area;
	if (( p = pgetstr( "pc", &a )) != NULL ) {
	    if (( pr->p_pagecost_msg =
		    (char *)malloc( strlen( p ) + 1 )) == NULL ) {
		LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) );
		exit( 1 );
	    }
	    strcpy( pr->p_pagecost_msg, p );
	    pr->p_pagecost = 0;
	} else if ( pr->p_flags & P_ACCOUNT ) {
	    if (( c = pgetnum( "pc" )) < 0 ) {
		pr->p_pagecost = defprinter.p_pagecost;
	    } else {
		pr->p_pagecost = c;
	    }
	    pr->p_pagecost_msg = NULL;
	}

	/*
	 * Get lpd lock file.
	 */
	if ( pr->p_lock != NULL && pr->p_lock != defprinter.p_lock ) {
	    free( pr->p_lock );
	}
	a = area;
	if (( p = pgetstr( "lo", &a )) == NULL ) {
	    pr->p_lock = defprinter.p_lock;
	} else {
	    if (( pr->p_lock = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
		LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) );
		exit( 1 );
	    }
	    strcpy( pr->p_lock, p );
	}

	endprent();
    }
#endif /* HAVE_CUPS */

    return( 0 );
}
