d2071ace59470f7745875e8bb012f8ac71818190
[ira/wip.git] / source3 / printing / printing.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    printing routines
5    Copyright (C) Andrew Tridgell 1992-1995
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 extern int DEBUGLEVEL;
24 extern connection_struct Connections[];
25 extern files_struct Files[];
26
27 static BOOL * lpq_cache_reset=NULL;
28
29 static int check_lpq_cache(int snum) {
30   static int lpq_caches=0;
31   
32   if (lpq_caches <= snum) {
33       BOOL * p;
34       p = (BOOL *) Realloc(lpq_cache_reset,(snum+1)*sizeof(BOOL));
35       if (p) {
36          lpq_cache_reset=p;
37          lpq_caches = snum+1;
38       }
39   }
40   return lpq_caches;
41 }
42
43 void lpq_reset(int snum)
44 {
45   if (check_lpq_cache(snum) > snum) lpq_cache_reset[snum]=True;
46 }
47
48
49 /****************************************************************************
50 Build the print command in the supplied buffer. This means getting the
51 print command for the service and inserting the printer name and the
52 print file name. Return NULL on error, else the passed buffer pointer.
53 ****************************************************************************/
54 static char *build_print_command(int cnum, char *command, char *syscmd, char *filename1)
55 {
56   int snum = SNUM(cnum);
57   char *tstr;
58   pstring filename;
59   
60   /* get the print command for the service. */
61   tstr = command;
62   if (!syscmd || !tstr) {
63     DEBUG(0,("No print command for service `%s'\n", SERVICE(snum)));
64     return (NULL);
65   }
66
67   /* copy the command into the buffer for extensive meddling. */
68   StrnCpy(syscmd, tstr, sizeof(pstring) - 1);
69   
70   /* look for "%s" in the string. If there is no %s, we cannot print. */   
71   if (!strstr(syscmd, "%s") && !strstr(syscmd, "%f")) {
72     DEBUG(2,("WARNING! No placeholder for the filename in the print command for service %s!\n", SERVICE(snum)));
73   }
74   
75   if (strstr(syscmd,"%s")) {
76     int iOffset = PTR_DIFF(strstr(syscmd, "%s"),syscmd);
77     
78     /* construct the full path for the filename, shouldn't be necessary unless
79        the subshell causes a "cd" to be executed.
80        Only use the full path if there isn't a / preceding the %s */
81     if (iOffset==0 || syscmd[iOffset-1] != '/') {
82       StrnCpy(filename,Connections[cnum].connectpath,sizeof(filename)-1);
83       trim_string(filename,"","/");
84       strcat(filename,"/");
85       strcat(filename,filename1);
86     }
87     else
88       strcpy(filename,filename1);
89     
90     string_sub(syscmd, "%s", filename);
91   }
92   
93   string_sub(syscmd, "%f", filename1);
94   
95   /* Does the service have a printername? If not, make a fake and empty    */
96   /* printer name. That way a %p is treated sanely if no printer */
97   /* name was specified to replace it. This eventuality is logged.         */
98   tstr = PRINTERNAME(snum);
99   if (tstr == NULL || tstr[0] == '\0') {
100     DEBUG(3,( "No printer name - using %s.\n", SERVICE(snum)));
101     tstr = SERVICE(snum);
102   }
103   
104   string_sub(syscmd, "%p", tstr);
105   
106   standard_sub(cnum,syscmd);
107   
108   return (syscmd);
109 }
110
111
112 /****************************************************************************
113 print a file - called on closing the file
114 ****************************************************************************/
115 void print_file(int fnum)
116 {
117   pstring syscmd;
118   int cnum = Files[fnum].cnum;
119   int snum=SNUM(cnum);
120   char *tempstr;
121
122   *syscmd = 0;
123
124   if (file_size(Files[fnum].name) <= 0) {
125     DEBUG(3,("Discarding null print job %s\n",Files[fnum].name));
126     sys_unlink(Files[fnum].name);
127     return;
128   }
129
130   tempstr = build_print_command(cnum, PRINTCOMMAND(snum), syscmd, Files[fnum].name);
131   if (tempstr != NULL)
132     {
133       int ret = smbrun(syscmd,NULL,False);
134       DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
135     }
136   else
137     DEBUG(0,("Null print command?\n"));
138   
139   lpq_reset(snum);
140 }
141
142 static char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
143                               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"};
144
145
146 /*******************************************************************
147 process time fields
148 ********************************************************************/
149 static time_t EntryTime(string tok[], int ptr, int count, int minimum)
150 {
151   time_t jobtime,jobtime1;
152
153   jobtime = time(NULL);         /* default case: take current time */
154   if (count >= minimum) {
155     struct tm *t;
156     int i, day, hour, min, sec;
157     char   *c;
158
159     for (i=0; i<13; i++) if (!strncmp(tok[ptr], Months[i],3)) break; /* Find month */
160     if (i<12) {
161       t = localtime(&jobtime);
162       day = atoi(tok[ptr+1]);
163       c=(char *)(tok[ptr+2]);
164       *(c+2)=0;
165       hour = atoi(c);
166       *(c+5)=0;
167       min = atoi(c+3);
168       if(*(c+6) != 0)sec = atoi(c+6);
169       else  sec=0;
170
171       if ((t->tm_mon < i)||
172           ((t->tm_mon == i)&&
173            ((t->tm_mday < day)||
174             ((t->tm_mday == day)&&
175              (t->tm_hour*60+t->tm_min < hour*60+min)))))
176         t->tm_year--;           /* last year's print job */
177
178       t->tm_mon = i;
179       t->tm_mday = day;
180       t->tm_hour = hour;
181       t->tm_min = min;
182       t->tm_sec = sec;
183       jobtime1 = mktime(t);
184       if (jobtime1 != (time_t)-1)
185         jobtime = jobtime1;
186     }
187   }
188   return jobtime;
189 }
190
191
192 /****************************************************************************
193 parse a lpq line
194
195 here is an example of lpq output under bsd
196
197 Warning: no daemon present
198 Rank   Owner      Job  Files                                 Total Size
199 1st    tridge     148  README                                8096 bytes
200
201 here is an example of lpq output under osf/1
202
203 Warning: no daemon present
204 Rank   Pri Owner      Job  Files                             Total Size
205 1st    0   tridge     148  README                            8096 bytes
206 ****************************************************************************/
207 static BOOL parse_lpq_bsd(char *line,print_queue_struct *buf,BOOL first)
208 {
209 #ifdef  OSF1
210 #define RANKTOK 0
211 #define PRIOTOK 1
212 #define USERTOK 2
213 #define JOBTOK  3
214 #define FILETOK 4
215 #define TOTALTOK 5
216 #define NTOK    6
217 #else   /* OSF1 */
218 #define RANKTOK 0
219 #define USERTOK 1
220 #define JOBTOK  2
221 #define FILETOK 3
222 #define TOTALTOK 4
223 #define NTOK    5
224 #endif  /* OSF1 */
225
226   string tok[NTOK];
227   int count=0;
228
229 #ifdef  OSF1
230   int length;
231   length = strlen(line);
232   if (line[length-3] == ':')
233         return(False);
234 #endif  /* OSF1 */
235
236   /* handle the case of "(standard input)" as a filename */
237   string_sub(line,"standard input","STDIN");
238   string_sub(line,"(","\"");
239   string_sub(line,")","\"");
240   
241   for (count=0; count<NTOK && next_token(&line,tok[count],NULL); count++) ;
242
243   /* we must get NTOK tokens */
244   if (count < NTOK)
245     return(False);
246
247   /* the Job and Total columns must be integer */
248   if (!isdigit(*tok[JOBTOK]) || !isdigit(*tok[TOTALTOK])) return(False);
249
250   /* if the fname contains a space then use STDIN */
251   if (strchr(tok[FILETOK],' '))
252     strcpy(tok[FILETOK],"STDIN");
253
254   /* only take the last part of the filename */
255   {
256     string tmp;
257     char *p = strrchr(tok[FILETOK],'/');
258     if (p)
259       {
260         strcpy(tmp,p+1);
261         strcpy(tok[FILETOK],tmp);
262       }
263   }
264         
265
266   buf->job = atoi(tok[JOBTOK]);
267   buf->size = atoi(tok[TOTALTOK]);
268   buf->status = strequal(tok[RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED;
269   buf->time = time(NULL);
270   StrnCpy(buf->user,tok[USERTOK],sizeof(buf->user)-1);
271   StrnCpy(buf->file,tok[FILETOK],sizeof(buf->file)-1);
272 #ifdef PRIOTOK
273   buf->priority = atoi(tok[PRIOTOK]);
274 #else
275   buf->priority = 1;
276 #endif
277   return(True);
278 }
279
280 /*
281 <magnus@hum.auc.dk>
282 LPRng_time modifies the current date by inserting the hour and minute from
283 the lpq output.  The lpq time looks like "23:15:07"
284 */
285 static time_t LPRng_time(string tok[],int pos)
286 {
287   time_t jobtime;
288   struct tm *t;
289   char tmp_time[9];
290
291   jobtime = time(NULL);         /* default case: take current time */
292   t = localtime(&jobtime);
293   t->tm_hour = atoi(tok[pos]);
294   StrnCpy(tmp_time,tok[pos],sizeof(tmp_time));
295   t->tm_min = atoi(tmp_time+3);
296   t->tm_sec = atoi(tmp_time+6);
297   jobtime = mktime(t);
298
299   return jobtime;
300 }
301
302
303 /****************************************************************************
304   parse a lpq line
305   <magnus@hum.auc.dk>
306   Most of the code is directly reused from parse_lpq_bsd()
307
308 here are two examples of lpq output under lprng (LPRng-2.3.0)
309
310 Printer: humprn@hum-fak
311   Queue: 1 printable job
312   Server: pid 4840 active, Unspooler: pid 4841 active
313   Status: job 'cfA659hum-fak', closing device at Fri Jun 21 10:10:21 1996
314  Rank  Owner           Class Job Files                           Size Time
315 active magnus@hum-fak      A 659 /var/spool/smb/Notesblok-ikke-na4024 10:03:31
316  
317 Printer: humprn@hum-fak (printing disabled)
318   Queue: 1 printable job
319   Warning: no server present
320   Status: finished operations at Fri Jun 21 10:10:32 1996
321  Rank  Owner           Class Job Files                           Size Time
322 1      magnus@hum-fak      A 387 /var/spool/smb/netbudget.xls    21230 10:50:53
323  
324 ****************************************************************************/
325 static BOOL parse_lpq_lprng(char *line,print_queue_struct *buf,BOOL first)
326 {
327 #define        LPRNG_RANKTOK   0
328 #define        LPRNG_USERTOK 1
329 #define        LPRNG_PRIOTOK 2
330 #define        LPRNG_JOBTOK    3
331 #define        LPRNG_FILETOK   4
332 #define        LPRNG_TOTALTOK 5
333 #define LPRNG_TIMETOK 6
334 #define        LPRNG_NTOK      7
335
336 /****************************************************************************
337 From lpd_status.c in LPRng source.
338 0        1         2         3         4         5         6         7
339 12345678901234567890123456789012345678901234567890123456789012345678901234 
340 " Rank  Owner           Class Job Files                           Size Time"
341                         plp_snprintf( msg, sizeof(msg), "%-6s %-19s %c %03d %-32s",
342                                 number, line, priority, cfp->number, error );
343                                 plp_snprintf( msg + len, sizeof(msg)-len, "%4d",
344                                         cfp->jobsize );
345                                 plp_snprintf( msg+len, sizeof(msg)-len, " %s",
346                                         Time_str( 1, cfp->statb.st_ctime ) );
347 ****************************************************************************/
348   /* The following define's are to be able to adjust the values if the
349 LPRng source changes.  This is from version 2.3.0.  Magnus  */
350 #define SPACE_W 1
351 #define RANK_W 6
352 #define OWNER_W 19
353 #define CLASS_W 1
354 #define JOB_W 3
355 #define FILE_W 32
356 /* The JOBSIZE_W is too small for big jobs, so time is pushed to the right */
357 #define JOBSIZE_W 4
358  
359 #define RANK_POS 0
360 #define OWNER_POS RANK_POS+RANK_W+SPACE_W
361 #define CLASS_POS OWNER_POS+OWNER_W+SPACE_W
362 #define JOB_POS CLASS_POS+CLASS_W+SPACE_W
363 #define FILE_POS JOB_POS+JOB_W+SPACE_W
364 #define JOBSIZE_POS FILE_POS+FILE_W
365
366   
367   string tok[LPRNG_NTOK];
368   int count=0;
369
370 /* 
371 Need to insert one space in front of the size, to be able to use
372 next_token() unchanged.  I would have liked to be able to insert a
373 space instead, to prevent losing that one char, but perl has spoiled
374 me :-\  So I did it the easiest way.
375
376 HINT: Use as short a path as possible for the samba spool directory.
377 A long spool-path will just waste significant chars of the file name.
378 */
379
380   line[JOBSIZE_POS-1]=' ';
381
382   /* handle the case of "(stdin)" as a filename */
383   string_sub(line,"stdin","STDIN");
384   string_sub(line,"(","\"");
385   string_sub(line,")","\"");
386   
387   for (count=0; count<LPRNG_NTOK && next_token(&line,tok[count],NULL); count++) ;
388
389   /* we must get LPRNG_NTOK tokens */
390   if (count < LPRNG_NTOK)
391     return(False);
392
393   /* the Job and Total columns must be integer */
394   if (!isdigit(*tok[LPRNG_JOBTOK]) || !isdigit(*tok[LPRNG_TOTALTOK])) return(False);
395
396   /* if the fname contains a space then use STDIN */
397   /* I do not understand how this would be possible. Magnus. */
398   if (strchr(tok[LPRNG_FILETOK],' '))
399     strcpy(tok[LPRNG_FILETOK],"STDIN");
400
401   /* only take the last part of the filename */
402   {
403     string tmp;
404     char *p = strrchr(tok[LPRNG_FILETOK],'/');
405     if (p)
406       {
407        strcpy(tmp,p+1);
408        strcpy(tok[LPRNG_FILETOK],tmp);
409       }
410   }
411        
412
413   buf->job = atoi(tok[LPRNG_JOBTOK]);
414   buf->size = atoi(tok[LPRNG_TOTALTOK]);
415   buf->status = strequal(tok[LPRNG_RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED;
416   /*  buf->time = time(NULL); */
417   buf->time = LPRng_time(tok,LPRNG_TIMETOK);
418 DEBUG(3,("Time reported for job %d is %s", buf->job, ctime(&buf->time)));
419   StrnCpy(buf->user,tok[LPRNG_USERTOK],sizeof(buf->user)-1);
420   StrnCpy(buf->file,tok[LPRNG_FILETOK],sizeof(buf->file)-1);
421 #ifdef LPRNG_PRIOTOK
422   /* Here I try to map the CLASS char to a number, but the number
423      is never shown in Print Manager under NT anyway... Magnus. */
424   buf->priority = atoi(tok[LPRNG_PRIOTOK]-('A'-1));
425 #else
426   buf->priority = 1;
427 #endif
428   return(True);
429 }
430
431
432
433 /*******************************************************************
434 parse lpq on an aix system
435
436 Queue   Dev   Status    Job Files              User         PP %   Blks  Cp Rnk
437 ------- ----- --------- --- ------------------ ---------- ---- -- ----- --- ---
438 lazer   lazer READY
439 lazer   lazer RUNNING   537 6297doc.A          kvintus@IE    0 10  2445   1   1
440               QUEUED    538 C.ps               root@IEDVB           124   1   2
441               QUEUED    539 E.ps               root@IEDVB            28   1   3
442               QUEUED    540 L.ps               root@IEDVB           172   1   4
443               QUEUED    541 P.ps               root@IEDVB            22   1   5
444 ********************************************************************/
445 static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first)
446 {
447   string tok[11];
448   int count=0;
449
450   /* handle the case of "(standard input)" as a filename */
451   string_sub(line,"standard input","STDIN");
452   string_sub(line,"(","\"");
453   string_sub(line,")","\"");
454
455   for (count=0; count<10 && next_token(&line,tok[count],NULL); count++) ;
456
457   /* we must get 6 tokens */
458   if (count < 10)
459   {
460       if ((count == 7) && (strcmp(tok[0],"QUEUED") == 0))
461       {
462           /* the 2nd and 5th columns must be integer */
463           if (!isdigit(*tok[1]) || !isdigit(*tok[4])) return(False);
464           buf->size = atoi(tok[4]) * 1024;
465           /* if the fname contains a space then use STDIN */
466           if (strchr(tok[2],' '))
467             strcpy(tok[2],"STDIN");
468
469           /* only take the last part of the filename */
470           {
471             string tmp;
472             char *p = strrchr(tok[2],'/');
473             if (p)
474               {
475                 strcpy(tmp,p+1);
476                 strcpy(tok[2],tmp);
477               }
478           }
479
480
481           buf->job = atoi(tok[1]);
482           buf->status = LPQ_QUEUED;
483           buf->priority = 0;
484           buf->time = time(NULL);
485           StrnCpy(buf->user,tok[3],sizeof(buf->user)-1);
486           StrnCpy(buf->file,tok[2],sizeof(buf->file)-1);
487       }
488       else
489       {
490           DEBUG(6,("parse_lpq_aix count=%d\n", count));
491           return(False);
492       }
493   }
494   else
495   {
496       /* the 4th and 9th columns must be integer */
497       if (!isdigit(*tok[3]) || !isdigit(*tok[8])) return(False);
498       buf->size = atoi(tok[8]) * 1024;
499       /* if the fname contains a space then use STDIN */
500       if (strchr(tok[4],' '))
501         strcpy(tok[4],"STDIN");
502
503       /* only take the last part of the filename */
504       {
505         string tmp;
506         char *p = strrchr(tok[4],'/');
507         if (p)
508           {
509             strcpy(tmp,p+1);
510             strcpy(tok[4],tmp);
511           }
512       }
513
514
515       buf->job = atoi(tok[3]);
516       buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED;
517       buf->priority = 0;
518       buf->time = time(NULL);
519       StrnCpy(buf->user,tok[5],sizeof(buf->user)-1);
520       StrnCpy(buf->file,tok[4],sizeof(buf->file)-1);
521   }
522
523
524   return(True);
525 }
526
527
528 /****************************************************************************
529 parse a lpq line
530 here is an example of lpq output under hpux; note there's no space after -o !
531 $> lpstat -oljplus
532 ljplus-2153         user           priority 0  Jan 19 08:14 on ljplus
533       util.c                                  125697 bytes
534       server.c                                110712 bytes
535 ljplus-2154         user           priority 0  Jan 19 08:14 from client
536       (standard input)                          7551 bytes
537 ****************************************************************************/
538 static BOOL parse_lpq_hpux(char * line, print_queue_struct *buf, BOOL first)
539 {
540   /* must read two lines to process, therefore keep some values static */
541   static BOOL header_line_ok=False, base_prio_reset=False;
542   static string jobuser;
543   static int jobid;
544   static int jobprio;
545   static time_t jobtime;
546   static int jobstat=LPQ_QUEUED;
547   /* to store minimum priority to print, lpstat command should be invoked
548      with -p option first, to work */
549   static int base_prio;
550  
551   int count;
552   char TAB = '\011';  
553   string tok[12];
554
555   /* If a line begins with a horizontal TAB, it is a subline type */
556   
557   if (line[0] == TAB) { /* subline */
558     /* check if it contains the base priority */
559     if (!strncmp(line,"\tfence priority : ",18)) {
560        base_prio=atoi(&line[18]);
561        DEBUG(4, ("fence priority set at %d\n", base_prio));
562     }
563     if (!header_line_ok) return (False); /* incorrect header line */
564     /* handle the case of "(standard input)" as a filename */
565     string_sub(line,"standard input","STDIN");
566     string_sub(line,"(","\"");
567     string_sub(line,")","\"");
568     
569     for (count=0; count<2 && next_token(&line,tok[count],NULL); count++) ;
570     /* we must get 2 tokens */
571     if (count < 2) return(False);
572     
573     /* the 2nd column must be integer */
574     if (!isdigit(*tok[1])) return(False);
575     
576     /* if the fname contains a space then use STDIN */
577     if (strchr(tok[0],' '))
578       strcpy(tok[0],"STDIN");
579     
580     buf->size = atoi(tok[1]);
581     StrnCpy(buf->file,tok[0],sizeof(buf->file)-1);
582     
583     /* fill things from header line */
584     buf->time = jobtime;
585     buf->job = jobid;
586     buf->status = jobstat;
587     buf->priority = jobprio;
588     StrnCpy(buf->user,jobuser,sizeof(buf->user)-1);
589     
590     return(True);
591   }
592   else { /* header line */
593     header_line_ok=False; /* reset it */
594     if (first) {
595        if (!base_prio_reset) {
596           base_prio=0; /* reset it */
597           base_prio_reset=True;
598        }
599     }
600     else if (base_prio) base_prio_reset=False;
601     
602     /* handle the dash in the job id */
603     string_sub(line,"-"," ");
604     
605     for (count=0; count<12 && next_token(&line,tok[count],NULL); count++) ;
606       
607     /* we must get 8 tokens */
608     if (count < 8) return(False);
609     
610     /* first token must be printer name (cannot check ?) */
611     /* the 2nd, 5th & 7th column must be integer */
612     if (!isdigit(*tok[1]) || !isdigit(*tok[4]) || !isdigit(*tok[6])) return(False);
613     jobid = atoi(tok[1]);
614     StrnCpy(jobuser,tok[2],sizeof(buf->user)-1);
615     jobprio = atoi(tok[4]);
616     
617     /* process time */
618     jobtime=EntryTime(tok, 5, count, 8);
619     if (jobprio < base_prio) {
620        jobstat = LPQ_PAUSED;
621        DEBUG (4, ("job %d is paused: prio %d < %d; jobstat=%d\n", jobid, jobprio, base_prio, jobstat));
622     }
623     else {
624        jobstat = LPQ_QUEUED;
625        if ((count >8) && (((strequal(tok[8],"on")) ||
626                            ((strequal(tok[8],"from")) && 
627                             ((count > 10)&&(strequal(tok[10],"on")))))))
628          jobstat = LPQ_PRINTING;
629     }
630     
631     header_line_ok=True; /* information is correct */
632     return(False); /* need subline info to include into queuelist */
633   }
634 }
635
636
637 /****************************************************************************
638 parse a lpq line
639
640 here is an example of "lpstat -o dcslw" output under sysv
641
642 dcslw-896               tridge            4712   Dec 20 10:30:30 on dcslw
643 dcslw-897               tridge            4712   Dec 20 10:30:30 being held
644
645 ****************************************************************************/
646 static BOOL parse_lpq_sysv(char *line,print_queue_struct *buf,BOOL first)
647 {
648   string tok[9];
649   int count=0;
650   char *p;
651
652   /* handle the dash in the job id */
653   string_sub(line,"-"," ");
654   
655   for (count=0; count<9 && next_token(&line,tok[count],NULL); count++) ;
656
657   /* we must get 7 tokens */
658   if (count < 7)
659     return(False);
660
661   /* the 2nd and 4th, 6th columns must be integer */
662   if (!isdigit(*tok[1]) || !isdigit(*tok[3])) return(False);
663   if (!isdigit(*tok[5])) return(False);
664
665   /* if the user contains a ! then trim the first part of it */  
666   if ((p=strchr(tok[2],'!')))
667     {
668       string tmp;
669       strcpy(tmp,p+1);
670       strcpy(tok[2],tmp);
671     }
672     
673
674   buf->job = atoi(tok[1]);
675   buf->size = atoi(tok[3]);
676   if (count > 7 && strequal(tok[7],"on"))
677     buf->status = LPQ_PRINTING;
678   else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held"))
679     buf->status = LPQ_PAUSED;
680   else
681     buf->status = LPQ_QUEUED;
682   buf->priority = 0;
683   buf->time = EntryTime(tok, 4, count, 7);
684   StrnCpy(buf->user,tok[2],sizeof(buf->user)-1);
685   StrnCpy(buf->file,tok[2],sizeof(buf->file)-1);
686   return(True);
687 }
688
689 /****************************************************************************
690 parse a lpq line
691
692 here is an example of lpq output under qnx
693 Spooler: /qnx/spooler, on node 1
694 Printer: txt        (ready) 
695 0000:     root  [job #1    ]   active 1146 bytes        /etc/profile
696 0001:     root  [job #2    ]    ready 2378 bytes        /etc/install
697 0002:     root  [job #3    ]    ready 1146 bytes        -- standard input --
698 ****************************************************************************/
699 static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first)
700 {
701   string tok[7];
702   int count=0;
703
704   DEBUG(0,("antes [%s]\n", line));
705
706   /* handle the case of "-- standard input --" as a filename */
707   string_sub(line,"standard input","STDIN");
708   DEBUG(0,("despues [%s]\n", line));
709   string_sub(line,"-- ","\"");
710   string_sub(line," --","\"");
711   DEBUG(0,("despues 1 [%s]\n", line));
712
713   string_sub(line,"[job #","");
714   string_sub(line,"]","");
715   DEBUG(0,("despues 2 [%s]\n", line));
716
717   
718   
719   for (count=0; count<7 && next_token(&line,tok[count],NULL); count++) ;
720
721   /* we must get 7 tokens */
722   if (count < 7)
723     return(False);
724
725   /* the 3rd and 5th columns must be integer */
726   if (!isdigit(*tok[2]) || !isdigit(*tok[4])) return(False);
727
728   /* only take the last part of the filename */
729   {
730     string tmp;
731     char *p = strrchr(tok[6],'/');
732     if (p)
733       {
734         strcpy(tmp,p+1);
735         strcpy(tok[6],tmp);
736       }
737   }
738         
739
740   buf->job = atoi(tok[2]);
741   buf->size = atoi(tok[4]);
742   buf->status = strequal(tok[3],"active")?LPQ_PRINTING:LPQ_QUEUED;
743   buf->priority = 0;
744   buf->time = time(NULL);
745   StrnCpy(buf->user,tok[1],sizeof(buf->user)-1);
746   StrnCpy(buf->file,tok[6],sizeof(buf->file)-1);
747   return(True);
748 }
749
750
751 /****************************************************************************
752   parse a lpq line for the plp printing system
753   Bertrand Wallrich <Bertrand.Wallrich@loria.fr>
754
755 redone by tridge. Here is a sample queue:
756
757 Local  Printer 'lp2' (fjall):
758   Printing (started at Jun 15 13:33:58, attempt 1).
759     Rank Owner       Pr Opt  Job Host        Files           Size     Date
760   active tridge      X  -    6   fjall       /etc/hosts      739      Jun 15 13:33
761      3rd tridge      X  -    7   fjall       /etc/hosts      739      Jun 15 13:33
762
763 ****************************************************************************/
764 static BOOL parse_lpq_plp(char *line,print_queue_struct *buf,BOOL first)
765 {
766   string tok[11];
767   int count=0;
768
769   /* handle the case of "(standard input)" as a filename */
770   string_sub(line,"stdin","STDIN");
771   string_sub(line,"(","\"");
772   string_sub(line,")","\"");
773   
774   for (count=0; count<11 && next_token(&line,tok[count],NULL); count++) ;
775
776   /* we must get 11 tokens */
777   if (count < 11)
778     return(False);
779
780   /* the first must be "active" or begin with an integer */
781   if (strcmp(tok[0],"active") && !isdigit(tok[0][0]))
782     return(False);
783
784   /* the 5th and 8th must be integer */
785   if (!isdigit(*tok[4]) || !isdigit(*tok[7])) 
786     return(False);
787
788   /* if the fname contains a space then use STDIN */
789   if (strchr(tok[6],' '))
790     strcpy(tok[6],"STDIN");
791
792   /* only take the last part of the filename */
793   {
794     string tmp;
795     char *p = strrchr(tok[6],'/');
796     if (p)
797       {
798         strcpy(tmp,p+1);
799         strcpy(tok[6],tmp);
800       }
801   }
802
803
804   buf->job = atoi(tok[4]);
805
806   buf->size = atoi(tok[7]);
807   if (strchr(tok[7],'K'))
808     buf->size *= 1024;
809   if (strchr(tok[7],'M'))
810     buf->size *= 1024*1024;
811
812   buf->status = strequal(tok[0],"active")?LPQ_PRINTING:LPQ_QUEUED;
813   buf->priority = 0;
814   buf->time = time(NULL);
815   StrnCpy(buf->user,tok[1],sizeof(buf->user)-1);
816   StrnCpy(buf->file,tok[6],sizeof(buf->file)-1);
817   return(True);
818 }
819
820
821
822 char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
823 char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
824 char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL };
825
826 /****************************************************************************
827 parse a lpq line. Choose printing style
828 ****************************************************************************/
829 static BOOL parse_lpq_entry(int snum,char *line,
830                             print_queue_struct *buf,
831                             print_status_struct *status,BOOL first)
832 {
833   BOOL ret;
834
835   switch (lp_printing())
836     {
837     case PRINT_SYSV:
838       ret = parse_lpq_sysv(line,buf,first);
839       break;
840     case PRINT_AIX:      
841       ret = parse_lpq_aix(line,buf,first);
842       break;
843     case PRINT_HPUX:
844       ret = parse_lpq_hpux(line,buf,first);
845       break;
846     case PRINT_QNX:
847       ret = parse_lpq_qnx(line,buf,first);
848       break;
849     case PRINT_LPRNG:
850       ret = parse_lpq_lprng(line,buf,first);
851       break;
852     case PRINT_PLP:
853       ret = parse_lpq_plp(line,buf,first);
854       break;
855     default:
856       ret = parse_lpq_bsd(line,buf,first);
857       break;
858     }
859
860 #ifdef LPQ_GUEST_TO_USER
861   if (ret) {
862     extern pstring sesssetup_user;
863     /* change guest entries to the current logged in user to make
864        them appear deletable to windows */
865     if (sesssetup_user[0] && strequal(buf->user,lp_guestaccount(snum)))
866       strcpy(buf->user,sesssetup_user);
867   }
868 #endif
869
870   /* We don't want the newline in the status message. */
871   {
872     char *p = strchr(line,'\n');
873     if (p) *p = 0;
874   }
875
876   if (status && !ret)
877     {
878       /* a few simple checks to see if the line might be a
879          printer status line: 
880          handle them so that most severe condition is shown */
881       int i;
882       strlower(line);
883       
884       switch (status->status) {
885       case LPSTAT_OK:
886         for (i=0; stat0_strings[i]; i++)
887           if (strstr(line,stat0_strings[i])) {
888             StrnCpy(status->message,line,sizeof(status->message)-1);
889             status->status=LPSTAT_OK;
890           }
891       case LPSTAT_STOPPED:
892         for (i=0; stat1_strings[i]; i++)
893           if (strstr(line,stat1_strings[i])) {
894             StrnCpy(status->message,line,sizeof(status->message)-1);
895             status->status=LPSTAT_STOPPED;
896           }
897       case LPSTAT_ERROR:
898         for (i=0; stat2_strings[i]; i++)
899           if (strstr(line,stat2_strings[i])) {
900             StrnCpy(status->message,line,sizeof(status->message)-1);
901             status->status=LPSTAT_ERROR;
902           }
903         break;
904       }
905     }
906
907   return(ret);
908 }
909
910 /****************************************************************************
911 get a printer queue
912 ****************************************************************************/
913 int get_printqueue(int snum,int cnum,print_queue_struct **queue,
914                    print_status_struct *status)
915 {
916   char *lpq_command = lp_lpqcommand(snum);
917   char *printername = PRINTERNAME(snum);
918   int ret=0,count=0;
919   pstring syscmd;
920   fstring outfile;
921   pstring line;
922   FILE *f;
923   struct stat sbuf;
924   BOOL dorun=True;
925   int cachetime = lp_lpqcachetime();
926
927   *line = 0;
928   check_lpq_cache(snum);
929   
930   if (!printername || !*printername)
931     {
932       DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
933             lp_servicename(snum),snum));
934       printername = lp_servicename(snum);
935     }
936     
937   if (!lpq_command || !(*lpq_command))
938     {
939       DEBUG(5,("No lpq command\n"));
940       return(0);
941     }
942     
943   strcpy(syscmd,lpq_command);
944   string_sub(syscmd,"%p",printername);
945
946   standard_sub(cnum,syscmd);
947
948   sprintf(outfile,"%s/lpq.%08x",tmpdir(),str_checksum(syscmd));
949   
950   if (!lpq_cache_reset[snum] && cachetime && !stat(outfile,&sbuf)) 
951     {
952       if (time(NULL) - sbuf.st_mtime < cachetime) {
953         DEBUG(3,("Using cached lpq output\n"));
954         dorun = False;
955       }
956     }
957
958   if (dorun) {
959     ret = smbrun(syscmd,outfile,True);
960     DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
961   }
962
963   lpq_cache_reset[snum] = False;
964
965   f = fopen(outfile,"r");
966   if (!f) {
967     return(0);
968   }
969
970   if (status) {
971     strcpy(status->message,"");
972     status->status = LPSTAT_OK;
973   }
974       
975   while (fgets(line,sizeof(pstring),f))
976     {
977       DEBUG(6,("QUEUE2: %s\n",line));
978
979       *queue = Realloc(*queue,sizeof(print_queue_struct)*(count+1));
980       if (! *queue)
981         {
982           count = 0;
983           break;
984         }
985
986       bzero((char *)&(*queue)[count],sizeof(**queue));
987           
988       /* parse it */
989       if (!parse_lpq_entry(snum,line,&(*queue)[count],status,count==0))
990         continue;
991           
992       count++;
993     }         
994
995   fclose(f);
996
997   if (!cachetime) {
998     unlink(outfile);
999   } else {
1000     /* we only expect this to succeed on trapdoor systems, on normal systems
1001      the file is owned by root */
1002     chmod(outfile,0666);
1003   }
1004   return(count);
1005 }
1006
1007
1008 /****************************************************************************
1009 delete a printer queue entry
1010 ****************************************************************************/
1011 void del_printqueue(int cnum,int snum,int jobid)
1012 {
1013   char *lprm_command = lp_lprmcommand(snum);
1014   char *printername = PRINTERNAME(snum);
1015   pstring syscmd;
1016   char jobstr[20];
1017   int ret;
1018
1019   if (!printername || !*printername)
1020     {
1021       DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
1022             lp_servicename(snum),snum));
1023       printername = lp_servicename(snum);
1024     }
1025     
1026   if (!lprm_command || !(*lprm_command))
1027     {
1028       DEBUG(5,("No lprm command\n"));
1029       return;
1030     }
1031     
1032   sprintf(jobstr,"%d",jobid);
1033
1034   strcpy(syscmd,lprm_command);
1035   string_sub(syscmd,"%p",printername);
1036   string_sub(syscmd,"%j",jobstr);
1037   standard_sub(cnum,syscmd);
1038
1039   ret = smbrun(syscmd,NULL,False);
1040   DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));  
1041   lpq_reset(snum); /* queue has changed */
1042 }
1043
1044 /****************************************************************************
1045 change status of a printer queue entry
1046 ****************************************************************************/
1047 void status_printjob(int cnum,int snum,int jobid,int status)
1048 {
1049   char *lpstatus_command = 
1050     (status==LPQ_PAUSED?lp_lppausecommand(snum):lp_lpresumecommand(snum));
1051   char *printername = PRINTERNAME(snum);
1052   pstring syscmd;
1053   char jobstr[20];
1054   int ret;
1055
1056   if (!printername || !*printername)
1057     {
1058       DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
1059             lp_servicename(snum),snum));
1060       printername = lp_servicename(snum);
1061     }
1062     
1063   if (!lpstatus_command || !(*lpstatus_command))
1064     {
1065       DEBUG(5,("No lpstatus command to %s job\n",
1066                (status==LPQ_PAUSED?"pause":"resume")));
1067       return;
1068     }
1069     
1070   sprintf(jobstr,"%d",jobid);
1071
1072   strcpy(syscmd,lpstatus_command);
1073   string_sub(syscmd,"%p",printername);
1074   string_sub(syscmd,"%j",jobstr);
1075   standard_sub(cnum,syscmd);
1076
1077   ret = smbrun(syscmd,NULL,False);
1078   DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));  
1079   lpq_reset(snum); /* queue has changed */
1080 }
1081
1082