%% options copyright owner = Dirk Krause copyright year = 2013-2014 license = bsd %% module #include "dk3all.h" #include "dk3print.h" #include "dk3prcfg.h" #include "dkt-version.h" $!trace-include /** Job structure. */ typedef struct { dk3_app_t *app; /**< Application structure. */ dkChar const * const *msg; /**< Localized texts. */ dkChar const * const *nlmsg; /**< Not localized texts. */ dk3_option_set_t *opt; /**< Options. */ dk3_print_conf_t *pc; /**< Print configuration. */ int cmd; /**< Command to execute. */ int comp; /**< Only jobs from this machine. */ int exval; /**< Exit status code. */ } WPC_JOB; #if DK3_ON_WINDOWS /** Information required to delete a print job. */ typedef struct { DWORD position; /**< Position in queue. */ DWORD jobid; /**< Job ID. */ } wprclean_printjob_t; #endif #ifndef WPRCLEAN_QUEUE_JOB_LIST_SIZE /** Buffer size for job list. */ #define WPRCLEAN_QUEUE_JOB_LIST_SIZE 4096 #endif #ifndef WPRCLEAN_NUM_JOBS /** Number of jobs to retrieve at once. */ #define WPRCLEAN_NUM_JOBS 64 #endif /** Texts used by the program, not localized. */ static dkChar const * const wprclean_nlmsg[] = { $!string-table macro=dkT # # 0: Program group name # dkt-3 # # 1: String table file name. # wprclean.str # # 2: Help file name. # wprclean.txt $!end }; /** Texts used by the program, replaced by localized versions. */ static dkChar const * const wprclean_msg[] = { $!string-table macro=dkT,file=wprclean.str # # 0 1 # Print job deleted. # # 2 3 4 # Failed to delete print job , reason: . # # 5 6 # Failed to open printer " "! # # 7 8 # Printer " " not found! $!end }; /** Options for the wprclean program. */ static dk3_option_t const wprclean_options[] = { { dkT('h'), dkT("help"), 0 }, { dkT('v'), dkT("version"), 0 }, { dkT('L'), dkT("license-terms"), 0 }, { dkT('c'), dkT("computer"), 0} }; /** Number of options in wprclean_options. */ static size_t const wprclean_sz_options = sizeof(wprclean_options)/sizeof(dk3_option_t); /** Default help text shown if help file not found. */ static dkChar const * const wprclean_text_help[] = { $!text macro=dkT NAME wprclean - Windows print cleanup SYNOPSIS wprclean [-c] [] wprclean [-h] [-v] [-L] DESCRIPTION The wprclean program deletes all print jobs created by the current user. It is intended for use in computer labs or PC classrooms in schools and universities. OPTIONS -c --computer removes only print jobs created on the current computer. -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 can be used to conigure short alias names for Windows print queues. AUTHOR Dirk Krause COPYRIGHT AND LICENSE Run wprclean --license to see the license conditions. SEE ALSO http://dktools.sourceforge.net $!end }; /** License conditions. */ static dkChar const * const wprclean_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 wprclean_version[] = { DKT_VERSION }; #if DK3_ON_WINDOWS /** Delete print job information. @param jptr Print job to delete. */ static void wprclean_printjob_delete(wprclean_printjob_t *jptr) { dk3_release(jptr); } static wprclean_printjob_t * wprclean_printjob_new(DWORD pos, DWORD jid, dk3_app_t *app) { wprclean_printjob_t *back = NULL; back = dk3_new_app(wprclean_printjob_t,1,app); if(back) { back->position = pos; back->jobid = jid; } return back; } /** Compare print jobs by position, largest number first. @param l Left job. @param r Right job. @param cr Comparison criteria (ignored). @return Comparison result. */ static int wprclean_compare_printjobs(void const *l, void const *r, int cr) { wprclean_printjob_t const *pl; /* Left print job. */ wprclean_printjob_t const *pr; /* Right print job. */ int back = 0; if(l) { if(r) { pl = (wprclean_printjob_t const *)l; pr = (wprclean_printjob_t const *)r; if(pl->position > pr->position) { back = 1; } else { if(pl->position < pr->position) { back = -1; } } } else { back = 1; } } else { if(r) { back = -1; } } /* Invert. */ switch(back) { case 1: { back = -1; } break; case -1: { back = 1; } break; } return back; } #endif /* DK3_ON_WINDOWS */ /** Delete the current users jobs from the printer. @param job Job structure. @param pr Printer. */ static void wprclean_windows_printer(WPC_JOB *job, dk3_printer_t *pr) { #if DK3_ON_WINDOWS dkChar cn[DK3_MAX_PATH]; /* Current computer name. */ dkChar nb1[64]; /* Job ID as text. */ dkChar nb2[64]; /* Last error as text. */ char peb[WPRCLEAN_QUEUE_JOB_LIST_SIZE]; /* Buffer for enumeration. */ #if DK3_CHAR_SIZE > 1 JOB_INFO_1W *jobptr; /* Current print job. */ #else JOB_INFO_1A *jobptr; /* Current print job. */ #endif wprclean_printjob_t *pj; /* Current job to mark for deletion. */ dk3_sto_t *sJobs; /* Storage for jobs to delete. */ dk3_sto_it_t *iJobs; /* Iterator through storage. */ wprclean_printjob_t *jptr; /* Current job to delete. */ dkChar const *logname; /* Current users login name. */ HANDLE hPr; /* Handle for printer. */ DWORD szpeb; /* Size of peb buffer. */ DWORD cbNeeded; /* Number of bytes needed. */ DWORD cbReturned; /* Number of print jobs returned. */ DWORD startindex; /* Start index for enumeration. */ DWORD i; /* Index of current job. */ DWORD le; /* Last error code. */ BOOL bres; /* Operation result. */ BOOL cc; /* Flag: Can continue. */ BOOL mustsave; /* Flag: Must mark job for deletion. */ $? "+ wprclean_windows_printer" logname = dk3app_get_logname(job->app); if(logname) { $? ". logname" if(dk3sf_get_hostname_app(cn,DK3_MAX_PATH,job->app)) { $? ". hostname \"%ls\"", cn sJobs = dk3sto_open_app(job->app); if(sJobs) { $? ". sJobs" dk3sto_set_comp(sJobs, wprclean_compare_printjobs, 0); iJobs = dk3sto_it_open(sJobs); if(iJobs) { $? ". iJobs" hPr = INVALID_HANDLE_VALUE; #if DK3_CHAR_SIZE > 1 bres = OpenPrinterW((dkChar *)(pr->name), &hPr, NULL); #else bres = OpenPrinterA((dkChar *)(pr->name), &hPr, NULL); #endif if(bres) { $? ". OpenPrinter" dk3sf_initialize_stdout(); dk3sf_fputs(pr->name, stdout); dk3sf_fputc(dkT('\n'), stdout); fflush(stdout); /* Enumerate print jobs. */ cc = TRUE; startindex = 0; while(cc) { cbNeeded = 0; cbReturned = 0; szpeb = sizeof(peb); #if DK3_CHAR_SIZE > 1 bres = EnumJobsW( hPr, startindex, (DWORD)WPRCLEAN_NUM_JOBS, 1, (LPBYTE)peb, szpeb, &cbNeeded, &cbReturned ); #else bres = EnumJobsA( hPr, startindex, (DWORD)WPRCLEAN_NUM_JOBS, 1, (LPBYTE)peb, szpeb, &cbNeeded, &cbReturned ); #endif if(bres) { $? ". EnumJobs" if(cbReturned > 0) { $? ". cbReturned %lu", (unsigned long)cbReturned #if DK3_CHAR_SIZE > 1 jobptr = (JOB_INFO_1W *)peb; #else jobptr = (JOB_INFO_1A *)peb; #endif for(i = 0; i < cbReturned; i++) { mustsave = FALSE; if(jobptr->pUserName) { $? ". job username" if(dk3str_casecmp(logname, jobptr->pUserName) == 0) { $? ". names match" if(job->comp) { if(jobptr->pMachineName) { $? ". job machinename \"%ls\"", jobptr->pMachineName if(dk3str_casecmp(cn, jobptr->pMachineName) == 0) { $? ". names match" mustsave = TRUE; } else { if(dkT('\\') == (jobptr->pMachineName)[0]) { if(dkT('\\') == (jobptr->pMachineName)[1]) { if(dk3str_casecmp(cn, &((jobptr->pMachineName)[2])) == 0) { mustsave = TRUE; } } } } } else { $? ". no job machinename" mustsave = TRUE; } } else { mustsave = TRUE; } } } if(mustsave) { $? ". must save" pj = wprclean_printjob_new( jobptr->Position, jobptr->JobId, job->app ); if(pj) { $? ". pj" if(!dk3sto_add(sJobs, (void *)pj)) { wprclean_printjob_delete(pj); $? "! add" job->exval = 1; } } else { $? "! pj" job->exval = 1; } } jobptr++; } startindex += cbReturned; } else { $? "! cbReturned" cc = FALSE; } } else { $? "! EnumJobs" cc = FALSE; } } /* Remove saved print jobs from queue. */ dk3sto_it_reset(iJobs); while(NULL != (jptr = (wprclean_printjob_t *)dk3sto_it_next(iJobs))) { $? ". job to delete" #if VERSION_BEFORE_20140716 dk3sf_sprintf3(nb1,dkT("%lu"),(unsigned long)(jptr->jobid)); #else if (!(dk3ma_um_to_string(nb1,DK3_SIZEOF(nb1,dkChar),(dk3_um_t)(jptr->jobid)))) { nb1[0] = dkT('\0'); } #endif #if DK3_CHAR_SIZE > 1 bres = SetJobW(hPr, jptr->jobid, 0, NULL, JOB_CONTROL_DELETE); #else bres = SetJobA(hPr, jptr->jobid, 0, NULL, JOB_CONTROL_DELETE); #endif if(bres) { /* DELETED */ dk3sf_fputc(dkT('\t'), stdout); dk3sf_fputs((job->msg)[0], stdout); dk3sf_fputs(nb1, stdout); dk3sf_fputs((job->msg)[1], stdout); dk3sf_fputc(dkT('\n'), stdout); fflush(stdout); } else { /* FAILED */ le = GetLastError(); dk3sf_fputc(dkT('\t'), stdout); #if VERSION_BEFORE_20140716 dk3sf_sprintf3(nb2,dkT("%lu"),(unsigned long)le); #else if(!(dk3ma_um_to_string(nb2,DK3_SIZEOF(nb2,dkChar),(dk3_um_t)le))) { nb2[0] = dkT('\0'); } #endif dk3sf_fputs((job->msg)[2], stdout); dk3sf_fputs(nb1, stdout); dk3sf_fputs((job->msg)[3], stdout); dk3sf_fputs(nb2, stdout); dk3sf_fputs((job->msg)[4], stdout); dk3sf_fputc(dkT('\n'), stdout); fflush(stdout); job->exval = 1; } } ClosePrinter(hPr); } else { $? "! OpenPrinter" /* ERROR: Failed to open printer! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 5, 6, pr->name); job->exval = 1; } /* Release saved jobs. */ dk3sto_it_reset(iJobs); while(NULL != (jptr = (wprclean_printjob_t *)dk3sto_it_next(iJobs))) { wprclean_printjob_delete(jptr); } dk3sto_it_close(iJobs); } else { job->exval = 1; } dk3sto_close(sJobs); } else { job->exval = 1; } } else { /* ERROR: Failed to find host name */ job->exval = 1; } } else { /* ERROR: Failed to find log name */ job->exval = 1; } $? "- wprclean_windows_printer" #endif } /** Clean up all printers on the system. @param job Job structure. */ static void wprclean_cleanup_all_printers(WPC_JOB *job) { void *vptr; /* Current printer as returned from iterator. */ dk3_printer_t *pr; /* Current printer. */ dk3sto_it_reset((job->pc)->iPrinters); while(NULL != (vptr = dk3sto_it_next((job->pc)->iPrinters))) { pr = (dk3_printer_t *)vptr; if(DK3_PRINTER_TYPE_WINDOWS == pr->t_p) { wprclean_windows_printer(job, pr); } } } /** Clean up the named printers. @param job Job structure. @param numargs Number of command line arguments. */ static void wprclean_cleanup_named_printers(WPC_JOB *job, int numargs) { dkChar const *pn; /* Printer name. */ dk3_printer_t *pr; /* Printer. */ int i; /* Index of current printer. */ /* Pass 1: Local Windows printers. */ for(i = 0; i < numargs; i++) { pn = dk3opt_get_arg(job->opt, i); if(pn) { pr = dk3print_get_printer(job->pc, pn); if(pr) { if(DK3_PRINTER_TYPE_WINDOWS == pr->t_p) { wprclean_windows_printer(job, pr); } } else { job->exval = 1; /* ERROR: Printer not found! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 7, 8, pn); } } } } /** Do cleanup. @param job Job structure. */ static void wprclean_do_cleanup(WPC_JOB *job) { int numargs; /* Number of command line arguments. */ job->pc = dk3print_conf_open(job->app, 1); if(job->pc) { numargs = dk3opt_get_num_args(job->opt); job->exval = 0; if(0 == numargs) { wprclean_cleanup_all_printers(job); } else { wprclean_cleanup_named_printers(job, numargs); } dk3print_conf_close(job->pc); job->pc = NULL; } } /** Run with a configured job structure. @param job Job structure. */ static void wprclean_run(WPC_JOB *job) { job->opt = dk3opt_open_from_app( wprclean_options, wprclean_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) { wprclean_do_cleanup(job); } else { job->exval = 0; dk3sf_initialize_stdout(); if((job->cmd) & DK3_APP_CMD_VERSION) { dk3sf_fputs(wprclean_version, stdout); dk3sf_fputc(dkT('\n'), stdout); } if((job->cmd) & DK3_APP_CMD_LICENSE) { dk3sf_fputt(wprclean_text_license, stdout); } if((job->cmd) & DK3_APP_CMD_HELP) { dk3app_help(job->app, wprclean_nlmsg[2], wprclean_text_help); } } } dk3opt_close(job->opt); job->opt = NULL; } } /** 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 { WPC_JOB job; /* wprclean job. */ int exval = 1; /* Exit status code. */ $!trace-init wprclean.deb $? "+ main" job.app = dk3app_open_command( argc, (dkChar const * const *)argv, wprclean_nlmsg[0] ); if(job.app) { job.msg = dk3app_messages( job.app, wprclean_nlmsg[1], (dkChar const **)wprclean_msg ); job.nlmsg = wprclean_nlmsg; job.cmd = 0; job.comp = 0; job.exval = 1; job.pc = NULL; wprclean_run(&job); exval = job.exval; dk3app_close(job.app); } else { fputs("wprclean: ERROR: Not enough memory!\n", stderr); fflush(stderr); } $? "- main %d", exval $!trace-end fflush(stdout); exit(exval); return exval; }