%% options copyright owner = Dirk Krause copyright year = 2013-2014 license = bsd %% module #include "dk3all.h" #include "dk3sock.h" #include "dk3print.h" #include "dk3prcfg.h" #include "dkt-version.h" $!trace-include #ifndef LPRNGCL_MAX_QUEUENAME_SIZE /** Maximum length of queue name. */ #define LPRNGCL_MAX_QUEUENAME_SIZE 256 #endif #ifndef LPRNGCL_REQUEST_SIZE /** Maximum length of request. */ #define LPRNGCL_REQUEST_SIZE 256 #endif #ifndef LPRNGCL_RECEIVE_BUFFER_SIZE /** Buffer size for response from server. */ #define LPRNGCL_RECEIVE_BUFFER_SIZE 4096 #endif /** Job structure. */ typedef struct { dk3_app_t *app; /**< Application structure. */ dkChar const * const *msg; /**< Localized texts. */ dkChar const * const *nlmsg; /**< Not localized texts. */ dkChar const *tmpfn; /**< Name for temporary file. */ dk3_option_set_t *opt; /**< Options. */ dk3_print_conf_t *pc; /**< Print configuration. */ dk3_sto_t *sph; /**< Print host storage. */ dk3_sto_it_t *iph; /**< Iterator through print hosts. */ int cmd; /**< Command to execute. */ int comp; /**< Flag: Only jobs from current computer. */ int exval; /**< Exit status code. */ int havep; /**< Flag: Have printers. */ } LCL_JOB; /** Print host structure. */ typedef struct { dkChar const *hn; /**< Host name. */ dk3_sto_t *sQueues; /**< Print queues. */ dk3_sto_it_t *iQueues; /**< Queues iterator. */ double toc; /**< Timeout for connect. */ double tos; /**< Timeout for send. */ double tor; /**< Timeout for receive. */ } LCL_HOST; /** Print queue. */ typedef struct { dkChar const *qn; /**< Queue name. */ double toc; /**< Timeout for connect. */ double tos; /**< Timeout for send. */ double tor; /**< Timeout for receive. */ } LCL_QUEUE; /** Texts used by the program, not localized. */ static dkChar const * const lprngcl_nlmsg[] = { $!string-table macro=dkT # # 0: Program group name # dkt-3 # # 1: String table file name. # lprngcl.str # # 2: Help file name. # lprngcl.txt # # 3: File open mode for temporary file. # wb $!end }; /** 8-bit character strings used by the module, not localized. */ char const * const lprngcl_c8_kw[] = { $!string-table # # 0: Space # # # 1: Newline # \n # # 2: At # @ $!end }; /** Texts used by the program, replaced by localized versions. */ static dkChar const * const lprngcl_msg[] = { $!string-table macro=dkT,file=lprngcl.str # # 0 1 # Queue name not configured for " "! # # 2 3 # Print host name not configured for " "! # # 4 # Printer/user names too long for LPRng request! # # 5 6 # Failed to convert " " to print host encoding! # # 7 # Failed to find users login name! # # 8 # No queues configured to clean up. # # 9 10 # Printer " " not found! # # 11 12 # Printer name " " too long! $!end }; /** Flag: Use -c option (not supported by LPRng). */ #define LPRNGCL_USE_C 0 /** Options for the lprngcl program. */ static dk3_option_t const lprngcl_options[] = { { dkT('h'), dkT("help"), 0 }, { dkT('v'), dkT("version"), 0 }, { dkT('L'), dkT("license-terms"), 0 } #if LPRNGCL_USE_C , { dkT('c'), dkT("computer"), 0 } #endif }; /** Number of options in lprngcl_options. */ static size_t const lprngcl_sz_options = sizeof(lprngcl_options)/sizeof(dk3_option_t); /** Default help text shown if help file not found. */ static dkChar const * const lprngcl_text_help[] = { $!text macro=dkT NAME lprngcl - Clean LPRng print queues SYNOPSIS lprngcl [] lprngcl [-h] [-v] [-L] DESCRIPTION The lprngcl program deletes all print jobs created by the current user from the named LPD/LPRng print queues or from all found LPD/LPRng print queues if no printers are specified. It is intended for use in computer labs or PC classrooms in schools and universities. OPTIONS -h --help shows this help text. -v --version shows version information. -L --license-terms shows the license terms. RETURN VALUE The program returns exit status code 0 on success, all other status codes indicate an error. FILES The dk3print.conf file must be used to configure - type ("lprng" or "lpd") - host (print host running the LPD or LPRng software) - queue (Queue name on the remote print host). Additionally you can configure short alias names for the print queues. EXAMPLES Here is an example of a dk3print.conf file: [host ps] encoding = plain alias = ps.my-domain.org alias = 192.168.3.4 [printer Xerox Phaser 4500DP PS] alias = lp type = lprng host = ps queue = lp [printer HP Laserjet 4M] alias = hplj type = lprng host = ps queue = hplj AUTHOR Dirk Krause COPYRIGHT AND LICENSE Run lprngcl --license to see the license conditions. SEE ALSO http://dktools.sourceforge.net $!end }; /** License conditions. */ static dkChar const * const lprngcl_text_license[] = { $!text macro=dkT Copyright (c) 2013, 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 copyright 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 copyright holder(s) 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. $!end }; /** Version information. */ static dkChar const lprngcl_version[] = { DKT_VERSION }; /** Delete queue structure. @param qptr Queue to delete. */ static void lprngcl_delete_queue(LCL_QUEUE *qptr) { $? "+ lprngcl_delete_queue" if(qptr) { $? ". name = \"%s\"", qptr->qn $? ". timeouts = %lg %lg %lg", qptr->toc, qptr->tos, qptr->tor qptr->toc = qptr->tos = qptr->tor = 0.0; dk3_release(qptr->qn); dk3_delete(qptr); } $? "- lprngcl_delete_queue" } /** Create new queue entry. @param qn Queue name. @param toc Connect timeout. @param tos Send timeout. @param tor Receive timeout. @param app Application structure for diagnostics. @return Pointer to new queue structure on success, NULL on error. */ static LCL_QUEUE * lprngcl_new_queue( dkChar const *qn, double toc, double tos, double tor, dk3_app_t *app ) { LCL_QUEUE *back = NULL; if(qn) { back = dk3_new_app(LCL_QUEUE,1,app); if(back) { back->toc = toc; back->tos = tos; back->tor = tor; back->qn = dk3str_dup_app(qn, app); if(!(back->qn)) { lprngcl_delete_queue(back); back = NULL; } } } return back; } /** Compare two queues. @param l Left object. @param r Right object or name. @param cr Comparison criteria (0=queue/queue, 1=queue/name). @return Comparison result. */ static int lprngcl_compare_queues(void const *l, void const *r, int cr) { int back = 0; LCL_QUEUE const *pl; /* Left queue. */ LCL_QUEUE const *pr; /* Right queue. */ if(l) { if(r) { pl = (LCL_QUEUE const *)l; pr = (LCL_QUEUE const *)r; switch(cr) { case 1: { if(pl->qn) { back = dk3str_cmp(pl->qn, (dkChar *)r); if(back < 0) back = -1; if(back > 0) back = 1; } else { back = -1; } } break; default: { if(pl->qn) { if(pr->qn) { back = dk3str_cmp(pl->qn, pr->qn); if(back < 0) back = -1; if(back > 0) back = 1; } else back = 1; } else { if(pr->qn) back = -1; } } break; } } else back = 1; } else { if(r) back = -1; } return back; } /** Destroy LCL_HOST, release memory. @param ptr Host structure to destroy. */ static void lprngcl_delete_host(LCL_HOST *ptr) { void *vptr; /* Current queue as returned from iterator. */ LCL_QUEUE *queueptr; /* Current queue to delete. */ #if TRACE_DEBUG dkChar const *qptr; /* Queue name to report. */ #endif $? "+ lprngcl_delete_host" if(ptr) { $? ". name = \"%s\"", TR_STR(ptr->hn) dk3_release(ptr->hn); if(ptr->sQueues) { if(ptr->iQueues) { dk3sto_it_reset(ptr->iQueues); while(NULL != (vptr = dk3sto_it_next(ptr->iQueues))) { queueptr = (LCL_QUEUE *)vptr; #if TRACE_DEBUG qptr = queueptr->qn; #endif $? ". queue = \"%s\"", TR_STR(qptr) lprngcl_delete_queue(queueptr); } dk3sto_it_close(ptr->iQueues); } dk3sto_close(ptr->sQueues); } ptr->sQueues = NULL; ptr->iQueues = NULL; dk3_delete(ptr); } $? "- lprngcl_delete_host" } /** Compare two host entries. @param l Left object. @param r Right object or name. @param cr Comparison criteria (0=host/host, 1=host/name). @return Comparison result. */ static int lprngcl_compare_hosts(void const *l, void const *r, int cr) { LCL_HOST const *pl; /* Left host. */ LCL_HOST const *pr; /* Right host. */ dkChar const *name; /* Right host name. */ int back = 0; if(l) { if(r) { pl = (LCL_HOST const *)l; switch(cr) { case 1: { name = (dkChar const *)r; if(pl->hn) { back = dk3str_cmp(pl->hn, name); if(back < 0) back = -1; if(back > 0) back = 1; } else { back = -1; } } break; default: { pr = (LCL_HOST const *)r; if(pl->hn) { if(pr->hn) { back = dk3str_cmp(pl->hn, pr->hn); if(back < 0) back = -1; if(back > 0) back = 1; } else { back = 1; } } else { if(pr->hn) { back = -1; } } } break; } } else { back = 1; } } else { if(r) { back = -1; } } return back; } /** Create new host entry. @param hn Host name. @param app Application structure. @return Pointer to new structure on success, NULL on error. */ static LCL_HOST * lprngcl_new_host( dkChar const *hn, dk3_app_t *app ) { LCL_HOST *back = NULL; int ok = 0; /* Flag: Success. */ $? "+ lprngcl_new_host \"%s\"", TR_STR(hn) if(hn) { back = dk3_new_app(LCL_HOST,1,app); if(back) { back->sQueues = NULL; back->iQueues = NULL; back->hn = dk3str_dup_app(hn, app); if(back->hn) { back->sQueues = dk3sto_open_app(app); if(back->sQueues) { dk3sto_set_comp(back->sQueues, lprngcl_compare_queues, 0); back->iQueues = dk3sto_it_open(back->sQueues); if(back->iQueues) { ok = 1; } } } if(!(ok)) { lprngcl_delete_host(back); back = NULL; } } } $? "- lprngcl_new_host %s", TR_PTR(back) return back; } /** Get host timeouts. @param hptr Destination host to configure. @param job Job structure. @param hn Host name. */ static void lprngcl_transfer_host_to( LCL_HOST *hptr, LCL_JOB *job, dkChar const *hn ) { dk3_print_host_t *ph; /* Host to search for configuration. */ ph = dk3print_get_host(job->pc, hn); if(ph) { hptr->toc = ph->to_c; hptr->tos = ph->to_s; hptr->tor = ph->to_r; } } /** Add host and queue to list of cleanup queues. @param job Job structure. @param prname Printer name, may be NULL. @param hostname Print host name. @param queuename Name of queue on host. @param toc Timeout for connection attempts. @param tos Timeout for send operations. @param tor Timeout for receive operations. */ static void lprngcl_add_queue( LCL_JOB *job, dkChar const *prname, dkChar const *hostname, dkChar const *queuename, double toc, double tos, double tor ) { LCL_HOST *hptr; /* Server host for queue. */ LCL_QUEUE *qptr; /* New queue to add. */ $? "+ lprngcl_add_queue \"%s\" \"%s\" \"%s\"", TR_STR(prname), TR_STR(hostname), TR_STR(queuename) hptr = (LCL_HOST *)dk3sto_it_find_like(job->iph, (void *)hostname, 1); if(!(hptr)) { $? ". no hptr" hptr = lprngcl_new_host(hostname, job->app); if(hptr) { $? ". hptr" if(!dk3sto_add(job->sph, (void *)hptr)) { lprngcl_delete_host(hptr); $? "! add" hptr = NULL; job->exval = 1; } else { $? ". add" lprngcl_transfer_host_to(hptr, job, hostname); } } else { $? "! hptr" job->exval = 1; } } else { $? ". hptr found" } if(hptr) { $? ". name = \"%s\"", TR_STR(hptr->hn) qptr = (LCL_QUEUE *)dk3sto_it_find_like( hptr->iQueues, (void *)queuename, 1 ); if(qptr) { $? ". qptr" job->havep = 1; } else { $? ". no qptr" qptr = lprngcl_new_queue(queuename, toc, tos, tor, job->app); if(qptr) { if(dk3sto_add(hptr->sQueues, (void *)qptr)) { job->havep = 1; } else { lprngcl_delete_queue(qptr); qptr = NULL; job->exval = 1; } } else { job->exval = 1; } } } $? "- lprngcl_add_queue" } /** Add one queue to list of cleanup queues. @param job Job structure. @param pr Printer to add. @param det LPRng queue details. */ static void lprngcl_add_printer( LCL_JOB *job, dk3_printer_t *pr, dk3_printer_details_t *det ) { $? "+ lprngcl_add_printer" if((det->lprng).hostname) { if((det->lprng).queuename) { lprngcl_add_queue( job, pr->name, (det->lprng).hostname, (det->lprng).queuename, (det->lprng).to_c, (det->lprng).to_s, (det->lprng).to_r ); } else { $? "! queuename" /* ERROR: Queue name not configured! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 0, 1, pr->name); } } else { $? "! hostname" /* ERROR: Hostname not configured! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 2, 3, pr->name); } $? "- lprngcl_add_printer" } /** Write one file line to standard output, tabulator at start of line. @param obj Object modified by handler, NULL here. @param il Input line. @return 1. */ static int lprngcl_response_line_handler(void *obj, dkChar *il) { dk3str_delnl(il); dk3sf_fputc(dkT('\t'), stdout); dk3sf_fputs(il, stdout); dk3sf_fputc(dkT('\n'), stdout); return 1; } /** Split double timeout value to seconds and microseconds. @param secs Destination variable for seconds. @param usecs Destination variable for microseconds. @param value Timeout value. */ static void lprngcl_set_timeout( long *secs, long *usecs, double value ) { double ds; /* Seconds. */ double du; /* Microseconds. */ *secs = 0L; *usecs = 0L; if(value > 0.0) { ds = floor(value); du = value - ds; du = 1000000.0 * du; *secs = (long)ds; *usecs = (long)du; } } /** Process one request (send request to host and show response). @param job Job structure. @param hptr Host structure. @param rq Request data. @param enc Encoding used on server. @param toc Timeout for connection attempts. @param tos Timeout for send operations. @param tor Timeout for receive operations. @return 1 if we can do further connections to the same host, 0 otherwise. */ static int lprngcl_process_request( LCL_JOB *job, LCL_HOST *hptr, char const *rq, int enc, double toc, double tos, double tor ) { char buf[LPRNGCL_RECEIVE_BUFFER_SIZE]; /* Buffer for response from print server. */ dkChar dbuf[LPRNGCL_RECEIVE_BUFFER_SIZE]; /* Buffer to process response file line by line. */ FILE *tmpf = NULL; /* Temporary file. */ dk3_socket_t sock = INVALID_SOCKET; /* Socket. */ long tocs = 0L; /* Connect timeout seconds. */ long tocu = 0L; /* Connect timeout microseconds. */ long toss = 0L; /* Send timeout seconds. */ long tosu = 0L; /* Send timeout microseconds. */ long tors = 0L; /* Receive timeout seconds. */ long toru = 0L; /* Receive timeout microseconds. */ int back = 1; #if 0 int bufl = 0; /* Buffer length. */ #endif int szwr = 0; /* Size written. */ int show = 0; /* Show temporary file. */ $? "+ lprngcl_process_request" tmpf = dk3sf_fopen_app(job->tmpfn, lprngcl_nlmsg[3], job->app); if(tmpf) { show = 1; } lprngcl_set_timeout(&tocs, &tocu, toc); lprngcl_set_timeout(&toss, &tosu, tos); lprngcl_set_timeout(&tors, &toru, tor); $? ". connect timeout %ld %ld", tocs, tocu $? ". send timeout %ld %ld", toss, tosu $? ". recive timeout %ld %ld", tors, toru sock = dk3socket_dkchar_open_net_stream_client( hptr->hn, 515, 0, tocs, tocu, NULL, job->app ); if(dk3socket_check(sock)) { szwr = dk3socket_send(sock, rq, strlen(rq), toss, tosu, NULL, job->app); if((int)strlen(rq) != szwr) { /* Fewer bytes written than expected! */ job->exval = 1; } szwr = dk3socket_shutdown(sock, DK3_TCPIP_SHUTDOWN_WRITE, NULL, job->app); if(szwr) { do { szwr = dk3socket_recv( sock, buf, sizeof(buf), tors, toru, NULL, job->app ); if((szwr > 0) && (tmpf)) { dk3sf_fwrite_app(buf, 1, (size_t)szwr, tmpf, job->app); } } while(szwr > 0); } else { /* Shutdown failed */ } dk3socket_close(sock, NULL, job->app); sock = INVALID_SOCKET; } else { /* Failed to connect to server! */ back = 0; job->exval = 1; } if(tmpf) { fclose(tmpf); } if(show) { dk3stream_process_filename_lines_app( NULL, lprngcl_response_line_handler, job->tmpfn, dbuf, DK3_SIZEOF(dbuf,dkChar), dk3app_get_encoding(job->app), enc, job->app ); } $? "- lprngcl_process_request %d", back return back; } /** Do cleanup for one remote host. @param job Job structure. @param hptr Current host to process. */ static void lprngcl_cleanup_for_host(LCL_JOB *job, LCL_HOST *hptr) { char rq[LPRNGCL_REQUEST_SIZE]; /* Buffer to construct request. */ char bu[LPRNGCL_REQUEST_SIZE]; /* Buffer for temporary conversions. */ LCL_QUEUE *qptr; /* Current queue. */ dk3_print_host_t *ph; /* Current print host. */ dk3_print_host_alias_t *al; /* Print host alias. */ dkChar const *queuename; /* Current queue name. */ dkChar const *logname; /* Current users login. */ #if LPRNGCL_USE_C dkChar const *hostname; /* Current computer name. */ #endif double toc; /* Timeout for connect. */ double tos; /* Timeout for send. */ double tor; /* Timeout for receive. */ #if LPRNGCL_USE_C size_t sl; /* Required string length. */ #endif int cc; /* Flag: Can continue. */ int encoding; /* Print host encoding. */ int res; /* Operation result. */ int ie; /* Input encoding. */ int rqok = 0; /* Flag: Request constructed. */ $? "+ lprngcl_cleanup_for_host \"%s\"", TR_STR(hptr->hn) /* Find encoding used by the server. */ ie = dk3app_get_encoding(job->app); encoding = DK3_ENCODING_UTF8; ph = (dk3_print_host_t *)dk3sto_it_find_like( (job->pc)->iPrintHosts, (void *)(hptr->hn), 1 ); if(!(ph)) { al = (dk3_print_host_alias_t *)dk3sto_it_find_like( (job->pc)->iHostAliases, (void *)(hptr->hn), 1 ); if(al) { ph = al->host; } } if(ph) { if(DK3_ENCODING_UTF8 == ph->enc) { encoding = DK3_ENCODING_UTF8; } else { encoding = DK3_ENCODING_PLAIN; } } /* Process print queues. */ logname = dk3app_get_logname(job->app); if(logname) { dk3sto_it_reset(hptr->iQueues); cc = 1; do { qptr = (LCL_QUEUE *)dk3sto_it_next(hptr->iQueues); if(qptr) { queuename = qptr->qn; /* Set timeouts. */ toc = qptr->toc; tos = qptr->tos; tor = qptr->tor; if(toc < 0.0) { toc = hptr->toc; } if(tos < 0.0) { tos = hptr->tos; } if(tor < 0.0) { tor = hptr->tor; } /* Print queue name. */ dk3sf_fputs(queuename, stdout); dk3sf_fputc(dkT('@'), stdout); dk3sf_fputs(hptr->hn, stdout); dk3sf_fputc(dkT('\n'), stdout); /* Construct request. */ rq[0] = ' '; rq[1] = '\0'; switch(encoding) { case DK3_ENCODING_UTF8: { res = dk3str_to_c8u_app(bu, sizeof(bu), queuename, ie, job->app); } break; default: { res = dk3str_to_c8p_app(bu, sizeof(bu), queuename, ie, job->app); } break; } if(res) { if(sizeof(rq) > (strlen(rq) + strlen(bu))) { strcat(rq, bu); if(sizeof(rq) > (strlen(rq) + strlen(lprngcl_c8_kw[0]))) { strcat(rq, lprngcl_c8_kw[0]); switch(encoding) { case DK3_ENCODING_UTF8: { res = dk3str_to_c8u_app( bu, sizeof(bu), logname, ie, job->app ); } break; default: { res = dk3str_to_c8p_app( bu, sizeof(bu), logname, ie, job->app ); } break; } if(res) { if(sizeof(rq) > (strlen(rq) + strlen(bu))) { strcat(rq, bu); if(sizeof(rq) > (strlen(rq) + strlen(lprngcl_c8_kw[0]))) { strcat(rq, lprngcl_c8_kw[0]); if(sizeof(rq) > (strlen(rq) + strlen(bu))) { strcat(rq, bu); rqok = 1; #if LPRNGCL_USE_C if(job->comp) { rqok = 0; if(sizeof(rq) > (strlen(rq)+strlen(lprngcl_c8_kw[2]))) { hostname = dk3app_get_hostname(job->app); switch(encoding) { case DK3_ENCODING_UTF8: { res = dk3str_to_c8u_app( bu, sizeof(bu), hostname, ie, job->app ); } break; default: { res = dk3str_to_c8p_app( bu, sizeof(bu), hostname, ie, job->app ); } break; } if(res) { sl = strlen(rq) + strlen(bu); if(sizeof(rq) > sl) { rqok = 1; strcat(rq, lprngcl_c8_kw[2]); strcat(rq, bu); } else { /* ERROR: Names too long! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4); } } } else { /* ERROR: Names too long! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4); } } #endif if(rqok) { rqok = 0; if(sizeof(rq) > (strlen(rq)+strlen(lprngcl_c8_kw[1]))) { rqok = 1; strcat(rq, lprngcl_c8_kw[1]); } else { /* ERROR: Names too long! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4); } } } else { /* ERROR: User name too long! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4); } } else { /* ERRROR: User name too long! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4); } } else { /* ERROR: User name too long! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4); } } else { /* ERROR: Failed to convert log name! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 5, 6, logname); } } else { /* ERROR: Printer name too long! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4); } } else { /* ERROR: Printer name too long! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4); } } else { job->exval = 1; } /* Process request. */ if(rqok) { $? ". REQUEST=\"%s\"", rq rq[0] = 0x05; if(!lprngcl_process_request(job, hptr, rq, encoding, toc, tos, tor)) { cc = 0; } } } else { cc = 0; } } while((qptr) && (cc)); } else { /* ERROR: Log name not found! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 7); } $? "- lprngcl_cleanup_for_host" } /** Cleanup up all queues marked for cleanup. @param job Job structure. */ static void lprngcl_cleanup_listed_printers(LCL_JOB *job) { LCL_HOST *hptr; /* Current host to process. */ void *vptr; /* Host as returned from iterator. */ $? "+ lprngcl_cleanup_listed_printers" if(job->havep) { $? ". have printers" if(dk3socket_up(NULL, job->app)) { $? ". TCP/IP up" dk3sto_it_reset(job->iph); while(NULL != (vptr = dk3sto_it_next(job->iph))) { hptr = (LCL_HOST *)vptr; lprngcl_cleanup_for_host(job, hptr); } dk3socket_down(NULL, job->app); } else { $? "! TCP/IP" job->exval = 1; } } else { /* WARNING: No queues configured to clean up. */ dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 8); } $? "- lprngcl_cleanup_listed_printers" } /** Clean up all printers on the system. @param job Job structure. */ static void lprngcl_cleanup_all_printers(LCL_JOB *job) { void *vptr; /* Printer as returned from iterator. */ dk3_printer_t *pr; /* Current printer. */ $? "+ lprngcl_cleanup_all_printers" dk3sto_it_reset((job->pc)->iPrinters); while(NULL != (vptr = dk3sto_it_next((job->pc)->iPrinters))) { pr = (dk3_printer_t *)vptr; switch(pr->t_p) { case DK3_PRINTER_TYPE_WINDOWS: { switch(pr->t_s) { case DK3_PRINTER_TYPE_LPD: case DK3_PRINTER_TYPE_LPRNG: { lprngcl_add_printer(job, pr, &(pr->det_s)); } break; } } break; case DK3_PRINTER_TYPE_LPD: case DK3_PRINTER_TYPE_LPRNG: { lprngcl_add_printer(job, pr, &(pr->det_p)); } break; } } lprngcl_cleanup_listed_printers(job); $? "- lprngcl_cleanup_all_printers" } /** Clean up the named printers. @param job Job structure. @param numargs Number of command line arguments. */ static void lprngcl_cleanup_named_printers(LCL_JOB *job, int numargs) { dkChar buf[LPRNGCL_MAX_QUEUENAME_SIZE]; /* Temporary buffer for queue name. */ dkChar const *pname; /* Print queue name. */ dkChar *p1; /* Print host name. */ dk3_printer_t *pr; /* Printer entry. */ dk3_printer_alias_t *al; /* Printer alias. */ $? "+ lprngcl_cleanup_named_printers" int i; for(i = 0; i < numargs; i++) { pname = dk3opt_get_arg(job->opt, i); if(pname) { $? ". pname = \"%s\"", TR_STR(pname) if(dk3str_len(pname) < DK3_SIZEOF(buf,dkChar)) { dk3str_cpy_not_overlapped(buf, pname); p1 = dk3str_chr(buf, dkT('@')); if(p1) { $? ". direct queue" *(p1++) = dkT('\0'); lprngcl_add_queue(job, pname, p1, buf, -1.0, -1.0, -1.0); } else { $? ". from configuration" pr = (dk3_printer_t *)dk3sto_it_find_like( (job->pc)->iPrinters, (void *)buf, 1 ); if(!(pr)) { al = (dk3_printer_alias_t *)dk3sto_it_find_like( (job->pc)->iPrintAliases, (void *)buf, 1 ); if(al) { pr = al->printer; } } if(pr) { switch(pr->t_p) { case DK3_PRINTER_TYPE_WINDOWS: { switch(pr->t_s) { case DK3_PRINTER_TYPE_LPD: case DK3_PRINTER_TYPE_LPRNG: { lprngcl_add_printer(job, pr, &(pr->det_s)); } break; } } break; case DK3_PRINTER_TYPE_LPD: case DK3_PRINTER_TYPE_LPRNG: { lprngcl_add_printer(job, pr, &(pr->det_p)); } break; } } else { /* ERROR: Printer not found! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 9, 10, pname); } } } else { /* ERROR: Printer name too long! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 11, 12, pname); } } } lprngcl_cleanup_listed_printers(job); $? "- lprngcl_cleanup_named_printers" } /** Do cleanup. @param job Job structure. */ static void lprngcl_do_cleanup(LCL_JOB *job) { dkChar tmpfn[DK3_MAX_PATH]; /* File name for temporary file. */ int numargs; /* Number of command line arguments. */ $? "+ lprngcl_do_cleanup" if(dk3app_get_temp_file_name(job->app, tmpfn, DK3_SIZEOF(tmpfn,dkChar))) { job->tmpfn = dk3str_dup_app(tmpfn, job->app); if(job->tmpfn) { job->pc = dk3print_conf_open(job->app, 1); if(job->pc) { job->sph = dk3sto_open_app(job->app); if(job->sph) { dk3sto_set_comp(job->sph, lprngcl_compare_hosts, 0); job->iph = dk3sto_it_open(job->sph); if(job->iph) { numargs = dk3opt_get_num_args(job->opt); job->exval = 0; if(0 == numargs) { lprngcl_cleanup_all_printers(job); } else { lprngcl_cleanup_named_printers(job, numargs); } dk3print_conf_close(job->pc); job->pc = NULL; } } } } } $? "- lprngcl_do_cleanup" } /** Run with a configured job structure. @param job Job structure. */ static void lprngcl_run(LCL_JOB *job) { $? "+ lprngcl_run" job->opt = dk3opt_open_from_app( lprngcl_options, lprngcl_sz_options, dkT('\0'), NULL, job->app ); if(job->opt) { if(0 == dk3opt_get_error_code(job->opt)) { if(dk3opt_is_set(job->opt, dkT('h'))) { job->cmd |= DK3_APP_CMD_HELP; } if(dk3opt_is_set(job->opt, dkT('v'))) { job->cmd |= DK3_APP_CMD_VERSION; } if(dk3opt_is_set(job->opt, dkT('L'))) { job->cmd |= DK3_APP_CMD_LICENSE; } if(dk3opt_is_set(job->opt, dkT('c'))) { job->comp = 1; } if(0 == job->cmd) { $? ". run normally" lprngcl_do_cleanup(job); } else { job->exval = 0; dk3sf_initialize_stdout(); if((job->cmd) & DK3_APP_CMD_VERSION) { dk3sf_fputs(lprngcl_version, stdout); dk3sf_fputc(dkT('\n'), stdout); } if((job->cmd) & DK3_APP_CMD_LICENSE) { dk3sf_fputt(lprngcl_text_license, stdout); } if((job->cmd) & DK3_APP_CMD_HELP) { dk3app_help(job->app, lprngcl_nlmsg[2], lprngcl_text_help); } } } dk3opt_close(job->opt); job->opt = NULL; } $? "- lprngcl_run" } /** Program entry point. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, any other value indicates an error. */ DK3_MAIN { LCL_JOB job; /* Cleanup job. */ void *vptr; /* Host to clean up. */ int exval = 1; /* Exit status code. */ $!trace-init lprngcl.deb $? "+ main" job.app = dk3app_open_command( argc, (dkChar const * const *)argv, lprngcl_nlmsg[0] ); if(job.app) { /* Initialize job structure. */ job.msg = dk3app_messages( job.app, lprngcl_nlmsg[1], (dkChar const **)lprngcl_msg ); job.nlmsg = lprngcl_nlmsg; job.cmd = 0; job.comp = 0; job.exval = 1; job.pc = NULL; job.tmpfn = NULL; job.sph = NULL; job.iph = NULL; job.havep = 0; /* Run print queue cleanup */ lprngcl_run(&job); /* Clean up job structure. */ if(job.sph) { if(job.iph) { dk3sto_it_reset(job.iph); while(NULL != (vptr = dk3sto_it_next(job.iph))) { lprngcl_delete_host((LCL_HOST *)vptr); } dk3sto_it_close(job.iph); } dk3sto_close(job.sph); } job.sph = NULL; if(job.tmpfn) { dk3sf_remove_file_app(job.tmpfn, NULL); dk3_release(job.tmpfn);; } exval = job.exval; dk3app_close(job.app); } else { fputs("lprngcl: ERROR: Not enough memory!\n", stderr); fflush(stderr); } $? "- main %d", exval $!trace-end fflush(stdout); exit(exval); return exval; }