convert more code to use XFILE
[jra/samba/.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 extern int DEBUGLEVEL;
69
70 #ifdef AIX
71 /*  ******************************************
72      Extend for AIX system and qconfig file
73        from 'boulard@univ-rennes1.fr
74     ****************************************** */
75 static int strlocate(char *xpLine,char *xpS)
76 {
77         int iS,iL,iRet;
78         char *p;
79         iS = strlen(xpS);
80         iL = strlen(xpLine);
81
82         iRet = 0;
83         p = xpLine;
84         while (iL >= iS)
85         {
86                 if (strncmp(p,xpS,iS) == 0) {iRet =1;break;};
87                 p++;
88                 iL--;
89         }
90         /*DEBUG(3,(" strlocate %s in line '%s',ret=%d\n",xpS,xpLine,iRet));*/
91         
92         return(iRet);
93 }
94         
95         
96 /* ******************************************************************* */
97 /* *    Scan qconfig and search all virtual printer (device printer) * */
98 /* ******************************************************************* */
99 static void ScanQconfig_fn(char *psz,void (*fn)(char *, char *))
100 {
101         int iEtat;
102         XFILE *pfile;
103         char *line,*p;
104         pstring name,comment;
105         line  = NULL;
106         *name = 0;
107         *comment = 0;
108
109         if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
110         {
111               DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
112               return;
113         }
114
115         iEtat = 0;
116         /* scan qconfig file for searching <printername>:       */
117         for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
118         {
119                 if (*line == '*' || *line == 0)
120                 continue;
121                 switch (iEtat)
122                 {
123                         case 0: /* locate an entry */
124                          if (*line == '\t' || *line == ' ') continue;
125                          if ((p=strchr_m(line,':')))
126                          {
127                                 *p = '\0';
128                                 p = strtok(line,":");
129                                 if (strcmp(p,"bsh")!=0)
130                                   {
131                                     pstrcpy(name,p);
132                                     iEtat = 1;
133                                     continue;
134                                   }
135                          }
136                          break;
137                         case 1: /* scanning device stanza */
138                          if (*line == '*' || *line == 0) continue;
139                          if (*line != '\t' && *line != ' ')
140                          {
141                            /* name is found without stanza device  */
142                            /* probably a good printer ???               */
143                            fn(name,comment);
144                            iEtat = 0;
145                            continue;
146                           }
147                         
148                           if (strlocate(line,"backend"))
149                           {
150                                 /* it's a device, not a virtual printer*/
151                                 iEtat = 0;
152                           }
153                           else if (strlocate(line,"device"))
154                           {
155                                 /* it's a good virtual printer */
156                                 fn(name,comment);
157                                 iEtat = 0;
158                                 continue;
159                           }
160                           break;
161                 }
162         }
163         x_fclose(pfile);
164 }
165
166 /* Scan qconfig file and locate de printername */
167
168 static BOOL ScanQconfig(char *psz,char *pszPrintername)
169 {
170         int iLg,iEtat;
171         XFILE *pfile;
172         char *pName;
173         char *line;
174
175         pName = NULL;
176         line  = NULL;
177         if ((pszPrintername!= NULL) && ((iLg = strlen(pszPrintername)) > 0))
178          pName = malloc(iLg+10);
179         if (pName == NULL)
180         {
181                 DEBUG(0,(" Unable to allocate memory for printer %s\n",pszPrintername));
182                 return(False);
183         }
184         if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
185         {
186               DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
187               free(pName);
188               return(False);
189         }
190         slprintf(pName, iLg + 9, "%s:",pszPrintername);
191         iLg = strlen(pName);
192         /*DEBUG(3,( " Looking for entry %s\n",pName));*/
193         iEtat = 0;
194         /* scan qconfig file for searching <printername>:       */
195         for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
196         {
197                 if (*line == '*' || *line == 0)
198                 continue;
199                 switch (iEtat)
200                 {
201                         case 0: /* scanning entry */
202                          if (strncmp(line,pName,iLg) == 0)
203                          {
204                                 iEtat = 1;
205                                 continue;
206                          }
207                          break;
208                         case 1: /* scanning device stanza */
209                          if (*line == '*' || *line == 0) continue;
210                          if (*line != '\t' && *line != ' ')
211                          {
212                            /* name is found without stanza device  */
213                            /* probably a good printer ???               */
214                            free (line);
215                            free(pName);
216                            fclose(pfile);
217                            return(True);
218                           }
219                         
220                           if (strlocate(line,"backend"))
221                           {
222                                 /* it's a device, not a virtual printer*/
223                                 iEtat = 0;
224                           }
225                           else if (strlocate(line,"device"))
226                           {
227                                 /* it's a good virtual printer */
228                                 free (line);
229                                 free(pName);
230                                 fclose(pfile);
231                                 return(True);
232                           }
233                           break;
234                 }
235         }
236         free (pName);
237         x_fclose(pfile);
238         return(False);
239 }
240 #endif /* AIX */
241
242
243 /***************************************************************************
244 Scan printcap file pszPrintcapname for a printer called pszPrintername. 
245 Return True if found, else False. Returns False on error, too, after logging 
246 the error at level 0. For generality, the printcap name may be passed - if
247 passed as NULL, the configuration will be queried for the name. pszPrintername
248 must be in DOS codepage.
249 The xxx_printername_ok functions need fixing to understand they are being
250 given a DOS codepage. FIXME !! JRA.
251 ***************************************************************************/
252 BOOL pcap_printername_ok(char *pszPrintername, char *pszPrintcapname)
253 {
254   char *line=NULL;
255   char *psz;
256   char *p,*q;
257   XFILE *pfile;
258
259   if (pszPrintername == NULL || pszPrintername[0] == '\0')
260     {
261       DEBUG(0,( "Attempt to locate null printername! Internal error?\n"));
262       return(False);
263     }
264
265   /* only go looking if no printcap name supplied */
266   if ((psz = pszPrintcapname) == NULL || psz[0] == '\0')
267     if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
268       {
269         DEBUG(0,( "No printcap file name configured!\n"));
270         return(False);
271       }
272
273 #ifdef HAVE_CUPS
274     if (strequal(psz, "cups"))
275        return (cups_printername_ok(pszPrintername));
276 #endif /* HAVE_CUPS */
277
278 #ifdef SYSV
279     if (strequal(psz, "lpstat"))
280        return (sysv_printername_ok(pszPrintername));
281 #endif
282
283 #ifdef AIX
284   if (strlocate(psz,"/qconfig"))
285      return(ScanQconfig(psz,pszPrintername));
286 #endif
287
288   if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
289     {
290       DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
291       return(False);
292     }
293
294   for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
295     {
296       if (*line == '#' || *line == 0)
297         continue;
298
299       /* now we have a real printer line - cut it off at the first : */      
300       p = strchr_m(line,':');
301       if (p) *p = 0;
302       
303       /* now just check if the name is in the list */
304       /* NOTE: I avoid strtok as the fn calling this one may be using it */
305       for (p=line; p; p=q)
306         {
307           if ((q = strchr_m(p,'|'))) *q++ = 0;
308
309           if (strequal(p,pszPrintername))
310             {
311               /* normalise the case */
312               pstrcpy(pszPrintername,p);
313               free(line);
314               x_fclose(pfile);
315               return(True);           
316             }
317           p = q;
318         }
319     }
320
321   x_fclose(pfile);
322   return(False);
323 }
324
325
326 /***************************************************************************
327 run a function on each printer name in the printcap file. The function is 
328 passed the primary name and the comment (if possible). Note the fn() takes
329 strings in DOS codepage. This means the xxx_printer_fn() calls must be fixed
330 to return DOS codepage. FIXME !! JRA.
331 ***************************************************************************/
332 void pcap_printer_fn(void (*fn)(char *, char *))
333 {
334   pstring name,comment;
335   char *line;
336   char *psz;
337   char *p,*q;
338   XFILE *pfile;
339
340   /* only go looking if no printcap name supplied */
341   if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
342     {
343       DEBUG(0,( "No printcap file name configured!\n"));
344       return;
345     }
346
347 #ifdef HAVE_CUPS
348     if (strequal(psz, "cups")) {
349       cups_printer_fn(fn);
350       return;
351     }
352 #endif /* HAVE_CUPS */
353
354 #ifdef SYSV
355     if (strequal(psz, "lpstat")) {
356       sysv_printer_fn(fn);
357       return;
358     }
359 #endif
360
361 #ifdef AIX
362   if (strlocate(psz,"/qconfig"))
363   {
364         ScanQconfig_fn(psz,fn);
365      return;
366   }
367 #endif
368
369   if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
370     {
371       DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
372       return;
373     }
374
375   for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
376     {
377       if (*line == '#' || *line == 0)
378         continue;
379
380       /* now we have a real printer line - cut it off at the first : */      
381       p = strchr_m(line,':');
382       if (p) *p = 0;
383       
384       /* now find the most likely printer name and comment 
385        this is pure guesswork, but it's better than nothing */
386       *name = 0;
387       *comment = 0;
388       for (p=line; p; p=q)
389         {
390           BOOL has_punctuation;
391           if ((q = strchr_m(p,'|'))) *q++ = 0;
392
393           has_punctuation = (strchr_m(p,' ') || strchr_m(p,'\t') || strchr_m(p,'(') || strchr_m(p,')'));
394
395           if (strlen(p)>strlen(comment) && has_punctuation)
396             {
397               StrnCpy(comment,p,sizeof(comment)-1);
398               continue;
399             }
400
401           if (strlen(p) <= MAXPRINTERLEN && strlen(p)>strlen(name) && !has_punctuation)
402             {
403               if (!*comment) pstrcpy(comment,name);
404               pstrcpy(name,p);
405               continue;
406             }
407
408           if (!strchr_m(comment,' ') && 
409               strlen(p) > strlen(comment))
410             {
411               StrnCpy(comment,p,sizeof(comment)-1);
412               continue;
413             }
414         }
415
416       comment[60] = 0;
417       name[MAXPRINTERLEN] = 0;
418
419       if (*name) 
420         fn(name,comment);
421     }
422   x_fclose(pfile);
423 }