first pass at updating head branch to be to be the same as the SAMBA_2_0 branch
[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 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         FILE *pfile;
103         char *line,*p;
104         pstring name,comment;
105         line  = NULL;
106         *name = 0;
107         *comment = 0;
108
109         if ((pfile = sys_fopen(psz, "r")) == 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(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         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         FILE *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 = sys_fopen(psz, "r")) == 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         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.
248 ***************************************************************************/
249 BOOL pcap_printername_ok(char *pszPrintername, char *pszPrintcapname)
250 {
251   char *line=NULL;
252   char *psz;
253   char *p,*q;
254   FILE *pfile;
255
256   if (pszPrintername == NULL || pszPrintername[0] == '\0')
257     {
258       DEBUG(0,( "Attempt to locate null printername! Internal error?\n"));
259       return(False);
260     }
261
262   /* only go looking if no printcap name supplied */
263   if ((psz = pszPrintcapname) == NULL || psz[0] == '\0')
264     if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
265       {
266         DEBUG(0,( "No printcap file name configured!\n"));
267         return(False);
268       }
269
270 #ifdef HAVE_LIBCUPS
271     if (strequal(psz, "cups"))
272        return (cups_printername_ok(pszPrintername));
273 #endif /* HAVE_LIBCUPS */
274
275 #ifdef SYSV
276     if (strequal(psz, "lpstat"))
277        return (sysv_printername_ok(pszPrintername));
278 #endif
279
280 #ifdef AIX
281   if (strlocate(psz,"/qconfig"))
282      return(ScanQconfig(psz,pszPrintername));
283 #endif
284
285   if ((pfile = sys_fopen(psz, "r")) == NULL)
286     {
287       DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
288       return(False);
289     }
290
291   for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
292     {
293       if (*line == '#' || *line == 0)
294         continue;
295
296       /* now we have a real printer line - cut it off at the first : */      
297       p = strchr(line,':');
298       if (p) *p = 0;
299       
300       /* now just check if the name is in the list */
301       /* NOTE: I avoid strtok as the fn calling this one may be using it */
302       for (p=line; p; p=q)
303         {
304           if ((q = strchr(p,'|'))) *q++ = 0;
305
306           if (strequal(p,pszPrintername))
307             {
308               /* normalise the case */
309               pstrcpy(pszPrintername,p);
310               free(line);
311               fclose(pfile);
312               return(True);           
313             }
314           p = q;
315         }
316     }
317
318   fclose(pfile);
319   return(False);
320 }
321
322
323 /***************************************************************************
324 run a function on each printer name in the printcap file. The function is 
325 passed the primary name and the comment (if possible)
326 ***************************************************************************/
327 void pcap_printer_fn(void (*fn)(char *, char *))
328 {
329   pstring name,comment;
330   char *line;
331   char *psz;
332   char *p,*q;
333   FILE *pfile;
334
335   /* only go looking if no printcap name supplied */
336   if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
337     {
338       DEBUG(0,( "No printcap file name configured!\n"));
339       return;
340     }
341
342 #ifdef HAVE_LIBCUPS
343     if (strequal(psz, "cups")) {
344       cups_printer_fn(fn);
345       return;
346     }
347 #endif /* HAVE_LIBCUPS */
348
349 #ifdef SYSV
350     if (strequal(psz, "lpstat")) {
351       sysv_printer_fn(fn);
352       return;
353     }
354 #endif
355
356 #ifdef AIX
357   if (strlocate(psz,"/qconfig"))
358   {
359         ScanQconfig_fn(psz,fn);
360      return;
361   }
362 #endif
363
364   if ((pfile = sys_fopen(psz, "r")) == NULL)
365     {
366       DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
367       return;
368     }
369
370   for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
371     {
372       if (*line == '#' || *line == 0)
373         continue;
374
375       /* now we have a real printer line - cut it off at the first : */      
376       p = strchr(line,':');
377       if (p) *p = 0;
378       
379       /* now find the most likely printer name and comment 
380        this is pure guesswork, but it's better than nothing */
381       *name = 0;
382       *comment = 0;
383       for (p=line; p; p=q)
384         {
385           BOOL has_punctuation;
386           if ((q = strchr(p,'|'))) *q++ = 0;
387
388           has_punctuation = (strchr(p,' ') || strchr(p,'(') || strchr(p,')'));
389
390           if (strlen(p)>strlen(comment) && has_punctuation)
391             {
392               StrnCpy(comment,p,sizeof(comment)-1);
393               continue;
394             }
395
396           if (strlen(p) <= MAXPRINTERLEN && strlen(p)>strlen(name) && !has_punctuation)
397             {
398               if (!*comment) pstrcpy(comment,name);
399               pstrcpy(name,p);
400               continue;
401             }
402
403           if (!strchr(comment,' ') && 
404               strlen(p) > strlen(comment))
405             {
406               StrnCpy(comment,p,sizeof(comment)-1);
407               continue;
408             }
409         }
410
411       comment[60] = 0;
412       name[MAXPRINTERLEN] = 0;
413
414       if (*name)
415         fn(name,comment);
416     }
417   fclose(pfile);
418 }