summaryrefslogtreecommitdiff
path: root/source3/printing
diff options
context:
space:
mode:
Diffstat (limited to 'source3/printing')
-rw-r--r--source3/printing/load.c64
-rw-r--r--source3/printing/lpq_parse.c1152
-rw-r--r--source3/printing/notify.c571
-rw-r--r--source3/printing/nt_printing.c5892
-rw-r--r--source3/printing/pcap.c263
-rw-r--r--source3/printing/print_aix.c123
-rw-r--r--source3/printing/print_cups.c1347
-rw-r--r--source3/printing/print_generic.c301
-rw-r--r--source3/printing/print_iprint.c1240
-rw-r--r--source3/printing/print_svid.c127
-rw-r--r--source3/printing/printfsp.c122
-rw-r--r--source3/printing/printing.c2888
-rw-r--r--source3/printing/printing_db.c215
13 files changed, 14305 insertions, 0 deletions
diff --git a/source3/printing/load.c b/source3/printing/load.c
new file mode 100644
index 0000000000..23144d5a95
--- /dev/null
+++ b/source3/printing/load.c
@@ -0,0 +1,64 @@
+/*
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+
+/***************************************************************************
+auto-load some homes and printer services
+***************************************************************************/
+static void add_auto_printers(void)
+{
+ const char *p;
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ char *str;
+ char *saveptr;
+
+ if (pnum < 0)
+ return;
+
+ if ((str = SMB_STRDUP(lp_auto_services())) == NULL)
+ return;
+
+ for (p = strtok_r(str, LIST_SEP, &saveptr); p;
+ p = strtok_r(NULL, LIST_SEP, &saveptr)) {
+ if (lp_servicenumber(p) >= 0)
+ continue;
+
+ if (pcap_printername_ok(p))
+ lp_add_printer(p, pnum);
+ }
+
+ SAFE_FREE(str);
+}
+
+/***************************************************************************
+load automatic printer services
+***************************************************************************/
+void load_printers(void)
+{
+ if (!pcap_cache_loaded())
+ pcap_cache_reload();
+
+ add_auto_printers();
+
+ /* load all printcap printers */
+ if (lp_load_printers() && lp_servicenumber(PRINTERS_NAME) >= 0)
+ pcap_printer_fn(lp_add_one_printer);
+}
diff --git a/source3/printing/lpq_parse.c b/source3/printing/lpq_parse.c
new file mode 100644
index 0000000000..addf2d14aa
--- /dev/null
+++ b/source3/printing/lpq_parse.c
@@ -0,0 +1,1152 @@
+/*
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+static const char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"};
+
+
+/*******************************************************************
+ Process time fields
+********************************************************************/
+
+static time_t EntryTime(char *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;
+
+ for (i=0; i<13; i++) {
+ if (!strncmp(tok[ptr], Months[i],3)) {
+ break; /* Find month */
+ }
+ }
+
+ if (i<12) {
+ fstring c;
+ t = localtime(&jobtime);
+ if (!t) {
+ return (time_t)-1;
+ }
+ day = atoi(tok[ptr+1]);
+ fstrcpy(c,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;
+ TALLOC_CTX *ctx = talloc_tos();
+ char *line2 = NULL;
+ char *saveptr;
+
+ line2 = talloc_strdup(ctx, line);
+ if (!line2) {
+ return false;
+ }
+
+#ifdef OSF1
+ {
+ size_t length;
+ length = strlen(line2);
+ if (line2[length-3] == ':') {
+ return False;
+ }
+ }
+#endif /* OSF1 */
+
+ /* FIXME: Use next_token_talloc rather than strtok! */
+ tok[0] = strtok_r(line2," \t", &saveptr);
+ count++;
+
+ while ((count < MAXTOK)
+ && ((tok[count] = strtok_r(NULL, " \t", &saveptr)) != NULL)) {
+ 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);
+ fstrcpy(buf->fs_user,tok[USERTOK]);
+ fstrcpy(buf->fs_file,tok[FILETOK]);
+
+ if ((FILETOK + 1) != TOTALTOK) {
+ int i;
+
+ for (i = (FILETOK + 1); i < TOTALTOK; i++) {
+ /* FIXME: Using fstrcat rather than other means is a bit
+ * inefficient; this might be a problem for enormous queues with
+ * many fields. */
+ fstrcat(buf->fs_file, " ");
+ fstrcat(buf->fs_file, tok[i]);
+ }
+ /* 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 (!t) {
+ return (time_t)-1;
+ }
+
+ 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. */
+
+ char *tokarr[LPRNG_MAXTOK];
+ const char *cptr;
+ char *ptr;
+ int num_tok = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ cptr = line;
+ while((num_tok < LPRNG_MAXTOK) && next_token_talloc(frame, &cptr,
+ &tokarr[num_tok], " \t")) {
+ num_tok++;
+ }
+
+ /* We must get at least LPRNG_NTOK tokens. */
+ if (num_tok < LPRNG_NTOK) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ if (!isdigit((int)*tokarr[LPRNG_JOBTOK]) || !isdigit((int)*tokarr[LPRNG_TOTALTOK])) {
+ TALLOC_FREE(frame);
+ 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 (strequal(tokarr[LPRNG_RANKTOK],"done")) {
+ buf->status = LPQ_PRINTED;
+ } 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]);
+
+ fstrcpy(buf->fs_user,tokarr[LPRNG_USERTOK]);
+
+ /* The '@hostname' prevents windows from displaying the printing icon
+ * for the current user on the taskbar. Plop in a null.
+ */
+
+ if ((ptr = strchr_m(buf->fs_user,'@')) != NULL) {
+ *ptr = '\0';
+ }
+
+ fstrcpy(buf->fs_file,tokarr[LPRNG_FILETOK]);
+
+ if ((LPRNG_FILETOK + 1) != LPRNG_TOTALTOK) {
+ int i;
+
+ for (i = (LPRNG_FILETOK + 1); i < LPRNG_TOTALTOK; i++) {
+ /* FIXME: Using fstrcat rather than other means is a bit
+ * inefficient; this might be a problem for enormous queues with
+ * many fields. */
+ fstrcat(buf->fs_file, " ");
+ fstrcat(buf->fs_file, tokarr[i]);
+ }
+ /* Ensure null termination. */
+ buf->fs_file[sizeof(buf->fs_file)-1] = '\0';
+ }
+
+ TALLOC_FREE(frame);
+ 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)
+{
+ char *tok[11];
+ int count=0;
+ const char *cline = line;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* handle the case of "(standard input)" as a filename */
+ string_sub(line,"standard input","STDIN",0);
+ all_string_sub(line,"(","\"",0);
+ all_string_sub(line,")","\"",0);
+
+ for (count=0; count<10 &&
+ next_token_talloc(frame,&cline,&tok[count],NULL); 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])) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+ buf->size = atoi(tok[4]) * 1024;
+ /* if the fname contains a space then use STDIN */
+ if (strchr_m(tok[2],' ')) {
+ tok[2] = talloc_strdup(frame,"STDIN");
+ if (!tok[2]) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ }
+
+ /* only take the last part of the filename */
+ {
+ char *p = strrchr_m(tok[2],'/');
+ if (p) {
+ tok[2] = p+1;
+ }
+ }
+
+ buf->job = atoi(tok[1]);
+ buf->status = strequal(tok[0],"HELD")?LPQ_PAUSED:LPQ_QUEUED;
+ buf->priority = 0;
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user,tok[3]);
+ fstrcpy(buf->fs_file,tok[2]);
+ } else {
+ DEBUG(6,("parse_lpq_aix count=%d\n", count));
+ TALLOC_FREE(frame);
+ return False;
+ }
+ } else {
+ /* the 4th and 9th columns must be integer */
+ if (!isdigit((int)*tok[3]) || !isdigit((int)*tok[8])) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ buf->size = atoi(tok[8]) * 1024;
+ /* if the fname contains a space then use STDIN */
+ if (strchr_m(tok[4],' ')) {
+ tok[4] = talloc_strdup(frame,"STDIN");
+ if (!tok[4]) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ }
+
+ /* only take the last part of the filename */
+ {
+ char *p = strrchr_m(tok[4],'/');
+ if (p) {
+ tok[4] = p+1;
+ }
+ }
+
+ buf->job = atoi(tok[3]);
+ buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED;
+ buf->priority = 0;
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user,tok[5]);
+ fstrcpy(buf->fs_file,tok[4]);
+ }
+
+ TALLOC_FREE(frame);
+ 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 char *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';
+ const char *cline = line;
+ char *tok[12];
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* 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) {
+ TALLOC_FREE(frame);
+ return False; /* incorrect header line */
+ }
+
+ /* handle the case of "(standard input)" as a filename */
+ string_sub(line,"standard input","STDIN",0);
+ all_string_sub(line,"(","\"",0);
+ all_string_sub(line,")","\"",0);
+
+ for (count=0; count<2 &&
+ next_token_talloc(frame, &cline, &tok[count],NULL);
+ count++) {
+ ;
+ }
+ /* we must get 2 tokens */
+ if (count < 2) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ /* the 2nd column must be integer */
+ if (!isdigit((int)*tok[1])) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ /* if the fname contains a space then use STDIN */
+ if (strchr_m(tok[0],' ')) {
+ tok[0] = talloc_strdup(frame, "STDIN");
+ if (!tok[0]) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ }
+
+ buf->size = atoi(tok[1]);
+ fstrcpy(buf->fs_file,tok[0]);
+
+ /* fill things from header line */
+ buf->time = jobtime;
+ buf->job = jobid;
+ buf->status = jobstat;
+ buf->priority = jobprio;
+ if (jobuser) {
+ fstrcpy(buf->fs_user,jobuser);
+ } else {
+ buf->fs_user[0] = '\0';
+ }
+
+ TALLOC_FREE(frame);
+ 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,"-"," ",0);
+
+ for (count=0; count<12 &&
+ next_token_talloc(frame, &cline, &tok[count],NULL);
+ count++) {
+ ;
+ }
+
+ /* we must get 8 tokens */
+ if (count < 8) {
+ TALLOC_FREE(frame);
+ 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])) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+ jobid = atoi(tok[1]);
+ SAFE_FREE(jobuser);
+ jobuser = SMB_STRDUP(tok[2]);
+ 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 */
+ TALLOC_FREE(frame);
+ 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)
+{
+ char *tok[9];
+ int count=0;
+ char *p;
+ const char *cline = line;
+ TALLOC_CTX *frame = NULL;
+
+ /*
+ * 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 = ' ';
+ }
+
+ frame = talloc_stackframe();
+ for (count=0; count<9 &&
+ next_token_talloc(frame, &cline, &tok[count],NULL);
+ count++) {
+ ;
+ }
+
+ /* we must get 7 tokens */
+ if (count < 7) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ /* the 2nd and 4th, 6th columns must be integer */
+ if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[3])) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+ if (!isdigit((int)*tok[5])) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ /* if the user contains a ! then trim the first part of it */
+ if ((p=strchr_m(tok[2],'!'))) {
+ tok[2] = p+1;
+ }
+
+ 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);
+ fstrcpy(buf->fs_user,tok[2]);
+ fstrcpy(buf->fs_file,tok[2]);
+ TALLOC_FREE(frame);
+ 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)
+{
+ char *tok[7];
+ int count=0;
+ const char *cline = line;
+ TALLOC_CTX *frame = NULL;
+
+ DEBUG(4,("antes [%s]\n", line));
+
+ /* handle the case of "-- standard input --" as a filename */
+ string_sub(line,"standard input","STDIN",0);
+ DEBUG(4,("despues [%s]\n", line));
+ all_string_sub(line,"-- ","\"",0);
+ all_string_sub(line," --","\"",0);
+ DEBUG(4,("despues 1 [%s]\n", line));
+
+ string_sub(line,"[job #","",0);
+ string_sub(line,"]","",0);
+ DEBUG(4,("despues 2 [%s]\n", line));
+
+ frame = talloc_stackframe();
+ for (count=0; count<7 &&
+ next_token_talloc(frame,&cline,&tok[count],NULL);
+ count++) {
+ ;
+ }
+
+ /* we must get 7 tokens */
+ if (count < 7) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ /* the 3rd and 5th columns must be integer */
+ if (!isdigit((int)*tok[2]) || !isdigit((int)*tok[4])) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ /* only take the last part of the filename */
+ {
+ char *p = strrchr_m(tok[6],'/');
+ if (p) {
+ tok[6] = p+1;
+ }
+ }
+
+ 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);
+ fstrcpy(buf->fs_user,tok[1]);
+ fstrcpy(buf->fs_file,tok[6]);
+ TALLOC_FREE(frame);
+ 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)
+{
+ char *tok[11];
+ int count=0;
+ const char *cline = line;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* handle the case of "(standard input)" as a filename */
+ string_sub(line,"stdin","STDIN",0);
+ all_string_sub(line,"(","\"",0);
+ all_string_sub(line,")","\"",0);
+
+ for (count=0; count<11 &&
+ next_token_talloc(frame,&cline,&tok[count],NULL);
+ count++) {
+ ;
+ }
+
+ /* we must get 11 tokens */
+ if (count < 11) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ /* the first must be "active" or begin with an integer */
+ if (strcmp(tok[0],"active") && !isdigit((int)tok[0][0])) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ /* the 5th and 8th must be integer */
+ if (!isdigit((int)*tok[4]) || !isdigit((int)*tok[7])) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ /* if the fname contains a space then use STDIN */
+ if (strchr_m(tok[6],' ')) {
+ tok[6] = talloc_strdup(frame, "STDIN");
+ if (!tok[6]) {
+ TALLOC_FREE(frame);
+ 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[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);
+ fstrcpy(buf->fs_user,tok[1]);
+ fstrcpy(buf->fs_file,tok[6]);
+ TALLOC_FREE(frame);
+ 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;
+
+ char parse_line_char[sizeof(nt_lpq_line)];
+ nt_lpq_line *parse_line = (nt_lpq_line *)parse_line_char;
+#define LPRNT_PRINTING "Printing"
+#define LPRNT_WAITING "Waiting"
+#define LPRNT_PAUSED "Paused"
+
+ memset(parse_line_char, '\0', sizeof(parse_line_char));
+ strncpy(parse_line_char, line, sizeof(parse_line_char) -1);
+
+ if (strlen(parse_line_char) != sizeof(parse_line_char) - 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_char(parse_line->status, '\0', ' ');
+ 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_char(parse_line->jobname, '\0', ' ');
+
+ buf->job = atoi(parse_line->jobid);
+ buf->priority = 0;
+ buf->size = atoi(parse_line->size);
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user, parse_line->owner);
+ fstrcpy(buf->fs_file, parse_line->jobname);
+ 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;
+
+ char parse_line_char[sizeof(os2_lpq_line)];
+ os2_lpq_line *parse_line = (os2_lpq_line *)parse_line_char;
+#define LPROS2_PRINTING "Printing"
+#define LPROS2_WAITING "Queued"
+#define LPROS2_PAUSED "Paused"
+
+ memset(parse_line_char, '\0', sizeof(parse_line_char));
+ strncpy(parse_line_char, line, sizeof(parse_line_char) -1);
+
+ if (strlen(parse_line_char) != sizeof(parse_line_char) - 1) {
+ return False;
+ }
+
+ /* Get the jobid */
+ buf->job = atoi(parse_line->jobid);
+
+ /* Get the job name */
+ parse_line->space2[0] = '\0';
+ trim_char(parse_line->jobname, '\0', ' ');
+ fstrcpy(buf->fs_file, parse_line->jobname);
+
+ 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_char(parse_line->status, '\0', ' ');
+ if (!strequal(parse_line->status, LPROS2_PRINTING) &&
+ !strequal(parse_line->status, LPROS2_PAUSED) &&
+ !strequal(parse_line->status, LPROS2_WAITING)) {
+ return False;
+ }
+
+ fstrcpy(buf->fs_user, parse_line->owner);
+ 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 const char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
+static const char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
+static const 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;
+ char *tok;
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char *cline = line;
+
+ /* First line is printer status */
+
+ if (!isdigit(line[0])) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ /* Parse a print job entry */
+
+ while(next_token_talloc(frame, &cline, &tok, NULL)) {
+ 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++;
+ }
+
+ TALLOC_FREE(frame);
+ return True;
+}
+
+#endif /* DEVELOPER */
+
+/****************************************************************************
+parse a lpq line. Choose printing style
+****************************************************************************/
+
+bool parse_lpq_entry(enum printing_types printing_type,char *line,
+ print_queue_struct *buf,
+ print_status_struct *status,bool first)
+{
+ bool ret;
+
+ switch (printing_type) {
+ 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_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 (!ret && (printing_type==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_m(line);
+
+ switch (status->status) {
+ case LPSTAT_OK:
+ for (i=0; stat0_strings[i]; i++) {
+ if (strstr_m(line,stat0_strings[i])) {
+ fstrcpy(status->message,line);
+ status->status=LPSTAT_OK;
+ return ret;
+ }
+ }
+ /* fallthrough */
+ case LPSTAT_STOPPED:
+ for (i=0; stat1_strings[i]; i++) {
+ if (strstr_m(line,stat1_strings[i])) {
+ fstrcpy(status->message,line);
+ status->status=LPSTAT_STOPPED;
+ return ret;
+ }
+ }
+ /* fallthrough */
+ case LPSTAT_ERROR:
+ for (i=0; stat2_strings[i]; i++) {
+ if (strstr_m(line,stat2_strings[i])) {
+ fstrcpy(status->message,line);
+ status->status=LPSTAT_ERROR;
+ return ret;
+ }
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
diff --git a/source3/printing/notify.c b/source3/printing/notify.c
new file mode 100644
index 0000000000..23df17c389
--- /dev/null
+++ b/source3/printing/notify.c
@@ -0,0 +1,571 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ printing backend routines
+ Copyright (C) Tim Potter, 2002
+ Copyright (C) Gerald Carter, 2002
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "printing.h"
+
+static TALLOC_CTX *send_ctx;
+
+static unsigned int num_messages;
+
+static struct notify_queue {
+ struct notify_queue *next, *prev;
+ struct spoolss_notify_msg *msg;
+ struct timeval tv;
+ uint8 *buf;
+ size_t buflen;
+} *notify_queue_head = NULL;
+
+
+static bool create_send_ctx(void)
+{
+ if (!send_ctx)
+ send_ctx = talloc_init("print notify queue");
+
+ if (!send_ctx)
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+ Turn a queue name into a snum.
+****************************************************************************/
+
+int print_queue_snum(const char *qname)
+{
+ int snum = lp_servicenumber(qname);
+ if (snum == -1 || !lp_print_ok(snum))
+ return -1;
+ return snum;
+}
+
+/*******************************************************************
+ Used to decide if we need a short select timeout.
+*******************************************************************/
+
+bool print_notify_messages_pending(void)
+{
+ return (notify_queue_head != NULL);
+}
+
+/*******************************************************************
+ Flatten data into a message.
+*******************************************************************/
+
+static bool flatten_message(struct notify_queue *q)
+{
+ struct spoolss_notify_msg *msg = q->msg;
+ uint8 *buf = NULL;
+ size_t buflen = 0, len;
+
+again:
+ len = 0;
+
+ /* Pack header */
+
+ len += tdb_pack(buf + len, buflen - len, "f", msg->printer);
+
+ len += tdb_pack(buf + len, buflen - len, "ddddddd",
+ (uint32)q->tv.tv_sec, (uint32)q->tv.tv_usec,
+ msg->type, msg->field, msg->id, msg->len, msg->flags);
+
+ /* Pack data */
+
+ if (msg->len == 0)
+ len += tdb_pack(buf + len, buflen - len, "dd",
+ msg->notify.value[0], msg->notify.value[1]);
+ else
+ len += tdb_pack(buf + len, buflen - len, "B",
+ msg->len, msg->notify.data);
+
+ if (buflen != len) {
+ buf = (uint8 *)TALLOC_REALLOC(send_ctx, buf, len);
+ if (!buf)
+ return False;
+ buflen = len;
+ goto again;
+ }
+
+ q->buf = buf;
+ q->buflen = buflen;
+
+ return True;
+}
+
+/*******************************************************************
+ Send the batched messages - on a per-printer basis.
+*******************************************************************/
+
+static void print_notify_send_messages_to_printer(struct messaging_context *msg_ctx,
+ const char *printer,
+ unsigned int timeout)
+{
+ char *buf;
+ struct notify_queue *pq, *pq_next;
+ size_t msg_count = 0, offset = 0;
+ size_t num_pids = 0;
+ size_t i;
+ pid_t *pid_list = NULL;
+ struct timeval end_time = timeval_zero();
+
+ /* Count the space needed to send the messages. */
+ for (pq = notify_queue_head; pq; pq = pq->next) {
+ if (strequal(printer, pq->msg->printer)) {
+ if (!flatten_message(pq)) {
+ DEBUG(0,("print_notify_send_messages: Out of memory\n"));
+ talloc_free_children(send_ctx);
+ num_messages = 0;
+ return;
+ }
+ offset += (pq->buflen + 4);
+ msg_count++;
+ }
+ }
+ offset += 4; /* For count. */
+
+ buf = (char *)TALLOC(send_ctx, offset);
+ if (!buf) {
+ DEBUG(0,("print_notify_send_messages: Out of memory\n"));
+ talloc_free_children(send_ctx);
+ num_messages = 0;
+ return;
+ }
+
+ offset = 0;
+ SIVAL(buf,offset,msg_count);
+ offset += 4;
+ for (pq = notify_queue_head; pq; pq = pq_next) {
+ pq_next = pq->next;
+
+ if (strequal(printer, pq->msg->printer)) {
+ SIVAL(buf,offset,pq->buflen);
+ offset += 4;
+ memcpy(buf + offset, pq->buf, pq->buflen);
+ offset += pq->buflen;
+
+ /* Remove from list. */
+ DLIST_REMOVE(notify_queue_head, pq);
+ }
+ }
+
+ DEBUG(5, ("print_notify_send_messages_to_printer: sending %lu print notify message%s to printer %s\n",
+ (unsigned long)msg_count, msg_count != 1 ? "s" : "", printer));
+
+ /*
+ * Get the list of PID's to send to.
+ */
+
+ if (!print_notify_pid_list(printer, send_ctx, &num_pids, &pid_list))
+ return;
+
+ if (timeout != 0) {
+ end_time = timeval_current_ofs(timeout, 0);
+ }
+
+ for (i = 0; i < num_pids; i++) {
+ messaging_send_buf(msg_ctx,
+ pid_to_procid(pid_list[i]),
+ MSG_PRINTER_NOTIFY2 | MSG_FLAG_LOWPRIORITY,
+ (uint8 *)buf, offset);
+
+ if ((timeout != 0) && timeval_expired(&end_time)) {
+ break;
+ }
+ }
+}
+
+/*******************************************************************
+ Actually send the batched messages.
+*******************************************************************/
+
+void print_notify_send_messages(struct messaging_context *msg_ctx,
+ unsigned int timeout)
+{
+ if (!print_notify_messages_pending())
+ return;
+
+ if (!create_send_ctx())
+ return;
+
+ while (print_notify_messages_pending())
+ print_notify_send_messages_to_printer(
+ msg_ctx, notify_queue_head->msg->printer, timeout);
+
+ talloc_free_children(send_ctx);
+ num_messages = 0;
+}
+
+/**********************************************************************
+ deep copy a SPOOLSS_NOTIFY_MSG structure using a TALLOC_CTX
+ *********************************************************************/
+
+static bool copy_notify2_msg( SPOOLSS_NOTIFY_MSG *to, SPOOLSS_NOTIFY_MSG *from )
+{
+
+ if ( !to || !from )
+ return False;
+
+ memcpy( to, from, sizeof(SPOOLSS_NOTIFY_MSG) );
+
+ if ( from->len ) {
+ to->notify.data = (char *)TALLOC_MEMDUP(send_ctx, from->notify.data, from->len );
+ if ( !to->notify.data ) {
+ DEBUG(0,("copy_notify2_msg: TALLOC_MEMDUP() of size [%d] failed!\n", from->len ));
+ return False;
+ }
+ }
+
+
+ return True;
+}
+
+/*******************************************************************
+ Batch up print notify messages.
+*******************************************************************/
+
+static void send_spoolss_notify2_msg(SPOOLSS_NOTIFY_MSG *msg)
+{
+ struct notify_queue *pnqueue, *tmp_ptr;
+
+ /*
+ * Ensure we only have one job total_bytes and job total_pages for
+ * each job. There is no point in sending multiple messages that match
+ * as they will just cause flickering updates in the client.
+ */
+
+ if ((num_messages < 100) && (msg->type == JOB_NOTIFY_TYPE)
+ && (msg->field == JOB_NOTIFY_TOTAL_BYTES
+ || msg->field == JOB_NOTIFY_TOTAL_PAGES ))
+ {
+
+ for (tmp_ptr = notify_queue_head; tmp_ptr; tmp_ptr = tmp_ptr->next)
+ {
+ if (tmp_ptr->msg->type == msg->type &&
+ tmp_ptr->msg->field == msg->field &&
+ tmp_ptr->msg->id == msg->id &&
+ tmp_ptr->msg->flags == msg->flags &&
+ strequal(tmp_ptr->msg->printer, msg->printer)) {
+
+ DEBUG(5,("send_spoolss_notify2_msg: replacing message 0x%02x/0x%02x for "
+ "printer %s in notify_queue\n", msg->type, msg->field, msg->printer));
+
+ tmp_ptr->msg = msg;
+ return;
+ }
+ }
+ }
+
+ /* Store the message on the pending queue. */
+
+ pnqueue = TALLOC_P(send_ctx, struct notify_queue);
+ if (!pnqueue) {
+ DEBUG(0,("send_spoolss_notify2_msg: Out of memory.\n"));
+ return;
+ }
+
+ /* allocate a new msg structure and copy the fields */
+
+ if ( !(pnqueue->msg = TALLOC_P(send_ctx, SPOOLSS_NOTIFY_MSG)) ) {
+ DEBUG(0,("send_spoolss_notify2_msg: talloc() of size [%lu] failed!\n",
+ (unsigned long)sizeof(SPOOLSS_NOTIFY_MSG)));
+ return;
+ }
+ copy_notify2_msg(pnqueue->msg, msg);
+ GetTimeOfDay(&pnqueue->tv);
+ pnqueue->buf = NULL;
+ pnqueue->buflen = 0;
+
+ DEBUG(5, ("send_spoolss_notify2_msg: appending message 0x%02x/0x%02x for printer %s \
+to notify_queue_head\n", msg->type, msg->field, msg->printer));
+
+ /*
+ * Note we add to the end of the list to ensure
+ * the messages are sent in the order they were received. JRA.
+ */
+
+ DLIST_ADD_END(notify_queue_head, pnqueue, struct notify_queue *);
+ num_messages++;
+}
+
+static void send_notify_field_values(const char *sharename, uint32 type,
+ uint32 field, uint32 id, uint32 value1,
+ uint32 value2, uint32 flags)
+{
+ struct spoolss_notify_msg *msg;
+
+ if (lp_disable_spoolss())
+ return;
+
+ if (!create_send_ctx())
+ return;
+
+ msg = TALLOC_P(send_ctx, struct spoolss_notify_msg);
+ if (!msg)
+ return;
+
+ ZERO_STRUCTP(msg);
+
+ fstrcpy(msg->printer, sharename);
+ msg->type = type;
+ msg->field = field;
+ msg->id = id;
+ msg->notify.value[0] = value1;
+ msg->notify.value[1] = value2;
+ msg->flags = flags;
+
+ send_spoolss_notify2_msg(msg);
+}
+
+static void send_notify_field_buffer(const char *sharename, uint32 type,
+ uint32 field, uint32 id, uint32 len,
+ const char *buffer)
+{
+ struct spoolss_notify_msg *msg;
+
+ if (lp_disable_spoolss())
+ return;
+
+ if (!create_send_ctx())
+ return;
+
+ msg = TALLOC_P(send_ctx, struct spoolss_notify_msg);
+ if (!msg)
+ return;
+
+ ZERO_STRUCTP(msg);
+
+ fstrcpy(msg->printer, sharename);
+ msg->type = type;
+ msg->field = field;
+ msg->id = id;
+ msg->len = len;
+ msg->notify.data = CONST_DISCARD(char *,buffer);
+
+ send_spoolss_notify2_msg(msg);
+}
+
+/* Send a message that the printer status has changed */
+
+void notify_printer_status_byname(const char *sharename, uint32 status)
+{
+ /* Printer status stored in value1 */
+
+ send_notify_field_values(sharename, PRINTER_NOTIFY_TYPE,
+ PRINTER_NOTIFY_STATUS, 0,
+ status, 0, 0);
+}
+
+void notify_printer_status(int snum, uint32 status)
+{
+ const char *sharename = SERVICE(snum);
+
+ if (sharename)
+ notify_printer_status_byname(sharename, status);
+}
+
+void notify_job_status_byname(const char *sharename, uint32 jobid, uint32 status,
+ uint32 flags)
+{
+ /* Job id stored in id field, status in value1 */
+
+ send_notify_field_values(sharename, JOB_NOTIFY_TYPE,
+ JOB_NOTIFY_STATUS, jobid,
+ status, 0, flags);
+}
+
+void notify_job_status(const char *sharename, uint32 jobid, uint32 status)
+{
+ notify_job_status_byname(sharename, jobid, status, 0);
+}
+
+void notify_job_total_bytes(const char *sharename, uint32 jobid,
+ uint32 size)
+{
+ /* Job id stored in id field, status in value1 */
+
+ send_notify_field_values(sharename, JOB_NOTIFY_TYPE,
+ JOB_NOTIFY_TOTAL_BYTES, jobid,
+ size, 0, 0);
+}
+
+void notify_job_total_pages(const char *sharename, uint32 jobid,
+ uint32 pages)
+{
+ /* Job id stored in id field, status in value1 */
+
+ send_notify_field_values(sharename, JOB_NOTIFY_TYPE,
+ JOB_NOTIFY_TOTAL_PAGES, jobid,
+ pages, 0, 0);
+}
+
+void notify_job_username(const char *sharename, uint32 jobid, char *name)
+{
+ send_notify_field_buffer(
+ sharename, JOB_NOTIFY_TYPE, JOB_NOTIFY_USER_NAME,
+ jobid, strlen(name) + 1, name);
+}
+
+void notify_job_name(const char *sharename, uint32 jobid, char *name)
+{
+ send_notify_field_buffer(
+ sharename, JOB_NOTIFY_TYPE, JOB_NOTIFY_DOCUMENT,
+ jobid, strlen(name) + 1, name);
+}
+
+void notify_job_submitted(const char *sharename, uint32 jobid,
+ time_t submitted)
+{
+ send_notify_field_buffer(
+ sharename, JOB_NOTIFY_TYPE, JOB_NOTIFY_SUBMITTED,
+ jobid, sizeof(submitted), (char *)&submitted);
+}
+
+void notify_printer_driver(int snum, char *driver_name)
+{
+ const char *sharename = SERVICE(snum);
+
+ send_notify_field_buffer(
+ sharename, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME,
+ snum, strlen(driver_name) + 1, driver_name);
+}
+
+void notify_printer_comment(int snum, char *comment)
+{
+ const char *sharename = SERVICE(snum);
+
+ send_notify_field_buffer(
+ sharename, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT,
+ snum, strlen(comment) + 1, comment);
+}
+
+void notify_printer_sharename(int snum, char *share_name)
+{
+ const char *sharename = SERVICE(snum);
+
+ send_notify_field_buffer(
+ sharename, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME,
+ snum, strlen(share_name) + 1, share_name);
+}
+
+void notify_printer_printername(int snum, char *printername)
+{
+ const char *sharename = SERVICE(snum);
+
+ send_notify_field_buffer(
+ sharename, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINTER_NAME,
+ snum, strlen(printername) + 1, printername);
+}
+
+void notify_printer_port(int snum, char *port_name)
+{
+ const char *sharename = SERVICE(snum);
+
+ send_notify_field_buffer(
+ sharename, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME,
+ snum, strlen(port_name) + 1, port_name);
+}
+
+void notify_printer_location(int snum, char *location)
+{
+ const char *sharename = SERVICE(snum);
+
+ send_notify_field_buffer(
+ sharename, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION,
+ snum, strlen(location) + 1, location);
+}
+
+void notify_printer_byname( const char *printername, uint32 change, const char *value )
+{
+ int snum = print_queue_snum(printername);
+ int type = PRINTER_NOTIFY_TYPE;
+
+ if ( snum == -1 )
+ return;
+
+ send_notify_field_buffer( printername, type, change, snum, strlen(value)+1, value );
+}
+
+
+/****************************************************************************
+ Return a malloced list of pid_t's that are interested in getting update
+ messages on this print queue. Used in printing/notify to send the messages.
+****************************************************************************/
+
+bool print_notify_pid_list(const char *printername, TALLOC_CTX *mem_ctx, size_t *p_num_pids, pid_t **pp_pid_list)
+{
+ struct tdb_print_db *pdb = NULL;
+ TDB_CONTEXT *tdb = NULL;
+ TDB_DATA data;
+ bool ret = True;
+ size_t i, num_pids, offset;
+ pid_t *pid_list;
+
+ *p_num_pids = 0;
+ *pp_pid_list = NULL;
+
+ pdb = get_print_db_byname(printername);
+ if (!pdb)
+ return False;
+ tdb = pdb->tdb;
+
+ if (tdb_read_lock_bystring_with_timeout(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
+ DEBUG(0,("print_notify_pid_list: Failed to lock printer %s database\n",
+ printername));
+ if (pdb)
+ release_print_db(pdb);
+ return False;
+ }
+
+ data = get_printer_notify_pid_list( tdb, printername, True );
+
+ if (!data.dptr) {
+ ret = True;
+ goto done;
+ }
+
+ num_pids = data.dsize / 8;
+
+ if (num_pids) {
+ if ((pid_list = TALLOC_ARRAY(mem_ctx, pid_t, num_pids)) == NULL) {
+ ret = False;
+ goto done;
+ }
+ } else {
+ pid_list = NULL;
+ }
+
+ for( i = 0, offset = 0; i < num_pids; offset += 8, i++)
+ pid_list[i] = (pid_t)IVAL(data.dptr, offset);
+
+ *pp_pid_list = pid_list;
+ *p_num_pids = num_pids;
+
+ ret = True;
+
+ done:
+
+ tdb_read_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
+ if (pdb)
+ release_print_db(pdb);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
diff --git a/source3/printing/nt_printing.c b/source3/printing/nt_printing.c
new file mode 100644
index 0000000000..ded985c260
--- /dev/null
+++ b/source3/printing/nt_printing.c
@@ -0,0 +1,5892 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000.
+ * Copyright (C) Gerald Carter 2002-2005.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+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_4 4 /* fix generic bits in security descriptors */
+#define NTDRIVERS_DATABASE_VERSION_5 5 /* normalize keys in ntprinters.tdb */
+
+/* Map generic permissions to printer object specific permissions */
+
+const struct generic_mapping printer_generic_mapping = {
+ PRINTER_READ,
+ PRINTER_WRITE,
+ PRINTER_EXECUTE,
+ PRINTER_ALL_ACCESS
+};
+
+const struct standard_mapping printer_std_mapping = {
+ PRINTER_READ,
+ PRINTER_WRITE,
+ PRINTER_EXECUTE,
+ PRINTER_ALL_ACCESS
+};
+
+/* Map generic permissions to print server object specific permissions */
+
+const struct generic_mapping printserver_generic_mapping = {
+ SERVER_READ,
+ SERVER_WRITE,
+ SERVER_EXECUTE,
+ SERVER_ALL_ACCESS
+};
+
+const struct generic_mapping printserver_std_mapping = {
+ SERVER_READ,
+ SERVER_WRITE,
+ SERVER_EXECUTE,
+ SERVER_ALL_ACCESS
+};
+
+/* Map generic permissions to job object specific permissions */
+
+const struct generic_mapping job_generic_mapping = {
+ JOB_READ,
+ JOB_WRITE,
+ JOB_EXECUTE,
+ JOB_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 const 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}
+};
+
+struct table_node {
+ const char *long_archi;
+ const char *short_archi;
+ int version;
+};
+
+#define SPL_ARCH_WIN40 "WIN40"
+#define SPL_ARCH_W32X86 "W32X86"
+#define SPL_ARCH_W32MIPS "W32MIPS"
+#define SPL_ARCH_W32ALPHA "W32ALPHA"
+#define SPL_ARCH_W32PPC "W32PPC"
+#define SPL_ARCH_IA64 "IA64"
+#define SPL_ARCH_X64 "x64"
+
+static const struct table_node archi_table[]= {
+
+ {"Windows 4.0", SPL_ARCH_WIN40, 0 },
+ {"Windows NT x86", SPL_ARCH_W32X86, 2 },
+ {"Windows NT R4000", SPL_ARCH_W32MIPS, 2 },
+ {"Windows NT Alpha_AXP", SPL_ARCH_W32ALPHA, 2 },
+ {"Windows NT PowerPC", SPL_ARCH_W32PPC, 2 },
+ {"Windows IA64", SPL_ARCH_IA64, 3 },
+ {"Windows x64", SPL_ARCH_X64, 3 },
+ {NULL, "", -1 }
+};
+
+
+/****************************************************************************
+ generate a new TDB_DATA key for storing a printer
+****************************************************************************/
+
+static TDB_DATA make_printer_tdbkey(TALLOC_CTX *ctx, const char *sharename )
+{
+ fstring share;
+ char *keystr = NULL;
+ TDB_DATA key;
+
+ fstrcpy(share, sharename);
+ strlower_m(share);
+
+ keystr = talloc_asprintf(ctx, "%s%s", PRINTERS_PREFIX, share);
+ key = string_term_tdb_data(keystr ? keystr : "");
+
+ return key;
+}
+
+/****************************************************************************
+ generate a new TDB_DATA key for storing a printer security descriptor
+****************************************************************************/
+
+static TDB_DATA make_printers_secdesc_tdbkey(TALLOC_CTX *ctx,
+ const char* sharename )
+{
+ fstring share;
+ char *keystr = NULL;
+ TDB_DATA key;
+
+ fstrcpy(share, sharename );
+ strlower_m(share);
+
+ keystr = talloc_asprintf(ctx, "%s%s", SECDESC_PREFIX, share);
+ key = string_term_tdb_data(keystr ? keystr : "");
+
+ return key;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+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((const char *)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) {
+ SAFE_FREE(dbuf.dptr);
+ 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) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to delete form. Error (%s)\n", tdb_errorstr(tdb_drivers)));
+ return False;
+ }
+ }
+
+ if (strncmp((const char *)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) {
+ SAFE_FREE(dbuf.dptr);
+ 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) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to delete printer. Error (%s)\n", tdb_errorstr(tdb_drivers)));
+ return False;
+ }
+ }
+
+ if (strncmp((const char *)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) {
+ SAFE_FREE(dbuf.dptr);
+ 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) {
+ SAFE_FREE(dbuf.dptr);
+ 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;
+}
+
+/*******************************************************************
+ Fix an issue with security descriptors. Printer sec_desc must
+ use more than the generic bits that were previously used
+ in <= 3.0.14a. They must also have a owner and group SID assigned.
+ Otherwise, any printers than have been migrated to a Windows
+ host using printmig.exe will not be accessible.
+*******************************************************************/
+
+static int sec_desc_upg_fn( TDB_CONTEXT *the_tdb, TDB_DATA key,
+ TDB_DATA data, void *state )
+{
+ prs_struct ps;
+ SEC_DESC_BUF *sd_orig = NULL;
+ SEC_DESC_BUF *sd_new, *sd_store;
+ SEC_DESC *sec, *new_sec;
+ TALLOC_CTX *ctx = state;
+ int result, i;
+ uint32 sd_size;
+ size_t size_new_sec;
+
+ if (!data.dptr || data.dsize == 0) {
+ return 0;
+ }
+
+ if ( strncmp((const char *) key.dptr, SECDESC_PREFIX, strlen(SECDESC_PREFIX) ) != 0 ) {
+ return 0;
+ }
+
+ /* upgrade the security descriptor */
+
+ ZERO_STRUCT( ps );
+
+ prs_init_empty( &ps, ctx, UNMARSHALL );
+ prs_give_memory( &ps, (char *)data.dptr, data.dsize, False );
+
+ if ( !sec_io_desc_buf( "sec_desc_upg_fn", &sd_orig, &ps, 1 ) ) {
+ /* delete bad entries */
+ DEBUG(0,("sec_desc_upg_fn: Failed to parse original sec_desc for %si. Deleting....\n",
+ (const char *)key.dptr ));
+ tdb_delete( tdb_printers, key );
+ prs_mem_free( &ps );
+ return 0;
+ }
+
+ if (!sd_orig) {
+ prs_mem_free( &ps );
+ return 0;
+ }
+ sec = sd_orig->sd;
+
+ /* is this even valid? */
+
+ if ( !sec->dacl ) {
+ prs_mem_free( &ps );
+ return 0;
+ }
+
+ /* update access masks */
+
+ for ( i=0; i<sec->dacl->num_aces; i++ ) {
+ switch ( sec->dacl->aces[i].access_mask ) {
+ case (GENERIC_READ_ACCESS | GENERIC_WRITE_ACCESS | GENERIC_EXECUTE_ACCESS):
+ sec->dacl->aces[i].access_mask = PRINTER_ACE_PRINT;
+ break;
+
+ case GENERIC_ALL_ACCESS:
+ sec->dacl->aces[i].access_mask = PRINTER_ACE_FULL_CONTROL;
+ break;
+
+ case READ_CONTROL_ACCESS:
+ sec->dacl->aces[i].access_mask = PRINTER_ACE_MANAGE_DOCUMENTS;
+
+ default: /* no change */
+ break;
+ }
+ }
+
+ /* create a new SEC_DESC with the appropriate owner and group SIDs */
+
+ new_sec = make_sec_desc( ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE,
+ &global_sid_Builtin_Administrators,
+ &global_sid_Builtin_Administrators,
+ NULL, NULL, &size_new_sec );
+ if (!new_sec) {
+ prs_mem_free( &ps );
+ return 0;
+ }
+ sd_new = make_sec_desc_buf( ctx, size_new_sec, new_sec );
+ if (!sd_new) {
+ prs_mem_free( &ps );
+ return 0;
+ }
+
+ if ( !(sd_store = sec_desc_merge( ctx, sd_new, sd_orig )) ) {
+ DEBUG(0,("sec_desc_upg_fn: Failed to update sec_desc for %s\n", key.dptr ));
+ prs_mem_free( &ps );
+ return 0;
+ }
+
+ prs_mem_free( &ps );
+
+ /* store it back */
+
+ sd_size = ndr_size_security_descriptor(sd_store->sd, 0)
+ + sizeof(SEC_DESC_BUF);
+ if ( !prs_init(&ps, sd_size, ctx, MARSHALL) ) {
+ DEBUG(0,("sec_desc_upg_fn: Failed to allocate prs memory for %s\n", key.dptr ));
+ return 0;
+ }
+
+ if ( !sec_io_desc_buf( "sec_desc_upg_fn", &sd_store, &ps, 1 ) ) {
+ DEBUG(0,("sec_desc_upg_fn: Failed to parse new sec_desc for %s\n", key.dptr ));
+ prs_mem_free( &ps );
+ return 0;
+ }
+
+ data.dptr = (uint8 *)prs_data_p( &ps );
+ data.dsize = sd_size;
+
+ result = tdb_store( tdb_printers, key, data, TDB_REPLACE );
+
+ prs_mem_free( &ps );
+
+ /* 0 to continue and non-zero to stop traversal */
+
+ return (result == -1);
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool upgrade_to_version_4(void)
+{
+ TALLOC_CTX *ctx;
+ int result;
+
+ DEBUG(0,("upgrade_to_version_4: upgrading printer security descriptors\n"));
+
+ if ( !(ctx = talloc_init( "upgrade_to_version_4" )) )
+ return False;
+
+ result = tdb_traverse( tdb_printers, sec_desc_upg_fn, ctx );
+
+ talloc_destroy( ctx );
+
+ return ( result != -1 );
+}
+
+/*******************************************************************
+ Fix an issue with security descriptors. Printer sec_desc must
+ use more than the generic bits that were previously used
+ in <= 3.0.14a. They must also have a owner and group SID assigned.
+ Otherwise, any printers than have been migrated to a Windows
+ host using printmig.exe will not be accessible.
+*******************************************************************/
+
+static int normalize_printers_fn( TDB_CONTEXT *the_tdb, TDB_DATA key,
+ TDB_DATA data, void *state )
+{
+ TALLOC_CTX *ctx = talloc_tos();
+ TDB_DATA new_key;
+
+ if (!data.dptr || data.dsize == 0)
+ return 0;
+
+ /* upgrade printer records and security descriptors */
+
+ if ( strncmp((const char *) key.dptr, PRINTERS_PREFIX, strlen(PRINTERS_PREFIX) ) == 0 ) {
+ new_key = make_printer_tdbkey(ctx, (const char *)key.dptr+strlen(PRINTERS_PREFIX) );
+ }
+ else if ( strncmp((const char *) key.dptr, SECDESC_PREFIX, strlen(SECDESC_PREFIX) ) == 0 ) {
+ new_key = make_printers_secdesc_tdbkey(ctx, (const char *)key.dptr+strlen(SECDESC_PREFIX) );
+ }
+ else {
+ /* ignore this record */
+ return 0;
+ }
+
+ /* delete the original record and store under the normalized key */
+
+ if ( tdb_delete( the_tdb, key ) != 0 ) {
+ DEBUG(0,("normalize_printers_fn: tdb_delete for [%s] failed!\n",
+ key.dptr));
+ return 1;
+ }
+
+ if ( tdb_store( the_tdb, new_key, data, TDB_REPLACE) != 0 ) {
+ DEBUG(0,("normalize_printers_fn: failed to store new record for [%s]!\n",
+ key.dptr));
+ return 1;
+ }
+
+ return 0;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool upgrade_to_version_5(void)
+{
+ TALLOC_CTX *ctx;
+ int result;
+
+ DEBUG(0,("upgrade_to_version_5: normalizing printer keys\n"));
+
+ if ( !(ctx = talloc_init( "upgrade_to_version_5" )) )
+ return False;
+
+ result = tdb_traverse( tdb_printers, normalize_printers_fn, NULL );
+
+ talloc_destroy( ctx );
+
+ return ( result != -1 );
+}
+
+/****************************************************************************
+ Open the NT printing tdbs. Done once before fork().
+****************************************************************************/
+
+bool nt_printing_init(struct messaging_context *msg_ctx)
+{
+ const char *vstring = "INFO/version";
+ WERROR win_rc;
+ int32 vers_id;
+
+ if ( tdb_drivers && tdb_printers && tdb_forms )
+ return True;
+
+ if (tdb_drivers)
+ tdb_close(tdb_drivers);
+ tdb_drivers = tdb_open_log(state_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",
+ state_path("ntdrivers.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ if (tdb_printers)
+ tdb_close(tdb_printers);
+ tdb_printers = tdb_open_log(state_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",
+ state_path("ntprinters.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ if (tdb_forms)
+ tdb_close(tdb_forms);
+ tdb_forms = tdb_open_log(state_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",
+ state_path("ntforms.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ /* handle a Samba upgrade */
+
+ vers_id = tdb_fetch_int32(tdb_drivers, vstring);
+ if (vers_id == -1) {
+ DEBUG(10, ("Fresh database\n"));
+ tdb_store_int32( tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION_5 );
+ vers_id = NTDRIVERS_DATABASE_VERSION_5;
+ }
+
+ if ( vers_id != NTDRIVERS_DATABASE_VERSION_5 ) {
+
+ if ((vers_id == NTDRIVERS_DATABASE_VERSION_1) || (IREV(vers_id) == NTDRIVERS_DATABASE_VERSION_1)) {
+ if (!upgrade_to_version_3())
+ return False;
+ tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION_3);
+ vers_id = NTDRIVERS_DATABASE_VERSION_3;
+ }
+
+ 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_3);
+ vers_id = NTDRIVERS_DATABASE_VERSION_3;
+ }
+
+ if (vers_id == NTDRIVERS_DATABASE_VERSION_3 ) {
+ if ( !upgrade_to_version_4() )
+ return False;
+ tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION_4);
+ vers_id = NTDRIVERS_DATABASE_VERSION_4;
+ }
+
+ if (vers_id == NTDRIVERS_DATABASE_VERSION_4 ) {
+ if ( !upgrade_to_version_5() )
+ return False;
+ tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION_5);
+ vers_id = NTDRIVERS_DATABASE_VERSION_5;
+ }
+
+
+ if ( vers_id != NTDRIVERS_DATABASE_VERSION_5 ) {
+ DEBUG(0,("nt_printing_init: Unknown printer database version [%d]\n", vers_id));
+ return False;
+ }
+ }
+
+ update_c_setprinter(True);
+
+ /*
+ * register callback to handle updating printers as new
+ * drivers are installed
+ */
+
+ messaging_register(msg_ctx, NULL, MSG_PRINTER_DRVUPGRADE,
+ do_drv_upgrade_printer);
+
+ /*
+ * register callback to handle updating printer data
+ * when a driver is initialized
+ */
+
+ messaging_register(msg_ctx, NULL, MSG_PRINTERDATA_INIT_RESET,
+ reset_all_printerdata);
+
+ /* of course, none of the message callbacks matter if you don't
+ tell messages.c that you interested in receiving PRINT_GENERAL
+ msgs. This is done in claim_connection() */
+
+
+ if ( lp_security() == SEC_ADS ) {
+ win_rc = check_published_printers();
+ if (!W_ERROR_IS_OK(win_rc))
+ DEBUG(0, ("nt_printing_init: error checking published printers: %s\n", dos_errstr(win_rc)));
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Function to allow filename parsing "the old way".
+********************************************************************/
+
+static char *driver_unix_convert(connection_struct *conn,
+ const char *old_name,
+ SMB_STRUCT_STAT *pst)
+{
+ TALLOC_CTX *ctx = talloc_tos();
+ char *name = talloc_strdup(ctx, old_name);
+ char *new_name = NULL;
+
+ if (!name) {
+ return NULL;
+ }
+ unix_format(name);
+ name = unix_clean_name(ctx, name);
+ if (!name) {
+ return NULL;
+ }
+ trim_string(name,"/","/");
+ unix_convert(ctx,conn, name, false, &new_name, NULL, pst);
+ return new_name;
+}
+
+/*******************************************************************
+ 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));
+ if (!*list) {
+ return 0;
+ }
+ 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));
+ 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 form;
+ int ret;
+ int i;
+ int n = 0;
+
+ *list = NULL;
+
+ for (kbuf = tdb_firstkey(tdb_forms);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb_forms, kbuf), safe_free(kbuf.dptr), kbuf=newkey)
+ {
+ if (strncmp((const char *)kbuf.dptr, FORMS_PREFIX, strlen(FORMS_PREFIX)) != 0)
+ continue;
+
+ dbuf = tdb_fetch(tdb_forms, kbuf);
+ if (!dbuf.dptr)
+ continue;
+
+ fstrcpy(form.name, (const char *)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;
+
+ *list = SMB_REALLOC_ARRAY(*list, nt_forms_struct, n+1);
+ if (!*list) {
+ DEBUG(0,("get_ntforms: Realloc fail.\n"));
+ return 0;
+ }
+ (*list)[n] = form;
+ n++;
+ }
+
+
+ return n;
+}
+
+/****************************************************************************
+write a form struct list
+****************************************************************************/
+
+int write_ntforms(nt_forms_struct **list, int number)
+{
+ TALLOC_CTX *ctx = talloc_tos();
+ char *buf = NULL;
+ char *key = NULL;
+ int len;
+ TDB_DATA dbuf;
+ int i;
+
+ for (i=0;i<number;i++) {
+ /* save index, so list is rebuilt in correct order */
+ len = tdb_pack(NULL, 0, "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) {
+ continue;
+ }
+ buf = TALLOC_ARRAY(ctx, char, len);
+ if (!buf) {
+ return 0;
+ }
+ len = tdb_pack((uint8 *)buf, len, "dddddddd",
+ i, (*list)[i].flag, (*list)[i].width, (*list)[i].length,
+ (*list)[i].left, (*list)[i].top, (*list)[i].right,
+ (*list)[i].bottom);
+ key = talloc_asprintf(ctx, "%s%s", FORMS_PREFIX, (*list)[i].name);
+ if (!key) {
+ return 0;
+ }
+ dbuf.dsize = len;
+ dbuf.dptr = (uint8 *)buf;
+ if (tdb_store_bystring(tdb_forms, key, dbuf, TDB_REPLACE) != 0) {
+ TALLOC_FREE(key);
+ TALLOC_FREE(buf);
+ break;
+ }
+ TALLOC_FREE(key);
+ TALLOC_FREE(buf);
+ }
+
+ 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 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));
+ for (n=0; n<*count; n++) {
+ if ( strequal((*list)[n].name, form_name) ) {
+ update=True;
+ break;
+ }
+ }
+
+ if (update==False) {
+ if((*list=SMB_REALLOC_ARRAY(*list, nt_forms_struct, n+1)) == NULL) {
+ DEBUG(0,("add_a_form: failed to enlarge forms list!\n"));
+ return False;
+ }
+ unistr2_to_ascii((*list)[n].name, &form->name, sizeof((*list)[n].name));
+ (*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;
+
+ DEBUG(6,("add_a_form: Successfully %s form [%s]\n",
+ update ? "updated" : "added", form_name));
+
+ return True;
+}
+
+/****************************************************************************
+ Delete a named form struct.
+****************************************************************************/
+
+bool delete_a_form(nt_forms_struct **list, UNISTR2 *del_name, int *count, WERROR *ret)
+{
+ char *key = NULL;
+ int n=0;
+ fstring form_name;
+
+ *ret = WERR_OK;
+
+ unistr2_to_ascii(form_name, del_name, sizeof(form_name));
+
+ 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;
+ }
+
+ if (asprintf(&key, "%s%s", FORMS_PREFIX, (*list)[n].name) < 0) {
+ *ret = WERR_NOMEM;
+ return false;
+ }
+ if (tdb_delete_bystring(tdb_forms, key) != 0) {
+ SAFE_FREE(key);
+ *ret = WERR_NOMEM;
+ return False;
+ }
+ SAFE_FREE(key);
+ 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));
+
+ 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, const char *architecture, uint32 version)
+{
+ int total=0;
+ const char *short_archi;
+ char *key = NULL;
+ TDB_DATA kbuf, newkey;
+
+ short_archi = get_short_archi(architecture);
+ if (!short_archi) {
+ return 0;
+ }
+
+ if (asprintf(&key, "%s%s/%d/", DRIVERS_PREFIX,
+ short_archi, version) < 0) {
+ return 0;
+ }
+
+ for (kbuf = tdb_firstkey(tdb_drivers);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb_drivers, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+ if (strncmp((const char *)kbuf.dptr, key, strlen(key)) != 0)
+ continue;
+
+ if((*list = SMB_REALLOC_ARRAY(*list, fstring, total+1)) == NULL) {
+ DEBUG(0,("get_ntdrivers: failed to enlarge list!\n"));
+ SAFE_FREE(key);
+ return -1;
+ }
+
+ fstrcpy((*list)[total], (const char *)kbuf.dptr+strlen(key));
+ total++;
+ }
+
+ SAFE_FREE(key);
+ return(total);
+}
+
+/****************************************************************************
+ Function to do the mapping between the long architecture name and
+ the short one.
+****************************************************************************/
+
+const char *get_short_archi(const char *long_archi)
+{
+ 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(10,("Unknown architecture [%s] !\n", long_archi));
+ return NULL;
+ }
+
+ /* this might be client code - but shouldn't this be an fstrcpy etc? */
+
+ DEBUGADD(108,("index: [%d]\n", i));
+ DEBUGADD(108,("long architecture: [%s]\n", archi_table[i].long_archi));
+ DEBUGADD(108,("short architecture: [%s]\n", archi_table[i].short_archi));
+
+ return archi_table[i].short_archi;
+}
+
+/****************************************************************************
+ 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 = NULL;
+ ssize_t byte_count;
+
+ if ((buf=(char *)SMB_MALLOC(DOS_HEADER_SIZE)) == NULL) {
+ DEBUG(0,("get_file_version: PE file [%s] DOS Header malloc failed bytes = %d\n",
+ fname, DOS_HEADER_SIZE));
+ goto error_exit;
+ }
+
+ 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 = %lu\n",
+ fname, (unsigned long)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 (SMB_VFS_LSEEK(fsp, 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;
+ }
+
+ /* Note: DOS_HEADER_SIZE and NE_HEADER_SIZE are incidentally same */
+ if ((byte_count = vfs_read_data(fsp, buf, NE_HEADER_SIZE)) < NE_HEADER_SIZE) {
+ DEBUG(3,("get_file_version: File [%s] Windows header too short, bytes read = %lu\n",
+ fname, (unsigned long)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) {
+ unsigned int num_sections;
+ unsigned int section_table_bytes;
+
+ /* Just skip over optional header to get to section table */
+ if (SMB_VFS_LSEEK(fsp,
+ SVAL(buf,PE_HEADER_OPTIONAL_HEADER_SIZE)-(NE_HEADER_SIZE-PE_HEADER_SIZE),
+ SEEK_CUR) == (SMB_OFF_T)-1) {
+ DEBUG(3,("get_file_version: File [%s] Windows optional header too short, errno = %d\n",
+ fname, errno));
+ 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;
+ if (section_table_bytes == 0)
+ goto error_exit;
+
+ SAFE_FREE(buf);
+ if ((buf=(char *)SMB_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 = %lu\n",
+ fname, (unsigned long)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) {
+ unsigned int section_pos = IVAL(buf,sec_offset+PE_HEADER_SECT_PTR_DATA_OFFSET);
+ unsigned int section_bytes = IVAL(buf,sec_offset+PE_HEADER_SECT_SIZE_DATA_OFFSET);
+
+ if (section_bytes == 0)
+ goto error_exit;
+
+ SAFE_FREE(buf);
+ if ((buf=(char *)SMB_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 (SMB_VFS_LSEEK(fsp, 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 = %lu\n",
+ fname, (unsigned long)byte_count));
+ goto error_exit;
+ }
+
+ if (section_bytes < VS_VERSION_INFO_UNICODE_SIZE)
+ 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=(char *)SMB_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 = -(SMB_VFS_LSEEK(fsp, 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;
+ char *filepath = NULL;
+
+ uint32 new_major;
+ uint32 new_minor;
+ time_t new_create_time;
+
+ uint32 old_major;
+ uint32 old_minor;
+ time_t old_create_time;
+
+ files_struct *fsp = NULL;
+ SMB_STRUCT_STAT st;
+ SMB_STRUCT_STAT stat_buf;
+
+ NTSTATUS status;
+
+ SET_STAT_INVALID(st);
+ SET_STAT_INVALID(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) */
+ filepath = driver_unix_convert(conn,old_file,&stat_buf);
+ if (!filepath) {
+ goto error_exit;
+ }
+
+ status = open_file_ntcreate(conn, NULL, filepath, &stat_buf,
+ FILE_GENERIC_READ,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN,
+ 0,
+ FILE_ATTRIBUTE_NORMAL,
+ INTERNAL_OPEN_ONLY,
+ NULL, &fsp);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /* 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 1;
+
+ } 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 (SMB_VFS_FSTAT(fsp, &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, NORMAL_CLOSE);
+
+ /* Get file version info (if available) for new file */
+ filepath = driver_unix_convert(conn,new_file,&stat_buf);
+ if (!filepath) {
+ goto error_exit;
+ }
+
+ status = open_file_ntcreate(conn, NULL, filepath, &stat_buf,
+ FILE_GENERIC_READ,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN,
+ 0,
+ FILE_ATTRIBUTE_NORMAL,
+ INTERNAL_OPEN_ONLY,
+ NULL, &fsp);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /* 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 (SMB_VFS_FSTAT(fsp, &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, NORMAL_CLOSE);
+
+ if (use_version && (new_major != old_major || new_minor != old_minor)) {
+ /* 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 1;
+ }
+ else {
+ DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file));
+ return 0;
+ }
+
+ } 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 1;
+ }
+ else {
+ DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file));
+ return 0;
+ }
+ }
+
+ error_exit:
+ if(fsp)
+ close_file(fsp, NORMAL_CLOSE);
+ return -1;
+}
+
+/****************************************************************************
+Determine the correct cVersion associated with an architecture and driver
+****************************************************************************/
+static uint32 get_correct_cversion(const char *architecture, fstring driverpath_in,
+ struct current_user *user, WERROR *perr)
+{
+ int cversion;
+ NTSTATUS nt_status;
+ char *driverpath = NULL;
+ DATA_BLOB null_pw;
+ fstring res_type;
+ files_struct *fsp = NULL;
+ SMB_STRUCT_STAT st;
+ connection_struct *conn;
+ NTSTATUS status;
+
+ SET_STAT_INVALID(st);
+
+ *perr = WERR_INVALID_PARAM;
+
+ /* If architecture is Windows 95/98/ME, the version is always 0. */
+ if (strcmp(architecture, SPL_ARCH_WIN40) == 0) {
+ DEBUG(10,("get_correct_cversion: Driver is Win9x, cversion = 0\n"));
+ *perr = WERR_OK;
+ return 0;
+ }
+
+ /* If architecture is Windows x64, the version is always 3. */
+ if (strcmp(architecture, SPL_ARCH_X64) == 0) {
+ DEBUG(10,("get_correct_cversion: Driver is x64, cversion = 3\n"));
+ *perr = WERR_OK;
+ return 3;
+ }
+
+ /*
+ * 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;
+ fstrcpy(res_type, "A:");
+ become_root();
+ conn = make_connection_with_chdir("print$", null_pw, res_type, 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, user->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. */
+ driverpath = talloc_asprintf(talloc_tos(),
+ "%s/%s",
+ architecture,
+ driverpath_in);
+ if (!driverpath) {
+ *perr = WERR_NOMEM;
+ goto error_exit;
+ }
+
+ driverpath = driver_unix_convert(conn,driverpath,&st);
+ if (!driverpath) {
+ *perr = WERR_NOMEM;
+ goto error_exit;
+ }
+
+ if (!vfs_file_exist(conn, driverpath, &st)) {
+ *perr = WERR_BADFILE;
+ goto error_exit;
+ }
+
+ status = open_file_ntcreate(conn, NULL, driverpath, &st,
+ FILE_GENERIC_READ,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN,
+ 0,
+ FILE_ATTRIBUTE_NORMAL,
+ INTERNAL_OPEN_ONLY,
+ NULL, &fsp);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ 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, NORMAL_CLOSE);
+ close_cnum(conn, user->vuid);
+ unbecome_user();
+ *perr = WERR_OK;
+ return cversion;
+
+
+ error_exit:
+
+ if(fsp)
+ close_file(fsp, NORMAL_CLOSE);
+
+ 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)
+{
+ const char *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);
+ }
+ }
+ }
+
+ architecture = get_short_archi(driver->environment);
+ if (!architecture) {
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ /* 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)
+{
+ const char *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);
+ }
+ }
+ }
+
+ architecture = get_short_archi(driver->environment);
+ if (!architecture) {
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ /* 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
+
+/****************************************************************************
+****************************************************************************/
+WERROR 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;
+ const char *architecture;
+ char *new_dir = NULL;
+ char *old_name = NULL;
+ char *new_name = NULL;
+ DATA_BLOB null_pw;
+ connection_struct *conn;
+ NTSTATUS nt_status;
+ fstring res_type;
+ SMB_STRUCT_STAT st;
+ int i;
+ TALLOC_CTX *ctx = talloc_tos();
+ int ver = 0;
+
+ *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 WERR_UNKNOWN_LEVEL;
+ }
+
+ architecture = get_short_archi(driver->environment);
+ if (!architecture) {
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ /*
+ * 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.
+ */
+
+ null_pw = data_blob_null;
+ fstrcpy(res_type, "A:");
+ become_root();
+ conn = make_connection_with_chdir("print$", null_pw, res_type, 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 WERR_NO_SUCH_SHARE;
+ }
+
+ /*
+ * 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 WERR_ACCESS_DENIED;
+ }
+
+ /* WE ARE NOW RUNNING AS USER conn->vuid !!!!! */
+
+ /*
+ * make the directories version and version\driver_name
+ * under the architecture directory.
+ */
+ DEBUG(5,("Creating first directory\n"));
+ new_dir = talloc_asprintf(ctx,
+ "%s/%d",
+ architecture,
+ driver->cversion);
+ if (!new_dir) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ new_dir = driver_unix_convert(conn,new_dir,&st);
+ if (!new_dir) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+
+ create_directory(conn, NULL, 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)) {
+ new_name = talloc_asprintf(ctx,
+ "%s/%s",
+ architecture,
+ driver->driverpath);
+ if (!new_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ old_name = talloc_asprintf(ctx,
+ "%s/%s",
+ new_dir,
+ driver->driverpath);
+ if (!old_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ new_name = driver_unix_convert(conn,new_name,&st);
+ if (!new_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ if ( !NT_STATUS_IS_OK(copy_file(ctx,conn, new_name, old_name, OPENX_FILE_EXISTS_TRUNCATE|
+ OPENX_FILE_CREATE_IF_NOT_EXIST, 0, False))) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = WERR_ACCESS_DENIED;
+ ver = -1;
+ }
+ }
+ }
+
+ if (driver->datafile && strlen(driver->datafile)) {
+ if (!strequal(driver->datafile, driver->driverpath)) {
+ new_name = talloc_asprintf(ctx,
+ "%s/%s",
+ architecture,
+ driver->datafile);
+ if (!new_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ old_name = talloc_asprintf(ctx,
+ "%s/%s",
+ new_dir,
+ driver->datafile);
+ if (!old_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ new_name = driver_unix_convert(conn,new_name,&st);
+ if (!new_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ if ( !NT_STATUS_IS_OK(copy_file(ctx,conn, new_name, old_name, OPENX_FILE_EXISTS_TRUNCATE|
+ OPENX_FILE_CREATE_IF_NOT_EXIST, 0, False))) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = WERR_ACCESS_DENIED;
+ ver = -1;
+ }
+ }
+ }
+ }
+
+ if (driver->configfile && strlen(driver->configfile)) {
+ if (!strequal(driver->configfile, driver->driverpath) &&
+ !strequal(driver->configfile, driver->datafile)) {
+ new_name = talloc_asprintf(ctx,
+ "%s/%s",
+ architecture,
+ driver->configfile);
+ if (!new_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ old_name = talloc_asprintf(ctx,
+ "%s/%s",
+ new_dir,
+ driver->configfile);
+ if (!old_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ new_name = driver_unix_convert(conn,new_name,&st);
+ if (!new_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ if ( !NT_STATUS_IS_OK(copy_file(ctx,conn, new_name, old_name, OPENX_FILE_EXISTS_TRUNCATE|
+ OPENX_FILE_CREATE_IF_NOT_EXIST, 0, False))) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = WERR_ACCESS_DENIED;
+ ver = -1;
+ }
+ }
+ }
+ }
+
+ if (driver->helpfile && strlen(driver->helpfile)) {
+ if (!strequal(driver->helpfile, driver->driverpath) &&
+ !strequal(driver->helpfile, driver->datafile) &&
+ !strequal(driver->helpfile, driver->configfile)) {
+ new_name = talloc_asprintf(ctx,
+ "%s/%s",
+ architecture,
+ driver->helpfile);
+ if (!new_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ old_name = talloc_asprintf(ctx,
+ "%s/%s",
+ new_dir,
+ driver->helpfile);
+ if (!old_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ new_name = driver_unix_convert(conn,new_name,&st);
+ if (!new_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ if ( !NT_STATUS_IS_OK(copy_file(ctx,conn, new_name, old_name, OPENX_FILE_EXISTS_TRUNCATE|
+ OPENX_FILE_CREATE_IF_NOT_EXIST, 0, False))) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = WERR_ACCESS_DENIED;
+ ver = -1;
+ }
+ }
+ }
+ }
+
+ 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;
+ }
+ }
+
+ new_name = talloc_asprintf(ctx,
+ "%s/%s",
+ architecture,
+ driver->dependentfiles[i]);
+ if (!new_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ old_name = talloc_asprintf(ctx,
+ "%s/%s",
+ new_dir,
+ driver->dependentfiles[i]);
+ if (!old_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ new_name = driver_unix_convert(conn,new_name,&st);
+ if (!new_name) {
+ *perr = WERR_NOMEM;
+ goto err_exit;
+ }
+ if ( !NT_STATUS_IS_OK(copy_file(ctx,conn, new_name, old_name,
+ OPENX_FILE_EXISTS_TRUNCATE|
+ OPENX_FILE_CREATE_IF_NOT_EXIST, 0, False))) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = WERR_ACCESS_DENIED;
+ ver = -1;
+ }
+ }
+ }
+ NextDriver: ;
+ }
+ }
+
+ err_exit:
+
+ close_cnum(conn, user->vuid);
+ unbecome_user();
+
+ if (W_ERROR_EQUAL(*perr, WERR_OK)) {
+ return WERR_OK;
+ }
+ if (ver == -1) {
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+ return (*perr);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static uint32 add_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver)
+{
+ TALLOC_CTX *ctx = talloc_tos();
+ int len, buflen;
+ const char *architecture;
+ char *directory = NULL;
+ fstring temp_name;
+ char *key = NULL;
+ uint8 *buf;
+ int i, ret;
+ TDB_DATA dbuf;
+
+ architecture = get_short_archi(driver->environment);
+ if (!architecture) {
+ return (uint32)-1;
+ }
+
+ /* 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.
+ */
+
+ directory = talloc_asprintf(ctx, "\\print$\\%s\\%d\\",
+ architecture, driver->cversion);
+ if (!directory) {
+ return (uint32)-1;
+ }
+
+ /* .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);
+ }
+ }
+
+ key = talloc_asprintf(ctx, "%s%s/%d/%s", DRIVERS_PREFIX,
+ architecture, driver->cversion, driver->name);
+ if (!key) {
+ return (uint32)-1;
+ }
+
+ 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) {
+ buf = (uint8 *)SMB_REALLOC(buf, len);
+ if (!buf) {
+ DEBUG(0,("add_a_printer_driver_3: failed to enlarge buffer\n!"));
+ ret = -1;
+ goto done;
+ }
+ buflen = len;
+ goto again;
+ }
+
+ dbuf.dptr = buf;
+ dbuf.dsize = len;
+
+ ret = tdb_store_bystring(tdb_drivers, key, 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, const char *driver, const char *arch)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 info;
+
+ ZERO_STRUCT(info);
+
+ fstrcpy(info.name, driver);
+ fstrcpy(info.defaultdatatype, "RAW");
+
+ fstrcpy(info.driverpath, "");
+ fstrcpy(info.datafile, "");
+ fstrcpy(info.configfile, "");
+ fstrcpy(info.helpfile, "");
+
+ if ((info.dependentfiles= SMB_MALLOC_ARRAY(fstring, 2)) == NULL)
+ return WERR_NOMEM;
+
+ memset(info.dependentfiles, '\0', 2*sizeof(fstring));
+ fstrcpy(info.dependentfiles[0], "");
+
+ *info_ptr = (NT_PRINTER_DRIVER_INFO_LEVEL_3 *)memdup(&info, sizeof(info));
+ if (!*info_ptr) {
+ SAFE_FREE(info.dependentfiles);
+ return WERR_NOMEM;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR get_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, fstring drivername, const char *arch, uint32 version)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 driver;
+ TDB_DATA dbuf;
+ const char *architecture;
+ int len = 0;
+ int i;
+ char *key = NULL;
+
+ ZERO_STRUCT(driver);
+
+ architecture = get_short_archi(arch);
+ if ( !architecture ) {
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ /* Windows 4.0 (i.e. win9x) should always use a version of 0 */
+
+ if ( strcmp( architecture, SPL_ARCH_WIN40 ) == 0 )
+ version = 0;
+
+ DEBUG(8,("get_a_printer_driver_3: [%s%s/%d/%s]\n", DRIVERS_PREFIX, architecture, version, drivername));
+
+ if (asprintf(&key, "%s%s/%d/%s", DRIVERS_PREFIX,
+ architecture, version, drivername) < 0) {
+ return WERR_NOMEM;
+ }
+
+ dbuf = tdb_fetch_bystring(tdb_drivers, key);
+ if (!dbuf.dptr) {
+ SAFE_FREE(key);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ 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) {
+ driver.dependentfiles = SMB_REALLOC_ARRAY(driver.dependentfiles, fstring, i+2);
+ if ( !driver.dependentfiles ) {
+ DEBUG(0,("get_a_printer_driver_3: failed to enlarge buffer!\n"));
+ break;
+ }
+
+ len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f",
+ &driver.dependentfiles[i]);
+ i++;
+ }
+
+ if ( driver.dependentfiles )
+ fstrcpy( driver.dependentfiles[i], "" );
+
+ SAFE_FREE(dbuf.dptr);
+ SAFE_FREE(key);
+
+ if (len != dbuf.dsize) {
+ SAFE_FREE(driver.dependentfiles);
+
+ return get_a_printer_driver_3_default(info_ptr, drivername, arch);
+ }
+
+ *info_ptr = (NT_PRINTER_DRIVER_INFO_LEVEL_3 *)memdup(&driver, sizeof(driver));
+ if (!*info_ptr) {
+ SAFE_FREE(driver.dependentfiles);
+ return WERR_NOMEM;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ 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(20,("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(20,("version:[%d]\n", info3->cversion));
+ DEBUGADD(20,("name:[%s]\n", info3->name));
+ DEBUGADD(20,("environment:[%s]\n", info3->environment));
+ DEBUGADD(20,("driverpath:[%s]\n", info3->driverpath));
+ DEBUGADD(20,("datafile:[%s]\n", info3->datafile));
+ DEBUGADD(20,("configfile:[%s]\n", info3->configfile));
+ DEBUGADD(20,("helpfile:[%s]\n", info3->helpfile));
+ DEBUGADD(20,("monitorname:[%s]\n", info3->monitorname));
+ DEBUGADD(20,("defaultdatatype:[%s]\n", info3->defaultdatatype));
+
+ for (i=0; info3->dependentfiles &&
+ *info3->dependentfiles[i]; i++) {
+ DEBUGADD(20,("dependentfile:[%s]\n",
+ info3->dependentfiles[i]));
+ }
+ result=0;
+ }
+ break;
+ }
+ default:
+ DEBUGADD(20,("dump_a_printer_driver: Level %u not implemented\n", (unsigned int)level));
+ result=1;
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+int pack_devicemode(NT_DEVICEMODE *nt_devmode, uint8 *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->nt_dev_private);
+
+ if (nt_devmode->nt_dev_private) {
+ len += tdb_pack(buf+len, buflen-len, "B",
+ nt_devmode->driverextra,
+ nt_devmode->nt_dev_private);
+ }
+
+ DEBUG(8,("Packed devicemode [%s]\n", nt_devmode->formname));
+
+ return len;
+}
+
+/****************************************************************************
+ Pack all values in all printer keys
+ ***************************************************************************/
+
+static int pack_values(NT_PRINTER_DATA *data, uint8 *buf, int buflen)
+{
+ int len = 0;
+ int i, j;
+ REGISTRY_VALUE *val;
+ REGVAL_CTR *val_ctr;
+ char *path = NULL;
+ int num_values;
+
+ if ( !data )
+ return 0;
+
+ /* loop over all keys */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ val_ctr = data->keys[i].values;
+ num_values = regval_ctr_numvals( val_ctr );
+
+ /* pack the keyname followed by a empty value */
+
+ len += tdb_pack(buf+len, buflen-len, "pPdB",
+ &data->keys[i].name,
+ data->keys[i].name,
+ REG_NONE,
+ 0,
+ NULL);
+
+ /* now loop over all values */
+
+ for ( j=0; j<num_values; j++ ) {
+ /* pathname should be stored as <key>\<value> */
+
+ val = regval_ctr_specific_value( val_ctr, j );
+ if (asprintf(&path, "%s\\%s",
+ data->keys[i].name,
+ regval_name(val)) < 0) {
+ return -1;
+ }
+
+ len += tdb_pack(buf+len, buflen-len, "pPdB",
+ val,
+ path,
+ regval_type(val),
+ regval_size(val),
+ regval_data_p(val) );
+
+ DEBUG(8,("specific: [%s], len: %d\n", regval_name(val), regval_size(val)));
+ SAFE_FREE(path);
+ }
+
+ }
+
+ /* terminator */
+
+ len += tdb_pack(buf+len, buflen-len, "p", NULL);
+
+ return len;
+}
+
+
+/****************************************************************************
+ Delete a printer - this just deletes the printer info file, any open
+ handles are not affected.
+****************************************************************************/
+
+uint32 del_a_printer(const char *sharename)
+{
+ TDB_DATA kbuf;
+ char *printdb_path = NULL;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ kbuf = make_printer_tdbkey(ctx, sharename);
+ tdb_delete(tdb_printers, kbuf);
+
+ kbuf= make_printers_secdesc_tdbkey(ctx, sharename);
+ tdb_delete(tdb_printers, kbuf);
+
+ close_all_print_db();
+
+ if (geteuid() == 0) {
+ if (asprintf(&printdb_path, "%s%s.tdb",
+ lock_path("printing/"),
+ sharename) < 0) {
+ return (uint32)-1;
+ }
+ unlink(printdb_path);
+ SAFE_FREE(printdb_path);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR update_a_printer_2(NT_PRINTER_INFO_LEVEL_2 *info)
+{
+ uint8 *buf;
+ int buflen, len;
+ int retlen;
+ 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 i
+ * path=\\other_server\printer
+ *
+ * We only store the printername, not \\server\printername
+ */
+
+ if ( info->servername[0] != '\0' ) {
+ trim_string(info->printername, info->servername, NULL);
+ trim_char(info->printername, '\\', '\0');
+ 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);
+ retlen = pack_values( info->data, buf+len, buflen-len );
+ if (retlen == -1) {
+ ret = WERR_NOMEM;
+ goto done;
+ }
+ len += retlen;
+
+ if (buflen != len) {
+ buf = (uint8 *)SMB_REALLOC(buf, len);
+ if (!buf) {
+ DEBUG(0,("update_a_printer_2: failed to enlarge buffer!\n"));
+ ret = WERR_NOMEM;
+ goto done;
+ }
+ buflen = len;
+ goto again;
+ }
+
+ kbuf = make_printer_tdbkey(talloc_tos(), info->sharename );
+
+ 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;
+}
+
+
+/****************************************************************************
+ Malloc and return an NT devicemode.
+****************************************************************************/
+
+NT_DEVICEMODE *construct_nt_devicemode(const fstring default_devicename)
+{
+
+ char adevice[MAXDEVICENAME];
+ NT_DEVICEMODE *nt_devmode = SMB_MALLOC_P(NT_DEVICEMODE);
+
+ if (nt_devmode == NULL) {
+ DEBUG(0,("construct_nt_devicemode: malloc fail.\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCTP(nt_devmode);
+
+ slprintf(adevice, sizeof(adevice), "%s", default_devicename);
+ 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->nt_dev_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 ( !nt_devicemode )
+ return 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->nt_dev_private = NULL;
+ if (nt_devicemode->nt_dev_private != NULL) {
+ if ((new_nt_devicemode->nt_dev_private = (uint8 *)memdup(nt_devicemode->nt_dev_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->nt_dev_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;
+
+ if ( !info )
+ return;
+
+ free_nt_devicemode(&info->devmode);
+
+ TALLOC_FREE( *info_ptr );
+}
+
+
+/****************************************************************************
+****************************************************************************/
+int unpack_devicemode(NT_DEVICEMODE **nt_devmode, const uint8 *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.nt_dev_private);
+
+ if (devmode.nt_dev_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.nt_dev_private);
+ devmode.driverextra=(uint16)extra_len;
+
+ /* check to catch an invalid TDB entry so we don't segfault */
+ if (devmode.driverextra == 0) {
+ devmode.nt_dev_private = NULL;
+ }
+ }
+
+ *nt_devmode = (NT_DEVICEMODE *)memdup(&devmode, sizeof(devmode));
+ if (!*nt_devmode) {
+ SAFE_FREE(devmode.nt_dev_private);
+ return -1;
+ }
+
+ DEBUG(8,("Unpacked devicemode [%s](%s)\n", devmode.devicename, devmode.formname));
+ if (devmode.nt_dev_private)
+ DEBUG(8,("with a private section of %d bytes\n", devmode.driverextra));
+
+ return len;
+}
+
+/****************************************************************************
+ Allocate and initialize a new slot.
+***************************************************************************/
+
+int add_new_printer_key( NT_PRINTER_DATA *data, const char *name )
+{
+ NT_PRINTER_KEY *d;
+ int key_index;
+
+ if ( !name || !data )
+ return -1;
+
+ /* allocate another slot in the NT_PRINTER_KEY array */
+
+ if ( !(d = TALLOC_REALLOC_ARRAY( data, data->keys, NT_PRINTER_KEY, data->num_keys+1)) ) {
+ DEBUG(0,("add_new_printer_key: Realloc() failed!\n"));
+ return -1;
+ }
+
+ data->keys = d;
+
+ key_index = data->num_keys;
+
+ /* initialze new key */
+
+ data->keys[key_index].name = talloc_strdup( data, name );
+
+ if ( !(data->keys[key_index].values = TALLOC_ZERO_P( data, REGVAL_CTR )) )
+ return -1;
+
+ data->num_keys++;
+
+ DEBUG(10,("add_new_printer_key: Inserted new data key [%s]\n", name ));
+
+ return key_index;
+}
+
+/****************************************************************************
+ search for a registry key name in the existing printer data
+ ***************************************************************************/
+
+int delete_printer_key( NT_PRINTER_DATA *data, const char *name )
+{
+ int i;
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( strequal( data->keys[i].name, name ) ) {
+
+ /* cleanup memory */
+
+ TALLOC_FREE( data->keys[i].name );
+ TALLOC_FREE( data->keys[i].values );
+
+ /* if not the end of the array, move remaining elements down one slot */
+
+ data->num_keys--;
+ if ( data->num_keys && (i < data->num_keys) )
+ memmove( &data->keys[i], &data->keys[i+1], sizeof(NT_PRINTER_KEY)*(data->num_keys-i) );
+
+ break;
+ }
+ }
+
+
+ return data->num_keys;
+}
+
+/****************************************************************************
+ search for a registry key name in the existing printer data
+ ***************************************************************************/
+
+int lookup_printerkey( NT_PRINTER_DATA *data, const char *name )
+{
+ int key_index = -1;
+ int i;
+
+ if ( !data || !name )
+ return -1;
+
+ DEBUG(12,("lookup_printerkey: Looking for [%s]\n", name));
+
+ /* loop over all existing keys */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( strequal(data->keys[i].name, name) ) {
+ DEBUG(12,("lookup_printerkey: Found [%s]!\n", name));
+ key_index = i;
+ break;
+
+ }
+ }
+
+ return key_index;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+int get_printer_subkeys( NT_PRINTER_DATA *data, const char* key, fstring **subkeys )
+{
+ int i, j;
+ int key_len;
+ int num_subkeys = 0;
+ char *p;
+ fstring *subkeys_ptr = NULL;
+ fstring subkeyname;
+
+ *subkeys = NULL;
+
+ if ( !data )
+ return 0;
+
+ if ( !key )
+ return -1;
+
+ /* special case of asking for the top level printer data registry key names */
+
+ if ( strlen(key) == 0 ) {
+ for ( i=0; i<data->num_keys; i++ ) {
+
+ /* found a match, so allocate space and copy the name */
+
+ if ( !(subkeys_ptr = SMB_REALLOC_ARRAY( subkeys_ptr, fstring, num_subkeys+2)) ) {
+ DEBUG(0,("get_printer_subkeys: Realloc failed for [%d] entries!\n",
+ num_subkeys+1));
+ return -1;
+ }
+
+ fstrcpy( subkeys_ptr[num_subkeys], data->keys[i].name );
+ num_subkeys++;
+ }
+
+ goto done;
+ }
+
+ /* asking for the subkeys of some key */
+ /* subkey paths are stored in the key name using '\' as the delimiter */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( StrnCaseCmp(data->keys[i].name, key, strlen(key)) == 0 ) {
+
+ /* if we found the exact key, then break */
+ key_len = strlen( key );
+ if ( strlen(data->keys[i].name) == key_len )
+ break;
+
+ /* get subkey path */
+
+ p = data->keys[i].name + key_len;
+ if ( *p == '\\' )
+ p++;
+ fstrcpy( subkeyname, p );
+ if ( (p = strchr( subkeyname, '\\' )) )
+ *p = '\0';
+
+ /* don't add a key more than once */
+
+ for ( j=0; j<num_subkeys; j++ ) {
+ if ( strequal( subkeys_ptr[j], subkeyname ) )
+ break;
+ }
+
+ if ( j != num_subkeys )
+ continue;
+
+ /* found a match, so allocate space and copy the name */
+
+ if ( !(subkeys_ptr = SMB_REALLOC_ARRAY( subkeys_ptr, fstring, num_subkeys+2)) ) {
+ DEBUG(0,("get_printer_subkeys: Realloc failed for [%d] entries!\n",
+ num_subkeys+1));
+ return 0;
+ }
+
+ fstrcpy( subkeys_ptr[num_subkeys], subkeyname );
+ num_subkeys++;
+ }
+
+ }
+
+ /* return error if the key was not found */
+
+ if ( i == data->num_keys ) {
+ SAFE_FREE(subkeys_ptr);
+ return -1;
+ }
+
+done:
+ /* tag off the end */
+
+ if (num_subkeys)
+ fstrcpy(subkeys_ptr[num_subkeys], "" );
+
+ *subkeys = subkeys_ptr;
+
+ return num_subkeys;
+}
+
+#ifdef HAVE_ADS
+static void map_sz_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+ const char *sz)
+{
+ smb_ucs2_t conv_str[1024];
+ size_t str_size;
+
+ regval_ctr_delvalue(ctr, val_name);
+ str_size = push_ucs2(NULL, conv_str, sz, sizeof(conv_str),
+ STR_TERMINATE | STR_NOALIGN);
+ regval_ctr_addvalue(ctr, val_name, REG_SZ,
+ (char *) conv_str, str_size);
+}
+
+static void map_dword_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+ uint32 dword)
+{
+ regval_ctr_delvalue(ctr, val_name);
+ regval_ctr_addvalue(ctr, val_name, REG_DWORD,
+ (char *) &dword, sizeof(dword));
+}
+
+static void map_bool_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+ bool b)
+{
+ uint8 bin_bool = (b ? 1 : 0);
+ regval_ctr_delvalue(ctr, val_name);
+ regval_ctr_addvalue(ctr, val_name, REG_BINARY,
+ (char *) &bin_bool, sizeof(bin_bool));
+}
+
+static void map_single_multi_sz_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+ const char *multi_sz)
+{
+ smb_ucs2_t *conv_strs = NULL;
+ size_t str_size;
+
+ /* a multi-sz has to have a null string terminator, i.e., the last
+ string must be followed by two nulls */
+ str_size = strlen(multi_sz) + 2;
+ conv_strs = SMB_CALLOC_ARRAY(smb_ucs2_t, str_size);
+ if (!conv_strs) {
+ return;
+ }
+
+ /* Change to byte units. */
+ str_size *= sizeof(smb_ucs2_t);
+ push_ucs2(NULL, conv_strs, multi_sz, str_size,
+ STR_TERMINATE | STR_NOALIGN);
+
+ regval_ctr_delvalue(ctr, val_name);
+ regval_ctr_addvalue(ctr, val_name, REG_MULTI_SZ,
+ (char *) conv_strs, str_size);
+ safe_free(conv_strs);
+
+}
+
+/****************************************************************************
+ * Map the NT_PRINTER_INFO_LEVEL_2 data into DsSpooler keys for publishing.
+ *
+ * @param info2 NT_PRINTER_INFO_LEVEL_2 describing printer - gets modified
+ * @return bool indicating success or failure
+ ***************************************************************************/
+
+static bool map_nt_printer_info2_to_dsspooler(NT_PRINTER_INFO_LEVEL_2 *info2)
+{
+ REGVAL_CTR *ctr = NULL;
+ fstring longname;
+ const char *dnssuffix;
+ char *allocated_string = NULL;
+ const char *ascii_str;
+ int i;
+
+ if ((i = lookup_printerkey(info2->data, SPOOL_DSSPOOLER_KEY)) < 0)
+ i = add_new_printer_key(info2->data, SPOOL_DSSPOOLER_KEY);
+ ctr = info2->data->keys[i].values;
+
+ map_sz_into_ctr(ctr, SPOOL_REG_PRINTERNAME, info2->sharename);
+ map_sz_into_ctr(ctr, SPOOL_REG_SHORTSERVERNAME, global_myname());
+
+ /* we make the assumption that the netbios name is the same
+ as the DNS name sinc ethe former will be what we used to
+ join the domain */
+
+ dnssuffix = get_mydnsdomname(talloc_tos());
+ if (dnssuffix && *dnssuffix) {
+ fstr_sprintf( longname, "%s.%s", global_myname(), dnssuffix );
+ } else {
+ fstrcpy( longname, global_myname() );
+ }
+
+ map_sz_into_ctr(ctr, SPOOL_REG_SERVERNAME, longname);
+
+ asprintf(&allocated_string, "\\\\%s\\%s", longname, info2->sharename);
+ map_sz_into_ctr(ctr, SPOOL_REG_UNCNAME, allocated_string);
+ SAFE_FREE(allocated_string);
+
+ map_dword_into_ctr(ctr, SPOOL_REG_VERSIONNUMBER, 4);
+ map_sz_into_ctr(ctr, SPOOL_REG_DRIVERNAME, info2->drivername);
+ map_sz_into_ctr(ctr, SPOOL_REG_LOCATION, info2->location);
+ map_sz_into_ctr(ctr, SPOOL_REG_DESCRIPTION, info2->comment);
+ map_single_multi_sz_into_ctr(ctr, SPOOL_REG_PORTNAME, info2->portname);
+ map_sz_into_ctr(ctr, SPOOL_REG_PRINTSEPARATORFILE, info2->sepfile);
+ map_dword_into_ctr(ctr, SPOOL_REG_PRINTSTARTTIME, info2->starttime);
+ map_dword_into_ctr(ctr, SPOOL_REG_PRINTENDTIME, info2->untiltime);
+ map_dword_into_ctr(ctr, SPOOL_REG_PRIORITY, info2->priority);
+
+ map_bool_into_ctr(ctr, SPOOL_REG_PRINTKEEPPRINTEDJOBS,
+ (info2->attributes &
+ PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS));
+
+ switch (info2->attributes & 0x3) {
+ case 0:
+ ascii_str = SPOOL_REGVAL_PRINTWHILESPOOLING;
+ break;
+ case 1:
+ ascii_str = SPOOL_REGVAL_PRINTAFTERSPOOLED;
+ break;
+ case 2:
+ ascii_str = SPOOL_REGVAL_PRINTDIRECT;
+ break;
+ default:
+ ascii_str = "unknown";
+ }
+ map_sz_into_ctr(ctr, SPOOL_REG_PRINTSPOOLING, ascii_str);
+
+ return True;
+}
+
+/*****************************************************************
+ ****************************************************************/
+
+static void store_printer_guid(NT_PRINTER_INFO_LEVEL_2 *info2,
+ struct GUID guid)
+{
+ int i;
+ REGVAL_CTR *ctr=NULL;
+ UNISTR2 unistr_guid;
+
+ /* find the DsSpooler key */
+ if ((i = lookup_printerkey(info2->data, SPOOL_DSSPOOLER_KEY)) < 0)
+ i = add_new_printer_key(info2->data, SPOOL_DSSPOOLER_KEY);
+ ctr = info2->data->keys[i].values;
+
+ regval_ctr_delvalue(ctr, "objectGUID");
+
+ /* We used to store this as a REG_BINARY but that causes
+ Vista to whine */
+
+ ZERO_STRUCT( unistr_guid );
+
+ init_unistr2( &unistr_guid, smb_uuid_string(talloc_tos(), guid),
+ UNI_STR_TERMINATE );
+
+ regval_ctr_addvalue(ctr, "objectGUID", REG_SZ,
+ (char *)unistr_guid.buffer,
+ unistr_guid.uni_max_len*2);
+
+}
+
+static WERROR nt_printer_publish_ads(ADS_STRUCT *ads,
+ NT_PRINTER_INFO_LEVEL *printer)
+{
+ ADS_STATUS ads_rc;
+ LDAPMessage *res;
+ char *prt_dn = NULL, *srv_dn, *srv_cn_0, *srv_cn_escaped, *sharename_escaped;
+ char *srv_dn_utf8, **srv_cn_utf8;
+ TALLOC_CTX *ctx;
+ ADS_MODLIST mods;
+ const char *attrs[] = {"objectGUID", NULL};
+ struct GUID guid;
+ WERROR win_rc = WERR_OK;
+ size_t converted_size;
+
+ DEBUG(5, ("publishing printer %s\n", printer->info_2->printername));
+
+ /* figure out where to publish */
+ ads_find_machine_acct(ads, &res, global_myname());
+
+ /* We use ldap_get_dn here as we need the answer
+ * in utf8 to call ldap_explode_dn(). JRA. */
+
+ srv_dn_utf8 = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
+ if (!srv_dn_utf8) {
+ ads_destroy(&ads);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ ads_msgfree(ads, res);
+ srv_cn_utf8 = ldap_explode_dn(srv_dn_utf8, 1);
+ if (!srv_cn_utf8) {
+ ldap_memfree(srv_dn_utf8);
+ ads_destroy(&ads);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ /* Now convert to CH_UNIX. */
+ if (!pull_utf8_allocate(&srv_dn, srv_dn_utf8, &converted_size)) {
+ ldap_memfree(srv_dn_utf8);
+ ldap_memfree(srv_cn_utf8);
+ ads_destroy(&ads);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ if (!pull_utf8_allocate(&srv_cn_0, srv_cn_utf8[0], &converted_size)) {
+ ldap_memfree(srv_dn_utf8);
+ ldap_memfree(srv_cn_utf8);
+ ads_destroy(&ads);
+ SAFE_FREE(srv_dn);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+
+ ldap_memfree(srv_dn_utf8);
+ ldap_memfree(srv_cn_utf8);
+
+ srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn_0);
+ if (!srv_cn_escaped) {
+ SAFE_FREE(srv_cn_0);
+ ldap_memfree(srv_dn_utf8);
+ ads_destroy(&ads);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ sharename_escaped = escape_rdn_val_string_alloc(printer->info_2->sharename);
+ if (!sharename_escaped) {
+ SAFE_FREE(srv_cn_escaped);
+ SAFE_FREE(srv_cn_0);
+ ldap_memfree(srv_dn_utf8);
+ ads_destroy(&ads);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+
+
+ asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, sharename_escaped, srv_dn);
+
+ SAFE_FREE(srv_dn);
+ SAFE_FREE(srv_cn_0);
+ SAFE_FREE(srv_cn_escaped);
+ SAFE_FREE(sharename_escaped);
+
+ /* build the ads mods */
+ ctx = talloc_init("nt_printer_publish_ads");
+ if (ctx == NULL) {
+ SAFE_FREE(prt_dn);
+ return WERR_NOMEM;
+ }
+
+ mods = ads_init_mods(ctx);
+
+ if (mods == NULL) {
+ SAFE_FREE(prt_dn);
+ talloc_destroy(ctx);
+ return WERR_NOMEM;
+ }
+
+ get_local_printer_publishing_data(ctx, &mods, printer->info_2->data);
+ ads_mod_str(ctx, &mods, SPOOL_REG_PRINTERNAME,
+ printer->info_2->sharename);
+
+ /* publish it */
+ ads_rc = ads_mod_printer_entry(ads, prt_dn, ctx, &mods);
+ if (ads_rc.err.rc == LDAP_NO_SUCH_OBJECT) {
+ int i;
+ for (i=0; mods[i] != 0; i++)
+ ;
+ mods[i] = (LDAPMod *)-1;
+ ads_rc = ads_add_printer_entry(ads, prt_dn, ctx, &mods);
+ }
+
+ if (!ADS_ERR_OK(ads_rc))
+ DEBUG(3, ("error publishing %s: %s\n", printer->info_2->sharename, ads_errstr(ads_rc)));
+
+ talloc_destroy(ctx);
+
+ /* retreive the guid and store it locally */
+ if (ADS_ERR_OK(ads_search_dn(ads, &res, prt_dn, attrs))) {
+ ZERO_STRUCT(guid);
+ ads_pull_guid(ads, res, &guid);
+ ads_msgfree(ads, res);
+ store_printer_guid(printer->info_2, guid);
+ win_rc = mod_a_printer(printer, 2);
+ }
+
+ SAFE_FREE(prt_dn);
+ return win_rc;
+}
+
+static WERROR nt_printer_unpublish_ads(ADS_STRUCT *ads,
+ NT_PRINTER_INFO_LEVEL *printer)
+{
+ ADS_STATUS ads_rc;
+ LDAPMessage *res;
+ char *prt_dn = NULL;
+
+ DEBUG(5, ("unpublishing printer %s\n", printer->info_2->printername));
+
+ /* remove the printer from the directory */
+ ads_rc = ads_find_printer_on_server(ads, &res,
+ printer->info_2->sharename, global_myname());
+
+ if (ADS_ERR_OK(ads_rc) && ads_count_replies(ads, res)) {
+ prt_dn = ads_get_dn(ads, res);
+ if (!prt_dn) {
+ ads_msgfree(ads, res);
+ return WERR_NOMEM;
+ }
+ ads_rc = ads_del_dn(ads, prt_dn);
+ ads_memfree(ads, prt_dn);
+ }
+
+ ads_msgfree(ads, res);
+ return WERR_OK;
+}
+
+/****************************************************************************
+ * Publish a printer in the directory
+ *
+ * @param snum describing printer service
+ * @return WERROR indicating status of publishing
+ ***************************************************************************/
+
+WERROR nt_printer_publish(Printer_entry *print_hnd, int snum, int action)
+{
+ ADS_STATUS ads_rc;
+ ADS_STRUCT *ads = NULL;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ WERROR win_rc;
+
+ win_rc = get_a_printer(print_hnd, &printer, 2, lp_servicename(snum));
+ if (!W_ERROR_IS_OK(win_rc))
+ goto done;
+
+ switch (action) {
+ case SPOOL_DS_PUBLISH:
+ case SPOOL_DS_UPDATE:
+ /* set the DsSpooler info and attributes */
+ if (!(map_nt_printer_info2_to_dsspooler(printer->info_2))) {
+ win_rc = WERR_NOMEM;
+ goto done;
+ }
+
+ printer->info_2->attributes |= PRINTER_ATTRIBUTE_PUBLISHED;
+ break;
+ case SPOOL_DS_UNPUBLISH:
+ printer->info_2->attributes ^= PRINTER_ATTRIBUTE_PUBLISHED;
+ break;
+ default:
+ win_rc = WERR_NOT_SUPPORTED;
+ goto done;
+ }
+
+ win_rc = mod_a_printer(printer, 2);
+ if (!W_ERROR_IS_OK(win_rc)) {
+ DEBUG(3, ("err %d saving data\n", W_ERROR_V(win_rc)));
+ goto done;
+ }
+
+ ads = ads_init(lp_realm(), lp_workgroup(), NULL);
+ if (!ads) {
+ DEBUG(3, ("ads_init() failed\n"));
+ win_rc = WERR_SERVER_UNAVAILABLE;
+ goto done;
+ }
+ setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
+ SAFE_FREE(ads->auth.password);
+ ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
+ NULL, NULL);
+
+ /* ads_connect() will find the DC for us */
+ ads_rc = ads_connect(ads);
+ if (!ADS_ERR_OK(ads_rc)) {
+ DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
+ win_rc = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ switch (action) {
+ case SPOOL_DS_PUBLISH:
+ case SPOOL_DS_UPDATE:
+ win_rc = nt_printer_publish_ads(ads, printer);
+ break;
+ case SPOOL_DS_UNPUBLISH:
+ win_rc = nt_printer_unpublish_ads(ads, printer);
+ break;
+ }
+
+done:
+ free_a_printer(&printer, 2);
+ ads_destroy(&ads);
+ return win_rc;
+}
+
+WERROR check_published_printers(void)
+{
+ ADS_STATUS ads_rc;
+ ADS_STRUCT *ads = NULL;
+ int snum;
+ int n_services = lp_numservices();
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ ads = ads_init(lp_realm(), lp_workgroup(), NULL);
+ if (!ads) {
+ DEBUG(3, ("ads_init() failed\n"));
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
+ SAFE_FREE(ads->auth.password);
+ ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
+ NULL, NULL);
+
+ /* ads_connect() will find the DC for us */
+ ads_rc = ads_connect(ads);
+ if (!ADS_ERR_OK(ads_rc)) {
+ DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
+ ads_destroy(&ads);
+ ads_kdestroy("MEMORY:prtpub_cache");
+ return WERR_ACCESS_DENIED;
+ }
+
+ for (snum = 0; snum < n_services; snum++) {
+ if (!(lp_snum_ok(snum) && lp_print_ok(snum)))
+ continue;
+
+ if (W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2,
+ lp_servicename(snum))) &&
+ (printer->info_2->attributes & PRINTER_ATTRIBUTE_PUBLISHED))
+ nt_printer_publish_ads(ads, printer);
+
+ free_a_printer(&printer, 2);
+ }
+
+ ads_destroy(&ads);
+ ads_kdestroy("MEMORY:prtpub_cache");
+ return WERR_OK;
+}
+
+bool is_printer_published(Printer_entry *print_hnd, int snum,
+ struct GUID *guid)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ REGVAL_CTR *ctr;
+ REGISTRY_VALUE *guid_val;
+ WERROR win_rc;
+ int i;
+ bool ret = False;
+
+ win_rc = get_a_printer(print_hnd, &printer, 2, lp_servicename(snum));
+
+ if (!W_ERROR_IS_OK(win_rc) ||
+ !(printer->info_2->attributes & PRINTER_ATTRIBUTE_PUBLISHED) ||
+ ((i = lookup_printerkey(printer->info_2->data, SPOOL_DSSPOOLER_KEY)) < 0) ||
+ !(ctr = printer->info_2->data->keys[i].values) ||
+ !(guid_val = regval_ctr_getvalue(ctr, "objectGUID")))
+ {
+ free_a_printer(&printer, 2);
+ return False;
+ }
+
+ /* fetching printer guids really ought to be a separate function. */
+
+ if ( guid ) {
+ fstring guid_str;
+
+ /* We used to store the guid as REG_BINARY, then swapped
+ to REG_SZ for Vista compatibility so check for both */
+
+ switch ( regval_type(guid_val) ){
+ case REG_SZ:
+ rpcstr_pull( guid_str, regval_data_p(guid_val),
+ sizeof(guid_str)-1, -1, STR_TERMINATE );
+ ret = smb_string_to_uuid( guid_str, guid );
+ break;
+ case REG_BINARY:
+ if ( regval_size(guid_val) != sizeof(struct GUID) ) {
+ ret = False;
+ break;
+ }
+ memcpy(guid, regval_data_p(guid_val), sizeof(struct GUID));
+ break;
+ default:
+ DEBUG(0,("is_printer_published: GUID value stored as "
+ "invaluid type (%d)\n", regval_type(guid_val) ));
+ break;
+ }
+ }
+
+ free_a_printer(&printer, 2);
+ return ret;
+}
+#else
+WERROR nt_printer_publish(Printer_entry *print_hnd, int snum, int action)
+{
+ return WERR_OK;
+}
+
+WERROR check_published_printers(void)
+{
+ return WERR_OK;
+}
+
+bool is_printer_published(Printer_entry *print_hnd, int snum,
+ struct GUID *guid)
+{
+ return False;
+}
+#endif /* HAVE_ADS */
+
+/****************************************************************************
+ ***************************************************************************/
+
+WERROR delete_all_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key )
+{
+ NT_PRINTER_DATA *data;
+ int i;
+ int removed_keys = 0;
+ int empty_slot;
+
+ data = p2->data;
+ empty_slot = data->num_keys;
+
+ if ( !key )
+ return WERR_INVALID_PARAM;
+
+ /* remove all keys */
+
+ if ( !strlen(key) ) {
+
+ TALLOC_FREE( data );
+
+ p2->data = NULL;
+
+ DEBUG(8,("delete_all_printer_data: Removed all Printer Data from printer [%s]\n",
+ p2->printername ));
+
+ return WERR_OK;
+ }
+
+ /* remove a specific key (and all subkeys) */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( StrnCaseCmp( data->keys[i].name, key, strlen(key)) == 0 ) {
+ DEBUG(8,("delete_all_printer_data: Removed all Printer Data from key [%s]\n",
+ data->keys[i].name));
+
+ TALLOC_FREE( data->keys[i].name );
+ TALLOC_FREE( data->keys[i].values );
+
+ /* mark the slot as empty */
+
+ ZERO_STRUCTP( &data->keys[i] );
+ }
+ }
+
+ /* find the first empty slot */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( !data->keys[i].name ) {
+ empty_slot = i;
+ removed_keys++;
+ break;
+ }
+ }
+
+ if ( i == data->num_keys )
+ /* nothing was removed */
+ return WERR_INVALID_PARAM;
+
+ /* move everything down */
+
+ for ( i=empty_slot+1; i<data->num_keys; i++ ) {
+ if ( data->keys[i].name ) {
+ memcpy( &data->keys[empty_slot], &data->keys[i], sizeof(NT_PRINTER_KEY) );
+ ZERO_STRUCTP( &data->keys[i] );
+ empty_slot++;
+ removed_keys++;
+ }
+ }
+
+ /* update count */
+
+ data->num_keys -= removed_keys;
+
+ /* sanity check to see if anything is left */
+
+ if ( !data->num_keys ) {
+ DEBUG(8,("delete_all_printer_data: No keys left for printer [%s]\n", p2->printername ));
+
+ SAFE_FREE( data->keys );
+ ZERO_STRUCTP( data );
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+WERROR delete_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key, const char *value )
+{
+ WERROR result = WERR_OK;
+ int key_index;
+
+ /* we must have names on non-zero length */
+
+ if ( !key || !*key|| !value || !*value )
+ return WERR_INVALID_NAME;
+
+ /* find the printer key first */
+
+ key_index = lookup_printerkey( p2->data, key );
+ if ( key_index == -1 )
+ return WERR_OK;
+
+ /* make sure the value exists so we can return the correct error code */
+
+ if ( !regval_ctr_getvalue( p2->data->keys[key_index].values, value ) )
+ return WERR_BADFILE;
+
+ regval_ctr_delvalue( p2->data->keys[key_index].values, value );
+
+ DEBUG(8,("delete_printer_data: Removed key => [%s], value => [%s]\n",
+ key, value ));
+
+ return result;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+WERROR add_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key, const char *value,
+ uint32 type, uint8 *data, int real_len )
+{
+ WERROR result = WERR_OK;
+ int key_index;
+
+ /* we must have names on non-zero length */
+
+ if ( !key || !*key|| !value || !*value )
+ return WERR_INVALID_NAME;
+
+ /* find the printer key first */
+
+ key_index = lookup_printerkey( p2->data, key );
+ if ( key_index == -1 )
+ key_index = add_new_printer_key( p2->data, key );
+
+ if ( key_index == -1 )
+ return WERR_NOMEM;
+
+ regval_ctr_addvalue( p2->data->keys[key_index].values, value,
+ type, (const char *)data, real_len );
+
+ DEBUG(8,("add_printer_data: Added key => [%s], value => [%s], type=> [%d], size => [%d]\n",
+ key, value, type, real_len ));
+
+ return result;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+REGISTRY_VALUE* get_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key, const char *value )
+{
+ int key_index;
+
+ if ( (key_index = lookup_printerkey( p2->data, key )) == -1 )
+ return NULL;
+
+ DEBUG(8,("get_printer_data: Attempting to lookup key => [%s], value => [%s]\n",
+ key, value ));
+
+ return regval_ctr_getvalue( p2->data->keys[key_index].values, value );
+}
+
+/****************************************************************************
+ Unpack a list of registry values frem the TDB
+ ***************************************************************************/
+
+static int unpack_values(NT_PRINTER_DATA *printer_data, const uint8 *buf, int buflen)
+{
+ int len = 0;
+ uint32 type;
+ fstring string;
+ const char *valuename = NULL;
+ const char *keyname = NULL;
+ char *str;
+ int size;
+ uint8 *data_p;
+ REGISTRY_VALUE *regval_p;
+ int key_index;
+
+ /* add the "PrinterDriverData" key first for performance reasons */
+
+ add_new_printer_key( printer_data, SPOOL_PRINTERDATA_KEY );
+
+ /* loop and unpack the rest of the registry values */
+
+ while ( True ) {
+
+ /* check to see if there are any more registry values */
+
+ regval_p = NULL;
+ len += tdb_unpack(buf+len, buflen-len, "p", &regval_p);
+ if ( !regval_p )
+ break;
+
+ /* unpack the next regval */
+
+ len += tdb_unpack(buf+len, buflen-len, "fdB",
+ string,
+ &type,
+ &size,
+ &data_p);
+
+ /* lookup for subkey names which have a type of REG_NONE */
+ /* there's no data with this entry */
+
+ if ( type == REG_NONE ) {
+ if ( (key_index=lookup_printerkey( printer_data, string)) == -1 )
+ add_new_printer_key( printer_data, string );
+ continue;
+ }
+
+ /*
+ * break of the keyname from the value name.
+ * Valuenames can have embedded '\'s so be careful.
+ * only support one level of keys. See the
+ * "Konica Fiery S300 50C-K v1.1. enu" 2k driver.
+ * -- jerry
+ */
+
+ str = strchr_m( string, '\\');
+
+ /* Put in "PrinterDriverData" is no key specified */
+
+ if ( !str ) {
+ keyname = SPOOL_PRINTERDATA_KEY;
+ valuename = string;
+ }
+ else {
+ *str = '\0';
+ keyname = string;
+ valuename = str+1;
+ }
+
+ /* see if we need a new key */
+
+ if ( (key_index=lookup_printerkey( printer_data, keyname )) == -1 )
+ key_index = add_new_printer_key( printer_data, keyname );
+
+ if ( key_index == -1 ) {
+ DEBUG(0,("unpack_values: Failed to allocate a new key [%s]!\n",
+ keyname));
+ break;
+ }
+
+ DEBUG(8,("specific: [%s:%s], len: %d\n", keyname, valuename, size));
+
+ /* Vista doesn't like unknown REG_BINARY values in DsSpooler.
+ Thanks to Martin Zielinski for the hint. */
+
+ if ( type == REG_BINARY &&
+ strequal( keyname, SPOOL_DSSPOOLER_KEY ) &&
+ strequal( valuename, "objectGUID" ) )
+ {
+ struct GUID guid;
+ UNISTR2 unistr_guid;
+
+ ZERO_STRUCT( unistr_guid );
+
+ /* convert the GUID to a UNICODE string */
+
+ memcpy( &guid, data_p, sizeof(struct GUID) );
+
+ init_unistr2( &unistr_guid,
+ smb_uuid_string(talloc_tos(), guid),
+ UNI_STR_TERMINATE );
+
+ regval_ctr_addvalue( printer_data->keys[key_index].values,
+ valuename, REG_SZ,
+ (const char *)unistr_guid.buffer,
+ unistr_guid.uni_str_len*2 );
+
+ } else {
+ /* add the value */
+
+ regval_ctr_addvalue( printer_data->keys[key_index].values,
+ valuename, type, (const char *)data_p,
+ size );
+ }
+
+ SAFE_FREE(data_p); /* 'B' option to tdbpack does a malloc() */
+
+ }
+
+ return len;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+static char *last_from;
+static char *last_to;
+
+static const char *get_last_from(void)
+{
+ if (!last_from) {
+ return "";
+ }
+ return last_from;
+}
+
+static const char *get_last_to(void)
+{
+ if (!last_to) {
+ return "";
+ }
+ return last_to;
+}
+
+static bool set_last_from_to(const char *from, const char *to)
+{
+ char *orig_from = last_from;
+ char *orig_to = last_to;
+
+ last_from = SMB_STRDUP(from);
+ last_to = SMB_STRDUP(to);
+
+ SAFE_FREE(orig_from);
+ SAFE_FREE(orig_to);
+
+ if (!last_from || !last_to) {
+ SAFE_FREE(last_from);
+ SAFE_FREE(last_to);
+ return false;
+ }
+ return true;
+}
+
+static void map_to_os2_driver(fstring drivername)
+{
+ char *mapfile = lp_os2_driver_map();
+ char **lines = NULL;
+ int numlines = 0;
+ int i;
+
+ if (!strlen(drivername))
+ return;
+
+ if (!*mapfile)
+ return;
+
+ if (strequal(drivername,get_last_from())) {
+ DEBUG(3,("Mapped Windows driver %s to OS/2 driver %s\n",
+ drivername,get_last_to()));
+ fstrcpy(drivername,get_last_to());
+ return;
+ }
+
+ lines = file_lines_load(mapfile, &numlines,0);
+ if (numlines == 0 || lines == NULL) {
+ DEBUG(0,("No entries in OS/2 driver map %s\n",mapfile));
+ SAFE_FREE(lines);
+ 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));
+ set_last_from_to(drivername,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,
+ const char *servername,
+ const char* sharename,
+ bool get_loc_com)
+{
+ int snum = lp_servicenumber(sharename);
+
+ slprintf(info->servername, sizeof(info->servername)-1, "\\\\%s", servername);
+ slprintf(info->printername, sizeof(info->printername)-1, "\\\\%s\\%s",
+ servername, sharename);
+ fstrcpy(info->sharename, sharename);
+ fstrcpy(info->portname, SAMBA_PRINTER_PORT_NAME);
+
+ /* 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));
+
+ strlcpy(info->comment, "", sizeof(info->comment));
+ fstrcpy(info->printprocessor, "winprint");
+ fstrcpy(info->datatype, "RAW");
+
+#ifdef HAVE_CUPS
+ if (get_loc_com && (enum printing_types)lp_printing(snum) == PRINT_CUPS ) {
+ /* Pull the location and comment strings from cups if we don't
+ already have one */
+ if ( !strlen(info->location) || !strlen(info->comment) )
+ cups_pull_comment_location( info );
+ }
+#endif
+
+ info->attributes = PRINTER_ATTRIBUTE_SAMBA;
+
+ 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;
+ }
+
+ if (!nt_printing_getsec(info, sharename, &info->secdesc_buf)) {
+ 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,
+ const char *servername,
+ const char *sharename,
+ bool get_loc_com)
+{
+ int len = 0;
+ int snum = lp_servicenumber(sharename);
+ TDB_DATA kbuf, dbuf;
+ fstring printername;
+ char adevice[MAXDEVICENAME];
+ char *comment = NULL;
+
+ kbuf = make_printer_tdbkey(talloc_tos(), sharename);
+
+ dbuf = tdb_fetch(tdb_printers, kbuf);
+ if (!dbuf.dptr) {
+ return get_a_printer_2_default(info, servername,
+ sharename, get_loc_com);
+ }
+
+ 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,
+ &comment,
+ info->location,
+ info->sepfile,
+ info->printprocessor,
+ info->datatype,
+ info->parameters);
+
+ if (comment) {
+ strlcpy(info->comment, comment, sizeof(info->comment));
+ SAFE_FREE(comment);
+ }
+
+ /* Samba has to have shared raw drivers. */
+ info->attributes |= PRINTER_ATTRIBUTE_SAMBA;
+ info->attributes &= ~PRINTER_ATTRIBUTE_NOT_SAMBA;
+
+ /* Restore the stripped strings. */
+ slprintf(info->servername, sizeof(info->servername)-1, "\\\\%s", servername);
+
+ if ( lp_force_printername(snum) ) {
+ slprintf(printername, sizeof(printername)-1, "\\\\%s\\%s", servername, sharename );
+ } else {
+ slprintf(printername, sizeof(printername)-1, "\\\\%s\\%s", servername, info->printername);
+ }
+
+ fstrcpy(info->printername, printername);
+
+#ifdef HAVE_CUPS
+ if (get_loc_com && (enum printing_types)lp_printing(snum) == PRINT_CUPS ) {
+ /* Pull the location and comment strings from cups if we don't
+ already have one */
+ if ( !strlen(info->location) || !strlen(info->comment) )
+ cups_pull_comment_location( info );
+ }
+#endif
+
+ 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(snum) && !info->devmode) {
+ DEBUG(8,("get_a_printer_2: Constructing a default device mode for [%s]\n",
+ printername));
+ info->devmode = construct_nt_devicemode(printername);
+ }
+
+ slprintf( adevice, sizeof(adevice), "%s", info->printername );
+ if (info->devmode) {
+ fstrcpy(info->devmode->devicename, adevice);
+ }
+
+ if ( !(info->data = TALLOC_ZERO_P( info, NT_PRINTER_DATA )) ) {
+ DEBUG(0,("unpack_values: talloc() failed!\n"));
+ SAFE_FREE(dbuf.dptr);
+ return WERR_NOMEM;
+ }
+ len += unpack_values( info->data, 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 ! */
+
+ if (!nt_printing_getsec(info, sharename, &info->secdesc_buf)) {
+ SAFE_FREE(dbuf.dptr);
+ return WERR_NOMEM;
+ }
+
+ /* Fix for OS/2 drivers. */
+
+ if (get_remote_arch() == RA_OS2) {
+ map_to_os2_driver(info->drivername);
+ }
+
+ SAFE_FREE(dbuf.dptr);
+
+ 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;
+}
+
+/****************************************************************************
+ 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 bool set_driver_init_2( NT_PRINTER_INFO_LEVEL_2 *info_ptr )
+{
+ int len = 0;
+ char *key = NULL;
+ TDB_DATA dbuf;
+ NT_PRINTER_INFO_LEVEL_2 info;
+
+
+ ZERO_STRUCT(info);
+
+ /*
+ * Delete any printer data 'values' 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).
+ */
+
+ if ( info_ptr->data )
+ delete_all_printer_data( info_ptr, "" );
+
+ if (asprintf(&key, "%s%s", DRIVER_INIT_PREFIX,
+ info_ptr->drivername) < 0) {
+ return false;
+ }
+
+ dbuf = tdb_fetch_bystring(tdb_drivers, key);
+ 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);
+ SAFE_FREE(key);
+ return false;
+ }
+
+ SAFE_FREE(key);
+ /*
+ * 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.
+ */
+
+ if ( info.devmode ) {
+ 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)
+ *
+ * Later examination revealed that Windows NT/2k does reset the
+ * the printer's device mode, bit **only** when you change a
+ * property of the device mode such as the page orientation.
+ * --jerry
+ */
+
+
+ /* Bind the saved DEVMODE to the new the printer */
+
+ free_nt_devicemode(&info_ptr->devmode);
+ info_ptr->devmode = info.devmode;
+
+ DEBUG(10,("set_driver_init_2: Set printer [%s] init %s DEVMODE for driver [%s]\n",
+ info_ptr->printername, info_ptr->devmode?"VALID":"NULL", info_ptr->drivername));
+
+ /* Add the printer data 'values' to the new printer */
+
+ if ( !(info_ptr->data = TALLOC_ZERO_P( info_ptr, NT_PRINTER_DATA )) ) {
+ DEBUG(0,("set_driver_init_2: talloc() failed!\n"));
+ return False;
+ }
+
+ len += unpack_values( info_ptr->data, 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.
+****************************************************************************/
+
+bool set_driver_init(NT_PRINTER_INFO_LEVEL *printer, uint32 level)
+{
+ bool result = False;
+
+ switch (level) {
+ case 2:
+ result = set_driver_init_2(printer->info_2);
+ break;
+
+ default:
+ DEBUG(0,("set_driver_init: Programmer's error! Unknown driver_init level [%d]\n",
+ level));
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ Delete driver init data stored for a specified driver
+****************************************************************************/
+
+bool del_driver_init(char *drivername)
+{
+ char *key;
+ bool ret;
+
+ if (!drivername || !*drivername) {
+ DEBUG(3,("del_driver_init: No drivername specified!\n"));
+ return false;
+ }
+
+ if (asprintf(&key, "%s%s", DRIVER_INIT_PREFIX, drivername) < 0) {
+ return false;
+ }
+
+ DEBUG(6,("del_driver_init: Removing driver init data for [%s]\n",
+ drivername));
+
+ ret = (tdb_delete_bystring(tdb_drivers, key) == 0);
+ SAFE_FREE(key);
+ return ret;
+}
+
+/****************************************************************************
+ Pack up the DEVMODE and values 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)
+{
+ char *key = NULL;
+ uint8 *buf;
+ int buflen, len, ret;
+ int retlen;
+ TDB_DATA dbuf;
+
+ buf = NULL;
+ buflen = 0;
+
+ again:
+ len = 0;
+ len += pack_devicemode(info->devmode, buf+len, buflen-len);
+
+ retlen = pack_values( info->data, buf+len, buflen-len );
+ if (retlen == -1) {
+ ret = -1;
+ goto done;
+ }
+ len += retlen;
+
+ if (buflen < len) {
+ buf = (uint8 *)SMB_REALLOC(buf, len);
+ if (!buf) {
+ DEBUG(0, ("update_driver_init_2: failed to enlarge buffer!\n"));
+ ret = -1;
+ goto done;
+ }
+ buflen = len;
+ goto again;
+ }
+
+ SAFE_FREE(key);
+ if (asprintf(&key, "%s%s", DRIVER_INIT_PREFIX, info->drivername) < 0) {
+ ret = (uint32)-1;
+ goto done;
+ }
+
+ dbuf.dptr = buf;
+ dbuf.dsize = len;
+
+ ret = tdb_store_bystring(tdb_drivers, key, 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 & values for driver [%s]\n",
+ info->sharename, info->drivername));
+
+ return ret;
+}
+
+/****************************************************************************
+ Update (i.e. save) the driver init info (DEVMODE and values) for a printer
+****************************************************************************/
+
+static 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( TALLOC_CTX *ctx, NT_DEVICEMODE *nt_devmode, uint8 *data, uint32 data_len )
+{
+ bool result = False;
+ prs_struct ps;
+ DEVICEMODE devmode;
+
+ ZERO_STRUCT(devmode);
+
+ prs_init_empty(&ps, ctx, UNMARSHALL);
+ ps.data_p = (char *)data;
+ ps.buffer_size = 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, uint8 *data, uint32 data_len )
+{
+ 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.
+ */
+ DEBUG(8,("save_driver_init_2: Enter...\n"));
+
+ if ( !printer->info_2->devmode && data_len ) {
+ /*
+ * Set devmode on printer info, so entire printer initialization can be
+ * saved to tdb.
+ */
+
+ if ((ctx = talloc_init("save_driver_init_2")) == NULL)
+ return WERR_NOMEM;
+
+ if ((nt_devmode = SMB_MALLOC_P(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( ctx, nt_devmode, data, data_len )) {
+ 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));
+ }
+
+ done:
+ talloc_destroy(ctx);
+ free_nt_devicemode( &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, uint8 *data, uint32 data_len)
+{
+ WERROR status = WERR_OK;
+
+ switch (level) {
+ case 2:
+ status = save_driver_init_2( printer, data, data_len );
+ break;
+ default:
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return status;
+}
+
+/****************************************************************************
+ Get a NT_PRINTER_INFO_LEVEL struct. It returns malloced memory.
+
+ Previously the code had a memory allocation problem because it always
+ used the TALLOC_CTX from the Printer_entry*. This context lasts
+ as a long as the original handle is open. So if the client made a lot
+ of getprinter[data]() calls, the memory usage would climb. Now we use
+ a short lived TALLOC_CTX for printer_info_2 objects returned. We
+ still use the Printer_entry->ctx for maintaining the cache copy though
+ since that object must live as long as the handle by definition.
+ --jerry
+
+****************************************************************************/
+
+static WERROR get_a_printer_internal( Printer_entry *print_hnd, NT_PRINTER_INFO_LEVEL **pp_printer, uint32 level,
+ const char *sharename, bool get_loc_com)
+{
+ WERROR result;
+ fstring servername;
+
+ DEBUG(10,("get_a_printer: [%s] level %u\n", sharename, (unsigned int)level));
+
+ if ( !(*pp_printer = TALLOC_ZERO_P(NULL, NT_PRINTER_INFO_LEVEL)) ) {
+ DEBUG(0,("get_a_printer: talloc() fail.\n"));
+ return WERR_NOMEM;
+ }
+
+ switch (level) {
+ case 2:
+ if ( !((*pp_printer)->info_2 = TALLOC_ZERO_P(*pp_printer, NT_PRINTER_INFO_LEVEL_2)) ) {
+ DEBUG(0,("get_a_printer: talloc() fail.\n"));
+ TALLOC_FREE( *pp_printer );
+ return WERR_NOMEM;
+ }
+
+ if ( print_hnd )
+ fstrcpy( servername, print_hnd->servername );
+ else {
+ fstrcpy( servername, "%L" );
+ standard_sub_basic( "", "", servername,
+ sizeof(servername)-1 );
+ }
+
+ result = get_a_printer_2( (*pp_printer)->info_2,
+ servername, sharename, get_loc_com);
+
+ /* we have a new printer now. Save it with this handle */
+
+ if ( !W_ERROR_IS_OK(result) ) {
+ TALLOC_FREE( *pp_printer );
+ DEBUG(10,("get_a_printer: [%s] level %u returning %s\n",
+ sharename, (unsigned int)level, dos_errstr(result)));
+ return result;
+ }
+
+ dump_a_printer( *pp_printer, level);
+
+ break;
+
+ default:
+ TALLOC_FREE( *pp_printer );
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_OK;
+}
+
+WERROR get_a_printer( Printer_entry *print_hnd,
+ NT_PRINTER_INFO_LEVEL **pp_printer,
+ uint32 level,
+ const char *sharename)
+{
+ return get_a_printer_internal(print_hnd, pp_printer, level,
+ sharename, true);
+}
+
+WERROR get_a_printer_search( Printer_entry *print_hnd,
+ NT_PRINTER_INFO_LEVEL **pp_printer,
+ uint32 level,
+ const char *sharename)
+{
+ return get_a_printer_internal(print_hnd, pp_printer, level,
+ sharename, false);
+}
+
+/****************************************************************************
+ Deletes a NT_PRINTER_INFO_LEVEL struct.
+****************************************************************************/
+
+uint32 free_a_printer(NT_PRINTER_INFO_LEVEL **pp_printer, uint32 level)
+{
+ NT_PRINTER_INFO_LEVEL *printer = *pp_printer;
+
+ if ( !printer )
+ return 0;
+
+ switch (level) {
+ case 2:
+ if ( printer->info_2 )
+ free_nt_printer_info_level_2(&printer->info_2);
+ break;
+
+ default:
+ DEBUG(0,("free_a_printer: unknown level! [%d]\n", level ));
+ return 1;
+ }
+
+ TALLOC_FREE(*pp_printer);
+
+ return 0;
+}
+
+/****************************************************************************
+****************************************************************************/
+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 drivername, const char *architecture, uint32 version)
+{
+ WERROR result;
+
+ switch (level) {
+ case 3:
+ /* Sometime we just want any version of the driver */
+
+ if ( version == DRIVER_ANY_VERSION ) {
+ /* look for Win2k first and then for NT4 */
+ result = get_a_printer_driver_3(&driver->info_3, drivername,
+ architecture, 3);
+
+ if ( !W_ERROR_IS_OK(result) ) {
+ result = get_a_printer_driver_3( &driver->info_3,
+ drivername, architecture, 2 );
+ }
+ } else {
+ result = get_a_printer_driver_3(&driver->info_3, drivername,
+ 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 ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3 )
+{
+ int snum;
+ int n_services = lp_numservices();
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ bool in_use = False;
+
+ if ( !info_3 )
+ return False;
+
+ DEBUG(10,("printer_driver_in_use: Beginning search through ntprinters.tdb...\n"));
+
+ /* loop through the printers.tdb and check for the drivername */
+
+ for (snum=0; snum<n_services && !in_use; snum++) {
+ if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
+ continue;
+
+ if ( !W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_servicename(snum))) )
+ continue;
+
+ if ( strequal(info_3->name, printer->info_2->drivername) )
+ in_use = True;
+
+ free_a_printer( &printer, 2 );
+ }
+
+ DEBUG(10,("printer_driver_in_use: Completed search through ntprinters.tdb...\n"));
+
+ if ( in_use ) {
+ NT_PRINTER_DRIVER_INFO_LEVEL d;
+ WERROR werr;
+
+ DEBUG(5,("printer_driver_in_use: driver \"%s\" is currently in use\n", info_3->name));
+
+ /* we can still remove the driver if there is one of
+ "Windows NT x86" version 2 or 3 left */
+
+ if ( !strequal( "Windows NT x86", info_3->environment ) ) {
+ werr = get_a_printer_driver( &d, 3, info_3->name, "Windows NT x86", DRIVER_ANY_VERSION );
+ }
+ else {
+ switch ( info_3->cversion ) {
+ case 2:
+ werr = get_a_printer_driver( &d, 3, info_3->name, "Windows NT x86", 3 );
+ break;
+ case 3:
+ werr = get_a_printer_driver( &d, 3, info_3->name, "Windows NT x86", 2 );
+ break;
+ default:
+ DEBUG(0,("printer_driver_in_use: ERROR! unknown driver version (%d)\n",
+ info_3->cversion));
+ werr = WERR_UNKNOWN_PRINTER_DRIVER;
+ break;
+ }
+ }
+
+ /* now check the error code */
+
+ if ( W_ERROR_IS_OK(werr) ) {
+ /* it's ok to remove the driver, we have other architctures left */
+ in_use = False;
+ free_a_printer_driver( d, 3 );
+ }
+ }
+
+ /* report that the driver is not in use by default */
+
+ return in_use;
+}
+
+
+/**********************************************************************
+ Check to see if a ogiven file is in use by *info
+ *********************************************************************/
+
+static bool drv_file_in_use( char* file, NT_PRINTER_DRIVER_INFO_LEVEL_3 *info )
+{
+ int i = 0;
+
+ if ( !info )
+ return False;
+
+ /* mz: skip files that are in the list but already deleted */
+ if (!file || !file[0]) {
+ return false;
+ }
+
+ if ( strequal(file, info->driverpath) )
+ return True;
+
+ if ( strequal(file, info->datafile) )
+ return True;
+
+ if ( strequal(file, info->configfile) )
+ return True;
+
+ if ( strequal(file, info->helpfile) )
+ return True;
+
+ /* see of there are any dependent files to examine */
+
+ if ( !info->dependentfiles )
+ return False;
+
+ while ( *info->dependentfiles[i] ) {
+ if ( strequal(file, info->dependentfiles[i]) )
+ return True;
+ i++;
+ }
+
+ return False;
+
+}
+
+/**********************************************************************
+ Utility function to remove the dependent file pointed to by the
+ input parameter from the list
+ *********************************************************************/
+
+static void trim_dependent_file( fstring files[], int idx )
+{
+
+ /* bump everything down a slot */
+
+ while( *files[idx+1] ) {
+ fstrcpy( files[idx], files[idx+1] );
+ idx++;
+ }
+
+ *files[idx] = '\0';
+
+ return;
+}
+
+/**********************************************************************
+ Check if any of the files used by src are also used by drv
+ *********************************************************************/
+
+static bool trim_overlap_drv_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *src,
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *drv )
+{
+ bool in_use = False;
+ int i = 0;
+
+ if ( !src || !drv )
+ return False;
+
+ /* check each file. Remove it from the src structure if it overlaps */
+
+ if ( drv_file_in_use(src->driverpath, drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing driverfile [%s] from list\n", src->driverpath));
+ fstrcpy( src->driverpath, "" );
+ }
+
+ if ( drv_file_in_use(src->datafile, drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing datafile [%s] from list\n", src->datafile));
+ fstrcpy( src->datafile, "" );
+ }
+
+ if ( drv_file_in_use(src->configfile, drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing configfile [%s] from list\n", src->configfile));
+ fstrcpy( src->configfile, "" );
+ }
+
+ if ( drv_file_in_use(src->helpfile, drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing helpfile [%s] from list\n", src->helpfile));
+ fstrcpy( src->helpfile, "" );
+ }
+
+ /* are there any dependentfiles to examine? */
+
+ if ( !src->dependentfiles )
+ return in_use;
+
+ while ( *src->dependentfiles[i] ) {
+ if ( drv_file_in_use(src->dependentfiles[i], drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing [%s] from dependent file list\n", src->dependentfiles[i]));
+ trim_dependent_file( src->dependentfiles, i );
+ } else
+ i++;
+ }
+
+ return in_use;
+}
+
+/****************************************************************************
+ Determine whether or not a particular driver files are currently being
+ used by any other driver.
+
+ Return value is True if any files were in use by other drivers
+ and False otherwise.
+
+ Upon return, *info has been modified to only contain the driver files
+ which are not in use
+
+ Fix from mz:
+
+ This needs to check all drivers to ensure that all files in use
+ have been removed from *info, not just the ones in the first
+ match.
+****************************************************************************/
+
+bool printer_driver_files_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info )
+{
+ int i;
+ int ndrivers;
+ uint32 version;
+ fstring *list = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ bool in_use = false;
+
+ if ( !info )
+ return False;
+
+ version = info->cversion;
+
+ /* loop over all driver versions */
+
+ DEBUG(5,("printer_driver_files_in_use: Beginning search through ntdrivers.tdb...\n"));
+
+ /* get the list of drivers */
+
+ list = NULL;
+ ndrivers = get_ntdrivers(&list, info->environment, version);
+
+ DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n",
+ ndrivers, info->environment, version));
+
+ /* check each driver for overlap in files */
+
+ for (i=0; i<ndrivers; i++) {
+ DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+
+ ZERO_STRUCT(driver);
+
+ if ( !W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, list[i], info->environment, version)) ) {
+ SAFE_FREE(list);
+ return True;
+ }
+
+ /* check if d2 uses any files from d1 */
+ /* only if this is a different driver than the one being deleted */
+
+ if ( !strequal(info->name, driver.info_3->name) ) {
+ if ( trim_overlap_drv_files(info, driver.info_3) ) {
+ /* mz: Do not instantly return -
+ * we need to ensure this file isn't
+ * also in use by other drivers. */
+ in_use = true;
+ }
+ }
+
+ free_a_printer_driver(driver, 3);
+ }
+
+ SAFE_FREE(list);
+
+ DEBUG(5,("printer_driver_files_in_use: Completed search through ntdrivers.tdb...\n"));
+
+ driver.info_3 = info;
+
+ if ( DEBUGLEVEL >= 20 )
+ dump_a_printer_driver( driver, 3 );
+
+ return in_use;
+}
+
+/****************************************************************************
+ Actually delete the driver files. Make sure that
+ printer_driver_files_in_use() return False before calling
+ this.
+****************************************************************************/
+
+static bool delete_driver_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3, struct current_user *user )
+{
+ int i = 0;
+ char *s;
+ const char *file;
+ connection_struct *conn;
+ DATA_BLOB null_pw;
+ NTSTATUS nt_status;
+ fstring res_type;
+ SMB_STRUCT_STAT st;
+
+ if ( !info_3 )
+ return False;
+
+ DEBUG(6,("delete_driver_files: deleting driver [%s] - version [%d]\n", info_3->name, info_3->cversion));
+
+ /*
+ * 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.
+ */
+
+ null_pw = data_blob_null;
+ fstrcpy(res_type, "A:");
+ become_root();
+ conn = make_connection_with_chdir( "print$", null_pw, res_type, user->vuid, &nt_status );
+ unbecome_root();
+
+ if ( !conn ) {
+ DEBUG(0,("delete_driver_files: Unable to connect\n"));
+ return False;
+ }
+
+ if ( !CAN_WRITE(conn) ) {
+ DEBUG(3,("delete_driver_files: Cannot delete print driver when [print$] is read-only\n"));
+ return False;
+ }
+
+ /* Save who we are - we are temporarily becoming the connection user. */
+
+ if ( !become_user(conn, conn->vuid) ) {
+ DEBUG(0,("delete_driver_files: Can't become user!\n"));
+ return False;
+ }
+
+ /* now delete the files; must strip the '\print$' string from
+ fron of path */
+
+ if ( *info_3->driverpath ) {
+ if ( (s = strchr( &info_3->driverpath[1], '\\' )) != NULL ) {
+ file = s;
+ driver_unix_convert(conn,file,&st);
+ DEBUG(10,("deleting driverfile [%s]\n", s));
+ unlink_internals(conn, NULL, 0, file, False);
+ }
+ }
+
+ if ( *info_3->configfile ) {
+ if ( (s = strchr( &info_3->configfile[1], '\\' )) != NULL ) {
+ file = s;
+ driver_unix_convert(conn,file,&st);
+ DEBUG(10,("deleting configfile [%s]\n", s));
+ unlink_internals(conn, NULL, 0, file, False);
+ }
+ }
+
+ if ( *info_3->datafile ) {
+ if ( (s = strchr( &info_3->datafile[1], '\\' )) != NULL ) {
+ file = s;
+ driver_unix_convert(conn,file,&st);
+ DEBUG(10,("deleting datafile [%s]\n", s));
+ unlink_internals(conn, NULL, 0, file, False);
+ }
+ }
+
+ if ( *info_3->helpfile ) {
+ if ( (s = strchr( &info_3->helpfile[1], '\\' )) != NULL ) {
+ file = s;
+ driver_unix_convert(conn,file,&st);
+ DEBUG(10,("deleting helpfile [%s]\n", s));
+ unlink_internals(conn, NULL, 0, file, False);
+ }
+ }
+
+ /* check if we are done removing files */
+
+ if ( info_3->dependentfiles ) {
+ while ( info_3->dependentfiles[i][0] ) {
+ char *p;
+
+ /* bypass the "\print$" portion of the path */
+
+ if ( (p = strchr( info_3->dependentfiles[i]+1, '\\' )) != NULL ) {
+ file = p;
+ driver_unix_convert(conn,file,&st);
+ DEBUG(10,("deleting dependent file [%s]\n", file));
+ unlink_internals(conn, NULL, 0, file, False);
+ }
+
+ i++;
+ }
+ }
+
+ unbecome_user();
+
+ return true;
+}
+
+/****************************************************************************
+ 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 *info_3, struct current_user *user,
+ uint32 version, bool delete_files )
+{
+ char *key = NULL;
+ const char *arch;
+ TDB_DATA dbuf;
+ NT_PRINTER_DRIVER_INFO_LEVEL ctr;
+
+ /* delete the tdb data first */
+
+ arch = get_short_archi(info_3->environment);
+ if (!arch) {
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+ if (asprintf(&key, "%s%s/%d/%s", DRIVERS_PREFIX,
+ arch, version, info_3->name) < 0) {
+ return WERR_NOMEM;
+ }
+
+ DEBUG(5,("delete_printer_driver: key = [%s] delete_files = %s\n",
+ key, delete_files ? "TRUE" : "FALSE" ));
+
+ ctr.info_3 = info_3;
+ dump_a_printer_driver( ctr, 3 );
+
+ /* check if the driver actually exists for this environment */
+
+ dbuf = tdb_fetch_bystring( tdb_drivers, key );
+ if ( !dbuf.dptr ) {
+ DEBUG(8,("delete_printer_driver: Driver unknown [%s]\n", key));
+ SAFE_FREE(key);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ SAFE_FREE( dbuf.dptr );
+
+ /* ok... the driver exists so the delete should return success */
+
+ if (tdb_delete_bystring(tdb_drivers, key) == -1) {
+ DEBUG (0,("delete_printer_driver: fail to delete %s!\n", key));
+ SAFE_FREE(key);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /*
+ * now delete any associated files if delete_files == True
+ * even if this part failes, we return succes because the
+ * driver doesn not exist any more
+ */
+
+ if ( delete_files )
+ delete_driver_files( info_3, user );
+
+ DEBUG(5,("delete_printer_driver: driver delete successful [%s]\n", key));
+ SAFE_FREE(key);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Store a security desc for a printer.
+****************************************************************************/
+
+WERROR nt_printing_setsec(const char *sharename, SEC_DESC_BUF *secdesc_ctr)
+{
+ SEC_DESC_BUF *new_secdesc_ctr = NULL;
+ SEC_DESC_BUF *old_secdesc_ctr = NULL;
+ prs_struct ps;
+ bool prs_init_done = false;
+ TALLOC_CTX *mem_ctx = NULL;
+ TDB_DATA kbuf;
+ WERROR status;
+
+ mem_ctx = talloc_init("nt_printing_setsec");
+ 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->sd->owner_sid || !secdesc_ctr->sd->group_sid) {
+ DOM_SID *owner_sid, *group_sid;
+ SEC_ACL *dacl, *sacl;
+ SEC_DESC *psd = NULL;
+ size_t size;
+
+ if (!nt_printing_getsec(mem_ctx, sharename, &old_secdesc_ctr)) {
+ status = WERR_NOMEM;
+ goto out;
+ }
+
+ /* Pick out correct owner and group sids */
+
+ owner_sid = secdesc_ctr->sd->owner_sid ?
+ secdesc_ctr->sd->owner_sid :
+ old_secdesc_ctr->sd->owner_sid;
+
+ group_sid = secdesc_ctr->sd->group_sid ?
+ secdesc_ctr->sd->group_sid :
+ old_secdesc_ctr->sd->group_sid;
+
+ dacl = secdesc_ctr->sd->dacl ?
+ secdesc_ctr->sd->dacl :
+ old_secdesc_ctr->sd->dacl;
+
+ sacl = secdesc_ctr->sd->sacl ?
+ secdesc_ctr->sd->sacl :
+ old_secdesc_ctr->sd->sacl;
+
+ /* Make a deep copy of the security descriptor */
+
+ psd = make_sec_desc(mem_ctx, secdesc_ctr->sd->revision, secdesc_ctr->sd->type,
+ owner_sid, group_sid,
+ sacl,
+ dacl,
+ &size);
+
+ if (!psd) {
+ status = WERR_NOMEM;
+ goto out;
+ }
+
+ 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 */
+
+ if (!prs_init(&ps,
+ (uint32)ndr_size_security_descriptor(new_secdesc_ctr->sd, 0)
+ + sizeof(SEC_DESC_BUF), mem_ctx, MARSHALL) ) {
+ status = WERR_NOMEM;
+ goto out;
+ }
+
+
+ prs_init_done = true;
+
+ if (!sec_io_desc_buf("nt_printing_setsec", &new_secdesc_ctr,
+ &ps, 1)) {
+ status = WERR_BADFUNC;
+ goto out;
+ }
+
+ kbuf = make_printers_secdesc_tdbkey(mem_ctx, sharename );
+
+ if (tdb_prs_store(tdb_printers, kbuf, &ps)==0) {
+ status = WERR_OK;
+ } else {
+ DEBUG(1,("Failed to store secdesc for %s\n", sharename));
+ status = WERR_BADFUNC;
+ }
+
+ /* Free malloc'ed memory */
+
+ out:
+
+ if (prs_init_done) {
+ 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)
+{
+ SEC_ACE ace[5]; /* max number of ace entries */
+ int i = 0;
+ SEC_ACCESS sa;
+ SEC_ACL *psa = NULL;
+ SEC_DESC_BUF *sdb = NULL;
+ SEC_DESC *psd = NULL;
+ DOM_SID adm_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[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+ /* Add the domain admins group if we are a DC */
+
+ if ( IS_DC ) {
+ DOM_SID domadmins_sid;
+
+ sid_copy(&domadmins_sid, get_global_sam_sid());
+ sid_append_rid(&domadmins_sid, DOMAIN_GROUP_RID_ADMINS);
+
+ init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL);
+ init_sec_ace(&ace[i++], &domadmins_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, sa,
+ SEC_ACE_FLAG_OBJECT_INHERIT | SEC_ACE_FLAG_INHERIT_ONLY);
+ init_sec_ace(&ace[i++], &domadmins_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+ }
+ else if (secrets_fetch_domain_sid(lp_workgroup(), &adm_sid)) {
+ sid_append_rid(&adm_sid, DOMAIN_USER_RID_ADMIN);
+
+ init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL);
+ init_sec_ace(&ace[i++], &adm_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, sa,
+ SEC_ACE_FLAG_OBJECT_INHERIT | SEC_ACE_FLAG_INHERIT_ONLY);
+ init_sec_ace(&ace[i++], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+ }
+
+ /* add BUILTIN\Administrators as FULL CONTROL */
+
+ init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL);
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, sa,
+ SEC_ACE_FLAG_OBJECT_INHERIT | SEC_ACE_FLAG_INHERIT_ONLY);
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+ /* Make the security descriptor owned by the BUILTIN\Administrators */
+
+ /* 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, i, ace)) != NULL) {
+ psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE,
+ &global_sid_Builtin_Administrators,
+ &global_sid_Builtin_Administrators,
+ 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, const char *sharename, SEC_DESC_BUF **secdesc_ctr)
+{
+ prs_struct ps;
+ TDB_DATA kbuf;
+ char *temp;
+
+ if (strlen(sharename) > 2 && (temp = strchr(sharename + 2, '\\'))) {
+ sharename = temp + 1;
+ }
+
+ ZERO_STRUCT(ps);
+
+ /* Fetch security descriptor from tdb */
+
+ kbuf = make_printers_secdesc_tdbkey(ctx, sharename );
+
+ if (tdb_prs_fetch(tdb_printers, kbuf, &ps, ctx)!=0 ||
+ !sec_io_desc_buf("nt_printing_getsec", secdesc_ctr, &ps, 1)) {
+
+ prs_mem_free(&ps);
+
+ DEBUG(4,("using default secdesc for %s\n", sharename));
+
+ if (!(*secdesc_ctr = construct_default_printer_sdb(ctx))) {
+ return False;
+ }
+
+ /* Save default security descriptor for later */
+
+ if (!prs_init(&ps, (uint32)ndr_size_security_descriptor((*secdesc_ctr)->sd, 0) +
+ sizeof(SEC_DESC_BUF), ctx, MARSHALL))
+ return False;
+
+ if (sec_io_desc_buf("nt_printing_getsec", secdesc_ctr, &ps, 1)) {
+ tdb_prs_store(tdb_printers, kbuf, &ps);
+ }
+
+ prs_mem_free(&ps);
+
+ return True;
+ }
+
+ prs_mem_free(&ps);
+
+ /* 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)->sd->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)->sd->revision, (*secdesc_ctr)->sd->type,
+ &owner_sid,
+ (*secdesc_ctr)->sd->group_sid,
+ (*secdesc_ctr)->sd->sacl,
+ (*secdesc_ctr)->sd->dacl,
+ &size);
+
+ if (!psd) {
+ return False;
+ }
+
+ new_secdesc_ctr = make_sec_desc_buf(ctx, size, psd);
+ if (!new_secdesc_ctr) {
+ return False;
+ }
+
+ /* Swap with other one */
+
+ *secdesc_ctr = new_secdesc_ctr;
+
+ /* Set it */
+
+ nt_printing_setsec(sharename, *secdesc_ctr);
+ }
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ SEC_ACL *the_acl = (*secdesc_ctr)->sd->dacl;
+ int i;
+
+ DEBUG(10, ("secdesc_ctr for %s has %d aces:\n",
+ sharename, the_acl->num_aces));
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ DEBUG(10, ("%s %d %d 0x%08x\n",
+ sid_string_dbg(&the_acl->aces[i].trustee),
+ the_acl->aces[i].type, the_acl->aces[i].flags,
+ the_acl->aces[i].access_mask));
+ }
+ }
+
+ 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->aces[i].access_mask,
+ &printer_generic_mapping);
+ }
+}
+
+void map_job_permissions(SEC_DESC *sd)
+{
+ int i;
+
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ se_map_generic(&sd->dacl->aces[i].access_mask,
+ &job_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
+
+ Try access control in the following order (for performance reasons):
+ 1) root ans SE_PRINT_OPERATOR can do anything (easy check)
+ 2) check security descriptor (bit comparisons in memory)
+ 3) "printer admins" (may result in numerous calls to winbind)
+
+ ****************************************************************************/
+bool print_access_check(struct auth_serversupplied_info *server_info, int snum,
+ int access_type)
+{
+ SEC_DESC_BUF *secdesc = NULL;
+ uint32 access_granted;
+ NTSTATUS status;
+ bool result;
+ const char *pname;
+ TALLOC_CTX *mem_ctx = NULL;
+ SE_PRIV se_printop = SE_PRINT_OPERATOR;
+
+ /* If user is NULL then use the current_user structure */
+
+ /* Always allow root or SE_PRINT_OPERATROR to do anything */
+
+ if (server_info->utok.uid == 0
+ || user_has_privileges(server_info->ptok, &se_printop ) ) {
+ return True;
+ }
+
+ /* Get printer name */
+
+ pname = PRINTERNAME(snum);
+
+ if (!pname || !*pname) {
+ errno = EACCES;
+ return False;
+ }
+
+ /* Get printer security descriptor */
+
+ if(!(mem_ctx = talloc_init("print_access_check"))) {
+ errno = ENOMEM;
+ return False;
+ }
+
+ if (!nt_printing_getsec(mem_ctx, pname, &secdesc)) {
+ talloc_destroy(mem_ctx);
+ errno = ENOMEM;
+ return False;
+ }
+
+ 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->sd, False);
+
+ if (!secdesc) {
+ talloc_destroy(mem_ctx);
+ errno = ENOMEM;
+ return False;
+ }
+
+ map_job_permissions(secdesc->sd);
+ } else {
+ map_printer_permissions(secdesc->sd);
+ }
+
+ /* Check access */
+ result = se_access_check(secdesc->sd, server_info->ptok, access_type,
+ &access_granted, &status);
+
+ DEBUG(4, ("access check was %s\n", result ? "SUCCESS" : "FAILURE"));
+
+ /* see if we need to try the printer admin list */
+
+ if ((access_granted == 0) &&
+ (token_contains_name_in_list(uidtoname(server_info->utok.uid),
+ NULL, NULL, server_info->ptok,
+ lp_printer_admin(snum)))) {
+ talloc_destroy(mem_ctx);
+ return True;
+ }
+
+ talloc_destroy(mem_ctx);
+
+ if (!result) {
+ errno = EACCES;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ Check the time parameters allow a print operation.
+*****************************************************************************/
+
+bool print_time_access_check(const char *servicename)
+{
+ 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(NULL, &printer, 2, servicename)))
+ 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;
+}
+
+/****************************************************************************
+ Fill in the servername sent in the _spoolss_open_printer_ex() call
+****************************************************************************/
+
+char* get_server_name( Printer_entry *printer )
+{
+ return printer->servername;
+}
+
+
diff --git a/source3/printing/pcap.c b/source3/printing/pcap.c
new file mode 100644
index 0000000000..30cb254a29
--- /dev/null
+++ b/source3/printing/pcap.c
@@ -0,0 +1,263 @@
+/*
+ Unix SMB/CIFS implementation.
+ printcap parsing
+ 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
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * This module contains code to parse and cache printcap data, possibly
+ * in concert with the CUPS/SYSV/AIX-specific code found elsewhere.
+ *
+ * The way this module looks at the printcap file is very simplistic.
+ * Only the local printcap file is inspected (no searching of NIS
+ * databases etc).
+ *
+ * There are assumed to be one or more printer names per record, held
+ * as a set of sub-fields separated by vertical bar symbols ('|') in the
+ * first field of the record. The field separator is assumed to be a colon
+ * ':' and the record separator a newline.
+ *
+ * Lines ending with a backspace '\' are assumed to flag that the following
+ * line is a continuation line so that a set of lines can be read as one
+ * printcap entry.
+ *
+ * A line stating with a hash '#' is assumed to be a comment and is ignored
+ * Comments are discarded before the record is strung together from the
+ * set of continuation lines.
+ *
+ * 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.
+ *
+ * Modified to call iPrint support if printcap name is set to "iprint"
+ * in smb.conf.
+ */
+
+#include "includes.h"
+
+
+typedef struct pcap_cache {
+ char *name;
+ char *comment;
+ struct pcap_cache *next;
+} pcap_cache_t;
+
+static pcap_cache_t *pcap_cache = NULL;
+
+bool pcap_cache_add(const char *name, const char *comment)
+{
+ pcap_cache_t *p;
+
+ if (name == NULL || ((p = SMB_MALLOC_P(pcap_cache_t)) == NULL))
+ return False;
+
+ p->name = SMB_STRDUP(name);
+ p->comment = (comment && *comment) ? SMB_STRDUP(comment) : NULL;
+
+ p->next = pcap_cache;
+ pcap_cache = p;
+
+ return True;
+}
+
+static void pcap_cache_destroy(pcap_cache_t *cache)
+{
+ pcap_cache_t *p, *next;
+
+ for (p = cache; p != NULL; p = next) {
+ next = p->next;
+
+ SAFE_FREE(p->name);
+ SAFE_FREE(p->comment);
+ SAFE_FREE(p);
+ }
+}
+
+bool pcap_cache_loaded(void)
+{
+ return (pcap_cache != NULL);
+}
+
+void pcap_cache_reload(void)
+{
+ const char *pcap_name = lp_printcapname();
+ bool pcap_reloaded = False;
+ pcap_cache_t *tmp_cache = NULL;
+ XFILE *pcap_file;
+ char *pcap_line;
+
+ DEBUG(3, ("reloading printcap cache\n"));
+
+ /* only go looking if no printcap name supplied */
+ if (pcap_name == NULL || *pcap_name == 0) {
+ DEBUG(0, ("No printcap file name configured!\n"));
+ return;
+ }
+
+ tmp_cache = pcap_cache;
+ pcap_cache = NULL;
+
+#ifdef HAVE_CUPS
+ if (strequal(pcap_name, "cups")) {
+ pcap_reloaded = cups_cache_reload();
+ goto done;
+ }
+#endif
+
+#ifdef HAVE_IPRINT
+ if (strequal(pcap_name, "iprint")) {
+ pcap_reloaded = iprint_cache_reload();
+ goto done;
+ }
+#endif
+
+#if defined(SYSV) || defined(HPUX)
+ if (strequal(pcap_name, "lpstat")) {
+ pcap_reloaded = sysv_cache_reload();
+ goto done;
+ }
+#endif
+
+#ifdef AIX
+ if (strstr_m(pcap_name, "/qconfig") != NULL) {
+ pcap_reloaded = aix_cache_reload();
+ goto done;
+ }
+#endif
+
+ /* handle standard printcap - moved from pcap_printer_fn() */
+
+ if ((pcap_file = x_fopen(pcap_name, O_RDONLY, 0)) == NULL) {
+ DEBUG(0, ("Unable to open printcap file %s for read!\n", pcap_name));
+ goto done;
+ }
+
+ for (; (pcap_line = fgets_slash(NULL, 1024, pcap_file)) != NULL; safe_free(pcap_line)) {
+ char name[MAXPRINTERLEN+1];
+ char comment[62];
+ char *p, *q;
+
+ if (*pcap_line == '#' || *pcap_line == 0)
+ continue;
+
+ /* now we have a real printer line - cut at the first : */
+ if ((p = strchr_m(pcap_line, ':')) != NULL)
+ *p = 0;
+
+ /*
+ * now find the most likely printer name and comment
+ * this is pure guesswork, but it's better than nothing
+ */
+ for (*name = *comment = 0, p = pcap_line; p != NULL; p = q) {
+ bool has_punctuation;
+
+ if ((q = strchr_m(p, '|')) != NULL)
+ *q++ = 0;
+
+ has_punctuation = (strchr_m(p, ' ') ||
+ strchr_m(p, '\t') ||
+ strchr_m(p, '(') ||
+ strchr_m(p, ')'));
+
+ if (strlen(p) > strlen(comment) && has_punctuation) {
+ strlcpy(comment, p, sizeof(comment));
+ continue;
+ }
+
+ if (strlen(p) <= MAXPRINTERLEN &&
+ strlen(p) > strlen(name) && !has_punctuation) {
+ if (!*comment) {
+ strlcpy(comment, name, sizeof(comment));
+ }
+ strlcpy(name, p, sizeof(name));
+ continue;
+ }
+
+ if (!strchr_m(comment, ' ') &&
+ strlen(p) > strlen(comment)) {
+ strlcpy(comment, p, sizeof(comment));
+ continue;
+ }
+ }
+
+ comment[60] = 0;
+ name[MAXPRINTERLEN] = 0;
+
+ if (*name && !pcap_cache_add(name, comment)) {
+ x_fclose(pcap_file);
+ goto done;
+ }
+ }
+
+ x_fclose(pcap_file);
+ pcap_reloaded = True;
+
+done:
+ DEBUG(3, ("reload status: %s\n", (pcap_reloaded) ? "ok" : "error"));
+
+ if (pcap_reloaded)
+ pcap_cache_destroy(tmp_cache);
+ else {
+ pcap_cache_destroy(pcap_cache);
+ pcap_cache = tmp_cache;
+ }
+
+ return;
+}
+
+
+bool pcap_printername_ok(const char *printername)
+{
+ pcap_cache_t *p;
+
+ for (p = pcap_cache; p != NULL; p = p->next)
+ if (strequal(p->name, printername))
+ return True;
+
+ 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). Note the fn() takes
+strings in DOS codepage. This means the xxx_printer_fn() calls must be fixed
+to return DOS codepage. FIXME !! JRA.
+
+XXX: I'm not sure if this comment still applies.. Anyone? -Rob
+***************************************************************************/
+void pcap_printer_fn(void (*fn)(char *, char *))
+{
+ pcap_cache_t *p;
+
+ for (p = pcap_cache; p != NULL; p = p->next)
+ fn(p->name, p->comment);
+
+ return;
+}
diff --git a/source3/printing/print_aix.c b/source3/printing/print_aix.c
new file mode 100644
index 0000000000..57590cc39e
--- /dev/null
+++ b/source3/printing/print_aix.c
@@ -0,0 +1,123 @@
+/*
+ AIX-specific printcap loading
+ Copyright (C) Jean-Pierre.Boulard@univ-rennes1.fr 1996
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * This module implements AIX-specific printcap loading. Most of the code
+ * here was originally provided by Jean-Pierre.Boulard@univ-rennes1.fr in
+ * the Samba 1.9.14 release, and was formerly contained in pcap.c. It has
+ * been moved here and condensed as part of a larger effort to clean up and
+ * simplify the printcap code. -- Rob Foehl, 2004/12/06
+ */
+
+#include "includes.h"
+
+#ifdef AIX
+bool aix_cache_reload(void)
+{
+ int iEtat;
+ XFILE *pfile;
+ char *line = NULL, *p;
+ char *name = NULL;
+ TALLOC_CTX *ctx = talloc_init("aix_cache_reload");
+
+ if (!ctx) {
+ return false;
+ }
+
+ DEBUG(5, ("reloading aix printcap cache\n"));
+
+ if ((pfile = x_fopen(lp_printcapname(), O_RDONLY, 0)) == NULL) {
+ DEBUG(0,( "Unable to open qconfig file %s for read!\n", lp_printcapname()));
+ TALLOC_FREE(ctx);
+ return false;
+ }
+
+ iEtat = 0;
+ /* scan qconfig file for searching <printername>: */
+ for (;(line = fgets_slash(NULL, 1024, pfile)); safe_free(line)) {
+ if (*line == '*' || *line == 0)
+ continue;
+
+ switch (iEtat) {
+ case 0: /* locate an entry */
+ if (*line == '\t' || *line == ' ')
+ continue;
+
+ if ((p = strchr_m(line, ':'))) {
+ char *saveptr;
+ *p = '\0';
+ p = strtok_r(line, ":", &saveptr);
+ if (strcmp(p, "bsh") != 0) {
+ name = talloc_strdup(ctx, p);
+ if (!name) {
+ safe_free(line);
+ x_fclose(pfile);
+ TALLOC_FREE(ctx);
+ return false;
+ }
+ iEtat = 1;
+ continue;
+ }
+ }
+ break;
+
+ case 1: /* scanning device stanza */
+ if (*line == '*' || *line == 0)
+ continue;
+
+ if (*line != '\t' && *line != ' ') {
+ /* name is found without stanza device */
+ /* probably a good printer ??? */
+ iEtat = 0;
+ if (!pcap_cache_add(name, NULL)) {
+ safe_free(line);
+ x_fclose(pfile);
+ TALLOC_FREE(ctx);
+ return false;
+ }
+ continue;
+ }
+
+ if (strstr_m(line, "backend")) {
+ /* it's a device, not a virtual printer */
+ iEtat = 0;
+ } else if (strstr_m(line, "device")) {
+ /* it's a good virtual printer */
+ iEtat = 0;
+ if (!pcap_cache_add(name, NULL)) {
+ safe_free(line);
+ x_fclose(pfile);
+ TALLOC_FREE(ctx);
+ return false;
+ }
+ continue;
+ }
+ break;
+ }
+ }
+
+ x_fclose(pfile);
+ TALLOC_FREE(ctx);
+ return true;
+}
+
+#else
+/* this keeps fussy compilers happy */
+ void print_aix_dummy(void);
+ void print_aix_dummy(void) {}
+#endif /* AIX */
diff --git a/source3/printing/print_cups.c b/source3/printing/print_cups.c
new file mode 100644
index 0000000000..593c5c7a1f
--- /dev/null
+++ b/source3/printing/print_cups.c
@@ -0,0 +1,1347 @@
+/*
+ * Support code for the Common UNIX Printing System ("CUPS")
+ *
+ * Copyright 1999-2003 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "printing.h"
+
+#ifdef HAVE_CUPS
+#include <cups/cups.h>
+#include <cups/language.h>
+
+extern userdom_struct current_user_info;
+
+/*
+ * 'cups_passwd_cb()' - The CUPS password callback...
+ */
+
+static const char * /* O - Password or NULL */
+cups_passwd_cb(const char *prompt) /* I - Prompt */
+{
+ /*
+ * Always return NULL to indicate that no password is available...
+ */
+
+ return (NULL);
+}
+
+static http_t *cups_connect(void)
+{
+ http_t *http;
+ char *server, *p;
+ int port;
+
+ if (lp_cups_server() != NULL && strlen(lp_cups_server()) > 0) {
+ server = smb_xstrdup(lp_cups_server());
+ } else {
+ server = smb_xstrdup(cupsServer());
+ }
+
+ p = strchr(server, ':');
+ if (p) {
+ port = atoi(p+1);
+ *p = '\0';
+ } else {
+ port = ippPort();
+ }
+
+ DEBUG(10, ("connecting to cups server %s:%d\n",
+ server, port));
+
+ if ((http = httpConnect(server, port)) == NULL) {
+ DEBUG(0,("Unable to connect to CUPS server %s:%d - %s\n",
+ server, port, strerror(errno)));
+ SAFE_FREE(server);
+ return NULL;
+ }
+
+ SAFE_FREE(server);
+ return http;
+}
+
+bool cups_cache_reload(void)
+{
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language = NULL; /* Default language */
+ char *name, /* printer-name attribute */
+ *info; /* printer-info attribute */
+ static const char *requested[] =/* Requested attributes */
+ {
+ "printer-name",
+ "printer-info"
+ };
+ bool ret = False;
+
+ DEBUG(5, ("reloading cups printcap cache\n"));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = cups_connect()) == NULL) {
+ goto out;
+ }
+
+ /*
+ * 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())));
+ goto out;
+ }
+
+ 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;
+ 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-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 (!pcap_cache_add(name, info)) {
+ goto out;
+ }
+ }
+
+ ippDelete(response);
+ response = NULL;
+
+ /*
+ * Build a CUPS_GET_CLASSES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_CLASSES;
+ request->request.op.request_id = 1;
+
+ 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())));
+ goto out;
+ }
+
+ 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;
+ 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-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 (!pcap_cache_add(name, info)) {
+ goto out;
+ }
+ }
+
+ ret = True;
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return ret;
+}
+
+
+/*
+ * 'cups_job_delete()' - Delete a job.
+ */
+
+static int cups_job_delete(const char *sharename, const char *lprm_command, struct printjob *pjob)
+{
+ int ret = 1; /* Return value */
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ cups_lang_t *language = NULL; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_job_delete(%s, %p (%d))\n", sharename, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = cups_connect()) == NULL) {
+ goto out;
+ }
+
+ /*
+ * 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...
+ */
+
+ 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;
+ }
+ } else {
+ DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+ }
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return ret;
+}
+
+
+/*
+ * 'cups_job_pause()' - Pause a job.
+ */
+
+static int cups_job_pause(int snum, struct printjob *pjob)
+{
+ int ret = 1; /* Return value */
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ cups_lang_t *language = NULL; /* 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 = cups_connect()) == NULL) {
+ goto out;
+ }
+
+ /*
+ * 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...
+ */
+
+ 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;
+ }
+ } else {
+ DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+ }
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return ret;
+}
+
+
+/*
+ * 'cups_job_resume()' - Resume a paused job.
+ */
+
+static int cups_job_resume(int snum, struct printjob *pjob)
+{
+ int ret = 1; /* Return value */
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ cups_lang_t *language = NULL; /* 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 = cups_connect()) == NULL) {
+ goto out;
+ }
+
+ /*
+ * 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...
+ */
+
+ 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;
+ }
+ } else {
+ DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+ }
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return ret;
+}
+
+
+/*
+ * 'cups_job_submit()' - Submit a job for printing.
+ */
+
+static int cups_job_submit(int snum, struct printjob *pjob)
+{
+ int ret = 1; /* Return value */
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ cups_lang_t *language = NULL; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+ const char *clientname = NULL; /* hostname of client for job-originating-host attribute */
+ char *new_jobname = NULL;
+ int num_options = 0;
+ cups_option_t *options = NULL;
+ char addr[INET6_ADDRSTRLEN];
+
+ 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 = cups_connect()) == NULL) {
+ goto out;
+ }
+
+ /*
+ * 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);
+
+ clientname = client_name(get_client_fd());
+ if (strcmp(clientname, "UNKNOWN") == 0) {
+ clientname = client_addr(get_client_fd(),addr,sizeof(addr));
+ }
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "job-originating-host-name", NULL,
+ clientname);
+
+ if (asprintf(&new_jobname,"%s%.8u %s", PRINT_SPOOL_PREFIX,
+ (unsigned int)pjob->smbjob, pjob->jobname) < 0) {
+ goto out;
+ }
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
+ new_jobname);
+
+ /*
+ * add any options defined in smb.conf
+ */
+
+ num_options = 0;
+ options = NULL;
+ num_options = cupsParseOptions(lp_cups_options(snum), num_options, &options);
+
+ if ( num_options )
+ cupsEncodeOptions(request, num_options, options);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ slprintf(uri, sizeof(uri) - 1, "/printers/%s", PRINTERNAME(snum));
+
+ 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;
+ }
+ } else {
+ DEBUG(0,("Unable to print file to `%s' - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+ }
+
+ if ( ret == 0 )
+ unlink(pjob->filename);
+ /* else print_job_end will do it for us */
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ SAFE_FREE(new_jobname);
+
+ return ret;
+}
+
+/*
+ * 'cups_queue_get()' - Get all the jobs in the print queue.
+ */
+
+static int cups_queue_get(const char *sharename,
+ enum printing_types printing_type,
+ char *lpq_command,
+ print_queue_struct **q,
+ print_status_struct *status)
+{
+ fstring printername;
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ ipp_attribute_t *attr = NULL; /* Current attribute */
+ cups_lang_t *language = NULL; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+ int qcount = 0, /* Number of active queue entries */
+ qalloc = 0; /* Number of queue entries allocated */
+ print_queue_struct *queue = NULL, /* 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"
+ };
+
+ *q = NULL;
+
+ /* HACK ALERT!!! The problem with support the 'printer name'
+ option is that we key the tdb off the sharename. So we will
+ overload the lpq_command string to pass in the printername
+ (which is basically what we do for non-cups printers ... using
+ the lpq_command to get the queue listing). */
+
+ fstrcpy( printername, lpq_command );
+
+ DEBUG(5,("cups_queue_get(%s, %p, %p)\n", printername, q, status));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = cups_connect()) == NULL) {
+ goto out;
+ }
+
+ /*
+ * Generate the printer URI...
+ */
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", printername);
+
+ /*
+ * 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())));
+ goto out;
+ }
+
+ 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)));
+ goto out;
+ }
+
+ /*
+ * 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;
+
+ queue = SMB_REALLOC_ARRAY(queue, print_queue_struct, qalloc);
+
+ if (queue == NULL) {
+ DEBUG(0,("cups_queue_get: Not enough memory!"));
+ qcount = 0;
+ goto out;
+ }
+ }
+
+ 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);
+ response = NULL;
+
+ /*
+ * 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;
+
+ 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,
+ ippErrorString(cupsLastError())));
+ *q = queue;
+ goto out;
+ }
+
+ if (response->request.status.status_code >= IPP_OK_CONFLICT) {
+ DEBUG(0,("Unable to get printer status for %s - %s\n", printername,
+ ippErrorString(response->request.status.status_code)));
+ *q = queue;
+ goto out;
+ }
+
+ /*
+ * 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);
+
+ /*
+ * Return the job queue...
+ */
+
+ *q = queue;
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return qcount;
+}
+
+
+/*
+ * 'cups_queue_pause()' - Pause a print queue.
+ */
+
+static int cups_queue_pause(int snum)
+{
+ int ret = 1; /* Return value */
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ cups_lang_t *language = NULL; /* 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 = cups_connect()) == NULL) {
+ goto out;
+ }
+
+ /*
+ * 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...
+ */
+
+ 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;
+ }
+ } else {
+ DEBUG(0,("Unable to pause printer %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+ }
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return ret;
+}
+
+
+/*
+ * 'cups_queue_resume()' - Restart a print queue.
+ */
+
+static int cups_queue_resume(int snum)
+{
+ int ret = 1; /* Return value */
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ cups_lang_t *language = NULL; /* 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 = cups_connect()) == NULL) {
+ goto out;
+ }
+
+ /*
+ * 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...
+ */
+
+ 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;
+ }
+ } else {
+ DEBUG(0,("Unable to resume printer %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+ }
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return ret;
+}
+
+/*******************************************************************
+ * CUPS printing interface definitions...
+ ******************************************************************/
+
+struct printif cups_printif =
+{
+ PRINT_CUPS,
+ cups_queue_get,
+ cups_queue_pause,
+ cups_queue_resume,
+ cups_job_delete,
+ cups_job_pause,
+ cups_job_resume,
+ cups_job_submit,
+};
+
+bool cups_pull_comment_location(NT_PRINTER_INFO_LEVEL_2 *printer)
+{
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language = NULL; /* Default language */
+ char *name, /* printer-name attribute */
+ *info, /* printer-info attribute */
+ *location; /* printer-location attribute */
+ char uri[HTTP_MAX_URI];
+ static const char *requested[] =/* Requested attributes */
+ {
+ "printer-name",
+ "printer-info",
+ "printer-location"
+ };
+ bool ret = False;
+
+ DEBUG(5, ("pulling %s location\n", printer->sharename));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = cups_connect()) == NULL) {
+ goto out;
+ }
+
+ 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);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://%s/printers/%s",
+ lp_cups_server(), printer->sharename);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ 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 attributes - %s\n",
+ ippErrorString(cupsLastError())));
+ goto out;
+ }
+
+ 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;
+ info = NULL;
+ location = NULL;
+
+ while ( attr && (attr->group_tag == IPP_TAG_PRINTER) ) {
+ /* Grab the comment if we don't have one */
+ if ( (strcmp(attr->name, "printer-info") == 0)
+ && (attr->value_tag == IPP_TAG_TEXT)
+ && !strlen(printer->comment) )
+ {
+ DEBUG(5,("cups_pull_comment_location: Using cups comment: %s\n",
+ attr->values[0].string.text));
+ strlcpy(printer->comment,
+ attr->values[0].string.text,
+ sizeof(printer->comment));
+ }
+
+ /* Grab the location if we don't have one */
+ if ( (strcmp(attr->name, "printer-location") == 0)
+ && (attr->value_tag == IPP_TAG_TEXT)
+ && !strlen(printer->location) )
+ {
+ DEBUG(5,("cups_pull_comment_location: Using cups location: %s\n",
+ attr->values[0].string.text));
+ fstrcpy(printer->location,attr->values[0].string.text);
+ }
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (name == NULL)
+ break;
+
+ }
+
+ ret = True;
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return ret;
+}
+
+#else
+ /* this keeps fussy compilers happy */
+ void print_cups_dummy(void);
+ 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..2a324fdd5c
--- /dev/null
+++ b/source3/printing/print_generic.c
@@ -0,0 +1,301 @@
+/*
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "printing.h"
+
+extern struct current_user current_user;
+extern userdom_struct current_user_info;
+
+/****************************************************************************
+ 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, const char* printername, bool do_sub,
+ const char *command, int *outfd, ...)
+{
+ char *syscmd;
+ char *arg;
+ int ret;
+ TALLOC_CTX *ctx = talloc_tos();
+ va_list ap;
+ va_start(ap, outfd);
+
+ /* check for a valid system printername and valid command to run */
+
+ if ( !printername || !*printername ) {
+ va_end(ap);
+ return -1;
+ }
+
+ if (!command || !*command) {
+ va_end(ap);
+ return -1;
+ }
+
+ syscmd = talloc_strdup(ctx, command);
+ if (!syscmd) {
+ va_end(ap);
+ return -1;
+ }
+
+ while ((arg = va_arg(ap, char *))) {
+ char *value = va_arg(ap,char *);
+ syscmd = talloc_string_sub(ctx, syscmd, arg, value);
+ if (!syscmd) {
+ va_end(ap);
+ return -1;
+ }
+ }
+ va_end(ap);
+
+ syscmd = talloc_string_sub(ctx, syscmd, "%p", printername);
+ if (!syscmd) {
+ return -1;
+ }
+
+ if (do_sub && snum != -1) {
+ syscmd = talloc_sub_advanced(ctx,
+ lp_servicename(snum),
+ current_user_info.unix_name,
+ "",
+ current_user.ut.gid,
+ get_current_username(),
+ current_user_info.domain,
+ syscmd);
+ if (!syscmd) {
+ return -1;
+ }
+ }
+
+ ret = smbrun_no_sanitize(syscmd,outfd);
+
+ DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
+
+ return ret;
+}
+
+
+/****************************************************************************
+delete a print job
+****************************************************************************/
+static int generic_job_delete( const char *sharename, const char *lprm_command, struct printjob *pjob)
+{
+ fstring jobstr;
+
+ /* need to delete the spooled entry */
+ slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob);
+ return print_run_command( -1, sharename, False, lprm_command, 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, PRINTERNAME(snum), True,
+ 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, PRINTERNAME(snum), True,
+ 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 = -1;
+ char *current_directory = NULL;
+ char *print_directory = NULL;
+ char *wd = NULL;
+ char *p = NULL;
+ char *jobname = NULL;
+ TALLOC_CTX *ctx = talloc_tos();
+ fstring job_page_count, job_size;
+
+ /* we print from the directory path to give the best chance of
+ parsing the lpq output */
+ current_directory = TALLOC_ARRAY(ctx,
+ char,
+ PATH_MAX+1);
+ if (!current_directory) {
+ return -1;
+ }
+ wd = sys_getwd(current_directory);
+ if (!wd) {
+ return -1;
+ }
+
+ print_directory = talloc_strdup(ctx, pjob->filename);
+ if (!print_directory) {
+ return -1;
+ }
+ p = strrchr_m(print_directory,'/');
+ if (!p) {
+ return -1;
+ }
+ *p++ = 0;
+
+ if (chdir(print_directory) != 0) {
+ return -1;
+ }
+
+ jobname = talloc_strdup(ctx, pjob->jobname);
+ if (!jobname) {
+ ret = -1;
+ goto out;
+ }
+ jobname = talloc_string_sub(ctx, jobname, "'", "_");
+ if (!jobname) {
+ ret = -1;
+ goto out;
+ }
+ slprintf(job_page_count, sizeof(job_page_count)-1, "%d", pjob->page_count);
+ slprintf(job_size, sizeof(job_size)-1, "%lu", (unsigned long)pjob->size);
+
+ /* send it to the system spooler */
+ ret = print_run_command(snum, PRINTERNAME(snum), True,
+ lp_printcommand(snum), NULL,
+ "%s", p,
+ "%J", jobname,
+ "%f", p,
+ "%z", job_size,
+ "%c", job_page_count,
+ NULL);
+
+ out:
+
+ chdir(wd);
+ TALLOC_FREE(current_directory);
+ return ret;
+}
+
+
+/****************************************************************************
+get the current list of queued jobs
+****************************************************************************/
+static int generic_queue_get(const char *printer_name,
+ enum printing_types printing_type,
+ char *lpq_command,
+ print_queue_struct **q,
+ print_status_struct *status)
+{
+ char **qlines;
+ int fd;
+ int numlines, i, qcount;
+ print_queue_struct *queue = NULL;
+
+ /* never do substitution when running the 'lpq command' since we can't
+ get it rigt when using the background update daemon. Make the caller
+ do it before passing off the command string to us here. */
+
+ print_run_command(-1, printer_name, False, lpq_command, &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,0);
+ close(fd);
+
+ /* turn the lpq output into a series of job structures */
+ qcount = 0;
+ ZERO_STRUCTP(status);
+ if (numlines && qlines) {
+ queue = SMB_MALLOC_ARRAY(print_queue_struct, numlines+1);
+ if (!queue) {
+ file_lines_free(qlines);
+ *q = NULL;
+ return 0;
+ }
+ memset(queue, '\0', sizeof(print_queue_struct)*(numlines+1));
+
+ for (i=0; i<numlines; i++) {
+ /* parse the line */
+ if (parse_lpq_entry(printing_type,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, PRINTERNAME(snum), True, lp_queuepausecommand(snum), NULL, NULL);
+}
+
+/****************************************************************************
+ resume a queue
+****************************************************************************/
+static int generic_queue_resume(int snum)
+{
+ return print_run_command(snum, PRINTERNAME(snum), True, lp_queueresumecommand(snum), NULL, NULL);
+}
+
+/****************************************************************************
+ * Generic printing interface definitions...
+ ***************************************************************************/
+
+struct printif generic_printif =
+{
+ DEFAULT_PRINTING,
+ generic_queue_get,
+ generic_queue_pause,
+ generic_queue_resume,
+ generic_job_delete,
+ generic_job_pause,
+ generic_job_resume,
+ generic_job_submit,
+};
+
diff --git a/source3/printing/print_iprint.c b/source3/printing/print_iprint.c
new file mode 100644
index 0000000000..b038f8d1fe
--- /dev/null
+++ b/source3/printing/print_iprint.c
@@ -0,0 +1,1240 @@
+/*
+ * Support code for Novell iPrint using the Common UNIX Printing
+ * System ("CUPS") libraries
+ *
+ * Copyright 1999-2003 by Michael R Sweet.
+ * Portions Copyright 2005 by Joel J. Smith.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "printing.h"
+
+#ifdef HAVE_IPRINT
+#include <cups/cups.h>
+#include <cups/language.h>
+
+#define OPERATION_NOVELL_LIST_PRINTERS 0x401A
+#define OPERATION_NOVELL_MGMT 0x401C
+#define NOVELL_SERVER_SYSNAME "sysname="
+#define NOVELL_SERVER_SYSNAME_NETWARE "NetWare IA32"
+#define NOVELL_SERVER_VERSION_STRING "iprintserverversion="
+#define NOVELL_SERVER_VERSION_OES_SP1 33554432
+
+/*
+ * 'iprint_passwd_cb()' - The iPrint password callback...
+ */
+
+static const char * /* O - Password or NULL */
+iprint_passwd_cb(const char *prompt) /* I - Prompt */
+{
+ /*
+ * Always return NULL to indicate that no password is available...
+ */
+
+ return (NULL);
+}
+
+static const char *iprint_server(void)
+{
+ if ((lp_iprint_server() != NULL) && (strlen(lp_iprint_server()) > 0)) {
+ DEBUG(10, ("iprint server explicitly set to %s\n",
+ lp_iprint_server()));
+ return lp_iprint_server();
+ }
+
+ DEBUG(10, ("iprint server left to default %s\n", cupsServer()));
+ return cupsServer();
+}
+
+/*
+ * Pass in an already connected http_t*
+ * Returns the server version if one can be found, multiplied by
+ * -1 for all NetWare versions. Returns 0 if a server version
+ * cannot be determined
+ */
+
+static int iprint_get_server_version(http_t *http, char* serviceUri)
+{
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language = NULL; /* Default language */
+ char *ver; /* server version pointer */
+ char *vertmp; /* server version tmp pointer */
+ int serverVersion = 0; /* server version */
+ char *os; /* server os */
+ int osFlag = 0; /* 0 for NetWare, 1 for anything else */
+ char *temp; /* pointer for string manipulation */
+
+ /*
+ * Build an OPERATION_NOVELL_MGMT("get-server-version") request,
+ * which requires the following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * operation-name
+ * service-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = (ipp_op_t)OPERATION_NOVELL_MGMT;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, "utf-8");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "service-uri", NULL, serviceUri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "operation-name", NULL, "get-server-version");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if (((response = cupsDoRequest(http, request, "/ipp/")) == NULL) ||
+ (response->request.status.status_code >= IPP_OK_CONFLICT))
+ goto out;
+
+ if (((attr = ippFindAttribute(response, "server-version",
+ IPP_TAG_STRING)) != NULL)) {
+ if ((ver = strstr(attr->values[0].string.text,
+ NOVELL_SERVER_VERSION_STRING)) != NULL) {
+ ver += strlen(NOVELL_SERVER_VERSION_STRING);
+ /*
+ * Strangely, libcups stores a IPP_TAG_STRING (octet
+ * string) as a null-terminated string with no length
+ * even though it could be binary data with nulls in
+ * it. Luckily, in this case the value is not binary.
+ */
+ serverVersion = strtol(ver, &vertmp, 10);
+
+ /* Check for not found, overflow or negative version */
+ if ((ver == vertmp) || (serverVersion < 0))
+ serverVersion = 0;
+ }
+
+ if ((os = strstr(attr->values[0].string.text,
+ NOVELL_SERVER_SYSNAME)) != NULL) {
+ os += strlen(NOVELL_SERVER_SYSNAME);
+ if ((temp = strchr(os,'<')) != NULL)
+ *temp = '\0';
+ if (strcmp(os,NOVELL_SERVER_SYSNAME_NETWARE))
+ osFlag = 1; /* 1 for non-NetWare systems */
+ }
+ }
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (osFlag == 0)
+ serverVersion *= -1;
+
+ return serverVersion;
+}
+
+
+static int iprint_cache_add_printer(http_t *http,
+ int reqId,
+ char* url)
+{
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language = NULL; /* Default language */
+ char *name, /* printer-name attribute */
+ *info, /* printer-info attribute */
+ smb_enabled, /* smb-enabled attribute */
+ secure; /* security-enabled attrib. */
+
+ char *httpPath; /* path portion of the printer-uri */
+
+ static const char *pattrs[] = /* Requested printer attributes */
+ {
+ "printer-name",
+ "security-enabled",
+ "printer-info",
+ "smb-enabled"
+ };
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = reqId;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, "utf-8");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, url);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes",
+ (sizeof(pattrs) / sizeof(pattrs[0])),
+ NULL, pattrs);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((httpPath = strstr(url,"://")) == NULL ||
+ (httpPath = strchr(httpPath+3,'/')) == NULL)
+ {
+ ippDelete(request);
+ request = NULL;
+ goto out;
+ }
+
+ if ((response = cupsDoRequest(http, request, httpPath)) == NULL) {
+ ipp_status_t lastErr = cupsLastError();
+
+ /*
+ * Ignore printers that cannot be queried without credentials
+ */
+ if (lastErr == IPP_FORBIDDEN ||
+ lastErr == IPP_NOT_AUTHENTICATED ||
+ lastErr == IPP_NOT_AUTHORIZED)
+ goto out;
+
+ DEBUG(0,("Unable to get printer list - %s\n",
+ ippErrorString(lastErr)));
+ goto out;
+ }
+
+ 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;
+ info = NULL;
+ smb_enabled= 1;
+ secure = 0;
+
+ 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-info") == 0 &&
+ (attr->value_tag == IPP_TAG_TEXT ||
+ attr->value_tag == IPP_TAG_TEXTLANG))
+ info = attr->values[0].string.text;
+
+ /*
+ * If the smb-enabled attribute is present and the
+ * value is set to 0, don't show the printer.
+ * If the attribute is not present, assume that the
+ * printer should show up
+ */
+ if (!strcmp(attr->name, "smb-enabled") &&
+ ((attr->value_tag == IPP_TAG_INTEGER &&
+ !attr->values[0].integer) ||
+ (attr->value_tag == IPP_TAG_BOOLEAN &&
+ !attr->values[0].boolean)))
+ smb_enabled = 0;
+
+ /*
+ * If the security-enabled attribute is present and the
+ * value is set to 1, don't show the printer.
+ * If the attribute is not present, assume that the
+ * printer should show up
+ */
+ if (!strcmp(attr->name, "security-enabled") &&
+ ((attr->value_tag == IPP_TAG_INTEGER &&
+ attr->values[0].integer) ||
+ (attr->value_tag == IPP_TAG_BOOLEAN &&
+ attr->values[0].boolean)))
+ secure = 1;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ * Make sure the printer is not a secure printer
+ * and make sure smb printing hasn't been explicitly
+ * disabled for the printer
+ */
+
+ if (name != NULL && !secure && smb_enabled)
+ pcap_cache_add(name, info);
+ }
+
+ out:
+ if (response)
+ ippDelete(response);
+ return(0);
+}
+
+bool iprint_cache_reload(void)
+{
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language = NULL; /* Default language */
+ int i;
+ bool ret = False;
+
+ DEBUG(5, ("reloading iprint printcap cache\n"));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(iprint_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
+ DEBUG(0,("Unable to connect to iPrint server %s - %s\n",
+ iprint_server(), strerror(errno)));
+ goto out;
+ }
+
+ /*
+ * Build a OPERATION_NOVELL_LIST_PRINTERS request, which requires the following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id =
+ (ipp_op_t)OPERATION_NOVELL_LIST_PRINTERS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, "utf-8");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "ipp-server", NULL, "ippSrvr");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/ipp")) == NULL) {
+ DEBUG(0,("Unable to get printer list - %s\n",
+ ippErrorString(cupsLastError())));
+ goto out;
+ }
+
+ 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...
+ */
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ (attr->value_tag == IPP_TAG_URI ||
+ attr->value_tag == IPP_TAG_NAME ||
+ attr->value_tag == IPP_TAG_TEXT ||
+ attr->value_tag == IPP_TAG_NAMELANG ||
+ attr->value_tag == IPP_TAG_TEXTLANG))
+ {
+ for (i = 0; i<attr->num_values; i++)
+ {
+ char *url = attr->values[i].string.text;
+ if (!url || !strlen(url))
+ continue;
+ iprint_cache_add_printer(http, i+2, url);
+ }
+ }
+ attr = attr->next;
+ }
+ }
+
+ ret = True;
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return ret;
+}
+
+
+/*
+ * 'iprint_job_delete()' - Delete a job.
+ */
+
+static int iprint_job_delete(const char *sharename, const char *lprm_command, struct printjob *pjob)
+{
+ int ret = 1; /* Return value */
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ cups_lang_t *language = NULL; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+ char httpPath[HTTP_MAX_URI]; /* path portion of the printer-uri */
+
+
+ DEBUG(5,("iprint_job_delete(%s, %p (%d))\n", sharename, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(iprint_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
+ DEBUG(0,("Unable to connect to iPrint server %s - %s\n",
+ iprint_server(), strerror(errno)));
+ goto out;
+ }
+
+ /*
+ * Build an IPP_CANCEL_JOB request, which uses the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * job-id
+ * 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, "utf-8");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), sharename);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", pjob->sysjob);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, pjob->user);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", sharename);
+
+ if ((response = cupsDoRequest(http, request, httpPath)) != 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;
+ }
+ } else {
+ DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+ }
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return ret;
+}
+
+
+/*
+ * 'iprint_job_pause()' - Pause a job.
+ */
+
+static int iprint_job_pause(int snum, struct printjob *pjob)
+{
+ int ret = 1; /* Return value */
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ cups_lang_t *language = NULL; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+ char httpPath[HTTP_MAX_URI]; /* path portion of the printer-uri */
+
+
+ DEBUG(5,("iprint_job_pause(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(iprint_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
+ DEBUG(0,("Unable to connect to iPrint server %s - %s\n",
+ iprint_server(), strerror(errno)));
+ goto out;
+ }
+
+ /*
+ * Build an IPP_HOLD_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * job-id
+ * 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, "utf-8");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), PRINTERNAME(snum));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", pjob->sysjob);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, pjob->user);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", PRINTERNAME(snum));
+
+ if ((response = cupsDoRequest(http, request, httpPath)) != 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;
+ }
+ } else {
+ DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+ }
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return ret;
+}
+
+
+/*
+ * 'iprint_job_resume()' - Resume a paused job.
+ */
+
+static int iprint_job_resume(int snum, struct printjob *pjob)
+{
+ int ret = 1; /* Return value */
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ cups_lang_t *language = NULL; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+ char httpPath[HTTP_MAX_URI]; /* path portion of the printer-uri */
+
+
+ DEBUG(5,("iprint_job_resume(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(iprint_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
+ DEBUG(0,("Unable to connect to iPrint server %s - %s\n",
+ iprint_server(), strerror(errno)));
+ goto out;
+ }
+
+ /*
+ * Build an IPP_RELEASE_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * job-id
+ * 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, "utf-8");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), PRINTERNAME(snum));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", pjob->sysjob);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, pjob->user);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", PRINTERNAME(snum));
+
+ if ((response = cupsDoRequest(http, request, httpPath)) != 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;
+ }
+ } else {
+ DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+ }
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return ret;
+}
+
+
+/*
+ * 'iprint_job_submit()' - Submit a job for printing.
+ */
+
+static int iprint_job_submit(int snum, struct printjob *pjob)
+{
+ int ret = 1; /* Return value */
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language = NULL; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+ const char *clientname = NULL; /* hostname of client for job-originating-host attribute */
+ char addr[INET6_ADDRSTRLEN];
+
+ DEBUG(5,("iprint_job_submit(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(iprint_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
+ DEBUG(0,("Unable to connect to iPrint server %s - %s\n",
+ iprint_server(), strerror(errno)));
+ goto out;
+ }
+
+ /*
+ * 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, "utf-8");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), 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);
+
+ clientname = client_name(get_client_fd());
+ if (strcmp(clientname, "UNKNOWN") == 0) {
+ clientname = client_addr(get_client_fd(),addr,sizeof(addr));
+ }
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "job-originating-host-name", NULL,
+ clientname);
+
+ 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, "/ipp/%s", PRINTERNAME(snum));
+
+ 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;
+ }
+ } else {
+ DEBUG(0,("Unable to print file to `%s' - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+ }
+
+ if ( ret == 0 )
+ unlink(pjob->filename);
+ /* else print_job_end will do it for us */
+
+ if ( ret == 0 ) {
+
+ attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER);
+ if (attr != NULL && attr->group_tag == IPP_TAG_JOB)
+ {
+ pjob->sysjob = attr->values[0].integer;
+ }
+ }
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return ret;
+}
+
+/*
+ * 'iprint_queue_get()' - Get all the jobs in the print queue.
+ */
+
+static int iprint_queue_get(const char *sharename,
+ enum printing_types printing_type,
+ char *lpq_command,
+ print_queue_struct **q,
+ print_status_struct *status)
+{
+ fstring printername;
+ http_t *http = NULL; /* HTTP connection to server */
+ ipp_t *request = NULL, /* IPP Request */
+ *response = NULL; /* IPP Response */
+ ipp_attribute_t *attr = NULL; /* Current attribute */
+ cups_lang_t *language = NULL; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+ char serviceUri[HTTP_MAX_URI]; /* service-uri attribute */
+ char httpPath[HTTP_MAX_URI]; /* path portion of the uri */
+ int jobUseUnixTime = 0; /* Whether job times should
+ * be assumed to be Unix time */
+ int qcount = 0, /* Number of active queue entries */
+ qalloc = 0; /* Number of queue entries allocated */
+ print_queue_struct *queue = NULL, /* 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 */
+ time_t printer_current_time = 0; /* printer's current time */
+ time_t printer_up_time = 0; /* printer's uptime */
+ 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",
+ "printer-current-time",
+ "printer-up-time"
+ };
+
+ *q = NULL;
+
+ /* HACK ALERT!!! The porblem with support the 'printer name'
+ option is that we key the tdb off the sharename. So we will
+ overload the lpq_command string to pass in the printername
+ (which is basically what we do for non-cups printers ... using
+ the lpq_command to get the queue listing). */
+
+ fstrcpy( printername, lpq_command );
+
+ DEBUG(5,("iprint_queue_get(%s, %p, %p)\n", printername, q, status));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(iprint_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
+ DEBUG(0,("Unable to connect to iPrint server %s - %s\n",
+ iprint_server(), strerror(errno)));
+ goto out;
+ }
+
+ /*
+ * Generate the printer URI and the service URI that goes with it...
+ */
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), printername);
+ slprintf(serviceUri, sizeof(serviceUri) - 1, "ipp://%s/ipp/", iprint_server());
+
+ /*
+ * For Linux iPrint servers from OES SP1 on, the iPrint server
+ * uses Unix time for job start times unless it detects the iPrint
+ * client in an http User-Agent header. (This was done to accomodate
+ * CUPS broken behavior. According to RFC 2911, section 4.3.14, job
+ * start times are supposed to be relative to how long the printer has
+ * been up.) Since libcups doesn't allow us to set that header before
+ * the request is sent, this ugly hack allows us to detect the server
+ * version and decide how to interpret the job time.
+ */
+ if (iprint_get_server_version(http, serviceUri) >=
+ NOVELL_SERVER_VERSION_OES_SP1)
+ jobUseUnixTime = 1;
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 2;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, "utf-8");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes",
+ (sizeof(pattrs) / sizeof(pattrs[0])),
+ NULL, pattrs);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", printername);
+
+ if ((response = cupsDoRequest(http, request, httpPath)) == NULL) {
+ DEBUG(0,("Unable to get printer status for %s - %s\n", printername,
+ ippErrorString(cupsLastError())));
+ *q = queue;
+ goto out;
+ }
+
+ if (response->request.status.status_code >= IPP_OK_CONFLICT) {
+ DEBUG(0,("Unable to get printer status for %s - %s\n", printername,
+ ippErrorString(response->request.status.status_code)));
+ *q = queue;
+ goto out;
+ }
+
+ /*
+ * 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);
+
+ if ((attr = ippFindAttribute(response, "printer-current-time",
+ IPP_TAG_DATE)) != NULL)
+ printer_current_time = ippDateToTime(attr->values[0].date);
+
+ if ((attr = ippFindAttribute(response, "printer-up-time",
+ IPP_TAG_INTEGER)) != NULL)
+ printer_up_time = attr->values[0].integer;
+
+ ippDelete(response);
+ response = NULL;
+
+ /*
+ * 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 = 3;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, "utf-8");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes",
+ (sizeof(jattrs) / sizeof(jattrs[0])),
+ NULL, jattrs);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", printername);
+
+ if ((response = cupsDoRequest(http, request, httpPath)) == NULL) {
+ DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
+ ippErrorString(cupsLastError())));
+ goto out;
+ }
+
+ 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)));
+ goto out;
+ }
+
+ /*
+ * 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;
+
+ queue = SMB_REALLOC_ARRAY(queue, print_queue_struct, qalloc);
+
+ if (queue == NULL) {
+ DEBUG(0,("iprint_queue_get: Not enough memory!"));
+ qcount = 0;
+ goto out;
+ }
+ }
+
+ 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)
+ {
+ /*
+ * If jobs times are in Unix time, the accuracy of the job
+ * start time depends upon the iPrint server's time being
+ * set correctly. Otherwise, the accuracy depends upon
+ * the Samba server's time being set correctly
+ */
+
+ if (jobUseUnixTime)
+ job_time = attr->values[0].integer;
+ else
+ job_time = time(NULL) - printer_up_time + attr->values[0].integer;
+ }
+
+ if (strcmp(attr->name, "job-name") == 0 &&
+ (attr->value_tag == IPP_TAG_NAMELANG ||
+ 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_NAMELANG ||
+ 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;
+ }
+
+ /*
+ * Return the job queue...
+ */
+
+ *q = queue;
+
+ out:
+ if (response)
+ ippDelete(response);
+
+ if (language)
+ cupsLangFree(language);
+
+ if (http)
+ httpClose(http);
+
+ return qcount;
+}
+
+
+/*
+ * 'iprint_queue_pause()' - Pause a print queue.
+ */
+
+static int iprint_queue_pause(int snum)
+{
+ return(-1); /* Not supported without credentials */
+}
+
+
+/*
+ * 'iprint_queue_resume()' - Restart a print queue.
+ */
+
+static int iprint_queue_resume(int snum)
+{
+ return(-1); /* Not supported without credentials */
+}
+
+/*******************************************************************
+ * iPrint printing interface definitions...
+ ******************************************************************/
+
+struct printif iprint_printif =
+{
+ PRINT_IPRINT,
+ iprint_queue_get,
+ iprint_queue_pause,
+ iprint_queue_resume,
+ iprint_job_delete,
+ iprint_job_pause,
+ iprint_job_resume,
+ iprint_job_submit,
+};
+
+#else
+ /* this keeps fussy compilers happy */
+ void print_iprint_dummy(void);
+ void print_iprint_dummy(void) {}
+#endif /* HAVE_IPRINT */
diff --git a/source3/printing/print_svid.c b/source3/printing/print_svid.c
new file mode 100644
index 0000000000..7e91d3a677
--- /dev/null
+++ b/source3/printing/print_svid.c
@@ -0,0 +1,127 @@
+/*
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * 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"
+
+#if defined(SYSV) || defined(HPUX)
+bool sysv_cache_reload(void)
+{
+ char **lines;
+ int i;
+
+#if defined(HPUX)
+ DEBUG(5, ("reloading hpux printcap cache\n"));
+#else
+ DEBUG(5, ("reloading sysv printcap cache\n"));
+#endif
+
+ if ((lines = file_lines_pload("/usr/bin/lpstat -v", NULL)) == NULL)
+ {
+#if defined(HPUX)
+
+ /*
+ * if "lpstat -v" is NULL then we check if schedular is running if it is
+ * that means no printers are added on the HP-UX system, if schedular is not
+ * running we display reload error.
+ */
+
+ char **scheduler;
+ scheduler = file_lines_pload("/usr/bin/lpstat -r", NULL);
+ if(!strcmp(*scheduler,"scheduler is running")){
+ DEBUG(3,("No Printers found!!!\n"));
+ file_lines_free(scheduler);
+ return True;
+ }
+ else{
+ DEBUG(3,("Scheduler is not running!!!\n"));
+ file_lines_free(scheduler);
+ return False;
+ }
+#else
+ DEBUG(3,("No Printers found!!!\n"));
+ return False;
+#endif
+ }
+
+ for (i = 0; lines[i]; i++) {
+ 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 (!pcap_cache_add(name, NULL)) {
+ file_lines_free(lines);
+ return False;
+ }
+ }
+
+ file_lines_free(lines);
+ return True;
+}
+
+#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..c6749226fd
--- /dev/null
+++ b/source3/printing/printfsp.c
@@ -0,0 +1,122 @@
+/*
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/***************************************************************************
+open a print file and setup a fsp for it. This is a wrapper around
+print_job_start().
+***************************************************************************/
+
+NTSTATUS print_fsp_open(connection_struct *conn, const char *fname,
+ uint16_t current_vuid, files_struct **result)
+{
+ int jobid;
+ SMB_STRUCT_STAT sbuf;
+ files_struct *fsp;
+ fstring name;
+ NTSTATUS status;
+
+ status = file_new(conn, &fsp);
+ if(!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ fstrcpy( name, "Remote Downlevel Document");
+ if (fname) {
+ const char *p = strrchr(fname, '/');
+ fstrcat(name, " ");
+ if (!p) {
+ p = fname;
+ }
+ fstrcat(name, p);
+ }
+
+ jobid = print_job_start(conn->server_info, SNUM(conn), name, NULL);
+ if (jobid == -1) {
+ status = map_nt_error_from_unix(errno);
+ file_free(fsp);
+ return status;
+ }
+
+ /* Convert to RAP id. */
+ fsp->rap_print_jobid = pjobid_to_rap(lp_const_servicename(SNUM(conn)), jobid);
+ if (fsp->rap_print_jobid == 0) {
+ /* We need to delete the entry in the tdb. */
+ pjob_delete(lp_const_servicename(SNUM(conn)), jobid);
+ file_free(fsp);
+ return NT_STATUS_ACCESS_DENIED; /* No errno around here */
+ }
+
+ /* setup a full fsp */
+ fsp->fh->fd = print_job_fd(lp_const_servicename(SNUM(conn)),jobid);
+ GetTimeOfDay(&fsp->open_time);
+ fsp->vuid = current_vuid;
+ fsp->fh->pos = -1;
+ fsp->can_lock = True;
+ fsp->can_read = False;
+ fsp->access_mask = FILE_GENERIC_WRITE;
+ fsp->can_write = True;
+ fsp->print_file = True;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = False;
+ string_set(&fsp->fsp_name,print_job_fname(lp_const_servicename(SNUM(conn)),jobid));
+ fsp->wcp = NULL;
+ SMB_VFS_FSTAT(fsp, &sbuf);
+ fsp->mode = sbuf.st_mode;
+ fsp->file_id = vfs_file_id_from_sbuf(conn, &sbuf);
+
+ conn->num_files_open++;
+
+ *result = fsp;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Print a file - called on closing the file.
+****************************************************************************/
+
+void print_fsp_end(files_struct *fsp, enum file_close_type close_type)
+{
+ uint32 jobid;
+ fstring sharename;
+
+ if (fsp->fh->private_options & FILE_DELETE_ON_CLOSE) {
+ /*
+ * Truncate the job. print_job_end will take
+ * care of deleting it for us. JRA.
+ */
+ sys_ftruncate(fsp->fh->fd, 0);
+ }
+
+ if (fsp->fsp_name) {
+ string_free(&fsp->fsp_name);
+ }
+
+ if (!rap_to_pjobid(fsp->rap_print_jobid, sharename, &jobid)) {
+ DEBUG(3,("print_fsp_end: Unable to convert RAP jobid %u to print jobid.\n",
+ (unsigned int)fsp->rap_print_jobid ));
+ return;
+ }
+
+ print_job_end(SNUM(fsp->conn),jobid, close_type);
+}
diff --git a/source3/printing/printing.c b/source3/printing/printing.c
new file mode 100644
index 0000000000..1016e6183d
--- /dev/null
+++ b/source3/printing/printing.c
@@ -0,0 +1,2888 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ printing backend routines
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 2002
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "printing.h"
+
+extern SIG_ATOMIC_T got_sig_term;
+extern SIG_ATOMIC_T reload_after_sighup;
+extern struct current_user current_user;
+extern userdom_struct current_user_info;
+
+/* Current printer interface */
+static bool remove_from_jobs_changed(const char* sharename, uint32 jobid);
+
+/*
+ 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.
+*/
+
+static TDB_CONTEXT *rap_tdb;
+static uint16 next_rap_jobid;
+struct rap_jobid_key {
+ fstring sharename;
+ uint32 jobid;
+};
+
+/***************************************************************************
+ Nightmare. LANMAN jobid's are 16 bit numbers..... We must map them to 32
+ bit RPC jobids.... JRA.
+***************************************************************************/
+
+uint16 pjobid_to_rap(const char* sharename, uint32 jobid)
+{
+ uint16 rap_jobid;
+ TDB_DATA data, key;
+ struct rap_jobid_key jinfo;
+ uint8 buf[2];
+
+ DEBUG(10,("pjobid_to_rap: called.\n"));
+
+ if (!rap_tdb) {
+ /* Create the in-memory tdb. */
+ rap_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL, (O_RDWR|O_CREAT), 0644);
+ if (!rap_tdb)
+ return 0;
+ }
+
+ ZERO_STRUCT( jinfo );
+ fstrcpy( jinfo.sharename, sharename );
+ jinfo.jobid = jobid;
+ key.dptr = (uint8 *)&jinfo;
+ key.dsize = sizeof(jinfo);
+
+ data = tdb_fetch(rap_tdb, key);
+ if (data.dptr && data.dsize == sizeof(uint16)) {
+ rap_jobid = SVAL(data.dptr, 0);
+ SAFE_FREE(data.dptr);
+ DEBUG(10,("pjobid_to_rap: jobid %u maps to RAP jobid %u\n",
+ (unsigned int)jobid, (unsigned int)rap_jobid));
+ return rap_jobid;
+ }
+ SAFE_FREE(data.dptr);
+ /* Not found - create and store mapping. */
+ rap_jobid = ++next_rap_jobid;
+ if (rap_jobid == 0)
+ rap_jobid = ++next_rap_jobid;
+ SSVAL(buf,0,rap_jobid);
+ data.dptr = buf;
+ data.dsize = sizeof(rap_jobid);
+ tdb_store(rap_tdb, key, data, TDB_REPLACE);
+ tdb_store(rap_tdb, data, key, TDB_REPLACE);
+
+ DEBUG(10,("pjobid_to_rap: created jobid %u maps to RAP jobid %u\n",
+ (unsigned int)jobid, (unsigned int)rap_jobid));
+ return rap_jobid;
+}
+
+bool rap_to_pjobid(uint16 rap_jobid, fstring sharename, uint32 *pjobid)
+{
+ TDB_DATA data, key;
+ uint8 buf[2];
+
+ DEBUG(10,("rap_to_pjobid called.\n"));
+
+ if (!rap_tdb)
+ return False;
+
+ SSVAL(buf,0,rap_jobid);
+ key.dptr = buf;
+ key.dsize = sizeof(rap_jobid);
+ data = tdb_fetch(rap_tdb, key);
+ if ( data.dptr && data.dsize == sizeof(struct rap_jobid_key) )
+ {
+ struct rap_jobid_key *jinfo = (struct rap_jobid_key*)data.dptr;
+ fstrcpy( sharename, jinfo->sharename );
+ *pjobid = jinfo->jobid;
+ DEBUG(10,("rap_to_pjobid: jobid %u maps to RAP jobid %u\n",
+ (unsigned int)*pjobid, (unsigned int)rap_jobid));
+ SAFE_FREE(data.dptr);
+ return True;
+ }
+
+ DEBUG(10,("rap_to_pjobid: Failed to lookup RAP jobid %u\n",
+ (unsigned int)rap_jobid));
+ SAFE_FREE(data.dptr);
+ return False;
+}
+
+static void rap_jobid_delete(const char* sharename, uint32 jobid)
+{
+ TDB_DATA key, data;
+ uint16 rap_jobid;
+ struct rap_jobid_key jinfo;
+ uint8 buf[2];
+
+ DEBUG(10,("rap_jobid_delete: called.\n"));
+
+ if (!rap_tdb)
+ return;
+
+ ZERO_STRUCT( jinfo );
+ fstrcpy( jinfo.sharename, sharename );
+ jinfo.jobid = jobid;
+ key.dptr = (uint8 *)&jinfo;
+ key.dsize = sizeof(jinfo);
+
+ data = tdb_fetch(rap_tdb, key);
+ if (!data.dptr || (data.dsize != sizeof(uint16))) {
+ DEBUG(10,("rap_jobid_delete: cannot find jobid %u\n",
+ (unsigned int)jobid ));
+ SAFE_FREE(data.dptr);
+ return;
+ }
+
+ DEBUG(10,("rap_jobid_delete: deleting jobid %u\n",
+ (unsigned int)jobid ));
+
+ rap_jobid = SVAL(data.dptr, 0);
+ SAFE_FREE(data.dptr);
+ SSVAL(buf,0,rap_jobid);
+ data.dptr = buf;
+ data.dsize = sizeof(rap_jobid);
+ tdb_delete(rap_tdb, key);
+ tdb_delete(rap_tdb, data);
+}
+
+static int get_queue_status(const char* sharename, print_status_struct *);
+
+/****************************************************************************
+ Initialise the printing backend. Called once at startup before the fork().
+****************************************************************************/
+
+bool print_backend_init(struct messaging_context *msg_ctx)
+{
+ const char *sversion = "INFO/version";
+ int services = lp_numservices();
+ int snum;
+
+ unlink(lock_path("printing.tdb"));
+ mkdir(lock_path("printing"),0755);
+
+ /* handle a Samba upgrade */
+
+ for (snum = 0; snum < services; snum++) {
+ struct tdb_print_db *pdb;
+ if (!lp_print_ok(snum))
+ continue;
+
+ pdb = get_print_db_byname(lp_const_servicename(snum));
+ if (!pdb)
+ continue;
+ if (tdb_lock_bystring(pdb->tdb, sversion) == -1) {
+ DEBUG(0,("print_backend_init: Failed to open printer %s database\n", lp_const_servicename(snum) ));
+ release_print_db(pdb);
+ return False;
+ }
+ if (tdb_fetch_int32(pdb->tdb, sversion) != PRINT_DATABASE_VERSION) {
+ tdb_wipe_all(pdb->tdb);
+ tdb_store_int32(pdb->tdb, sversion, PRINT_DATABASE_VERSION);
+ }
+ tdb_unlock_bystring(pdb->tdb, sversion);
+ release_print_db(pdb);
+ }
+
+ close_all_print_db(); /* Don't leave any open. */
+
+ /* do NT print initialization... */
+ return nt_printing_init(msg_ctx);
+}
+
+/****************************************************************************
+ Shut down printing backend. Called once at shutdown to close the tdb.
+****************************************************************************/
+
+void printing_end(void)
+{
+ close_all_print_db(); /* Don't leave any open. */
+}
+
+/****************************************************************************
+ Retrieve the set of printing functions for a given service. This allows
+ us to set the printer function table based on the value of the 'printing'
+ service parameter.
+
+ Use the generic interface as the default and only use cups interface only
+ when asked for (and only when supported)
+****************************************************************************/
+
+static struct printif *get_printer_fns_from_type( enum printing_types type )
+{
+ struct printif *printer_fns = &generic_printif;
+
+#ifdef HAVE_CUPS
+ if ( type == PRINT_CUPS ) {
+ printer_fns = &cups_printif;
+ }
+#endif /* HAVE_CUPS */
+
+#ifdef HAVE_IPRINT
+ if ( type == PRINT_IPRINT ) {
+ printer_fns = &iprint_printif;
+ }
+#endif /* HAVE_IPRINT */
+
+ printer_fns->type = type;
+
+ return printer_fns;
+}
+
+static struct printif *get_printer_fns( int snum )
+{
+ return get_printer_fns_from_type( (enum printing_types)lp_printing(snum) );
+}
+
+
+/****************************************************************************
+ Useful function to generate a tdb key.
+****************************************************************************/
+
+static TDB_DATA print_key(uint32 jobid, uint32 *tmp)
+{
+ TDB_DATA ret;
+
+ SIVAL(tmp, 0, jobid);
+ ret.dptr = (uint8 *)tmp;
+ ret.dsize = sizeof(*tmp);
+ return ret;
+}
+
+/***********************************************************************
+ unpack a pjob from a tdb buffer
+***********************************************************************/
+
+int unpack_pjob( uint8 *buf, int buflen, struct printjob *pjob )
+{
+ int len = 0;
+ int used;
+ uint32 pjpid, pjsysjob, pjfd, pjstarttime, pjstatus;
+ uint32 pjsize, pjpage_count, pjspooled, pjsmbjob;
+
+ if ( !buf || !pjob )
+ return -1;
+
+ len += tdb_unpack(buf+len, buflen-len, "dddddddddffff",
+ &pjpid,
+ &pjsysjob,
+ &pjfd,
+ &pjstarttime,
+ &pjstatus,
+ &pjsize,
+ &pjpage_count,
+ &pjspooled,
+ &pjsmbjob,
+ pjob->filename,
+ pjob->jobname,
+ pjob->user,
+ pjob->queuename);
+
+ if ( len == -1 )
+ return -1;
+
+ if ( (used = unpack_devicemode(&pjob->nt_devmode, buf+len, buflen-len)) == -1 )
+ return -1;
+
+ len += used;
+
+ pjob->pid = pjpid;
+ pjob->sysjob = pjsysjob;
+ pjob->fd = pjfd;
+ pjob->starttime = pjstarttime;
+ pjob->status = pjstatus;
+ pjob->size = pjsize;
+ pjob->page_count = pjpage_count;
+ pjob->spooled = pjspooled;
+ pjob->smbjob = pjsmbjob;
+
+ return len;
+
+}
+
+/****************************************************************************
+ Useful function to find a print job in the database.
+****************************************************************************/
+
+static struct printjob *print_job_find(const char *sharename, uint32 jobid)
+{
+ static struct printjob pjob;
+ uint32_t tmp;
+ TDB_DATA ret;
+ struct tdb_print_db *pdb = get_print_db_byname(sharename);
+
+ DEBUG(10,("print_job_find: looking up job %u for share %s\n",
+ (unsigned int)jobid, sharename ));
+
+ if (!pdb) {
+ return NULL;
+ }
+
+ ret = tdb_fetch(pdb->tdb, print_key(jobid, &tmp));
+ release_print_db(pdb);
+
+ if (!ret.dptr) {
+ DEBUG(10,("print_job_find: failed to find jobid %u.\n", (unsigned int)jobid ));
+ return NULL;
+ }
+
+ if ( pjob.nt_devmode ) {
+ free_nt_devicemode( &pjob.nt_devmode );
+ }
+
+ ZERO_STRUCT( pjob );
+
+ if ( unpack_pjob( ret.dptr, ret.dsize, &pjob ) == -1 ) {
+ DEBUG(10,("print_job_find: failed to unpack jobid %u.\n", (unsigned int)jobid ));
+ SAFE_FREE(ret.dptr);
+ return NULL;
+ }
+
+ SAFE_FREE(ret.dptr);
+
+ DEBUG(10,("print_job_find: returning system job %d for jobid %u.\n",
+ (int)pjob.sysjob, (unsigned int)jobid ));
+
+ return &pjob;
+}
+
+/* Convert a unix jobid to a smb jobid */
+
+struct unixjob_traverse_state {
+ int sysjob;
+ uint32 sysjob_to_jobid_value;
+};
+
+static int unixjob_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key,
+ TDB_DATA data, void *private_data)
+{
+ struct printjob *pjob;
+ struct unixjob_traverse_state *state =
+ (struct unixjob_traverse_state *)private_data;
+
+ if (!data.dptr || data.dsize == 0)
+ return 0;
+
+ pjob = (struct printjob *)data.dptr;
+ if (key.dsize != sizeof(uint32))
+ return 0;
+
+ if (state->sysjob == pjob->sysjob) {
+ uint32 jobid = IVAL(key.dptr,0);
+
+ state->sysjob_to_jobid_value = jobid;
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ This is a *horribly expensive call as we have to iterate through all the
+ current printer tdb's. Don't do this often ! JRA.
+****************************************************************************/
+
+uint32 sysjob_to_jobid(int unix_jobid)
+{
+ int services = lp_numservices();
+ int snum;
+ struct unixjob_traverse_state state;
+
+ state.sysjob = unix_jobid;
+ state.sysjob_to_jobid_value = (uint32)-1;
+
+ for (snum = 0; snum < services; snum++) {
+ struct tdb_print_db *pdb;
+ if (!lp_print_ok(snum))
+ continue;
+ pdb = get_print_db_byname(lp_const_servicename(snum));
+ if (!pdb) {
+ continue;
+ }
+ tdb_traverse(pdb->tdb, unixjob_traverse_fn, &state);
+ release_print_db(pdb);
+ if (state.sysjob_to_jobid_value != (uint32)-1)
+ return state.sysjob_to_jobid_value;
+ }
+ return (uint32)-1;
+}
+
+/****************************************************************************
+ Send notifications based on what has changed after a pjob_store.
+****************************************************************************/
+
+static const struct {
+ uint32 lpq_status;
+ uint32 spoolss_status;
+} lpq_to_spoolss_status_map[] = {
+ { LPQ_QUEUED, JOB_STATUS_QUEUED },
+ { LPQ_PAUSED, JOB_STATUS_PAUSED },
+ { LPQ_SPOOLING, JOB_STATUS_SPOOLING },
+ { LPQ_PRINTING, JOB_STATUS_PRINTING },
+ { LPQ_DELETING, JOB_STATUS_DELETING },
+ { LPQ_OFFLINE, JOB_STATUS_OFFLINE },
+ { LPQ_PAPEROUT, JOB_STATUS_PAPEROUT },
+ { LPQ_PRINTED, JOB_STATUS_PRINTED },
+ { LPQ_DELETED, JOB_STATUS_DELETED },
+ { LPQ_BLOCKED, JOB_STATUS_BLOCKED },
+ { LPQ_USER_INTERVENTION, JOB_STATUS_USER_INTERVENTION },
+ { -1, 0 }
+};
+
+/* Convert a lpq status value stored in printing.tdb into the
+ appropriate win32 API constant. */
+
+static uint32 map_to_spoolss_status(uint32 lpq_status)
+{
+ int i = 0;
+
+ while (lpq_to_spoolss_status_map[i].lpq_status != -1) {
+ if (lpq_to_spoolss_status_map[i].lpq_status == lpq_status)
+ return lpq_to_spoolss_status_map[i].spoolss_status;
+ i++;
+ }
+
+ return 0;
+}
+
+static void pjob_store_notify(const char* sharename, uint32 jobid, struct printjob *old_data,
+ struct printjob *new_data)
+{
+ bool new_job = False;
+
+ if (!old_data)
+ new_job = True;
+
+ /* Job attributes that can't be changed. We only send
+ notification for these on a new job. */
+
+ /* ACHTUNG! Due to a bug in Samba's spoolss parsing of the
+ NOTIFY_INFO_DATA buffer, we *have* to send the job submission
+ time first or else we'll end up with potential alignment
+ errors. I don't think the systemtime should be spooled as
+ a string, but this gets us around that error.
+ --jerry (i'll feel dirty for this) */
+
+ if (new_job) {
+ notify_job_submitted(sharename, jobid, new_data->starttime);
+ notify_job_username(sharename, jobid, new_data->user);
+ }
+
+ if (new_job || !strequal(old_data->jobname, new_data->jobname))
+ notify_job_name(sharename, jobid, new_data->jobname);
+
+ /* Job attributes of a new job or attributes that can be
+ modified. */
+
+ if (new_job || !strequal(old_data->jobname, new_data->jobname))
+ notify_job_name(sharename, jobid, new_data->jobname);
+
+ if (new_job || old_data->status != new_data->status)
+ notify_job_status(sharename, jobid, map_to_spoolss_status(new_data->status));
+
+ if (new_job || old_data->size != new_data->size)
+ notify_job_total_bytes(sharename, jobid, new_data->size);
+
+ if (new_job || old_data->page_count != new_data->page_count)
+ notify_job_total_pages(sharename, jobid, new_data->page_count);
+}
+
+/****************************************************************************
+ Store a job structure back to the database.
+****************************************************************************/
+
+static bool pjob_store(const char* sharename, uint32 jobid, struct printjob *pjob)
+{
+ uint32_t tmp;
+ TDB_DATA old_data, new_data;
+ bool ret = False;
+ struct tdb_print_db *pdb = get_print_db_byname(sharename);
+ uint8 *buf = NULL;
+ int len, newlen, buflen;
+
+
+ if (!pdb)
+ return False;
+
+ /* Get old data */
+
+ old_data = tdb_fetch(pdb->tdb, print_key(jobid, &tmp));
+
+ /* Doh! Now we have to pack/unpack data since the NT_DEVICEMODE was added */
+
+ newlen = 0;
+
+ do {
+ len = 0;
+ buflen = newlen;
+ len += tdb_pack(buf+len, buflen-len, "dddddddddffff",
+ (uint32)pjob->pid,
+ (uint32)pjob->sysjob,
+ (uint32)pjob->fd,
+ (uint32)pjob->starttime,
+ (uint32)pjob->status,
+ (uint32)pjob->size,
+ (uint32)pjob->page_count,
+ (uint32)pjob->spooled,
+ (uint32)pjob->smbjob,
+ pjob->filename,
+ pjob->jobname,
+ pjob->user,
+ pjob->queuename);
+
+ len += pack_devicemode(pjob->nt_devmode, buf+len, buflen-len);
+
+ if (buflen != len) {
+ buf = (uint8 *)SMB_REALLOC(buf, len);
+ if (!buf) {
+ DEBUG(0,("pjob_store: failed to enlarge buffer!\n"));
+ goto done;
+ }
+ newlen = len;
+ }
+ } while ( buflen != len );
+
+
+ /* Store new data */
+
+ new_data.dptr = buf;
+ new_data.dsize = len;
+ ret = (tdb_store(pdb->tdb, print_key(jobid, &tmp), new_data,
+ TDB_REPLACE) == 0);
+
+ release_print_db(pdb);
+
+ /* Send notify updates for what has changed */
+
+ if ( ret ) {
+ struct printjob old_pjob;
+
+ if ( old_data.dsize )
+ {
+ if ( unpack_pjob( old_data.dptr, old_data.dsize, &old_pjob ) != -1 )
+ {
+ pjob_store_notify( sharename, jobid, &old_pjob , pjob );
+ free_nt_devicemode( &old_pjob.nt_devmode );
+ }
+ }
+ else {
+ /* new job */
+ pjob_store_notify( sharename, jobid, NULL, pjob );
+ }
+ }
+
+done:
+ SAFE_FREE( old_data.dptr );
+ SAFE_FREE( buf );
+
+ return ret;
+}
+
+/****************************************************************************
+ Remove a job structure from the database.
+****************************************************************************/
+
+void pjob_delete(const char* sharename, uint32 jobid)
+{
+ uint32_t tmp;
+ struct printjob *pjob;
+ uint32 job_status = 0;
+ struct tdb_print_db *pdb;
+
+ pdb = get_print_db_byname( sharename );
+
+ if (!pdb)
+ return;
+
+ pjob = print_job_find( sharename, jobid );
+
+ if (!pjob) {
+ DEBUG(5, ("pjob_delete: we were asked to delete nonexistent job %u\n",
+ (unsigned int)jobid));
+ release_print_db(pdb);
+ return;
+ }
+
+ /* We must cycle through JOB_STATUS_DELETING and
+ JOB_STATUS_DELETED for the port monitor to delete the job
+ properly. */
+
+ job_status = JOB_STATUS_DELETING|JOB_STATUS_DELETED;
+ notify_job_status(sharename, jobid, job_status);
+
+ /* Remove from printing.tdb */
+
+ tdb_delete(pdb->tdb, print_key(jobid, &tmp));
+ remove_from_jobs_changed(sharename, jobid);
+ release_print_db( pdb );
+ rap_jobid_delete(sharename, jobid);
+}
+
+/****************************************************************************
+ Parse a file name from the system spooler to generate a jobid.
+****************************************************************************/
+
+static uint32 print_parse_jobid(char *fname)
+{
+ int jobid;
+
+ if (strncmp(fname,PRINT_SPOOL_PREFIX,strlen(PRINT_SPOOL_PREFIX)) != 0)
+ return (uint32)-1;
+ fname += strlen(PRINT_SPOOL_PREFIX);
+
+ jobid = atoi(fname);
+ if (jobid <= 0)
+ return (uint32)-1;
+
+ return (uint32)jobid;
+}
+
+/****************************************************************************
+ List a unix job in the print database.
+****************************************************************************/
+
+static void print_unix_job(const char *sharename, print_queue_struct *q, uint32 jobid)
+{
+ struct printjob pj, *old_pj;
+
+ if (jobid == (uint32)-1)
+ jobid = q->job + UNIX_JOB_START;
+
+ /* Preserve the timestamp on an existing unix print job */
+
+ old_pj = print_job_find(sharename, 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;
+ fstrcpy(pj.filename, old_pj ? old_pj->filename : "");
+ if (jobid < UNIX_JOB_START) {
+ pj.smbjob = True;
+ fstrcpy(pj.jobname, old_pj ? old_pj->jobname : "Remote Downlevel Document");
+ } else {
+ pj.smbjob = False;
+ fstrcpy(pj.jobname, old_pj ? old_pj->jobname : q->fs_file);
+ }
+ fstrcpy(pj.user, old_pj ? old_pj->user : q->fs_user);
+ fstrcpy(pj.queuename, old_pj ? old_pj->queuename : sharename );
+
+ pjob_store(sharename, jobid, &pj);
+}
+
+
+struct traverse_struct {
+ print_queue_struct *queue;
+ int qcount, snum, maxcount, total_jobs;
+ const char *sharename;
+ time_t lpq_time;
+ const char *lprm_command;
+ struct printif *print_if;
+};
+
+/****************************************************************************
+ 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;
+ uint32 jobid;
+ int i = 0;
+
+ if ( key.dsize != sizeof(jobid) )
+ return 0;
+
+ jobid = IVAL(key.dptr, 0);
+ if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 )
+ return 0;
+ free_nt_devicemode( &pjob.nt_devmode );
+
+
+ if (!pjob.smbjob) {
+ /* remove a unix job if it isn't in the system queue any more */
+
+ for (i=0;i<ts->qcount;i++) {
+ uint32 u_jobid = (ts->queue[i].job + UNIX_JOB_START);
+ if (jobid == u_jobid)
+ break;
+ }
+ if (i == ts->qcount) {
+ DEBUG(10,("traverse_fn_delete: pjob %u deleted due to !smbjob\n",
+ (unsigned int)jobid ));
+ pjob_delete(ts->sharename, jobid);
+ return 0;
+ }
+
+ /* need to continue the the bottom of the function to
+ save the correct attributes */
+ }
+
+ /* 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_by_pid(pjob.pid)) {
+ DEBUG(10,("traverse_fn_delete: pjob %u deleted due to !process_exists (%u)\n",
+ (unsigned int)jobid, (unsigned int)pjob.pid ));
+ pjob_delete(ts->sharename, jobid);
+ } else
+ ts->total_jobs++;
+ return 0;
+ }
+
+ /* this check only makes sense for jobs submitted from Windows clients */
+
+ if ( pjob.smbjob ) {
+ for (i=0;i<ts->qcount;i++) {
+ uint32 curr_jobid;
+
+ if ( pjob.status == LPQ_DELETED )
+ continue;
+
+ curr_jobid = print_parse_jobid(ts->queue[i].fs_file);
+
+ if (jobid == curr_jobid) {
+
+ /* try to clean up any jobs that need to be deleted */
+
+ if ( pjob.status == LPQ_DELETING ) {
+ int result;
+
+ result = (*(ts->print_if->job_delete))(
+ ts->sharename, ts->lprm_command, &pjob );
+
+ if ( result != 0 ) {
+ /* if we can't delete, then reset the job status */
+ pjob.status = LPQ_QUEUED;
+ pjob_store(ts->sharename, jobid, &pjob);
+ }
+ else {
+ /* if we deleted the job, the remove the tdb record */
+ pjob_delete(ts->sharename, jobid);
+ pjob.status = LPQ_DELETED;
+ }
+
+ }
+
+ 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) {
+
+ /* 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.
+ Don't delete the job if it was submitted after the lpq_time. */
+
+ if (pjob.starttime < ts->lpq_time) {
+ DEBUG(10,("traverse_fn_delete: pjob %u deleted due to pjob.starttime (%u) < ts->lpq_time (%u)\n",
+ (unsigned int)jobid,
+ (unsigned int)pjob.starttime,
+ (unsigned int)ts->lpq_time ));
+ pjob_delete(ts->sharename, jobid);
+ } else
+ ts->total_jobs++;
+ return 0;
+ }
+
+ /* Save the pjob attributes we will store.
+ FIXME!!! This is the only place where queue->job
+ represents the SMB jobid --jerry */
+
+ 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->total_jobs++;
+
+ return 0;
+}
+
+/****************************************************************************
+ Check if the print queue has been updated recently enough.
+****************************************************************************/
+
+static void print_cache_flush(const char *sharename)
+{
+ fstring key;
+ struct tdb_print_db *pdb = get_print_db_byname(sharename);
+
+ if (!pdb)
+ return;
+ slprintf(key, sizeof(key)-1, "CACHE/%s", sharename);
+ tdb_store_int32(pdb->tdb, key, -1);
+ release_print_db(pdb);
+}
+
+/****************************************************************************
+ Check if someone already thinks they are doing the update.
+****************************************************************************/
+
+static pid_t get_updating_pid(const char *sharename)
+{
+ fstring keystr;
+ TDB_DATA data, key;
+ pid_t updating_pid;
+ struct tdb_print_db *pdb = get_print_db_byname(sharename);
+
+ if (!pdb)
+ return (pid_t)-1;
+ slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", sharename);
+ key = string_tdb_data(keystr);
+
+ data = tdb_fetch(pdb->tdb, key);
+ release_print_db(pdb);
+ if (!data.dptr || data.dsize != sizeof(pid_t)) {
+ SAFE_FREE(data.dptr);
+ return (pid_t)-1;
+ }
+
+ updating_pid = IVAL(data.dptr, 0);
+ SAFE_FREE(data.dptr);
+
+ if (process_exists_by_pid(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 the tdb.
+****************************************************************************/
+
+static void set_updating_pid(const fstring sharename, bool updating)
+{
+ fstring keystr;
+ TDB_DATA key;
+ TDB_DATA data;
+ pid_t updating_pid = sys_getpid();
+ uint8 buffer[4];
+
+ struct tdb_print_db *pdb = get_print_db_byname(sharename);
+
+ if (!pdb)
+ return;
+
+ slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", sharename);
+ key = string_tdb_data(keystr);
+
+ DEBUG(5, ("set_updating_pid: %s updating lpq cache for print share %s\n",
+ updating ? "" : "not ",
+ sharename ));
+
+ if ( !updating ) {
+ tdb_delete(pdb->tdb, key);
+ release_print_db(pdb);
+ return;
+ }
+
+ SIVAL( buffer, 0, updating_pid);
+ data.dptr = buffer;
+ data.dsize = 4; /* we always assume this is a 4 byte value */
+
+ tdb_store(pdb->tdb, key, data, TDB_REPLACE);
+ release_print_db(pdb);
+}
+
+/****************************************************************************
+ 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;
+}
+
+/****************************************************************************
+ Store the sorted queue representation for later portmon retrieval.
+ Skip deleted jobs
+****************************************************************************/
+
+static void store_queue_struct(struct tdb_print_db *pdb, struct traverse_struct *pts)
+{
+ TDB_DATA data;
+ int max_reported_jobs = lp_max_reported_jobs(pts->snum);
+ print_queue_struct *queue = pts->queue;
+ size_t len;
+ size_t i;
+ unsigned int qcount;
+
+ if (max_reported_jobs && (max_reported_jobs < pts->qcount))
+ pts->qcount = max_reported_jobs;
+ qcount = 0;
+
+ /* Work out the size. */
+ data.dsize = 0;
+ data.dsize += tdb_pack(NULL, 0, "d", qcount);
+
+ for (i = 0; i < pts->qcount; i++) {
+ if ( queue[i].status == LPQ_DELETED )
+ continue;
+
+ qcount++;
+ data.dsize += tdb_pack(NULL, 0, "ddddddff",
+ (uint32)queue[i].job,
+ (uint32)queue[i].size,
+ (uint32)queue[i].page_count,
+ (uint32)queue[i].status,
+ (uint32)queue[i].priority,
+ (uint32)queue[i].time,
+ queue[i].fs_user,
+ queue[i].fs_file);
+ }
+
+ if ((data.dptr = (uint8 *)SMB_MALLOC(data.dsize)) == NULL)
+ return;
+
+ len = 0;
+ len += tdb_pack(data.dptr + len, data.dsize - len, "d", qcount);
+ for (i = 0; i < pts->qcount; i++) {
+ if ( queue[i].status == LPQ_DELETED )
+ continue;
+
+ len += tdb_pack(data.dptr + len, data.dsize - len, "ddddddff",
+ (uint32)queue[i].job,
+ (uint32)queue[i].size,
+ (uint32)queue[i].page_count,
+ (uint32)queue[i].status,
+ (uint32)queue[i].priority,
+ (uint32)queue[i].time,
+ queue[i].fs_user,
+ queue[i].fs_file);
+ }
+
+ tdb_store(pdb->tdb, string_tdb_data("INFO/linear_queue_array"), data,
+ TDB_REPLACE);
+ SAFE_FREE(data.dptr);
+ return;
+}
+
+static TDB_DATA get_jobs_changed_data(struct tdb_print_db *pdb)
+{
+ TDB_DATA data;
+
+ ZERO_STRUCT(data);
+
+ data = tdb_fetch(pdb->tdb, string_tdb_data("INFO/jobs_changed"));
+ if (data.dptr == NULL || data.dsize == 0 || (data.dsize % 4 != 0)) {
+ SAFE_FREE(data.dptr);
+ ZERO_STRUCT(data);
+ }
+
+ return data;
+}
+
+static void check_job_changed(const char *sharename, TDB_DATA data, uint32 jobid)
+{
+ unsigned int i;
+ unsigned int job_count = data.dsize / 4;
+
+ for (i = 0; i < job_count; i++) {
+ uint32 ch_jobid;
+
+ ch_jobid = IVAL(data.dptr, i*4);
+ if (ch_jobid == jobid)
+ remove_from_jobs_changed(sharename, jobid);
+ }
+}
+
+/****************************************************************************
+ Check if the print queue has been updated recently enough.
+****************************************************************************/
+
+static bool print_cache_expired(const char *sharename, bool check_pending)
+{
+ fstring key;
+ time_t last_qscan_time, time_now = time(NULL);
+ struct tdb_print_db *pdb = get_print_db_byname(sharename);
+ bool result = False;
+
+ if (!pdb)
+ return False;
+
+ snprintf(key, sizeof(key), "CACHE/%s", sharename);
+ last_qscan_time = (time_t)tdb_fetch_int32(pdb->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))
+ {
+ uint32 u;
+ time_t msg_pending_time;
+
+ DEBUG(4, ("print_cache_expired: cache expired for queue %s "
+ "(last_qscan_time = %d, time now = %d, qcachetime = %d)\n",
+ sharename, (int)last_qscan_time, (int)time_now,
+ (int)lp_lpqcachetime() ));
+
+ /* check if another smbd has already sent a message to update the
+ queue. Give the pending message one minute to clear and
+ then send another message anyways. Make sure to check for
+ clocks that have been run forward and then back again. */
+
+ snprintf(key, sizeof(key), "MSG_PENDING/%s", sharename);
+
+ if ( check_pending
+ && tdb_fetch_uint32( pdb->tdb, key, &u )
+ && (msg_pending_time=u) > 0
+ && msg_pending_time <= time_now
+ && (time_now - msg_pending_time) < 60 )
+ {
+ DEBUG(4,("print_cache_expired: message already pending for %s. Accepting cache\n",
+ sharename));
+ goto done;
+ }
+
+ result = True;
+ }
+
+done:
+ release_print_db(pdb);
+ return result;
+}
+
+/****************************************************************************
+ main work for updating the lpq cahe for a printer queue
+****************************************************************************/
+
+static void print_queue_update_internal( const char *sharename,
+ struct printif *current_printif,
+ char *lpq_command, char *lprm_command )
+{
+ int i, qcount;
+ print_queue_struct *queue = NULL;
+ print_status_struct status;
+ print_status_struct old_status;
+ struct printjob *pjob;
+ struct traverse_struct tstruct;
+ TDB_DATA data, key;
+ TDB_DATA jcdata;
+ fstring keystr, cachestr;
+ struct tdb_print_db *pdb = get_print_db_byname(sharename);
+
+ if (!pdb) {
+ return;
+ }
+
+ DEBUG(5,("print_queue_update_internal: printer = %s, type = %d, lpq command = [%s]\n",
+ sharename, current_printif->type, lpq_command));
+
+ /*
+ * 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", sharename);
+ tdb_store_int32(pdb->tdb, cachestr, (int)time(NULL));
+
+ /* get the current queue using the appropriate interface */
+ ZERO_STRUCT(status);
+
+ qcount = (*(current_printif->queue_get))(sharename,
+ current_printif->type,
+ lpq_command, &queue, &status);
+
+ DEBUG(3, ("print_queue_update_internal: %d job%s in queue for %s\n",
+ qcount, (qcount != 1) ? "s" : "", sharename));
+
+ /* Sort the queue by submission time otherwise they are displayed
+ in hash order. */
+
+ qsort(queue, qcount, sizeof(print_queue_struct),
+ QSORT_CAST(printjob_comp));
+
+ /*
+ 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
+ */
+
+ jcdata = get_jobs_changed_data(pdb);
+
+ for (i=0; i<qcount; i++) {
+ uint32 jobid = print_parse_jobid(queue[i].fs_file);
+
+ if (jobid == (uint32)-1) {
+ /* assume its a unix print job */
+ print_unix_job(sharename, &queue[i], jobid);
+ continue;
+ }
+
+ /* we have an active SMB print job - update its status */
+ pjob = print_job_find(sharename, 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(sharename, &queue[i], jobid);
+ continue;
+ }
+
+ pjob->sysjob = queue[i].job;
+
+ /* don't reset the status on jobs to be deleted */
+
+ if ( pjob->status != LPQ_DELETING )
+ pjob->status = queue[i].status;
+
+ pjob_store(sharename, jobid, pjob);
+
+ check_job_changed(sharename, jcdata, jobid);
+ }
+
+ SAFE_FREE(jcdata.dptr);
+
+ /* now delete any queued entries that don't appear in the
+ system queue */
+ tstruct.queue = queue;
+ tstruct.qcount = qcount;
+ tstruct.snum = -1;
+ tstruct.total_jobs = 0;
+ tstruct.lpq_time = time(NULL);
+ tstruct.sharename = sharename;
+ tstruct.lprm_command = lprm_command;
+ tstruct.print_if = current_printif;
+
+ tdb_traverse(pdb->tdb, traverse_fn_delete, (void *)&tstruct);
+
+ /* Store the linearised queue, max jobs only. */
+ store_queue_struct(pdb, &tstruct);
+
+ SAFE_FREE(tstruct.queue);
+
+ DEBUG(10,("print_queue_update_internal: printer %s INFO/total_jobs = %d\n",
+ sharename, tstruct.total_jobs ));
+
+ tdb_store_int32(pdb->tdb, "INFO/total_jobs", tstruct.total_jobs);
+
+ get_queue_status(sharename, &old_status);
+ if (old_status.qcount != qcount)
+ DEBUG(10,("print_queue_update_internal: queue status change %d jobs -> %d jobs for printer %s\n",
+ old_status.qcount, qcount, sharename));
+
+ /* store the new queue status structure */
+ slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", sharename);
+ key = string_tdb_data(keystr);
+
+ status.qcount = qcount;
+ data.dptr = (uint8 *)&status;
+ data.dsize = sizeof(status);
+ tdb_store(pdb->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", sharename);
+ tdb_store_int32(pdb->tdb, keystr, (int32)time(NULL));
+
+ /* clear the msg pending record for this queue */
+
+ snprintf(keystr, sizeof(keystr), "MSG_PENDING/%s", sharename);
+
+ if ( !tdb_store_uint32( pdb->tdb, keystr, 0 ) ) {
+ /* log a message but continue on */
+
+ DEBUG(0,("print_queue_update: failed to store MSG_PENDING flag for [%s]!\n",
+ sharename));
+ }
+
+ release_print_db( pdb );
+
+ return;
+}
+
+/****************************************************************************
+ Update the internal database from the system print queue for a queue.
+ obtain a lock on the print queue before proceeding (needed when mutiple
+ smbd processes maytry to update the lpq cache concurrently).
+****************************************************************************/
+
+static void print_queue_update_with_lock( const char *sharename,
+ struct printif *current_printif,
+ char *lpq_command, char *lprm_command )
+{
+ fstring keystr;
+ struct tdb_print_db *pdb;
+
+ DEBUG(5,("print_queue_update_with_lock: printer share = %s\n", sharename));
+ pdb = get_print_db_byname(sharename);
+ if (!pdb)
+ return;
+
+ if ( !print_cache_expired(sharename, False) ) {
+ DEBUG(5,("print_queue_update_with_lock: print cache for %s is still ok\n", sharename));
+ release_print_db(pdb);
+ return;
+ }
+
+ /*
+ * Check to see if someone else is doing this update.
+ * This is essentially a mutex on the update.
+ */
+
+ if (get_updating_pid(sharename) != -1) {
+ release_print_db(pdb);
+ return;
+ }
+
+ /* Lock the queue for the database update */
+
+ slprintf(keystr, sizeof(keystr) - 1, "LOCK/%s", sharename);
+ /* Only wait 10 seconds for this. */
+ if (tdb_lock_bystring_with_timeout(pdb->tdb, keystr, 10) == -1) {
+ DEBUG(0,("print_queue_update_with_lock: Failed to lock printer %s database\n", sharename));
+ release_print_db(pdb);
+ return;
+ }
+
+ /*
+ * Ensure that no one else got in here.
+ * If the updating pid is still -1 then we are
+ * the winner.
+ */
+
+ if (get_updating_pid(sharename) != -1) {
+ /*
+ * Someone else is doing the update, exit.
+ */
+ tdb_unlock_bystring(pdb->tdb, keystr);
+ release_print_db(pdb);
+ return;
+ }
+
+ /*
+ * We're going to do the update ourselves.
+ */
+
+ /* Tell others we're doing the update. */
+ set_updating_pid(sharename, True);
+
+ /*
+ * Allow others to enter and notice we're doing
+ * the update.
+ */
+
+ tdb_unlock_bystring(pdb->tdb, keystr);
+
+ /* do the main work now */
+
+ print_queue_update_internal( sharename, current_printif,
+ lpq_command, lprm_command );
+
+ /* Delete our pid from the db. */
+ set_updating_pid(sharename, False);
+ release_print_db(pdb);
+}
+
+/****************************************************************************
+this is the receive function of the background lpq updater
+****************************************************************************/
+static void print_queue_receive(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ fstring sharename;
+ char *lpqcommand = NULL, *lprmcommand = NULL;
+ int printing_type;
+ size_t len;
+
+ len = tdb_unpack( (uint8 *)data->data, data->length, "fdPP",
+ sharename,
+ &printing_type,
+ &lpqcommand,
+ &lprmcommand );
+
+ if ( len == -1 ) {
+ SAFE_FREE(lpqcommand);
+ SAFE_FREE(lprmcommand);
+ DEBUG(0,("print_queue_receive: Got invalid print queue update message\n"));
+ return;
+ }
+
+ print_queue_update_with_lock(sharename,
+ get_printer_fns_from_type((enum printing_types)printing_type),
+ lpqcommand, lprmcommand );
+
+ SAFE_FREE(lpqcommand);
+ SAFE_FREE(lprmcommand);
+ return;
+}
+
+static pid_t background_lpq_updater_pid = -1;
+
+/****************************************************************************
+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"));
+
+ if (!reinit_after_fork(smbd_messaging_context(), true)) {
+ DEBUG(0,("reinit_after_fork() failed\n"));
+ smb_panic("reinit_after_fork() failed");
+ }
+
+ claim_connection( NULL, "smbd lpq backend",
+ FLAG_MSG_GENERAL|FLAG_MSG_SMBD|FLAG_MSG_PRINT_GENERAL);
+
+ if (!locking_init()) {
+ exit(1);
+ }
+
+ messaging_register(smbd_messaging_context(), NULL,
+ MSG_PRINTER_UPDATE, print_queue_receive);
+
+ DEBUG(5,("start_background_queue: background LPQ thread waiting for messages\n"));
+ while (1) {
+ pause();
+
+ /* check for some essential signals first */
+
+ if (got_sig_term) {
+ exit_server_cleanly(NULL);
+ }
+
+ if (reload_after_sighup) {
+ change_to_root_user();
+ DEBUG(1,("Reloading services after SIGHUP\n"));
+ reload_services(False);
+ reload_after_sighup = 0;
+ }
+
+ /* now check for messages */
+
+ DEBUG(10,("start_background_queue: background LPQ thread got a message\n"));
+ message_dispatch(smbd_messaging_context());
+
+ /* process any pending print change notify messages */
+
+ print_notify_send_messages(smbd_messaging_context(),
+ 0);
+ }
+ }
+}
+
+/****************************************************************************
+update the internal database from the system print queue for a queue
+****************************************************************************/
+
+static void print_queue_update(int snum, bool force)
+{
+ fstring key;
+ fstring sharename;
+ char *lpqcommand = NULL;
+ char *lprmcommand = NULL;
+ uint8 *buffer = NULL;
+ size_t len = 0;
+ size_t newlen;
+ struct tdb_print_db *pdb;
+ int type;
+ struct printif *current_printif;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ fstrcpy( sharename, lp_const_servicename(snum));
+
+ /* don't strip out characters like '$' from the printername */
+
+ lpqcommand = talloc_string_sub2(ctx,
+ lp_lpqcommand(snum),
+ "%p",
+ PRINTERNAME(snum),
+ false, false, false);
+ if (!lpqcommand) {
+ return;
+ }
+ lpqcommand = talloc_sub_advanced(ctx,
+ lp_servicename(snum),
+ current_user_info.unix_name,
+ "",
+ current_user.ut.gid,
+ get_current_username(),
+ current_user_info.domain,
+ lpqcommand);
+ if (!lpqcommand) {
+ return;
+ }
+
+ lprmcommand = talloc_string_sub2(ctx,
+ lp_lprmcommand(snum),
+ "%p",
+ PRINTERNAME(snum),
+ false, false, false);
+ if (!lprmcommand) {
+ return;
+ }
+ lprmcommand = talloc_sub_advanced(ctx,
+ lp_servicename(snum),
+ current_user_info.unix_name,
+ "",
+ current_user.ut.gid,
+ get_current_username(),
+ current_user_info.domain,
+ lprmcommand);
+ if (!lprmcommand) {
+ return;
+ }
+
+ /*
+ * Make sure that the background queue process exists.
+ * Otherwise just do the update ourselves
+ */
+
+ if ( force || background_lpq_updater_pid == -1 ) {
+ DEBUG(4,("print_queue_update: updating queue [%s] myself\n", sharename));
+ current_printif = get_printer_fns( snum );
+ print_queue_update_with_lock( sharename, current_printif, lpqcommand, lprmcommand );
+
+ return;
+ }
+
+ type = lp_printing(snum);
+
+ /* get the length */
+
+ len = tdb_pack( NULL, 0, "fdPP",
+ sharename,
+ type,
+ lpqcommand,
+ lprmcommand );
+
+ buffer = SMB_XMALLOC_ARRAY( uint8, len );
+
+ /* now pack the buffer */
+ newlen = tdb_pack( buffer, len, "fdPP",
+ sharename,
+ type,
+ lpqcommand,
+ lprmcommand );
+
+ SMB_ASSERT( newlen == len );
+
+ DEBUG(10,("print_queue_update: Sending message -> printer = %s, "
+ "type = %d, lpq command = [%s] lprm command = [%s]\n",
+ sharename, type, lpqcommand, lprmcommand ));
+
+ /* here we set a msg pending record for other smbd processes
+ to throttle the number of duplicate print_queue_update msgs
+ sent. */
+
+ pdb = get_print_db_byname(sharename);
+ if (!pdb) {
+ SAFE_FREE(buffer);
+ return;
+ }
+
+ snprintf(key, sizeof(key), "MSG_PENDING/%s", sharename);
+
+ if ( !tdb_store_uint32( pdb->tdb, key, time(NULL) ) ) {
+ /* log a message but continue on */
+
+ DEBUG(0,("print_queue_update: failed to store MSG_PENDING flag for [%s]!\n",
+ sharename));
+ }
+
+ release_print_db( pdb );
+
+ /* finally send the message */
+
+ messaging_send_buf(smbd_messaging_context(),
+ pid_to_procid(background_lpq_updater_pid),
+ MSG_PRINTER_UPDATE, (uint8 *)buffer, len);
+
+ SAFE_FREE( buffer );
+
+ return;
+}
+
+/****************************************************************************
+ Create/Update an entry in the print tdb that will allow us to send notify
+ updates only to interested smbd's.
+****************************************************************************/
+
+bool print_notify_register_pid(int snum)
+{
+ TDB_DATA data;
+ struct tdb_print_db *pdb = NULL;
+ TDB_CONTEXT *tdb = NULL;
+ const char *printername;
+ uint32 mypid = (uint32)sys_getpid();
+ bool ret = False;
+ size_t i;
+
+ /* if (snum == -1), then the change notify request was
+ on a print server handle and we need to register on
+ all print queus */
+
+ if (snum == -1)
+ {
+ int num_services = lp_numservices();
+ int idx;
+
+ for ( idx=0; idx<num_services; idx++ ) {
+ if (lp_snum_ok(idx) && lp_print_ok(idx) )
+ print_notify_register_pid(idx);
+ }
+
+ return True;
+ }
+ else /* register for a specific printer */
+ {
+ printername = lp_const_servicename(snum);
+ pdb = get_print_db_byname(printername);
+ if (!pdb)
+ return False;
+ tdb = pdb->tdb;
+ }
+
+ if (tdb_lock_bystring_with_timeout(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
+ DEBUG(0,("print_notify_register_pid: Failed to lock printer %s\n",
+ printername));
+ if (pdb)
+ release_print_db(pdb);
+ return False;
+ }
+
+ data = get_printer_notify_pid_list( tdb, printername, True );
+
+ /* Add ourselves and increase the refcount. */
+
+ for (i = 0; i < data.dsize; i += 8) {
+ if (IVAL(data.dptr,i) == mypid) {
+ uint32 new_refcount = IVAL(data.dptr, i+4) + 1;
+ SIVAL(data.dptr, i+4, new_refcount);
+ break;
+ }
+ }
+
+ if (i == data.dsize) {
+ /* We weren't in the list. Realloc. */
+ data.dptr = (uint8 *)SMB_REALLOC(data.dptr, data.dsize + 8);
+ if (!data.dptr) {
+ DEBUG(0,("print_notify_register_pid: Relloc fail for printer %s\n",
+ printername));
+ goto done;
+ }
+ data.dsize += 8;
+ SIVAL(data.dptr,data.dsize - 8,mypid);
+ SIVAL(data.dptr,data.dsize - 4,1); /* Refcount. */
+ }
+
+ /* Store back the record. */
+ if (tdb_store_bystring(tdb, NOTIFY_PID_LIST_KEY, data, TDB_REPLACE) == -1) {
+ DEBUG(0,("print_notify_register_pid: Failed to update pid \
+list for printer %s\n", printername));
+ goto done;
+ }
+
+ ret = True;
+
+ done:
+
+ tdb_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
+ if (pdb)
+ release_print_db(pdb);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+/****************************************************************************
+ Update an entry in the print tdb that will allow us to send notify
+ updates only to interested smbd's.
+****************************************************************************/
+
+bool print_notify_deregister_pid(int snum)
+{
+ TDB_DATA data;
+ struct tdb_print_db *pdb = NULL;
+ TDB_CONTEXT *tdb = NULL;
+ const char *printername;
+ uint32 mypid = (uint32)sys_getpid();
+ size_t i;
+ bool ret = False;
+
+ /* if ( snum == -1 ), we are deregister a print server handle
+ which means to deregister on all print queues */
+
+ if (snum == -1)
+ {
+ int num_services = lp_numservices();
+ int idx;
+
+ for ( idx=0; idx<num_services; idx++ ) {
+ if ( lp_snum_ok(idx) && lp_print_ok(idx) )
+ print_notify_deregister_pid(idx);
+ }
+
+ return True;
+ }
+ else /* deregister a specific printer */
+ {
+ printername = lp_const_servicename(snum);
+ pdb = get_print_db_byname(printername);
+ if (!pdb)
+ return False;
+ tdb = pdb->tdb;
+ }
+
+ if (tdb_lock_bystring_with_timeout(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
+ DEBUG(0,("print_notify_register_pid: Failed to lock \
+printer %s database\n", printername));
+ if (pdb)
+ release_print_db(pdb);
+ return False;
+ }
+
+ data = get_printer_notify_pid_list( tdb, printername, True );
+
+ /* Reduce refcount. Remove ourselves if zero. */
+
+ for (i = 0; i < data.dsize; ) {
+ if (IVAL(data.dptr,i) == mypid) {
+ uint32 refcount = IVAL(data.dptr, i+4);
+
+ refcount--;
+
+ if (refcount == 0) {
+ if (data.dsize - i > 8)
+ memmove( &data.dptr[i], &data.dptr[i+8], data.dsize - i - 8);
+ data.dsize -= 8;
+ continue;
+ }
+ SIVAL(data.dptr, i+4, refcount);
+ }
+
+ i += 8;
+ }
+
+ if (data.dsize == 0)
+ SAFE_FREE(data.dptr);
+
+ /* Store back the record. */
+ if (tdb_store_bystring(tdb, NOTIFY_PID_LIST_KEY, data, TDB_REPLACE) == -1) {
+ DEBUG(0,("print_notify_register_pid: Failed to update pid \
+list for printer %s\n", printername));
+ goto done;
+ }
+
+ ret = True;
+
+ done:
+
+ tdb_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
+ if (pdb)
+ release_print_db(pdb);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+/****************************************************************************
+ Check if a jobid is valid. It is valid if it exists in the database.
+****************************************************************************/
+
+bool print_job_exists(const char* sharename, uint32 jobid)
+{
+ struct tdb_print_db *pdb = get_print_db_byname(sharename);
+ bool ret;
+ uint32_t tmp;
+
+ if (!pdb)
+ return False;
+ ret = tdb_exists(pdb->tdb, print_key(jobid, &tmp));
+ release_print_db(pdb);
+ return ret;
+}
+
+/****************************************************************************
+ Give the fd used for a jobid.
+****************************************************************************/
+
+int print_job_fd(const char* sharename, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(sharename, jobid);
+ if (!pjob)
+ return -1;
+ /* don't allow another process to get this info - it is meaningless */
+ if (pjob->pid != sys_getpid())
+ 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(const char* sharename, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(sharename, jobid);
+ if (!pjob || pjob->spooled || pjob->pid != sys_getpid())
+ return NULL;
+ return pjob->filename;
+}
+
+
+/****************************************************************************
+ Give the filename used for a jobid.
+ Only valid for the process doing the spooling and when the job
+ has not been spooled.
+****************************************************************************/
+
+NT_DEVICEMODE *print_job_devmode(const char* sharename, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(sharename, jobid);
+
+ if ( !pjob )
+ return NULL;
+
+ return pjob->nt_devmode;
+}
+
+/****************************************************************************
+ Set the place in the queue for a job.
+****************************************************************************/
+
+bool print_job_set_place(const char *sharename, uint32 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(const char *sharename, uint32 jobid, char *name)
+{
+ struct printjob *pjob;
+
+ pjob = print_job_find(sharename, jobid);
+ if (!pjob || pjob->pid != sys_getpid())
+ return False;
+
+ fstrcpy(pjob->jobname, name);
+ return pjob_store(sharename, jobid, pjob);
+}
+
+/***************************************************************************
+ Remove a jobid from the 'jobs changed' list.
+***************************************************************************/
+
+static bool remove_from_jobs_changed(const char* sharename, uint32 jobid)
+{
+ struct tdb_print_db *pdb = get_print_db_byname(sharename);
+ TDB_DATA data, key;
+ size_t job_count, i;
+ bool ret = False;
+ bool gotlock = False;
+
+ if (!pdb) {
+ return False;
+ }
+
+ ZERO_STRUCT(data);
+
+ key = string_tdb_data("INFO/jobs_changed");
+
+ if (tdb_chainlock_with_timeout(pdb->tdb, key, 5) == -1)
+ goto out;
+
+ gotlock = True;
+
+ data = tdb_fetch(pdb->tdb, key);
+
+ if (data.dptr == NULL || data.dsize == 0 || (data.dsize % 4 != 0))
+ goto out;
+
+ job_count = data.dsize / 4;
+ for (i = 0; i < job_count; i++) {
+ uint32 ch_jobid;
+
+ ch_jobid = IVAL(data.dptr, i*4);
+ if (ch_jobid == jobid) {
+ if (i < job_count -1 )
+ memmove(data.dptr + (i*4), data.dptr + (i*4) + 4, (job_count - i - 1)*4 );
+ data.dsize -= 4;
+ if (tdb_store(pdb->tdb, key, data, TDB_REPLACE) == -1)
+ goto out;
+ break;
+ }
+ }
+
+ ret = True;
+ out:
+
+ if (gotlock)
+ tdb_chainunlock(pdb->tdb, key);
+ SAFE_FREE(data.dptr);
+ release_print_db(pdb);
+ if (ret)
+ DEBUG(10,("remove_from_jobs_changed: removed jobid %u\n", (unsigned int)jobid ));
+ else
+ DEBUG(10,("remove_from_jobs_changed: Failed to remove jobid %u\n", (unsigned int)jobid ));
+ return ret;
+}
+
+/****************************************************************************
+ Delete a print job - don't update queue.
+****************************************************************************/
+
+static bool print_job_delete1(int snum, uint32 jobid)
+{
+ const char* sharename = lp_const_servicename(snum);
+ struct printjob *pjob = print_job_find(sharename, jobid);
+ int result = 0;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!pjob)
+ return False;
+
+ /*
+ * If already deleting just return.
+ */
+
+ if (pjob->status == LPQ_DELETING)
+ return True;
+
+ /* Hrm - we need to be able to cope with deleting a job before it
+ has reached the spooler. Just mark it as LPQ_DELETING and
+ let the print_queue_update() code rmeove the record */
+
+
+ if (pjob->sysjob == -1) {
+ DEBUG(5, ("attempt to delete job %u not seen by lpr\n", (unsigned int)jobid));
+ }
+
+ /* Set the tdb entry to be deleting. */
+
+ pjob->status = LPQ_DELETING;
+ pjob_store(sharename, jobid, pjob);
+
+ if (pjob->spooled && pjob->sysjob != -1)
+ {
+ result = (*(current_printif->job_delete))(
+ PRINTERNAME(snum),
+ lp_lprmcommand(snum),
+ pjob);
+
+ /* Delete the tdb entry if the delete succeeded or the job hasn't
+ been spooled. */
+
+ if (result == 0) {
+ struct tdb_print_db *pdb = get_print_db_byname(sharename);
+ int njobs = 1;
+
+ if (!pdb)
+ return False;
+ pjob_delete(sharename, jobid);
+ /* Ensure we keep a rough count of the number of total jobs... */
+ tdb_change_int32_atomic(pdb->tdb, "INFO/total_jobs", &njobs, -1);
+ release_print_db(pdb);
+ }
+ }
+
+ remove_from_jobs_changed( sharename, jobid );
+
+ return (result == 0);
+}
+
+/****************************************************************************
+ Return true if the current user owns the print job.
+****************************************************************************/
+
+static bool is_owner(struct auth_serversupplied_info *server_info,
+ const char *servicename,
+ uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(servicename, jobid);
+
+ if (!pjob || !server_info)
+ return False;
+
+ return strequal(pjob->user, server_info->sanitized_username);
+}
+
+/****************************************************************************
+ Delete a print job.
+****************************************************************************/
+
+bool print_job_delete(struct auth_serversupplied_info *server_info, int snum,
+ uint32 jobid, WERROR *errcode)
+{
+ const char* sharename = lp_const_servicename( snum );
+ struct printjob *pjob;
+ bool owner;
+ char *fname;
+
+ *errcode = WERR_OK;
+
+ owner = is_owner(server_info, lp_const_servicename(snum), jobid);
+
+ /* Check access against security descriptor or whether the user
+ owns their job. */
+
+ if (!owner &&
+ !print_access_check(server_info, snum, JOB_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("delete denied by security descriptor\n"));
+ *errcode = WERR_ACCESS_DENIED;
+
+ /* BEGIN_ADMIN_LOG */
+ sys_adminlog( LOG_ERR,
+ "Permission denied-- user not allowed to delete, \
+pause, or resume print job. User name: %s. Printer name: %s.",
+ uidtoname(server_info->utok.uid),
+ PRINTERNAME(snum) );
+ /* END_ADMIN_LOG */
+
+ return False;
+ }
+
+ /*
+ * get the spooled filename of the print job
+ * if this works, then the file has not been spooled
+ * to the underlying print system. Just delete the
+ * spool file & return.
+ */
+
+ if ( (fname = print_job_fname( sharename, jobid )) != NULL )
+ {
+ /* remove the spool file */
+ DEBUG(10,("print_job_delete: Removing spool file [%s]\n", fname ));
+ if ( unlink( fname ) == -1 ) {
+ *errcode = map_werror_from_unix(errno);
+ return False;
+ }
+ }
+
+ if (!print_job_delete1(snum, jobid)) {
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ /* force update the database and say the delete failed if the
+ job still exists */
+
+ print_queue_update(snum, True);
+
+ pjob = print_job_find(sharename, jobid);
+ if ( pjob && (pjob->status != LPQ_DELETING) )
+ *errcode = WERR_ACCESS_DENIED;
+
+ return (pjob == NULL );
+}
+
+/****************************************************************************
+ Pause a job.
+****************************************************************************/
+
+bool print_job_pause(struct auth_serversupplied_info *server_info, int snum,
+ uint32 jobid, WERROR *errcode)
+{
+ const char* sharename = lp_const_servicename(snum);
+ struct printjob *pjob;
+ int ret = -1;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ pjob = print_job_find(sharename, jobid);
+
+ if (!pjob || !server_info) {
+ DEBUG(10, ("print_job_pause: no pjob or user for jobid %u\n",
+ (unsigned int)jobid ));
+ return False;
+ }
+
+ if (!pjob->spooled || pjob->sysjob == -1) {
+ DEBUG(10, ("print_job_pause: not spooled or bad sysjob = %d for jobid %u\n",
+ (int)pjob->sysjob, (unsigned int)jobid ));
+ return False;
+ }
+
+ if (!is_owner(server_info, lp_const_servicename(snum), jobid) &&
+ !print_access_check(server_info, snum, JOB_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("pause denied by security descriptor\n"));
+
+ /* BEGIN_ADMIN_LOG */
+ sys_adminlog( LOG_ERR,
+ "Permission denied-- user not allowed to delete, \
+pause, or resume print job. User name: %s. Printer name: %s.",
+ uidtoname(server_info->utok.uid),
+ PRINTERNAME(snum) );
+ /* END_ADMIN_LOG */
+
+ *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(lp_const_servicename(snum));
+
+ /* Send a printer notify message */
+
+ notify_job_status(sharename, jobid, JOB_STATUS_PAUSED);
+
+ /* how do we tell if this succeeded? */
+
+ return True;
+}
+
+/****************************************************************************
+ Resume a job.
+****************************************************************************/
+
+bool print_job_resume(struct auth_serversupplied_info *server_info, int snum,
+ uint32 jobid, WERROR *errcode)
+{
+ const char *sharename = lp_const_servicename(snum);
+ struct printjob *pjob;
+ int ret;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ pjob = print_job_find(sharename, jobid);
+
+ if (!pjob || !server_info) {
+ DEBUG(10, ("print_job_resume: no pjob or user for jobid %u\n",
+ (unsigned int)jobid ));
+ return False;
+ }
+
+ if (!pjob->spooled || pjob->sysjob == -1) {
+ DEBUG(10, ("print_job_resume: not spooled or bad sysjob = %d for jobid %u\n",
+ (int)pjob->sysjob, (unsigned int)jobid ));
+ return False;
+ }
+
+ if (!is_owner(server_info, lp_const_servicename(snum), jobid) &&
+ !print_access_check(server_info, snum, JOB_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("resume denied by security descriptor\n"));
+ *errcode = WERR_ACCESS_DENIED;
+
+ /* BEGIN_ADMIN_LOG */
+ sys_adminlog( LOG_ERR,
+ "Permission denied-- user not allowed to delete, \
+pause, or resume print job. User name: %s. Printer name: %s.",
+ uidtoname(server_info->utok.uid),
+ PRINTERNAME(snum) );
+ /* END_ADMIN_LOG */
+ 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(lp_const_servicename(snum));
+
+ /* Send a printer notify message */
+
+ notify_job_status(sharename, jobid, JOB_STATUS_QUEUED);
+
+ return True;
+}
+
+/****************************************************************************
+ Write to a print file.
+****************************************************************************/
+
+ssize_t print_job_write(int snum, uint32 jobid, const char *buf, SMB_OFF_T pos, size_t size)
+{
+ const char* sharename = lp_const_servicename(snum);
+ int return_code;
+ struct printjob *pjob;
+
+ pjob = print_job_find(sharename, jobid);
+
+ if (!pjob)
+ return -1;
+ /* don't allow another process to get this info - it is meaningless */
+ if (pjob->pid != sys_getpid())
+ return -1;
+
+ return_code = write_data_at_offset(pjob->fd, buf, size, pos);
+
+ if (return_code>0) {
+ pjob->size += size;
+ pjob_store(sharename, jobid, pjob);
+ }
+ return return_code;
+}
+
+/****************************************************************************
+ Get the queue status - do not update if db is out of date.
+****************************************************************************/
+
+static int get_queue_status(const char* sharename, print_status_struct *status)
+{
+ fstring keystr;
+ TDB_DATA data;
+ struct tdb_print_db *pdb = get_print_db_byname(sharename);
+ int len;
+
+ if (status) {
+ ZERO_STRUCTP(status);
+ }
+
+ if (!pdb)
+ return 0;
+
+ if (status) {
+ fstr_sprintf(keystr, "STATUS/%s", sharename);
+ data = tdb_fetch(pdb->tdb, string_tdb_data(keystr));
+ if (data.dptr) {
+ if (data.dsize == sizeof(print_status_struct))
+ /* this memcpy is ok since the status struct was
+ not packed before storing it in the tdb */
+ memcpy(status, data.dptr, sizeof(print_status_struct));
+ SAFE_FREE(data.dptr);
+ }
+ }
+ len = tdb_fetch_int32(pdb->tdb, "INFO/total_jobs");
+ release_print_db(pdb);
+ return (len == -1 ? 0 : len);
+}
+
+/****************************************************************************
+ Determine the number of jobs in a queue.
+****************************************************************************/
+
+int print_queue_length(int snum, print_status_struct *pstatus)
+{
+ const char* sharename = lp_const_servicename( snum );
+ print_status_struct status;
+ int len;
+
+ ZERO_STRUCT( status );
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(lp_const_servicename(snum), True))
+ print_queue_update(snum, False);
+
+ /* also fetch the queue status */
+ memset(&status, 0, sizeof(status));
+ len = get_queue_status(sharename, &status);
+
+ if (pstatus)
+ *pstatus = status;
+
+ return len;
+}
+
+/***************************************************************************
+ Allocate a jobid. Hold the lock for as short a time as possible.
+***************************************************************************/
+
+static bool allocate_print_jobid(struct tdb_print_db *pdb, int snum, const char *sharename, uint32 *pjobid)
+{
+ int i;
+ uint32 jobid;
+
+ *pjobid = (uint32)-1;
+
+ for (i = 0; i < 3; i++) {
+ /* Lock the database - only wait 20 seconds. */
+ if (tdb_lock_bystring_with_timeout(pdb->tdb, "INFO/nextjob", 20) == -1) {
+ DEBUG(0,("allocate_print_jobid: failed to lock printing database %s\n", sharename));
+ return False;
+ }
+
+ if (!tdb_fetch_uint32(pdb->tdb, "INFO/nextjob", &jobid)) {
+ if (tdb_error(pdb->tdb) != TDB_ERR_NOEXIST) {
+ DEBUG(0, ("allocate_print_jobid: failed to fetch INFO/nextjob for print queue %s\n",
+ sharename));
+ return False;
+ }
+ jobid = 0;
+ }
+
+ jobid = NEXT_JOBID(jobid);
+
+ if (tdb_store_int32(pdb->tdb, "INFO/nextjob", jobid)==-1) {
+ DEBUG(3, ("allocate_print_jobid: failed to store INFO/nextjob.\n"));
+ tdb_unlock_bystring(pdb->tdb, "INFO/nextjob");
+ return False;
+ }
+
+ /* We've finished with the INFO/nextjob lock. */
+ tdb_unlock_bystring(pdb->tdb, "INFO/nextjob");
+
+ if (!print_job_exists(sharename, jobid))
+ break;
+ }
+
+ if (i > 2) {
+ DEBUG(0, ("allocate_print_jobid: failed to allocate a print job for queue %s\n",
+ sharename));
+ /* Probably full... */
+ errno = ENOSPC;
+ return False;
+ }
+
+ /* Store a dummy placeholder. */
+ {
+ uint32_t tmp;
+ TDB_DATA dum;
+ dum.dptr = NULL;
+ dum.dsize = 0;
+ if (tdb_store(pdb->tdb, print_key(jobid, &tmp), dum,
+ TDB_INSERT) == -1) {
+ DEBUG(3, ("allocate_print_jobid: jobid (%d) failed to store placeholder.\n",
+ jobid ));
+ return False;
+ }
+ }
+
+ *pjobid = jobid;
+ return True;
+}
+
+/***************************************************************************
+ Append a jobid to the 'jobs changed' list.
+***************************************************************************/
+
+static bool add_to_jobs_changed(struct tdb_print_db *pdb, uint32 jobid)
+{
+ TDB_DATA data;
+ uint32 store_jobid;
+
+ SIVAL(&store_jobid, 0, jobid);
+ data.dptr = (uint8 *)&store_jobid;
+ data.dsize = 4;
+
+ DEBUG(10,("add_to_jobs_changed: Added jobid %u\n", (unsigned int)jobid ));
+
+ return (tdb_append(pdb->tdb, string_tdb_data("INFO/jobs_changed"),
+ data) == 0);
+}
+
+/***************************************************************************
+ Start spooling a job - return the jobid.
+***************************************************************************/
+
+uint32 print_job_start(struct auth_serversupplied_info *server_info, int snum,
+ char *jobname, NT_DEVICEMODE *nt_devmode )
+{
+ uint32 jobid;
+ char *path;
+ struct printjob pjob;
+ const char *sharename = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(sharename);
+ int njobs;
+
+ errno = 0;
+
+ if (!pdb)
+ return (uint32)-1;
+
+ if (!print_access_check(server_info, snum, PRINTER_ACCESS_USE)) {
+ DEBUG(3, ("print_job_start: job start denied by security descriptor\n"));
+ release_print_db(pdb);
+ return (uint32)-1;
+ }
+
+ if (!print_time_access_check(lp_servicename(snum))) {
+ DEBUG(3, ("print_job_start: job start denied by time check\n"));
+ release_print_db(pdb);
+ return (uint32)-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"));
+ release_print_db(pdb);
+ errno = ENOSPC;
+ return (uint32)-1;
+ }
+ }
+
+ /* for autoloaded printers, check that the printcap entry still exists */
+ if (lp_autoloaded(snum) && !pcap_printername_ok(lp_const_servicename(snum))) {
+ DEBUG(3, ("print_job_start: printer name %s check failed.\n", lp_const_servicename(snum) ));
+ release_print_db(pdb);
+ errno = ENOENT;
+ return (uint32)-1;
+ }
+
+ /* Insure the maximum queue size is not violated */
+ if ((njobs = print_queue_length(snum,NULL)) > lp_maxprintjobs(snum)) {
+ DEBUG(3, ("print_job_start: Queue %s number of jobs (%d) larger than max printjobs per queue (%d).\n",
+ sharename, njobs, lp_maxprintjobs(snum) ));
+ release_print_db(pdb);
+ errno = ENOSPC;
+ return (uint32)-1;
+ }
+
+ DEBUG(10,("print_job_start: Queue %s number of jobs (%d), max printjobs = %d\n",
+ sharename, njobs, lp_maxprintjobs(snum) ));
+
+ if (!allocate_print_jobid(pdb, snum, sharename, &jobid))
+ goto fail;
+
+ /* create the database entry */
+
+ ZERO_STRUCT(pjob);
+
+ pjob.pid = sys_getpid();
+ pjob.sysjob = -1;
+ pjob.fd = -1;
+ pjob.starttime = time(NULL);
+ pjob.status = LPQ_SPOOLING;
+ pjob.size = 0;
+ pjob.spooled = False;
+ pjob.smbjob = True;
+ pjob.nt_devmode = nt_devmode;
+
+ fstrcpy(pjob.jobname, jobname);
+
+ fstrcpy(pjob.user, lp_printjob_username(snum));
+ standard_sub_advanced(sharename, server_info->sanitized_username,
+ path, server_info->utok.gid,
+ server_info->sanitized_username,
+ pdb_get_domain(server_info->sam_account),
+ pjob.user, sizeof(pjob.user)-1);
+ /* ensure NULL termination */
+ pjob.user[sizeof(pjob.user)-1] = '\0';
+
+ fstrcpy(pjob.queuename, lp_const_servicename(snum));
+
+ /* we have a job entry - now create the spool file */
+ slprintf(pjob.filename, sizeof(pjob.filename)-1, "%s/%s%.8u.XXXXXX",
+ path, PRINT_SPOOL_PREFIX, (unsigned int)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;
+ }
+
+ pjob_store(sharename, jobid, &pjob);
+
+ /* Update the 'jobs changed' entry used by print_queue_status. */
+ add_to_jobs_changed(pdb, jobid);
+
+ /* Ensure we keep a rough count of the number of total jobs... */
+ tdb_change_int32_atomic(pdb->tdb, "INFO/total_jobs", &njobs, 1);
+
+ release_print_db(pdb);
+
+ return jobid;
+
+ fail:
+ if (jobid != -1)
+ pjob_delete(sharename, jobid);
+
+ release_print_db(pdb);
+
+ DEBUG(3, ("print_job_start: returning fail. Error = %s\n", strerror(errno) ));
+ return (uint32)-1;
+}
+
+/****************************************************************************
+ Update the number of pages spooled to jobid
+****************************************************************************/
+
+void print_job_endpage(int snum, uint32 jobid)
+{
+ const char* sharename = lp_const_servicename(snum);
+ struct printjob *pjob;
+
+ pjob = print_job_find(sharename, jobid);
+ if (!pjob)
+ return;
+ /* don't allow another process to get this info - it is meaningless */
+ if (pjob->pid != sys_getpid())
+ return;
+
+ pjob->page_count++;
+ pjob_store(sharename, 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 snum, uint32 jobid, enum file_close_type close_type)
+{
+ const char* sharename = lp_const_servicename(snum);
+ struct printjob *pjob;
+ int ret;
+ SMB_STRUCT_STAT sbuf;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ pjob = print_job_find(sharename, jobid);
+
+ if (!pjob)
+ return False;
+
+ if (pjob->spooled || pjob->pid != sys_getpid())
+ return False;
+
+ if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_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 quite 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);
+ pjob_delete(sharename, jobid);
+ return True;
+ }
+
+ pjob->smbjob = jobid;
+
+ ret = (*(current_printif->job_submit))(snum, pjob);
+
+ if (ret)
+ goto fail;
+
+ /* The print job has been successfully handed over to the back-end */
+
+ pjob->spooled = True;
+ pjob->status = LPQ_QUEUED;
+ pjob_store(sharename, jobid, pjob);
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(lp_const_servicename(snum), True))
+ print_queue_update(snum, False);
+
+ return True;
+
+fail:
+
+ /* The print job was not successfully started. Cleanup */
+ /* Still need to add proper error return propagation! 010122:JRR */
+ unlink(pjob->filename);
+ pjob_delete(sharename, jobid);
+ return False;
+}
+
+/****************************************************************************
+ Get a snapshot of jobs in the system without traversing.
+****************************************************************************/
+
+static bool get_stored_queue_info(struct tdb_print_db *pdb, int snum, int *pcount, print_queue_struct **ppqueue)
+{
+ TDB_DATA data, cgdata;
+ print_queue_struct *queue = NULL;
+ uint32 qcount = 0;
+ uint32 extra_count = 0;
+ int total_count = 0;
+ size_t len = 0;
+ uint32 i;
+ int max_reported_jobs = lp_max_reported_jobs(snum);
+ bool ret = False;
+ const char* sharename = lp_servicename(snum);
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(lp_const_servicename(snum), True))
+ print_queue_update(snum, False);
+
+ *pcount = 0;
+ *ppqueue = NULL;
+
+ ZERO_STRUCT(data);
+ ZERO_STRUCT(cgdata);
+
+ /* Get the stored queue data. */
+ data = tdb_fetch(pdb->tdb, string_tdb_data("INFO/linear_queue_array"));
+
+ if (data.dptr && data.dsize >= sizeof(qcount))
+ len += tdb_unpack(data.dptr + len, data.dsize - len, "d", &qcount);
+
+ /* Get the changed jobs list. */
+ cgdata = tdb_fetch(pdb->tdb, string_tdb_data("INFO/jobs_changed"));
+ if (cgdata.dptr != NULL && (cgdata.dsize % 4 == 0))
+ extra_count = cgdata.dsize/4;
+
+ DEBUG(5,("get_stored_queue_info: qcount = %u, extra_count = %u\n", (unsigned int)qcount, (unsigned int)extra_count));
+
+ /* Allocate the queue size. */
+ if (qcount == 0 && extra_count == 0)
+ goto out;
+
+ if ((queue = SMB_MALLOC_ARRAY(print_queue_struct, qcount + extra_count)) == NULL)
+ goto out;
+
+ /* Retrieve the linearised queue data. */
+
+ for( i = 0; i < qcount; i++) {
+ uint32 qjob, qsize, qpage_count, qstatus, qpriority, qtime;
+ len += tdb_unpack(data.dptr + len, data.dsize - len, "ddddddff",
+ &qjob,
+ &qsize,
+ &qpage_count,
+ &qstatus,
+ &qpriority,
+ &qtime,
+ queue[i].fs_user,
+ queue[i].fs_file);
+ queue[i].job = qjob;
+ queue[i].size = qsize;
+ queue[i].page_count = qpage_count;
+ queue[i].status = qstatus;
+ queue[i].priority = qpriority;
+ queue[i].time = qtime;
+ }
+
+ total_count = qcount;
+
+ /* Add in the changed jobids. */
+ for( i = 0; i < extra_count; i++) {
+ uint32 jobid;
+ struct printjob *pjob;
+
+ jobid = IVAL(cgdata.dptr, i*4);
+ DEBUG(5,("get_stored_queue_info: changed job = %u\n", (unsigned int)jobid));
+ pjob = print_job_find(lp_const_servicename(snum), jobid);
+ if (!pjob) {
+ DEBUG(5,("get_stored_queue_info: failed to find changed job = %u\n", (unsigned int)jobid));
+ remove_from_jobs_changed(sharename, jobid);
+ continue;
+ }
+
+ queue[total_count].job = jobid;
+ queue[total_count].size = pjob->size;
+ queue[total_count].page_count = pjob->page_count;
+ queue[total_count].status = pjob->status;
+ queue[total_count].priority = 1;
+ queue[total_count].time = pjob->starttime;
+ fstrcpy(queue[total_count].fs_user, pjob->user);
+ fstrcpy(queue[total_count].fs_file, pjob->jobname);
+ total_count++;
+ }
+
+ /* Sort the queue by submission time otherwise they are displayed
+ in hash order. */
+
+ qsort(queue, total_count, sizeof(print_queue_struct), QSORT_CAST(printjob_comp));
+
+ DEBUG(5,("get_stored_queue_info: total_count = %u\n", (unsigned int)total_count));
+
+ if (max_reported_jobs && total_count > max_reported_jobs)
+ total_count = max_reported_jobs;
+
+ *ppqueue = queue;
+ *pcount = total_count;
+
+ ret = True;
+
+ out:
+
+ SAFE_FREE(data.dptr);
+ SAFE_FREE(cgdata.dptr);
+ return ret;
+}
+
+/****************************************************************************
+ Get a printer queue listing.
+ set queue = NULL and status = NULL if you just want to update the cache
+****************************************************************************/
+
+int print_queue_status(int snum,
+ print_queue_struct **ppqueue,
+ print_status_struct *status)
+{
+ fstring keystr;
+ TDB_DATA data, key;
+ const char *sharename;
+ struct tdb_print_db *pdb;
+ int count = 0;
+
+ /* make sure the database is up to date */
+
+ if (print_cache_expired(lp_const_servicename(snum), True))
+ print_queue_update(snum, False);
+
+ /* return if we are done */
+ if ( !ppqueue || !status )
+ return 0;
+
+ *ppqueue = NULL;
+ sharename = lp_const_servicename(snum);
+ pdb = get_print_db_byname(sharename);
+
+ if (!pdb)
+ return 0;
+
+ /*
+ * 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", sharename);
+ key = string_tdb_data(keystr);
+
+ data = tdb_fetch(pdb->tdb, key);
+ if (data.dptr) {
+ if (data.dsize == sizeof(*status)) {
+ /* this memcpy is ok since the status struct was
+ not packed before storing it in the tdb */
+ memcpy(status, data.dptr, sizeof(*status));
+ }
+ SAFE_FREE(data.dptr);
+ }
+
+ /*
+ * Now, fetch the print queue information. We first count the number
+ * of entries, and then only retrieve the queue if necessary.
+ */
+
+ if (!get_stored_queue_info(pdb, snum, &count, ppqueue)) {
+ release_print_db(pdb);
+ return 0;
+ }
+
+ release_print_db(pdb);
+ return count;
+}
+
+/****************************************************************************
+ Pause a queue.
+****************************************************************************/
+
+bool print_queue_pause(struct auth_serversupplied_info *server_info, int snum,
+ WERROR *errcode)
+{
+ int ret;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!print_access_check(server_info, snum,
+ PRINTER_ACCESS_ADMINISTER)) {
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+
+ become_root();
+
+ ret = (*(current_printif->queue_pause))(snum);
+
+ unbecome_root();
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* force update the database */
+ print_cache_flush(lp_const_servicename(snum));
+
+ /* Send a printer notify message */
+
+ notify_printer_status(snum, PRINTER_STATUS_PAUSED);
+
+ return True;
+}
+
+/****************************************************************************
+ Resume a queue.
+****************************************************************************/
+
+bool print_queue_resume(struct auth_serversupplied_info *server_info, int snum,
+ WERROR *errcode)
+{
+ int ret;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!print_access_check(server_info, snum,
+ PRINTER_ACCESS_ADMINISTER)) {
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ become_root();
+
+ ret = (*(current_printif->queue_resume))(snum);
+
+ unbecome_root();
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(lp_const_servicename(snum), True))
+ print_queue_update(snum, True);
+
+ /* Send a printer notify message */
+
+ notify_printer_status(snum, PRINTER_STATUS_OK);
+
+ return True;
+}
+
+/****************************************************************************
+ Purge a queue - implemented by deleting all jobs that we can delete.
+****************************************************************************/
+
+bool print_queue_purge(struct auth_serversupplied_info *server_info, int snum,
+ WERROR *errcode)
+{
+ print_queue_struct *queue;
+ print_status_struct status;
+ 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, True);
+
+ can_job_admin = print_access_check(server_info, snum,
+ JOB_ACCESS_ADMINISTER);
+ njobs = print_queue_status(snum, &queue, &status);
+
+ if ( can_job_admin )
+ become_root();
+
+ for (i=0;i<njobs;i++) {
+ bool owner = is_owner(server_info, lp_const_servicename(snum),
+ queue[i].job);
+
+ if (owner || can_job_admin) {
+ print_job_delete1(snum, queue[i].job);
+ }
+ }
+
+ if ( can_job_admin )
+ unbecome_root();
+
+ /* update the cache */
+ print_queue_update( snum, True );
+
+ SAFE_FREE(queue);
+
+ return True;
+}
diff --git a/source3/printing/printing_db.c b/source3/printing/printing_db.c
new file mode 100644
index 0000000000..94319f96d0
--- /dev/null
+++ b/source3/printing/printing_db.c
@@ -0,0 +1,215 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ printing backend routines
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 2002
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "printing.h"
+
+static struct tdb_print_db *print_db_head;
+
+/****************************************************************************
+ Function to find or create the printer specific job tdb given a printername.
+ Limits the number of tdb's open to MAX_PRINT_DBS_OPEN.
+****************************************************************************/
+
+struct tdb_print_db *get_print_db_byname(const char *printername)
+{
+ struct tdb_print_db *p = NULL, *last_entry = NULL;
+ int num_open = 0;
+ char *printdb_path = NULL;
+ bool done_become_root = False;
+
+ SMB_ASSERT(printername != NULL);
+
+ for (p = print_db_head, last_entry = print_db_head; p; p = p->next) {
+ /* Ensure the list terminates... JRA. */
+ SMB_ASSERT(p->next != print_db_head);
+
+ if (p->tdb && strequal(p->printer_name, printername)) {
+ DLIST_PROMOTE(print_db_head, p);
+ p->ref_count++;
+ return p;
+ }
+ num_open++;
+ last_entry = p;
+ }
+
+ /* Not found. */
+ if (num_open >= MAX_PRINT_DBS_OPEN) {
+ /* Try and recycle the last entry. */
+ if (print_db_head && last_entry) {
+ DLIST_PROMOTE(print_db_head, last_entry);
+ }
+
+ for (p = print_db_head; p; p = p->next) {
+ if (p->ref_count)
+ continue;
+ if (p->tdb) {
+ if (tdb_close(print_db_head->tdb)) {
+ DEBUG(0,("get_print_db: Failed to close tdb for printer %s\n",
+ print_db_head->printer_name ));
+ return NULL;
+ }
+ }
+ p->tdb = NULL;
+ p->ref_count = 0;
+ memset(p->printer_name, '\0', sizeof(p->printer_name));
+ break;
+ }
+ if (p && print_db_head) {
+ DLIST_PROMOTE(print_db_head, p);
+ p = print_db_head;
+ }
+ }
+
+ if (!p) {
+ /* Create one. */
+ p = SMB_MALLOC_P(struct tdb_print_db);
+ if (!p) {
+ DEBUG(0,("get_print_db: malloc fail !\n"));
+ return NULL;
+ }
+ ZERO_STRUCTP(p);
+ DLIST_ADD(print_db_head, p);
+ }
+
+ if (asprintf(&printdb_path, "%s%s.tdb",
+ lock_path("printing/"),
+ printername) < 0) {
+ DLIST_REMOVE(print_db_head, p);
+ SAFE_FREE(p);
+ return NULL;
+ }
+
+ if (geteuid() != 0) {
+ become_root();
+ done_become_root = True;
+ }
+
+ p->tdb = tdb_open_log(printdb_path, 5000, TDB_DEFAULT, O_RDWR|O_CREAT,
+ 0600);
+
+ if (done_become_root)
+ unbecome_root();
+
+ if (!p->tdb) {
+ DEBUG(0,("get_print_db: Failed to open printer backend database %s.\n",
+ printdb_path ));
+ DLIST_REMOVE(print_db_head, p);
+ SAFE_FREE(printdb_path);
+ SAFE_FREE(p);
+ return NULL;
+ }
+ SAFE_FREE(printdb_path);
+ fstrcpy(p->printer_name, printername);
+ p->ref_count++;
+ return p;
+}
+
+/***************************************************************************
+ Remove a reference count.
+****************************************************************************/
+
+void release_print_db( struct tdb_print_db *pdb)
+{
+ pdb->ref_count--;
+ SMB_ASSERT(pdb->ref_count >= 0);
+}
+
+/***************************************************************************
+ Close all open print db entries.
+****************************************************************************/
+
+void close_all_print_db(void)
+{
+ struct tdb_print_db *p = NULL, *next_p = NULL;
+
+ for (p = print_db_head; p; p = next_p) {
+ next_p = p->next;
+
+ if (p->tdb)
+ tdb_close(p->tdb);
+ DLIST_REMOVE(print_db_head, p);
+ ZERO_STRUCTP(p);
+ SAFE_FREE(p);
+ }
+}
+
+
+/****************************************************************************
+ Fetch and clean the pid_t record list for all pids interested in notify
+ messages. data needs freeing on exit.
+****************************************************************************/
+
+TDB_DATA get_printer_notify_pid_list(TDB_CONTEXT *tdb, const char *printer_name, bool cleanlist)
+{
+ TDB_DATA data;
+ size_t i;
+
+ ZERO_STRUCT(data);
+
+ data = tdb_fetch_bystring( tdb, NOTIFY_PID_LIST_KEY );
+
+ if (!data.dptr) {
+ ZERO_STRUCT(data);
+ return data;
+ }
+
+ if (data.dsize % 8) {
+ DEBUG(0,("get_printer_notify_pid_list: Size of record for printer %s not a multiple of 8 !\n", printer_name ));
+ tdb_delete_bystring(tdb, NOTIFY_PID_LIST_KEY );
+ SAFE_FREE(data.dptr);
+ ZERO_STRUCT(data);
+ return data;
+ }
+
+ if (!cleanlist)
+ return data;
+
+ /*
+ * Weed out all dead entries.
+ */
+
+ for( i = 0; i < data.dsize; i += 8) {
+ pid_t pid = (pid_t)IVAL(data.dptr, i);
+
+ if (pid == sys_getpid())
+ continue;
+
+ /* Entry is dead if process doesn't exist or refcount is zero. */
+
+ while ((i < data.dsize) && ((IVAL(data.dptr, i + 4) == 0) || !process_exists_by_pid(pid))) {
+
+ /* Refcount == zero is a logic error and should never happen. */
+ if (IVAL(data.dptr, i + 4) == 0) {
+ DEBUG(0,("get_printer_notify_pid_list: Refcount == 0 for pid = %u printer %s !\n",
+ (unsigned int)pid, printer_name ));
+ }
+
+ if (data.dsize - i > 8)
+ memmove( &data.dptr[i], &data.dptr[i+8], data.dsize - i - 8);
+ data.dsize -= 8;
+ }
+ }
+
+ return data;
+}
+
+