/* 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 prqdctrl.c Handle control requests. This module handles control ... requests passed to prqd. */ /** Inside the prqdctrl module. */ #define PRQDCTRL_C 1 #include "prqd.h" $(trace-include) /** Control reqest types. */ static char *ctrl_commands[] = { "r$eset", "a$dd", "i$nfo", "d$atabase-cleanup", NULL }; /** Trailing strings in db entries of interest for info files. */ static char *trailing_strings_for_info[] = { "a", "p", NULL }; /** Leading strings in db entries of interest for db cleanup. */ static char *leading_strings_for_cleanup[] = { "a", "p", "j", NULL }; /** Argument names. */ static char *arg_types[] = { /* 0 */ "u$ser", /* 1 */ "c$lass", /* 2 */ "pa$ges", /* 3 */ "pr$inter", NULL }; /** Database entry type for printer. */ static char dbentry_type_p[] = { "p" }; /** A name matching all patterns. */ static char pat_ast[] = { "*" }; /** String comparison function for sorted storage. @param l Left side string. @param r Right side string. @param cr Comparison criteria (ignored). @return String comparison result. */ static int char_compare DK_P3(void *,l, void *,r, int,cr) { int back = 0; if(l) { if(r) { back = strcmp((char *)l, (char *)r); } else { back = 1; } } else { if(r) { back = -1; } } return back; } /** Check whether a name matches a pattern. @param pat The pattern. @param nam The name (* may be used to match all patterns). @return 1 on match, 0 on mismatch. */ static int pattern_matches DK_P2(char *,pat, char *,nam) { int back = 0; $? "+ pattern_matches \"%s\"=\"%s\"", TR_STR(pat), TR_STR(nam) if((pat) && (nam)) { back = 1; if(strcmp(pat, pat_ast) != 0) { if(strcmp(pat, nam) != 0) { back = 0; } } } $? "- pattern_matches %d", back return back; } /** Check if the user exists. @param n User name to check. @return 1 if user exists, 0 if user does not exist. */ static int have_user DK_P1(char *,n) { int back = 0; struct passwd *pw = NULL; pw = getpwnam(n); if(pw) { back = 1; } return back; } /** Database traversal function, used in cleanup request. @param pj Pointer to print job structure. @param k Key string. @param v Value string. @return 1 on success, 0 on error, -1 on fatal error (abort traversal). */ static int traverse_cleanup DK_P3(PJ *,pj, char *,k, char *,v) { int back = 1; char keybuffer[PRQD_DBENTRY_SIZE]; char *p1; char *p2; int must_remove; must_remove = 0; if(strlen(k) < sizeof(keybuffer)) { strcpy(keybuffer, k); p1 = strchr(keybuffer, ':'); if(p1) { *(p1++) = '\0'; switch(dkstr_array_index(leading_strings_for_cleanup, keybuffer, 0)) { case 0: case 1: { /* a, p */ p2 = strchr(p1, ':'); if(p2) { *(p2++) = '\0'; if(dksto_it_find_like((pj->prqdc)->cli, p1, 1)) { if(!have_user(p2)) { must_remove = 1; } } else { must_remove = 1; } } } break; case 2: { /* j */ if(!dksto_it_find_like((pj->prqdc)->pri, p1, 1)) { must_remove = 1; } } break; } } } else { /* Key too long */ } if(must_remove) { p1 = dkstr_dup(k); if(p1) { if(!dksto_add(pj->cr_st, (void *)p1)) { dk_delete(p1); back = 0; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42)); } } } return back; } /** Database traversal function, used in info request. @param pj Pointer to print job structure. @param k Key string. @param v Value string. @return 1 on success, 0 on error, -1 on fatal error (abort traversal). */ static int traverse_info DK_P3(PJ *,pj, char *,k, char *,v) { int back = 1; char *p1, *p2, *p3; $? "+ traverse_info %s \"%s\"=\"%s\"", TR_PTR(pj), TR_STR(k), TR_STR(v) p1 = dkstr_start(k, NULL); if(p1) { p2 = dkstr_chr(p1, ':'); if(p2) { *(p2++) = '\0'; switch(dkstr_array_index(trailing_strings_for_info, p1, 0)) { case 0: case 1: { p3 = dksto_it_find_like(pj->cr_sti, p2, 0); if(!p3) { p3 = dkstr_dup(p2); if(p3) { if(!dksto_add(pj->cr_st, (void *)p3)) { dk_delete(p3); prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42)); } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42)); } } } break; } } } $? "- traverse_info %d", back return back; } /** Database traversal function, used in reset request. @param pj Pointer to print job structure. @param k Key string. @param v Value string. @return 1 on success, 0 on error, -1 on fatal error (abort traversal). */ static int traverse_reset DK_P3(PJ *,pj, char *,k, char *,v) { int back = 1; char keybuffer[PRQD_DBENTRY_SIZE], *p1, *p2, *p3; $? "+ traverse_reset %s \"%s\"=\"%s\"", TR_PTR(pj), TR_STR(k), TR_STR(v) if(strlen(k) < sizeof(keybuffer)) { strcpy(keybuffer, k); p1 = dkstr_start(keybuffer, NULL); p2 = dkstr_chr(p1, ':'); if(p2) { *(p2++) = '\0'; p3 = dkstr_chr(p2, ':'); if(p3) { *(p3++) = '\0'; dkstr_chomp(p1, NULL); dkstr_chomp(p2, NULL); dkstr_chomp(p3, NULL); if(strcmp(dbentry_type_p, p1) == 0) { $? ". is pages entry" if(pattern_matches(pj->cr_cname, p2)) { $? ". class name matches" if(pattern_matches(pj->cr_uname, p3)) { $? ". user name matches" $? ". must delete entry \"%s\"", k p1 = dkstr_dup(k); if(p1) { if(!dksto_add(pj->cr_st, (void *)p1)) { $? "! failed to store" dk_delete(p1); back = -1; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42)); } } else { $? "! memory" back = -1; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42)); } } else { $? ". user name does not match" } } else { $? ". class name does not match" } } else { $? ". not a pages entry" } } else { $? ". no user name" } } else { $? ". no class name" } } else { back = 0; } $? "- traverse_reset %d", back return back; } /** Split arguments into substrings, set cr_xxx in pj. @param pj Pointer to print job structure. @param args Instruction arguments. */ static void split_args DK_P2(PJ *,pj, char *,args) { char *p1, *p2, *p3; int act; p1 = dkstr_start(args, NULL); while(p1) { p2 = dkstr_next(p1, NULL); p3 = dkstr_chr(p1, '='); if(p3) { *(p3++) = '\0'; dkstr_chomp(p1, NULL); p3 = dkstr_start(p3, NULL); if(p3) { act = dkstr_array_abbr(arg_types, p1, '$', 0); switch(act) { case 0: { pj->cr_uname = p3; } break; case 1: { pj->cr_cname = p3; } break; case 2: { long d; pj->cr_deltap = 0L; pj->cr_pages = p3; if(sscanf(p3, "%ld", &d) == 1) { pj->cr_deltap = d; } } break; case 3: { pj->cr_pname = p3; } break; } } } p1 = p2; } $? ". pj->cr_uname=\"%s\"", TR_STR(pj->cr_uname) $? ". pj->cr_cname=\"%s\"", TR_STR(pj->cr_cname) $? ". pj->cr_pname=\"%s\"", TR_STR(pj->cr_pname) $? ". pj->cr_pages=\"%s\"", TR_STR(pj->cr_pages) $? ". pj->cr_deltap=%ld", pj->cr_deltap } #if USE_KEYVALUE static void kv_delete DK_P1(KEYVALUE *,p) { char *x; if(p) { if(p->k) { x = p->k; dk_delete(x); } if(p->v) { x = p->v; dk_delete(x); } p->k = NULL; p->v = NULL; dk_delete(p); } } static KEYVALUE *kv_new DK_P2(char *,k, char *,v) { KEYVALUE *back = NULL; back = dk_new(KEYVALUE,1); if(back) { back->k = dkstr_dup(k); back->v = dkstr_dup(v); if(!((k) && (v))) { kv_delete(back); back = NULL; } } return back; } static int kv_compare DK_P3(void *,l, void *,r, int,cr) { int back = 0; KEYVALUE *pl, *pr; pl = (KEYVALUE *)l; pr = (KEYVALUE *)r; if(l) { if(r) { if(pl->k) { if(pr->k) { back = strcmp(pl->k, pr->k); } else { back = 1; } } else { if(pr->k) { back = -1; } } } else { back = 1; } } else { if(r) { back = -1; } } return back; } #endif /** Process a reset request. @param pj Pointer to print job structure. @param args Further arguments. @return 1 on success, 0 on error. */ static int reset_request DK_P2(PJ *,pj, char *,args) { int back = 0; char *delptr, *p1, *p2; $? "+ reset_request \"%s\"", args split_args(pj, args); pj->cr_st = NULL; pj->cr_sti = NULL; pj->cr_st = dksto_open(0); if(pj->cr_st) { dksto_set_comp(pj->cr_st, char_compare, 0); pj->cr_sti = dksto_it_open(pj->cr_st); if(pj->cr_sti) { prqdbe_traverse(pj, traverse_reset); dksto_it_reset(pj->cr_sti); while((delptr = (char *)dksto_it_next(pj->cr_sti)) != NULL) { $? ". delete dbentry \"%s\"", delptr prqdbe_delete(pj, delptr); } dksto_it_reset(pj->cr_sti); while((delptr = (char *)dksto_it_next(pj->cr_sti)) != NULL) { $? ". release entry \"%s\"", TR_STR(delptr) p1 = dkstr_chr(delptr, ':'); if(p1) { *(p1++) = '\0'; p2 = dkstr_chr(p1, ':'); if(p2) { *(p2++) = '\0'; prqd_write_userinfo_file(pj, p2, p1); } } dk_delete(delptr); } dksto_it_close(pj->cr_sti); pj->cr_sti = NULL; } else { pj->e1 = 1; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42)); } dksto_close(pj->cr_st); pj->cr_st = NULL; } else { pj->e1 = 1; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42)); } $? "- reset_request %d", back return back; } /** Add pages to a users print account. @param pj Pointer to print job structure. @param args Further arguments. @return 1 on success, 0 on error. */ static int add_request DK_P2(PJ *,pj, char *,args) { int back = 0; char keybuffer[PRQD_DBENTRY_SIZE], valbuffer[PRQD_DBENTRY_SIZE]; long uap = 0L; $? "+ add_request \"%s\"", args split_args(pj, args); if(pj->cr_uname) { if(pj->cr_cname) { if(strcmp(pj->cr_uname, pat_ast)) { if(strcmp(pj->cr_cname, pat_ast)) { if((3 + strlen(pj->cr_uname) + strlen(pj->cr_cname)) < sizeof(keybuffer)) { sprintf(keybuffer, "a:%s:%s", pj->cr_cname, pj->cr_uname); if(prqdbe_fetch(pj, keybuffer, valbuffer, sizeof(valbuffer))) { long l; if(sscanf(valbuffer, "%ld", &l) == 1) { uap = l; } } uap = uap + pj->cr_deltap; sprintf(valbuffer, "%ld", uap); if(prqdbe_store(pj, keybuffer, valbuffer)) { back = 1; } prqd_write_userinfo_file(pj, pj->cr_uname, pj->cr_cname); $? ". new dbentry \"%s\"=\"%s\"", keybuffer, valbuffer } else { $? "! name too long" prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(41), pj->cr_uname, pj->cr_cname); } } else { $? "! can not use *" prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(83)); } } else { $? "! can not use *" prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(83)); } } else { $? "! no class name" prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(82)); } } else { $? "! no user name" prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(81)); } $? "- add_request %d", back return back; } /** Process "control info ..." request. @param pj Pointer to print job structure. @param args Pointer to further arguments. @return 1 on success, 0 on error. */ static int info_request DK_P2(PJ *,pj, char *,args) { int back = 0; char *delptr, *p1, *p2; $? "+ info_request" split_args(pj, args); pj->cr_st = NULL; pj->cr_sti = NULL; pj->cr_st = dksto_open(0); if(pj->cr_st) { dksto_set_comp(pj->cr_st, char_compare, 0); pj->cr_sti = dksto_it_open(pj->cr_st); if(pj->cr_sti) { prqdbe_traverse(pj, traverse_info); dksto_it_reset(pj->cr_sti); while((delptr = (char *)dksto_it_next(pj->cr_sti)) != NULL) { $? ". release entry \"%s\"", TR_STR(delptr) p1 = dkstr_start(delptr, NULL); if(p1) { p2 = dkstr_chr(p1, ':'); if(p2) { *(p2++) = '\0'; prqd_write_userinfo_file(pj, p2, p1); } } dk_delete(delptr); } dksto_it_close(pj->cr_sti); } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42)); } dksto_close(pj->cr_st); pj->cr_st = NULL; } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42)); } $? "- info_request" return back; } /** Clean up the database. Remove entries if: - the printer class does not longer exist or - the user does not longer exist. @param pj Pointer to print job structure. @return 1 on success, 0 on error. */ static int cleanup_database DK_P1(PJ *,pj) { int back = 0; char valbuffer[PRQD_DBENTRY_SIZE]; char *delptr = NULL; pj->cr_st = NULL; pj->cr_sti = NULL; pj->cr_st = dksto_open(0); if(pj->cr_st) { dksto_set_comp(pj->cr_st, char_compare, 0); pj->cr_sti = dksto_it_open(pj->cr_st); if(pj->cr_sti) { back = prqdbe_traverse(pj, traverse_cleanup); if(back == -1) { back = 0; } dksto_it_reset(pj->cr_sti); while((delptr = (char *)dksto_it_next(pj->cr_sti)) != NULL) { DK_MEMRES(valbuffer,sizeof(valbuffer)); if(prqdbe_fetch(pj, delptr, valbuffer, sizeof(valbuffer))) { /* LOG: Remove entry ...=... */ prqdlog(pj, PRQD_PRIO_INFO, prqd_get_kw(85), delptr, valbuffer); } else { /* FAILED TO FETCH ENTRY */ back = 0; } if(!prqdbe_delete(pj, delptr)) { /* FAILED TO DELETE DB entry */ back = 0; } dk_delete(delptr); } dksto_it_close(pj->cr_sti); } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42)); } dksto_close(pj->cr_st); } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42)); } return back; } /** Perform a control request. @param pj Pointer to print job structure. @param args Pointer to further arguments. @return 1 on success, 0 on error. */ int prqdctrl_request DK_P2(PJ *,pj, char *,args) { int back = 0; int act = 0; char *pcmd, *parg; $? "+ prqdctrl_request" pj->cr_uname = NULL; pj->cr_cname = NULL; pj->cr_pages = NULL; pj->cr_pname = NULL; pj->cr_deltap = 0L; pcmd = dkstr_start(args, NULL); if(pcmd) { parg = dkstr_next(pcmd, NULL); dkstr_chomp(pcmd, NULL); act = dkstr_array_abbr(ctrl_commands, pcmd, '$', 0); switch(act) { case 0: { $? ". reset" back = reset_request(pj, parg); } break; case 1: { $? ". add" back = add_request(pj, parg); } break; case 2: { back = info_request(pj, parg); } break; case 3: { $? ". database-cleanup" back = cleanup_database(pj); } break; default: { $? "! wrong request" prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(80), pcmd); } break; } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(79)); } $? "- prqdctrl_request %d", back return back; }