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