b10dbba9c5727805183093fe752596e516b060eb
[ira/wip.git] / source3 / printing / pcap.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    printcap parsing
5    Copyright (C) Karl Auer 1993-1998
6
7    Re-working by Martin Kiff, 1994
8    
9    Re-written again by Andrew Tridgell
10
11    Modified for SVID support by Norm Jacobs, 1997
12
13    Modified for CUPS support by Michael Sweet, 1999
14    
15    This program is free software; you can redistribute it and/or modify
16    it under the terms of the GNU General Public License as published by
17    the Free Software Foundation; either version 2 of the License, or
18    (at your option) any later version.
19    
20    This program is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23    GNU General Public License for more details.
24    
25    You should have received a copy of the GNU General Public License
26    along with this program; if not, write to the Free Software
27    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 */
29
30 /*
31  *  Parse printcap file.
32  *
33  *  This module does exactly one thing - it looks into the printcap file
34  *  and tells callers if a specified string appears as a printer name.
35  *
36  *  The way this module looks at the printcap file is very simplistic.
37  *  Only the local printcap file is inspected (no searching of NIS
38  *  databases etc).
39  *
40  *  There are assumed to be one or more printer names per record, held
41  *  as a set of sub-fields separated by vertical bar symbols ('|') in the
42  *  first field of the record. The field separator is assumed to be a colon
43  *  ':' and the record separator a newline.
44  * 
45  *  Lines ending with a backspace '\' are assumed to flag that the following
46  *  line is a continuation line so that a set of lines can be read as one
47  *  printcap entry.
48  *
49  *  A line stating with a hash '#' is assumed to be a comment and is ignored
50  *  Comments are discarded before the record is strung together from the
51  *  set of continuation lines.
52  *
53  *  Opening a pipe for "lpc status" and reading that would probably 
54  *  be pretty effective. Code to do this already exists in the freely
55  *  distributable PCNFS server code.
56  *
57  *  Modified to call SVID/XPG4 support if printcap name is set to "lpstat"
58  *  in smb.conf under Solaris.
59  *
60  *  Modified to call CUPS support if printcap name is set to "cups"
61  *  in smb.conf.
62  */
63
64 #include "includes.h"
65
66 #include "smb.h"
67
68 #ifdef AIX
69 /*  ******************************************
70      Extend for AIX system and qconfig file
71        from 'boulard@univ-rennes1.fr
72     ****************************************** */
73 static int strlocate(char *xpLine,char *xpS)
74 {
75         int iS,iL,iRet;
76         char *p;
77         iS = strlen(xpS);
78         iL = strlen(xpLine);
79
80         iRet = 0;
81         p = xpLine;
82         while (iL >= iS)
83         {
84                 if (strncmp(p,xpS,iS) == 0) {iRet =1;break;};
85                 p++;
86                 iL--;
87         }
88         /*DEBUG(3,(" strlocate %s in line '%s',ret=%d\n",xpS,xpLine,iRet));*/
89         
90         return(iRet);
91 }
92         
93         
94 /* ******************************************************************* */
95 /* *    Scan qconfig and search all virtual printer (device printer) * */
96 /* ******************************************************************* */
97 static void ScanQconfig_fn(char *psz,void (*fn)(char *, char *))
98 {
99         int iEtat;
100         XFILE *pfile;
101         char *line,*p;
102         pstring name,comment;
103         line  = NULL;
104         *name = 0;
105         *comment = 0;
106
107         if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
108         {
109               DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
110               return;
111         }
112
113         iEtat = 0;
114         /* scan qconfig file for searching <printername>:       */
115         for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
116         {
117                 if (*line == '*' || *line == 0)
118                 continue;
119                 switch (iEtat)
120                 {
121                         case 0: /* locate an entry */
122                          if (*line == '\t' || *line == ' ') continue;
123                          if ((p=strchr_m(line,':')))
124                          {
125                                 *p = '\0';
126                                 p = strtok(line,":");
127                                 if (strcmp(p,"bsh")!=0)
128                                   {
129                                     pstrcpy(name,p);
130                                     iEtat = 1;
131                                     continue;
132                                   }
133                          }
134                          break;
135                         case 1: /* scanning device stanza */
136                          if (*line == '*' || *line == 0) continue;
137                          if (*line != '\t' && *line != ' ')
138                          {
139                            /* name is found without stanza device  */
140                            /* probably a good printer ???               */
141                            fn(name,comment);
142                            iEtat = 0;
143                            continue;
144                           }
145                         
146                           if (strlocate(line,"backend"))
147                           {
148                                 /* it's a device, not a virtual printer*/
149                                 iEtat = 0;
150                           }
151                           else if (strlocate(line,"device"))
152                           {
153                                 /* it's a good virtual printer */
154                                 fn(name,comment);
155                                 iEtat = 0;
156                                 continue;
157                           }
158                           break;
159                 }
160         }
161         x_fclose(pfile);
162 }
163
164 /* Scan qconfig file and locate de printername */
165
166 static BOOL ScanQconfig(char *psz,char *pszPrintername)
167 {
168         int iLg,iEtat;
169         XFILE *pfile;
170         char *pName;
171         char *line;
172
173         pName = NULL;
174         line  = NULL;
175         if ((pszPrintername!= NULL) && ((iLg = strlen(pszPrintername)) > 0))
176          pName = malloc(iLg+10);
177         if (pName == NULL)
178         {
179                 DEBUG(0,(" Unable to allocate memory for printer %s\n",pszPrintername));
180                 return(False);
181         }
182         if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
183         {
184               DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
185               free(pName);
186               return(False);
187         }
188         slprintf(pName, iLg + 9, "%s:",pszPrintername);
189         iLg = strlen(pName);
190         /*DEBUG(3,( " Looking for entry %s\n",pName));*/
191         iEtat = 0;
192         /* scan qconfig file for searching <printername>:       */
193         for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
194         {
195                 if (*line == '*' || *line == 0)
196                 continue;
197                 switch (iEtat)
198                 {
199                         case 0: /* scanning entry */
200                          if (strncmp(line,pName,iLg) == 0)
201                          {
202                                 iEtat = 1;
203                                 continue;
204                          }
205                          break;
206                         case 1: /* scanning device stanza */
207                          if (*line == '*' || *line == 0) continue;
208                          if (*line != '\t' && *line != ' ')
209                          {
210                            /* name is found without stanza device  */
211                            /* probably a good printer ???               */
212                            free (line);
213                            free(pName);
214                            fclose(pfile);
215                            return(True);
216                           }
217                         
218                           if (strlocate(line,"backend"))
219                           {
220                                 /* it's a device, not a virtual printer*/
221                                 iEtat = 0;
222                           }
223                           else if (strlocate(line,"device"))
224                           {
225                                 /* it's a good virtual printer */
226                                 free (line);
227                                 free(pName);
228                                 fclose(pfile);
229                                 return(True);
230                           }
231                           break;
232                 }
233         }
234         free (pName);
235         x_fclose(pfile);
236         return(False);
237 }
238 #endif /* AIX */
239
240
241 /***************************************************************************
242 Scan printcap file pszPrintcapname for a printer called pszPrintername. 
243 Return True if found, else False. Returns False on error, too, after logging 
244 the error at level 0. For generality, the printcap name may be passed - if
245 passed as NULL, the configuration will be queried for the name. pszPrintername
246 must be in DOS codepage.
247 The xxx_printername_ok functions need fixing to understand they are being
248 given a DOS codepage. FIXME !! JRA.
249 ***************************************************************************/
250 BOOL pcap_printername_ok(char *pszPrintername, char *pszPrintcapname)
251 {
252   char *line=NULL;
253   char *psz;
254   char *p,*q;
255   XFILE *pfile;
256
257   if (pszPrintername == NULL || pszPrintername[0] == '\0')
258     {
259       DEBUG(0,( "Attempt to locate null printername! Internal error?\n"));
260       return(False);
261     }
262
263   /* only go looking if no printcap name supplied */
264   if ((psz = pszPrintcapname) == NULL || psz[0] == '\0')
265     if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
266       {
267         DEBUG(0,( "No printcap file name configured!\n"));
268         return(False);
269       }
270
271 #ifdef HAVE_CUPS
272     if (strequal(psz, "cups"))
273        return (cups_printername_ok(pszPrintername));
274 #endif /* HAVE_CUPS */
275
276 #ifdef SYSV
277     if (strequal(psz, "lpstat"))
278        return (sysv_printername_ok(pszPrintername));
279 #endif
280
281 #ifdef AIX
282   if (strlocate(psz,"/qconfig"))
283      return(ScanQconfig(psz,pszPrintername));
284 #endif
285
286   if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
287     {
288       DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
289       return(False);
290     }
291
292   for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
293     {
294       if (*line == '#' || *line == 0)
295         continue;
296
297       /* now we have a real printer line - cut it off at the first : */      
298       p = strchr_m(line,':');
299       if (p) *p = 0;
300       
301       /* now just check if the name is in the list */
302       /* NOTE: I avoid strtok as the fn calling this one may be using it */
303       for (p=line; p; p=q)
304         {
305           if ((q = strchr_m(p,'|'))) *q++ = 0;
306
307           if (strequal(p,pszPrintername))
308             {
309               /* normalise the case */
310               pstrcpy(pszPrintername,p);
311               free(line);
312               x_fclose(pfile);
313               return(True);           
314             }
315           p = q;
316         }
317     }
318
319   x_fclose(pfile);
320   return(False);
321 }
322
323
324 /***************************************************************************
325 run a function on each printer name in the printcap file. The function is 
326 passed the primary name and the comment (if possible). Note the fn() takes
327 strings in DOS codepage. This means the xxx_printer_fn() calls must be fixed
328 to return DOS codepage. FIXME !! JRA.
329 ***************************************************************************/
330 void pcap_printer_fn(void (*fn)(char *, char *))
331 {
332   pstring name,comment;
333   char *line;
334   char *psz;
335   char *p,*q;
336   XFILE *pfile;
337
338   /* only go looking if no printcap name supplied */
339   if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
340     {
341       DEBUG(0,( "No printcap file name configured!\n"));
342       return;
343     }
344
345 #ifdef HAVE_CUPS
346     if (strequal(psz, "cups")) {
347       cups_printer_fn(fn);
348       return;
349     }
350 #endif /* HAVE_CUPS */
351
352 #ifdef SYSV
353     if (strequal(psz, "lpstat")) {
354       sysv_printer_fn(fn);
355       return;
356     }
357 #endif
358
359 #ifdef AIX
360   if (strlocate(psz,"/qconfig"))
361   {
362         ScanQconfig_fn(psz,fn);
363      return;
364   }
365 #endif
366
367   if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
368     {
369       DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
370       return;
371     }
372
373   for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
374     {
375       if (*line == '#' || *line == 0)
376         continue;
377
378       /* now we have a real printer line - cut it off at the first : */      
379       p = strchr_m(line,':');
380       if (p) *p = 0;
381       
382       /* now find the most likely printer name and comment 
383        this is pure guesswork, but it's better than nothing */
384       *name = 0;
385       *comment = 0;
386       for (p=line; p; p=q)
387         {
388           BOOL has_punctuation;
389           if ((q = strchr_m(p,'|'))) *q++ = 0;
390
391           has_punctuation = (strchr_m(p,' ') || strchr_m(p,'\t') || strchr_m(p,'(') || strchr_m(p,')'));
392
393           if (strlen(p)>strlen(comment) && has_punctuation)
394             {
395               StrnCpy(comment,p,sizeof(comment)-1);
396               continue;
397             }
398
399           if (strlen(p) <= MAXPRINTERLEN && strlen(p)>strlen(name) && !has_punctuation)
400             {
401               if (!*comment) pstrcpy(comment,name);
402               pstrcpy(name,p);
403               continue;
404             }
405
406           if (!strchr_m(comment,' ') && 
407               strlen(p) > strlen(comment))
408             {
409               StrnCpy(comment,p,sizeof(comment)-1);
410               continue;
411             }
412         }
413
414       comment[60] = 0;
415       name[MAXPRINTERLEN] = 0;
416
417       if (*name) 
418         fn(name,comment);
419     }
420   x_fclose(pfile);
421 }