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