bounds check next_token() to prevent possible buffer overflows
[Samba/gbeck.git] / source / printing / printing.c
blobfae4c1cc05e0ec1c0419c26357b3ca5e25e39955
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 printing routines
5 Copyright (C) Andrew Tridgell 1992-1998
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.
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.
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.
22 #include "includes.h"
23 extern int DEBUGLEVEL;
25 static BOOL * lpq_cache_reset=NULL;
27 static int check_lpq_cache(int snum) {
28 static int lpq_caches=0;
30 if (lpq_caches <= snum) {
31 BOOL * p;
32 p = (BOOL *) Realloc(lpq_cache_reset,(snum+1)*sizeof(BOOL));
33 if (p) {
34 lpq_cache_reset=p;
35 lpq_caches = snum+1;
38 return lpq_caches;
41 void lpq_reset(int snum)
43 if (check_lpq_cache(snum) > snum) lpq_cache_reset[snum]=True;
47 /****************************************************************************
48 Build the print command in the supplied buffer. This means getting the
49 print command for the service and inserting the printer name and the
50 print file name. Return NULL on error, else the passed buffer pointer.
51 ****************************************************************************/
52 static char *build_print_command(connection_struct *conn,
53 char *command,
54 char *syscmd, char *filename1)
56 int snum = SNUM(conn);
57 char *tstr;
58 pstring filename;
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",
64 SERVICE(snum)));
65 return (NULL);
68 /* copy the command into the buffer for extensive meddling. */
69 StrnCpy(syscmd, tstr, sizeof(pstring) - 1);
71 /* look for "%s" in the string. If there is no %s, we cannot print. */
72 if (!strstr(syscmd, "%s") && !strstr(syscmd, "%f")) {
73 DEBUG(2,("WARNING! No placeholder for the filename in the print command for service %s!\n", SERVICE(snum)));
76 if (strstr(syscmd,"%s")) {
77 pstrcpy(filename,filename1);
79 string_sub(syscmd, "%s", filename);
82 string_sub(syscmd, "%f", filename1);
84 /* Does the service have a printername? If not, make a fake
85 and empty */
86 /* printer name. That way a %p is treated sanely if no printer */
87 /* name was specified to replace it. This eventuality is logged. */
88 tstr = PRINTERNAME(snum);
89 if (tstr == NULL || tstr[0] == '\0') {
90 DEBUG(3,( "No printer name - using %s.\n", SERVICE(snum)));
91 tstr = SERVICE(snum);
94 string_sub(syscmd, "%p", tstr);
96 standard_sub(conn,syscmd);
98 return (syscmd);
102 /****************************************************************************
103 print a file - called on closing the file
104 ****************************************************************************/
105 void print_file(connection_struct *conn, files_struct *file)
107 pstring syscmd;
108 int snum = SNUM(conn);
109 char *tempstr;
111 *syscmd = 0;
113 if (file_size(file->fsp_name) <= 0) {
114 DEBUG(3,("Discarding null print job %s\n",file->fsp_name));
115 sys_unlink(file->fsp_name);
116 return;
119 tempstr = build_print_command(conn,
120 PRINTCOMMAND(snum),
121 syscmd, file->fsp_name);
122 if (tempstr != NULL) {
123 int ret = smbrun(syscmd,NULL,False);
124 DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
125 } else {
126 DEBUG(0,("Null print command?\n"));
129 lpq_reset(snum);
132 static char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
133 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"};
136 /*******************************************************************
137 process time fields
138 ********************************************************************/
139 static time_t EntryTime(fstring tok[], int ptr, int count, int minimum)
141 time_t jobtime,jobtime1;
143 jobtime = time(NULL); /* default case: take current time */
144 if (count >= minimum) {
145 struct tm *t;
146 int i, day, hour, min, sec;
147 char *c;
149 for (i=0; i<13; i++) if (!strncmp(tok[ptr], Months[i],3)) break; /* Find month */
150 if (i<12) {
151 t = localtime(&jobtime);
152 day = atoi(tok[ptr+1]);
153 c=(char *)(tok[ptr+2]);
154 *(c+2)=0;
155 hour = atoi(c);
156 *(c+5)=0;
157 min = atoi(c+3);
158 if(*(c+6) != 0)sec = atoi(c+6);
159 else sec=0;
161 if ((t->tm_mon < i)||
162 ((t->tm_mon == i)&&
163 ((t->tm_mday < day)||
164 ((t->tm_mday == day)&&
165 (t->tm_hour*60+t->tm_min < hour*60+min)))))
166 t->tm_year--; /* last year's print job */
168 t->tm_mon = i;
169 t->tm_mday = day;
170 t->tm_hour = hour;
171 t->tm_min = min;
172 t->tm_sec = sec;
173 jobtime1 = mktime(t);
174 if (jobtime1 != (time_t)-1)
175 jobtime = jobtime1;
178 return jobtime;
182 /****************************************************************************
183 parse a lpq line
185 here is an example of lpq output under bsd
187 Warning: no daemon present
188 Rank Owner Job Files Total Size
189 1st tridge 148 README 8096 bytes
191 here is an example of lpq output under osf/1
193 Warning: no daemon present
194 Rank Pri Owner Job Files Total Size
195 1st 0 tridge 148 README 8096 bytes
196 ****************************************************************************/
197 static BOOL parse_lpq_bsd(char *line,print_queue_struct *buf,BOOL first)
199 #ifdef OSF1
200 #define RANKTOK 0
201 #define PRIOTOK 1
202 #define USERTOK 2
203 #define JOBTOK 3
204 #define FILETOK 4
205 #define TOTALTOK 5
206 #define NTOK 6
207 #else /* OSF1 */
208 #define RANKTOK 0
209 #define USERTOK 1
210 #define JOBTOK 2
211 #define FILETOK 3
212 #define TOTALTOK 4
213 #define NTOK 5
214 #endif /* OSF1 */
216 fstring tok[NTOK];
217 int count=0;
219 #ifdef OSF1
220 int length;
221 length = strlen(line);
222 if (line[length-3] == ':')
223 return(False);
224 #endif /* OSF1 */
226 /* handle the case of "(standard input)" as a filename */
227 string_sub(line,"standard input","STDIN");
228 string_sub(line,"(","\"");
229 string_sub(line,")","\"");
231 for (count=0;
232 count<NTOK &&
233 next_token(&line,tok[count],NULL, sizeof(tok[count]));
234 count++) ;
236 /* we must get NTOK tokens */
237 if (count < NTOK)
238 return(False);
240 /* the Job and Total columns must be integer */
241 if (!isdigit((int)*tok[JOBTOK]) || !isdigit((int)*tok[TOTALTOK])) return(False);
243 /* if the fname contains a space then use STDIN */
244 if (strchr(tok[FILETOK],' '))
245 fstrcpy(tok[FILETOK],"STDIN");
247 /* only take the last part of the filename */
249 fstring tmp;
250 char *p = strrchr(tok[FILETOK],'/');
251 if (p)
253 fstrcpy(tmp,p+1);
254 fstrcpy(tok[FILETOK],tmp);
259 buf->job = atoi(tok[JOBTOK]);
260 buf->size = atoi(tok[TOTALTOK]);
261 buf->status = strequal(tok[RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED;
262 buf->time = time(NULL);
263 StrnCpy(buf->user,tok[USERTOK],sizeof(buf->user)-1);
264 StrnCpy(buf->file,tok[FILETOK],sizeof(buf->file)-1);
265 #ifdef PRIOTOK
266 buf->priority = atoi(tok[PRIOTOK]);
267 #else
268 buf->priority = 1;
269 #endif
270 return(True);
274 <magnus@hum.auc.dk>
275 LPRng_time modifies the current date by inserting the hour and minute from
276 the lpq output. The lpq time looks like "23:15:07"
278 static time_t LPRng_time(fstring tok[],int pos)
280 time_t jobtime;
281 struct tm *t;
282 fstring tmp_time;
284 jobtime = time(NULL); /* default case: take current time */
285 t = localtime(&jobtime);
286 t->tm_hour = atoi(tok[pos]);
287 fstrcpy(tmp_time,tok[pos]);
288 t->tm_min = atoi(tmp_time+3);
289 t->tm_sec = atoi(tmp_time+6);
290 jobtime = mktime(t);
292 return jobtime;
296 /****************************************************************************
297 parse a lpq line
298 <magnus@hum.auc.dk>
299 Most of the code is directly reused from parse_lpq_bsd()
301 here are two examples of lpq output under lprng (LPRng-2.3.0)
303 Printer: humprn@hum-fak
304 Queue: 1 printable job
305 Server: pid 4840 active, Unspooler: pid 4841 active
306 Status: job 'cfA659hum-fak', closing device at Fri Jun 21 10:10:21 1996
307 Rank Owner Class Job Files Size Time
308 active magnus@hum-fak A 659 /var/spool/smb/Notesblok-ikke-na4024 10:03:31
310 Printer: humprn@hum-fak (printing disabled)
311 Queue: 1 printable job
312 Warning: no server present
313 Status: finished operations at Fri Jun 21 10:10:32 1996
314 Rank Owner Class Job Files Size Time
315 1 magnus@hum-fak A 387 /var/spool/smb/netbudget.xls 21230 10:50:53
317 ******************************************************************************
319 NEW FOR LPRng-3.3.5 !
321 <reinelt@eunet.at>
322 This will not happen anymore: with LPRng-3.3.5 there is always a blank between
323 the filename and the size, and the format has changed:
325 Printer: lj6@lizard 'HP LaserJet 6P'
326 Queue: 2 printable jobs
327 Server: pid 11941 active
328 Unspooler: pid 11942 active
329 Status: printed all 1818 bytes at 19:49:59
330 Rank Owner/ID Class Job Files Size Time
331 active root@lizard+937 A 937 (stdin) 1818 19:49:58
332 2 root@lizard+969 A 969 junk.txt 2170 19:50:12
334 ****************************************************************************/
335 static BOOL parse_lpq_lprng(char *line,print_queue_struct *buf,BOOL first)
337 #define LPRNG_RANKTOK 0
338 #define LPRNG_USERTOK 1
339 #define LPRNG_PRIOTOK 2
340 #define LPRNG_JOBTOK 3
341 #define LPRNG_FILETOK 4
342 #define LPRNG_TOTALTOK 5
343 #define LPRNG_TIMETOK 6
344 #define LPRNG_NTOK 7
346 /****************************************************************************
347 From lpd_status.c in LPRng source.
348 0 1 2 3 4 5 6 7
349 12345678901234567890123456789012345678901234567890123456789012345678901234
350 " Rank Owner Class Job Files Size Time"
351 plp_snprintf( msg, sizeof(msg), "%-6s %-19s %c %03d %-32s",
352 number, line, priority, cfp->number, error );
353 plp_snprintf( msg + len, sizeof(msg)-len, "%4d",
354 cfp->jobsize );
355 plp_snprintf( msg+len, sizeof(msg)-len, " %s",
356 Time_str( 1, cfp->statb.st_ctime ) );
357 ****************************************************************************/
358 /* The following define's are to be able to adjust the values if the
359 LPRng source changes. This is from version 2.3.0. Magnus */
360 #define SPACE_W 1
361 #define RANK_W 6
362 #define OWNER_W 19
363 #define CLASS_W 1
364 #define JOB_W 3
365 #define FILE_W 32
366 /* The JOBSIZE_W is too small for big jobs, so time is pushed to the right */
367 #define JOBSIZE_W 4
369 #define RANK_POS 0
370 #define OWNER_POS RANK_POS+RANK_W+SPACE_W
371 #define CLASS_POS OWNER_POS+OWNER_W+SPACE_W
372 #define JOB_POS CLASS_POS+CLASS_W+SPACE_W
373 #define FILE_POS JOB_POS+JOB_W+SPACE_W
374 #define JOBSIZE_POS FILE_POS+FILE_W
377 fstring tok[LPRNG_NTOK];
378 int count=0;
380 #ifdef OLD_LPRNG
381 /* We only need this bugfix for older versions of lprng - current
382 information is that version 3.3.5 must not have this line
383 in order to work correctly.
387 Need to insert one space in front of the size, to be able to use
388 next_token() unchanged. I would have liked to be able to insert a
389 space instead, to prevent losing that one char, but perl has spoiled
390 me :-\ So I did it the easiest way.
392 HINT: Use as short a path as possible for the samba spool directory.
393 A long spool-path will just waste significant chars of the file name.
396 line[JOBSIZE_POS-1]=' ';
397 #endif /* OLD_LPRNG */
399 /* handle the case of "(stdin)" as a filename */
400 string_sub(line,"stdin","STDIN");
401 string_sub(line,"(","\"");
402 string_sub(line,")","\"");
404 for (count=0;
405 count<LPRNG_NTOK &&
406 next_token(&line,tok[count],NULL, sizeof(tok[count]));
407 count++) ;
409 /* we must get LPRNG_NTOK tokens */
410 if (count < LPRNG_NTOK)
411 return(False);
413 /* the Job and Total columns must be integer */
414 if (!isdigit((int)*tok[LPRNG_JOBTOK]) || !isdigit((int)*tok[LPRNG_TOTALTOK])) return(False);
416 /* if the fname contains a space then use STDIN */
417 /* I do not understand how this would be possible. Magnus. */
418 if (strchr(tok[LPRNG_FILETOK],' '))
419 fstrcpy(tok[LPRNG_FILETOK],"STDIN");
421 /* only take the last part of the filename */
423 fstring tmp;
424 char *p = strrchr(tok[LPRNG_FILETOK],'/');
425 if (p)
427 fstrcpy(tmp,p+1);
428 fstrcpy(tok[LPRNG_FILETOK],tmp);
433 buf->job = atoi(tok[LPRNG_JOBTOK]);
434 buf->size = atoi(tok[LPRNG_TOTALTOK]);
435 if (strequal(tok[LPRNG_RANKTOK],"active"))
436 buf->status = LPQ_PRINTING;
437 else if (strequal(tok[LPRNG_RANKTOK],"hold"))
438 buf->status = LPQ_PAUSED;
439 else
440 buf->status = LPQ_QUEUED;
441 /* buf->time = time(NULL); */
442 buf->time = LPRng_time(tok,LPRNG_TIMETOK);
443 DEBUG(3,("Time reported for job %d is %s", buf->job, ctime(&buf->time)));
444 StrnCpy(buf->user,tok[LPRNG_USERTOK],sizeof(buf->user)-1);
445 StrnCpy(buf->file,tok[LPRNG_FILETOK],sizeof(buf->file)-1);
446 #ifdef LPRNG_PRIOTOK
447 /* Here I try to map the CLASS char to a number, but the number
448 is never shown in Print Manager under NT anyway... Magnus. */
449 buf->priority = atoi(tok[LPRNG_PRIOTOK]-('A'-1));
450 #else
451 buf->priority = 1;
452 #endif
453 return(True);
458 /*******************************************************************
459 parse lpq on an aix system
461 Queue Dev Status Job Files User PP % Blks Cp Rnk
462 ------- ----- --------- --- ------------------ ---------- ---- -- ----- --- ---
463 lazer lazer READY
464 lazer lazer RUNNING 537 6297doc.A kvintus@IE 0 10 2445 1 1
465 QUEUED 538 C.ps root@IEDVB 124 1 2
466 QUEUED 539 E.ps root@IEDVB 28 1 3
467 QUEUED 540 L.ps root@IEDVB 172 1 4
468 QUEUED 541 P.ps root@IEDVB 22 1 5
469 ********************************************************************/
470 static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first)
472 fstring tok[11];
473 int count=0;
475 /* handle the case of "(standard input)" as a filename */
476 string_sub(line,"standard input","STDIN");
477 string_sub(line,"(","\"");
478 string_sub(line,")","\"");
480 for (count=0;
481 count<10 &&
482 next_token(&line,tok[count],NULL, sizeof(tok[count]));
483 count++) ;
485 /* we must get 6 tokens */
486 if (count < 10)
488 if ((count == 7) && ((strcmp(tok[0],"QUEUED") == 0) || (strcmp(tok[0],"HELD") == 0)))
490 /* the 2nd and 5th columns must be integer */
491 if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[4])) return(False);
492 buf->size = atoi(tok[4]) * 1024;
493 /* if the fname contains a space then use STDIN */
494 if (strchr(tok[2],' '))
495 fstrcpy(tok[2],"STDIN");
497 /* only take the last part of the filename */
499 fstring tmp;
500 char *p = strrchr(tok[2],'/');
501 if (p)
503 fstrcpy(tmp,p+1);
504 fstrcpy(tok[2],tmp);
509 buf->job = atoi(tok[1]);
510 buf->status = strequal(tok[0],"HELD")?LPQ_PAUSED:LPQ_QUEUED;
511 buf->priority = 0;
512 buf->time = time(NULL);
513 StrnCpy(buf->user,tok[3],sizeof(buf->user)-1);
514 StrnCpy(buf->file,tok[2],sizeof(buf->file)-1);
516 else
518 DEBUG(6,("parse_lpq_aix count=%d\n", count));
519 return(False);
522 else
524 /* the 4th and 9th columns must be integer */
525 if (!isdigit((int)*tok[3]) || !isdigit((int)*tok[8])) return(False);
526 buf->size = atoi(tok[8]) * 1024;
527 /* if the fname contains a space then use STDIN */
528 if (strchr(tok[4],' '))
529 fstrcpy(tok[4],"STDIN");
531 /* only take the last part of the filename */
533 fstring tmp;
534 char *p = strrchr(tok[4],'/');
535 if (p)
537 fstrcpy(tmp,p+1);
538 fstrcpy(tok[4],tmp);
543 buf->job = atoi(tok[3]);
544 buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED;
545 buf->priority = 0;
546 buf->time = time(NULL);
547 StrnCpy(buf->user,tok[5],sizeof(buf->user)-1);
548 StrnCpy(buf->file,tok[4],sizeof(buf->file)-1);
552 return(True);
556 /****************************************************************************
557 parse a lpq line
558 here is an example of lpq output under hpux; note there's no space after -o !
559 $> lpstat -oljplus
560 ljplus-2153 user priority 0 Jan 19 08:14 on ljplus
561 util.c 125697 bytes
562 server.c 110712 bytes
563 ljplus-2154 user priority 0 Jan 19 08:14 from client
564 (standard input) 7551 bytes
565 ****************************************************************************/
566 static BOOL parse_lpq_hpux(char * line, print_queue_struct *buf, BOOL first)
568 /* must read two lines to process, therefore keep some values static */
569 static BOOL header_line_ok=False, base_prio_reset=False;
570 static fstring jobuser;
571 static int jobid;
572 static int jobprio;
573 static time_t jobtime;
574 static int jobstat=LPQ_QUEUED;
575 /* to store minimum priority to print, lpstat command should be invoked
576 with -p option first, to work */
577 static int base_prio;
579 int count;
580 char TAB = '\011';
581 fstring tok[12];
583 /* If a line begins with a horizontal TAB, it is a subline type */
585 if (line[0] == TAB) { /* subline */
586 /* check if it contains the base priority */
587 if (!strncmp(line,"\tfence priority : ",18)) {
588 base_prio=atoi(&line[18]);
589 DEBUG(4, ("fence priority set at %d\n", base_prio));
591 if (!header_line_ok) return (False); /* incorrect header line */
592 /* handle the case of "(standard input)" as a filename */
593 string_sub(line,"standard input","STDIN");
594 string_sub(line,"(","\"");
595 string_sub(line,")","\"");
597 for (count=0; count<2 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ;
598 /* we must get 2 tokens */
599 if (count < 2) return(False);
601 /* the 2nd column must be integer */
602 if (!isdigit((int)*tok[1])) return(False);
604 /* if the fname contains a space then use STDIN */
605 if (strchr(tok[0],' '))
606 fstrcpy(tok[0],"STDIN");
608 buf->size = atoi(tok[1]);
609 StrnCpy(buf->file,tok[0],sizeof(buf->file)-1);
611 /* fill things from header line */
612 buf->time = jobtime;
613 buf->job = jobid;
614 buf->status = jobstat;
615 buf->priority = jobprio;
616 StrnCpy(buf->user,jobuser,sizeof(buf->user)-1);
618 return(True);
620 else { /* header line */
621 header_line_ok=False; /* reset it */
622 if (first) {
623 if (!base_prio_reset) {
624 base_prio=0; /* reset it */
625 base_prio_reset=True;
628 else if (base_prio) base_prio_reset=False;
630 /* handle the dash in the job id */
631 string_sub(line,"-"," ");
633 for (count=0; count<12 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ;
635 /* we must get 8 tokens */
636 if (count < 8) return(False);
638 /* first token must be printer name (cannot check ?) */
639 /* the 2nd, 5th & 7th column must be integer */
640 if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[4]) || !isdigit((int)*tok[6])) return(False);
641 jobid = atoi(tok[1]);
642 StrnCpy(jobuser,tok[2],sizeof(buf->user)-1);
643 jobprio = atoi(tok[4]);
645 /* process time */
646 jobtime=EntryTime(tok, 5, count, 8);
647 if (jobprio < base_prio) {
648 jobstat = LPQ_PAUSED;
649 DEBUG (4, ("job %d is paused: prio %d < %d; jobstat=%d\n", jobid, jobprio, base_prio, jobstat));
651 else {
652 jobstat = LPQ_QUEUED;
653 if ((count >8) && (((strequal(tok[8],"on")) ||
654 ((strequal(tok[8],"from")) &&
655 ((count > 10)&&(strequal(tok[10],"on")))))))
656 jobstat = LPQ_PRINTING;
659 header_line_ok=True; /* information is correct */
660 return(False); /* need subline info to include into queuelist */
665 /****************************************************************************
666 parse a lpq line
668 here is an example of "lpstat -o dcslw" output under sysv
670 dcslw-896 tridge 4712 Dec 20 10:30:30 on dcslw
671 dcslw-897 tridge 4712 Dec 20 10:30:30 being held
673 ****************************************************************************/
674 static BOOL parse_lpq_sysv(char *line,print_queue_struct *buf,BOOL first)
676 fstring tok[9];
677 int count=0;
678 char *p;
680 /* handle the dash in the job id */
681 string_sub(line,"-"," ");
683 for (count=0; count<9 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ;
685 /* we must get 7 tokens */
686 if (count < 7)
687 return(False);
689 /* the 2nd and 4th, 6th columns must be integer */
690 if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[3])) return(False);
691 if (!isdigit((int)*tok[5])) return(False);
693 /* if the user contains a ! then trim the first part of it */
694 if ((p=strchr(tok[2],'!')))
696 fstring tmp;
697 fstrcpy(tmp,p+1);
698 fstrcpy(tok[2],tmp);
702 buf->job = atoi(tok[1]);
703 buf->size = atoi(tok[3]);
704 if (count > 7 && strequal(tok[7],"on"))
705 buf->status = LPQ_PRINTING;
706 else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held"))
707 buf->status = LPQ_PAUSED;
708 else
709 buf->status = LPQ_QUEUED;
710 buf->priority = 0;
711 buf->time = EntryTime(tok, 4, count, 7);
712 StrnCpy(buf->user,tok[2],sizeof(buf->user)-1);
713 StrnCpy(buf->file,tok[2],sizeof(buf->file)-1);
714 return(True);
717 /****************************************************************************
718 parse a lpq line
720 here is an example of lpq output under qnx
721 Spooler: /qnx/spooler, on node 1
722 Printer: txt (ready)
723 0000: root [job #1 ] active 1146 bytes /etc/profile
724 0001: root [job #2 ] ready 2378 bytes /etc/install
725 0002: root [job #3 ] ready 1146 bytes -- standard input --
726 ****************************************************************************/
727 static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first)
729 fstring tok[7];
730 int count=0;
732 DEBUG(0,("antes [%s]\n", line));
734 /* handle the case of "-- standard input --" as a filename */
735 string_sub(line,"standard input","STDIN");
736 DEBUG(0,("despues [%s]\n", line));
737 string_sub(line,"-- ","\"");
738 string_sub(line," --","\"");
739 DEBUG(0,("despues 1 [%s]\n", line));
741 string_sub(line,"[job #","");
742 string_sub(line,"]","");
743 DEBUG(0,("despues 2 [%s]\n", line));
747 for (count=0; count<7 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ;
749 /* we must get 7 tokens */
750 if (count < 7)
751 return(False);
753 /* the 3rd and 5th columns must be integer */
754 if (!isdigit((int)*tok[2]) || !isdigit((int)*tok[4])) return(False);
756 /* only take the last part of the filename */
758 fstring tmp;
759 char *p = strrchr(tok[6],'/');
760 if (p)
762 fstrcpy(tmp,p+1);
763 fstrcpy(tok[6],tmp);
768 buf->job = atoi(tok[2]);
769 buf->size = atoi(tok[4]);
770 buf->status = strequal(tok[3],"active")?LPQ_PRINTING:LPQ_QUEUED;
771 buf->priority = 0;
772 buf->time = time(NULL);
773 StrnCpy(buf->user,tok[1],sizeof(buf->user)-1);
774 StrnCpy(buf->file,tok[6],sizeof(buf->file)-1);
775 return(True);
779 /****************************************************************************
780 parse a lpq line for the plp printing system
781 Bertrand Wallrich <Bertrand.Wallrich@loria.fr>
783 redone by tridge. Here is a sample queue:
785 Local Printer 'lp2' (fjall):
786 Printing (started at Jun 15 13:33:58, attempt 1).
787 Rank Owner Pr Opt Job Host Files Size Date
788 active tridge X - 6 fjall /etc/hosts 739 Jun 15 13:33
789 3rd tridge X - 7 fjall /etc/hosts 739 Jun 15 13:33
791 ****************************************************************************/
792 static BOOL parse_lpq_plp(char *line,print_queue_struct *buf,BOOL first)
794 fstring tok[11];
795 int count=0;
797 /* handle the case of "(standard input)" as a filename */
798 string_sub(line,"stdin","STDIN");
799 string_sub(line,"(","\"");
800 string_sub(line,")","\"");
802 for (count=0; count<11 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ;
804 /* we must get 11 tokens */
805 if (count < 11)
806 return(False);
808 /* the first must be "active" or begin with an integer */
809 if (strcmp(tok[0],"active") && !isdigit((int)tok[0][0]))
810 return(False);
812 /* the 5th and 8th must be integer */
813 if (!isdigit((int)*tok[4]) || !isdigit((int)*tok[7]))
814 return(False);
816 /* if the fname contains a space then use STDIN */
817 if (strchr(tok[6],' '))
818 fstrcpy(tok[6],"STDIN");
820 /* only take the last part of the filename */
822 fstring tmp;
823 char *p = strrchr(tok[6],'/');
824 if (p)
826 fstrcpy(tmp,p+1);
827 fstrcpy(tok[6],tmp);
832 buf->job = atoi(tok[4]);
834 buf->size = atoi(tok[7]);
835 if (strchr(tok[7],'K'))
836 buf->size *= 1024;
837 if (strchr(tok[7],'M'))
838 buf->size *= 1024*1024;
840 buf->status = strequal(tok[0],"active")?LPQ_PRINTING:LPQ_QUEUED;
841 buf->priority = 0;
842 buf->time = time(NULL);
843 StrnCpy(buf->user,tok[1],sizeof(buf->user)-1);
844 StrnCpy(buf->file,tok[6],sizeof(buf->file)-1);
845 return(True);
848 /****************************************************************************
849 parse a qstat line
851 here is an example of "qstat -l -d qms" output under softq
853 Queue qms: 2 jobs; daemon active (313); enabled; accepting;
854 job-ID submission-time pri size owner title
855 205980: H 98/03/09 13:04:05 0 15733 stephenf chap1.ps
856 206086:> 98/03/12 17:24:40 0 659 chris -
857 206087: 98/03/12 17:24:45 0 4876 chris -
858 Total: 21268 bytes in queue
861 ****************************************************************************/
862 static BOOL parse_lpq_softq(char *line,print_queue_struct *buf,BOOL first)
864 fstring tok[10];
865 int count=0;
867 /* mung all the ":"s to spaces*/
868 string_sub(line,":"," ");
870 for (count=0; count<10 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ;
872 /* we must get 9 tokens */
873 if (count < 9)
874 return(False);
876 /* the 1st and 7th columns must be integer */
877 if (!isdigit((int)*tok[0]) || !isdigit((int)*tok[6])) return(False);
878 /* if the 2nd column is either '>' or 'H' then the 7th and 8th must be
879 * integer, else it's the 6th and 7th that must be
881 if (*tok[1] == 'H' || *tok[1] == '>')
883 if (!isdigit((int)*tok[7]))
884 return(False);
885 buf->status = *tok[1] == '>' ? LPQ_PRINTING : LPQ_PAUSED;
886 count = 1;
888 else
890 if (!isdigit((int)*tok[5]))
891 return(False);
892 buf->status = LPQ_QUEUED;
893 count = 0;
897 buf->job = atoi(tok[0]);
898 buf->size = atoi(tok[count+6]);
899 buf->priority = atoi(tok[count+5]);
900 StrnCpy(buf->user,tok[count+7],sizeof(buf->user)-1);
901 StrnCpy(buf->file,tok[count+8],sizeof(buf->file)-1);
902 buf->time = time(NULL); /* default case: take current time */
904 time_t jobtime;
905 struct tm *t;
907 t = localtime(&buf->time);
908 t->tm_mday = atoi(tok[count+2]+6);
909 t->tm_mon = atoi(tok[count+2]+3);
910 switch (*tok[count+2])
912 case 7: case 8: case 9: t->tm_year = atoi(tok[count+2]) + 1900; break;
913 default: t->tm_year = atoi(tok[count+2]) + 2000; break;
916 t->tm_hour = atoi(tok[count+3]);
917 t->tm_min = atoi(tok[count+4]);
918 t->tm_sec = atoi(tok[count+5]);
919 jobtime = mktime(t);
920 if (jobtime != (time_t)-1)
921 buf->time = jobtime;
924 return(True);
928 char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
929 char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
930 char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL };
932 /****************************************************************************
933 parse a lpq line. Choose printing style
934 ****************************************************************************/
935 static BOOL parse_lpq_entry(int snum,char *line,
936 print_queue_struct *buf,
937 print_status_struct *status,BOOL first)
939 BOOL ret;
941 switch (lp_printing(snum))
943 case PRINT_SYSV:
944 ret = parse_lpq_sysv(line,buf,first);
945 break;
946 case PRINT_AIX:
947 ret = parse_lpq_aix(line,buf,first);
948 break;
949 case PRINT_HPUX:
950 ret = parse_lpq_hpux(line,buf,first);
951 break;
952 case PRINT_QNX:
953 ret = parse_lpq_qnx(line,buf,first);
954 break;
955 case PRINT_LPRNG:
956 ret = parse_lpq_lprng(line,buf,first);
957 break;
958 case PRINT_PLP:
959 ret = parse_lpq_plp(line,buf,first);
960 break;
961 case PRINT_SOFTQ:
962 ret = parse_lpq_softq(line,buf,first);
963 break;
964 default:
965 ret = parse_lpq_bsd(line,buf,first);
966 break;
969 #ifdef LPQ_GUEST_TO_USER
970 if (ret) {
971 extern pstring sesssetup_user;
972 /* change guest entries to the current logged in user to make
973 them appear deletable to windows */
974 if (sesssetup_user[0] && strequal(buf->user,lp_guestaccount(snum)))
975 pstrcpy(buf->user,sesssetup_user);
977 #endif
979 /* We don't want the newline in the status message. */
981 char *p = strchr(line,'\n');
982 if (p) *p = 0;
985 if (status && !ret)
987 /* a few simple checks to see if the line might be a
988 printer status line:
989 handle them so that most severe condition is shown */
990 int i;
991 strlower(line);
993 switch (status->status) {
994 case LPSTAT_OK:
995 for (i=0; stat0_strings[i]; i++)
996 if (strstr(line,stat0_strings[i])) {
997 StrnCpy(status->message,line,sizeof(status->message)-1);
998 status->status=LPSTAT_OK;
999 return ret;
1001 case LPSTAT_STOPPED:
1002 for (i=0; stat1_strings[i]; i++)
1003 if (strstr(line,stat1_strings[i])) {
1004 StrnCpy(status->message,line,sizeof(status->message)-1);
1005 status->status=LPSTAT_STOPPED;
1006 return ret;
1008 case LPSTAT_ERROR:
1009 for (i=0; stat2_strings[i]; i++)
1010 if (strstr(line,stat2_strings[i])) {
1011 StrnCpy(status->message,line,sizeof(status->message)-1);
1012 status->status=LPSTAT_ERROR;
1013 return ret;
1015 break;
1019 return(ret);
1022 /****************************************************************************
1023 get a printer queue
1024 ****************************************************************************/
1025 int get_printqueue(int snum,
1026 connection_struct *conn,print_queue_struct **queue,
1027 print_status_struct *status)
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();
1040 *line = 0;
1041 check_lpq_cache(snum);
1043 if (!printername || !*printername) {
1044 DEBUG(6,("xx replacing printer name with service (snum=(%s,%d))\n",
1045 lp_servicename(snum),snum));
1046 printername = lp_servicename(snum);
1049 if (!lpq_command || !(*lpq_command)) {
1050 DEBUG(5,("No lpq command\n"));
1051 return(0);
1054 pstrcpy(syscmd,lpq_command);
1055 string_sub(syscmd,"%p",printername);
1057 standard_sub(conn,syscmd);
1059 slprintf(outfile,sizeof(outfile)-1, "%s/lpq.%08x",tmpdir(),str_checksum(syscmd));
1061 if (!lpq_cache_reset[snum] && cachetime && !stat(outfile,&sbuf)) {
1062 if (time(NULL) - sbuf.st_mtime < cachetime) {
1063 DEBUG(3,("Using cached lpq output\n"));
1064 dorun = False;
1068 if (dorun) {
1069 ret = smbrun(syscmd,outfile,True);
1070 DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
1073 lpq_cache_reset[snum] = False;
1075 f = fopen(outfile,"r");
1076 if (!f) {
1077 return(0);
1080 if (status) {
1081 fstrcpy(status->message,"");
1082 status->status = LPSTAT_OK;
1085 while (fgets(line,sizeof(pstring),f)) {
1086 DEBUG(6,("QUEUE2: %s\n",line));
1088 *queue = Realloc(*queue,sizeof(print_queue_struct)*(count+1));
1089 if (! *queue) {
1090 count = 0;
1091 break;
1094 bzero((char *)&(*queue)[count],sizeof(**queue));
1096 /* parse it */
1097 if (!parse_lpq_entry(snum,line,
1098 &(*queue)[count],status,count==0))
1099 continue;
1101 count++;
1104 fclose(f);
1106 if (!cachetime) {
1107 unlink(outfile);
1108 } else {
1109 /* we only expect this to succeed on trapdoor systems,
1110 on normal systems the file is owned by root */
1111 chmod(outfile,0666);
1113 return(count);
1117 /****************************************************************************
1118 delete a printer queue entry
1119 ****************************************************************************/
1120 void del_printqueue(connection_struct *conn,int snum,int jobid)
1122 char *lprm_command = lp_lprmcommand(snum);
1123 char *printername = PRINTERNAME(snum);
1124 pstring syscmd;
1125 char jobstr[20];
1126 int ret;
1128 if (!printername || !*printername)
1130 DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
1131 lp_servicename(snum),snum));
1132 printername = lp_servicename(snum);
1135 if (!lprm_command || !(*lprm_command))
1137 DEBUG(5,("No lprm command\n"));
1138 return;
1141 slprintf(jobstr,sizeof(jobstr)-1,"%d",jobid);
1143 pstrcpy(syscmd,lprm_command);
1144 string_sub(syscmd,"%p",printername);
1145 string_sub(syscmd,"%j",jobstr);
1146 standard_sub(conn,syscmd);
1148 ret = smbrun(syscmd,NULL,False);
1149 DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
1150 lpq_reset(snum); /* queue has changed */
1153 /****************************************************************************
1154 change status of a printer queue entry
1155 ****************************************************************************/
1156 void status_printjob(connection_struct *conn,int snum,int jobid,int status)
1158 char *lpstatus_command =
1159 (status==LPQ_PAUSED?lp_lppausecommand(snum):lp_lpresumecommand(snum));
1160 char *printername = PRINTERNAME(snum);
1161 pstring syscmd;
1162 char jobstr[20];
1163 int ret;
1165 if (!printername || !*printername)
1167 DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
1168 lp_servicename(snum),snum));
1169 printername = lp_servicename(snum);
1172 if (!lpstatus_command || !(*lpstatus_command))
1174 DEBUG(5,("No lpstatus command to %s job\n",
1175 (status==LPQ_PAUSED?"pause":"resume")));
1176 return;
1179 slprintf(jobstr,sizeof(jobstr)-1,"%d",jobid);
1181 pstrcpy(syscmd,lpstatus_command);
1182 string_sub(syscmd,"%p",printername);
1183 string_sub(syscmd,"%j",jobstr);
1184 standard_sub(conn,syscmd);
1186 ret = smbrun(syscmd,NULL,False);
1187 DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
1188 lpq_reset(snum); /* queue has changed */
1193 /****************************************************************************
1194 we encode print job numbers over the wire so that when we get them back we can
1195 tell not only what print job they are but also what service it belongs to,
1196 this is to overcome the problem that windows clients tend to send the wrong
1197 service number when doing print queue manipulation!
1198 ****************************************************************************/
1199 int printjob_encode(int snum, int job)
1201 return ((snum&0xFF)<<8) | (job & 0xFF);
1204 /****************************************************************************
1205 and now decode them again ...
1206 ****************************************************************************/
1207 void printjob_decode(int jobid, int *snum, int *job)
1209 (*snum) = (jobid >> 8) & 0xFF;
1210 (*job) = jobid & 0xFF;
1213 /****************************************************************************
1214 Change status of a printer queue
1215 ****************************************************************************/
1217 void status_printqueue(connection_struct *conn,int snum,int status)
1219 char *queuestatus_command = (status==LPSTAT_STOPPED ?
1220 lp_queuepausecommand(snum):lp_queueresumecommand(snum));
1221 char *printername = PRINTERNAME(snum);
1222 pstring syscmd;
1223 int ret;
1225 if (!printername || !*printername) {
1226 DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
1227 lp_servicename(snum),snum));
1228 printername = lp_servicename(snum);
1231 if (!queuestatus_command || !(*queuestatus_command)) {
1232 DEBUG(5,("No queuestatus command to %s job\n",
1233 (status==LPSTAT_STOPPED?"pause":"resume")));
1234 return;
1237 pstrcpy(syscmd,queuestatus_command);
1238 string_sub(syscmd,"%p",printername);
1239 standard_sub(conn,syscmd);
1241 ret = smbrun(syscmd,NULL,False);
1242 DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
1243 lpq_reset(snum); /* queue has changed */
1248 /***************************************************************************
1249 auto-load printer services
1250 ***************************************************************************/
1251 static void add_all_printers(void)
1253 int printers = lp_servicenumber(PRINTERS_NAME);
1255 if (printers < 0) return;
1257 pcap_printer_fn(lp_add_one_printer);
1260 /***************************************************************************
1261 auto-load some homes and printer services
1262 ***************************************************************************/
1263 static void add_auto_printers(void)
1265 char *p;
1266 int printers;
1267 char *str = lp_auto_services();
1269 if (!str) return;
1271 printers = lp_servicenumber(PRINTERS_NAME);
1273 if (printers < 0) return;
1275 for (p=strtok(str,LIST_SEP);p;p=strtok(NULL,LIST_SEP)) {
1276 if (lp_servicenumber(p) >= 0) continue;
1278 if (pcap_printername_ok(p,NULL)) {
1279 lp_add_printer(p,printers);
1284 /***************************************************************************
1285 load automatic printer services
1286 ***************************************************************************/
1287 void load_printers(void)
1289 add_auto_printers();
1290 if (lp_load_printers())
1291 add_all_printers();