printing.c: Fixed overflow by one problem in LPRng.
[samba.git] / source3 / printing / printing.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    printing routines
5    Copyright (C) Andrew Tridgell 1992-1998
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       pstrcat(filename,"/");
85       pstrcat(filename,filename1);
86     }
87     else
88       pstrcpy(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(fstring 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   fstring 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     fstrcpy(tok[FILETOK],"STDIN");
253
254   /* only take the last part of the filename */
255   {
256     fstring tmp;
257     char *p = strrchr(tok[FILETOK],'/');
258     if (p)
259       {
260         fstrcpy(tmp,p+1);
261         fstrcpy(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(fstring tok[],int pos)
286 {
287   time_t jobtime;
288   struct tm *t;
289   fstring tmp_time;
290
291   jobtime = time(NULL);         /* default case: take current time */
292   t = localtime(&jobtime);
293   t->tm_hour = atoi(tok[pos]);
294   fstrcpy(tmp_time,tok[pos]);
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
326 NEW FOR LPRng-3.3.5 !
327
328 <reinelt@eunet.at> 
329 This will not happen anymore: with LPRng-3.3.5 there is always a blank between
330 the filename and the size, and the format has changed:
331
332 Printer: lj6@lizard  'HP LaserJet 6P'
333  Queue: 2 printable jobs
334  Server: pid 11941 active
335  Unspooler: pid 11942 active
336  Status: printed all 1818 bytes at 19:49:59
337  Rank   Owner/ID                   Class Job  Files               Size Time
338 active  root@lizard+937                A  937 (stdin)            1818 19:49:58
339 2       root@lizard+969                A  969 junk.txt           2170 19:50:12
340  
341 ****************************************************************************/
342 static BOOL parse_lpq_lprng(char *line,print_queue_struct *buf,BOOL first)
343 {
344 #define        LPRNG_RANKTOK   0
345 #define        LPRNG_USERTOK 1
346 #define        LPRNG_PRIOTOK 2
347 #define        LPRNG_JOBTOK    3
348 #define        LPRNG_FILETOK   4
349 #define        LPRNG_TOTALTOK 5
350 #define LPRNG_TIMETOK 6
351 #define        LPRNG_NTOK      7
352
353 /****************************************************************************
354 From lpd_status.c in LPRng source.
355 0        1         2         3         4         5         6         7
356 12345678901234567890123456789012345678901234567890123456789012345678901234 
357 " Rank  Owner           Class Job Files                           Size Time"
358                         plp_snprintf( msg, sizeof(msg), "%-6s %-19s %c %03d %-32s",
359                                 number, line, priority, cfp->number, error );
360                                 plp_snprintf( msg + len, sizeof(msg)-len, "%4d",
361                                         cfp->jobsize );
362                                 plp_snprintf( msg+len, sizeof(msg)-len, " %s",
363                                         Time_str( 1, cfp->statb.st_ctime ) );
364 ****************************************************************************/
365   /* The following define's are to be able to adjust the values if the
366 LPRng source changes.  This is from version 2.3.0.  Magnus  */
367 #define SPACE_W 1
368 #define RANK_W 6
369 #define OWNER_W 19
370 #define CLASS_W 1
371 #define JOB_W 3
372 #define FILE_W 32
373 /* The JOBSIZE_W is too small for big jobs, so time is pushed to the right */
374 #define JOBSIZE_W 4
375  
376 #define RANK_POS 0
377 #define OWNER_POS RANK_POS+RANK_W+SPACE_W
378 #define CLASS_POS OWNER_POS+OWNER_W+SPACE_W
379 #define JOB_POS CLASS_POS+CLASS_W+SPACE_W
380 #define FILE_POS JOB_POS+JOB_W+SPACE_W
381 #define JOBSIZE_POS FILE_POS+FILE_W
382
383   
384   fstring tok[LPRNG_NTOK];
385   int count=0;
386
387 #ifdef OLD_LPRNG
388 /* We only need this bugfix for older versions of lprng - current
389    information is that version 3.3.5 must not have this line
390    in order to work correctly.
391 */
392
393 /* 
394 Need to insert one space in front of the size, to be able to use
395 next_token() unchanged.  I would have liked to be able to insert a
396 space instead, to prevent losing that one char, but perl has spoiled
397 me :-\  So I did it the easiest way.
398
399 HINT: Use as short a path as possible for the samba spool directory.
400 A long spool-path will just waste significant chars of the file name.
401 */
402
403   line[JOBSIZE_POS-1]=' ';
404 #endif /* OLD_LPRNG */
405
406   /* handle the case of "(stdin)" as a filename */
407   string_sub(line,"stdin","STDIN");
408   string_sub(line,"(","\"");
409   string_sub(line,")","\"");
410   
411   for (count=0; count<LPRNG_NTOK && next_token(&line,tok[count],NULL); count++) ;
412
413   /* we must get LPRNG_NTOK tokens */
414   if (count < LPRNG_NTOK)
415     return(False);
416
417   /* the Job and Total columns must be integer */
418   if (!isdigit(*tok[LPRNG_JOBTOK]) || !isdigit(*tok[LPRNG_TOTALTOK])) return(False);
419
420   /* if the fname contains a space then use STDIN */
421   /* I do not understand how this would be possible. Magnus. */
422   if (strchr(tok[LPRNG_FILETOK],' '))
423     fstrcpy(tok[LPRNG_FILETOK],"STDIN");
424
425   /* only take the last part of the filename */
426   {
427     fstring tmp;
428     char *p = strrchr(tok[LPRNG_FILETOK],'/');
429     if (p)
430       {
431        fstrcpy(tmp,p+1);
432        fstrcpy(tok[LPRNG_FILETOK],tmp);
433       }
434   }
435        
436
437   buf->job = atoi(tok[LPRNG_JOBTOK]);
438   buf->size = atoi(tok[LPRNG_TOTALTOK]);
439   if (strequal(tok[LPRNG_RANKTOK],"active"))
440     buf->status = LPQ_PRINTING;
441   else if (strequal(tok[LPRNG_RANKTOK],"hold"))
442     buf->status = LPQ_PAUSED;
443   else
444     buf->status = LPQ_QUEUED;
445   /*  buf->time = time(NULL); */
446   buf->time = LPRng_time(tok,LPRNG_TIMETOK);
447 DEBUG(3,("Time reported for job %d is %s", buf->job, ctime(&buf->time)));
448   StrnCpy(buf->user,tok[LPRNG_USERTOK],sizeof(buf->user)-1);
449   StrnCpy(buf->file,tok[LPRNG_FILETOK],sizeof(buf->file)-1);
450 #ifdef LPRNG_PRIOTOK
451   /* Here I try to map the CLASS char to a number, but the number
452      is never shown in Print Manager under NT anyway... Magnus. */
453   buf->priority = atoi(tok[LPRNG_PRIOTOK]-('A'-1));
454 #else
455   buf->priority = 1;
456 #endif
457   return(True);
458 }
459
460
461
462 /*******************************************************************
463 parse lpq on an aix system
464
465 Queue   Dev   Status    Job Files              User         PP %   Blks  Cp Rnk
466 ------- ----- --------- --- ------------------ ---------- ---- -- ----- --- ---
467 lazer   lazer READY
468 lazer   lazer RUNNING   537 6297doc.A          kvintus@IE    0 10  2445   1   1
469               QUEUED    538 C.ps               root@IEDVB           124   1   2
470               QUEUED    539 E.ps               root@IEDVB            28   1   3
471               QUEUED    540 L.ps               root@IEDVB           172   1   4
472               QUEUED    541 P.ps               root@IEDVB            22   1   5
473 ********************************************************************/
474 static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first)
475 {
476   fstring tok[11];
477   int count=0;
478
479   /* handle the case of "(standard input)" as a filename */
480   string_sub(line,"standard input","STDIN");
481   string_sub(line,"(","\"");
482   string_sub(line,")","\"");
483
484   for (count=0; count<10 && next_token(&line,tok[count],NULL); count++) ;
485
486   /* we must get 6 tokens */
487   if (count < 10)
488   {
489       if ((count == 7) && ((strcmp(tok[0],"QUEUED") == 0) || (strcmp(tok[0],"HELD") == 0)))
490       {
491           /* the 2nd and 5th columns must be integer */
492           if (!isdigit(*tok[1]) || !isdigit(*tok[4])) return(False);
493           buf->size = atoi(tok[4]) * 1024;
494           /* if the fname contains a space then use STDIN */
495           if (strchr(tok[2],' '))
496             fstrcpy(tok[2],"STDIN");
497
498           /* only take the last part of the filename */
499           {
500             fstring tmp;
501             char *p = strrchr(tok[2],'/');
502             if (p)
503               {
504                 fstrcpy(tmp,p+1);
505                 fstrcpy(tok[2],tmp);
506               }
507           }
508
509
510           buf->job = atoi(tok[1]);
511           buf->status = strequal(tok[0],"HELD")?LPQ_PAUSED:LPQ_QUEUED;
512           buf->priority = 0;
513           buf->time = time(NULL);
514           StrnCpy(buf->user,tok[3],sizeof(buf->user)-1);
515           StrnCpy(buf->file,tok[2],sizeof(buf->file)-1);
516       }
517       else
518       {
519           DEBUG(6,("parse_lpq_aix count=%d\n", count));
520           return(False);
521       }
522   }
523   else
524   {
525       /* the 4th and 9th columns must be integer */
526       if (!isdigit(*tok[3]) || !isdigit(*tok[8])) return(False);
527       buf->size = atoi(tok[8]) * 1024;
528       /* if the fname contains a space then use STDIN */
529       if (strchr(tok[4],' '))
530         fstrcpy(tok[4],"STDIN");
531
532       /* only take the last part of the filename */
533       {
534         fstring tmp;
535         char *p = strrchr(tok[4],'/');
536         if (p)
537           {
538             fstrcpy(tmp,p+1);
539             fstrcpy(tok[4],tmp);
540           }
541       }
542
543
544       buf->job = atoi(tok[3]);
545       buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED;
546       buf->priority = 0;
547       buf->time = time(NULL);
548       StrnCpy(buf->user,tok[5],sizeof(buf->user)-1);
549       StrnCpy(buf->file,tok[4],sizeof(buf->file)-1);
550   }
551
552
553   return(True);
554 }
555
556
557 /****************************************************************************
558 parse a lpq line
559 here is an example of lpq output under hpux; note there's no space after -o !
560 $> lpstat -oljplus
561 ljplus-2153         user           priority 0  Jan 19 08:14 on ljplus
562       util.c                                  125697 bytes
563       server.c                                110712 bytes
564 ljplus-2154         user           priority 0  Jan 19 08:14 from client
565       (standard input)                          7551 bytes
566 ****************************************************************************/
567 static BOOL parse_lpq_hpux(char * line, print_queue_struct *buf, BOOL first)
568 {
569   /* must read two lines to process, therefore keep some values static */
570   static BOOL header_line_ok=False, base_prio_reset=False;
571   static fstring jobuser;
572   static int jobid;
573   static int jobprio;
574   static time_t jobtime;
575   static int jobstat=LPQ_QUEUED;
576   /* to store minimum priority to print, lpstat command should be invoked
577      with -p option first, to work */
578   static int base_prio;
579  
580   int count;
581   char TAB = '\011';  
582   fstring tok[12];
583
584   /* If a line begins with a horizontal TAB, it is a subline type */
585   
586   if (line[0] == TAB) { /* subline */
587     /* check if it contains the base priority */
588     if (!strncmp(line,"\tfence priority : ",18)) {
589        base_prio=atoi(&line[18]);
590        DEBUG(4, ("fence priority set at %d\n", base_prio));
591     }
592     if (!header_line_ok) return (False); /* incorrect header line */
593     /* handle the case of "(standard input)" as a filename */
594     string_sub(line,"standard input","STDIN");
595     string_sub(line,"(","\"");
596     string_sub(line,")","\"");
597     
598     for (count=0; count<2 && next_token(&line,tok[count],NULL); count++) ;
599     /* we must get 2 tokens */
600     if (count < 2) return(False);
601     
602     /* the 2nd column must be integer */
603     if (!isdigit(*tok[1])) return(False);
604     
605     /* if the fname contains a space then use STDIN */
606     if (strchr(tok[0],' '))
607       fstrcpy(tok[0],"STDIN");
608     
609     buf->size = atoi(tok[1]);
610     StrnCpy(buf->file,tok[0],sizeof(buf->file)-1);
611     
612     /* fill things from header line */
613     buf->time = jobtime;
614     buf->job = jobid;
615     buf->status = jobstat;
616     buf->priority = jobprio;
617     StrnCpy(buf->user,jobuser,sizeof(buf->user)-1);
618     
619     return(True);
620   }
621   else { /* header line */
622     header_line_ok=False; /* reset it */
623     if (first) {
624        if (!base_prio_reset) {
625           base_prio=0; /* reset it */
626           base_prio_reset=True;
627        }
628     }
629     else if (base_prio) base_prio_reset=False;
630     
631     /* handle the dash in the job id */
632     string_sub(line,"-"," ");
633     
634     for (count=0; count<12 && next_token(&line,tok[count],NULL); count++) ;
635       
636     /* we must get 8 tokens */
637     if (count < 8) return(False);
638     
639     /* first token must be printer name (cannot check ?) */
640     /* the 2nd, 5th & 7th column must be integer */
641     if (!isdigit(*tok[1]) || !isdigit(*tok[4]) || !isdigit(*tok[6])) return(False);
642     jobid = atoi(tok[1]);
643     StrnCpy(jobuser,tok[2],sizeof(buf->user)-1);
644     jobprio = atoi(tok[4]);
645     
646     /* process time */
647     jobtime=EntryTime(tok, 5, count, 8);
648     if (jobprio < base_prio) {
649        jobstat = LPQ_PAUSED;
650        DEBUG (4, ("job %d is paused: prio %d < %d; jobstat=%d\n", jobid, jobprio, base_prio, jobstat));
651     }
652     else {
653        jobstat = LPQ_QUEUED;
654        if ((count >8) && (((strequal(tok[8],"on")) ||
655                            ((strequal(tok[8],"from")) && 
656                             ((count > 10)&&(strequal(tok[10],"on")))))))
657          jobstat = LPQ_PRINTING;
658     }
659     
660     header_line_ok=True; /* information is correct */
661     return(False); /* need subline info to include into queuelist */
662   }
663 }
664
665
666 /****************************************************************************
667 parse a lpq line
668
669 here is an example of "lpstat -o dcslw" output under sysv
670
671 dcslw-896               tridge            4712   Dec 20 10:30:30 on dcslw
672 dcslw-897               tridge            4712   Dec 20 10:30:30 being held
673
674 ****************************************************************************/
675 static BOOL parse_lpq_sysv(char *line,print_queue_struct *buf,BOOL first)
676 {
677   fstring tok[9];
678   int count=0;
679   char *p;
680
681   /* handle the dash in the job id */
682   string_sub(line,"-"," ");
683   
684   for (count=0; count<9 && next_token(&line,tok[count],NULL); count++) ;
685
686   /* we must get 7 tokens */
687   if (count < 7)
688     return(False);
689
690   /* the 2nd and 4th, 6th columns must be integer */
691   if (!isdigit(*tok[1]) || !isdigit(*tok[3])) return(False);
692   if (!isdigit(*tok[5])) return(False);
693
694   /* if the user contains a ! then trim the first part of it */  
695   if ((p=strchr(tok[2],'!')))
696     {
697       fstring tmp;
698       fstrcpy(tmp,p+1);
699       fstrcpy(tok[2],tmp);
700     }
701     
702
703   buf->job = atoi(tok[1]);
704   buf->size = atoi(tok[3]);
705   if (count > 7 && strequal(tok[7],"on"))
706     buf->status = LPQ_PRINTING;
707   else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held"))
708     buf->status = LPQ_PAUSED;
709   else
710     buf->status = LPQ_QUEUED;
711   buf->priority = 0;
712   buf->time = EntryTime(tok, 4, count, 7);
713   StrnCpy(buf->user,tok[2],sizeof(buf->user)-1);
714   StrnCpy(buf->file,tok[2],sizeof(buf->file)-1);
715   return(True);
716 }
717
718 /****************************************************************************
719 parse a lpq line
720
721 here is an example of lpq output under qnx
722 Spooler: /qnx/spooler, on node 1
723 Printer: txt        (ready) 
724 0000:     root  [job #1    ]   active 1146 bytes        /etc/profile
725 0001:     root  [job #2    ]    ready 2378 bytes        /etc/install
726 0002:     root  [job #3    ]    ready 1146 bytes        -- standard input --
727 ****************************************************************************/
728 static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first)
729 {
730   fstring tok[7];
731   int count=0;
732
733   DEBUG(0,("antes [%s]\n", line));
734
735   /* handle the case of "-- standard input --" as a filename */
736   string_sub(line,"standard input","STDIN");
737   DEBUG(0,("despues [%s]\n", line));
738   string_sub(line,"-- ","\"");
739   string_sub(line," --","\"");
740   DEBUG(0,("despues 1 [%s]\n", line));
741
742   string_sub(line,"[job #","");
743   string_sub(line,"]","");
744   DEBUG(0,("despues 2 [%s]\n", line));
745
746   
747   
748   for (count=0; count<7 && next_token(&line,tok[count],NULL); count++) ;
749
750   /* we must get 7 tokens */
751   if (count < 7)
752     return(False);
753
754   /* the 3rd and 5th columns must be integer */
755   if (!isdigit(*tok[2]) || !isdigit(*tok[4])) return(False);
756
757   /* only take the last part of the filename */
758   {
759     fstring tmp;
760     char *p = strrchr(tok[6],'/');
761     if (p)
762       {
763         fstrcpy(tmp,p+1);
764         fstrcpy(tok[6],tmp);
765       }
766   }
767         
768
769   buf->job = atoi(tok[2]);
770   buf->size = atoi(tok[4]);
771   buf->status = strequal(tok[3],"active")?LPQ_PRINTING:LPQ_QUEUED;
772   buf->priority = 0;
773   buf->time = time(NULL);
774   StrnCpy(buf->user,tok[1],sizeof(buf->user)-1);
775   StrnCpy(buf->file,tok[6],sizeof(buf->file)-1);
776   return(True);
777 }
778
779
780 /****************************************************************************
781   parse a lpq line for the plp printing system
782   Bertrand Wallrich <Bertrand.Wallrich@loria.fr>
783
784 redone by tridge. Here is a sample queue:
785
786 Local  Printer 'lp2' (fjall):
787   Printing (started at Jun 15 13:33:58, attempt 1).
788     Rank Owner       Pr Opt  Job Host        Files           Size     Date
789   active tridge      X  -    6   fjall       /etc/hosts      739      Jun 15 13:33
790      3rd tridge      X  -    7   fjall       /etc/hosts      739      Jun 15 13:33
791
792 ****************************************************************************/
793 static BOOL parse_lpq_plp(char *line,print_queue_struct *buf,BOOL first)
794 {
795   fstring tok[11];
796   int count=0;
797
798   /* handle the case of "(standard input)" as a filename */
799   string_sub(line,"stdin","STDIN");
800   string_sub(line,"(","\"");
801   string_sub(line,")","\"");
802   
803   for (count=0; count<11 && next_token(&line,tok[count],NULL); count++) ;
804
805   /* we must get 11 tokens */
806   if (count < 11)
807     return(False);
808
809   /* the first must be "active" or begin with an integer */
810   if (strcmp(tok[0],"active") && !isdigit(tok[0][0]))
811     return(False);
812
813   /* the 5th and 8th must be integer */
814   if (!isdigit(*tok[4]) || !isdigit(*tok[7])) 
815     return(False);
816
817   /* if the fname contains a space then use STDIN */
818   if (strchr(tok[6],' '))
819     fstrcpy(tok[6],"STDIN");
820
821   /* only take the last part of the filename */
822   {
823     fstring tmp;
824     char *p = strrchr(tok[6],'/');
825     if (p)
826       {
827         fstrcpy(tmp,p+1);
828         fstrcpy(tok[6],tmp);
829       }
830   }
831
832
833   buf->job = atoi(tok[4]);
834
835   buf->size = atoi(tok[7]);
836   if (strchr(tok[7],'K'))
837     buf->size *= 1024;
838   if (strchr(tok[7],'M'))
839     buf->size *= 1024*1024;
840
841   buf->status = strequal(tok[0],"active")?LPQ_PRINTING:LPQ_QUEUED;
842   buf->priority = 0;
843   buf->time = time(NULL);
844   StrnCpy(buf->user,tok[1],sizeof(buf->user)-1);
845   StrnCpy(buf->file,tok[6],sizeof(buf->file)-1);
846   return(True);
847 }
848
849 /****************************************************************************
850 parse a qstat line
851
852 here is an example of "qstat -l -d qms" output under softq
853
854 Queue qms: 2 jobs; daemon active (313); enabled; accepting;
855  job-ID   submission-time     pri     size owner      title 
856 205980: H 98/03/09 13:04:05     0    15733 stephenf   chap1.ps
857 206086:>  98/03/12 17:24:40     0      659 chris      -
858 206087:   98/03/12 17:24:45     0     4876 chris      -
859 Total:      21268 bytes in queue
860
861
862 ****************************************************************************/
863 static BOOL parse_lpq_softq(char *line,print_queue_struct *buf,BOOL first)
864 {
865   fstring tok[10];
866   int count=0;
867
868   /* mung all the ":"s to spaces*/
869   string_sub(line,":"," ");
870   
871   for (count=0; count<10 && next_token(&line,tok[count],NULL); count++) ;
872
873   /* we must get 9 tokens */
874   if (count < 9)
875     return(False);
876
877   /* the 1st and 7th columns must be integer */
878   if (!isdigit(*tok[0]) || !isdigit(*tok[6]))  return(False);
879   /* if the 2nd column is either '>' or 'H' then the 7th and 8th must be
880    * integer, else it's the 6th and 7th that must be
881    */
882   if (*tok[1] == 'H' || *tok[1] == '>')
883     {
884       if (!isdigit(*tok[7]))
885         return(False);
886       buf->status = *tok[1] == '>' ? LPQ_PRINTING : LPQ_PAUSED;
887       count = 1;
888     }
889   else
890     {
891       if (!isdigit(*tok[5]))
892         return(False);
893       buf->status = LPQ_QUEUED;
894       count = 0;
895     }
896         
897
898   buf->job = atoi(tok[0]);
899   buf->size = atoi(tok[count+6]);
900   buf->priority = atoi(tok[count+5]);
901   StrnCpy(buf->user,tok[count+7],sizeof(buf->user)-1);
902   StrnCpy(buf->file,tok[count+8],sizeof(buf->file)-1);
903   buf->time = time(NULL);               /* default case: take current time */
904   {
905     time_t jobtime;
906     struct tm *t;
907
908     t = localtime(&buf->time);
909     t->tm_mday = atoi(tok[count+2]+6);
910     t->tm_mon  = atoi(tok[count+2]+3);
911     switch (*tok[count+2])
912     {
913     case 7: case 8: case 9: t->tm_year = atoi(tok[count+2]) + 1900; break;
914     default:                t->tm_year = atoi(tok[count+2]) + 2000; break;
915     }
916
917     t->tm_hour = atoi(tok[count+3]);
918     t->tm_min = atoi(tok[count+4]);
919     t->tm_sec = atoi(tok[count+5]);
920     jobtime = mktime(t);
921     if (jobtime != (time_t)-1)
922       buf->time = jobtime; 
923   }
924
925   return(True);
926 }
927
928
929 char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
930 char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
931 char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL };
932
933 /****************************************************************************
934 parse a lpq line. Choose printing style
935 ****************************************************************************/
936 static BOOL parse_lpq_entry(int snum,char *line,
937                             print_queue_struct *buf,
938                             print_status_struct *status,BOOL first)
939 {
940   BOOL ret;
941
942   switch (lp_printing(snum))
943     {
944     case PRINT_SYSV:
945       ret = parse_lpq_sysv(line,buf,first);
946       break;
947     case PRINT_AIX:      
948       ret = parse_lpq_aix(line,buf,first);
949       break;
950     case PRINT_HPUX:
951       ret = parse_lpq_hpux(line,buf,first);
952       break;
953     case PRINT_QNX:
954       ret = parse_lpq_qnx(line,buf,first);
955       break;
956     case PRINT_LPRNG:
957       ret = parse_lpq_lprng(line,buf,first);
958       break;
959     case PRINT_PLP:
960       ret = parse_lpq_plp(line,buf,first);
961       break;
962     case PRINT_SOFTQ:
963       ret = parse_lpq_softq(line,buf,first);
964       break;
965     default:
966       ret = parse_lpq_bsd(line,buf,first);
967       break;
968     }
969
970 #ifdef LPQ_GUEST_TO_USER
971   if (ret) {
972     extern pstring sesssetup_user;
973     /* change guest entries to the current logged in user to make
974        them appear deletable to windows */
975     if (sesssetup_user[0] && strequal(buf->user,lp_guestaccount(snum)))
976       pstrcpy(buf->user,sesssetup_user);
977   }
978 #endif
979
980   /* We don't want the newline in the status message. */
981   {
982     char *p = strchr(line,'\n');
983     if (p) *p = 0;
984   }
985
986   if (status && !ret)
987     {
988       /* a few simple checks to see if the line might be a
989          printer status line: 
990          handle them so that most severe condition is shown */
991       int i;
992       strlower(line);
993       
994       switch (status->status) {
995       case LPSTAT_OK:
996         for (i=0; stat0_strings[i]; i++)
997           if (strstr(line,stat0_strings[i])) {
998             StrnCpy(status->message,line,sizeof(status->message)-1);
999             status->status=LPSTAT_OK;
1000             return ret;
1001           }
1002       case LPSTAT_STOPPED:
1003         for (i=0; stat1_strings[i]; i++)
1004           if (strstr(line,stat1_strings[i])) {
1005             StrnCpy(status->message,line,sizeof(status->message)-1);
1006             status->status=LPSTAT_STOPPED;
1007             return ret;
1008           }
1009       case LPSTAT_ERROR:
1010         for (i=0; stat2_strings[i]; i++)
1011           if (strstr(line,stat2_strings[i])) {
1012             StrnCpy(status->message,line,sizeof(status->message)-1);
1013             status->status=LPSTAT_ERROR;
1014             return ret;
1015           }
1016         break;
1017       }
1018     }
1019
1020   return(ret);
1021 }
1022
1023 /****************************************************************************
1024 get a printer queue
1025 ****************************************************************************/
1026 int get_printqueue(int snum,int cnum,print_queue_struct **queue,
1027                    print_status_struct *status)
1028 {
1029   char *lpq_command = lp_lpqcommand(snum);
1030   char *printername = PRINTERNAME(snum);
1031   int ret=0,count=0;
1032   pstring syscmd;
1033   fstring outfile;
1034   pstring line;
1035   FILE *f;
1036   struct stat sbuf;
1037   BOOL dorun=True;
1038   int cachetime = lp_lpqcachetime();
1039
1040   *line = 0;
1041   check_lpq_cache(snum);
1042   
1043   if (!printername || !*printername)
1044     {
1045       DEBUG(6,("xx replacing printer name with service (snum=(%s,%d))\n",
1046                lp_servicename(snum),snum));
1047       printername = lp_servicename(snum);
1048     }
1049     
1050   if (!lpq_command || !(*lpq_command))
1051     {
1052       DEBUG(5,("No lpq command\n"));
1053       return(0);
1054     }
1055     
1056   pstrcpy(syscmd,lpq_command);
1057   string_sub(syscmd,"%p",printername);
1058
1059   standard_sub(cnum,syscmd);
1060
1061   slprintf(outfile,sizeof(outfile)-1, "%s/lpq.%08x",tmpdir(),str_checksum(syscmd));
1062   
1063   if (!lpq_cache_reset[snum] && cachetime && !stat(outfile,&sbuf)) 
1064     {
1065       if (time(NULL) - sbuf.st_mtime < cachetime) {
1066         DEBUG(3,("Using cached lpq output\n"));
1067         dorun = False;
1068       }
1069     }
1070
1071   if (dorun) {
1072     ret = smbrun(syscmd,outfile,True);
1073     DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
1074   }
1075
1076   lpq_cache_reset[snum] = False;
1077
1078   f = fopen(outfile,"r");
1079   if (!f) {
1080     return(0);
1081   }
1082
1083   if (status) {
1084     fstrcpy(status->message,"");
1085     status->status = LPSTAT_OK;
1086   }
1087       
1088   while (fgets(line,sizeof(pstring),f))
1089     {
1090       DEBUG(6,("QUEUE2: %s\n",line));
1091
1092       *queue = Realloc(*queue,sizeof(print_queue_struct)*(count+1));
1093       if (! *queue)
1094         {
1095           count = 0;
1096           break;
1097         }
1098
1099       bzero((char *)&(*queue)[count],sizeof(**queue));
1100           
1101       /* parse it */
1102       if (!parse_lpq_entry(snum,line,&(*queue)[count],status,count==0))
1103         continue;
1104           
1105       count++;
1106     }         
1107
1108   fclose(f);
1109
1110   if (!cachetime) {
1111     unlink(outfile);
1112   } else {
1113     /* we only expect this to succeed on trapdoor systems, on normal systems
1114      the file is owned by root */
1115     chmod(outfile,0666);
1116   }
1117   return(count);
1118 }
1119
1120
1121 /****************************************************************************
1122 delete a printer queue entry
1123 ****************************************************************************/
1124 void del_printqueue(int cnum,int snum,int jobid)
1125 {
1126   char *lprm_command = lp_lprmcommand(snum);
1127   char *printername = PRINTERNAME(snum);
1128   pstring syscmd;
1129   char jobstr[20];
1130   int ret;
1131
1132   if (!printername || !*printername)
1133     {
1134       DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
1135             lp_servicename(snum),snum));
1136       printername = lp_servicename(snum);
1137     }
1138     
1139   if (!lprm_command || !(*lprm_command))
1140     {
1141       DEBUG(5,("No lprm command\n"));
1142       return;
1143     }
1144     
1145   slprintf(jobstr,sizeof(jobstr)-1,"%d",jobid);
1146
1147   pstrcpy(syscmd,lprm_command);
1148   string_sub(syscmd,"%p",printername);
1149   string_sub(syscmd,"%j",jobstr);
1150   standard_sub(cnum,syscmd);
1151
1152   ret = smbrun(syscmd,NULL,False);
1153   DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));  
1154   lpq_reset(snum); /* queue has changed */
1155 }
1156
1157 /****************************************************************************
1158 change status of a printer queue entry
1159 ****************************************************************************/
1160 void status_printjob(int cnum,int snum,int jobid,int status)
1161 {
1162   char *lpstatus_command = 
1163     (status==LPQ_PAUSED?lp_lppausecommand(snum):lp_lpresumecommand(snum));
1164   char *printername = PRINTERNAME(snum);
1165   pstring syscmd;
1166   char jobstr[20];
1167   int ret;
1168
1169   if (!printername || !*printername)
1170     {
1171       DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
1172             lp_servicename(snum),snum));
1173       printername = lp_servicename(snum);
1174     }
1175     
1176   if (!lpstatus_command || !(*lpstatus_command))
1177     {
1178       DEBUG(5,("No lpstatus command to %s job\n",
1179                (status==LPQ_PAUSED?"pause":"resume")));
1180       return;
1181     }
1182     
1183   slprintf(jobstr,sizeof(jobstr)-1,"%d",jobid);
1184
1185   pstrcpy(syscmd,lpstatus_command);
1186   string_sub(syscmd,"%p",printername);
1187   string_sub(syscmd,"%j",jobstr);
1188   standard_sub(cnum,syscmd);
1189
1190   ret = smbrun(syscmd,NULL,False);
1191   DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));  
1192   lpq_reset(snum); /* queue has changed */
1193 }
1194
1195
1196
1197 /****************************************************************************
1198 we encode print job numbers over the wire so that when we get them back we can
1199 tell not only what print job they are but also what service it belongs to,
1200 this is to overcome the problem that windows clients tend to send the wrong
1201 service number when doing print queue manipulation!
1202 ****************************************************************************/
1203 int printjob_encode(int snum, int job)
1204 {
1205         return ((snum&0xFF)<<8) | (job & 0xFF);
1206 }
1207
1208 /****************************************************************************
1209 and now decode them again ...
1210 ****************************************************************************/
1211 void printjob_decode(int jobid, int *snum, int *job)
1212 {
1213         (*snum) = (jobid >> 8) & 0xFF;
1214         (*job) = jobid & 0xFF;
1215 }