diff options
Diffstat (limited to 'source3/printing')
-rw-r--r-- | source3/printing/.cvsignore | 0 | ||||
-rw-r--r-- | source3/printing/load.c | 73 | ||||
-rw-r--r-- | source3/printing/lpq_parse.c | 1101 | ||||
-rw-r--r-- | source3/printing/nt_printing.c | 4050 | ||||
-rw-r--r-- | source3/printing/pcap.c | 127 | ||||
-rw-r--r-- | source3/printing/print_cups.c | 1189 | ||||
-rw-r--r-- | source3/printing/print_generic.c | 247 | ||||
-rw-r--r-- | source3/printing/print_svid.c | 145 | ||||
-rw-r--r-- | source3/printing/printfsp.c | 104 | ||||
-rw-r--r-- | source3/printing/printing.c | 2229 |
10 files changed, 8388 insertions, 877 deletions
diff --git a/source3/printing/.cvsignore b/source3/printing/.cvsignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/source3/printing/.cvsignore diff --git a/source3/printing/load.c b/source3/printing/load.c new file mode 100644 index 0000000000..ed967fb0a7 --- /dev/null +++ b/source3/printing/load.c @@ -0,0 +1,73 @@ +/* + Unix SMB/CIFS implementation. + load printer lists + Copyright (C) Andrew Tridgell 1992-2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +/*************************************************************************** +auto-load printer services +***************************************************************************/ +void add_all_printers(void) +{ + int printers = lp_servicenumber(PRINTERS_NAME); + + if (printers < 0) return; + + pcap_printer_fn(lp_add_one_printer); +} + +/*************************************************************************** +auto-load some homes and printer services +***************************************************************************/ +static void add_auto_printers(void) +{ + char *p; + int printers; + char *str = strdup(lp_auto_services()); + + if (!str) return; + + printers = lp_servicenumber(PRINTERS_NAME); + + if (printers < 0) { + SAFE_FREE(str); + return; + } + + for (p=strtok(str,LIST_SEP);p;p=strtok(NULL,LIST_SEP)) { + if (lp_servicenumber(p) >= 0) continue; + + if (pcap_printername_ok(p,NULL)) { + lp_add_printer(p,printers); + } + } + + SAFE_FREE(str); +} + +/*************************************************************************** +load automatic printer services +***************************************************************************/ +void load_printers(void) +{ + add_auto_printers(); + if (lp_load_printers()) + add_all_printers(); +} diff --git a/source3/printing/lpq_parse.c b/source3/printing/lpq_parse.c new file mode 100644 index 0000000000..13b87045cd --- /dev/null +++ b/source3/printing/lpq_parse.c @@ -0,0 +1,1101 @@ +/* + Unix SMB/CIFS implementation. + lpq parsing routines + Copyright (C) Andrew Tridgell 2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +static char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"}; + + +/******************************************************************* +process time fields +********************************************************************/ +static time_t EntryTime(fstring tok[], int ptr, int count, int minimum) +{ + time_t jobtime,jobtime1; + + jobtime = time(NULL); /* default case: take current time */ + if (count >= minimum) { + struct tm *t; + int i, day, hour, min, sec; + char *c; + + for (i=0; i<13; i++) if (!strncmp(tok[ptr], Months[i],3)) break; /* Find month */ + if (i<12) { + t = localtime(&jobtime); + day = atoi(tok[ptr+1]); + c=(char *)(tok[ptr+2]); + *(c+2)=0; + hour = atoi(c); + *(c+5)=0; + min = atoi(c+3); + if(*(c+6) != 0)sec = atoi(c+6); + else sec=0; + + if ((t->tm_mon < i)|| + ((t->tm_mon == i)&& + ((t->tm_mday < day)|| + ((t->tm_mday == day)&& + (t->tm_hour*60+t->tm_min < hour*60+min))))) + t->tm_year--; /* last year's print job */ + + t->tm_mon = i; + t->tm_mday = day; + t->tm_hour = hour; + t->tm_min = min; + t->tm_sec = sec; + jobtime1 = mktime(t); + if (jobtime1 != (time_t)-1) + jobtime = jobtime1; + } + } + return jobtime; +} + + +/**************************************************************************** +parse a lpq line + +here is an example of lpq output under bsd + +Warning: no daemon present +Rank Owner Job Files Total Size +1st tridge 148 README 8096 bytes + +here is an example of lpq output under osf/1 + +Warning: no daemon present +Rank Pri Owner Job Files Total Size +1st 0 tridge 148 README 8096 bytes + + +<allan@umich.edu> June 30, 1998. +Modified to handle file names with spaces, like the parse_lpq_lprng code +further below. +****************************************************************************/ +static BOOL parse_lpq_bsd(char *line,print_queue_struct *buf,BOOL first) +{ +#ifdef OSF1 +#define RANKTOK 0 +#define PRIOTOK 1 +#define USERTOK 2 +#define JOBTOK 3 +#define FILETOK 4 +#define TOTALTOK (count - 2) +#define NTOK 6 +#define MAXTOK 128 +#else /* OSF1 */ +#define RANKTOK 0 +#define USERTOK 1 +#define JOBTOK 2 +#define FILETOK 3 +#define TOTALTOK (count - 2) +#define NTOK 5 +#define MAXTOK 128 +#endif /* OSF1 */ + + char *tok[MAXTOK]; + int count = 0; + pstring line2; + + pstrcpy(line2,line); + +#ifdef OSF1 + { + size_t length; + length = strlen(line2); + if (line2[length-3] == ':') + return(False); + } +#endif /* OSF1 */ + + /* FIXME: Use next_token rather than strtok! */ + tok[0] = strtok(line2," \t"); + count++; + + while (((tok[count] = strtok(NULL," \t")) != NULL) && (count < MAXTOK)) { + count++; + } + + /* we must get at least NTOK tokens */ + if (count < NTOK) + return(False); + + /* the Job and Total columns must be integer */ + if (!isdigit((int)*tok[JOBTOK]) || !isdigit((int)*tok[TOTALTOK])) return(False); + + buf->job = atoi(tok[JOBTOK]); + buf->size = atoi(tok[TOTALTOK]); + buf->status = strequal(tok[RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED; + buf->time = time(NULL); + StrnCpy(buf->fs_user,tok[USERTOK],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[FILETOK],sizeof(buf->fs_file)-1); + + if ((FILETOK + 1) != TOTALTOK) { + int bufsize; + int i; + + bufsize = sizeof(buf->fs_file) - strlen(buf->fs_file) - 1; + + for (i = (FILETOK + 1); i < TOTALTOK; i++) { + safe_strcat(buf->fs_file," ",bufsize); + safe_strcat(buf->fs_file,tok[i],bufsize - 1); + bufsize = sizeof(buf->fs_file) - strlen(buf->fs_file) - 1; + if (bufsize <= 0) { + break; + } + } + /* Ensure null termination. */ + buf->fs_file[sizeof(buf->fs_file)-1] = '\0'; + } + +#ifdef PRIOTOK + buf->priority = atoi(tok[PRIOTOK]); +#else + buf->priority = 1; +#endif + return(True); +} + +/* +<magnus@hum.auc.dk> +LPRng_time modifies the current date by inserting the hour and minute from +the lpq output. The lpq time looks like "23:15:07" + +<allan@umich.edu> June 30, 1998. +Modified to work with the re-written parse_lpq_lprng routine. + +<J.P.M.v.Itegem@tue.nl> Dec 17,1999 +Modified to work with lprng 3.16 +With lprng 3.16 The lpq time looks like + "23:15:07" + "23:15:07.100" + "1999-12-16-23:15:07" + "1999-12-16-23:15:07.100" + +*/ +static time_t LPRng_time(char *time_string) +{ + time_t jobtime; + struct tm t; + + jobtime = time(NULL); /* default case: take current time */ + t = *localtime(&jobtime); + + if ( atoi(time_string) < 24 ){ + t.tm_hour = atoi(time_string); + t.tm_min = atoi(time_string+3); + t.tm_sec = atoi(time_string+6); + } else { + t.tm_year = atoi(time_string)-1900; + t.tm_mon = atoi(time_string+5)-1; + t.tm_mday = atoi(time_string+8); + t.tm_hour = atoi(time_string+11); + t.tm_min = atoi(time_string+14); + t.tm_sec = atoi(time_string+17); + } + jobtime = mktime(&t); + + return jobtime; +} + + +/**************************************************************************** + parse a lprng lpq line + <allan@umich.edu> June 30, 1998. + Re-wrote this to handle file names with spaces, multiple file names on one + lpq line, etc; +****************************************************************************/ +static BOOL parse_lpq_lprng(char *line,print_queue_struct *buf,BOOL first) +{ +#define LPRNG_RANKTOK 0 +#define LPRNG_USERTOK 1 +#define LPRNG_PRIOTOK 2 +#define LPRNG_JOBTOK 3 +#define LPRNG_FILETOK 4 +#define LPRNG_TOTALTOK (num_tok - 2) +#define LPRNG_TIMETOK (num_tok - 1) +#define LPRNG_NTOK 7 +#define LPRNG_MAXTOK 128 /* PFMA just to keep us from running away. */ + + fstring tokarr[LPRNG_MAXTOK]; + char *cptr; + int num_tok = 0; + pstring line2; + + pstrcpy(line2,line); + cptr = line2; + while(next_token( &cptr, tokarr[num_tok], " \t", sizeof(fstring)) && (num_tok < LPRNG_MAXTOK)) + num_tok++; + + /* We must get at least LPRNG_NTOK tokens. */ + if (num_tok < LPRNG_NTOK) { + return(False); + } + + if (!isdigit((int)*tokarr[LPRNG_JOBTOK]) || !isdigit((int)*tokarr[LPRNG_TOTALTOK])) { + return(False); + } + + buf->job = atoi(tokarr[LPRNG_JOBTOK]); + buf->size = atoi(tokarr[LPRNG_TOTALTOK]); + + if (strequal(tokarr[LPRNG_RANKTOK],"active")) { + buf->status = LPQ_PRINTING; + } else if (isdigit((int)*tokarr[LPRNG_RANKTOK])) { + buf->status = LPQ_QUEUED; + } else { + buf->status = LPQ_PAUSED; + } + + buf->priority = *tokarr[LPRNG_PRIOTOK] -'A'; + + buf->time = LPRng_time(tokarr[LPRNG_TIMETOK]); + + StrnCpy(buf->fs_user,tokarr[LPRNG_USERTOK],sizeof(buf->fs_user)-1); + + /* The '@hostname' prevents windows from displaying the printing icon + * for the current user on the taskbar. Plop in a null. + */ + + if ((cptr = strchr_m(buf->fs_user,'@')) != NULL) { + *cptr = '\0'; + } + + StrnCpy(buf->fs_file,tokarr[LPRNG_FILETOK],sizeof(buf->fs_file)-1); + + if ((LPRNG_FILETOK + 1) != LPRNG_TOTALTOK) { + int bufsize; + int i; + + bufsize = sizeof(buf->fs_file) - strlen(buf->fs_file) - 1; + + for (i = (LPRNG_FILETOK + 1); i < LPRNG_TOTALTOK; i++) { + safe_strcat(buf->fs_file," ",bufsize); + safe_strcat(buf->fs_file,tokarr[i],bufsize - 1); + bufsize = sizeof(buf->fs_file) - strlen(buf->fs_file) - 1; + if (bufsize <= 0) { + break; + } + } + /* Ensure null termination. */ + buf->fs_file[sizeof(buf->fs_file)-1] = '\0'; + } + + return(True); +} + + + +/******************************************************************* +parse lpq on an aix system + +Queue Dev Status Job Files User PP % Blks Cp Rnk +------- ----- --------- --- ------------------ ---------- ---- -- ----- --- --- +lazer lazer READY +lazer lazer RUNNING 537 6297doc.A kvintus@IE 0 10 2445 1 1 + QUEUED 538 C.ps root@IEDVB 124 1 2 + QUEUED 539 E.ps root@IEDVB 28 1 3 + QUEUED 540 L.ps root@IEDVB 172 1 4 + QUEUED 541 P.ps root@IEDVB 22 1 5 +********************************************************************/ +static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first) +{ + fstring tok[11]; + int count=0; + + /* handle the case of "(standard input)" as a filename */ + pstring_sub(line,"standard input","STDIN"); + all_string_sub(line,"(","\"",0); + all_string_sub(line,")","\"",0); + + for (count=0; + count<10 && + next_token(&line,tok[count],NULL, sizeof(tok[count])); + count++) ; + + /* we must get 6 tokens */ + if (count < 10) + { + if ((count == 7) && ((strcmp(tok[0],"QUEUED") == 0) || (strcmp(tok[0],"HELD") == 0))) + { + /* the 2nd and 5th columns must be integer */ + if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[4])) return(False); + buf->size = atoi(tok[4]) * 1024; + /* if the fname contains a space then use STDIN */ + if (strchr_m(tok[2],' ')) + fstrcpy(tok[2],"STDIN"); + + /* only take the last part of the filename */ + { + fstring tmp; + char *p = strrchr_m(tok[2],'/'); + if (p) + { + fstrcpy(tmp,p+1); + fstrcpy(tok[2],tmp); + } + } + + + buf->job = atoi(tok[1]); + buf->status = strequal(tok[0],"HELD")?LPQ_PAUSED:LPQ_QUEUED; + buf->priority = 0; + buf->time = time(NULL); + StrnCpy(buf->fs_user,tok[3],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[2],sizeof(buf->fs_file)-1); + } + else + { + DEBUG(6,("parse_lpq_aix count=%d\n", count)); + return(False); + } + } + else + { + /* the 4th and 9th columns must be integer */ + if (!isdigit((int)*tok[3]) || !isdigit((int)*tok[8])) return(False); + buf->size = atoi(tok[8]) * 1024; + /* if the fname contains a space then use STDIN */ + if (strchr_m(tok[4],' ')) + fstrcpy(tok[4],"STDIN"); + + /* only take the last part of the filename */ + { + fstring tmp; + char *p = strrchr_m(tok[4],'/'); + if (p) + { + fstrcpy(tmp,p+1); + fstrcpy(tok[4],tmp); + } + } + + + buf->job = atoi(tok[3]); + buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED; + buf->priority = 0; + buf->time = time(NULL); + StrnCpy(buf->fs_user,tok[5],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[4],sizeof(buf->fs_file)-1); + } + + + return(True); +} + + +/**************************************************************************** +parse a lpq line +here is an example of lpq output under hpux; note there's no space after -o ! +$> lpstat -oljplus +ljplus-2153 user priority 0 Jan 19 08:14 on ljplus + util.c 125697 bytes + server.c 110712 bytes +ljplus-2154 user priority 0 Jan 19 08:14 from client + (standard input) 7551 bytes +****************************************************************************/ +static BOOL parse_lpq_hpux(char * line, print_queue_struct *buf, BOOL first) +{ + /* must read two lines to process, therefore keep some values static */ + static BOOL header_line_ok=False, base_prio_reset=False; + static fstring jobuser; + static int jobid; + static int jobprio; + static time_t jobtime; + static int jobstat=LPQ_QUEUED; + /* to store minimum priority to print, lpstat command should be invoked + with -p option first, to work */ + static int base_prio; + + int count; + char htab = '\011'; + fstring tok[12]; + + /* If a line begins with a horizontal TAB, it is a subline type */ + + if (line[0] == htab) { /* subline */ + /* check if it contains the base priority */ + if (!strncmp(line,"\tfence priority : ",18)) { + base_prio=atoi(&line[18]); + DEBUG(4, ("fence priority set at %d\n", base_prio)); + } + if (!header_line_ok) return (False); /* incorrect header line */ + /* handle the case of "(standard input)" as a filename */ + pstring_sub(line,"standard input","STDIN"); + all_string_sub(line,"(","\"",0); + all_string_sub(line,")","\"",0); + + for (count=0; count<2 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ; + /* we must get 2 tokens */ + if (count < 2) return(False); + + /* the 2nd column must be integer */ + if (!isdigit((int)*tok[1])) return(False); + + /* if the fname contains a space then use STDIN */ + if (strchr_m(tok[0],' ')) + fstrcpy(tok[0],"STDIN"); + + buf->size = atoi(tok[1]); + StrnCpy(buf->fs_file,tok[0],sizeof(buf->fs_file)-1); + + /* fill things from header line */ + buf->time = jobtime; + buf->job = jobid; + buf->status = jobstat; + buf->priority = jobprio; + StrnCpy(buf->fs_user,jobuser,sizeof(buf->fs_user)-1); + + return(True); + } + else { /* header line */ + header_line_ok=False; /* reset it */ + if (first) { + if (!base_prio_reset) { + base_prio=0; /* reset it */ + base_prio_reset=True; + } + } + else if (base_prio) base_prio_reset=False; + + /* handle the dash in the job id */ + pstring_sub(line,"-"," "); + + for (count=0; count<12 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ; + + /* we must get 8 tokens */ + if (count < 8) return(False); + + /* first token must be printer name (cannot check ?) */ + /* the 2nd, 5th & 7th column must be integer */ + if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[4]) || !isdigit((int)*tok[6])) return(False); + jobid = atoi(tok[1]); + StrnCpy(jobuser,tok[2],sizeof(buf->fs_user)-1); + jobprio = atoi(tok[4]); + + /* process time */ + jobtime=EntryTime(tok, 5, count, 8); + if (jobprio < base_prio) { + jobstat = LPQ_PAUSED; + DEBUG (4, ("job %d is paused: prio %d < %d; jobstat=%d\n", jobid, jobprio, base_prio, jobstat)); + } + else { + jobstat = LPQ_QUEUED; + if ((count >8) && (((strequal(tok[8],"on")) || + ((strequal(tok[8],"from")) && + ((count > 10)&&(strequal(tok[10],"on"))))))) + jobstat = LPQ_PRINTING; + } + + header_line_ok=True; /* information is correct */ + return(False); /* need subline info to include into queuelist */ + } +} + + +/**************************************************************************** +parse a lpstat line + +here is an example of "lpstat -o dcslw" output under sysv + +dcslw-896 tridge 4712 Dec 20 10:30:30 on dcslw +dcslw-897 tridge 4712 Dec 20 10:30:30 being held + +****************************************************************************/ +static BOOL parse_lpq_sysv(char *line,print_queue_struct *buf,BOOL first) +{ + fstring tok[9]; + int count=0; + char *p; + + /* + * Handle the dash in the job id, but make sure that we skip over + * the printer name in case we have a dash in that. + * Patch from Dom.Mitchell@palmerharvey.co.uk. + */ + + /* + * Move to the first space. + */ + for (p = line ; !isspace(*p) && *p; p++) + ; + + /* + * Back up until the last '-' character or + * start of line. + */ + for (; (p >= line) && (*p != '-'); p--) + ; + + if((p >= line) && (*p == '-')) + *p = ' '; + + for (count=0; count<9 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) + ; + + /* we must get 7 tokens */ + if (count < 7) + return(False); + + /* the 2nd and 4th, 6th columns must be integer */ + if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[3])) + return(False); + if (!isdigit((int)*tok[5])) + return(False); + + /* if the user contains a ! then trim the first part of it */ + if ((p=strchr_m(tok[2],'!'))) { + fstring tmp; + fstrcpy(tmp,p+1); + fstrcpy(tok[2],tmp); + } + + buf->job = atoi(tok[1]); + buf->size = atoi(tok[3]); + if (count > 7 && strequal(tok[7],"on")) + buf->status = LPQ_PRINTING; + else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held")) + buf->status = LPQ_PAUSED; + else + buf->status = LPQ_QUEUED; + buf->priority = 0; + buf->time = EntryTime(tok, 4, count, 7); + StrnCpy(buf->fs_user,tok[2],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[2],sizeof(buf->fs_file)-1); + return(True); +} + +/**************************************************************************** +parse a lpq line + +here is an example of lpq output under qnx +Spooler: /qnx/spooler, on node 1 +Printer: txt (ready) +0000: root [job #1 ] active 1146 bytes /etc/profile +0001: root [job #2 ] ready 2378 bytes /etc/install +0002: root [job #3 ] ready 1146 bytes -- standard input -- +****************************************************************************/ +static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first) +{ + fstring tok[7]; + int count=0; + + DEBUG(4,("antes [%s]\n", line)); + + /* handle the case of "-- standard input --" as a filename */ + pstring_sub(line,"standard input","STDIN"); + DEBUG(4,("despues [%s]\n", line)); + all_string_sub(line,"-- ","\"",0); + all_string_sub(line," --","\"",0); + DEBUG(4,("despues 1 [%s]\n", line)); + + pstring_sub(line,"[job #",""); + pstring_sub(line,"]",""); + DEBUG(4,("despues 2 [%s]\n", line)); + + + + for (count=0; count<7 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ; + + /* we must get 7 tokens */ + if (count < 7) + return(False); + + /* the 3rd and 5th columns must be integer */ + if (!isdigit((int)*tok[2]) || !isdigit((int)*tok[4])) return(False); + + /* only take the last part of the filename */ + { + fstring tmp; + char *p = strrchr_m(tok[6],'/'); + if (p) + { + fstrcpy(tmp,p+1); + fstrcpy(tok[6],tmp); + } + } + + + buf->job = atoi(tok[2]); + buf->size = atoi(tok[4]); + buf->status = strequal(tok[3],"active")?LPQ_PRINTING:LPQ_QUEUED; + buf->priority = 0; + buf->time = time(NULL); + StrnCpy(buf->fs_user,tok[1],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[6],sizeof(buf->fs_file)-1); + return(True); +} + + +/**************************************************************************** + parse a lpq line for the plp printing system + Bertrand Wallrich <Bertrand.Wallrich@loria.fr> + +redone by tridge. Here is a sample queue: + +Local Printer 'lp2' (fjall): + Printing (started at Jun 15 13:33:58, attempt 1). + Rank Owner Pr Opt Job Host Files Size Date + active tridge X - 6 fjall /etc/hosts 739 Jun 15 13:33 + 3rd tridge X - 7 fjall /etc/hosts 739 Jun 15 13:33 + +****************************************************************************/ +static BOOL parse_lpq_plp(char *line,print_queue_struct *buf,BOOL first) +{ + fstring tok[11]; + int count=0; + + /* handle the case of "(standard input)" as a filename */ + pstring_sub(line,"stdin","STDIN"); + all_string_sub(line,"(","\"",0); + all_string_sub(line,")","\"",0); + + for (count=0; count<11 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ; + + /* we must get 11 tokens */ + if (count < 11) + return(False); + + /* the first must be "active" or begin with an integer */ + if (strcmp(tok[0],"active") && !isdigit((int)tok[0][0])) + return(False); + + /* the 5th and 8th must be integer */ + if (!isdigit((int)*tok[4]) || !isdigit((int)*tok[7])) + return(False); + + /* if the fname contains a space then use STDIN */ + if (strchr_m(tok[6],' ')) + fstrcpy(tok[6],"STDIN"); + + /* only take the last part of the filename */ + { + fstring tmp; + char *p = strrchr_m(tok[6],'/'); + if (p) + { + fstrcpy(tmp,p+1); + fstrcpy(tok[6],tmp); + } + } + + + buf->job = atoi(tok[4]); + + buf->size = atoi(tok[7]); + if (strchr_m(tok[7],'K')) + buf->size *= 1024; + if (strchr_m(tok[7],'M')) + buf->size *= 1024*1024; + + buf->status = strequal(tok[0],"active")?LPQ_PRINTING:LPQ_QUEUED; + buf->priority = 0; + buf->time = time(NULL); + StrnCpy(buf->fs_user,tok[1],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[6],sizeof(buf->fs_file)-1); + return(True); +} + +/**************************************************************************** +parse a qstat line + +here is an example of "qstat -l -d qms" output under softq + +Queue qms: 2 jobs; daemon active (313); enabled; accepting; + job-ID submission-time pri size owner title +205980: H 98/03/09 13:04:05 0 15733 stephenf chap1.ps +206086:> 98/03/12 17:24:40 0 659 chris - +206087: 98/03/12 17:24:45 0 4876 chris - +Total: 21268 bytes in queue + + +****************************************************************************/ +static BOOL parse_lpq_softq(char *line,print_queue_struct *buf,BOOL first) +{ + fstring tok[10]; + int count=0; + + /* mung all the ":"s to spaces*/ + pstring_sub(line,":"," "); + + for (count=0; count<10 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ; + + /* we must get 9 tokens */ + if (count < 9) + return(False); + + /* the 1st and 7th columns must be integer */ + if (!isdigit((int)*tok[0]) || !isdigit((int)*tok[6])) return(False); + /* if the 2nd column is either '>' or 'H' then the 7th and 8th must be + * integer, else it's the 6th and 7th that must be + */ + if (*tok[1] == 'H' || *tok[1] == '>') + { + if (!isdigit((int)*tok[7])) + return(False); + buf->status = *tok[1] == '>' ? LPQ_PRINTING : LPQ_PAUSED; + count = 1; + } + else + { + if (!isdigit((int)*tok[5])) + return(False); + buf->status = LPQ_QUEUED; + count = 0; + } + + + buf->job = atoi(tok[0]); + buf->size = atoi(tok[count+6]); + buf->priority = atoi(tok[count+5]); + StrnCpy(buf->fs_user,tok[count+7],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[count+8],sizeof(buf->fs_file)-1); + buf->time = time(NULL); /* default case: take current time */ + { + time_t jobtime; + struct tm *t; + + t = localtime(&buf->time); + t->tm_mday = atoi(tok[count+2]+6); + t->tm_mon = atoi(tok[count+2]+3); + switch (*tok[count+2]) + { + case 7: case 8: case 9: t->tm_year = atoi(tok[count+2]); break; + default: t->tm_year = atoi(tok[count+2]); break; + } + + t->tm_hour = atoi(tok[count+3]); + t->tm_min = atoi(tok[count+4]); + t->tm_sec = atoi(tok[count+5]); + jobtime = mktime(t); + if (jobtime != (time_t)-1) + buf->time = jobtime; + } + + return(True); +} + +/******************************************************************* +parse lpq on an NT system + + Windows 2000 LPD Server + Printer \\10.0.0.2\NP17PCL (Paused) + +Owner Status Jobname Job-Id Size Pages Priority +---------------------------------------------------------------------------- +root (9.99. Printing /usr/lib/rhs/rhs-pr 3 625 0 1 +root (9.99. Paused /usr/lib/rhs/rhs-pr 4 625 0 1 +jmcd Waiting Re: Samba Open Sour 26 32476 1 1 + +********************************************************************/ +static BOOL parse_lpq_nt(char *line,print_queue_struct *buf,BOOL first) +{ +#define LPRNT_OWNSIZ 11 +#define LPRNT_STATSIZ 9 +#define LPRNT_JOBSIZ 19 +#define LPRNT_IDSIZ 6 +#define LPRNT_SIZSIZ 9 + typedef struct + { + char owner[LPRNT_OWNSIZ]; + char space1; + char status[LPRNT_STATSIZ]; + char space2; + char jobname[LPRNT_JOBSIZ]; + char space3; + char jobid[LPRNT_IDSIZ]; + char space4; + char size[LPRNT_SIZSIZ]; + char terminator; + } nt_lpq_line; + + nt_lpq_line parse_line; +#define LPRNT_PRINTING "Printing" +#define LPRNT_WAITING "Waiting" +#define LPRNT_PAUSED "Paused" + + memset(&parse_line, '\0', sizeof(parse_line)); + strncpy((char *) &parse_line, line, sizeof(parse_line) -1); + + if (strlen((char *) &parse_line) != sizeof(parse_line) - 1) + return(False); + + /* Just want the first word in the owner field - the username */ + if (strchr_m(parse_line.owner, ' ')) + *(strchr_m(parse_line.owner, ' ')) = '\0'; + else + parse_line.space1 = '\0'; + + /* Make sure we have an owner */ + if (!strlen(parse_line.owner)) + return(False); + + /* Make sure the status is valid */ + parse_line.space2 = '\0'; + trim_string(parse_line.status, NULL, " "); + if (!strequal(parse_line.status, LPRNT_PRINTING) && + !strequal(parse_line.status, LPRNT_PAUSED) && + !strequal(parse_line.status, LPRNT_WAITING)) + return(False); + + parse_line.space3 = '\0'; + trim_string(parse_line.jobname, NULL, " "); + + buf->job = atoi(parse_line.jobid); + buf->priority = 0; + buf->size = atoi(parse_line.size); + buf->time = time(NULL); + StrnCpy(buf->fs_user, parse_line.owner, sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file, parse_line.jobname, sizeof(buf->fs_file)-1); + if (strequal(parse_line.status, LPRNT_PRINTING)) + buf->status = LPQ_PRINTING; + else if (strequal(parse_line.status, LPRNT_PAUSED)) + buf->status = LPQ_PAUSED; + else + buf->status = LPQ_QUEUED; + + return(True); +} + +/******************************************************************* +parse lpq on an OS2 system + +JobID File Name Rank Size Status Comment +----- --------------- ------ -------- ------------ ------------ + 3 Control 1 68 Queued root@psflinu + 4 /etc/motd 2 11666 Queued root@psflinu + +********************************************************************/ +static BOOL parse_lpq_os2(char *line,print_queue_struct *buf,BOOL first) +{ +#define LPROS2_IDSIZ 5 +#define LPROS2_JOBSIZ 15 +#define LPROS2_SIZSIZ 8 +#define LPROS2_STATSIZ 12 +#define LPROS2_OWNSIZ 12 + typedef struct + { + char jobid[LPROS2_IDSIZ]; + char space1[2]; + char jobname[LPROS2_JOBSIZ]; + char space2[14]; + char size[LPROS2_SIZSIZ]; + char space3[4]; + char status[LPROS2_STATSIZ]; + char space4[4]; + char owner[LPROS2_OWNSIZ]; + char terminator; + } os2_lpq_line; + + os2_lpq_line parse_line; +#define LPROS2_PRINTING "Printing" +#define LPROS2_WAITING "Queued" +#define LPROS2_PAUSED "Paused" + + memset(&parse_line, '\0', sizeof(parse_line)); + strncpy((char *) &parse_line, line, sizeof(parse_line) -1); + + if (strlen((char *) &parse_line) != sizeof(parse_line) - 1) + return(False); + + /* Get the jobid */ + buf->job = atoi(parse_line.jobid); + + /* Get the job name */ + parse_line.space2[0] = '\0'; + trim_string(parse_line.jobname, NULL, " "); + StrnCpy(buf->fs_file, parse_line.jobname, sizeof(buf->fs_file)-1); + + buf->priority = 0; + buf->size = atoi(parse_line.size); + buf->time = time(NULL); + + /* Make sure we have an owner */ + if (!strlen(parse_line.owner)) + return(False); + + /* Make sure we have a valid status */ + parse_line.space4[0] = '\0'; + trim_string(parse_line.status, NULL, " "); + if (!strequal(parse_line.status, LPROS2_PRINTING) && + !strequal(parse_line.status, LPROS2_PAUSED) && + !strequal(parse_line.status, LPROS2_WAITING)) + return(False); + + StrnCpy(buf->fs_user, parse_line.owner, sizeof(buf->fs_user)-1); + if (strequal(parse_line.status, LPROS2_PRINTING)) + buf->status = LPQ_PRINTING; + else if (strequal(parse_line.status, LPROS2_PAUSED)) + buf->status = LPQ_PAUSED; + else + buf->status = LPQ_QUEUED; + + return(True); +} + +static char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL }; +static char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL }; +static char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL }; + +#ifdef DEVELOPER + +/**************************************************************************** +parse a vlp line +****************************************************************************/ +static BOOL parse_lpq_vlp(char *line,print_queue_struct *buf,BOOL first) +{ + int toknum = 0; + fstring tok; + + /* First line is printer status */ + + if (!isdigit(line[0])) return False; + + /* Parse a print job entry */ + + while(next_token(&line, tok, NULL, sizeof(fstring))) { + switch (toknum) { + case 0: + buf->job = atoi(tok); + break; + case 1: + buf->size = atoi(tok); + break; + case 2: + buf->status = atoi(tok); + break; + case 3: + buf->time = atoi(tok); + break; + case 4: + fstrcpy(buf->fs_user, tok); + break; + case 5: + fstrcpy(buf->fs_file, tok); + break; + } + toknum++; + } + + return True; +} + +#endif /* DEVELOPER */ + +/**************************************************************************** +parse a lpq line. Choose printing style +****************************************************************************/ +BOOL parse_lpq_entry(int snum,char *line, + print_queue_struct *buf, + print_status_struct *status,BOOL first) +{ + BOOL ret; + + switch (lp_printing(snum)) + { + case PRINT_SYSV: + ret = parse_lpq_sysv(line,buf,first); + break; + case PRINT_AIX: + ret = parse_lpq_aix(line,buf,first); + break; + case PRINT_HPUX: + ret = parse_lpq_hpux(line,buf,first); + break; + case PRINT_QNX: + ret = parse_lpq_qnx(line,buf,first); + break; + case PRINT_LPRNG: + ret = parse_lpq_lprng(line,buf,first); + break; + case PRINT_PLP: + ret = parse_lpq_plp(line,buf,first); + break; + case PRINT_SOFTQ: + ret = parse_lpq_softq(line,buf,first); + break; + case PRINT_LPRNT: + ret = parse_lpq_nt(line,buf,first); + break; + case PRINT_LPROS2: + ret = parse_lpq_os2(line,buf,first); + break; +#ifdef DEVELOPER + case PRINT_VLP: + case PRINT_TEST: + ret = parse_lpq_vlp(line,buf,first); + break; +#endif /* DEVELOPER */ + default: + ret = parse_lpq_bsd(line,buf,first); + break; + } + + /* We don't want the newline in the status message. */ + { + char *p = strchr_m(line,'\n'); + if (p) *p = 0; + } + + /* in the LPRNG case, we skip lines starting by a space.*/ + if (line && !ret && (lp_printing(snum)==PRINT_LPRNG) ) + { + if (line[0]==' ') + return ret; + } + + + if (status && !ret) + { + /* a few simple checks to see if the line might be a + printer status line: + handle them so that most severe condition is shown */ + int i; + strlower(line); + + switch (status->status) { + case LPSTAT_OK: + for (i=0; stat0_strings[i]; i++) + if (strstr(line,stat0_strings[i])) { + StrnCpy(status->message,line,sizeof(status->message)-1); + status->status=LPSTAT_OK; + return ret; + } + case LPSTAT_STOPPED: + for (i=0; stat1_strings[i]; i++) + if (strstr(line,stat1_strings[i])) { + StrnCpy(status->message,line,sizeof(status->message)-1); + status->status=LPSTAT_STOPPED; + return ret; + } + case LPSTAT_ERROR: + for (i=0; stat2_strings[i]; i++) + if (strstr(line,stat2_strings[i])) { + StrnCpy(status->message,line,sizeof(status->message)-1); + status->status=LPSTAT_ERROR; + return ret; + } + break; + } + } + + return(ret); +} diff --git a/source3/printing/nt_printing.c b/source3/printing/nt_printing.c new file mode 100644 index 0000000000..907c3fd8e6 --- /dev/null +++ b/source3/printing/nt_printing.c @@ -0,0 +1,4050 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Andrew Tridgell 1992-2000, + * Copyright (C) Jean François Micouleau 1998-2000. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +extern DOM_SID global_sid_World; + +static TDB_CONTEXT *tdb_forms; /* used for forms files */ +static TDB_CONTEXT *tdb_drivers; /* used for driver files */ +static TDB_CONTEXT *tdb_printers; /* used for printers files */ + +#define FORMS_PREFIX "FORMS/" +#define DRIVERS_PREFIX "DRIVERS/" +#define DRIVER_INIT_PREFIX "DRIVER_INIT/" +#define PRINTERS_PREFIX "PRINTERS/" +#define SECDESC_PREFIX "SECDESC/" +#define GLOBAL_C_SETPRINTER "GLOBALS/c_setprinter" + +#define NTDRIVERS_DATABASE_VERSION_1 1 +#define NTDRIVERS_DATABASE_VERSION_2 2 +#define NTDRIVERS_DATABASE_VERSION_3 3 /* little endian version of v2 */ + +#define NTDRIVERS_DATABASE_VERSION NTDRIVERS_DATABASE_VERSION_3 + +/* Map generic permissions to printer object specific permissions */ + +GENERIC_MAPPING printer_generic_mapping = { + PRINTER_READ, + PRINTER_WRITE, + PRINTER_EXECUTE, + PRINTER_ALL_ACCESS +}; + +STANDARD_MAPPING printer_std_mapping = { + PRINTER_READ, + PRINTER_WRITE, + PRINTER_EXECUTE, + PRINTER_ALL_ACCESS +}; + +/* We need one default form to support our default printer. Msoft adds the +forms it wants and in the ORDER it wants them (note: DEVMODE papersize is an +array index). Letter is always first, so (for the current code) additions +always put things in the correct order. */ +static nt_forms_struct default_forms[] = { + {"Letter",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368}, + {"Letter Small",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368}, + {"Tabloid",0x1,0x44368,0x696b8,0x0,0x0,0x44368,0x696b8}, + {"Ledger",0x1,0x696b8,0x44368,0x0,0x0,0x696b8,0x44368}, + {"Legal",0x1,0x34b5c,0x56d10,0x0,0x0,0x34b5c,0x56d10}, + {"Statement",0x1,0x221b4,0x34b5c,0x0,0x0,0x221b4,0x34b5c}, + {"Executive",0x1,0x2cf56,0x411cc,0x0,0x0,0x2cf56,0x411cc}, + {"A3",0x1,0x48828,0x668a0,0x0,0x0,0x48828,0x668a0}, + {"A4",0x1,0x33450,0x48828,0x0,0x0,0x33450,0x48828}, + {"A4 Small",0x1,0x33450,0x48828,0x0,0x0,0x33450,0x48828}, + {"A5",0x1,0x24220,0x33450,0x0,0x0,0x24220,0x33450}, + {"B4 (JIS)",0x1,0x3ebe8,0x58de0,0x0,0x0,0x3ebe8,0x58de0}, + {"B5 (JIS)",0x1,0x2c6f0,0x3ebe8,0x0,0x0,0x2c6f0,0x3ebe8}, + {"Folio",0x1,0x34b5c,0x509d8,0x0,0x0,0x34b5c,0x509d8}, + {"Quarto",0x1,0x347d8,0x43238,0x0,0x0,0x347d8,0x43238}, + {"10x14",0x1,0x3e030,0x56d10,0x0,0x0,0x3e030,0x56d10}, + {"11x17",0x1,0x44368,0x696b8,0x0,0x0,0x44368,0x696b8}, + {"Note",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368}, + {"Envelope #9",0x1,0x18079,0x37091,0x0,0x0,0x18079,0x37091}, + {"Envelope #10",0x1,0x19947,0x3ae94,0x0,0x0,0x19947,0x3ae94}, + {"Envelope #11",0x1,0x1be7c,0x40565,0x0,0x0,0x1be7c,0x40565}, + {"Envelope #12",0x1,0x1d74a,0x44368,0x0,0x0,0x1d74a,0x44368}, + {"Envelope #14",0x1,0x1f018,0x47504,0x0,0x0,0x1f018,0x47504}, + {"C size sheet",0x1,0x696b8,0x886d0,0x0,0x0,0x696b8,0x886d0}, + {"D size sheet",0x1,0x886d0,0xd2d70,0x0,0x0,0x886d0,0xd2d70}, + {"E size sheet",0x1,0xd2d70,0x110da0,0x0,0x0,0xd2d70,0x110da0}, + {"Envelope DL",0x1,0x1adb0,0x35b60,0x0,0x0,0x1adb0,0x35b60}, + {"Envelope C5",0x1,0x278d0,0x37e88,0x0,0x0,0x278d0,0x37e88}, + {"Envelope C3",0x1,0x4f1a0,0x6fd10,0x0,0x0,0x4f1a0,0x6fd10}, + {"Envelope C4",0x1,0x37e88,0x4f1a0,0x0,0x0,0x37e88,0x4f1a0}, + {"Envelope C6",0x1,0x1bd50,0x278d0,0x0,0x0,0x1bd50,0x278d0}, + {"Envelope C65",0x1,0x1bd50,0x37e88,0x0,0x0,0x1bd50,0x37e88}, + {"Envelope B4",0x1,0x3d090,0x562e8,0x0,0x0,0x3d090,0x562e8}, + {"Envelope B5",0x1,0x2af80,0x3d090,0x0,0x0,0x2af80,0x3d090}, + {"Envelope B6",0x1,0x2af80,0x1e848,0x0,0x0,0x2af80,0x1e848}, + {"Envelope",0x1,0x1adb0,0x38270,0x0,0x0,0x1adb0,0x38270}, + {"Envelope Monarch",0x1,0x18079,0x2e824,0x0,0x0,0x18079,0x2e824}, + {"6 3/4 Envelope",0x1,0x167ab,0x284ec,0x0,0x0,0x167ab,0x284ec}, + {"US Std Fanfold",0x1,0x5c3e1,0x44368,0x0,0x0,0x5c3e1,0x44368}, + {"German Std Fanfold",0x1,0x34b5c,0x4a6a0,0x0,0x0,0x34b5c,0x4a6a0}, + {"German Legal Fanfold",0x1,0x34b5c,0x509d8,0x0,0x0,0x34b5c,0x509d8}, + {"B4 (ISO)",0x1,0x3d090,0x562e8,0x0,0x0,0x3d090,0x562e8}, + {"Japanese Postcard",0x1,0x186a0,0x24220,0x0,0x0,0x186a0,0x24220}, + {"9x11",0x1,0x37cf8,0x44368,0x0,0x0,0x37cf8,0x44368}, + {"10x11",0x1,0x3e030,0x44368,0x0,0x0,0x3e030,0x44368}, + {"15x11",0x1,0x5d048,0x44368,0x0,0x0,0x5d048,0x44368}, + {"Envelope Invite",0x1,0x35b60,0x35b60,0x0,0x0,0x35b60,0x35b60}, + {"Reserved48",0x1,0x1,0x1,0x0,0x0,0x1,0x1}, + {"Reserved49",0x1,0x1,0x1,0x0,0x0,0x1,0x1}, + {"Letter Extra",0x1,0x3ae94,0x4a6a0,0x0,0x0,0x3ae94,0x4a6a0}, + {"Legal Extra",0x1,0x3ae94,0x5d048,0x0,0x0,0x3ae94,0x5d048}, + {"Tabloid Extra",0x1,0x4a6a0,0x6f9f0,0x0,0x0,0x4a6a0,0x6f9f0}, + {"A4 Extra",0x1,0x397c2,0x4eb16,0x0,0x0,0x397c2,0x4eb16}, + {"Letter Transverse",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368}, + {"A4 Transverse",0x1,0x33450,0x48828,0x0,0x0,0x33450,0x48828}, + {"Letter Extra Transverse",0x1,0x3ae94,0x4a6a0,0x0,0x0,0x3ae94,0x4a6a0}, + {"Super A",0x1,0x376b8,0x56ea0,0x0,0x0,0x376b8,0x56ea0}, + {"Super B",0x1,0x4a768,0x76e58,0x0,0x0,0x4a768,0x76e58}, + {"Letter Plus",0x1,0x34b5c,0x4eb16,0x0,0x0,0x34b5c,0x4eb16}, + {"A4 Plus",0x1,0x33450,0x50910,0x0,0x0,0x33450,0x50910}, + {"A5 Transverse",0x1,0x24220,0x33450,0x0,0x0,0x24220,0x33450}, + {"B5 (JIS) Transverse",0x1,0x2c6f0,0x3ebe8,0x0,0x0,0x2c6f0,0x3ebe8}, + {"A3 Extra",0x1,0x4e9d0,0x6ca48,0x0,0x0,0x4e9d0,0x6ca48}, + {"A5 Extra",0x1,0x2a7b0,0x395f8,0x0,0x0,0x2a7b0,0x395f8}, + {"B5 (ISO) Extra",0x1,0x31128,0x43620,0x0,0x0,0x31128,0x43620}, + {"A2",0x1,0x668a0,0x91050,0x0,0x0,0x668a0,0x91050}, + {"A3 Transverse",0x1,0x48828,0x668a0,0x0,0x0,0x48828,0x668a0}, + {"A3 Extra Transverse",0x1,0x4e9d0,0x6ca48,0x0,0x0,0x4e9d0,0x6ca48}, + {"Japanese Double Postcard",0x1,0x30d40,0x24220,0x0,0x0,0x30d40,0x24220}, + {"A6",0x1,0x19a28,0x24220,0x0,0x0,0x19a28,0x24220}, + {"Japanese Envelope Kaku #2",0x1,0x3a980,0x510e0,0x0,0x0,0x3a980,0x510e0}, + {"Japanese Envelope Kaku #3",0x1,0x34bc0,0x43a08,0x0,0x0,0x34bc0,0x43a08}, + {"Japanese Envelope Chou #3",0x1,0x1d4c0,0x395f8,0x0,0x0,0x1d4c0,0x395f8}, + {"Japanese Envelope Chou #4",0x1,0x15f90,0x320c8,0x0,0x0,0x15f90,0x320c8}, + {"Letter Rotated",0x1,0x44368,0x34b5c,0x0,0x0,0x44368,0x34b5c}, + {"A3 Rotated",0x1,0x668a0,0x48828,0x0,0x0,0x668a0,0x48828}, + {"A4 Rotated",0x1,0x48828,0x33450,0x0,0x0,0x48828,0x33450}, + {"A5 Rotated",0x1,0x33450,0x24220,0x0,0x0,0x33450,0x24220}, + {"B4 (JIS) Rotated",0x1,0x58de0,0x3ebe8,0x0,0x0,0x58de0,0x3ebe8}, + {"B5 (JIS) Rotated",0x1,0x3ebe8,0x2c6f0,0x0,0x0,0x3ebe8,0x2c6f0}, + {"Japanese Postcard Rotated",0x1,0x24220,0x186a0,0x0,0x0,0x24220,0x186a0}, + {"Double Japan Postcard Rotated",0x1,0x24220,0x30d40,0x0,0x0,0x24220,0x30d40}, + {"A6 Rotated",0x1,0x24220,0x19a28,0x0,0x0,0x24220,0x19a28}, + {"Japan Envelope Kaku #2 Rotated",0x1,0x510e0,0x3a980,0x0,0x0,0x510e0,0x3a980}, + {"Japan Envelope Kaku #3 Rotated",0x1,0x43a08,0x34bc0,0x0,0x0,0x43a08, 0x34bc0}, + {"Japan Envelope Chou #3 Rotated",0x1,0x395f8,0x1d4c0,0x0,0x0,0x395f8,0x1d4c0}, + {"Japan Envelope Chou #4 Rotated",0x1,0x320c8,0x15f90,0x0,0x0,0x320c8,0x15f90}, + {"B6 (JIS)",0x1,0x1f400,0x2c6f0,0x0,0x0,0x1f400,0x2c6f0}, + {"B6 (JIS) Rotated",0x1,0x2c6f0,0x1f400,0x0,0x0,0x2c6f0,0x1f400}, + {"12x11",0x1,0x4a724,0x443e1,0x0,0x0,0x4a724,0x443e1}, + {"Japan Envelope You #4",0x1,0x19a28,0x395f8,0x0,0x0,0x19a28,0x395f8}, + {"Japan Envelope You #4 Rotated",0x1,0x395f8,0x19a28,0x0,0x0,0x395f8,0x19a28}, + {"PRC 16K",0x1,0x2de60,0x3f7a0,0x0,0x0,0x2de60,0x3f7a0}, + {"PRC 32K",0x1,0x1fbd0,0x2cec0,0x0,0x0,0x1fbd0,0x2cec0}, + {"PRC 32K(Big)",0x1,0x222e0,0x318f8,0x0,0x0,0x222e0,0x318f8}, + {"PRC Envelope #1",0x1,0x18e70,0x28488,0x0,0x0,0x18e70,0x28488}, + {"PRC Envelope #2",0x1,0x18e70,0x2af80,0x0,0x0,0x18e70,0x2af80}, + {"PRC Envelope #3",0x1,0x1e848,0x2af80,0x0,0x0,0x1e848,0x2af80}, + {"PRC Envelope #4",0x1,0x1adb0,0x32c80,0x0,0x0,0x1adb0,0x32c80}, + {"PRC Envelope #5",0x1,0x1adb0,0x35b60,0x0,0x0,0x1adb0,0x35b60}, + {"PRC Envelope #6",0x1,0x1d4c0,0x38270,0x0,0x0,0x1d4c0,0x38270}, + {"PRC Envelope #7",0x1,0x27100,0x38270,0x0,0x0,0x27100,0x38270}, + {"PRC Envelope #8",0x1,0x1d4c0,0x4b708,0x0,0x0,0x1d4c0,0x4b708}, + {"PRC Envelope #9",0x1,0x37e88,0x4f1a0,0x0,0x0,0x37e88,0x4f1a0}, + {"PRC Envelope #10",0x1,0x4f1a0,0x6fd10,0x0,0x0,0x4f1a0,0x6fd10}, + {"PRC 16K Rotated",0x1,0x3f7a0,0x2de60,0x0,0x0,0x3f7a0,0x2de60}, + {"PRC 32K Rotated",0x1,0x2cec0,0x1fbd0,0x0,0x0,0x2cec0,0x1fbd0}, + {"PRC 32K(Big) Rotated",0x1,0x318f8,0x222e0,0x0,0x0,0x318f8,0x222e0}, + {"PRC Envelope #1 Rotated",0x1,0x28488,0x18e70,0x0,0x0,0x28488,0x18e70}, + {"PRC Envelope #2 Rotated",0x1,0x2af80,0x18e70,0x0,0x0,0x2af80,0x18e70}, + {"PRC Envelope #3 Rotated",0x1,0x2af80,0x1e848,0x0,0x0,0x2af80,0x1e848}, + {"PRC Envelope #4 Rotated",0x1,0x32c80,0x1adb0,0x0,0x0,0x32c80,0x1adb0}, + {"PRC Envelope #5 Rotated",0x1,0x35b60,0x1adb0,0x0,0x0,0x35b60,0x1adb0}, + {"PRC Envelope #6 Rotated",0x1,0x38270,0x1d4c0,0x0,0x0,0x38270,0x1d4c0}, + {"PRC Envelope #7 Rotated",0x1,0x38270,0x27100,0x0,0x0,0x38270,0x27100}, + {"PRC Envelope #8 Rotated",0x1,0x4b708,0x1d4c0,0x0,0x0,0x4b708,0x1d4c0}, + {"PRC Envelope #9 Rotated",0x1,0x4f1a0,0x37e88,0x0,0x0,0x4f1a0,0x37e88}, + {"PRC Envelope #10 Rotated",0x1,0x6fd10,0x4f1a0,0x0,0x0,0x6fd10,0x4f1a0} +}; + +static BOOL upgrade_to_version_3(void) +{ + TDB_DATA kbuf, newkey, dbuf; + + DEBUG(0,("upgrade_to_version_3: upgrading print tdb's to version 3\n")); + + for (kbuf = tdb_firstkey(tdb_drivers); kbuf.dptr; + newkey = tdb_nextkey(tdb_drivers, kbuf), safe_free(kbuf.dptr), kbuf=newkey) { + + dbuf = tdb_fetch(tdb_drivers, kbuf); + + if (strncmp(kbuf.dptr, FORMS_PREFIX, strlen(FORMS_PREFIX)) == 0) { + DEBUG(0,("upgrade_to_version_3:moving form\n")); + if (tdb_store(tdb_forms, kbuf, dbuf, TDB_REPLACE) != 0) { + DEBUG(0,("upgrade_to_version_3: failed to move form. Error (%s).\n", tdb_errorstr(tdb_forms))); + return False; + } + if (tdb_delete(tdb_drivers, kbuf) != 0) { + DEBUG(0,("upgrade_to_version_3: failed to delete form. Error (%s)\n", tdb_errorstr(tdb_drivers))); + return False; + } + } + + if (strncmp(kbuf.dptr, PRINTERS_PREFIX, strlen(PRINTERS_PREFIX)) == 0) { + DEBUG(0,("upgrade_to_version_3:moving printer\n")); + if (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) != 0) { + DEBUG(0,("upgrade_to_version_3: failed to move printer. Error (%s)\n", tdb_errorstr(tdb_printers))); + return False; + } + if (tdb_delete(tdb_drivers, kbuf) != 0) { + DEBUG(0,("upgrade_to_version_3: failed to delete printer. Error (%s)\n", tdb_errorstr(tdb_drivers))); + return False; + } + } + + if (strncmp(kbuf.dptr, SECDESC_PREFIX, strlen(SECDESC_PREFIX)) == 0) { + DEBUG(0,("upgrade_to_version_3:moving secdesc\n")); + if (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) != 0) { + DEBUG(0,("upgrade_to_version_3: failed to move secdesc. Error (%s)\n", tdb_errorstr(tdb_printers))); + return False; + } + if (tdb_delete(tdb_drivers, kbuf) != 0) { + DEBUG(0,("upgrade_to_version_3: failed to delete secdesc. Error (%s)\n", tdb_errorstr(tdb_drivers))); + return False; + } + } + + SAFE_FREE(dbuf.dptr); + } + + return True; +} + +/**************************************************************************** + Open the NT printing tdb. +****************************************************************************/ + +BOOL nt_printing_init(void) +{ + static pid_t local_pid; + char *vstring = "INFO/version"; + + if (tdb_drivers && tdb_printers && tdb_forms && local_pid == sys_getpid()) + return True; + + tdb_drivers = tdb_open_log(lock_path("ntdrivers.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + if (!tdb_drivers) { + DEBUG(0,("nt_printing_init: Failed to open nt drivers database %s (%s)\n", + lock_path("ntdrivers.tdb"), strerror(errno) )); + return False; + } + + tdb_printers = tdb_open_log(lock_path("ntprinters.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + if (!tdb_printers) { + DEBUG(0,("nt_printing_init: Failed to open nt printers database %s (%s)\n", + lock_path("ntprinters.tdb"), strerror(errno) )); + return False; + } + + tdb_forms = tdb_open_log(lock_path("ntforms.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + if (!tdb_forms) { + DEBUG(0,("nt_printing_init: Failed to open nt forms database %s (%s)\n", + lock_path("ntforms.tdb"), strerror(errno) )); + return False; + } + + local_pid = sys_getpid(); + + /* handle a Samba upgrade */ + tdb_lock_bystring(tdb_drivers, vstring); + { + int32 vers_id; + + /* Cope with byte-reversed older versions of the db. */ + vers_id = tdb_fetch_int32(tdb_drivers, vstring); + if ((vers_id == NTDRIVERS_DATABASE_VERSION_2) || (IREV(vers_id) == NTDRIVERS_DATABASE_VERSION_2)) { + /* Written on a bigendian machine with old fetch_int code. Save as le. */ + /* The only upgrade between V2 and V3 is to save the version in little-endian. */ + tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION); + vers_id = NTDRIVERS_DATABASE_VERSION; + } + + if (vers_id != NTDRIVERS_DATABASE_VERSION) { + + if ((vers_id == NTDRIVERS_DATABASE_VERSION_1) || (IREV(vers_id) == NTDRIVERS_DATABASE_VERSION_1)) { + if (!upgrade_to_version_3()) + return False; + } else + tdb_traverse(tdb_drivers, tdb_traverse_delete_fn, NULL); + + tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION); + } + } + tdb_unlock_bystring(tdb_drivers, vstring); + + update_c_setprinter(True); + + return True; +} + +/******************************************************************* + tdb traversal function for counting printers. +********************************************************************/ + +static int traverse_counting_printers(TDB_CONTEXT *t, TDB_DATA key, + TDB_DATA data, void *context) +{ + int *printer_count = (int*)context; + + if (memcmp(PRINTERS_PREFIX, key.dptr, sizeof(PRINTERS_PREFIX)-1) == 0) { + (*printer_count)++; + DEBUG(10,("traverse_counting_printers: printer = [%s] printer_count = %d\n", key.dptr, *printer_count)); + } + + return 0; +} + +/******************************************************************* + Update the spooler global c_setprinter. This variable is initialized + when the parent smbd starts with the number of existing printers. It + is monotonically increased by the current number of printers *after* + each add or delete printer RPC. Only Microsoft knows why... JRR020119 +********************************************************************/ + +uint32 update_c_setprinter(BOOL initialize) +{ + int32 c_setprinter; + int32 printer_count = 0; + + tdb_lock_bystring(tdb_printers, GLOBAL_C_SETPRINTER); + + /* Traverse the tdb, counting the printers */ + tdb_traverse(tdb_printers, traverse_counting_printers, (void *)&printer_count); + + /* If initializing, set c_setprinter to current printers count + * otherwise, bump it by the current printer count + */ + if (!initialize) + c_setprinter = tdb_fetch_int32(tdb_printers, GLOBAL_C_SETPRINTER) + printer_count; + else + c_setprinter = printer_count; + + DEBUG(10,("update_c_setprinter: c_setprinter = %u\n", (unsigned int)c_setprinter)); + tdb_store_int32(tdb_printers, GLOBAL_C_SETPRINTER, c_setprinter); + + tdb_unlock_bystring(tdb_printers, GLOBAL_C_SETPRINTER); + + return (uint32)c_setprinter; +} + +/******************************************************************* + Get the spooler global c_setprinter, accounting for initialization. +********************************************************************/ + +uint32 get_c_setprinter(void) +{ + int32 c_setprinter = tdb_fetch_int32(tdb_printers, GLOBAL_C_SETPRINTER); + + if (c_setprinter == (int32)-1) + c_setprinter = update_c_setprinter(True); + + DEBUG(10,("get_c_setprinter: c_setprinter = %d\n", c_setprinter)); + + return (uint32)c_setprinter; +} + +/**************************************************************************** + Get builtin form struct list. +****************************************************************************/ + +int get_builtin_ntforms(nt_forms_struct **list) +{ + *list = (nt_forms_struct *)memdup(&default_forms[0], sizeof(default_forms)); + return sizeof(default_forms) / sizeof(default_forms[0]); +} + +/**************************************************************************** + get a builtin form struct +****************************************************************************/ + +BOOL get_a_builtin_ntform(UNISTR2 *uni_formname,nt_forms_struct *form) +{ + int i,count; + fstring form_name; + unistr2_to_ascii(form_name, uni_formname, sizeof(form_name)-1); + DEBUGADD(6,("Looking for builtin form %s \n", form_name)); + count = sizeof(default_forms) / sizeof(default_forms[0]); + for (i=0;i<count;i++) { + if (strequal(form_name,default_forms[i].name)) { + DEBUGADD(6,("Found builtin form %s \n", form_name)); + memcpy(form,&default_forms[i],sizeof(*form)); + break; + } + } + + return (i !=count); +} + +/**************************************************************************** +get a form struct list +****************************************************************************/ +int get_ntforms(nt_forms_struct **list) +{ + TDB_DATA kbuf, newkey, dbuf; + nt_forms_struct *tl; + nt_forms_struct form; + int ret; + int i; + int n = 0; + + for (kbuf = tdb_firstkey(tdb_forms); + kbuf.dptr; + newkey = tdb_nextkey(tdb_forms, kbuf), safe_free(kbuf.dptr), kbuf=newkey) { + if (strncmp(kbuf.dptr, FORMS_PREFIX, strlen(FORMS_PREFIX)) != 0) continue; + + dbuf = tdb_fetch(tdb_forms, kbuf); + if (!dbuf.dptr) continue; + + fstrcpy(form.name, kbuf.dptr+strlen(FORMS_PREFIX)); + ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddddddd", + &i, &form.flag, &form.width, &form.length, &form.left, + &form.top, &form.right, &form.bottom); + SAFE_FREE(dbuf.dptr); + if (ret != dbuf.dsize) continue; + + tl = Realloc(*list, sizeof(nt_forms_struct)*(n+1)); + if (!tl) { + DEBUG(0,("get_ntforms: Realloc fail.\n")); + return 0; + } + *list = tl; + (*list)[n] = form; + n++; + } + + + return n; +} + +/**************************************************************************** +write a form struct list +****************************************************************************/ +int write_ntforms(nt_forms_struct **list, int number) +{ + pstring buf, key; + int len; + TDB_DATA kbuf,dbuf; + int i; + + for (i=0;i<number;i++) { + /* save index, so list is rebuilt in correct order */ + len = tdb_pack(buf, sizeof(buf), "dddddddd", + i, (*list)[i].flag, (*list)[i].width, (*list)[i].length, + (*list)[i].left, (*list)[i].top, (*list)[i].right, + (*list)[i].bottom); + if (len > sizeof(buf)) break; + slprintf(key, sizeof(key)-1, "%s%s", FORMS_PREFIX, (*list)[i].name); + kbuf.dsize = strlen(key)+1; + kbuf.dptr = key; + dbuf.dsize = len; + dbuf.dptr = buf; + if (tdb_store(tdb_forms, kbuf, dbuf, TDB_REPLACE) != 0) break; + } + + return i; +} + +/**************************************************************************** +add a form struct at the end of the list +****************************************************************************/ +BOOL add_a_form(nt_forms_struct **list, const FORM *form, int *count) +{ + int n=0; + BOOL update; + fstring form_name; + nt_forms_struct *tl; + + /* + * NT tries to add forms even when + * they are already in the base + * only update the values if already present + */ + + update=False; + + unistr2_to_ascii(form_name, &form->name, sizeof(form_name)-1); + for (n=0; n<*count; n++) { + if (!strncmp((*list)[n].name, form_name, strlen(form_name))) { + DEBUG(103, ("NT workaround, [%s] already exists\n", form_name)); + update=True; + break; + } + } + + if (update==False) { + if((tl=Realloc(*list, (n+1)*sizeof(nt_forms_struct))) == NULL) { + DEBUG(0,("add_a_form: failed to enlarge forms list!\n")); + return False; + } + *list = tl; + unistr2_to_ascii((*list)[n].name, &form->name, sizeof((*list)[n].name)-1); + (*count)++; + } + + (*list)[n].flag=form->flags; + (*list)[n].width=form->size_x; + (*list)[n].length=form->size_y; + (*list)[n].left=form->left; + (*list)[n].top=form->top; + (*list)[n].right=form->right; + (*list)[n].bottom=form->bottom; + + return True; +} + +/**************************************************************************** + delete a named form struct +****************************************************************************/ +BOOL delete_a_form(nt_forms_struct **list, UNISTR2 *del_name, int *count, WERROR *ret) +{ + pstring key; + TDB_DATA kbuf; + int n=0; + fstring form_name; + + *ret = WERR_OK; + + unistr2_to_ascii(form_name, del_name, sizeof(form_name)-1); + + for (n=0; n<*count; n++) { + if (!strncmp((*list)[n].name, form_name, strlen(form_name))) { + DEBUG(103, ("delete_a_form, [%s] in list\n", form_name)); + break; + } + } + + if (n == *count) { + DEBUG(10,("delete_a_form, [%s] not found\n", form_name)); + *ret = WERR_INVALID_PARAM; + return False; + } + + slprintf(key, sizeof(key)-1, "%s%s", FORMS_PREFIX, (*list)[n].name); + kbuf.dsize = strlen(key)+1; + kbuf.dptr = key; + if (tdb_delete(tdb_forms, kbuf) != 0) { + *ret = WERR_NOMEM; + return False; + } + + return True; +} + +/**************************************************************************** +update a form struct +****************************************************************************/ +void update_a_form(nt_forms_struct **list, const FORM *form, int count) +{ + int n=0; + fstring form_name; + unistr2_to_ascii(form_name, &(form->name), sizeof(form_name)-1); + + DEBUG(106, ("[%s]\n", form_name)); + for (n=0; n<count; n++) + { + DEBUGADD(106, ("n [%d]:[%s]\n", n, (*list)[n].name)); + if (!strncmp((*list)[n].name, form_name, strlen(form_name))) + break; + } + + if (n==count) return; + + (*list)[n].flag=form->flags; + (*list)[n].width=form->size_x; + (*list)[n].length=form->size_y; + (*list)[n].left=form->left; + (*list)[n].top=form->top; + (*list)[n].right=form->right; + (*list)[n].bottom=form->bottom; +} + +/**************************************************************************** +get the nt drivers list + +traverse the database and look-up the matching names +****************************************************************************/ +int get_ntdrivers(fstring **list, char *architecture, uint32 version) +{ + int total=0; + fstring short_archi; + fstring *fl; + pstring key; + TDB_DATA kbuf, newkey; + + get_short_archi(short_archi, architecture); + slprintf(key, sizeof(key)-1, "%s%s/%d/", DRIVERS_PREFIX, short_archi, version); + + for (kbuf = tdb_firstkey(tdb_drivers); + kbuf.dptr; + newkey = tdb_nextkey(tdb_drivers, kbuf), safe_free(kbuf.dptr), kbuf=newkey) { + if (strncmp(kbuf.dptr, key, strlen(key)) != 0) continue; + + if((fl = Realloc(*list, sizeof(fstring)*(total+1))) == NULL) { + DEBUG(0,("get_ntdrivers: failed to enlarge list!\n")); + return -1; + } + else *list = fl; + + fstrcpy((*list)[total], kbuf.dptr+strlen(key)); + total++; + } + + return(total); +} + +/**************************************************************************** +function to do the mapping between the long architecture name and +the short one. +****************************************************************************/ +BOOL get_short_archi(char *short_archi, char *long_archi) +{ + struct table { + char *long_archi; + char *short_archi; + }; + + struct table archi_table[]= + { + {"Windows 4.0", "WIN40" }, + {"Windows NT x86", "W32X86" }, + {"Windows NT R4000", "W32MIPS" }, + {"Windows NT Alpha_AXP", "W32ALPHA" }, + {"Windows NT PowerPC", "W32PPC" }, + {NULL, "" } + }; + + int i=-1; + + DEBUG(107,("Getting architecture dependant directory\n")); + do { + i++; + } while ( (archi_table[i].long_archi!=NULL ) && + StrCaseCmp(long_archi, archi_table[i].long_archi) ); + + if (archi_table[i].long_archi==NULL) { + DEBUGADD(107,("Unknown architecture [%s] !\n", long_archi)); + return False; + } + + StrnCpy (short_archi, archi_table[i].short_archi, strlen(archi_table[i].short_archi)); + + DEBUGADD(108,("index: [%d]\n", i)); + DEBUGADD(108,("long architecture: [%s]\n", long_archi)); + DEBUGADD(108,("short architecture: [%s]\n", short_archi)); + + return True; +} + +/**************************************************************************** + Version information in Microsoft files is held in a VS_VERSION_INFO structure. + There are two case to be covered here: PE (Portable Executable) and NE (New + Executable) files. Both files support the same INFO structure, but PE files + store the signature in unicode, and NE files store it as !unicode. + returns -1 on error, 1 on version info found, and 0 on no version info found. +****************************************************************************/ + +static int get_file_version(files_struct *fsp, char *fname,uint32 *major, uint32 *minor) +{ + int i; + char *buf; + ssize_t byte_count; + + if ((buf=malloc(PE_HEADER_SIZE)) == NULL) { + DEBUG(0,("get_file_version: PE file [%s] PE Header malloc failed bytes = %d\n", + fname, PE_HEADER_SIZE)); + goto error_exit; + } + + /* Note: DOS_HEADER_SIZE < malloc'ed PE_HEADER_SIZE */ + if ((byte_count = vfs_read_data(fsp, buf, DOS_HEADER_SIZE)) < DOS_HEADER_SIZE) { + DEBUG(3,("get_file_version: File [%s] DOS header too short, bytes read = %d\n", + fname, byte_count)); + goto no_version_info; + } + + /* Is this really a DOS header? */ + if (SVAL(buf,DOS_HEADER_MAGIC_OFFSET) != DOS_HEADER_MAGIC) { + DEBUG(6,("get_file_version: File [%s] bad DOS magic = 0x%x\n", + fname, SVAL(buf,DOS_HEADER_MAGIC_OFFSET))); + goto no_version_info; + } + + /* Skip OEM header (if any) and the DOS stub to start of Windows header */ + if (fsp->conn->vfs_ops.lseek(fsp, fsp->fd, SVAL(buf,DOS_HEADER_LFANEW_OFFSET), SEEK_SET) == (SMB_OFF_T)-1) { + DEBUG(3,("get_file_version: File [%s] too short, errno = %d\n", + fname, errno)); + /* Assume this isn't an error... the file just looks sort of like a PE/NE file */ + goto no_version_info; + } + + if ((byte_count = vfs_read_data(fsp, buf, PE_HEADER_SIZE)) < PE_HEADER_SIZE) { + DEBUG(3,("get_file_version: File [%s] Windows header too short, bytes read = %d\n", + fname, byte_count)); + /* Assume this isn't an error... the file just looks sort of like a PE/NE file */ + goto no_version_info; + } + + /* The header may be a PE (Portable Executable) or an NE (New Executable) */ + if (IVAL(buf,PE_HEADER_SIGNATURE_OFFSET) == PE_HEADER_SIGNATURE) { + int num_sections; + int section_table_bytes; + + if (SVAL(buf,PE_HEADER_MACHINE_OFFSET) != PE_HEADER_MACHINE_I386) { + DEBUG(3,("get_file_version: PE file [%s] wrong machine = 0x%x\n", + fname, SVAL(buf,PE_HEADER_MACHINE_OFFSET))); + /* At this point, we assume the file is in error. It still could be somthing + * else besides a PE file, but it unlikely at this point. + */ + goto error_exit; + } + + /* get the section table */ + num_sections = SVAL(buf,PE_HEADER_NUMBER_OF_SECTIONS); + section_table_bytes = num_sections * PE_HEADER_SECT_HEADER_SIZE; + SAFE_FREE(buf); + if ((buf=malloc(section_table_bytes)) == NULL) { + DEBUG(0,("get_file_version: PE file [%s] section table malloc failed bytes = %d\n", + fname, section_table_bytes)); + goto error_exit; + } + + if ((byte_count = vfs_read_data(fsp, buf, section_table_bytes)) < section_table_bytes) { + DEBUG(3,("get_file_version: PE file [%s] Section header too short, bytes read = %d\n", + fname, byte_count)); + goto error_exit; + } + + /* Iterate the section table looking for the resource section ".rsrc" */ + for (i = 0; i < num_sections; i++) { + int sec_offset = i * PE_HEADER_SECT_HEADER_SIZE; + + if (strcmp(".rsrc", &buf[sec_offset+PE_HEADER_SECT_NAME_OFFSET]) == 0) { + int section_pos = IVAL(buf,sec_offset+PE_HEADER_SECT_PTR_DATA_OFFSET); + int section_bytes = IVAL(buf,sec_offset+PE_HEADER_SECT_SIZE_DATA_OFFSET); + + SAFE_FREE(buf); + if ((buf=malloc(section_bytes)) == NULL) { + DEBUG(0,("get_file_version: PE file [%s] version malloc failed bytes = %d\n", + fname, section_bytes)); + goto error_exit; + } + + /* Seek to the start of the .rsrc section info */ + if (fsp->conn->vfs_ops.lseek(fsp, fsp->fd, section_pos, SEEK_SET) == (SMB_OFF_T)-1) { + DEBUG(3,("get_file_version: PE file [%s] too short for section info, errno = %d\n", + fname, errno)); + goto error_exit; + } + + if ((byte_count = vfs_read_data(fsp, buf, section_bytes)) < section_bytes) { + DEBUG(3,("get_file_version: PE file [%s] .rsrc section too short, bytes read = %d\n", + fname, byte_count)); + goto error_exit; + } + + for (i=0; i<section_bytes-VS_VERSION_INFO_UNICODE_SIZE; i++) { + /* Scan for 1st 3 unicoded bytes followed by word aligned magic value */ + if (buf[i] == 'V' && buf[i+1] == '\0' && buf[i+2] == 'S') { + /* Align to next long address */ + int pos = (i + sizeof(VS_SIGNATURE)*2 + 3) & 0xfffffffc; + + if (IVAL(buf,pos) == VS_MAGIC_VALUE) { + *major = IVAL(buf,pos+VS_MAJOR_OFFSET); + *minor = IVAL(buf,pos+VS_MINOR_OFFSET); + + DEBUG(6,("get_file_version: PE file [%s] Version = %08x:%08x (%d.%d.%d.%d)\n", + fname, *major, *minor, + (*major>>16)&0xffff, *major&0xffff, + (*minor>>16)&0xffff, *minor&0xffff)); + SAFE_FREE(buf); + return 1; + } + } + } + } + } + + /* Version info not found, fall back to origin date/time */ + DEBUG(10,("get_file_version: PE file [%s] has no version info\n", fname)); + SAFE_FREE(buf); + return 0; + + } else if (SVAL(buf,NE_HEADER_SIGNATURE_OFFSET) == NE_HEADER_SIGNATURE) { + if (CVAL(buf,NE_HEADER_TARGET_OS_OFFSET) != NE_HEADER_TARGOS_WIN ) { + DEBUG(3,("get_file_version: NE file [%s] wrong target OS = 0x%x\n", + fname, CVAL(buf,NE_HEADER_TARGET_OS_OFFSET))); + /* At this point, we assume the file is in error. It still could be somthing + * else besides a NE file, but it unlikely at this point. */ + goto error_exit; + } + + /* Allocate a bit more space to speed up things */ + SAFE_FREE(buf); + if ((buf=malloc(VS_NE_BUF_SIZE)) == NULL) { + DEBUG(0,("get_file_version: NE file [%s] malloc failed bytes = %d\n", + fname, PE_HEADER_SIZE)); + goto error_exit; + } + + /* This is a HACK! I got tired of trying to sort through the messy + * 'NE' file format. If anyone wants to clean this up please have at + * it, but this works. 'NE' files will eventually fade away. JRR */ + while((byte_count = vfs_read_data(fsp, buf, VS_NE_BUF_SIZE)) > 0) { + /* Cover case that should not occur in a well formed 'NE' .dll file */ + if (byte_count-VS_VERSION_INFO_SIZE <= 0) break; + + for(i=0; i<byte_count; i++) { + /* Fast skip past data that can't possibly match */ + if (buf[i] != 'V') continue; + + /* Potential match data crosses buf boundry, move it to beginning + * of buf, and fill the buf with as much as it will hold. */ + if (i>byte_count-VS_VERSION_INFO_SIZE) { + int bc; + + memcpy(buf, &buf[i], byte_count-i); + if ((bc = vfs_read_data(fsp, &buf[byte_count-i], VS_NE_BUF_SIZE- + (byte_count-i))) < 0) { + + DEBUG(0,("get_file_version: NE file [%s] Read error, errno=%d\n", + fname, errno)); + goto error_exit; + } + + byte_count = bc + (byte_count - i); + if (byte_count<VS_VERSION_INFO_SIZE) break; + + i = 0; + } + + /* Check that the full signature string and the magic number that + * follows exist (not a perfect solution, but the chances that this + * occurs in code is, well, remote. Yes I know I'm comparing the 'V' + * twice, as it is simpler to read the code. */ + if (strcmp(&buf[i], VS_SIGNATURE) == 0) { + /* Compute skip alignment to next long address */ + int skip = -(fsp->conn->vfs_ops.lseek(fsp, fsp->fd, 0, SEEK_CUR) - (byte_count - i) + + sizeof(VS_SIGNATURE)) & 3; + if (IVAL(buf,i+sizeof(VS_SIGNATURE)+skip) != 0xfeef04bd) continue; + + *major = IVAL(buf,i+sizeof(VS_SIGNATURE)+skip+VS_MAJOR_OFFSET); + *minor = IVAL(buf,i+sizeof(VS_SIGNATURE)+skip+VS_MINOR_OFFSET); + DEBUG(6,("get_file_version: NE file [%s] Version = %08x:%08x (%d.%d.%d.%d)\n", + fname, *major, *minor, + (*major>>16)&0xffff, *major&0xffff, + (*minor>>16)&0xffff, *minor&0xffff)); + SAFE_FREE(buf); + return 1; + } + } + } + + /* Version info not found, fall back to origin date/time */ + DEBUG(0,("get_file_version: NE file [%s] Version info not found\n", fname)); + SAFE_FREE(buf); + return 0; + + } else + /* Assume this isn't an error... the file just looks sort of like a PE/NE file */ + DEBUG(3,("get_file_version: File [%s] unknown file format, signature = 0x%x\n", + fname, IVAL(buf,PE_HEADER_SIGNATURE_OFFSET))); + + no_version_info: + SAFE_FREE(buf); + return 0; + + error_exit: + SAFE_FREE(buf); + return -1; +} + +/**************************************************************************** +Drivers for Microsoft systems contain multiple files. Often, multiple drivers +share one or more files. During the MS installation process files are checked +to insure that only a newer version of a shared file is installed over an +older version. There are several possibilities for this comparison. If there +is no previous version, the new one is newer (obviously). If either file is +missing the version info structure, compare the creation date (on Unix use +the modification date). Otherwise chose the numerically larger version number. +****************************************************************************/ +static int file_version_is_newer(connection_struct *conn, fstring new_file, + fstring old_file) +{ + BOOL use_version = True; + pstring filepath; + + uint32 new_major; + uint32 new_minor; + time_t new_create_time; + + uint32 old_major; + uint32 old_minor; + time_t old_create_time; + + int access_mode; + int action; + files_struct *fsp = NULL; + SMB_STRUCT_STAT st; + SMB_STRUCT_STAT stat_buf; + BOOL bad_path; + + ZERO_STRUCT(st); + ZERO_STRUCT(stat_buf); + new_create_time = (time_t)0; + old_create_time = (time_t)0; + + /* Get file version info (if available) for previous file (if it exists) */ + pstrcpy(filepath, old_file); + + unix_convert(filepath,conn,NULL,&bad_path,&stat_buf); + + fsp = open_file_shared(conn, filepath, &stat_buf, + SET_OPEN_MODE(DOS_OPEN_RDONLY), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), + 0, 0, &access_mode, &action); + if (!fsp) { + /* Old file not found, so by definition new file is in fact newer */ + DEBUG(10,("file_version_is_newer: Can't open old file [%s], errno = %d\n", + filepath, errno)); + return True; + + } else { + int ret = get_file_version(fsp, old_file, &old_major, &old_minor); + if (ret == -1) goto error_exit; + + if (!ret) { + DEBUG(6,("file_version_is_newer: Version info not found [%s], use mod time\n", + old_file)); + use_version = False; + if (fsp->conn->vfs_ops.fstat(fsp, fsp->fd, &st) == -1) goto error_exit; + old_create_time = st.st_mtime; + DEBUGADD(6,("file_version_is_newer: mod time = %ld sec\n", old_create_time)); + } + } + close_file(fsp, True); + + /* Get file version info (if available) for new file */ + pstrcpy(filepath, new_file); + unix_convert(filepath,conn,NULL,&bad_path,&stat_buf); + + fsp = open_file_shared(conn, filepath, &stat_buf, + SET_OPEN_MODE(DOS_OPEN_RDONLY), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), + 0, 0, &access_mode, &action); + if (!fsp) { + /* New file not found, this shouldn't occur if the caller did its job */ + DEBUG(3,("file_version_is_newer: Can't open new file [%s], errno = %d\n", + filepath, errno)); + goto error_exit; + + } else { + int ret = get_file_version(fsp, new_file, &new_major, &new_minor); + if (ret == -1) goto error_exit; + + if (!ret) { + DEBUG(6,("file_version_is_newer: Version info not found [%s], use mod time\n", + new_file)); + use_version = False; + if (fsp->conn->vfs_ops.fstat(fsp, fsp->fd, &st) == -1) goto error_exit; + new_create_time = st.st_mtime; + DEBUGADD(6,("file_version_is_newer: mod time = %ld sec\n", new_create_time)); + } + } + close_file(fsp, True); + + if (use_version) { + /* Compare versions and choose the larger version number */ + if (new_major > old_major || + (new_major == old_major && new_minor > old_minor)) { + + DEBUG(6,("file_version_is_newer: Replacing [%s] with [%s]\n", old_file, new_file)); + return True; + } + else { + DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file)); + return False; + } + + } else { + /* Compare modification time/dates and choose the newest time/date */ + if (new_create_time > old_create_time) { + DEBUG(6,("file_version_is_newer: Replacing [%s] with [%s]\n", old_file, new_file)); + return True; + } + else { + DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file)); + return False; + } + } + + error_exit: + if(fsp) + close_file(fsp, True); + return -1; +} + +/**************************************************************************** +Determine the correct cVersion associated with an architecture and driver +****************************************************************************/ +static uint32 get_correct_cversion(fstring architecture, fstring driverpath_in, + struct current_user *user, WERROR *perr) +{ + int cversion; + int access_mode; + int action; + NTSTATUS nt_status; + pstring driverpath; + DATA_BLOB null_pw; + files_struct *fsp = NULL; + BOOL bad_path; + SMB_STRUCT_STAT st; + connection_struct *conn; + + ZERO_STRUCT(st); + + *perr = WERR_INVALID_PARAM; + + /* If architecture is Windows 95/98/ME, the version is always 0. */ + if (strcmp(architecture, "WIN40") == 0) { + DEBUG(10,("get_correct_cversion: Driver is Win9x, cversion = 0\n")); + *perr = WERR_OK; + return 0; + } + + /* + * Connect to the print$ share under the same account as the user connected + * to the rpc pipe. Note we must still be root to do this. + */ + + /* Null password is ok - we are already an authenticated user... */ + null_pw = data_blob(NULL, 0); + become_root(); + conn = make_connection("print$", null_pw, "A:", user->vuid, &nt_status); + unbecome_root(); + + if (conn == NULL) { + DEBUG(0,("get_correct_cversion: Unable to connect\n")); + *perr = ntstatus_to_werror(nt_status); + return -1; + } + + /* We are temporarily becoming the connection user. */ + if (!become_user(conn, conn->vuid)) { + DEBUG(0,("get_correct_cversion: Can't become user!\n")); + *perr = WERR_ACCESS_DENIED; + return -1; + } + + /* Open the driver file (Portable Executable format) and determine the + * deriver the cversion. */ + slprintf(driverpath, sizeof(driverpath)-1, "%s/%s", architecture, driverpath_in); + + unix_convert(driverpath,conn,NULL,&bad_path,&st); + + fsp = open_file_shared(conn, driverpath, &st, + SET_OPEN_MODE(DOS_OPEN_RDONLY), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), + 0, 0, &access_mode, &action); + if (!fsp) { + DEBUG(3,("get_correct_cversion: Can't open file [%s], errno = %d\n", + driverpath, errno)); + *perr = WERR_ACCESS_DENIED; + goto error_exit; + } + else { + uint32 major; + uint32 minor; + int ret = get_file_version(fsp, driverpath, &major, &minor); + if (ret == -1) goto error_exit; + + if (!ret) { + DEBUG(6,("get_correct_cversion: Version info not found [%s]\n", driverpath)); + goto error_exit; + } + + /* + * This is a Microsoft'ism. See references in MSDN to VER_FILEVERSION + * for more details. Version in this case is not just the version of the + * file, but the version in the sense of kernal mode (2) vs. user mode + * (3) drivers. Other bits of the version fields are the version info. + * JRR 010716 + */ + cversion = major & 0x0000ffff; + switch (cversion) { + case 2: /* WinNT drivers */ + case 3: /* Win2K drivers */ + break; + + default: + DEBUG(6,("get_correct_cversion: cversion invalid [%s] cversion = %d\n", + driverpath, cversion)); + goto error_exit; + } + + DEBUG(10,("get_correct_cversion: Version info found [%s] major = 0x%x minor = 0x%x\n", + driverpath, major, minor)); + } + + DEBUG(10,("get_correct_cversion: Driver file [%s] cversion = %d\n", + driverpath, cversion)); + + close_file(fsp, True); + close_cnum(conn, user->vuid); + unbecome_user(); + *perr = WERR_OK; + return cversion; + + + error_exit: + + if(fsp) + close_file(fsp, True); + + close_cnum(conn, user->vuid); + unbecome_user(); + return -1; +} + +/**************************************************************************** +****************************************************************************/ +static WERROR clean_up_driver_struct_level_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver, + struct current_user *user) +{ + fstring architecture; + fstring new_name; + char *p; + int i; + WERROR err; + + /* clean up the driver name. + * we can get .\driver.dll + * or worse c:\windows\system\driver.dll ! + */ + /* using an intermediate string to not have overlaping memcpy()'s */ + if ((p = strrchr(driver->driverpath,'\\')) != NULL) { + fstrcpy(new_name, p+1); + fstrcpy(driver->driverpath, new_name); + } + + if ((p = strrchr(driver->datafile,'\\')) != NULL) { + fstrcpy(new_name, p+1); + fstrcpy(driver->datafile, new_name); + } + + if ((p = strrchr(driver->configfile,'\\')) != NULL) { + fstrcpy(new_name, p+1); + fstrcpy(driver->configfile, new_name); + } + + if ((p = strrchr(driver->helpfile,'\\')) != NULL) { + fstrcpy(new_name, p+1); + fstrcpy(driver->helpfile, new_name); + } + + if (driver->dependentfiles) { + for (i=0; *driver->dependentfiles[i]; i++) { + if ((p = strrchr(driver->dependentfiles[i],'\\')) != NULL) { + fstrcpy(new_name, p+1); + fstrcpy(driver->dependentfiles[i], new_name); + } + } + } + + get_short_archi(architecture, driver->environment); + + /* jfm:7/16/2000 the client always sends the cversion=0. + * The server should check which version the driver is by reading + * the PE header of driver->driverpath. + * + * For Windows 95/98 the version is 0 (so the value sent is correct) + * For Windows NT (the architecture doesn't matter) + * NT 3.1: cversion=0 + * NT 3.5/3.51: cversion=1 + * NT 4: cversion=2 + * NT2K: cversion=3 + */ + if ((driver->cversion = get_correct_cversion( architecture, + driver->driverpath, user, &err)) == -1) + return err; + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ +static WERROR clean_up_driver_struct_level_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver, + struct current_user *user) +{ + fstring architecture; + fstring new_name; + char *p; + int i; + WERROR err; + + /* clean up the driver name. + * we can get .\driver.dll + * or worse c:\windows\system\driver.dll ! + */ + /* using an intermediate string to not have overlaping memcpy()'s */ + if ((p = strrchr(driver->driverpath,'\\')) != NULL) { + fstrcpy(new_name, p+1); + fstrcpy(driver->driverpath, new_name); + } + + if ((p = strrchr(driver->datafile,'\\')) != NULL) { + fstrcpy(new_name, p+1); + fstrcpy(driver->datafile, new_name); + } + + if ((p = strrchr(driver->configfile,'\\')) != NULL) { + fstrcpy(new_name, p+1); + fstrcpy(driver->configfile, new_name); + } + + if ((p = strrchr(driver->helpfile,'\\')) != NULL) { + fstrcpy(new_name, p+1); + fstrcpy(driver->helpfile, new_name); + } + + if (driver->dependentfiles) { + for (i=0; *driver->dependentfiles[i]; i++) { + if ((p = strrchr(driver->dependentfiles[i],'\\')) != NULL) { + fstrcpy(new_name, p+1); + fstrcpy(driver->dependentfiles[i], new_name); + } + } + } + + get_short_archi(architecture, driver->environment); + + /* jfm:7/16/2000 the client always sends the cversion=0. + * The server should check which version the driver is by reading + * the PE header of driver->driverpath. + * + * For Windows 95/98 the version is 0 (so the value sent is correct) + * For Windows NT (the architecture doesn't matter) + * NT 3.1: cversion=0 + * NT 3.5/3.51: cversion=1 + * NT 4: cversion=2 + * NT2K: cversion=3 + */ + if ((driver->version = get_correct_cversion(architecture, + driver->driverpath, user, &err)) == -1) + return err; + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ +WERROR clean_up_driver_struct(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract, + uint32 level, struct current_user *user) +{ + switch (level) { + case 3: + { + NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver; + driver=driver_abstract.info_3; + return clean_up_driver_struct_level_3(driver, user); + } + case 6: + { + NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver; + driver=driver_abstract.info_6; + return clean_up_driver_struct_level_6(driver, user); + } + default: + return WERR_INVALID_PARAM; + } +} + +/**************************************************************************** + This function sucks and should be replaced. JRA. +****************************************************************************/ + +static void convert_level_6_to_level3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *dst, NT_PRINTER_DRIVER_INFO_LEVEL_6 *src) +{ + dst->cversion = src->version; + + fstrcpy( dst->name, src->name); + fstrcpy( dst->environment, src->environment); + fstrcpy( dst->driverpath, src->driverpath); + fstrcpy( dst->datafile, src->datafile); + fstrcpy( dst->configfile, src->configfile); + fstrcpy( dst->helpfile, src->helpfile); + fstrcpy( dst->monitorname, src->monitorname); + fstrcpy( dst->defaultdatatype, src->defaultdatatype); + dst->dependentfiles = src->dependentfiles; +} + +#if 0 /* Debugging function */ + +static char* ffmt(unsigned char *c){ + int i; + static char ffmt_str[17]; + + for (i=0; i<16; i++) { + if ((c[i] < ' ') || (c[i] > '~')) + ffmt_str[i]='.'; + else + ffmt_str[i]=c[i]; + } + ffmt_str[16]='\0'; + return ffmt_str; +} + +#endif + +/**************************************************************************** +****************************************************************************/ +BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract, uint32 level, + struct current_user *user, WERROR *perr) +{ + NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver; + NT_PRINTER_DRIVER_INFO_LEVEL_3 converted_driver; + fstring architecture; + pstring new_dir; + pstring old_name; + pstring new_name; + DATA_BLOB null_pw; + connection_struct *conn; + NTSTATUS nt_status; + pstring inbuf; + pstring outbuf; + int ver = 0; + int i; + + memset(inbuf, '\0', sizeof(inbuf)); + memset(outbuf, '\0', sizeof(outbuf)); + *perr = WERR_OK; + + if (level==3) + driver=driver_abstract.info_3; + else if (level==6) { + convert_level_6_to_level3(&converted_driver, driver_abstract.info_6); + driver = &converted_driver; + } else { + DEBUG(0,("move_driver_to_download_area: Unknown info level (%u)\n", (unsigned int)level )); + return False; + } + + get_short_archi(architecture, driver->environment); + + /* + * Connect to the print$ share under the same account as the user connected to the rpc pipe. + * Note we must be root to do this. + */ + + become_root(); + null_pw = data_blob(NULL, 0); + conn = make_connection("print$", null_pw, "A:", user->vuid, &nt_status); + unbecome_root(); + + if (conn == NULL) { + DEBUG(0,("move_driver_to_download_area: Unable to connect\n")); + *perr = ntstatus_to_werror(nt_status); + return False; + } + + /* + * Save who we are - we are temporarily becoming the connection user. + */ + + if (!become_user(conn, conn->vuid)) { + DEBUG(0,("move_driver_to_download_area: Can't become user!\n")); + return False; + } + + /* + * make the directories version and version\driver_name + * under the architecture directory. + */ + DEBUG(5,("Creating first directory\n")); + slprintf(new_dir, sizeof(new_dir)-1, "%s/%d", architecture, driver->cversion); + mkdir_internal(conn, new_dir); + + /* For each driver file, archi\filexxx.yyy, if there is a duplicate file + * listed for this driver which has already been moved, skip it (note: + * drivers may list the same file name several times. Then check if the + * file already exists in archi\cversion\, if so, check that the version + * info (or time stamps if version info is unavailable) is newer (or the + * date is later). If it is, move it to archi\cversion\filexxx.yyy. + * Otherwise, delete the file. + * + * If a file is not moved to archi\cversion\ because of an error, all the + * rest of the 'unmoved' driver files are removed from archi\. If one or + * more of the driver's files was already moved to archi\cversion\, it + * potentially leaves the driver in a partially updated state. Version + * trauma will most likely occur if an client attempts to use any printer + * bound to the driver. Perhaps a rewrite to make sure the moves can be + * done is appropriate... later JRR + */ + + DEBUG(5,("Moving files now !\n")); + + if (driver->driverpath && strlen(driver->driverpath)) { + slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->driverpath); + slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->driverpath); + if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) { + NTSTATUS status; + status = rename_internals(conn, new_name, old_name, True); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n", + new_name, old_name)); + *perr = ntstatus_to_werror(status); + unlink_internals(conn, 0, new_name); + ver = -1; + } + } + else + unlink_internals(conn, 0, new_name); + } + + if (driver->datafile && strlen(driver->datafile)) { + if (!strequal(driver->datafile, driver->driverpath)) { + slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->datafile); + slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->datafile); + if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) { + NTSTATUS status; + status = rename_internals(conn, new_name, old_name, True); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n", + new_name, old_name)); + *perr = ntstatus_to_werror(status); + unlink_internals(conn, 0, new_name); + ver = -1; + } + } + else + unlink_internals(conn, 0, new_name); + } + } + + if (driver->configfile && strlen(driver->configfile)) { + if (!strequal(driver->configfile, driver->driverpath) && + !strequal(driver->configfile, driver->datafile)) { + slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->configfile); + slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->configfile); + if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) { + NTSTATUS status; + status = rename_internals(conn, new_name, old_name, True); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n", + new_name, old_name)); + *perr = ntstatus_to_werror(status); + unlink_internals(conn, 0, new_name); + ver = -1; + } + } + else + unlink_internals(conn, 0, new_name); + } + } + + if (driver->helpfile && strlen(driver->helpfile)) { + if (!strequal(driver->helpfile, driver->driverpath) && + !strequal(driver->helpfile, driver->datafile) && + !strequal(driver->helpfile, driver->configfile)) { + slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->helpfile); + slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->helpfile); + if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) { + NTSTATUS status; + status = rename_internals(conn, new_name, old_name, True); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n", + new_name, old_name)); + *perr = ntstatus_to_werror(status); + unlink_internals(conn, 0, new_name); + ver = -1; + } + } + else + unlink_internals(conn, 0, new_name); + } + } + + if (driver->dependentfiles) { + for (i=0; *driver->dependentfiles[i]; i++) { + if (!strequal(driver->dependentfiles[i], driver->driverpath) && + !strequal(driver->dependentfiles[i], driver->datafile) && + !strequal(driver->dependentfiles[i], driver->configfile) && + !strequal(driver->dependentfiles[i], driver->helpfile)) { + int j; + for (j=0; j < i; j++) { + if (strequal(driver->dependentfiles[i], driver->dependentfiles[j])) { + goto NextDriver; + } + } + + slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->dependentfiles[i]); + slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->dependentfiles[i]); + if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) { + NTSTATUS status; + status = rename_internals(conn, new_name, old_name, True); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n", + new_name, old_name)); + *perr = ntstatus_to_werror(status); + unlink_internals(conn, 0, new_name); + ver = -1; + } + } + else + unlink_internals(conn, 0, new_name); + } + NextDriver: ; + } + } + + close_cnum(conn, user->vuid); + unbecome_user(); + + return ver == -1 ? False : True; +} + +/**************************************************************************** +****************************************************************************/ +static uint32 add_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver) +{ + int len, buflen; + fstring architecture; + pstring directory; + pstring temp_name; + pstring key; + char *buf; + int i, ret; + TDB_DATA kbuf, dbuf; + + get_short_archi(architecture, driver->environment); + + /* The names are relative. We store them in the form: \print$\arch\version\driver.xxx + * \\server is added in the rpc server layer. + * It does make sense to NOT store the server's name in the printer TDB. + */ + + slprintf(directory, sizeof(directory)-1, "\\print$\\%s\\%d\\", architecture, driver->cversion); + + /* .inf files do not always list a file for each of the four standard files. + * Don't prepend a path to a null filename, or client claims: + * "The server on which the printer resides does not have a suitable + * <printer driver name> printer driver installed. Click OK if you + * wish to install the driver on your local machine." + */ + if (strlen(driver->driverpath)) { + fstrcpy(temp_name, driver->driverpath); + slprintf(driver->driverpath, sizeof(driver->driverpath)-1, "%s%s", directory, temp_name); + } + + if (strlen(driver->datafile)) { + fstrcpy(temp_name, driver->datafile); + slprintf(driver->datafile, sizeof(driver->datafile)-1, "%s%s", directory, temp_name); + } + + if (strlen(driver->configfile)) { + fstrcpy(temp_name, driver->configfile); + slprintf(driver->configfile, sizeof(driver->configfile)-1, "%s%s", directory, temp_name); + } + + if (strlen(driver->helpfile)) { + fstrcpy(temp_name, driver->helpfile); + slprintf(driver->helpfile, sizeof(driver->helpfile)-1, "%s%s", directory, temp_name); + } + + if (driver->dependentfiles) { + for (i=0; *driver->dependentfiles[i]; i++) { + fstrcpy(temp_name, driver->dependentfiles[i]); + slprintf(driver->dependentfiles[i], sizeof(driver->dependentfiles[i])-1, "%s%s", directory, temp_name); + } + } + + slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX, architecture, driver->cversion, driver->name); + + DEBUG(5,("add_a_printer_driver_3: Adding driver with key %s\n", key )); + + buf = NULL; + len = buflen = 0; + + again: + len = 0; + len += tdb_pack(buf+len, buflen-len, "dffffffff", + driver->cversion, + driver->name, + driver->environment, + driver->driverpath, + driver->datafile, + driver->configfile, + driver->helpfile, + driver->monitorname, + driver->defaultdatatype); + + if (driver->dependentfiles) { + for (i=0; *driver->dependentfiles[i]; i++) { + len += tdb_pack(buf+len, buflen-len, "f", + driver->dependentfiles[i]); + } + } + + if (len != buflen) { + char *tb; + + tb = (char *)Realloc(buf, len); + if (!tb) { + DEBUG(0,("add_a_printer_driver_3: failed to enlarge buffer\n!")); + ret = -1; + goto done; + } + else buf = tb; + buflen = len; + goto again; + } + + + kbuf.dptr = key; + kbuf.dsize = strlen(key)+1; + dbuf.dptr = buf; + dbuf.dsize = len; + + ret = tdb_store(tdb_drivers, kbuf, dbuf, TDB_REPLACE); + +done: + if (ret) + DEBUG(0,("add_a_printer_driver_3: Adding driver with key %s failed.\n", key )); + + SAFE_FREE(buf); + return ret; +} + +/**************************************************************************** +****************************************************************************/ +static uint32 add_a_printer_driver_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver) +{ + NT_PRINTER_DRIVER_INFO_LEVEL_3 info3; + + ZERO_STRUCT(info3); + info3.cversion = driver->version; + fstrcpy(info3.name,driver->name); + fstrcpy(info3.environment,driver->environment); + fstrcpy(info3.driverpath,driver->driverpath); + fstrcpy(info3.datafile,driver->datafile); + fstrcpy(info3.configfile,driver->configfile); + fstrcpy(info3.helpfile,driver->helpfile); + fstrcpy(info3.monitorname,driver->monitorname); + fstrcpy(info3.defaultdatatype,driver->defaultdatatype); + info3.dependentfiles = driver->dependentfiles; + + return add_a_printer_driver_3(&info3); +} + + +/**************************************************************************** +****************************************************************************/ +static WERROR get_a_printer_driver_3_default(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, fstring in_prt, fstring in_arch) +{ + NT_PRINTER_DRIVER_INFO_LEVEL_3 info; + + ZERO_STRUCT(info); + + fstrcpy(info.name, in_prt); + fstrcpy(info.defaultdatatype, "RAW"); + + fstrcpy(info.driverpath, ""); + fstrcpy(info.datafile, ""); + fstrcpy(info.configfile, ""); + fstrcpy(info.helpfile, ""); + + if ((info.dependentfiles=(fstring *)malloc(2*sizeof(fstring))) == NULL) + return WERR_NOMEM; + + memset(info.dependentfiles, '\0', 2*sizeof(fstring)); + fstrcpy(info.dependentfiles[0], ""); + + *info_ptr = memdup(&info, sizeof(info)); + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ +static WERROR get_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, fstring in_prt, fstring in_arch, uint32 version) +{ + NT_PRINTER_DRIVER_INFO_LEVEL_3 driver; + TDB_DATA kbuf, dbuf; + fstring architecture; + int len = 0; + int i; + pstring key; + + ZERO_STRUCT(driver); + + get_short_archi(architecture, in_arch); + + DEBUG(8,("get_a_printer_driver_3: [%s%s/%d/%s]\n", DRIVERS_PREFIX, architecture, version, in_prt)); + + slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX, architecture, version, in_prt); + + kbuf.dptr = key; + kbuf.dsize = strlen(key)+1; + + dbuf = tdb_fetch(tdb_drivers, kbuf); +#if 0 + if (!dbuf.dptr) return get_a_printer_driver_3_default(info_ptr, in_prt, in_arch); +#else + if (!dbuf.dptr) return WERR_ACCESS_DENIED; +#endif + len += tdb_unpack(dbuf.dptr, dbuf.dsize, "dffffffff", + &driver.cversion, + driver.name, + driver.environment, + driver.driverpath, + driver.datafile, + driver.configfile, + driver.helpfile, + driver.monitorname, + driver.defaultdatatype); + + i=0; + while (len < dbuf.dsize) { + fstring *tddfs; + + tddfs = (fstring *)Realloc(driver.dependentfiles, + sizeof(fstring)*(i+2)); + if (tddfs == NULL) { + DEBUG(0,("get_a_printer_driver_3: failed to enlarge buffer!\n")); + break; + } + else driver.dependentfiles = tddfs; + + len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f", + &driver.dependentfiles[i]); + i++; + } + if (driver.dependentfiles != NULL) + fstrcpy(driver.dependentfiles[i], ""); + + SAFE_FREE(dbuf.dptr); + + if (len != dbuf.dsize) { + SAFE_FREE(driver.dependentfiles); + + return get_a_printer_driver_3_default(info_ptr, in_prt, in_arch); + } + + *info_ptr = (NT_PRINTER_DRIVER_INFO_LEVEL_3 *)memdup(&driver, sizeof(driver)); + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ +uint32 get_a_printer_driver_9x_compatible(pstring line, fstring model) +{ + NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3; + TDB_DATA kbuf; + pstring key; + int i; + line[0] = '\0'; + + slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX, "WIN40", 0, model); + DEBUG(10,("driver key: [%s]\n", key)); + + kbuf.dptr = key; + kbuf.dsize = strlen(key)+1; + if (!tdb_exists(tdb_drivers, kbuf)) + return False; + + ZERO_STRUCT(info3); + get_a_printer_driver_3(&info3, model, "Windows 4.0", 0); + + DEBUGADD(10,("info3->name [%s]\n", info3->name)); + DEBUGADD(10,("info3->datafile [%s]\n", info3->datafile)); + DEBUGADD(10,("info3->helpfile [%s]\n", info3->helpfile)); + DEBUGADD(10,("info3->monitorname [%s]\n", info3->monitorname)); + DEBUGADD(10,("info3->defaultdatatype [%s]\n", info3->defaultdatatype)); + for (i=0; info3->dependentfiles && *info3->dependentfiles[i]; i++) { + DEBUGADD(10,("info3->dependentfiles [%s]\n", info3->dependentfiles[i])); + } + DEBUGADD(10,("info3->environment [%s]\n", info3->environment)); + DEBUGADD(10,("info3->driverpath [%s]\n", info3->driverpath)); + DEBUGADD(10,("info3->configfile [%s]\n", info3->configfile)); + + /*pstrcat(line, info3->name); pstrcat(line, ":");*/ + trim_string(info3->driverpath, "\\print$\\WIN40\\0\\", 0); + pstrcat(line, info3->driverpath); + pstrcat(line, ":"); + trim_string(info3->datafile, "\\print$\\WIN40\\0\\", 0); + pstrcat(line, info3->datafile); + pstrcat(line, ":"); + trim_string(info3->helpfile, "\\print$\\WIN40\\0\\", 0); + pstrcat(line, info3->helpfile); + pstrcat(line, ":"); + trim_string(info3->monitorname, "\\print$\\WIN40\\0\\", 0); + pstrcat(line, info3->monitorname); + pstrcat(line, ":"); + pstrcat(line, "RAW"); /*info3->defaultdatatype);*/ + pstrcat(line, ":"); + + for (i=0; info3->dependentfiles && *info3->dependentfiles[i]; i++) { + if (i) + pstrcat(line, ","); /* don't end in a "," */ + trim_string(info3->dependentfiles[i], "\\print$\\WIN40\\0\\", 0); + pstrcat(line, info3->dependentfiles[i]); + } + + SAFE_FREE(info3); + + return True; +} + +/**************************************************************************** + Debugging function, dump at level 6 the struct in the logs. +****************************************************************************/ + +static uint32 dump_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level) +{ + uint32 result; + NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3; + int i; + + DEBUG(106,("Dumping printer driver at level [%d]\n", level)); + + switch (level) + { + case 3: + { + if (driver.info_3 == NULL) + result=5; + else { + info3=driver.info_3; + + DEBUGADD(106,("version:[%d]\n", info3->cversion)); + DEBUGADD(106,("name:[%s]\n", info3->name)); + DEBUGADD(106,("environment:[%s]\n", info3->environment)); + DEBUGADD(106,("driverpath:[%s]\n", info3->driverpath)); + DEBUGADD(106,("datafile:[%s]\n", info3->datafile)); + DEBUGADD(106,("configfile:[%s]\n", info3->configfile)); + DEBUGADD(106,("helpfile:[%s]\n", info3->helpfile)); + DEBUGADD(106,("monitorname:[%s]\n", info3->monitorname)); + DEBUGADD(106,("defaultdatatype:[%s]\n", info3->defaultdatatype)); + + for (i=0; info3->dependentfiles && + *info3->dependentfiles[i]; i++) { + DEBUGADD(106,("dependentfile:[%s]\n", + info3->dependentfiles[i])); + } + result=0; + } + break; + } + default: + DEBUGADD(106,("dump_a_printer_driver: Level %u not implemented\n", (unsigned int)level)); + result=1; + break; + } + + return result; +} + +/**************************************************************************** +****************************************************************************/ +static int pack_devicemode(NT_DEVICEMODE *nt_devmode, char *buf, int buflen) +{ + int len = 0; + + len += tdb_pack(buf+len, buflen-len, "p", nt_devmode); + + if (!nt_devmode) return len; + + len += tdb_pack(buf+len, buflen-len, "ffwwwwwwwwwwwwwwwwwwddddddddddddddp", + nt_devmode->devicename, + nt_devmode->formname, + + nt_devmode->specversion, + nt_devmode->driverversion, + nt_devmode->size, + nt_devmode->driverextra, + nt_devmode->orientation, + nt_devmode->papersize, + nt_devmode->paperlength, + nt_devmode->paperwidth, + nt_devmode->scale, + nt_devmode->copies, + nt_devmode->defaultsource, + nt_devmode->printquality, + nt_devmode->color, + nt_devmode->duplex, + nt_devmode->yresolution, + nt_devmode->ttoption, + nt_devmode->collate, + nt_devmode->logpixels, + + nt_devmode->fields, + nt_devmode->bitsperpel, + nt_devmode->pelswidth, + nt_devmode->pelsheight, + nt_devmode->displayflags, + nt_devmode->displayfrequency, + nt_devmode->icmmethod, + nt_devmode->icmintent, + nt_devmode->mediatype, + nt_devmode->dithertype, + nt_devmode->reserved1, + nt_devmode->reserved2, + nt_devmode->panningwidth, + nt_devmode->panningheight, + nt_devmode->private); + + + if (nt_devmode->private) { + len += tdb_pack(buf+len, buflen-len, "B", + nt_devmode->driverextra, + nt_devmode->private); + } + + DEBUG(8,("Packed devicemode [%s]\n", nt_devmode->formname)); + + return len; +} + +/**************************************************************************** +****************************************************************************/ +static int pack_specifics(NT_PRINTER_PARAM *param, char *buf, int buflen) +{ + int len = 0; + + while (param != NULL) { + len += tdb_pack(buf+len, buflen-len, "pfdB", + param, + param->value, + param->type, + param->data_len, + param->data); + param=param->next; + } + + len += tdb_pack(buf+len, buflen-len, "p", param); + + return len; +} + + +/**************************************************************************** + Delete a printer - this just deletes the printer info file, any open + handles are not affected. +****************************************************************************/ + +uint32 del_a_printer(char *sharename) +{ + pstring key; + TDB_DATA kbuf; + + slprintf(key, sizeof(key)-1, "%s%s", PRINTERS_PREFIX, sharename); + + kbuf.dptr=key; + kbuf.dsize=strlen(key)+1; + + tdb_delete(tdb_printers, kbuf); + return 0; +} + +/* FIXME!!! Reorder so this forward declaration is not necessary --jerry */ +static WERROR get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **, fstring); +static void free_nt_printer_info_level_2(NT_PRINTER_INFO_LEVEL_2 **); +/**************************************************************************** +****************************************************************************/ +static WERROR update_a_printer_2(NT_PRINTER_INFO_LEVEL_2 *info) +{ + pstring key; + char *buf; + int buflen, len; + WERROR ret; + TDB_DATA kbuf, dbuf; + + /* + * in addprinter: no servername and the printer is the name + * in setprinter: servername is \\server + * and printer is \\server\\printer + * + * Samba manages only local printers. + * we currently don't support things like path=\\other_server\printer + */ + + if (info->servername[0]!='\0') { + trim_string(info->printername, info->servername, NULL); + trim_string(info->printername, "\\", NULL); + info->servername[0]='\0'; + } + + /* + * JFM: one day I'll forget. + * below that's info->portname because that's the SAMBA sharename + * and I made NT 'thinks' it's the portname + * the info->sharename is the thing you can name when you add a printer + * that's the short-name when you create shared printer for 95/98 + * So I've made a limitation in SAMBA: you can only have 1 printer model + * behind a SAMBA share. + */ + + buf = NULL; + buflen = 0; + + again: + len = 0; + len += tdb_pack(buf+len, buflen-len, "dddddddddddfffffPfffff", + info->attributes, + info->priority, + info->default_priority, + info->starttime, + info->untiltime, + info->status, + info->cjobs, + info->averageppm, + info->changeid, + info->c_setprinter, + info->setuptime, + info->servername, + info->printername, + info->sharename, + info->portname, + info->drivername, + info->comment, + info->location, + info->sepfile, + info->printprocessor, + info->datatype, + info->parameters); + + len += pack_devicemode(info->devmode, buf+len, buflen-len); + + len += pack_specifics(info->specific, buf+len, buflen-len); + + if (buflen != len) { + char *tb; + + tb = (char *)Realloc(buf, len); + if (!tb) { + DEBUG(0,("update_a_printer_2: failed to enlarge buffer!\n")); + ret = WERR_NOMEM; + goto done; + } + else buf = tb; + buflen = len; + goto again; + } + + + slprintf(key, sizeof(key)-1, "%s%s", PRINTERS_PREFIX, info->sharename); + + kbuf.dptr = key; + kbuf.dsize = strlen(key)+1; + dbuf.dptr = buf; + dbuf.dsize = len; + + ret = (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) == 0? WERR_OK : WERR_NOMEM); + +done: + if (!W_ERROR_IS_OK(ret)) + DEBUG(8, ("error updating printer to tdb on disk\n")); + + SAFE_FREE(buf); + + DEBUG(8,("packed printer [%s] with driver [%s] portname=[%s] len=%d\n", + info->sharename, info->drivername, info->portname, len)); + + return ret; +} + + +/**************************************************************************** +****************************************************************************/ +void add_a_specific_param(NT_PRINTER_INFO_LEVEL_2 *info_2, NT_PRINTER_PARAM **param) +{ + NT_PRINTER_PARAM *current; + + DEBUG(108,("add_a_specific_param\n")); + + (*param)->next=NULL; + + if (info_2->specific == NULL) + { + info_2->specific=*param; + } + else + { + current=info_2->specific; + while (current->next != NULL) { + current=current->next; + } + current->next=*param; + } + + *param = NULL; +} + +/**************************************************************************** +****************************************************************************/ +BOOL unlink_specific_param_if_exist(NT_PRINTER_INFO_LEVEL_2 *info_2, NT_PRINTER_PARAM *param) +{ + NT_PRINTER_PARAM *current; + NT_PRINTER_PARAM *previous; + + current=info_2->specific; + previous=current; + + if (current==NULL) return (False); + + if ( !strcmp(current->value, param->value) && + (strlen(current->value)==strlen(param->value)) ) { + DEBUG(109,("deleting first value\n")); + info_2->specific=current->next; + SAFE_FREE(current->data); + SAFE_FREE(current); + DEBUG(109,("deleted first value\n")); + return (True); + } + + current=previous->next; + + while ( current!=NULL ) { + if (!strcmp(current->value, param->value) && + strlen(current->value)==strlen(param->value) ) { + DEBUG(109,("deleting current value\n")); + previous->next=current->next; + SAFE_FREE(current->data); + SAFE_FREE(current); + DEBUG(109,("deleted current value\n")); + return(True); + } + + previous=previous->next; + current=current->next; + } + return (False); +} + +/**************************************************************************** + Clean up and deallocate a (maybe partially) allocated NT_PRINTER_PARAM. +****************************************************************************/ +void free_nt_printer_param(NT_PRINTER_PARAM **param_ptr) +{ + NT_PRINTER_PARAM *param = *param_ptr; + + if(param == NULL) + return; + + DEBUG(106,("free_nt_printer_param: deleting param [%s]\n", param->value)); + + SAFE_FREE(param->data); + SAFE_FREE(*param_ptr); +} + +/**************************************************************************** + Malloc and return an NT devicemode. +****************************************************************************/ + +NT_DEVICEMODE *construct_nt_devicemode(const fstring default_devicename) +{ + + char adevice[32]; + NT_DEVICEMODE *nt_devmode = (NT_DEVICEMODE *)malloc(sizeof(NT_DEVICEMODE)); + + if (nt_devmode == NULL) { + DEBUG(0,("construct_nt_devicemode: malloc fail.\n")); + return NULL; + } + + ZERO_STRUCTP(nt_devmode); + + safe_strcpy(adevice, default_devicename, sizeof(adevice)); + fstrcpy(nt_devmode->devicename, adevice); + + fstrcpy(nt_devmode->formname, "Letter"); + + nt_devmode->specversion = 0x0401; + nt_devmode->driverversion = 0x0400; + nt_devmode->size = 0x00DC; + nt_devmode->driverextra = 0x0000; + nt_devmode->fields = FORMNAME | TTOPTION | PRINTQUALITY | + DEFAULTSOURCE | COPIES | SCALE | + PAPERSIZE | ORIENTATION; + nt_devmode->orientation = 1; + nt_devmode->papersize = PAPER_LETTER; + nt_devmode->paperlength = 0; + nt_devmode->paperwidth = 0; + nt_devmode->scale = 0x64; + nt_devmode->copies = 1; + nt_devmode->defaultsource = BIN_FORMSOURCE; + nt_devmode->printquality = RES_HIGH; /* 0x0258 */ + nt_devmode->color = COLOR_MONOCHROME; + nt_devmode->duplex = DUP_SIMPLEX; + nt_devmode->yresolution = 0; + nt_devmode->ttoption = TT_SUBDEV; + nt_devmode->collate = COLLATE_FALSE; + nt_devmode->icmmethod = 0; + nt_devmode->icmintent = 0; + nt_devmode->mediatype = 0; + nt_devmode->dithertype = 0; + + /* non utilisés par un driver d'imprimante */ + nt_devmode->logpixels = 0; + nt_devmode->bitsperpel = 0; + nt_devmode->pelswidth = 0; + nt_devmode->pelsheight = 0; + nt_devmode->displayflags = 0; + nt_devmode->displayfrequency = 0; + nt_devmode->reserved1 = 0; + nt_devmode->reserved2 = 0; + nt_devmode->panningwidth = 0; + nt_devmode->panningheight = 0; + + nt_devmode->private = NULL; + return nt_devmode; +} + +/**************************************************************************** + Deepcopy an NT devicemode. +****************************************************************************/ + +NT_DEVICEMODE *dup_nt_devicemode(NT_DEVICEMODE *nt_devicemode) +{ + NT_DEVICEMODE *new_nt_devicemode = NULL; + + if ((new_nt_devicemode = (NT_DEVICEMODE *)memdup(nt_devicemode, sizeof(NT_DEVICEMODE))) == NULL) { + DEBUG(0,("dup_nt_devicemode: malloc fail.\n")); + return NULL; + } + + new_nt_devicemode->private = NULL; + if (nt_devicemode->private != NULL) { + if ((new_nt_devicemode->private = memdup(nt_devicemode->private, nt_devicemode->driverextra)) == NULL) { + SAFE_FREE(new_nt_devicemode); + DEBUG(0,("dup_nt_devicemode: malloc fail.\n")); + return NULL; + } + } + + return new_nt_devicemode; +} + +/**************************************************************************** + Clean up and deallocate a (maybe partially) allocated NT_DEVICEMODE. +****************************************************************************/ + +void free_nt_devicemode(NT_DEVICEMODE **devmode_ptr) +{ + NT_DEVICEMODE *nt_devmode = *devmode_ptr; + + if(nt_devmode == NULL) + return; + + DEBUG(106,("free_nt_devicemode: deleting DEVMODE\n")); + + SAFE_FREE(nt_devmode->private); + SAFE_FREE(*devmode_ptr); +} + +/**************************************************************************** + Clean up and deallocate a (maybe partially) allocated NT_PRINTER_INFO_LEVEL_2. +****************************************************************************/ +static void free_nt_printer_info_level_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr) +{ + NT_PRINTER_INFO_LEVEL_2 *info = *info_ptr; + NT_PRINTER_PARAM *param_ptr; + + if(info == NULL) + return; + + DEBUG(106,("free_nt_printer_info_level_2: deleting info\n")); + + free_nt_devicemode(&info->devmode); + + for(param_ptr = info->specific; param_ptr; ) { + NT_PRINTER_PARAM *tofree = param_ptr; + + param_ptr = param_ptr->next; + free_nt_printer_param(&tofree); + } + + SAFE_FREE(*info_ptr); +} + + +/**************************************************************************** +****************************************************************************/ +static int unpack_devicemode(NT_DEVICEMODE **nt_devmode, char *buf, int buflen) +{ + int len = 0; + int extra_len = 0; + NT_DEVICEMODE devmode; + + ZERO_STRUCT(devmode); + + len += tdb_unpack(buf+len, buflen-len, "p", nt_devmode); + + if (!*nt_devmode) return len; + + len += tdb_unpack(buf+len, buflen-len, "ffwwwwwwwwwwwwwwwwwwddddddddddddddp", + devmode.devicename, + devmode.formname, + + &devmode.specversion, + &devmode.driverversion, + &devmode.size, + &devmode.driverextra, + &devmode.orientation, + &devmode.papersize, + &devmode.paperlength, + &devmode.paperwidth, + &devmode.scale, + &devmode.copies, + &devmode.defaultsource, + &devmode.printquality, + &devmode.color, + &devmode.duplex, + &devmode.yresolution, + &devmode.ttoption, + &devmode.collate, + &devmode.logpixels, + + &devmode.fields, + &devmode.bitsperpel, + &devmode.pelswidth, + &devmode.pelsheight, + &devmode.displayflags, + &devmode.displayfrequency, + &devmode.icmmethod, + &devmode.icmintent, + &devmode.mediatype, + &devmode.dithertype, + &devmode.reserved1, + &devmode.reserved2, + &devmode.panningwidth, + &devmode.panningheight, + &devmode.private); + + if (devmode.private) { + /* the len in tdb_unpack is an int value and + * devmode.driverextra is only a short + */ + len += tdb_unpack(buf+len, buflen-len, "B", &extra_len, &devmode.private); + devmode.driverextra=(uint16)extra_len; + + /* check to catch an invalid TDB entry so we don't segfault */ + if (devmode.driverextra == 0) { + devmode.private = NULL; + } + } + + *nt_devmode = (NT_DEVICEMODE *)memdup(&devmode, sizeof(devmode)); + + DEBUG(8,("Unpacked devicemode [%s](%s)\n", devmode.devicename, devmode.formname)); + if (devmode.private) + DEBUG(8,("with a private section of %d bytes\n", devmode.driverextra)); + + return len; +} + +/**************************************************************************** +****************************************************************************/ +static int unpack_specifics(NT_PRINTER_PARAM **list, char *buf, int buflen) +{ + int len = 0; + NT_PRINTER_PARAM param, *p; + + *list = NULL; + + while (1) { + len += tdb_unpack(buf+len, buflen-len, "p", &p); + if (!p) break; + + len += tdb_unpack(buf+len, buflen-len, "fdB", + param.value, + ¶m.type, + ¶m.data_len, + ¶m.data); + param.next = *list; + *list = memdup(¶m, sizeof(param)); + + DEBUG(8,("specific: [%s], len: %d\n", param.value, param.data_len)); + } + + return len; +} + +static void map_to_os2_driver(fstring drivername) +{ + static BOOL initialised=False; + static fstring last_from,last_to; + char *mapfile = lp_os2_driver_map(); + char **lines = NULL; + int numlines = 0; + int i; + + if (!strlen(drivername)) + return; + + if (!*mapfile) + return; + + if (!initialised) { + *last_from = *last_to = 0; + initialised = True; + } + + if (strequal(drivername,last_from)) { + DEBUG(3,("Mapped Windows driver %s to OS/2 driver %s\n",drivername,last_to)); + fstrcpy(drivername,last_to); + return; + } + + lines = file_lines_load(mapfile, &numlines); + if (numlines == 0) { + DEBUG(0,("No entries in OS/2 driver map %s\n",mapfile)); + return; + } + + DEBUG(4,("Scanning OS/2 driver map %s\n",mapfile)); + + for( i = 0; i < numlines; i++) { + char *nt_name = lines[i]; + char *os2_name = strchr(nt_name,'='); + + if (!os2_name) + continue; + + *os2_name++ = 0; + + while (isspace(*nt_name)) + nt_name++; + + if (!*nt_name || strchr("#;",*nt_name)) + continue; + + { + int l = strlen(nt_name); + while (l && isspace(nt_name[l-1])) { + nt_name[l-1] = 0; + l--; + } + } + + while (isspace(*os2_name)) + os2_name++; + + { + int l = strlen(os2_name); + while (l && isspace(os2_name[l-1])) { + os2_name[l-1] = 0; + l--; + } + } + + if (strequal(nt_name,drivername)) { + DEBUG(3,("Mapped windows driver %s to os2 driver%s\n",drivername,os2_name)); + fstrcpy(last_from,drivername); + fstrcpy(last_to,os2_name); + fstrcpy(drivername,os2_name); + file_lines_free(lines); + return; + } + } + + file_lines_free(lines); +} + +/**************************************************************************** +get a default printer info 2 struct +****************************************************************************/ +static WERROR get_a_printer_2_default(NT_PRINTER_INFO_LEVEL_2 **info_ptr, fstring sharename) +{ + int snum; + NT_PRINTER_INFO_LEVEL_2 info; + + ZERO_STRUCT(info); + + snum = lp_servicenumber(sharename); + + slprintf(info.servername, sizeof(info.servername)-1, "\\\\%s", get_called_name()); + slprintf(info.printername, sizeof(info.printername)-1, "\\\\%s\\%s", + get_called_name(), sharename); + fstrcpy(info.sharename, sharename); + fstrcpy(info.portname, SAMBA_PRINTER_PORT_NAME); + fstrcpy(info.drivername, lp_printerdriver(snum)); + + /* by setting the driver name to an empty string, a local NT admin + can now run the **local** APW to install a local printer driver + for a Samba shared printer in 2.2. Without this, drivers **must** be + installed on the Samba server for NT clients --jerry */ +#if 0 /* JERRY --do not uncomment-- */ + if (!*info.drivername) + fstrcpy(info.drivername, "NO DRIVER AVAILABLE FOR THIS PRINTER"); +#endif + + + DEBUG(10,("get_a_printer_2_default: driver name set to [%s]\n", info.drivername)); + + pstrcpy(info.comment, ""); + fstrcpy(info.printprocessor, "winprint"); + fstrcpy(info.datatype, "RAW"); + + info.attributes = PRINTER_ATTRIBUTE_SHARED | PRINTER_ATTRIBUTE_NETWORK; /* attributes */ + + info.starttime = 0; /* Minutes since 12:00am GMT */ + info.untiltime = 0; /* Minutes since 12:00am GMT */ + info.priority = 1; + info.default_priority = 1; + info.setuptime = (uint32)time(NULL); + + /* + * I changed this as I think it is better to have a generic + * DEVMODE than to crash Win2k explorer.exe --jerry + * See the HP Deskjet 990c Win2k drivers for an example. + * + * However the default devmode appears to cause problems + * with the HP CLJ 8500 PCL driver. Hence the addition of + * the "default devmode" parameter --jerry 22/01/2002 + */ + + if (lp_default_devmode(snum)) { + if ((info.devmode = construct_nt_devicemode(info.printername)) == NULL) + goto fail; + } + else { + info.devmode = NULL; + } + + /* This will get the current RPC talloc context, but we should be + passing this as a parameter... fixme... JRA ! */ + + if (!nt_printing_getsec(get_talloc_ctx(), sharename, &info.secdesc_buf)) + goto fail; + + *info_ptr = (NT_PRINTER_INFO_LEVEL_2 *)memdup(&info, sizeof(info)); + if (! *info_ptr) { + DEBUG(0,("get_a_printer_2_default: malloc fail.\n")); + goto fail; + } + + return WERR_OK; + + fail: + if (info.devmode) + free_nt_devicemode(&info.devmode); + return WERR_ACCESS_DENIED; +} + +/**************************************************************************** +****************************************************************************/ +static WERROR get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr, fstring sharename) +{ + pstring key; + NT_PRINTER_INFO_LEVEL_2 info; + int len = 0; + TDB_DATA kbuf, dbuf; + fstring printername; + + ZERO_STRUCT(info); + + slprintf(key, sizeof(key)-1, "%s%s", PRINTERS_PREFIX, sharename); + + kbuf.dptr = key; + kbuf.dsize = strlen(key)+1; + + dbuf = tdb_fetch(tdb_printers, kbuf); + if (!dbuf.dptr) + return get_a_printer_2_default(info_ptr, sharename); + + len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "dddddddddddfffffPfffff", + &info.attributes, + &info.priority, + &info.default_priority, + &info.starttime, + &info.untiltime, + &info.status, + &info.cjobs, + &info.averageppm, + &info.changeid, + &info.c_setprinter, + &info.setuptime, + info.servername, + info.printername, + info.sharename, + info.portname, + info.drivername, + info.comment, + info.location, + info.sepfile, + info.printprocessor, + info.datatype, + info.parameters); + + /* Samba has to have shared raw drivers. */ + info.attributes |= (PRINTER_ATTRIBUTE_SHARED | PRINTER_ATTRIBUTE_NETWORK); + + /* Restore the stripped strings. */ + slprintf(info.servername, sizeof(info.servername)-1, "\\\\%s", get_called_name()); + slprintf(printername, sizeof(printername)-1, "\\\\%s\\%s", get_called_name(), + info.printername); + fstrcpy(info.printername, printername); + + len += unpack_devicemode(&info.devmode,dbuf.dptr+len, dbuf.dsize-len); + + /* + * Some client drivers freak out if there is a NULL devmode + * (probably the driver is not checking before accessing + * the devmode pointer) --jerry + * + * See comments in get_a_printer_2_default() + */ + + if (lp_default_devmode(lp_servicenumber(sharename)) && !info.devmode) + { + DEBUG(8,("get_a_printer_2: Constructing a default device mode for [%s]\n", + printername)); + info.devmode = construct_nt_devicemode(printername); + } + + len += unpack_specifics(&info.specific,dbuf.dptr+len, dbuf.dsize-len); + + /* This will get the current RPC talloc context, but we should be + passing this as a parameter... fixme... JRA ! */ + + nt_printing_getsec(get_talloc_ctx(), sharename, &info.secdesc_buf); + + /* Fix for OS/2 drivers. */ + + if (get_remote_arch() == RA_OS2) + map_to_os2_driver(info.drivername); + + SAFE_FREE(dbuf.dptr); + *info_ptr=memdup(&info, sizeof(info)); + + DEBUG(9,("Unpacked printer [%s] name [%s] running driver [%s]\n", + sharename, info.printername, info.drivername)); + + return WERR_OK; +} + +/**************************************************************************** +debugging function, dump at level 6 the struct in the logs +****************************************************************************/ +static uint32 dump_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level) +{ + uint32 result; + NT_PRINTER_INFO_LEVEL_2 *info2; + + DEBUG(106,("Dumping printer at level [%d]\n", level)); + + switch (level) + { + case 2: + { + if (printer.info_2 == NULL) + result=5; + else + { + info2=printer.info_2; + + DEBUGADD(106,("attributes:[%d]\n", info2->attributes)); + DEBUGADD(106,("priority:[%d]\n", info2->priority)); + DEBUGADD(106,("default_priority:[%d]\n", info2->default_priority)); + DEBUGADD(106,("starttime:[%d]\n", info2->starttime)); + DEBUGADD(106,("untiltime:[%d]\n", info2->untiltime)); + DEBUGADD(106,("status:[%d]\n", info2->status)); + DEBUGADD(106,("cjobs:[%d]\n", info2->cjobs)); + DEBUGADD(106,("averageppm:[%d]\n", info2->averageppm)); + DEBUGADD(106,("changeid:[%d]\n", info2->changeid)); + DEBUGADD(106,("c_setprinter:[%d]\n", info2->c_setprinter)); + DEBUGADD(106,("setuptime:[%d]\n", info2->setuptime)); + + DEBUGADD(106,("servername:[%s]\n", info2->servername)); + DEBUGADD(106,("printername:[%s]\n", info2->printername)); + DEBUGADD(106,("sharename:[%s]\n", info2->sharename)); + DEBUGADD(106,("portname:[%s]\n", info2->portname)); + DEBUGADD(106,("drivername:[%s]\n", info2->drivername)); + DEBUGADD(106,("comment:[%s]\n", info2->comment)); + DEBUGADD(106,("location:[%s]\n", info2->location)); + DEBUGADD(106,("sepfile:[%s]\n", info2->sepfile)); + DEBUGADD(106,("printprocessor:[%s]\n", info2->printprocessor)); + DEBUGADD(106,("datatype:[%s]\n", info2->datatype)); + DEBUGADD(106,("parameters:[%s]\n", info2->parameters)); + result=0; + } + break; + } + default: + DEBUGADD(106,("dump_a_printer: Level %u not implemented\n", (unsigned int)level )); + result=1; + break; + } + + return result; +} + +/**************************************************************************** + Get the parameters we can substitute in an NT print job. +****************************************************************************/ + +void get_printer_subst_params(int snum, fstring *printername, fstring *sharename, fstring *portname) +{ + NT_PRINTER_INFO_LEVEL *printer = NULL; + + **printername = **sharename = **portname = '\0'; + + if (!W_ERROR_IS_OK(get_a_printer(&printer, 2, lp_servicename(snum)))) + return; + + fstrcpy(*printername, printer->info_2->printername); + fstrcpy(*sharename, printer->info_2->sharename); + fstrcpy(*portname, printer->info_2->portname); + + free_a_printer(&printer, 2); +} + +/**************************************************************************** + Update the changeid time. + This is SO NASTY as some drivers need this to change, others need it + static. This value will change every second, and I must hope that this + is enough..... DON'T CHANGE THIS CODE WITHOUT A TEST MATRIX THE SIZE OF + UTAH ! JRA. +****************************************************************************/ + +static uint32 rev_changeid(void) +{ + struct timeval tv; + + get_process_uptime(&tv); + +#if 1 /* JERRY */ + /* Return changeid as msec since spooler restart */ + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +#else + /* + * This setting seems to work well but is too untested + * to replace the above calculation. Left in for experiementation + * of the reader --jerry (Tue Mar 12 09:15:05 CST 2002) + */ + return tv.tv_sec * 10 + tv.tv_usec / 100000; +#endif +} + +/* + * The function below are the high level ones. + * only those ones must be called from the spoolss code. + * JFM. + */ + +/**************************************************************************** + Modify a printer. This is called from SETPRINTERDATA/DELETEPRINTERDATA. +****************************************************************************/ + +WERROR mod_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level) +{ + WERROR result; + + dump_a_printer(printer, level); + + switch (level) + { + case 2: + { + /* + * Update the changestamp. Emperical tests show that the + * ChangeID is always updated,but c_setprinter is + * global spooler variable (not per printer). + */ + + /* ChangeID **must** be increasing over the lifetime + of client's spoolss service in order for the + client's cache to show updates */ + + printer.info_2->changeid = rev_changeid(); + + /* + * Because one day someone will ask: + * NT->NT An admin connection to a remote + * printer show changes imeediately in + * the properities dialog + * + * A non-admin connection will only show the + * changes after viewing the properites page + * 2 times. Seems to be related to a + * race condition in the client between the spooler + * updating the local cache and the Explorer.exe GUI + * actually displaying the properties. + * + * This is fixed in Win2k. admin/non-admin + * connections both display changes immediately. + * + * 14/12/01 --jerry + */ + + result=update_a_printer_2(printer.info_2); + break; + } + default: + result=WERR_UNKNOWN_LEVEL; + break; + } + + return result; +} + +/**************************************************************************** + Initialize printer devmode & data with previously saved driver init values. +****************************************************************************/ + +static uint32 set_driver_init_2(NT_PRINTER_INFO_LEVEL_2 *info_ptr) +{ + int len = 0; + pstring key; + TDB_DATA kbuf, dbuf; + NT_PRINTER_PARAM *current; + NT_PRINTER_INFO_LEVEL_2 info; + + /* + * Delete any printer data 'specifics' already set. When called for driver + * replace, there will generally be some, but during an add printer, there + * should not be any (if there are delete them). + */ + while ( (current=info_ptr->specific) != NULL ) { + info_ptr->specific=current->next; + SAFE_FREE(current->data); + SAFE_FREE(current); + } + + ZERO_STRUCT(info); + + slprintf(key, sizeof(key)-1, "%s%s", DRIVER_INIT_PREFIX, info_ptr->drivername); + + kbuf.dptr = key; + kbuf.dsize = strlen(key)+1; + + dbuf = tdb_fetch(tdb_drivers, kbuf); + if (!dbuf.dptr) { + /* + * When changing to a driver that has no init info in the tdb, remove + * the previous drivers init info and leave the new on blank. + */ + free_nt_devicemode(&info_ptr->devmode); + return False; + } + + /* + * Get the saved DEVMODE.. + */ + len += unpack_devicemode(&info.devmode,dbuf.dptr+len, dbuf.dsize-len); + + /* + * The saved DEVMODE contains the devicename from the printer used during + * the initialization save. Change it to reflect the new printer. + */ + ZERO_STRUCT(info.devmode->devicename); + fstrcpy(info.devmode->devicename, info_ptr->printername); + + + /* + * NT/2k does not change out the entire DeviceMode of a printer + * when changing the driver. Only the driverextra, private, & + * driverversion fields. --jerry (Thu Mar 14 08:58:43 CST 2002) + */ + +#if 0 /* JERRY */ + + /* + * Bind the saved DEVMODE to the new the printer. + */ + free_nt_devicemode(&info_ptr->devmode); + info_ptr->devmode = info.devmode; +#else + /* copy the entire devmode if we currently don't have one */ + + if (!info_ptr->devmode) { + DEBUG(10,("set_driver_init_2: Current Devmode is NULL. Copying entire Device Mode\n")); + info_ptr->devmode = info.devmode; + } + else { + /* only set the necessary fields */ + + DEBUG(10,("set_driver_init_2: Setting driverversion [0x%x] and private data [0x%x]\n", + info.devmode->driverversion, info.devmode->driverextra)); + + info_ptr->devmode->driverversion = info.devmode->driverversion; + + SAFE_FREE(info_ptr->devmode->private); + info_ptr->devmode->private = NULL; + + if (info.devmode->driverversion) + info_ptr->devmode->private = memdup(info.devmode->private, info.devmode->driverversion); + + free_nt_devicemode(&info.devmode); + } +#endif + + DEBUG(10,("set_driver_init_2: Set printer [%s] init DEVMODE for driver [%s]\n", + info_ptr->printername, info_ptr->drivername)); + + /* + * Add the printer data 'specifics' to the new printer + */ + len += unpack_specifics(&info_ptr->specific,dbuf.dptr+len, dbuf.dsize-len); + + SAFE_FREE(dbuf.dptr); + + return True; +} + +/**************************************************************************** + Initialize printer devmode & data with previously saved driver init values. + When a printer is created using AddPrinter, the drivername bound to the + printer is used to lookup previously saved driver initialization info, which + is bound to the new printer. +****************************************************************************/ + +uint32 set_driver_init(NT_PRINTER_INFO_LEVEL *printer, uint32 level) +{ + uint32 result; + + switch (level) + { + case 2: + { + result=set_driver_init_2(printer->info_2); + break; + } + default: + result=1; + break; + } + + return result; +} + +/**************************************************************************** + Pack up the DEVMODE and specifics for a printer into a 'driver init' entry + in the tdb. Note: this is different from the driver entry and the printer + entry. There should be a single driver init entry for each driver regardless + of whether it was installed from NT or 2K. Technically, they should be + different, but they work out to the same struct. +****************************************************************************/ + +static uint32 update_driver_init_2(NT_PRINTER_INFO_LEVEL_2 *info) +{ + pstring key; + char *buf; + int buflen, len, ret; + TDB_DATA kbuf, dbuf; + + buf = NULL; + buflen = 0; + + again: + len = 0; + len += pack_devicemode(info->devmode, buf+len, buflen-len); + + len += pack_specifics(info->specific, buf+len, buflen-len); + + if (buflen != len) { + char *tb; + + tb = (char *)Realloc(buf, len); + if (!tb) { + DEBUG(0, ("update_driver_init_2: failed to enlarge buffer!\n")); + ret = -1; + goto done; + } + else buf = tb; + buflen = len; + goto again; + } + + slprintf(key, sizeof(key)-1, "%s%s", DRIVER_INIT_PREFIX, info->drivername); + + kbuf.dptr = key; + kbuf.dsize = strlen(key)+1; + dbuf.dptr = buf; + dbuf.dsize = len; + + ret = tdb_store(tdb_drivers, kbuf, dbuf, TDB_REPLACE); + +done: + if (ret == -1) + DEBUG(8, ("update_driver_init_2: error updating printer init to tdb on disk\n")); + + SAFE_FREE(buf); + + DEBUG(10,("update_driver_init_2: Saved printer [%s] init DEVMODE & specifics for driver [%s]\n", + info->sharename, info->drivername)); + + return ret; +} + +/**************************************************************************** + Update (i.e. save) the driver init info (DEVMODE and specifics) for a printer +****************************************************************************/ + +uint32 update_driver_init(NT_PRINTER_INFO_LEVEL printer, uint32 level) +{ + uint32 result; + + dump_a_printer(printer, level); + + switch (level) + { + case 2: + { + result=update_driver_init_2(printer.info_2); + break; + } + default: + result=1; + break; + } + + return result; +} + +/**************************************************************************** + Convert the printer data value, a REG_BINARY array, into an initialization + DEVMODE. Note: the array must be parsed as if it was a DEVMODE in an rpc... + got to keep the endians happy :). +****************************************************************************/ + +static BOOL convert_driver_init(NT_PRINTER_PARAM *param, TALLOC_CTX *ctx, NT_DEVICEMODE *nt_devmode) +{ + BOOL result = False; + prs_struct ps; + DEVICEMODE devmode; + + ZERO_STRUCT(devmode); + + prs_init(&ps, 0, ctx, UNMARSHALL); + ps.data_p = (char *)param->data; + ps.buffer_size = param->data_len; + + if (spoolss_io_devmode("phantom DEVMODE", &ps, 0, &devmode)) + result = convert_devicemode("", &devmode, &nt_devmode); + else + DEBUG(10,("convert_driver_init: error parsing DEVMODE\n")); + + return result; +} + +/**************************************************************************** + Set the DRIVER_INIT info in the tdb. Requires Win32 client code that: + + 1. Use the driver's config DLL to this UNC printername and: + a. Call DrvPrintEvent with PRINTER_EVENT_INITIALIZE + b. Call DrvConvertDevMode with CDM_DRIVER_DEFAULT to get default DEVMODE + 2. Call SetPrinterData with the 'magic' key and the DEVMODE as data. + + The last step triggers saving the "driver initialization" information for + this printer into the tdb. Later, new printers that use this driver will + have this initialization information bound to them. This simulates the + driver initialization, as if it had run on the Samba server (as it would + have done on NT). + + The Win32 client side code requirement sucks! But until we can run arbitrary + Win32 printer driver code on any Unix that Samba runs on, we are stuck with it. + + It would have been easier to use SetPrinter because all the UNMARSHALLING of + the DEVMODE is done there, but 2K/XP clients do not set the DEVMODE... think + about it and you will realize why. JRR 010720 +****************************************************************************/ + +static WERROR save_driver_init_2(NT_PRINTER_INFO_LEVEL *printer, NT_PRINTER_PARAM *param) +{ + WERROR status = WERR_OK; + TALLOC_CTX *ctx = NULL; + NT_DEVICEMODE *nt_devmode = NULL; + NT_DEVICEMODE *tmp_devmode = printer->info_2->devmode; + + /* + * When the DEVMODE is already set on the printer, don't try to unpack it. + */ + + if (!printer->info_2->devmode && param->data_len) { + /* + * Set devmode on printer info, so entire printer initialization can be + * saved to tdb. + */ + + if ((ctx = talloc_init()) == NULL) + return WERR_NOMEM; + + if ((nt_devmode = (NT_DEVICEMODE*)malloc(sizeof(NT_DEVICEMODE))) == NULL) { + status = WERR_NOMEM; + goto done; + } + + ZERO_STRUCTP(nt_devmode); + + /* + * The DEVMODE is held in the 'data' component of the param in raw binary. + * Convert it to to a devmode structure + */ + if (!convert_driver_init(param, ctx, nt_devmode)) { + DEBUG(10,("save_driver_init_2: error converting DEVMODE\n")); + status = WERR_INVALID_PARAM; + goto done; + } + + printer->info_2->devmode = nt_devmode; + } + + /* + * Pack up and add (or update) the DEVMODE and any current printer data to + * a 'driver init' element in the tdb + * + */ + + if (update_driver_init(*printer, 2)!=0) { + DEBUG(10,("save_driver_init_2: error updating DEVMODE\n")); + status = WERR_NOMEM; + goto done; + } + + /* + * If driver initialization info was successfully saved, set the current + * printer to match it. This allows initialization of the current printer + * as well as the driver. + */ + status = mod_a_printer(*printer, 2); + if (!W_ERROR_IS_OK(status)) { + DEBUG(10,("save_driver_init_2: error setting DEVMODE on printer [%s]\n", + printer->info_2->printername)); + } + +#if 0 /* JERRY */ + srv_spoolss_sendnotify(p, handle); +#endif + + done: + talloc_destroy(ctx); + if (nt_devmode) + SAFE_FREE(nt_devmode->private); + SAFE_FREE(nt_devmode); + printer->info_2->devmode = tmp_devmode; + + return status; +} + +/**************************************************************************** + Update the driver init info (DEVMODE and specifics) for a printer +****************************************************************************/ + +WERROR save_driver_init(NT_PRINTER_INFO_LEVEL *printer, uint32 level, NT_PRINTER_PARAM *param) +{ + WERROR status = WERR_OK; + + switch (level) + { + case 2: + { + status=save_driver_init_2(printer, param); + break; + } + default: + status=WERR_UNKNOWN_LEVEL; + break; + } + + return status; +} + +/**************************************************************************** + Get a NT_PRINTER_INFO_LEVEL struct. It returns malloced memory. +****************************************************************************/ + +WERROR get_a_printer(NT_PRINTER_INFO_LEVEL **pp_printer, uint32 level, fstring sharename) +{ + WERROR result; + NT_PRINTER_INFO_LEVEL *printer = NULL; + + *pp_printer = NULL; + + DEBUG(10,("get_a_printer: [%s] level %u\n", sharename, (unsigned int)level)); + + switch (level) + { + case 2: + { + if ((printer = (NT_PRINTER_INFO_LEVEL *)malloc(sizeof(NT_PRINTER_INFO_LEVEL))) == NULL) { + DEBUG(0,("get_a_printer: malloc fail.\n")); + return WERR_NOMEM; + } + ZERO_STRUCTP(printer); + result=get_a_printer_2(&printer->info_2, sharename); + if (W_ERROR_IS_OK(result)) { + dump_a_printer(*printer, level); + *pp_printer = printer; + } else { + SAFE_FREE(printer); + } + break; + } + default: + result=WERR_UNKNOWN_LEVEL; + break; + } + + DEBUG(10,("get_a_printer: [%s] level %u returning %s\n", sharename, (unsigned int)level, dos_errstr(result))); + + return result; +} + +/**************************************************************************** + Deletes a NT_PRINTER_INFO_LEVEL struct. +****************************************************************************/ + +uint32 free_a_printer(NT_PRINTER_INFO_LEVEL **pp_printer, uint32 level) +{ + uint32 result; + NT_PRINTER_INFO_LEVEL *printer = *pp_printer; + + DEBUG(104,("freeing a printer at level [%d]\n", level)); + + if (printer == NULL) + return 0; + + switch (level) + { + case 2: + { + if (printer->info_2 != NULL) + { + free_nt_printer_info_level_2(&printer->info_2); + result=0; + } + else + { + result=4; + } + break; + } + default: + result=1; + break; + } + + SAFE_FREE(*pp_printer); + return result; +} + +/**************************************************************************** +****************************************************************************/ +uint32 add_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level) +{ + uint32 result; + DEBUG(104,("adding a printer at level [%d]\n", level)); + dump_a_printer_driver(driver, level); + + switch (level) + { + case 3: + { + result=add_a_printer_driver_3(driver.info_3); + break; + } + + case 6: + { + result=add_a_printer_driver_6(driver.info_6); + break; + } + default: + result=1; + break; + } + + return result; +} +/**************************************************************************** +****************************************************************************/ +WERROR get_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL *driver, uint32 level, + fstring printername, fstring architecture, uint32 version) +{ + WERROR result; + + switch (level) + { + case 3: + { + result=get_a_printer_driver_3(&driver->info_3, printername, architecture, version); + break; + } + default: + result=W_ERROR(1); + break; + } + + if (W_ERROR_IS_OK(result)) + dump_a_printer_driver(*driver, level); + return result; +} + +/**************************************************************************** +****************************************************************************/ +uint32 free_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level) +{ + uint32 result; + + switch (level) + { + case 3: + { + NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3; + if (driver.info_3 != NULL) + { + info3=driver.info_3; + SAFE_FREE(info3->dependentfiles); + ZERO_STRUCTP(info3); + SAFE_FREE(info3); + result=0; + } + else + { + result=4; + } + break; + } + case 6: + { + NT_PRINTER_DRIVER_INFO_LEVEL_6 *info6; + if (driver.info_6 != NULL) + { + info6=driver.info_6; + SAFE_FREE(info6->dependentfiles); + SAFE_FREE(info6->previousnames); + ZERO_STRUCTP(info6); + SAFE_FREE(info6); + result=0; + } + else + { + result=4; + } + break; + } + default: + result=1; + break; + } + return result; +} + + +/**************************************************************************** + Determine whether or not a particular driver is currently assigned + to a printer +****************************************************************************/ +BOOL printer_driver_in_use (char *arch, char *driver) +{ + TDB_DATA kbuf, newkey, dbuf; + NT_PRINTER_INFO_LEVEL_2 info; + int ret; + + if (!tdb_printers) + if (!nt_printing_init()) + return False; + + DEBUG(5,("printer_driver_in_use: Beginning search through printers.tdb...\n")); + + /* loop through the printers.tdb and check for the drivername */ + for (kbuf = tdb_firstkey(tdb_printers); kbuf.dptr; + newkey = tdb_nextkey(tdb_printers, kbuf), safe_free(kbuf.dptr), kbuf=newkey) + { + + dbuf = tdb_fetch(tdb_printers, kbuf); + if (!dbuf.dptr) + continue; + + if (strncmp(kbuf.dptr, PRINTERS_PREFIX, strlen(PRINTERS_PREFIX)) != 0) + continue; + + ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddddddddddfffffPfffff", + &info.attributes, + &info.priority, + &info.default_priority, + &info.starttime, + &info.untiltime, + &info.status, + &info.cjobs, + &info.averageppm, + &info.changeid, + &info.c_setprinter, + &info.setuptime, + info.servername, + info.printername, + info.sharename, + info.portname, + info.drivername, + info.comment, + info.location, + info.sepfile, + info.printprocessor, + info.datatype, + info.parameters); + + SAFE_FREE(dbuf.dptr); + + if (ret == -1) { + DEBUG (0,("printer_driver_in_use: tdb_unpack failed for printer %s\n", + info.printername)); + continue; + } + + DEBUG (10,("printer_driver_in_use: Printer - %s (%s)\n", + info.printername, info.drivername)); + + if (strcmp(info.drivername, driver) == 0) + { + DEBUG(5,("printer_driver_in_use: Printer %s using %s\n", + info.printername, driver)); + return True; + } + } + DEBUG(5,("printer_driver_in_use: Completed search through printers.tdb...\n")); + + + + /* report that the driver is in use by default */ + return False; +} + +/**************************************************************************** + Remove a printer driver from the TDB. This assumes that the the driver was + previously looked up. + ***************************************************************************/ +WERROR delete_printer_driver (NT_PRINTER_DRIVER_INFO_LEVEL_3 *i) +{ + pstring key; + fstring arch; + TDB_DATA kbuf; + + + get_short_archi(arch, i->environment); + slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX, + arch, i->cversion, i->name); + DEBUG(5,("delete_printer_driver: key = [%s]\n", key)); + + kbuf.dptr=key; + kbuf.dsize=strlen(key)+1; + + if (tdb_delete(tdb_drivers, kbuf) == -1) { + DEBUG (0,("delete_printer_driver: fail to delete %s!\n", key)); + return WERR_ACCESS_DENIED; + } + + DEBUG(5,("delete_printer_driver: [%s] driver delete successful.\n", + i->name)); + + return WERR_OK; +} +/**************************************************************************** +****************************************************************************/ +BOOL get_specific_param_by_index(NT_PRINTER_INFO_LEVEL printer, uint32 level, uint32 param_index, + fstring value, uint8 **data, uint32 *type, uint32 *len) +{ + /* right now that's enough ! */ + NT_PRINTER_PARAM *param; + int i=0; + + param=printer.info_2->specific; + + while (param != NULL && i < param_index) { + param=param->next; + i++; + } + + if (param == NULL) + return False; + + /* exited because it exist */ + *type=param->type; + StrnCpy(value, param->value, sizeof(fstring)-1); + *data=(uint8 *)malloc(param->data_len*sizeof(uint8)); + if(*data == NULL) + return False; + ZERO_STRUCTP(*data); + memcpy(*data, param->data, param->data_len); + *len=param->data_len; + return True; +} + +/**************************************************************************** +****************************************************************************/ +BOOL get_specific_param(NT_PRINTER_INFO_LEVEL printer, uint32 level, + fstring value, uint8 **data, uint32 *type, uint32 *len) +{ + /* right now that's enough ! */ + NT_PRINTER_PARAM *param; + + DEBUG(10, ("get_specific_param\n")); + + param=printer.info_2->specific; + + while (param != NULL) + { +#if 1 /* JRA - I think this should be case insensitive.... */ + if ( strequal(value, param->value) +#else + if ( !strcmp(value, param->value) +#endif + && strlen(value)==strlen(param->value)) + break; + + param=param->next; + } + + if (param != NULL) + { + DEBUGADD(10, ("get_specific_param: found one param\n")); + /* exited because it exist */ + *type=param->type; + + *data=(uint8 *)malloc(param->data_len*sizeof(uint8)); + if(*data == NULL) + return False; + memcpy(*data, param->data, param->data_len); + *len=param->data_len; + + DEBUGADD(10, ("get_specific_param: exit true\n")); + return (True); + } + DEBUGADD(10, ("get_specific_param: exit false\n")); + return (False); +} + +/**************************************************************************** + Store a security desc for a printer. +****************************************************************************/ + +WERROR nt_printing_setsec(char *printername, SEC_DESC_BUF *secdesc_ctr) +{ + SEC_DESC_BUF *new_secdesc_ctr = NULL; + SEC_DESC_BUF *old_secdesc_ctr = NULL; + prs_struct ps; + TALLOC_CTX *mem_ctx = NULL; + fstring key; + WERROR status; + + mem_ctx = talloc_init(); + if (mem_ctx == NULL) + return WERR_NOMEM; + + /* The old owner and group sids of the security descriptor are not + present when new ACEs are added or removed by changing printer + permissions through NT. If they are NULL in the new security + descriptor then copy them over from the old one. */ + + if (!secdesc_ctr->sec->owner_sid || !secdesc_ctr->sec->grp_sid) { + DOM_SID *owner_sid, *group_sid; + SEC_ACL *dacl, *sacl; + SEC_DESC *psd = NULL; + size_t size; + + nt_printing_getsec(mem_ctx, printername, &old_secdesc_ctr); + + /* Pick out correct owner and group sids */ + + owner_sid = secdesc_ctr->sec->owner_sid ? + secdesc_ctr->sec->owner_sid : + old_secdesc_ctr->sec->owner_sid; + + group_sid = secdesc_ctr->sec->grp_sid ? + secdesc_ctr->sec->grp_sid : + old_secdesc_ctr->sec->grp_sid; + + dacl = secdesc_ctr->sec->dacl ? + secdesc_ctr->sec->dacl : + old_secdesc_ctr->sec->dacl; + + sacl = secdesc_ctr->sec->sacl ? + secdesc_ctr->sec->sacl : + old_secdesc_ctr->sec->sacl; + + /* Make a deep copy of the security descriptor */ + + psd = make_sec_desc(mem_ctx, secdesc_ctr->sec->revision, + owner_sid, group_sid, + sacl, + dacl, + &size); + + new_secdesc_ctr = make_sec_desc_buf(mem_ctx, size, psd); + } + + if (!new_secdesc_ctr) { + new_secdesc_ctr = secdesc_ctr; + } + + /* Store the security descriptor in a tdb */ + + prs_init(&ps, (uint32)sec_desc_size(new_secdesc_ctr->sec) + + sizeof(SEC_DESC_BUF), mem_ctx, MARSHALL); + + if (!sec_io_desc_buf("nt_printing_setsec", &new_secdesc_ctr, + &ps, 1)) { + status = WERR_BADFUNC; + goto out; + } + + slprintf(key, sizeof(key)-1, "SECDESC/%s", printername); + + if (tdb_prs_store(tdb_printers, key, &ps)==0) { + status = WERR_OK; + } else { + DEBUG(1,("Failed to store secdesc for %s\n", printername)); + status = WERR_BADFUNC; + } + + /* Free malloc'ed memory */ + + out: + + prs_mem_free(&ps); + if (mem_ctx) + talloc_destroy(mem_ctx); + return status; +} + +/**************************************************************************** + Construct a default security descriptor buffer for a printer. +****************************************************************************/ + +static SEC_DESC_BUF *construct_default_printer_sdb(TALLOC_CTX *ctx) +{ + extern DOM_SID global_sam_sid; + SEC_ACE ace[3]; + SEC_ACCESS sa; + SEC_ACL *psa = NULL; + SEC_DESC_BUF *sdb = NULL; + SEC_DESC *psd = NULL; + DOM_SID owner_sid; + size_t sd_size; + + /* Create an ACE where Everyone is allowed to print */ + + init_sec_access(&sa, PRINTER_ACE_PRINT); + init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, + sa, SEC_ACE_FLAG_CONTAINER_INHERIT); + + /* Make the security descriptor owned by the Administrators group + on the PDC of the domain. */ + + if (secrets_fetch_domain_sid(lp_workgroup(), &owner_sid)) { + sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN); + } else { + + /* Backup plan - make printer owned by admins. + This should emulate a lanman printer as security + settings can't be changed. */ + + sid_copy(&owner_sid, &global_sam_sid); + sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN); + } + + init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL); + init_sec_ace(&ace[1], &owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, + sa, SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_INHERIT_ONLY); + + init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL); + init_sec_ace(&ace[2], &owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, + sa, SEC_ACE_FLAG_CONTAINER_INHERIT); + + /* The ACL revision number in rpc_secdesc.h differs from the one + created by NT when setting ACE entries in printer + descriptors. NT4 complains about the property being edited by a + NT5 machine. */ + + if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) != NULL) { + psd = make_sec_desc(ctx, SEC_DESC_REVISION, + &owner_sid, NULL, + NULL, psa, &sd_size); + } + + if (!psd) { + DEBUG(0,("construct_default_printer_sd: Failed to make SEC_DESC.\n")); + return NULL; + } + + sdb = make_sec_desc_buf(ctx, sd_size, psd); + + DEBUG(4,("construct_default_printer_sdb: size = %u.\n", + (unsigned int)sd_size)); + + return sdb; +} + +/**************************************************************************** + Get a security desc for a printer. +****************************************************************************/ + +BOOL nt_printing_getsec(TALLOC_CTX *ctx, char *printername, SEC_DESC_BUF **secdesc_ctr) +{ + prs_struct ps; + fstring key; + char *temp; + + if ((temp = strchr(printername + 2, '\\'))) { + printername = temp + 1; + } + + /* Fetch security descriptor from tdb */ + + slprintf(key, sizeof(key)-1, "SECDESC/%s", printername); + + if (tdb_prs_fetch(tdb_printers, key, &ps, ctx)!=0 || + !sec_io_desc_buf("nt_printing_getsec", secdesc_ctr, &ps, 1)) { + + DEBUG(4,("using default secdesc for %s\n", printername)); + + if (!(*secdesc_ctr = construct_default_printer_sdb(ctx))) { + return False; + } + + /* Save default security descriptor for later */ + + prs_init(&ps, (uint32)sec_desc_size((*secdesc_ctr)->sec) + + sizeof(SEC_DESC_BUF), ctx, MARSHALL); + + if (sec_io_desc_buf("nt_printing_setsec", secdesc_ctr, &ps, 1)) + tdb_prs_store(tdb_printers, key, &ps); + + prs_mem_free(&ps); + + return True; + } + + /* If security descriptor is owned by S-1-1-0 and winbindd is up, + this security descriptor has been created when winbindd was + down. Take ownership of security descriptor. */ + + if (sid_equal((*secdesc_ctr)->sec->owner_sid, &global_sid_World)) { + DOM_SID owner_sid; + + /* Change sd owner to workgroup administrator */ + + if (secrets_fetch_domain_sid(lp_workgroup(), &owner_sid)) { + SEC_DESC_BUF *new_secdesc_ctr = NULL; + SEC_DESC *psd = NULL; + size_t size; + + /* Create new sd */ + + sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN); + + psd = make_sec_desc(ctx, (*secdesc_ctr)->sec->revision, + &owner_sid, + (*secdesc_ctr)->sec->grp_sid, + (*secdesc_ctr)->sec->sacl, + (*secdesc_ctr)->sec->dacl, + &size); + + new_secdesc_ctr = make_sec_desc_buf(ctx, size, psd); + + /* Swap with other one */ + + *secdesc_ctr = new_secdesc_ctr; + + /* Set it */ + + nt_printing_setsec(printername, *secdesc_ctr); + } + } + + if (DEBUGLEVEL >= 10) { + SEC_ACL *the_acl = (*secdesc_ctr)->sec->dacl; + int i; + + DEBUG(10, ("secdesc_ctr for %s has %d aces:\n", + printername, the_acl->num_aces)); + + for (i = 0; i < the_acl->num_aces; i++) { + fstring sid_str; + + sid_to_string(sid_str, &the_acl->ace[i].trustee); + + DEBUG(10, ("%s %d %d 0x%08x\n", sid_str, + the_acl->ace[i].type, the_acl->ace[i].flags, + the_acl->ace[i].info.mask)); + } + } + + prs_mem_free(&ps); + return True; +} + +/* error code: + 0: everything OK + 1: level not implemented + 2: file doesn't exist + 3: can't allocate memory + 4: can't free memory + 5: non existant struct +*/ + +/* + A printer and a printer driver are 2 different things. + NT manages them separatelly, Samba does the same. + Why ? Simply because it's easier and it makes sense ! + + Now explanation: You have 3 printers behind your samba server, + 2 of them are the same make and model (laser A and B). But laser B + has an 3000 sheet feeder and laser A doesn't such an option. + Your third printer is an old dot-matrix model for the accounting :-). + + If the /usr/local/samba/lib directory (default dir), you will have + 5 files to describe all of this. + + 3 files for the printers (1 by printer): + NTprinter_laser A + NTprinter_laser B + NTprinter_accounting + 2 files for the drivers (1 for the laser and 1 for the dot matrix) + NTdriver_printer model X + NTdriver_printer model Y + +jfm: I should use this comment for the text file to explain + same thing for the forms BTW. + Je devrais mettre mes commentaires en francais, ca serait mieux :-) + +*/ + +/* Convert generic access rights to printer object specific access rights. + It turns out that NT4 security descriptors use generic access rights and + NT5 the object specific ones. */ + +void map_printer_permissions(SEC_DESC *sd) +{ + int i; + + for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) { + se_map_generic(&sd->dacl->ace[i].info.mask, + &printer_generic_mapping); + } +} + +/**************************************************************************** + Check a user has permissions to perform the given operation. We use the + permission constants defined in include/rpc_spoolss.h to check the various + actions we perform when checking printer access. + + PRINTER_ACCESS_ADMINISTER: + print_queue_pause, print_queue_resume, update_printer_sec, + update_printer, spoolss_addprinterex_level_2, + _spoolss_setprinterdata + + PRINTER_ACCESS_USE: + print_job_start + + JOB_ACCESS_ADMINISTER: + print_job_delete, print_job_pause, print_job_resume, + print_queue_purge + + ****************************************************************************/ +BOOL print_access_check(struct current_user *user, int snum, int access_type) +{ + SEC_DESC_BUF *secdesc = NULL; + uint32 access_granted; + NTSTATUS status; + BOOL result; + char *pname; + TALLOC_CTX *mem_ctx = NULL; + extern struct current_user current_user; + + /* If user is NULL then use the current_user structure */ + + if (!user) + user = ¤t_user; + + /* Always allow root or printer admins to do anything */ + + if (user->uid == 0 || + user_in_list(uidtoname(user->uid), lp_printer_admin(snum))) { + return True; + } + + /* Get printer name */ + + pname = PRINTERNAME(snum); + + if (!pname || !*pname) { + errno = EACCES; + return False; + } + + /* Get printer security descriptor */ + + if(!(mem_ctx = talloc_init())) { + errno = ENOMEM; + return False; + } + + nt_printing_getsec(mem_ctx, pname, &secdesc); + + if (access_type == JOB_ACCESS_ADMINISTER) { + SEC_DESC_BUF *parent_secdesc = secdesc; + + /* Create a child security descriptor to check permissions + against. This is because print jobs are child objects + objects of a printer. */ + + secdesc = se_create_child_secdesc(mem_ctx, parent_secdesc->sec, False); + + /* Now this is the bit that really confuses me. The access + type needs to be changed from JOB_ACCESS_ADMINISTER to + PRINTER_ACCESS_ADMINISTER for this to work. Something + to do with the child (job) object becoming like a + printer?? -tpot */ + + access_type = PRINTER_ACCESS_ADMINISTER; + } + + /* Check access */ + + map_printer_permissions(secdesc->sec); + + result = se_access_check(secdesc->sec, user->nt_user_token, access_type, + &access_granted, &status); + + DEBUG(4, ("access check was %s\n", result ? "SUCCESS" : "FAILURE")); + + talloc_destroy(mem_ctx); + + if (!result) + errno = EACCES; + + return result; +} + +/**************************************************************************** + Check the time parameters allow a print operation. +*****************************************************************************/ + +BOOL print_time_access_check(int snum) +{ + NT_PRINTER_INFO_LEVEL *printer = NULL; + BOOL ok = False; + time_t now = time(NULL); + struct tm *t; + uint32 mins; + + if (!W_ERROR_IS_OK(get_a_printer(&printer, 2, lp_servicename(snum)))) + return False; + + if (printer->info_2->starttime == 0 && printer->info_2->untiltime == 0) + ok = True; + + t = gmtime(&now); + mins = (uint32)t->tm_hour*60 + (uint32)t->tm_min; + + if (mins >= printer->info_2->starttime && mins <= printer->info_2->untiltime) + ok = True; + + free_a_printer(&printer, 2); + + if (!ok) + errno = EACCES; + + return ok; +} + +#if 0 /* JERRY - not used */ +/**************************************************************************** + Attempt to write a default device. +*****************************************************************************/ + +WERROR printer_write_default_dev(int snum, const PRINTER_DEFAULT *printer_default) +{ + NT_PRINTER_INFO_LEVEL *printer = NULL; + WERROR result; + + /* + * Don't bother if no default devicemode was sent. + */ + + if (printer_default->devmode_cont.devmode == NULL) + return WERR_OK; + + result = get_a_printer(&printer, 2, lp_servicename(snum)); + if (!W_ERROR_IS_OK(result)) return result; + + /* + * Just ignore it if we already have a devmode. + */ +#if 0 + if (printer->info_2->devmode != NULL) + goto done; +#endif + /* + * We don't have a devicemode and we're trying to write + * one. Check we have the access needed. + */ + DEBUG(5,("printer_write_default_dev: access: %x\n", printer_default->access_required)); + + if ( (printer_default->access_required & PRINTER_ACCESS_ADMINISTER) != + PRINTER_ACCESS_ADMINISTER) { + DEBUG(5,("printer_write_default_dev: invalid request access to update: %x\n", printer_default->access_required)); + result = WERR_ACCESS_DENIED; + goto done; + } + + if (!print_access_check(NULL, snum, PRINTER_ACCESS_ADMINISTER)) { + DEBUG(5,("printer_write_default_dev: Access denied for printer %s\n", + lp_servicename(snum) )); + result = WERR_ACCESS_DENIED; + /*result = NT_STATUS_NO_PROBLEMO;*/ + goto done; + } + + DEBUG(5,("printer_write_default_dev: updating, check OK.\n")); + + /* + * Convert the on the wire devicemode format to the internal one. + */ + + if (!convert_devicemode(printer->info_2->printername, + printer_default->devmode_cont.devmode, + &printer->info_2->devmode)) { + result = WERR_NOMEM; + goto done; + } + + /* + * Finally write back to the tdb. + */ + + result = mod_a_printer(*printer, 2); + + done: + + free_a_printer(&printer, 2); + return result; +} +#endif /* JERRY */ diff --git a/source3/printing/pcap.c b/source3/printing/pcap.c index 8973b1627f..920c6f354e 100644 --- a/source3/printing/pcap.c +++ b/source3/printing/pcap.c @@ -1,12 +1,15 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. printcap parsing - Copyright (C) Karl Auer 1993,1994 + Copyright (C) Karl Auer 1993-1998 Re-working by Martin Kiff, 1994 Re-written again by Andrew Tridgell + + Modified for SVID support by Norm Jacobs, 1997 + + Modified for CUPS support by Michael Sweet, 1999 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -49,15 +52,17 @@ * Opening a pipe for "lpc status" and reading that would probably * be pretty effective. Code to do this already exists in the freely * distributable PCNFS server code. + * + * Modified to call SVID/XPG4 support if printcap name is set to "lpstat" + * in smb.conf under Solaris. + * + * Modified to call CUPS support if printcap name is set to "cups" + * in smb.conf. */ #include "includes.h" #include "smb.h" -#include "loadparm.h" -#include "pcap.h" - -extern int DEBUGLEVEL; #ifdef AIX /* ****************************************** @@ -66,7 +71,7 @@ extern int DEBUGLEVEL; ****************************************** */ static int strlocate(char *xpLine,char *xpS) { - int iS,iL,i,iRet; + int iS,iL,iRet; char *p; iS = strlen(xpS); iL = strlen(xpLine); @@ -88,17 +93,17 @@ static int strlocate(char *xpLine,char *xpS) /* ******************************************************************* */ /* * Scan qconfig and search all virtual printer (device printer) * */ /* ******************************************************************* */ -static void ScanQconfig_fn(char *psz,void (*fn)()) +static void ScanQconfig_fn(char *psz,void (*fn)(char *, char *)) { - int iLg,iEtat; - FILE *pfile; + int iEtat; + XFILE *pfile; char *line,*p; pstring name,comment; line = NULL; *name = 0; *comment = 0; - if ((pfile = fopen(psz, "r")) == NULL) + if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL) { DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz)); return; @@ -114,13 +119,13 @@ static void ScanQconfig_fn(char *psz,void (*fn)()) { case 0: /* locate an entry */ if (*line == '\t' || *line == ' ') continue; - if ((p=strchr(line,':'))) + if ((p=strchr_m(line,':'))) { *p = '\0'; p = strtok(line,":"); if (strcmp(p,"bsh")!=0) { - strcpy(name,p); + pstrcpy(name,p); iEtat = 1; continue; } @@ -152,7 +157,7 @@ static void ScanQconfig_fn(char *psz,void (*fn)()) break; } } - fclose(pfile); + x_fclose(pfile); } /* Scan qconfig file and locate de printername */ @@ -160,7 +165,7 @@ static void ScanQconfig_fn(char *psz,void (*fn)()) static BOOL ScanQconfig(char *psz,char *pszPrintername) { int iLg,iEtat; - FILE *pfile; + XFILE *pfile; char *pName; char *line; @@ -173,13 +178,13 @@ static BOOL ScanQconfig(char *psz,char *pszPrintername) DEBUG(0,(" Unable to allocate memory for printer %s\n",pszPrintername)); return(False); } - if ((pfile = fopen(psz, "r")) == NULL) + if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL) { DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz)); free(pName); return(False); } - sprintf(pName,"%s:",pszPrintername); + slprintf(pName, iLg + 9, "%s:",pszPrintername); iLg = strlen(pName); /*DEBUG(3,( " Looking for entry %s\n",pName));*/ iEtat = 0; @@ -226,23 +231,27 @@ static BOOL ScanQconfig(char *psz,char *pszPrintername) } } free (pName); - fclose(pfile); + x_fclose(pfile); return(False); } +#endif /* AIX */ + -#endif /*************************************************************************** Scan printcap file pszPrintcapname for a printer called pszPrintername. Return True if found, else False. Returns False on error, too, after logging the error at level 0. For generality, the printcap name may be passed - if -passed as NULL, the configuration will be queried for the name. +passed as NULL, the configuration will be queried for the name. pszPrintername +must be in DOS codepage. +The xxx_printername_ok functions need fixing to understand they are being +given a DOS codepage. FIXME !! JRA. ***************************************************************************/ BOOL pcap_printername_ok(char *pszPrintername, char *pszPrintcapname) { char *line=NULL; char *psz; char *p,*q; - FILE *pfile; + XFILE *pfile; if (pszPrintername == NULL || pszPrintername[0] == '\0') { @@ -257,11 +266,23 @@ BOOL pcap_printername_ok(char *pszPrintername, char *pszPrintcapname) DEBUG(0,( "No printcap file name configured!\n")); return(False); } + +#ifdef HAVE_CUPS + if (strequal(psz, "cups")) + return (cups_printername_ok(pszPrintername)); +#endif /* HAVE_CUPS */ + +#ifdef SYSV + if (strequal(psz, "lpstat")) + return (sysv_printername_ok(pszPrintername)); +#endif + #ifdef AIX - if (strlocate(psz,"/qconfig") != NULL) + if (strlocate(psz,"/qconfig")) return(ScanQconfig(psz,pszPrintername)); #endif - if ((pfile = fopen(psz, "r")) == NULL) + + if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL) { DEBUG(0,( "Unable to open printcap file %s for read!\n", psz)); return(False); @@ -273,44 +294,45 @@ BOOL pcap_printername_ok(char *pszPrintername, char *pszPrintcapname) continue; /* now we have a real printer line - cut it off at the first : */ - p = strchr(line,':'); + p = strchr_m(line,':'); if (p) *p = 0; /* now just check if the name is in the list */ /* NOTE: I avoid strtok as the fn calling this one may be using it */ for (p=line; p; p=q) { - if ((q = strchr(p,'|'))) *q++ = 0; + if ((q = strchr_m(p,'|'))) *q++ = 0; if (strequal(p,pszPrintername)) { /* normalise the case */ - strcpy(pszPrintername,p); + pstrcpy(pszPrintername,p); free(line); - fclose(pfile); + x_fclose(pfile); return(True); } p = q; } } - - fclose(pfile); + x_fclose(pfile); return(False); } /*************************************************************************** run a function on each printer name in the printcap file. The function is -passed the primary name and the comment (if possible) +passed the primary name and the comment (if possible). Note the fn() takes +strings in DOS codepage. This means the xxx_printer_fn() calls must be fixed +to return DOS codepage. FIXME !! JRA. ***************************************************************************/ -void pcap_printer_fn(void (*fn)()) +void pcap_printer_fn(void (*fn)(char *, char *)) { pstring name,comment; char *line; char *psz; char *p,*q; - FILE *pfile; + XFILE *pfile; /* only go looking if no printcap name supplied */ if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0')) @@ -319,14 +341,29 @@ void pcap_printer_fn(void (*fn)()) return; } +#ifdef HAVE_CUPS + if (strequal(psz, "cups")) { + cups_printer_fn(fn); + return; + } +#endif /* HAVE_CUPS */ + +#ifdef SYSV + if (strequal(psz, "lpstat")) { + sysv_printer_fn(fn); + return; + } +#endif + #ifdef AIX - if (strlocate(psz,"/qconfig") != NULL) + if (strlocate(psz,"/qconfig")) { ScanQconfig_fn(psz,fn); return; } #endif - if ((pfile = fopen(psz, "r")) == NULL) + + if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL) { DEBUG(0,( "Unable to open printcap file %s for read!\n", psz)); return; @@ -338,7 +375,7 @@ void pcap_printer_fn(void (*fn)()) continue; /* now we have a real printer line - cut it off at the first : */ - p = strchr(line,':'); + p = strchr_m(line,':'); if (p) *p = 0; /* now find the most likely printer name and comment @@ -348,9 +385,9 @@ void pcap_printer_fn(void (*fn)()) for (p=line; p; p=q) { BOOL has_punctuation; - if ((q = strchr(p,'|'))) *q++ = 0; + if ((q = strchr_m(p,'|'))) *q++ = 0; - has_punctuation = (strchr(p,' ') || strchr(p,'(') || strchr(p,')')); + has_punctuation = (strchr_m(p,' ') || strchr_m(p,'\t') || strchr_m(p,'(') || strchr_m(p,')')); if (strlen(p)>strlen(comment) && has_punctuation) { @@ -358,14 +395,14 @@ void pcap_printer_fn(void (*fn)()) continue; } - if (strlen(p) <= 8 && strlen(p)>strlen(name) && !has_punctuation) + if (strlen(p) <= MAXPRINTERLEN && strlen(p)>strlen(name) && !has_punctuation) { - if (!*comment) strcpy(comment,name); - strcpy(name,p); + if (!*comment) pstrcpy(comment,name); + pstrcpy(name,p); continue; } - if (!strchr(comment,' ') && + if (!strchr_m(comment,' ') && strlen(p) > strlen(comment)) { StrnCpy(comment,p,sizeof(comment)-1); @@ -374,10 +411,10 @@ void pcap_printer_fn(void (*fn)()) } comment[60] = 0; - name[8] = 0; + name[MAXPRINTERLEN] = 0; - if (*name) + if (*name) fn(name,comment); } - fclose(pfile); + x_fclose(pfile); } diff --git a/source3/printing/print_cups.c b/source3/printing/print_cups.c new file mode 100644 index 0000000000..b5315e10b2 --- /dev/null +++ b/source3/printing/print_cups.c @@ -0,0 +1,1189 @@ +/* + * Support code for the Common UNIX Printing System ("CUPS") + * + * Copyright 1999-2001 by Michael R Sweet. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "printing.h" +#include "smb.h" + +#ifdef HAVE_CUPS +#include <cups/cups.h> +#include <cups/language.h> + + +/* + * CUPS printing interface definitions... + */ + +static int cups_job_delete(int snum, struct printjob *pjob); +static int cups_job_pause(int snum, struct printjob *pjob); +static int cups_job_resume(int snum, struct printjob *pjob); +static int cups_job_submit(int snum, struct printjob *pjob); +static int cups_queue_get(int snum, print_queue_struct **q, + print_status_struct *status); +static int cups_queue_pause(int snum); +static int cups_queue_resume(int snum); + + +struct printif cups_printif = + { + cups_queue_get, + cups_queue_pause, + cups_queue_resume, + cups_job_delete, + cups_job_pause, + cups_job_resume, + cups_job_submit, + }; + +/* + * 'cups_passwd_cb()' - The CUPS password callback... + */ + +const char * /* O - Password or NULL */ +cups_passwd_cb(const char *prompt) /* I - Prompt */ +{ + /* + * Always return NULL to indicate that no password is available... + */ + + (void)prompt; + + return (NULL); +} + + +/* + * 'cups_printer_fn()' - Call a function for every printer known to the + * system. + */ + +void +cups_printer_fn(void (*fn)(char *, char *)) /* I - Function to call */ +{ + http_t *http; /* HTTP connection to server */ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + ipp_attribute_t *attr; /* Current attribute */ + cups_lang_t *language; /* Default language */ + char *name, /* printer-name attribute */ + *make_model, /* printer-make-and-model attribute */ + *info; /* printer-info attribute */ + static const char *requested[] =/* Requested attributes */ + { + "printer-name", + "printer-make-and-model", + "printer-info" + }; + + + DEBUG(5,("cups_printer_fn(%p)\n", fn)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(cups_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(cupsServer(), ippPort())) == NULL) + { + DEBUG(0,("Unable to connect to CUPS server %s - %s\n", + cupsServer(), strerror(errno))); + return; + } + + /* + * Build a CUPS_GET_PRINTERS request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * requested-attributes + */ + + request = ippNew(); + + request->request.op.operation_id = CUPS_GET_PRINTERS; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "requested-attributes", + (sizeof(requested) / sizeof(requested[0])), + NULL, requested); + + /* + * Do the request and get back a response... + */ + + if ((response = cupsDoRequest(http, request, "/")) == NULL) + { + DEBUG(0,("Unable to get printer list - %s\n", + ippErrorString(cupsLastError()))); + httpClose(http); + return; + } + + for (attr = response->attrs; attr != NULL;) + { + /* + * Skip leading attributes until we hit a printer... + */ + + while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER) + attr = attr->next; + + if (attr == NULL) + break; + + /* + * Pull the needed attributes from this printer... + */ + + name = NULL; + make_model = NULL; + info = NULL; + + while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER) + { + if (strcmp(attr->name, "printer-name") == 0 && + attr->value_tag == IPP_TAG_NAME) + name = attr->values[0].string.text; + + if (strcmp(attr->name, "printer-make-and-model") == 0 && + attr->value_tag == IPP_TAG_TEXT) + make_model = attr->values[0].string.text; + + if (strcmp(attr->name, "printer-info") == 0 && + attr->value_tag == IPP_TAG_TEXT) + info = attr->values[0].string.text; + + attr = attr->next; + } + + /* + * See if we have everything needed... + */ + + if (name == NULL) + break; + + if (info == NULL || !info[0]) + (*fn)(name, make_model); + else + (*fn)(name,info); + + + } + + ippDelete(response); + httpClose(http); +} + + +/* + * 'cups_printername_ok()' - Provide the equivalent of pcap_printername_ok() + * for CUPS. + */ + +int /* O - 1 if printer name OK */ +cups_printername_ok(char *name) /* I - Name of printer */ +{ + http_t *http; /* HTTP connection to server */ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + + + DEBUG(5,("cups_printername_ok(\"%s\")\n", name)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(cups_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(cupsServer(), ippPort())) == NULL) + { + DEBUG(0,("Unable to connect to CUPS server %s - %s\n", + cupsServer(), strerror(errno))); + return (0); + } + + /* + * Build an IPP_GET_PRINTER_ATTRS request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * requested-attributes + * printer-uri + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "requested-attributes", NULL, "printer-uri"); + + slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", name); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + /* + * Do the request and get back a response... + */ + + if ((response = cupsDoRequest(http, request, "/")) == NULL) + { + DEBUG(0,("Unable to get printer status for %s - %s\n", name, + ippErrorString(cupsLastError()))); + httpClose(http); + return (0); + } + + httpClose(http); + + if (response->request.status.status_code >= IPP_OK_CONFLICT) + { + DEBUG(0,("Unable to get printer status for %s - %s\n", name, + ippErrorString(response->request.status.status_code))); + ippDelete(response); + return (0); + } + else + { + ippDelete(response); + return (1); + } +} + + +/* + * 'cups_job_delete()' - Delete a job. + */ + +static int +cups_job_delete(int snum, struct printjob *pjob) +{ + int ret; /* Return value */ + http_t *http; /* HTTP connection to server */ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + + + DEBUG(5,("cups_job_delete(%d, %p (%d))\n", snum, pjob, pjob->sysjob)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(cups_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(cupsServer(), ippPort())) == NULL) + { + DEBUG(0,("Unable to connect to CUPS server %s - %s\n", + cupsServer(), strerror(errno))); + return (1); + } + + /* + * Build an IPP_CANCEL_JOB request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * job-uri + * requesting-user-name + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_CANCEL_JOB; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, pjob->user); + + /* + * Do the request and get back a response... + */ + + ret = 1; + + if ((response = cupsDoRequest(http, request, "/jobs")) != NULL) + { + if (response->request.status.status_code >= IPP_OK_CONFLICT) + DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob, + ippErrorString(cupsLastError()))); + else + ret = 0; + + ippDelete(response); + } + else + DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob, + ippErrorString(cupsLastError()))); + + httpClose(http); + + return (ret); +} + + +/* + * 'cups_job_pause()' - Pause a job. + */ + +static int +cups_job_pause(int snum, struct printjob *pjob) +{ + int ret; /* Return value */ + http_t *http; /* HTTP connection to server */ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + + + DEBUG(5,("cups_job_pause(%d, %p (%d))\n", snum, pjob, pjob->sysjob)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(cups_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(cupsServer(), ippPort())) == NULL) + { + DEBUG(0,("Unable to connect to CUPS server %s - %s\n", + cupsServer(), strerror(errno))); + return (1); + } + + /* + * Build an IPP_HOLD_JOB request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * job-uri + * requesting-user-name + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_HOLD_JOB; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, pjob->user); + + /* + * Do the request and get back a response... + */ + + ret = 1; + + if ((response = cupsDoRequest(http, request, "/jobs")) != NULL) + { + if (response->request.status.status_code >= IPP_OK_CONFLICT) + DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob, + ippErrorString(cupsLastError()))); + else + ret = 0; + + ippDelete(response); + } + else + DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob, + ippErrorString(cupsLastError()))); + + httpClose(http); + + return (ret); +} + + +/* + * 'cups_job_resume()' - Resume a paused job. + */ + +static int +cups_job_resume(int snum, struct printjob *pjob) +{ + int ret; /* Return value */ + http_t *http; /* HTTP connection to server */ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + + + DEBUG(5,("cups_job_resume(%d, %p (%d))\n", snum, pjob, pjob->sysjob)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(cups_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(cupsServer(), ippPort())) == NULL) + { + DEBUG(0,("Unable to connect to CUPS server %s - %s\n", + cupsServer(), strerror(errno))); + return (1); + } + + /* + * Build an IPP_RELEASE_JOB request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * job-uri + * requesting-user-name + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_RELEASE_JOB; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, pjob->user); + + /* + * Do the request and get back a response... + */ + + ret = 1; + + if ((response = cupsDoRequest(http, request, "/jobs")) != NULL) + { + if (response->request.status.status_code >= IPP_OK_CONFLICT) + DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob, + ippErrorString(cupsLastError()))); + else + ret = 0; + + ippDelete(response); + } + else + DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob, + ippErrorString(cupsLastError()))); + + httpClose(http); + + return (ret); +} + + +/* + * 'cups_job_submit()' - Submit a job for printing. + */ + +static int +cups_job_submit(int snum, struct printjob *pjob) +{ + int ret; /* Return value */ + http_t *http; /* HTTP connection to server */ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + + + DEBUG(5,("cups_job_submit(%d, %p (%d))\n", snum, pjob, pjob->sysjob)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(cups_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(cupsServer(), ippPort())) == NULL) + { + DEBUG(0,("Unable to connect to CUPS server %s - %s\n", + cupsServer(), strerror(errno))); + return (1); + } + + /* + * Build an IPP_PRINT_JOB request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * requesting-user-name + * [document-data] + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_PRINT_JOB; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", + PRINTERNAME(snum)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, pjob->user); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, + pjob->jobname); + + /* + * Do the request and get back a response... + */ + + slprintf(uri, sizeof(uri) - 1, "/printers/%s", PRINTERNAME(snum)); + + ret = 1; + if ((response = cupsDoFileRequest(http, request, uri, + pjob->filename)) != NULL) + { + if (response->request.status.status_code >= IPP_OK_CONFLICT) + DEBUG(0,("Unable to print file to %s - %s\n", PRINTERNAME(snum), + ippErrorString(cupsLastError()))); + else + ret = 0; + + ippDelete(response); + } + else + DEBUG(0,("Unable to print file to `%s' - %s\n", PRINTERNAME(snum), + ippErrorString(cupsLastError()))); + + httpClose(http); + + return (ret); +} + + +/* + * 'cups_queue_get()' - Get all the jobs in the print queue. + */ + +static int +cups_queue_get(int snum, print_queue_struct **q, print_status_struct *status) +{ + http_t *http; /* HTTP connection to server */ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + ipp_attribute_t *attr; /* Current attribute */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + int qcount, /* Number of active queue entries */ + qalloc; /* Number of queue entries allocated */ + print_queue_struct *queue, /* Queue entries */ + *temp; /* Temporary pointer for queue */ + const char *user_name, /* job-originating-user-name attribute */ + *job_name; /* job-name attribute */ + int job_id; /* job-id attribute */ + int job_k_octets; /* job-k-octets attribute */ + time_t job_time; /* time-at-creation attribute */ + ipp_jstate_t job_status; /* job-status attribute */ + int job_priority; /* job-priority attribute */ + static const char *jattrs[] = /* Requested job attributes */ + { + "job-id", + "job-k-octets", + "job-name", + "job-originating-user-name", + "job-priority", + "job-state", + "time-at-creation", + }; + static const char *pattrs[] = /* Requested printer attributes */ + { + "printer-state", + "printer-state-message" + }; + + + DEBUG(5,("cups_queue_get(%d, %p, %p)\n", snum, q, status)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(cups_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(cupsServer(), ippPort())) == NULL) + { + DEBUG(0,("Unable to connect to CUPS server %s - %s\n", + cupsServer(), strerror(errno))); + return (0); + } + + /* + * Generate the printer URI... + */ + + slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", + PRINTERNAME(snum)); + + /* + * Build an IPP_GET_JOBS request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * requested-attributes + * printer-uri + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_GET_JOBS; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "requested-attributes", + (sizeof(jattrs) / sizeof(jattrs[0])), + NULL, jattrs); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + /* + * Do the request and get back a response... + */ + + if ((response = cupsDoRequest(http, request, "/")) == NULL) + { + DEBUG(0,("Unable to get jobs for %s - %s\n", uri, + ippErrorString(cupsLastError()))); + httpClose(http); + return (0); + } + + if (response->request.status.status_code >= IPP_OK_CONFLICT) + { + DEBUG(0,("Unable to get jobs for %s - %s\n", uri, + ippErrorString(response->request.status.status_code))); + ippDelete(response); + httpClose(http); + + return (0); + } + + /* + * Process the jobs... + */ + + qcount = 0; + qalloc = 0; + queue = NULL; + + for (attr = response->attrs; attr != NULL; attr = attr->next) + { + /* + * Skip leading attributes until we hit a job... + */ + + while (attr != NULL && attr->group_tag != IPP_TAG_JOB) + attr = attr->next; + + if (attr == NULL) + break; + + /* + * Allocate memory as needed... + */ + if (qcount >= qalloc) + { + qalloc += 16; + + temp = Realloc(queue, sizeof(print_queue_struct) * qalloc); + + if (temp == NULL) + { + DEBUG(0,("cups_queue_get: Not enough memory!")); + ippDelete(response); + httpClose(http); + + free (queue); + return (0); + } + + queue = temp; + } + + temp = queue + qcount; + memset(temp, 0, sizeof(print_queue_struct)); + + /* + * Pull the needed attributes from this job... + */ + + job_id = 0; + job_priority = 50; + job_status = IPP_JOB_PENDING; + job_time = 0; + job_k_octets = 0; + user_name = NULL; + job_name = NULL; + + while (attr != NULL && attr->group_tag == IPP_TAG_JOB) + { + if (attr->name == NULL) + { + attr = attr->next; + break; + } + + if (strcmp(attr->name, "job-id") == 0 && + attr->value_tag == IPP_TAG_INTEGER) + job_id = attr->values[0].integer; + + if (strcmp(attr->name, "job-k-octets") == 0 && + attr->value_tag == IPP_TAG_INTEGER) + job_k_octets = attr->values[0].integer; + + if (strcmp(attr->name, "job-priority") == 0 && + attr->value_tag == IPP_TAG_INTEGER) + job_priority = attr->values[0].integer; + + if (strcmp(attr->name, "job-state") == 0 && + attr->value_tag == IPP_TAG_ENUM) + job_status = (ipp_jstate_t)(attr->values[0].integer); + + if (strcmp(attr->name, "time-at-creation") == 0 && + attr->value_tag == IPP_TAG_INTEGER) + job_time = attr->values[0].integer; + + if (strcmp(attr->name, "job-name") == 0 && + attr->value_tag == IPP_TAG_NAME) + job_name = attr->values[0].string.text; + + if (strcmp(attr->name, "job-originating-user-name") == 0 && + attr->value_tag == IPP_TAG_NAME) + user_name = attr->values[0].string.text; + + attr = attr->next; + } + + /* + * See if we have everything needed... + */ + + if (user_name == NULL || job_name == NULL || job_id == 0) + { + if (attr == NULL) + break; + else + continue; + } + + temp->job = job_id; + temp->size = job_k_octets * 1024; + temp->status = job_status == IPP_JOB_PENDING ? LPQ_QUEUED : + job_status == IPP_JOB_STOPPED ? LPQ_PAUSED : + job_status == IPP_JOB_HELD ? LPQ_PAUSED : + LPQ_PRINTING; + temp->priority = job_priority; + temp->time = job_time; + strncpy(temp->fs_user, user_name, sizeof(temp->fs_user) - 1); + strncpy(temp->fs_file, job_name, sizeof(temp->fs_file) - 1); + + qcount ++; + + if (attr == NULL) + break; + } + + ippDelete(response); + + /* + * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the + * following attributes: + * + * attributes-charset + * attributes-natural-language + * requested-attributes + * printer-uri + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "requested-attributes", + (sizeof(pattrs) / sizeof(pattrs[0])), + NULL, pattrs); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + /* + * Do the request and get back a response... + */ + + if ((response = cupsDoRequest(http, request, "/")) == NULL) + { + DEBUG(0,("Unable to get printer status for %s - %s\n", PRINTERNAME(snum), + ippErrorString(cupsLastError()))); + httpClose(http); + *q = queue; + return (qcount); + } + + if (response->request.status.status_code >= IPP_OK_CONFLICT) + { + DEBUG(0,("Unable to get printer status for %s - %s\n", PRINTERNAME(snum), + ippErrorString(response->request.status.status_code))); + ippDelete(response); + httpClose(http); + *q = queue; + return (qcount); + } + + /* + * Get the current printer status and convert it to the SAMBA values. + */ + + if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL) + { + if (attr->values[0].integer == IPP_PRINTER_STOPPED) + status->status = LPSTAT_STOPPED; + else + status->status = LPSTAT_OK; + } + + if ((attr = ippFindAttribute(response, "printer-state-message", + IPP_TAG_TEXT)) != NULL) + fstrcpy(status->message, attr->values[0].string.text); + + ippDelete(response); + + /* + * Return the job queue... + */ + + httpClose(http); + + *q = queue; + return (qcount); +} + + +/* + * 'cups_queue_pause()' - Pause a print queue. + */ + +static int +cups_queue_pause(int snum) +{ + extern userdom_struct current_user_info; + int ret; /* Return value */ + http_t *http; /* HTTP connection to server */ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + + + DEBUG(5,("cups_queue_pause(%d)\n", snum)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(cups_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(cupsServer(), ippPort())) == NULL) + { + DEBUG(0,("Unable to connect to CUPS server %s - %s\n", + cupsServer(), strerror(errno))); + return (1); + } + + /* + * Build an IPP_PAUSE_PRINTER request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * requesting-user-name + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_PAUSE_PRINTER; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", + PRINTERNAME(snum)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, current_user_info.unix_name); + + /* + * Do the request and get back a response... + */ + + ret = 1; + + if ((response = cupsDoRequest(http, request, "/admin/")) != NULL) + { + if (response->request.status.status_code >= IPP_OK_CONFLICT) + DEBUG(0,("Unable to pause printer %s - %s\n", PRINTERNAME(snum), + ippErrorString(cupsLastError()))); + else + ret = 0; + + ippDelete(response); + } + else + DEBUG(0,("Unable to pause printer %s - %s\n", PRINTERNAME(snum), + ippErrorString(cupsLastError()))); + + httpClose(http); + + return (ret); +} + + +/* + * 'cups_queue_resume()' - Restart a print queue. + */ + +static int +cups_queue_resume(int snum) +{ + extern userdom_struct current_user_info; + int ret; /* Return value */ + http_t *http; /* HTTP connection to server */ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + + + DEBUG(5,("cups_queue_resume(%d)\n", snum)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(cups_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(cupsServer(), ippPort())) == NULL) + { + DEBUG(0,("Unable to connect to CUPS server %s - %s\n", + cupsServer(), strerror(errno))); + return (1); + } + + /* + * Build an IPP_RESUME_PRINTER request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * requesting-user-name + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_RESUME_PRINTER; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", + PRINTERNAME(snum)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, current_user_info.unix_name); + + /* + * Do the request and get back a response... + */ + + ret = 1; + + if ((response = cupsDoRequest(http, request, "/admin/")) != NULL) + { + if (response->request.status.status_code >= IPP_OK_CONFLICT) + DEBUG(0,("Unable to resume printer %s - %s\n", PRINTERNAME(snum), + ippErrorString(cupsLastError()))); + else + ret = 0; + + ippDelete(response); + } + else + DEBUG(0,("Unable to resume printer %s - %s\n", PRINTERNAME(snum), + ippErrorString(cupsLastError()))); + + httpClose(http); + + return (ret); +} + + +#else + /* this keeps fussy compilers happy */ + void print_cups_dummy(void) {} +#endif /* HAVE_CUPS */ diff --git a/source3/printing/print_generic.c b/source3/printing/print_generic.c new file mode 100644 index 0000000000..e1517c5dcb --- /dev/null +++ b/source3/printing/print_generic.c @@ -0,0 +1,247 @@ +/* + Unix SMB/CIFS implementation. + printing command routines + Copyright (C) Andrew Tridgell 1992-2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "printing.h" + + +/* + * Generic printing interface definitions... + */ + +static int generic_job_delete(int snum, struct printjob *pjob); +static int generic_job_pause(int snum, struct printjob *pjob); +static int generic_job_resume(int snum, struct printjob *pjob); +static int generic_job_submit(int snum, struct printjob *pjob); +static int generic_queue_get(int snum, print_queue_struct **q, + print_status_struct *status); +static int generic_queue_pause(int snum); +static int generic_queue_resume(int snum); + + +struct printif generic_printif = + { + generic_queue_get, + generic_queue_pause, + generic_queue_resume, + generic_job_delete, + generic_job_pause, + generic_job_resume, + generic_job_submit, + }; + +/**************************************************************************** +run a given print command +a null terminated list of value/substitute pairs is provided +for local substitution strings +****************************************************************************/ +static int print_run_command(int snum,char *command, int *outfd, ...) +{ + + pstring syscmd; + char *p, *arg; + int ret; + va_list ap; + va_start(ap, outfd); + + if (!command || !*command) return -1; + + if (!VALID_SNUM(snum)) { + DEBUG(0,("Invalid snum %d for command %s\n", snum, command)); + return -1; + } + + pstrcpy(syscmd, command); + + while ((arg = va_arg(ap, char *))) { + char *value = va_arg(ap,char *); + pstring_sub(syscmd, arg, value); + } + va_end(ap); + + p = PRINTERNAME(snum); + + pstring_sub(syscmd, "%p", p); + standard_sub_snum(snum,syscmd); + + ret = smbrun(syscmd,outfd); + + DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret)); + + return ret; +} + + +/**************************************************************************** +delete a print job +****************************************************************************/ +static int generic_job_delete(int snum, struct printjob *pjob) +{ + fstring jobstr; + + /* need to delete the spooled entry */ + slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob); + return print_run_command( + snum, + lp_lprmcommand(snum), NULL, + "%j", jobstr, + "%T", http_timestring(pjob->starttime), + NULL); +} + +/**************************************************************************** +pause a job +****************************************************************************/ +static int generic_job_pause(int snum, struct printjob *pjob) +{ + fstring jobstr; + + /* need to pause the spooled entry */ + slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob); + return print_run_command(snum, + lp_lppausecommand(snum), NULL, + "%j", jobstr, + NULL); +} + +/**************************************************************************** +resume a job +****************************************************************************/ +static int generic_job_resume(int snum, struct printjob *pjob) +{ + fstring jobstr; + + /* need to pause the spooled entry */ + slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob); + return print_run_command(snum, + lp_lpresumecommand(snum), NULL, + "%j", jobstr, + NULL); +} + +/**************************************************************************** + Submit a file for printing - called from print_job_end() +****************************************************************************/ + +static int generic_job_submit(int snum, struct printjob *pjob) +{ + int ret; + pstring current_directory; + pstring print_directory; + char *wd, *p; + pstring jobname; + fstring job_page_count, job_size; + + /* we print from the directory path to give the best chance of + parsing the lpq output */ + wd = sys_getwd(current_directory); + if (!wd) + return 0; + + pstrcpy(print_directory, pjob->filename); + p = strrchr_m(print_directory,'/'); + if (!p) + return 0; + *p++ = 0; + + if (chdir(print_directory) != 0) + return 0; + + pstrcpy(jobname, pjob->jobname); + pstring_sub(jobname, "'", "_"); + slprintf(job_page_count, sizeof(job_page_count)-1, "%d", pjob->page_count); + slprintf(job_size, sizeof(job_size)-1, "%d", pjob->size); + + /* send it to the system spooler */ + ret = print_run_command(snum, + lp_printcommand(snum), NULL, + "%s", p, + "%J", jobname, + "%f", p, + "%z", job_size, + "%c", job_page_count, + NULL); + + chdir(wd); + + return ret; +} + + +/**************************************************************************** +get the current list of queued jobs +****************************************************************************/ +static int generic_queue_get(int snum, print_queue_struct **q, print_status_struct *status) +{ + char **qlines; + int fd; + int numlines, i, qcount; + print_queue_struct *queue = NULL; + fstring printer_name; + + fstrcpy(printer_name, lp_servicename(snum)); + + print_run_command(snum, lp_lpqcommand(snum), &fd, NULL); + + if (fd == -1) { + DEBUG(5,("generic_queue_get: Can't read print queue status for printer %s\n", + printer_name )); + return 0; + } + + numlines = 0; + qlines = fd_lines_load(fd, &numlines); + close(fd); + + /* turn the lpq output into a series of job structures */ + qcount = 0; + ZERO_STRUCTP(status); + if (numlines) + queue = (print_queue_struct *)malloc(sizeof(print_queue_struct)*(numlines+1)); + + if (queue) { + for (i=0; i<numlines; i++) { + /* parse the line */ + if (parse_lpq_entry(snum,qlines[i], + &queue[qcount],status,qcount==0)) { + qcount++; + } + } + } + file_lines_free(qlines); + + *q = queue; + return qcount; +} + +/**************************************************************************** + pause a queue +****************************************************************************/ +static int generic_queue_pause(int snum) +{ + return print_run_command(snum, lp_queuepausecommand(snum), NULL, NULL); +} + +/**************************************************************************** + resume a queue +****************************************************************************/ +static int generic_queue_resume(int snum) +{ + return print_run_command(snum, lp_queueresumecommand(snum), NULL, NULL); +} diff --git a/source3/printing/print_svid.c b/source3/printing/print_svid.c new file mode 100644 index 0000000000..44127c3700 --- /dev/null +++ b/source3/printing/print_svid.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 1997-1998 by Norm Jacobs, Colorado Springs, Colorado, USA + * Copyright (C) 1997-1998 by Sun Microsystem, Inc. + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This module implements support for gathering and comparing available + * printer information on a SVID or XPG4 compliant system. It does this + * through the use of the SVID/XPG4 command "lpstat(1)". + * + * The expectations is that execution of the command "lpstat -v" will + * generate responses in the form of: + * + * device for serial: /dev/term/b + * system for fax: server + * system for color: server (as printer chroma) + */ + + +#include "includes.h" +#include "smb.h" + +#ifdef SYSV + +typedef struct printer { + char *name; + struct printer *next; +} printer_t; +static printer_t *printers = NULL; + +static void populate_printers(void) +{ + char **lines; + int i; + + lines = file_lines_pload("/usr/bin/lpstat -v", NULL); + if (!lines) return; + + for (i=0;lines[i];i++) { + printer_t *ptmp; + char *name, *tmp; + char *buf = lines[i]; + + /* eat "system/device for " */ + if (((tmp = strchr_m(buf, ' ')) == NULL) || + ((tmp = strchr_m(++tmp, ' ')) == NULL)) + continue; + + /* + * In case we're only at the "for ". + */ + + if(!strncmp("for ",++tmp,4)) { + tmp=strchr_m(tmp, ' '); + tmp++; + } + + /* Eat whitespace. */ + + while(*tmp == ' ') + ++tmp; + + /* + * On HPUX there is an extra line that can be ignored. + * d.thibadeau 2001/08/09 + */ + if(!strncmp("remote to",tmp,9)) + continue; + + name = tmp; + + /* truncate the ": ..." */ + if ((tmp = strchr_m(name, ':')) != NULL) + *tmp = '\0'; + + /* add it to the cache */ + if ((ptmp = malloc(sizeof (*ptmp))) != NULL) { + ZERO_STRUCTP(ptmp); + if((ptmp->name = strdup(name)) == NULL) + DEBUG(0,("populate_printers: malloc fail in strdup !\n")); + ptmp->next = printers; + printers = ptmp; + } else { + DEBUG(0,("populate_printers: malloc fail for ptmp\n")); + } + } + + file_lines_free(lines); +} + + +/* + * provide the equivalent of pcap_printer_fn() for SVID/XPG4 conforming + * systems. It was unclear why pcap_printer_fn() was tossing names longer + * than 8 characters. I suspect that its a protocol limit, but amazingly + * names longer than 8 characters appear to work with my test + * clients (Win95/NT). + */ +void sysv_printer_fn(void (*fn)(char *, char *)) +{ + printer_t *tmp; + + if (printers == NULL) + populate_printers(); + for (tmp = printers; tmp != NULL; tmp = tmp->next) + (fn)(tmp->name, ""); +} + + +/* + * provide the equivalent of pcap_printername_ok() for SVID/XPG4 conforming + * systems. + */ +int sysv_printername_ok(char *name) +{ + printer_t *tmp; + + if (printers == NULL) + populate_printers(); + for (tmp = printers; tmp != NULL; tmp = tmp->next) + if (strcmp(tmp->name, name) == 0) + return (True); + return (False); +} + +#else +/* this keeps fussy compilers happy */ + void print_svid_dummy(void); + void print_svid_dummy(void) {} +#endif diff --git a/source3/printing/printfsp.c b/source3/printing/printfsp.c new file mode 100644 index 0000000000..9f33d57ad5 --- /dev/null +++ b/source3/printing/printfsp.c @@ -0,0 +1,104 @@ +/* + Unix SMB/CIFS implementation. + printing backend routines for smbd - using files_struct rather + than only snum + Copyright (C) Andrew Tridgell 1992-2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/*************************************************************************** +open a print file and setup a fsp for it. This is a wrapper around +print_job_start(). +***************************************************************************/ + +files_struct *print_fsp_open(connection_struct *conn, char *fname) +{ + int jobid; + SMB_STRUCT_STAT sbuf; + extern struct current_user current_user; + files_struct *fsp = file_new(conn); + fstring name; + + if(!fsp) + return NULL; + + fstrcpy( name, "Remote Downlevel Document"); + if (fname) { + char *p = strrchr(fname, '/'); + fstrcat(name, " "); + if (!p) + p = fname; + fstrcat(name, p); + } + + jobid = print_job_start(¤t_user, SNUM(conn), name); + if (jobid == -1) { + file_free(fsp); + return NULL; + } + + /* setup a full fsp */ + fsp->print_jobid = jobid; + fsp->fd = print_job_fd(jobid); + GetTimeOfDay(&fsp->open_time); + fsp->vuid = current_user.vuid; + fsp->size = 0; + fsp->pos = -1; + fsp->can_lock = True; + fsp->can_read = False; + fsp->can_write = True; + fsp->share_mode = 0; + fsp->print_file = True; + fsp->modified = False; + fsp->oplock_type = NO_OPLOCK; + fsp->sent_oplock_break = NO_BREAK_SENT; + fsp->is_directory = False; + fsp->directory_delete_on_close = False; + fsp->conn = conn; + string_set(&fsp->fsp_name,print_job_fname(jobid)); + fsp->wbmpx_ptr = NULL; + fsp->wcp = NULL; + conn->vfs_ops.fstat(fsp,fsp->fd, &sbuf); + fsp->mode = sbuf.st_mode; + fsp->inode = sbuf.st_ino; + fsp->dev = sbuf.st_dev; + + conn->num_files_open++; + + return fsp; +} + +/**************************************************************************** +print a file - called on closing the file +****************************************************************************/ +void print_fsp_end(files_struct *fsp, BOOL normal_close) +{ + if (fsp->share_mode == FILE_DELETE_ON_CLOSE) { + /* + * Truncate the job. print_job_end will take + * care of deleting it for us. JRA. + */ + sys_ftruncate(fsp->fd, 0); + } + + print_job_end(fsp->print_jobid, normal_close); + + if (fsp->fsp_name) { + string_free(&fsp->fsp_name); + } +} diff --git a/source3/printing/printing.c b/source3/printing/printing.c index 1dd8921800..ad5acb1505 100644 --- a/source3/printing/printing.c +++ b/source3/printing/printing.c @@ -1,8 +1,7 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. - printing routines - Copyright (C) Andrew Tridgell 1992-1995 + Unix SMB/CIFS implementation. + printing backend routines + Copyright (C) Andrew Tridgell 1992-2000 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,841 +18,1407 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "includes.h" -#include "loadparm.h" -extern int DEBUGLEVEL; -extern connection_struct Connections[]; -extern files_struct Files[]; - -static BOOL * lpq_cache_reset=NULL; - -static int check_lpq_cache(int snum) { - static int lpq_caches=0; - - if (lpq_caches <= snum) { - BOOL * p; - p = (BOOL *) Realloc(lpq_cache_reset,(snum+1)*sizeof(BOOL)); - if (p) { - lpq_cache_reset=p; - lpq_caches = snum+1; - } - } - return lpq_caches; -} - -void lpq_reset(int snum) -{ - if (check_lpq_cache(snum) > snum) lpq_cache_reset[snum]=True; -} - - -/**************************************************************************** -Build the print command in the supplied buffer. This means getting the -print command for the service and inserting the printer name and the -print file name. Return NULL on error, else the passed buffer pointer. -****************************************************************************/ -static char *build_print_command(int cnum, char *command, char *syscmd, char *filename1) -{ - int snum = SNUM(cnum); - char *tstr; - pstring filename; - - /* get the print command for the service. */ - tstr = command; - if (!syscmd || !tstr) { - DEBUG(0,("No print command for service `%s'\n", SERVICE(snum))); - return (NULL); - } - - /* copy the command into the buffer for extensive meddling. */ - StrnCpy(syscmd, tstr, sizeof(pstring) - 1); - - /* look for "%s" in the string. If there is no %s, we cannot print. */ - if (!strstr(syscmd, "%s") && !strstr(syscmd, "%f")) { - DEBUG(2,("WARNING! No placeholder for the filename in the print command for service %s!\n", SERVICE(snum))); - } - - if (strstr(syscmd,"%s")) { - int iOffset = strstr(syscmd, "%s") - syscmd; - - /* construct the full path for the filename, shouldn't be necessary unless - the subshell causes a "cd" to be executed. - Only use the full path if there isn't a / preceding the %s */ - if (iOffset==0 || syscmd[iOffset-1] != '/') { - StrnCpy(filename,Connections[cnum].connectpath,sizeof(filename)-1); - trim_string(filename,"","/"); - strcat(filename,"/"); - strcat(filename,filename1); - } - else - strcpy(filename,filename1); - - string_sub(syscmd, "%s", filename); - } - - string_sub(syscmd, "%f", filename1); - - /* Does the service have a printername? If not, make a fake and empty */ - /* printer name. That way a %p is treated sanely if no printer */ - /* name was specified to replace it. This eventuality is logged. */ - tstr = PRINTERNAME(snum); - if (tstr == NULL || tstr[0] == '\0') { - DEBUG(3,( "No printer name - using %s.\n", SERVICE(snum))); - tstr = SERVICE(snum); - } - - string_sub(syscmd, "%p", tstr); - - standard_sub(cnum,syscmd); - - return (syscmd); -} - - -/**************************************************************************** -print a file - called on closing the file -****************************************************************************/ -void print_file(int fnum) -{ - pstring syscmd; - int cnum = Files[fnum].cnum; - int snum=SNUM(cnum); - char *tempstr; - - *syscmd = 0; - - if (file_size(Files[fnum].name) <= 0) { - DEBUG(3,("Discarding null print job %s\n",Files[fnum].name)); - sys_unlink(Files[fnum].name); - return; - } - - tempstr = build_print_command(cnum, PRINTCOMMAND(snum), syscmd, Files[fnum].name); - if (tempstr != NULL) - { - int ret = smbrun(syscmd,NULL); - DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret)); - } - else - DEBUG(0,("Null print command?\n")); - - lpq_reset(snum); -} - -static char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"}; - - -/******************************************************************* -process time fields -********************************************************************/ -static time_t EntryTime(string tok[], int ptr, int count, int minimum) -{ - time_t jobtime; - - jobtime = time(NULL); /* default case: take current time */ - if (count >= minimum) { - struct tm *t; - int i, day, hour, min, sec; - char *c; - - for (i=0; i<13; i++) if (!strncmp(tok[ptr], Months[i],3)) break; /* Find month */ - if (i<12) { - t = localtime(&jobtime); - day = atoi(tok[ptr+1]); - c=(char *)(tok[ptr+2]); - *(c+2)=0; - hour = atoi(c); - *(c+5)=0; - min = atoi(c+3); - if(*(c+6) != 0)sec = atoi(c+6); - else sec=0; - - if ((t->tm_mon < i)|| - ((t->tm_mon == i)&& - ((t->tm_mday < day)|| - ((t->tm_mday == day)&& - (t->tm_hour*60+t->tm_min < hour*60+min))))) - t->tm_year--; /* last year's print job */ - - t->tm_mon = i; - t->tm_mday = day; - t->tm_hour = hour; - t->tm_min = min; - t->tm_sec = sec; - jobtime = mktime(t); - } - } - return jobtime; -} - - -/**************************************************************************** -parse a lpq line - -here is an example of lpq output under bsd - -Warning: no daemon present -Rank Owner Job Files Total Size -1st tridge 148 README 8096 bytes - -here is an example of lpq output under osf/1 - -Warning: no daemon present -Rank Pri Owner Job Files Total Size -1st 0 tridge 148 README 8096 bytes -****************************************************************************/ -static BOOL parse_lpq_bsd(char *line,print_queue_struct *buf,BOOL first) -{ -#ifdef OSF1 -#define RANKTOK 0 -#define PRIOTOK 1 -#define USERTOK 2 -#define JOBTOK 3 -#define FILETOK 4 -#define TOTALTOK 5 -#define NTOK 6 -#else /* OSF1 */ -#define RANKTOK 0 -#define USERTOK 1 -#define JOBTOK 2 -#define FILETOK 3 -#define TOTALTOK 4 -#define NTOK 5 -#endif /* OSF1 */ - - string tok[NTOK]; - int count=0; - -#ifdef OSF1 - int length; - length = strlen(line); - if (line[length-3] == ':') - return(False); -#endif /* OSF1 */ - - /* handle the case of "(standard input)" as a filename */ - string_sub(line,"standard input","STDIN"); - string_sub(line,"(","\""); - string_sub(line,")","\""); - - for (count=0; count<NTOK && next_token(&line,tok[count],NULL); count++) ; - - /* we must get NTOK tokens */ - if (count < NTOK) - return(False); - - /* the Job and Total columns must be integer */ - if (!isdigit(*tok[JOBTOK]) || !isdigit(*tok[TOTALTOK])) return(False); - - /* if the fname contains a space then use STDIN */ - if (strchr(tok[FILETOK],' ')) - strcpy(tok[FILETOK],"STDIN"); - - /* only take the last part of the filename */ - { - string tmp; - char *p = strrchr(tok[FILETOK],'/'); - if (p) - { - strcpy(tmp,p+1); - strcpy(tok[FILETOK],tmp); - } - } +#include "printing.h" + +/* Current printer interface */ +struct printif *current_printif = &generic_printif; + +/* + the printing backend revolves around a tdb database that stores the + SMB view of the print queue + + The key for this database is a jobid - a internally generated number that + uniquely identifies a print job + + reading the print queue involves two steps: + - possibly running lpq and updating the internal database from that + - reading entries from the database + + jobids are assigned when a job starts spooling. +*/ + +/* the open printing.tdb database */ +static TDB_CONTEXT *tdb; +static pid_t local_pid; + +static int get_queue_status(int, print_status_struct *); + +/**************************************************************************** + Initialise the printing backend. Called once at startup. + Does not survive a fork +****************************************************************************/ + +BOOL print_backend_init(void) +{ + char *sversion = "INFO/version"; + + if (tdb && local_pid == sys_getpid()) return True; + tdb = tdb_open_log(lock_path("printing.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + if (!tdb) { + DEBUG(0,("print_backend_init: Failed to open printing backend database. Error = [%s]\n", + tdb_errorstr(tdb))); + return False; + } + local_pid = sys_getpid(); + + /* handle a Samba upgrade */ + tdb_lock_bystring(tdb, sversion); + if (tdb_fetch_int32(tdb, sversion) != PRINT_DATABASE_VERSION) { + tdb_traverse(tdb, tdb_traverse_delete_fn, NULL); + tdb_store_int32(tdb, sversion, PRINT_DATABASE_VERSION); + } + tdb_unlock_bystring(tdb, sversion); + + /* select the appropriate printing interface... */ +#ifdef HAVE_CUPS + if (strcmp(lp_printcapname(), "cups") == 0) + current_printif = &cups_printif; +#endif /* HAVE_CUPS */ + + /* do NT print initialization... */ + return nt_printing_init(); +} + +/**************************************************************************** +useful function to generate a tdb key +****************************************************************************/ +static TDB_DATA print_key(int jobid) +{ + static int j; + TDB_DATA ret; + + j = jobid; + ret.dptr = (void *)&j; + ret.dsize = sizeof(j); + return ret; +} + +/**************************************************************************** +useful function to find a print job in the database +****************************************************************************/ +static struct printjob *print_job_find(int jobid) +{ + static struct printjob pjob; + TDB_DATA ret; + + ret = tdb_fetch(tdb, print_key(jobid)); + if (!ret.dptr || ret.dsize != sizeof(pjob)) return NULL; + + memcpy(&pjob, ret.dptr, sizeof(pjob)); + free(ret.dptr); + return &pjob; +} + +/**************************************************************************** +store a job structure back to the database +****************************************************************************/ +static BOOL print_job_store(int jobid, struct printjob *pjob) +{ + TDB_DATA d; + BOOL ret; + + d.dptr = (void *)pjob; + d.dsize = sizeof(*pjob); + ret = (tdb_store(tdb, print_key(jobid), d, TDB_REPLACE) == 0); + return ret; +} + +/**************************************************************************** +parse a file name from the system spooler to generate a jobid +****************************************************************************/ +static int print_parse_jobid(char *fname) +{ + int jobid; + + if (strncmp(fname,PRINT_SPOOL_PREFIX,strlen(PRINT_SPOOL_PREFIX)) != 0) return -1; + fname += strlen(PRINT_SPOOL_PREFIX); + + jobid = atoi(fname); + if (jobid <= 0) return -1; + + return jobid; +} + + +/**************************************************************************** +list a unix job in the print database +****************************************************************************/ +static void print_unix_job(int snum, print_queue_struct *q) +{ + int jobid = q->job + UNIX_JOB_START; + struct printjob pj, *old_pj; + + /* Preserve the timestamp on an existing unix print job */ + + old_pj = print_job_find(jobid); + + ZERO_STRUCT(pj); + + pj.pid = (pid_t)-1; + pj.sysjob = q->job; + pj.fd = -1; + pj.starttime = old_pj ? old_pj->starttime : q->time; + pj.status = q->status; + pj.size = q->size; + pj.spooled = True; + pj.smbjob = False; + fstrcpy(pj.filename, ""); + fstrcpy(pj.jobname, q->fs_file); + fstrcpy(pj.user, q->fs_user); + fstrcpy(pj.queuename, lp_servicename(snum)); + + print_job_store(jobid, &pj); +} + + +struct traverse_struct { + print_queue_struct *queue; + int qcount, snum, maxcount, total_jobs; +}; + +/* utility fn to delete any jobs that are no longer active */ +static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state) +{ + struct traverse_struct *ts = (struct traverse_struct *)state; + struct printjob pjob; + int i, jobid; + + if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) return 0; + memcpy(&jobid, key.dptr, sizeof(jobid)); + memcpy(&pjob, data.dptr, sizeof(pjob)); + + if (ts->snum != lp_servicenumber(pjob.queuename)) { + /* this isn't for the queue we are looking at */ + ts->total_jobs++; + return 0; + } + + if (!pjob.smbjob) { + /* remove a unix job if it isn't in the system queue any more */ + + for (i=0;i<ts->qcount;i++) { + if (jobid == ts->queue[i].job + UNIX_JOB_START) break; + } + if (i == ts->qcount) + tdb_delete(tdb, key); + else + ts->total_jobs++; + return 0; + } + + /* maybe it hasn't been spooled yet */ + if (!pjob.spooled) { + /* if a job is not spooled and the process doesn't + exist then kill it. This cleans up after smbd + deaths */ + if (!process_exists(pjob.pid)) + tdb_delete(tdb, key); + else + ts->total_jobs++; + return 0; + } + + for (i=0;i<ts->qcount;i++) { + int qid = print_parse_jobid(ts->queue[i].fs_file); + if (jobid == qid) break; + } + + /* The job isn't in the system queue - we have to assume it has + completed, so delete the database entry. */ + + if (i == ts->qcount) { + time_t cur_t = time(NULL); + + /* A race can occur between the time a job is spooled and + when it appears in the lpq output. This happens when + the job is added to printing.tdb when another smbd + running print_queue_update() has completed a lpq and + is currently traversing the printing tdb and deleting jobs. + A workaround is to not delete the job if it has been + submitted less than lp_lpqcachetime() seconds ago. */ + + if ((cur_t - pjob.starttime) > lp_lpqcachetime()) + tdb_delete(t, key); + else + ts->total_jobs++; + } + else + ts->total_jobs++; + + return 0; +} + +/**************************************************************************** +check if the print queue has been updated recently enough +****************************************************************************/ +static void print_cache_flush(int snum) +{ + fstring key; + slprintf(key, sizeof(key)-1, "CACHE/%s", lp_servicename(snum)); + tdb_store_int32(tdb, key, -1); +} + +/**************************************************************************** + Check if someone already thinks they are doing the update. +****************************************************************************/ + +static pid_t get_updating_pid(fstring printer_name) +{ + fstring keystr; + TDB_DATA data, key; + pid_t updating_pid; + + slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", printer_name); + key.dptr = keystr; + key.dsize = strlen(keystr); + + data = tdb_fetch(tdb, key); + if (!data.dptr || data.dsize != sizeof(pid_t)) + return (pid_t)-1; + + memcpy(&updating_pid, data.dptr, sizeof(pid_t)); + free(data.dptr); + + if (process_exists(updating_pid)) + return updating_pid; + + return (pid_t)-1; +} + +/**************************************************************************** + Set the fact that we're doing the update, or have finished doing the update + in th tdb. +****************************************************************************/ + +static void set_updating_pid(fstring printer_name, BOOL delete) +{ + fstring keystr; + TDB_DATA key; + TDB_DATA data; + pid_t updating_pid = sys_getpid(); + + slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", printer_name); + key.dptr = keystr; + key.dsize = strlen(keystr); + + if (delete) { + tdb_delete(tdb, key); + return; + } + data.dptr = (void *)&updating_pid; + data.dsize = sizeof(pid_t); + + tdb_store(tdb, key, data, TDB_REPLACE); +} + +/**************************************************************************** + Send a message saying the queue changed. +****************************************************************************/ + +static void send_queue_message(const char *printer_name, uint32 high, uint32 low) +{ + char msg[8 + sizeof(fstring)]; + SIVAL(msg,0,low); + SIVAL(msg,4,high); + fstrcpy(&msg[8], printer_name); + + message_send_all(conn_tdb_ctx(), MSG_PRINTER_NOTIFY, msg, 8 + strlen(printer_name) + 1, False, NULL); +} + +/**************************************************************************** +update the internal database from the system print queue for a queue in the background +****************************************************************************/ + +static void print_queue_update_background(int snum) +{ + int i, qcount; + print_queue_struct *queue = NULL; + print_status_struct status; + print_status_struct old_status; + struct printjob *pjob; + struct traverse_struct tstruct; + fstring keystr, printer_name, cachestr; + TDB_DATA data, key; + + fstrcpy(printer_name, lp_servicename(snum)); + + /* + * Check to see if someone else is doing this update. + * This is essentially a mutex on the update. + */ + + if (get_updating_pid(printer_name) != -1) + return; + + /* Lock the queue for the database update */ + + slprintf(keystr, sizeof(keystr) - 1, "LOCK/%s", printer_name); + tdb_lock_bystring(tdb, keystr); + + /* + * Ensure that no one else got in here. + * If the updating pid is still -1 then we are + * the winner. + */ + + if (get_updating_pid(printer_name) != -1) { + /* + * Someone else is doing the update, exit. + */ + tdb_unlock_bystring(tdb, keystr); + return; + } + + /* + * We're going to do the update ourselves. + */ + + /* Tell others we're doing the update. */ + set_updating_pid(printer_name, False); + + /* + * Allow others to enter and notice we're doing + * the update. + */ + + tdb_unlock_bystring(tdb, keystr); + + /* + * Update the cache time FIRST ! Stops others even + * attempting to get the lock and doing this + * if the lpq takes a long time. + */ + + slprintf(cachestr, sizeof(cachestr)-1, "CACHE/%s", printer_name); + tdb_store_int32(tdb, cachestr, (int)time(NULL)); - buf->job = atoi(tok[JOBTOK]); - buf->size = atoi(tok[TOTALTOK]); - buf->status = strequal(tok[RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED; - buf->time = time(NULL); - StrnCpy(buf->user,tok[USERTOK],sizeof(buf->user)-1); - StrnCpy(buf->file,tok[FILETOK],sizeof(buf->file)-1); -#ifdef PRIOTOK - buf->priority = atoi(tok[PRIOTOK]); -#else - buf->priority = 1; -#endif - return(True); -} - - - -/******************************************************************* -parse lpq on an aix system - -Queue Dev Status Job Files User PP % Blks Cp Rnk -------- ----- --------- --- ------------------ ---------- ---- -- ----- --- --- -lazer lazer READY -lazer lazer RUNNING 537 6297doc.A kvintus@IE 0 10 2445 1 1 - QUEUED 538 C.ps root@IEDVB 124 1 2 - QUEUED 539 E.ps root@IEDVB 28 1 3 - QUEUED 540 L.ps root@IEDVB 172 1 4 - QUEUED 541 P.ps root@IEDVB 22 1 5 -********************************************************************/ -static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first) -{ - string tok[11]; - int count=0; - - /* handle the case of "(standard input)" as a filename */ - string_sub(line,"standard input","STDIN"); - string_sub(line,"(","\""); - string_sub(line,")","\""); - - for (count=0; count<10 && next_token(&line,tok[count],NULL); count++) ; - - /* we must get 6 tokens */ - if (count < 10) - { - if ((count == 7) && (strcmp(tok[0],"QUEUED") == 0)) - { - /* the 2nd and 5th columns must be integer */ - if (!isdigit(*tok[1]) || !isdigit(*tok[4])) return(False); - buf->size = atoi(tok[4]) * 1024; - /* if the fname contains a space then use STDIN */ - if (strchr(tok[2],' ')) - strcpy(tok[2],"STDIN"); - - /* only take the last part of the filename */ - { - string tmp; - char *p = strrchr(tok[2],'/'); - if (p) - { - strcpy(tmp,p+1); - strcpy(tok[2],tmp); - } - } - - - buf->job = atoi(tok[1]); - buf->status = LPQ_QUEUED; - buf->priority = 0; - buf->time = time(NULL); - StrnCpy(buf->user,tok[3],sizeof(buf->user)-1); - StrnCpy(buf->file,tok[2],sizeof(buf->file)-1); - } - else - { - DEBUG(6,("parse_lpq_aix count=%d\n", count)); - return(False); - } - } - else - { - /* the 4th and 9th columns must be integer */ - if (!isdigit(*tok[3]) || !isdigit(*tok[8])) return(False); - buf->size = atoi(tok[8]) * 1024; - /* if the fname contains a space then use STDIN */ - if (strchr(tok[4],' ')) - strcpy(tok[4],"STDIN"); - - /* only take the last part of the filename */ - { - string tmp; - char *p = strrchr(tok[4],'/'); - if (p) - { - strcpy(tmp,p+1); - strcpy(tok[4],tmp); - } - } - - - buf->job = atoi(tok[3]); - buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED; - buf->priority = 0; - buf->time = time(NULL); - StrnCpy(buf->user,tok[5],sizeof(buf->user)-1); - StrnCpy(buf->file,tok[4],sizeof(buf->file)-1); - } - - - return(True); -} - - -/**************************************************************************** -parse a lpq line -here is an example of lpq output under hpux; note there's no space after -o ! -$> lpstat -oljplus -ljplus-2153 user priority 0 Jan 19 08:14 on ljplus - util.c 125697 bytes - server.c 110712 bytes -ljplus-2154 user priority 0 Jan 19 08:14 from client - (standard input) 7551 bytes -****************************************************************************/ -static BOOL parse_lpq_hpux(char * line, print_queue_struct *buf, BOOL first) -{ - /* must read two lines to process, therefore keep some values static */ - static BOOL header_line_ok=False, base_prio_reset=False; - static string jobuser; - static int jobid; - static int jobprio; - static time_t jobtime; - static int jobstat=LPQ_QUEUED; - /* to store minimum priority to print, lpstat command should be invoked - with -p option first, to work */ - static int base_prio; + /* get the current queue using the appropriate interface */ + ZERO_STRUCT(status); + + qcount = (*(current_printif->queue_get))(snum, &queue, &status); + + DEBUG(3, ("%d job%s in queue for %s\n", qcount, (qcount != 1) ? + "s" : "", printer_name)); + + /* + any job in the internal database that is marked as spooled + and doesn't exist in the system queue is considered finished + and removed from the database + + any job in the system database but not in the internal database + is added as a unix job + + fill in any system job numbers as we go + */ + for (i=0; i<qcount; i++) { + int jobid = print_parse_jobid(queue[i].fs_file); + + if (jobid == -1) { + /* assume its a unix print job */ + print_unix_job(snum, &queue[i]); + continue; + } + + /* we have an active SMB print job - update its status */ + pjob = print_job_find(jobid); + if (!pjob) { + /* err, somethings wrong. Probably smbd was restarted + with jobs in the queue. All we can do is treat them + like unix jobs. Pity. */ + print_unix_job(snum, &queue[i]); + continue; + } + + pjob->sysjob = queue[i].job; + pjob->status = queue[i].status; + + print_job_store(jobid, pjob); + } + + /* now delete any queued entries that don't appear in the + system queue */ + tstruct.queue = queue; + tstruct.qcount = qcount; + tstruct.snum = snum; + tstruct.total_jobs = 0; + + tdb_traverse(tdb, traverse_fn_delete, (void *)&tstruct); + + safe_free(tstruct.queue); + + tdb_store_int32(tdb, "INFO/total_jobs", tstruct.total_jobs); + + /* + * Get the old print status. We will use this to compare the + * number of jobs. If they have changed we need to send a + * "changed" message to the smbds. + */ + + if( qcount != get_queue_status(snum, &old_status)) { + DEBUG(10,("print_queue_update: queue status change %d jobs -> %d jobs for printer %s\n", + old_status.qcount, qcount, printer_name )); + send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB); + } + + /* store the new queue status structure */ + slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printer_name); + key.dptr = keystr; + key.dsize = strlen(keystr); + + status.qcount = qcount; + data.dptr = (void *)&status; + data.dsize = sizeof(status); + tdb_store(tdb, key, data, TDB_REPLACE); + + /* + * Update the cache time again. We want to do this call + * as little as possible... + */ + + slprintf(keystr, sizeof(keystr)-1, "CACHE/%s", printer_name); + tdb_store_int32(tdb, keystr, (int)time(NULL)); + + /* Delete our pid from the db. */ + set_updating_pid(printer_name, True); +} + +/**************************************************************************** +this is the receive function of the background lpq updater +****************************************************************************/ +static void print_queue_receive(int msg_type, pid_t src, void *buf, size_t len) +{ + int snum; + snum=*((int *)buf); + print_queue_update_background(snum); +} + +static pid_t background_lpq_updater_pid; + +/**************************************************************************** +main thread of the background lpq updater +****************************************************************************/ +void start_background_queue(void) +{ + DEBUG(3,("start_background_queue: Starting background LPQ thread\n")); + background_lpq_updater_pid = sys_fork(); + + if (background_lpq_updater_pid == -1) { + DEBUG(5,("start_background_queue: background LPQ thread failed to start. %s\n", strerror(errno) )); + exit(1); + } + + if(background_lpq_updater_pid == 0) { + /* Child. */ + DEBUG(5,("start_background_queue: background LPQ thread started\n")); + + claim_connection(NULL,"smbd lpq backend",0,False); + + if (!locking_init(0)) + exit(1); + + if (!print_backend_init()) + exit(1); + + message_register(MSG_PRINTER_UPDATE, print_queue_receive); + + DEBUG(5,("start_background_queue: background LPQ thread waiting for messages\n")); + while (1) { + pause(); + DEBUG(10,("start_background_queue: background LPQ thread got a message\n")); + message_dispatch(); + } + } +} + +/**************************************************************************** +update the internal database from the system print queue for a queue +****************************************************************************/ +static void print_queue_update(int snum) +{ + message_send_pid(background_lpq_updater_pid, MSG_PRINTER_UPDATE, &snum, sizeof(snum), False); +} + +/**************************************************************************** +check if a jobid is valid. It is valid if it exists in the database +****************************************************************************/ +BOOL print_job_exists(int jobid) +{ + return tdb_exists(tdb, print_key(jobid)); +} + + +/**************************************************************************** +work out which service a jobid is for +note that we have to look up by queue name to ensure that it works for +other than the process that started the job +****************************************************************************/ +int print_job_snum(int jobid) +{ + struct printjob *pjob = print_job_find(jobid); + if (!pjob) return -1; + + return find_service(pjob->queuename); +} + +/**************************************************************************** +give the fd used for a jobid +****************************************************************************/ +int print_job_fd(int jobid) +{ + struct printjob *pjob = print_job_find(jobid); + if (!pjob) return -1; + /* don't allow another process to get this info - it is meaningless */ + if (pjob->pid != local_pid) return -1; + return pjob->fd; +} + +/**************************************************************************** +give the filename used for a jobid +only valid for the process doing the spooling and when the job +has not been spooled +****************************************************************************/ +char *print_job_fname(int jobid) +{ + struct printjob *pjob = print_job_find(jobid); + if (!pjob || pjob->spooled || pjob->pid != local_pid) return NULL; + return pjob->filename; +} + + +/**************************************************************************** +set the place in the queue for a job +****************************************************************************/ +BOOL print_job_set_place(int jobid, int place) +{ + DEBUG(2,("print_job_set_place not implemented yet\n")); + return False; +} + +/**************************************************************************** +set the name of a job. Only possible for owner +****************************************************************************/ +BOOL print_job_set_name(int jobid, char *name) +{ + struct printjob *pjob = print_job_find(jobid); + if (!pjob || pjob->pid != local_pid) return False; + + fstrcpy(pjob->jobname, name); + return print_job_store(jobid, pjob); +} + + +/**************************************************************************** +delete a print job - don't update queue +****************************************************************************/ +static BOOL print_job_delete1(int jobid) +{ + struct printjob *pjob = print_job_find(jobid); + int snum, result = 0; + + if (!pjob) return False; + + /* + * If already deleting just return. + */ + + if (pjob->status == LPQ_DELETING) + return True; + + snum = print_job_snum(jobid); + if (snum == -1) { + DEBUG(5,("print_job_delete1: unknown service number for jobid %d\n", jobid)); + return False; + } + + /* Hrm - we need to be able to cope with deleting a job before it + has reached the spooler. */ + + if (pjob->sysjob == -1) { + DEBUG(5, ("attempt to delete job %d not seen by lpr\n", + jobid)); + } + + /* Set the tdb entry to be deleting. */ + + pjob->status = LPQ_DELETING; + print_job_store(jobid, pjob); + + if (pjob->spooled && pjob->sysjob != -1) + result = (*(current_printif->job_delete))(snum, pjob); + + /* Delete the tdb entry if the delete suceeded or the job hasn't + been spooled. */ + + if (result == 0) { + tdb_delete(tdb, print_key(jobid)); + } + + return (result == 0); +} + +/**************************************************************************** +return true if the current user owns the print job +****************************************************************************/ +static BOOL is_owner(struct current_user *user, int jobid) +{ + struct printjob *pjob = print_job_find(jobid); + user_struct *vuser; + + if (!pjob || !user) return False; + + if ((vuser = get_valid_user_struct(user->vuid)) != NULL) { + return strequal(pjob->user, vuser->user.smb_name); + } else { + return strequal(pjob->user, uidtoname(user->uid)); + } +} + +/**************************************************************************** +delete a print job +****************************************************************************/ +BOOL print_job_delete(struct current_user *user, int jobid, WERROR *errcode) +{ + int snum = print_job_snum(jobid); + char *printer_name; + BOOL owner; + + if (snum == -1) { + DEBUG(5,("print_job_delete: unknown service number for jobid %d\n", jobid)); + return False; + } + + owner = is_owner(user, jobid); + + /* Check access against security descriptor or whether the user + owns their job. */ + + if (!owner && + !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) { + DEBUG(3, ("delete denied by security descriptor\n")); + *errcode = WERR_ACCESS_DENIED; + return False; + } + + if (!print_job_delete1(jobid)) return False; + + /* force update the database and say the delete failed if the + job still exists */ + + print_queue_update(snum); + + /* Send a printer notify message */ + + printer_name = PRINTERNAME(snum); + + send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB); + + return !print_job_exists(jobid); +} + + +/**************************************************************************** +pause a job +****************************************************************************/ +BOOL print_job_pause(struct current_user *user, int jobid, WERROR *errcode) +{ + struct printjob *pjob = print_job_find(jobid); + int snum, ret = -1; + char *printer_name; + + if (!pjob || !user) return False; + + if (!pjob->spooled || pjob->sysjob == -1) return False; + + snum = print_job_snum(jobid); + if (snum == -1) { + DEBUG(5,("print_job_pause: unknown service number for jobid %d\n", jobid)); + return False; + } + if (snum == -1) { + DEBUG(5,("print_job_resume: unknown service number for jobid %d\n", jobid)); + return False; + } + + if (!is_owner(user, jobid) && + !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) { + DEBUG(3, ("pause denied by security descriptor\n")); + *errcode = WERR_ACCESS_DENIED; + return False; + } + + /* need to pause the spooled entry */ + ret = (*(current_printif->job_pause))(snum, pjob); + + if (ret != 0) { + *errcode = WERR_INVALID_PARAM; + return False; + } + + /* force update the database */ + print_cache_flush(snum); + + /* Send a printer notify message */ + + printer_name = PRINTERNAME(snum); + + send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB); + + /* how do we tell if this succeeded? */ + + return True; +} + +/**************************************************************************** +resume a job +****************************************************************************/ +BOOL print_job_resume(struct current_user *user, int jobid, WERROR *errcode) +{ + struct printjob *pjob = print_job_find(jobid); + char *printer_name; + int snum, ret; + + if (!pjob || !user) return False; + + if (!pjob->spooled || pjob->sysjob == -1) return False; + + snum = print_job_snum(jobid); + + if (!is_owner(user, jobid) && + !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) { + DEBUG(3, ("resume denied by security descriptor\n")); + *errcode = WERR_ACCESS_DENIED; + return False; + } + + ret = (*(current_printif->job_resume))(snum, pjob); + + if (ret != 0) { + *errcode = WERR_INVALID_PARAM; + return False; + } + + /* force update the database */ + print_cache_flush(snum); + + /* Send a printer notify message */ + + printer_name = PRINTERNAME(snum); + + send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB); + + return True; +} + +/**************************************************************************** +write to a print file +****************************************************************************/ +int print_job_write(int jobid, const char *buf, int size) +{ + int return_code; + struct printjob *pjob = print_job_find(jobid); + + if (!pjob) + return -1; + /* don't allow another process to get this info - it is meaningless */ + if (pjob->pid != local_pid) + return -1; + + return_code = write(pjob->fd, buf, size); + if (return_code>0) { + pjob->size += size; + print_job_store(jobid, pjob); + } + return return_code; +} + +/**************************************************************************** + Check if the print queue has been updated recently enough. +****************************************************************************/ + +static BOOL print_cache_expired(int snum) +{ + fstring key; + time_t last_qscan_time, time_now = time(NULL); + + slprintf(key, sizeof(key), "CACHE/%s", lp_servicename(snum)); + last_qscan_time = (time_t)tdb_fetch_int32(tdb, key); + + /* + * Invalidate the queue for 3 reasons. + * (1). last queue scan time == -1. + * (2). Current time - last queue scan time > allowed cache time. + * (3). last queue scan time > current time + MAX_CACHE_VALID_TIME (1 hour by default). + * This last test picks up machines for which the clock has been moved + * forward, an lpq scan done and then the clock moved back. Otherwise + * that last lpq scan would stay around for a loooong loooong time... :-). JRA. + */ + + if (last_qscan_time == ((time_t)-1) || (time_now - last_qscan_time) >= lp_lpqcachetime() || + last_qscan_time > (time_now + MAX_CACHE_VALID_TIME)) { + DEBUG(3, ("print cache expired for queue %s \ +(last_qscan_time = %d, time now = %d, qcachetime = %d)\n", lp_servicename(snum), + (int)last_qscan_time, (int)time_now, (int)lp_lpqcachetime() )); + return True; + } + return False; +} + +/**************************************************************************** + Get the queue status - do not update if db is out of date. +****************************************************************************/ +static int get_queue_status(int snum, print_status_struct *status) +{ + fstring keystr; + TDB_DATA data, key; + + ZERO_STRUCTP(status); + slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", lp_servicename(snum)); + key.dptr = keystr; + key.dsize = strlen(keystr); + data = tdb_fetch(tdb, key); + if (data.dptr) { + if (data.dsize == sizeof(print_status_struct)) { + memcpy(status, data.dptr, sizeof(print_status_struct)); + } + free(data.dptr); + } + return status->qcount; +} + +/**************************************************************************** + Determine the number of jobs in a queue. +****************************************************************************/ + +int print_queue_length(int snum, print_status_struct *pstatus) +{ + print_status_struct status; + int len; - int count; - char TAB = '\011'; - string tok[12]; - - /* If a line begins with a horizontal TAB, it is a subline type */ - - if (line[0] == TAB) { /* subline */ - /* check if it contains the base priority */ - if (!strncmp(line,"\tfence priority : ",18)) { - base_prio=atoi(&line[18]); - DEBUG(4, ("fence priority set at %d\n", base_prio)); - } - if (!header_line_ok) return (False); /* incorrect header line */ - /* handle the case of "(standard input)" as a filename */ - string_sub(line,"standard input","STDIN"); - string_sub(line,"(","\""); - string_sub(line,")","\""); - - for (count=0; count<2 && next_token(&line,tok[count],NULL); count++) ; - /* we must get 2 tokens */ - if (count < 2) return(False); - - /* the 2nd column must be integer */ - if (!isdigit(*tok[1])) return(False); - - /* if the fname contains a space then use STDIN */ - if (strchr(tok[0],' ')) - strcpy(tok[0],"STDIN"); - - buf->size = atoi(tok[1]); - StrnCpy(buf->file,tok[0],sizeof(buf->file)-1); - - /* fill things from header line */ - buf->time = jobtime; - buf->job = jobid; - buf->status = jobstat; - buf->priority = jobprio; - StrnCpy(buf->user,jobuser,sizeof(buf->user)-1); - - return(True); - } - else { /* header line */ - header_line_ok=False; /* reset it */ - if (first) { - if (!base_prio_reset) { - base_prio=0; /* reset it */ - base_prio_reset=True; - } - } - else if (base_prio) base_prio_reset=False; - - /* handle the dash in the job id */ - string_sub(line,"-"," "); - - for (count=0; count<12 && next_token(&line,tok[count],NULL); count++) ; - - /* we must get 8 tokens */ - if (count < 8) return(False); - - /* first token must be printer name (cannot check ?) */ - /* the 2nd, 5th & 7th column must be integer */ - if (!isdigit(*tok[1]) || !isdigit(*tok[4]) || !isdigit(*tok[6])) return(False); - jobid = atoi(tok[1]); - StrnCpy(jobuser,tok[2],sizeof(buf->user)-1); - jobprio = atoi(tok[4]); - - /* process time */ - jobtime=EntryTime(tok, 5, count, 8); - if (jobprio < base_prio) { - jobstat = LPQ_PAUSED; - DEBUG (4, ("job %d is paused: prio %d < %d; jobstat=%d\n", jobid, jobprio, base_prio, jobstat)); - } - else { - jobstat = LPQ_QUEUED; - if ((count >8) && (((strequal(tok[8],"on")) || - ((strequal(tok[8],"from")) && - ((count > 10)&&(strequal(tok[10],"on"))))))) - jobstat = LPQ_PRINTING; - } - - header_line_ok=True; /* information is correct */ - return(False); /* need subline info to include into queuelist */ - } -} - - -/**************************************************************************** -parse a lpq line - -here is an example of "lpstat -o dcslw" output under sysv - -dcslw-896 tridge 4712 Dec 20 10:30:30 on dcslw -dcslw-897 tridge 4712 Dec 20 10:30:30 being held - -****************************************************************************/ -static BOOL parse_lpq_sysv(char *line,print_queue_struct *buf,BOOL first) -{ - string tok[9]; - int count=0; - char *p; - - /* handle the dash in the job id */ - string_sub(line,"-"," "); - - for (count=0; count<9 && next_token(&line,tok[count],NULL); count++) ; - - /* we must get 7 tokens */ - if (count < 7) - return(False); - - /* the 2nd and 4th, 6th columns must be integer */ - if (!isdigit(*tok[1]) || !isdigit(*tok[3])) return(False); - if (!isdigit(*tok[5])) return(False); - - /* if the user contains a ! then trim the first part of it */ - if ((p=strchr(tok[2],'!'))) - { - string tmp; - strcpy(tmp,p+1); - strcpy(tok[2],tmp); - } - - - buf->job = atoi(tok[1]); - buf->size = atoi(tok[3]); - if (count > 7 && strequal(tok[7],"on")) - buf->status = LPQ_PRINTING; - else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held")) - buf->status = LPQ_PAUSED; - else - buf->status = LPQ_QUEUED; - buf->priority = 0; - buf->time = EntryTime(tok, 4, count, 7); - StrnCpy(buf->user,tok[2],sizeof(buf->user)-1); - StrnCpy(buf->file,tok[2],sizeof(buf->file)-1); - return(True); -} - -/**************************************************************************** -parse a lpq line - -here is an example of lpq output under qnx -Spooler: /qnx/spooler, on node 1 -Printer: txt (ready) -0000: root [job #1 ] active 1146 bytes /etc/profile -0001: root [job #2 ] ready 2378 bytes /etc/install -0002: root [job #3 ] ready 1146 bytes -- standard input -- -****************************************************************************/ -static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first) -{ - string tok[7]; - int count=0; - - DEBUG(0,("antes [%s]\n", line)); - - /* handle the case of "-- standard input --" as a filename */ - string_sub(line,"standard input","STDIN"); - DEBUG(0,("despues [%s]\n", line)); - string_sub(line,"-- ","\""); - string_sub(line," --","\""); - DEBUG(0,("despues 1 [%s]\n", line)); - - string_sub(line,"[job #",""); - string_sub(line,"]",""); - DEBUG(0,("despues 2 [%s]\n", line)); - - - - for (count=0; count<7 && next_token(&line,tok[count],NULL); count++) ; - - /* we must get 7 tokens */ - if (count < 7) - return(False); - - /* the 3rd and 5th columns must be integer */ - if (!isdigit(*tok[2]) || !isdigit(*tok[4])) return(False); + /* make sure the database is up to date */ + if (print_cache_expired(snum)) + print_queue_update(snum); + + /* also fetch the queue status */ + memset(&status, 0, sizeof(status)); + len = get_queue_status(snum, &status); + if (pstatus) + *pstatus = status; + return len; +} + +/**************************************************************************** + Determine the number of jobs in all queues. +****************************************************************************/ +static int get_total_jobs(int snum) +{ + int total_jobs; + + /* make sure the database is up to date */ + if (print_cache_expired(snum)) print_queue_update(snum); + + total_jobs = tdb_fetch_int32(tdb, "INFO/total_jobs"); + if (total_jobs >0) + return total_jobs; + else + return 0; +} + +/*************************************************************************** +start spooling a job - return the jobid +***************************************************************************/ +int print_job_start(struct current_user *user, int snum, char *jobname) +{ + int jobid; + char *path; + struct printjob pjob; + int next_jobid; + user_struct *vuser; + int njobs = 0; + + errno = 0; + + if (!print_access_check(user, snum, PRINTER_ACCESS_USE)) { + DEBUG(3, ("print_job_start: job start denied by security descriptor\n")); + return -1; + } + + if (!print_time_access_check(snum)) { + DEBUG(3, ("print_job_start: job start denied by time check\n")); + return -1; + } + + path = lp_pathname(snum); + + /* see if we have sufficient disk space */ + if (lp_minprintspace(snum)) { + SMB_BIG_UINT dspace, dsize; + if (sys_fsusage(path, &dspace, &dsize) == 0 && + dspace < 2*(SMB_BIG_UINT)lp_minprintspace(snum)) { + DEBUG(3, ("print_job_start: disk space check failed.\n")); + errno = ENOSPC; + return -1; + } + } + + /* for autoloaded printers, check that the printcap entry still exists */ + if (lp_autoloaded(snum) && !pcap_printername_ok(lp_servicename(snum), NULL)) { + DEBUG(3, ("print_job_start: printer name %s check failed.\n", lp_servicename(snum) )); + errno = ENOENT; + return -1; + } + + /* Insure the maximum queue size is not violated */ + if (lp_maxprintjobs(snum) && (njobs = print_queue_length(snum,NULL)) > lp_maxprintjobs(snum)) { + DEBUG(3, ("print_job_start: number of jobs (%d) larger than max printjobs per queue (%d).\n", + njobs, lp_maxprintjobs(snum) )); + errno = ENOSPC; + return -1; + } + + /* Insure the maximum print jobs in the system is not violated */ + if (lp_totalprintjobs() && get_total_jobs(snum) > lp_totalprintjobs()) { + DEBUG(3, ("print_job_start: number of jobs (%d) larger than max printjobs per system (%d).\n", + njobs, lp_totalprintjobs() )); + errno = ENOSPC; + return -1; + } + + /* create the database entry */ + ZERO_STRUCT(pjob); + pjob.pid = local_pid; + pjob.sysjob = -1; + pjob.fd = -1; + pjob.starttime = time(NULL); + pjob.status = LPQ_SPOOLING; + pjob.size = 0; + pjob.spooled = False; + pjob.smbjob = True; + + fstrcpy(pjob.jobname, jobname); + + if ((vuser = get_valid_user_struct(user->vuid)) != NULL) { + fstrcpy(pjob.user, vuser->user.smb_name); + } else { + fstrcpy(pjob.user, uidtoname(user->uid)); + } + + fstrcpy(pjob.queuename, lp_servicename(snum)); + + /* lock the database */ + tdb_lock_bystring(tdb, "INFO/nextjob"); + + next_jobid = tdb_fetch_int32(tdb, "INFO/nextjob"); + if (next_jobid == -1) + next_jobid = 1; + + for (jobid = NEXT_JOBID(next_jobid); jobid != next_jobid; jobid = NEXT_JOBID(jobid)) { + if (!print_job_exists(jobid)) + break; + } + if (jobid == next_jobid || !print_job_store(jobid, &pjob)) { + DEBUG(3, ("print_job_start: either jobid (%d)==next_jobid(%d) or print_job_store failed.\n", + jobid, next_jobid )); + jobid = -1; + goto fail; + } + + tdb_store_int32(tdb, "INFO/nextjob", jobid); + + /* we have a job entry - now create the spool file */ + slprintf(pjob.filename, sizeof(pjob.filename)-1, "%s/%s%.6d.XXXXXX", + path, PRINT_SPOOL_PREFIX, jobid); + pjob.fd = smb_mkstemp(pjob.filename); + + if (pjob.fd == -1) { + if (errno == EACCES) { + /* Common setup error, force a report. */ + DEBUG(0, ("print_job_start: insufficient permissions \ +to open spool file %s.\n", pjob.filename)); + } else { + /* Normal case, report at level 3 and above. */ + DEBUG(3, ("print_job_start: can't open spool file %s,\n", pjob.filename)); + DEBUGADD(3, ("errno = %d (%s).\n", errno, strerror(errno))); + } + goto fail; + } + + print_job_store(jobid, &pjob); + + tdb_unlock_bystring(tdb, "INFO/nextjob"); + + /* + * If the printer is marked as postscript output a leading + * file identifier to ensure the file is treated as a raw + * postscript file. + * This has a similar effect as CtrlD=0 in WIN.INI file. + * tim@fsg.com 09/06/94 + */ + if (lp_postscript(snum)) { + print_job_write(jobid, "%!\n",3); + } + + return jobid; + + fail: + if (jobid != -1) { + tdb_delete(tdb, print_key(jobid)); + } + + tdb_unlock_bystring(tdb, "INFO/nextjob"); + + DEBUG(3, ("print_job_start: returning fail. Error = %s\n", strerror(errno) )); + return -1; +} + +/**************************************************************************** + Update the number of pages spooled to jobid +****************************************************************************/ + +void print_job_endpage(int jobid) +{ + struct printjob *pjob = print_job_find(jobid); + if (!pjob) + return; + /* don't allow another process to get this info - it is meaningless */ + if (pjob->pid != local_pid) + return; + + pjob->page_count++; + print_job_store(jobid, pjob); +} + +/**************************************************************************** + Print a file - called on closing the file. This spools the job. + If normal close is false then we're tearing down the jobs - treat as an + error. +****************************************************************************/ + +BOOL print_job_end(int jobid, BOOL normal_close) +{ + struct printjob *pjob = print_job_find(jobid); + int snum, ret; + SMB_STRUCT_STAT sbuf; + + if (!pjob) + return False; + + if (pjob->spooled || pjob->pid != local_pid) + return False; + + snum = print_job_snum(jobid); + if (snum == -1) { + DEBUG(5,("print_job_end: unknown service number for jobid %d\n", jobid)); + return False; + } + + if (normal_close && (sys_fstat(pjob->fd, &sbuf) == 0)) { + pjob->size = sbuf.st_size; + close(pjob->fd); + pjob->fd = -1; + } else { + + /* + * Not a normal close or we couldn't stat the job file, + * so something has gone wrong. Cleanup. + */ + close(pjob->fd); + pjob->fd = -1; + DEBUG(3,("print_job_end: failed to stat file for jobid %d\n", jobid )); + goto fail; + } + + /* Technically, this is not quit right. If the printer has a separator + * page turned on, the NT spooler prints the separator page even if the + * print job is 0 bytes. 010215 JRR */ + if (pjob->size == 0 || pjob->status == LPQ_DELETING) { + /* don't bother spooling empty files or something being deleted. */ + DEBUG(5,("print_job_end: canceling spool of %s (%s)\n", + pjob->filename, pjob->size ? "deleted" : "zero length" )); + unlink(pjob->filename); + tdb_delete(tdb, print_key(jobid)); + return True; + } + + ret = (*(current_printif->job_submit))(snum, pjob); + + if (ret) + goto fail; + + /* The print job has been sucessfully handed over to the back-end */ + + pjob->spooled = True; + pjob->status = LPQ_QUEUED; + print_job_store(jobid, pjob); + + /* make sure the database is up to date */ + if (print_cache_expired(snum)) + print_queue_update(snum); + + return True; + +fail: + + /* The print job was not succesfully started. Cleanup */ + /* Still need to add proper error return propagation! 010122:JRR */ + unlink(pjob->filename); + tdb_delete(tdb, print_key(jobid)); + return False; +} + +/* utility fn to enumerate the print queue */ +static int traverse_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state) +{ + struct traverse_struct *ts = (struct traverse_struct *)state; + struct printjob pjob; + int i, jobid; + + if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) return 0; + memcpy(&jobid, key.dptr, sizeof(jobid)); + memcpy(&pjob, data.dptr, sizeof(pjob)); + + /* maybe it isn't for this queue */ + if (ts->snum != lp_servicenumber(pjob.queuename)) + return 0; + + if (ts->qcount >= ts->maxcount) return 0; + + i = ts->qcount; + + ts->queue[i].job = jobid; + ts->queue[i].size = pjob.size; + ts->queue[i].page_count = pjob.page_count; + ts->queue[i].status = pjob.status; + ts->queue[i].priority = 1; + ts->queue[i].time = pjob.starttime; + fstrcpy(ts->queue[i].fs_user, pjob.user); + fstrcpy(ts->queue[i].fs_file, pjob.jobname); + + ts->qcount++; + + return 0; +} + +struct traverse_count_struct { + int snum, count; +}; + +/* utility fn to count the number of entries in the print queue */ +static int traverse_count_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state) +{ + struct traverse_count_struct *ts = (struct traverse_count_struct *)state; + struct printjob pjob; + int jobid; + + if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) return 0; + memcpy(&jobid, key.dptr, sizeof(jobid)); + memcpy(&pjob, data.dptr, sizeof(pjob)); + + /* maybe it isn't for this queue */ + if (ts->snum != lp_servicenumber(pjob.queuename)) + return 0; + + ts->count++; + + return 0; +} + +/* Sort print jobs by submittal time */ + +static int printjob_comp(print_queue_struct *j1, print_queue_struct *j2) +{ + /* Silly cases */ + + if (!j1 && !j2) return 0; + if (!j1) return -1; + if (!j2) return 1; + + /* Sort on job start time */ + + if (j1->time == j2->time) return 0; + return (j1->time > j2->time) ? 1 : -1; +} + +/**************************************************************************** +get a printer queue listing +****************************************************************************/ +int print_queue_status(int snum, + print_queue_struct **queue, + print_status_struct *status) +{ + struct traverse_struct tstruct; + struct traverse_count_struct tsc; + fstring keystr; + TDB_DATA data, key; - /* only take the last part of the filename */ - { - string tmp; - char *p = strrchr(tok[6],'/'); - if (p) - { - strcpy(tmp,p+1); - strcpy(tok[6],tmp); - } - } + /* make sure the database is up to date */ + if (print_cache_expired(snum)) print_queue_update(snum); + + *queue = NULL; + + /* + * Fetch the queue status. We must do this first, as there may + * be no jobs in the queue. + */ + ZERO_STRUCTP(status); + slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", lp_servicename(snum)); + key.dptr = keystr; + key.dsize = strlen(keystr); + data = tdb_fetch(tdb, key); + if (data.dptr) { + if (data.dsize == sizeof(*status)) { + memcpy(status, data.dptr, sizeof(*status)); + } + free(data.dptr); + } + + /* + * Now, fetch the print queue information. We first count the number + * of entries, and then only retrieve the queue if necessary. + */ + tsc.count = 0; + tsc.snum = snum; + tdb_traverse(tdb, traverse_count_fn_queue, (void *)&tsc); + + if (tsc.count == 0) + return 0; + + /* Allocate the queue size. */ + if ((tstruct.queue = (print_queue_struct *) + malloc(sizeof(print_queue_struct)*tsc.count)) + == NULL) + return 0; + + /* + * Fill in the queue. + * We need maxcount as the queue size may have changed between + * the two calls to tdb_traverse. + */ + tstruct.qcount = 0; + tstruct.maxcount = tsc.count; + tstruct.snum = snum; + + tdb_traverse(tdb, traverse_fn_queue, (void *)&tstruct); + + /* Sort the queue by submission time otherwise they are displayed + in hash order. */ + + qsort(tstruct.queue, tstruct.qcount, sizeof(print_queue_struct), + QSORT_CAST(printjob_comp)); + + *queue = tstruct.queue; + return tstruct.qcount; +} + + +/**************************************************************************** +turn a queue name into a snum +****************************************************************************/ +int print_queue_snum(char *qname) +{ + int snum = lp_servicenumber(qname); + if (snum == -1 || !lp_print_ok(snum)) return -1; + return snum; +} + + +/**************************************************************************** + pause a queue +****************************************************************************/ +BOOL print_queue_pause(struct current_user *user, int snum, WERROR *errcode) +{ + char *printer_name; + int ret; + + if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) { + *errcode = WERR_ACCESS_DENIED; + return False; + } + + ret = (*(current_printif->queue_pause))(snum); + + if (ret != 0) { + *errcode = WERR_INVALID_PARAM; + return False; + } + + /* force update the database */ + print_cache_flush(snum); - buf->job = atoi(tok[2]); - buf->size = atoi(tok[4]); - buf->status = strequal(tok[3],"active")?LPQ_PRINTING:LPQ_QUEUED; - buf->priority = 0; - buf->time = time(NULL); - StrnCpy(buf->user,tok[1],sizeof(buf->user)-1); - StrnCpy(buf->file,tok[6],sizeof(buf->file)-1); - return(True); -} - - - -char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL }; -char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL }; -char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL }; - -/**************************************************************************** -parse a lpq line. Choose printing style -****************************************************************************/ -static BOOL parse_lpq_entry(int snum,char *line, - print_queue_struct *buf, - print_status_struct *status,BOOL first) -{ - BOOL ret; - - switch (lp_printing()) - { - case PRINT_SYSV: - ret = parse_lpq_sysv(line,buf,first); - break; - case PRINT_AIX: - ret = parse_lpq_aix(line,buf,first); - break; - case PRINT_HPUX: - ret = parse_lpq_hpux(line,buf,first); - break; - case PRINT_QNX: - ret = parse_lpq_qnx(line,buf,first); - break; - default: - ret = parse_lpq_bsd(line,buf,first); - break; - } - -#ifdef LPQ_GUEST_TO_USER - if (ret) { - extern pstring sesssetup_user; - /* change guest entries to the current logged in user to make - them appear deletable to windows */ - if (sesssetup_user[0] && strequal(buf->user,lp_guestaccount(snum))) - strcpy(buf->user,sesssetup_user); - } -#endif - - if (status && !ret) - { - /* a few simple checks to see if the line might be a - printer status line: - handle them so that most severe condition is shown */ - int i; - strlower(line); - - switch (status->status) { - case LPSTAT_OK: - for (i=0; stat0_strings[i]; i++) - if (strstr(line,stat0_strings[i])) { - StrnCpy(status->message,line,sizeof(status->message)-1); - status->status=LPSTAT_OK; - } - case LPSTAT_STOPPED: - for (i=0; stat1_strings[i]; i++) - if (strstr(line,stat1_strings[i])) { - StrnCpy(status->message,line,sizeof(status->message)-1); - status->status=LPSTAT_STOPPED; - } - case LPSTAT_ERROR: - for (i=0; stat2_strings[i]; i++) - if (strstr(line,stat2_strings[i])) { - StrnCpy(status->message,line,sizeof(status->message)-1); - status->status=LPSTAT_ERROR; - } - break; - } - } - - return(ret); -} - -/**************************************************************************** -get a printer queue -****************************************************************************/ -int get_printqueue(int snum,int cnum,print_queue_struct **queue, - print_status_struct *status) -{ - char *lpq_command = lp_lpqcommand(snum); - char *printername = PRINTERNAME(snum); - int ret=0,count=0; - pstring syscmd; - fstring outfile; - pstring line; - FILE *f; - struct stat sbuf; - BOOL dorun=True; - int cachetime = lp_lpqcachetime(); - int lfd = -1; - - *line = 0; - check_lpq_cache(snum); - - if (!printername || !*printername) - { - DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n", - lp_servicename(snum),snum)); - printername = lp_servicename(snum); - } - - if (!lpq_command || !(*lpq_command)) - { - DEBUG(5,("No lpq command\n")); - return(0); - } - - strcpy(syscmd,lpq_command); - string_sub(syscmd,"%p",printername); - - standard_sub(cnum,syscmd); - - sprintf(outfile,"/tmp/lpq.%08x",str_checksum(syscmd)); - - if (!lpq_cache_reset[snum] && cachetime && !stat(outfile,&sbuf)) - { - if (time(NULL) - sbuf.st_mtime < cachetime) { - DEBUG(3,("Using cached lpq output\n")); - dorun = False; - } - - if (dorun) { - lfd = file_lock(outfile,LPQ_LOCK_TIMEOUT); - if (lfd<0 || - (!fstat(lfd,&sbuf) && (time(NULL) - sbuf.st_mtime)<cachetime)) { - DEBUG(3,("Using cached lpq output\n")); - dorun = False; - file_unlock(lfd); lfd = -1; - } - } - } - - if (dorun) { - ret = smbrun(syscmd,outfile); - DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret)); - } - - lpq_cache_reset[snum] = False; - - f = fopen(outfile,"r"); - if (!f) { - if (lfd >= 0) file_unlock(lfd); - return(0); - } - - if (status) { - strcpy(status->message,""); - status->status = LPSTAT_OK; - } - - while (fgets(line,sizeof(pstring),f)) - { - DEBUG(6,("QUEUE2: %s\n",line)); - - *queue = Realloc(*queue,sizeof(print_queue_struct)*(count+1)); - if (! *queue) - { - count = 0; - break; - } - - bzero((char *)&(*queue)[count],sizeof(**queue)); - - /* parse it */ - if (!parse_lpq_entry(snum,line,&(*queue)[count],status,count==0)) - continue; - - count++; - } - - fclose(f); - - if (lfd >= 0) file_unlock(lfd); - - if (!cachetime) - unlink(outfile); - else - chmod(outfile,0666); - return(count); -} - - -/**************************************************************************** -delete a printer queue entry -****************************************************************************/ -void del_printqueue(int cnum,int snum,int jobid) -{ - char *lprm_command = lp_lprmcommand(snum); - char *printername = PRINTERNAME(snum); - pstring syscmd; - char jobstr[20]; - int ret; - - if (!printername || !*printername) - { - DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n", - lp_servicename(snum),snum)); - printername = lp_servicename(snum); - } - - if (!lprm_command || !(*lprm_command)) - { - DEBUG(5,("No lprm command\n")); - return; - } - - sprintf(jobstr,"%d",jobid); - - strcpy(syscmd,lprm_command); - string_sub(syscmd,"%p",printername); - string_sub(syscmd,"%j",jobstr); - standard_sub(cnum,syscmd); - - ret = smbrun(syscmd,NULL); - DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret)); - lpq_reset(snum); /* queue has changed */ -} - -/**************************************************************************** -change status of a printer queue entry -****************************************************************************/ -void status_printjob(int cnum,int snum,int jobid,int status) -{ - char *lpstatus_command = - (status==LPQ_PAUSED?lp_lppausecommand(snum):lp_lpresumecommand(snum)); - char *printername = PRINTERNAME(snum); - pstring syscmd; - char jobstr[20]; - int ret; - - if (!printername || !*printername) - { - DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n", - lp_servicename(snum),snum)); - printername = lp_servicename(snum); - } - - if (!lpstatus_command || !(*lpstatus_command)) - { - DEBUG(5,("No lpstatus command to %s job\n", - (status==LPQ_PAUSED?"pause":"resume"))); - return; - } - - sprintf(jobstr,"%d",jobid); - - strcpy(syscmd,lpstatus_command); - string_sub(syscmd,"%p",printername); - string_sub(syscmd,"%j",jobstr); - standard_sub(cnum,syscmd); + /* Send a printer notify message */ - ret = smbrun(syscmd,NULL); - DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret)); - lpq_reset(snum); /* queue has changed */ + printer_name = PRINTERNAME(snum); + + send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB); + + return True; } +/**************************************************************************** + resume a queue +****************************************************************************/ +BOOL print_queue_resume(struct current_user *user, int snum, WERROR *errcode) +{ + char *printer_name; + int ret; + + if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) { + *errcode = WERR_ACCESS_DENIED; + return False; + } + + ret = (*(current_printif->queue_resume))(snum); + if (ret != 0) { + *errcode = WERR_INVALID_PARAM; + return False; + } + + /* make sure the database is up to date */ + if (print_cache_expired(snum)) print_queue_update(snum); + + /* Send a printer notify message */ + + printer_name = PRINTERNAME(snum); + + send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB); + + return True; +} + +/**************************************************************************** + purge a queue - implemented by deleting all jobs that we can delete +****************************************************************************/ +BOOL print_queue_purge(struct current_user *user, int snum, WERROR *errcode) +{ + print_queue_struct *queue; + print_status_struct status; + char *printer_name; + int njobs, i; + BOOL can_job_admin; + + /* Force and update so the count is accurate (i.e. not a cached count) */ + print_queue_update(snum); + + can_job_admin = print_access_check(user, snum, JOB_ACCESS_ADMINISTER); + njobs = print_queue_status(snum, &queue, &status); + + for (i=0;i<njobs;i++) { + BOOL owner = is_owner(user, queue[i].job); + + if (owner || can_job_admin) { + print_job_delete1(queue[i].job); + } + } + + safe_free(queue); + + /* Send a printer notify message */ + + printer_name = PRINTERNAME(snum); + + send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB); + + return True; +} |