%% options copyright owner = Dirk Krause copyright year = 2012-2014 license = bsd %% module #include "dk3all.h" #include "dkt.h" #include "dkwt.h" $!trace-include /** Buffer size to enumerate printers. */ #define DKWT_PRINTER_ENUM_BUFFER_SIZE 1024 /** Buffer size to enumerate print jobs. */ #define DKWT_PRINTER_ENUM_JOBS_SIZE 8192 /** Number of print jobs to show in one request. */ #define DKWT_PRINTER_NUMBER_OF_JOBS 64 /** Local print queue. */ typedef struct { dkChar *qn; /**< Queue name. */ } DKWT_CP_QUEUE; /** Print job. */ typedef struct { DWORD jobId; /**< Job ID. */ DWORD pos; /**< Position in queue. */ dkChar const *docName; /**< Print job name. */ } DKWT_CP_JOB; /** Compare two print queues by name. @param l Left object. @param r Right object. @param cr Comparison criteria (0=queue/queue, 1=queue/name) @return Comparison result. */ static int dkwt_cp_queue_comp(void const *l, void const *r, int cr) { DKWT_CP_QUEUE const *ql; DKWT_CP_QUEUE const *qr; int back = 0; if(l) { if(r) { ql = (DKWT_CP_QUEUE const *)l; switch(cr) { case 1: { if(ql->qn) { back = dk3str_casecmp(ql->qn, (dkChar const *)r); if(back < -1) back = -1; if(back > 1) back = 1; } else back = -1; } break; default: { qr = (DKWT_CP_QUEUE const *)r; if(ql->qn) { if(qr->qn) { back = dk3str_casecmp(ql->qn, qr->qn); if(back < -1) back = -1; if(back > 1) back = 1; } else back = 1; } else { if(qr->qn) back = -1; } } break; } } else back = 1; } else { if(r) back = -1; } return back; } /** Destroy queue structure, release memory. @param q Queue structure to destroy. */ static void dkwt_cp_queue_del(DKWT_CP_QUEUE *q) { if(q) { dk3_release(q->qn); dk3_delete(q); } } /** Create queue structure, allocate memory. @param app Application structure for diagnostics, may be NULL. @param n Queue name. @return Pointer to new structure on success, NULL on error. */ static DKWT_CP_QUEUE * dkwt_cp_queue_new(dk3_app_t *app, dkChar const *n) { DKWT_CP_QUEUE *back = NULL; if(n) { back = dk3_new_app(DKWT_CP_QUEUE,1,app); if(back) { back->qn = NULL; back->qn = dk3str_dup_app(n, app); if(!(back->qn)) { dkwt_cp_queue_del(back); back = NULL; } } } return back; } /** Destroy job structure, release memory. @param j Job structure to delete. */ static void dkwt_cp_job_del(DKWT_CP_JOB *j) { if(j) { dk3_release(j->docName); j->jobId = 0; j->pos = 0; dk3_delete(j); } } /** Create new job structure, allocate memory. @param app Application structure for diagnostics. @param jobid Job ID. @param pos Job position in the queue. @param dn Document name. @return Pointer to new structure on success, NULL on error. */ static DKWT_CP_JOB * dkwt_cp_job_new(dk3_app_t *app, DWORD jobid, DWORD pos, dkChar const *dn) { DKWT_CP_JOB *back = NULL; back = dk3_new_app(DKWT_CP_JOB,1,app); if(back) { back->docName = NULL; back->jobId = jobid; back->pos = pos; if(dn) { back->docName = dk3str_dup_app(dn, app); if(!(back->docName)) { dkwt_cp_job_del(back); back = NULL; } } } return back; } /** Compare 2 jobs to find which one to delete first. @param l Left job. @param r Right job. @param cr Comparison criteria (ignored). @return Comparison result, jobs with higher position are deleted first. */ static int dkwt_cp_job_comp(void const *l, void const *r, int cr) { DKWT_CP_JOB const *jl; DKWT_CP_JOB const *jr; int back = 0; if(l) { if(r) { jl = (DKWT_CP_JOB const *)l; jr = (DKWT_CP_JOB const *)r; if(jl->pos < jr->pos) { back = 1; } else { if(jl->pos > jr->pos) { back = -1; } } } else back = 1; } else { if(r) back = -1; } return back; } /** Remove all print jobs for one user from a single queue. @param app Application structure for diagnostics. @param msg Localized message texts. @param kwnl Keywords, not localized. @param userName User name. @param hostName Host name. @param queue Queue to modify. */ static void dkwt_cp_remove_all_jobs( dk3_app_t *app, dkChar const * const *msg, dkChar const * const *kwnl, dkChar const *userName, dkChar const *hostName, DKWT_CP_QUEUE *queue ) { char jeb[DKWT_PRINTER_ENUM_JOBS_SIZE]; /* Enum buffer. */ dkChar idbuf[64]; dk3_sto_t *s_j; /* Jobs storage. */ dk3_sto_it_t *i_j; /* Iterator for jobs storage. */ JOB_INFO_1 *ji1; /* Job information pointer. */ DKWT_CP_JOB *job; /* Current job. */ HANDLE hPrinter; /* Handle for printer. */ DWORD szjeb; /* Size of jeb buffer. */ DWORD bneed; /* Bytes needed. */ DWORD nj; /* Number of jobs found. */ DWORD i; /* Index of current job to process. */ int rmj; /* Flag: Remove this job. */ BOOL res; /* Operation result. */ $? "+ dkwt_cp_remove_all_jobs" s_j = NULL; i_j = NULL; s_j = dk3sto_open_app(app); if(s_j) { dk3sto_set_comp(s_j, dkwt_cp_job_comp, 0); i_j = dk3sto_it_open(s_j); } if((s_j) && (i_j)) { /* PROGRESS: Cleaning up local queue... */ dk3app_log_3(app, DK3_LL_PROGRESS, msg, 83, 84, queue->qn); #if DK3_CHAR_SIZE > 1 res = OpenPrinterW(queue->qn, &hPrinter, NULL); #else res = OpenPrinterA(queue->qn, &hPrinter, NULL); #endif if(res) { /* Enumerate print jobs, save jobs to delete. */ szjeb = sizeof(jeb); bneed = 0; nj = 0; #if DK3_CHAR_SIZE > 1 res = EnumJobsW( hPrinter, 0, DKWT_PRINTER_NUMBER_OF_JOBS, 1, (LPBYTE)jeb, szjeb, &bneed, &nj ); #else res = EnumJobsA( hPrinter, 0, DKWT_PRINTER_NUMBER_OF_JOBS, 1, (LPBYTE)jeb, szjeb, &bneed, &nj ); #endif if(res) { $? ". EnumJobs" if(nj > 0) { $? ". nj=%d", (int)nj ji1 = (JOB_INFO_1 *)jeb; for(i = 0; i < nj; i++) { rmj = 0; if(ji1[i].pUserName) { $? ". job user \"%s\" / user \"%s\"", ji1[i].pUserName, userName if(0 == dk3str_casecmp(ji1[i].pUserName, userName)) { $? ". equal" rmj = 1; if(hostName) { $? ". have host name \"%s\"", hostName rmj = 0; if(ji1[i].pMachineName) { $? ". job host \"%s\"", ji1[i].pMachineName if(0 == dk3str_casecmp(ji1[i].pMachineName, hostName)) { $? ". ok" rmj = 1; } else { if(dkT('\\') == (ji1[i].pMachineName)[0]) { if(dkT('\\') == (ji1[i].pMachineName)[1]) { if(0 == dk3str_casecmp(&((ji1[i].pMachineName)[2]), hostName)) { rmj = 1; } } } } } else { $? ". no job host name" rmj = 1; } } else { $? ". host name not found" } } else { $? ". other user name" } } if(rmj) { $? ". must remove job" job = dkwt_cp_job_new(app, ji1[i].JobId, ji1[i].Position, ji1[i].pDocument); if(job) { if(!dk3sto_add(s_j, (void *)job)) { $? "! memory" dkwt_cp_job_del(job); } else { } } else { $? "! memory" } } } } else { $? ". no jobs in queue" } } else { $? "! EnumJobs" } /* Delete saved print jobs if any. */ dk3sto_it_reset(i_j); while(NULL != (job = (DKWT_CP_JOB *)dk3sto_it_next(i_j))) { $? ". deleting print job %lu", (unsigned long)(job->jobId) #if VERSION_20140716 dk3sf_sprintf3(idbuf, dkT("%lu"), (unsigned long)(job->jobId)); #else if (!(dk3ma_um_to_string(idbuf, DK3_SIZEOF(idbuf,dkChar), (dk3_um_t)(job->jobId)))) { idbuf[0] = dkT('\0'); } #endif if(SetJob(hPrinter, job->jobId, 0, NULL, JOB_CONTROL_DELETE)) { /* PROGRESS: Print job deleted. */ $? ". deleted" dk3app_log_5( app, DK3_LL_PROGRESS, msg, 85, 86, 87, idbuf, ((job->docName) ? (job->docName) : msg[88]) ); } else { $? "! failed to delete" /* ERROR: Failed to delete print job! */ dk3app_log_5( app, DK3_LL_ERROR, msg, 89, 90, 91, idbuf, ((job->docName) ? (job->docName) : msg[88]) ); } } ClosePrinter(hPrinter); } else { $? "! failed to open printer" /* ERROR: Failed to open printer! */ dk3app_log_3(app, DK3_LL_ERROR, msg, 92, 93, queue->qn); } } else { $? "! arguments" } /* Release memory. */ if(s_j) { if(i_j) { dk3sto_it_reset(i_j); while(NULL !=(job = (DKWT_CP_JOB *)dk3sto_it_next(i_j))) { dkwt_cp_job_del(job); } dk3sto_it_close(i_j); } dk3sto_close(s_j); } $? "- dkwt_cp_remove_all_jobs" } void dkwt_clear_printers_local( dk3_app_t *app, dkChar const * const *msg, dkChar const * const *kwnl, dkChar const *userName ) { char peb[DKWT_PRINTER_ENUM_BUFFER_SIZE]; dkChar hn[256]; /* Host name. */ dk3_sto_t *s_q; /* Storage for print queues. */ dk3_sto_it_t *i_q; /* Iterator for print queues storage. */ DKWT_CP_QUEUE *q; /* Current queue to process. */ PRINTER_INFO_4 *pi4; /* Printer information array. */ DWORD szpeb; /* Size of peb buffer. */ DWORD bneed; /* Needed bytes. */ DWORD np; /* Number of printers found. */ DWORD i; /* Index of current printer. */ DWORD szhn; /* Size of host name. */ BOOL res; /* Result from printer enumeration. */ s_q = NULL; i_q = NULL; if(userName) { hn[0] = dkT('\0'); szhn = DK3_SIZEOF(hn,dkChar); #if DK3_CHAR_SIZE > 1 res = GetComputerNameExW(ComputerNameNetBIOS, hn, &szhn); #else res = GetComputerNameExA(ComputerNameNetBIOS, hn, &szhn); #endif if(res) { hn[(szhn < (DK3_SIZEOF(hn,dkChar)-1)) ? szhn : (DK3_SIZEOF(hn,dkChar)-1)] = dkT('\0'); } else { szhn = 0; } s_q = dk3sto_open_app(app); if(s_q) { dk3sto_set_comp(s_q, dkwt_cp_queue_comp, 0); i_q = dk3sto_it_open(s_q); } if((s_q) && (i_q)) { /* Enumerate printers, fill s_q */ szpeb = sizeof(peb); bneed = 0; np = 0; #if DK3_CHAR_SIZE > 1 res = EnumPrintersW( PRINTER_ENUM_LOCAL, NULL, 4, (LPBYTE)peb, szpeb, &bneed, &np ); #else res = EnumPrintersA( PRINTER_ENUM_LOCAL, NULL, 4, (LPBYTE)peb, szpeb, &bneed, &np ); #endif if(res) { pi4 = (PRINTER_INFO_4 *)peb; if(np > 0) { for(i = 0; i < np; i++) { if(pi4[i].pPrinterName) { q = dkwt_cp_queue_new(app, pi4[i].pPrinterName); if(q) { if(!dk3sto_add(s_q, (void *)q)) { dkwt_cp_queue_del(q); } } } } } } else { /* ERROR: Failed to enumerate local printers */ dk3app_log_1(app, DK3_LL_ERROR, msg, 94); } /* Handle all the queues in i_q. */ dk3sto_it_reset(i_q); while(NULL != (q = (DKWT_CP_QUEUE *)dk3sto_it_next(i_q))) { dkwt_cp_remove_all_jobs(app, msg, kwnl, userName, ((szhn > 0) ? hn : NULL), q); } } /* Release memory. */ if(s_q) { if(i_q) { dk3sto_it_reset(i_q); while(NULL != (q = (DKWT_CP_QUEUE *)dk3sto_it_next(i_q))) { dkwt_cp_queue_del(q); } dk3sto_it_close(i_q); } dk3sto_close(s_q); } } else { /* ERROR: Missing user name! */ dk3app_log_1(app, DK3_LL_ERROR, msg, 95); } } /** Send request to host. @param app Application structure. @param msg Localized message texts. @param kwnl Keywords, not localized. @param rq Request to send. @param hn Host name to send the request to. */ static void dkwt_cp_send_request( dk3_app_t *app, dkChar const * const *msg, dkChar const * const *kwnl, char const *rq, dkChar const *hn ) { char buf[512]; dkChar eb[64]; struct sockaddr_in in; struct hostent *he; unsigned long ip; unsigned long *ptr; unsigned long **pptr; size_t lgt; SOCKET sock; int res; int cc; int ie; ip = 0UL; dk3enc_ipaddr_to_ul_app(hn, &ip, app); if(ip) { ip = dk3enc_htonl(ip); } else { ie = dk3app_get_encoding(app); if(dk3str_to_c8u_app(buf, sizeof(buf), hn, ie, app)) { he = gethostbyname(buf); if(he) { if(he->h_addr_list) { pptr = (unsigned long **)he->h_addr_list; ptr = *pptr; if(ptr) { ip = *ptr; } } } } else { /* ERROR: Failed to convert host name to UTF-8! */ } } if(ip) { sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(INVALID_SOCKET != sock) { in.sin_family = AF_INET; in.sin_addr.s_addr = ip; in.sin_port = dk3enc_htons(515); ip = dk3enc_ntohl(ip); sprintf( buf, "%lu.%lu.%lu.%lu", ((ip >> 24) & 0x000000FFUL), ((ip >> 16) & 0x000000FFUL), ((ip >> 8) & 0x000000FFUL), ( ip & 0x000000FFUL) ); if(dk3str_c8_to_str_simple_app(eb, DK3_SIZEOF(eb,dkChar), buf, app)) { dk3app_log_3(app, DK3_LL_DEBUG, msg, 115, 116, eb); } res = connect( sock, (const struct sockaddr *)(&in), sizeof(struct sockaddr_in) ); if(0 == res) { lgt = strlen(rq); res = send(sock, rq, (int)lgt, 0); if(res == lgt) { shutdown(sock, SD_SEND); cc = 1; while(cc) { cc = 0; res = recv(sock, buf, sizeof(buf), 0); if(res > 0) { cc = 1; } } /* PROGRESS: Print queue cleaned up successfully. */ dk3app_log_1(app, DK3_LL_PROGRESS, msg, 96); } else { /* ERROR: Failed to send request! */ dk3app_log_1(app, DK3_LL_ERROR, msg, 97); #if VERSION_BEFORE_20140716 dk3sf_sprintf3(eb, dkT("%d"), WSAGetLastError()); dk3app_log_3(app, DK3_LL_ERROR, msg, 113, 114, eb); #else if (dk3ma_im_to_string(eb, DK3_SIZEOF(eb,dkChar), (dk3_im_t)WSAGetLastError())) { dk3app_log_3(app, DK3_LL_ERROR, msg, 113, 114, eb); } #endif } } else { /* ERROR: Failed to connect! */ dk3app_log_1(app, DK3_LL_ERROR, msg, 98); #if VERSION_BEFORE_20140716 dk3sf_sprintf3(eb, dkT("%d"), WSAGetLastError()); dk3app_log_3(app, DK3_LL_ERROR, msg, 113, 114, eb); #else if (dk3ma_im_to_string(eb, DK3_SIZEOF(eb,dkChar), (dk3_im_t)WSAGetLastError())) { dk3app_log_3(app, DK3_LL_ERROR, msg, 113, 114, eb); } #endif } closesocket(sock); } else { /* ERROR: Failed to obtain socket! */ dk3app_log_1(app, DK3_LL_ERROR, msg, 99); } } else { /* ERROR: Failed to find IP address for host! */ dk3app_log_1(app, DK3_LL_ERROR, msg, 100); } } /** Clean up one remote print queue. @param app Application structure. @param msg Localized message texts. @param kwnl Keywords, not localized. @param userName User name. @param queueName Print queue name. */ static void dkwt_clear_one_remote_queue( dk3_app_t *app, dkChar const * const *msg, dkChar const * const *kwnl, dkChar const *userName, dkChar const *queueName ) { dkChar qnb[256]; /* Copy of queue name. */ char qnc[256]; /* Queue name as char. */ char unc[256]; /* User name as char. */ char rqc[512]; /* Request. */ dkChar *p1; /* Host name part. */ size_t sz; int ie; /* Used encoding. */ ie = dk3app_get_encoding(app); if(dk3str_len(queueName) < DK3_SIZEOF(qnb,dkChar)) { dk3str_cpy_not_overlapped(qnb, queueName); p1 = dk3str_chr(qnb, dkT('@')); if(p1) { *(p1++) = dkT('\0'); p1 = dk3str_start(p1, NULL); if(p1) { dk3str_normalize(qnb, NULL, dkT(' ')); if(dk3str_len(qnb) > 0) { if(dk3str_to_c8u_app(qnc, sizeof(qnc), qnb, ie, app)) { if(dk3str_to_c8u_app(unc, sizeof(unc), userName, ie, app)) { sz = strlen(qnc) + strlen(unc) + 8; if(sz < sizeof(rqc)) { sprintf(rqc, "%c%s %s all\n", 0x05, qnc, unc); /* PROGRESS: Cleaning up remote print queue ... */ dk3app_log_3(app, DK3_LL_PROGRESS, msg, 101, 102, queueName); dkwt_cp_send_request(app, msg, kwnl, rqc, p1); } else { /* ERROR: Reqeust becomes too long! */ dk3app_log_1(app, DK3_LL_ERROR, msg, 103); } } else { /* ERROR: Failed to re-encode user name to UTF-8! */ } } else { /* ERROR: Failed to re-encode the queue name to UTF-8! */ } } else { /* ERROR: Empty queue name! */ dk3app_log_3(app, DK3_LL_ERROR, msg, 104, 105, queueName); } } else { /* ERROR: Empty host name! */ dk3app_log_3(app, DK3_LL_ERROR, msg, 106, 107, queueName); } } else { /* ERROR: Not a queue@host name! */ dk3app_log_3(app, DK3_LL_ERROR, msg, 108, 109, queueName); } } else { /* ERROR: Queue name too long! */ dk3app_log_3(app, DK3_LL_ERROR, msg, 110, 111, queueName); } } void dkwt_clear_printers_remote( dk3_app_t *app, dkChar const * const *msg, dkChar const * const *kwnl, dkChar const *userName, dk3_sto_it_t *iterator ) { WORD vrq; WSADATA wsa; dkChar const *qn; vrq = MAKEWORD(2,0); if(0 == WSAStartup(vrq, &wsa)) { dk3sto_it_reset(iterator); while(NULL != (qn = (dkChar const *)dk3sto_it_next(iterator))) { dkwt_clear_one_remote_queue(app, msg, kwnl, userName, qn); } (void)WSACleanup(); } else { /* ERROR: Failed to initialize Windows socket! */ dk3app_log_1(app, DK3_LL_ERROR, msg, 112); } } /* vim: set ai sw=2 : */