| 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 3 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, see <http://www.gnu.org/licenses/>. | 
|---|
| 26 | */ | 
|---|
| 27 |  | 
|---|
| 28 | /* | 
|---|
| 29 | *  This module contains code to parse and cache printcap data, possibly | 
|---|
| 30 | *  in concert with the CUPS/SYSV/AIX-specific code found elsewhere. | 
|---|
| 31 | * | 
|---|
| 32 | *  The way this module looks at the printcap file is very simplistic. | 
|---|
| 33 | *  Only the local printcap file is inspected (no searching of NIS | 
|---|
| 34 | *  databases etc). | 
|---|
| 35 | * | 
|---|
| 36 | *  There are assumed to be one or more printer names per record, held | 
|---|
| 37 | *  as a set of sub-fields separated by vertical bar symbols ('|') in the | 
|---|
| 38 | *  first field of the record. The field separator is assumed to be a colon | 
|---|
| 39 | *  ':' and the record separator a newline. | 
|---|
| 40 | * | 
|---|
| 41 | *  Lines ending with a backspace '\' are assumed to flag that the following | 
|---|
| 42 | *  line is a continuation line so that a set of lines can be read as one | 
|---|
| 43 | *  printcap entry. | 
|---|
| 44 | * | 
|---|
| 45 | *  A line stating with a hash '#' is assumed to be a comment and is ignored | 
|---|
| 46 | *  Comments are discarded before the record is strung together from the | 
|---|
| 47 | *  set of continuation lines. | 
|---|
| 48 | * | 
|---|
| 49 | *  Opening a pipe for "lpc status" and reading that would probably | 
|---|
| 50 | *  be pretty effective. Code to do this already exists in the freely | 
|---|
| 51 | *  distributable PCNFS server code. | 
|---|
| 52 | * | 
|---|
| 53 | *  Modified to call SVID/XPG4 support if printcap name is set to "lpstat" | 
|---|
| 54 | *  in smb.conf under Solaris. | 
|---|
| 55 | * | 
|---|
| 56 | *  Modified to call CUPS support if printcap name is set to "cups" | 
|---|
| 57 | *  in smb.conf. | 
|---|
| 58 | * | 
|---|
| 59 | *  Modified to call iPrint support if printcap name is set to "iprint" | 
|---|
| 60 | *  in smb.conf. | 
|---|
| 61 | */ | 
|---|
| 62 |  | 
|---|
| 63 | #include "includes.h" | 
|---|
| 64 |  | 
|---|
| 65 |  | 
|---|
| 66 | struct pcap_cache { | 
|---|
| 67 | char *name; | 
|---|
| 68 | char *comment; | 
|---|
| 69 | struct pcap_cache *next; | 
|---|
| 70 | }; | 
|---|
| 71 |  | 
|---|
| 72 | /* The systemwide printcap cache. */ | 
|---|
| 73 | static struct pcap_cache *pcap_cache = NULL; | 
|---|
| 74 |  | 
|---|
| 75 | bool pcap_cache_add_specific(struct pcap_cache **ppcache, const char *name, const char *comment) | 
|---|
| 76 | { | 
|---|
| 77 | struct pcap_cache *p; | 
|---|
| 78 |  | 
|---|
| 79 | if (name == NULL || ((p = SMB_MALLOC_P(struct pcap_cache)) == NULL)) | 
|---|
| 80 | return false; | 
|---|
| 81 |  | 
|---|
| 82 | p->name = SMB_STRDUP(name); | 
|---|
| 83 | p->comment = (comment && *comment) ? SMB_STRDUP(comment) : NULL; | 
|---|
| 84 |  | 
|---|
| 85 | DEBUG(11,("pcap_cache_add_specific: Adding name %s info %s\n", | 
|---|
| 86 | p->name, p->comment ? p->comment : "")); | 
|---|
| 87 |  | 
|---|
| 88 | p->next = *ppcache; | 
|---|
| 89 | *ppcache = p; | 
|---|
| 90 |  | 
|---|
| 91 | return true; | 
|---|
| 92 | } | 
|---|
| 93 |  | 
|---|
| 94 | void pcap_cache_destroy_specific(struct pcap_cache **pp_cache) | 
|---|
| 95 | { | 
|---|
| 96 | struct pcap_cache *p, *next; | 
|---|
| 97 |  | 
|---|
| 98 | for (p = *pp_cache; p != NULL; p = next) { | 
|---|
| 99 | next = p->next; | 
|---|
| 100 |  | 
|---|
| 101 | SAFE_FREE(p->name); | 
|---|
| 102 | SAFE_FREE(p->comment); | 
|---|
| 103 | SAFE_FREE(p); | 
|---|
| 104 | } | 
|---|
| 105 | *pp_cache = NULL; | 
|---|
| 106 | } | 
|---|
| 107 |  | 
|---|
| 108 | bool pcap_cache_add(const char *name, const char *comment) | 
|---|
| 109 | { | 
|---|
| 110 | return pcap_cache_add_specific(&pcap_cache, name, comment); | 
|---|
| 111 | } | 
|---|
| 112 |  | 
|---|
| 113 | bool pcap_cache_loaded(void) | 
|---|
| 114 | { | 
|---|
| 115 | return (pcap_cache != NULL); | 
|---|
| 116 | } | 
|---|
| 117 |  | 
|---|
| 118 | void pcap_cache_replace(const struct pcap_cache *pcache) | 
|---|
| 119 | { | 
|---|
| 120 | const struct pcap_cache *p; | 
|---|
| 121 |  | 
|---|
| 122 | pcap_cache_destroy_specific(&pcap_cache); | 
|---|
| 123 | for (p = pcache; p; p = p->next) { | 
|---|
| 124 | pcap_cache_add(p->name, p->comment); | 
|---|
| 125 | } | 
|---|
| 126 | } | 
|---|
| 127 |  | 
|---|
| 128 | void pcap_cache_reload(void (*post_cache_fill_fn)(void)) | 
|---|
| 129 | { | 
|---|
| 130 | const char *pcap_name = lp_printcapname(); | 
|---|
| 131 | bool pcap_reloaded = False; | 
|---|
| 132 | struct pcap_cache *tmp_cache = NULL; | 
|---|
| 133 | XFILE *pcap_file; | 
|---|
| 134 | char *pcap_line; | 
|---|
| 135 | bool post_cache_fill_fn_handled = false; | 
|---|
| 136 |  | 
|---|
| 137 | DEBUG(3, ("reloading printcap cache\n")); | 
|---|
| 138 |  | 
|---|
| 139 | /* only go looking if no printcap name supplied */ | 
|---|
| 140 | if (pcap_name == NULL || *pcap_name == 0) { | 
|---|
| 141 | DEBUG(0, ("No printcap file name configured!\n")); | 
|---|
| 142 | return; | 
|---|
| 143 | } | 
|---|
| 144 |  | 
|---|
| 145 | tmp_cache = pcap_cache; | 
|---|
| 146 | pcap_cache = NULL; | 
|---|
| 147 |  | 
|---|
| 148 | #ifdef HAVE_CUPS | 
|---|
| 149 | if (strequal(pcap_name, "cups")) { | 
|---|
| 150 | pcap_reloaded = cups_cache_reload(post_cache_fill_fn); | 
|---|
| 151 | /* | 
|---|
| 152 | * cups_cache_reload() is async and calls post_cache_fill_fn() | 
|---|
| 153 | * on successful completion | 
|---|
| 154 | */ | 
|---|
| 155 | post_cache_fill_fn_handled = true; | 
|---|
| 156 | goto done; | 
|---|
| 157 | } | 
|---|
| 158 | #endif | 
|---|
| 159 |  | 
|---|
| 160 | #ifdef HAVE_IPRINT | 
|---|
| 161 | if (strequal(pcap_name, "iprint")) { | 
|---|
| 162 | pcap_reloaded = iprint_cache_reload(); | 
|---|
| 163 | goto done; | 
|---|
| 164 | } | 
|---|
| 165 | #endif | 
|---|
| 166 |  | 
|---|
| 167 | #if defined(SYSV) || defined(HPUX) | 
|---|
| 168 | if (strequal(pcap_name, "lpstat")) { | 
|---|
| 169 | pcap_reloaded = sysv_cache_reload(); | 
|---|
| 170 | goto done; | 
|---|
| 171 | } | 
|---|
| 172 | #endif | 
|---|
| 173 |  | 
|---|
| 174 | #ifdef AIX | 
|---|
| 175 | if (strstr_m(pcap_name, "/qconfig") != NULL) { | 
|---|
| 176 | pcap_reloaded = aix_cache_reload(); | 
|---|
| 177 | goto done; | 
|---|
| 178 | } | 
|---|
| 179 | #endif | 
|---|
| 180 |  | 
|---|
| 181 | /* handle standard printcap - moved from pcap_printer_fn() */ | 
|---|
| 182 |  | 
|---|
| 183 | if ((pcap_file = x_fopen(pcap_name, O_RDONLY, 0)) == NULL) { | 
|---|
| 184 | DEBUG(0, ("Unable to open printcap file %s for read!\n", pcap_name)); | 
|---|
| 185 | goto done; | 
|---|
| 186 | } | 
|---|
| 187 |  | 
|---|
| 188 | for (; (pcap_line = fgets_slash(NULL, 1024, pcap_file)) != NULL; free(pcap_line)) { | 
|---|
| 189 | char name[MAXPRINTERLEN+1]; | 
|---|
| 190 | char comment[62]; | 
|---|
| 191 | char *p, *q; | 
|---|
| 192 |  | 
|---|
| 193 | if (*pcap_line == '#' || *pcap_line == 0) | 
|---|
| 194 | continue; | 
|---|
| 195 |  | 
|---|
| 196 | /* now we have a real printer line - cut at the first : */ | 
|---|
| 197 | if ((p = strchr_m(pcap_line, ':')) != NULL) | 
|---|
| 198 | *p = 0; | 
|---|
| 199 |  | 
|---|
| 200 | /* | 
|---|
| 201 | * now find the most likely printer name and comment | 
|---|
| 202 | * this is pure guesswork, but it's better than nothing | 
|---|
| 203 | */ | 
|---|
| 204 | for (*name = *comment = 0, p = pcap_line; p != NULL; p = q) { | 
|---|
| 205 | bool has_punctuation; | 
|---|
| 206 |  | 
|---|
| 207 | if ((q = strchr_m(p, '|')) != NULL) | 
|---|
| 208 | *q++ = 0; | 
|---|
| 209 |  | 
|---|
| 210 | has_punctuation = (strchr_m(p, ' ') || | 
|---|
| 211 | strchr_m(p, '\t') || | 
|---|
| 212 | strchr_m(p, '"') || | 
|---|
| 213 | strchr_m(p, '\'') || | 
|---|
| 214 | strchr_m(p, ';') || | 
|---|
| 215 | strchr_m(p, ',') || | 
|---|
| 216 | strchr_m(p, '(') || | 
|---|
| 217 | strchr_m(p, ')')); | 
|---|
| 218 |  | 
|---|
| 219 | if (strlen(p) > strlen(comment) && has_punctuation) { | 
|---|
| 220 | strlcpy(comment, p, sizeof(comment)); | 
|---|
| 221 | continue; | 
|---|
| 222 | } | 
|---|
| 223 |  | 
|---|
| 224 | if (strlen(p) <= MAXPRINTERLEN && *name == '\0' && !has_punctuation) { | 
|---|
| 225 | if (!*comment) { | 
|---|
| 226 | strlcpy(comment, name, sizeof(comment)); | 
|---|
| 227 | } | 
|---|
| 228 | strlcpy(name, p, sizeof(name)); | 
|---|
| 229 | continue; | 
|---|
| 230 | } | 
|---|
| 231 |  | 
|---|
| 232 | if (!strchr_m(comment, ' ') && | 
|---|
| 233 | strlen(p) > strlen(comment)) { | 
|---|
| 234 | strlcpy(comment, p, sizeof(comment)); | 
|---|
| 235 | continue; | 
|---|
| 236 | } | 
|---|
| 237 | } | 
|---|
| 238 |  | 
|---|
| 239 | if (*name && !pcap_cache_add(name, comment)) { | 
|---|
| 240 | x_fclose(pcap_file); | 
|---|
| 241 | goto done; | 
|---|
| 242 | } | 
|---|
| 243 | } | 
|---|
| 244 |  | 
|---|
| 245 | x_fclose(pcap_file); | 
|---|
| 246 | pcap_reloaded = True; | 
|---|
| 247 |  | 
|---|
| 248 | done: | 
|---|
| 249 | DEBUG(3, ("reload status: %s\n", (pcap_reloaded) ? "ok" : "error")); | 
|---|
| 250 |  | 
|---|
| 251 | if (pcap_reloaded) { | 
|---|
| 252 | pcap_cache_destroy_specific(&tmp_cache); | 
|---|
| 253 | if ((post_cache_fill_fn_handled == false) | 
|---|
| 254 | && (post_cache_fill_fn != NULL)) { | 
|---|
| 255 | post_cache_fill_fn(); | 
|---|
| 256 | } | 
|---|
| 257 | } else { | 
|---|
| 258 | pcap_cache_destroy_specific(&pcap_cache); | 
|---|
| 259 | pcap_cache = tmp_cache; | 
|---|
| 260 | } | 
|---|
| 261 |  | 
|---|
| 262 | return; | 
|---|
| 263 | } | 
|---|
| 264 |  | 
|---|
| 265 |  | 
|---|
| 266 | bool pcap_printername_ok(const char *printername) | 
|---|
| 267 | { | 
|---|
| 268 | struct pcap_cache *p; | 
|---|
| 269 |  | 
|---|
| 270 | for (p = pcap_cache; p != NULL; p = p->next) | 
|---|
| 271 | if (strequal(p->name, printername)) | 
|---|
| 272 | return True; | 
|---|
| 273 |  | 
|---|
| 274 | return False; | 
|---|
| 275 | } | 
|---|
| 276 |  | 
|---|
| 277 | /*************************************************************************** | 
|---|
| 278 | run a function on each printer name in the printcap file. | 
|---|
| 279 | ***************************************************************************/ | 
|---|
| 280 |  | 
|---|
| 281 | void pcap_printer_fn_specific(const struct pcap_cache *pc, | 
|---|
| 282 | void (*fn)(const char *, const char *, void *), | 
|---|
| 283 | void *pdata) | 
|---|
| 284 | { | 
|---|
| 285 | const struct pcap_cache *p; | 
|---|
| 286 |  | 
|---|
| 287 | for (p = pc; p != NULL; p = p->next) | 
|---|
| 288 | fn(p->name, p->comment, pdata); | 
|---|
| 289 |  | 
|---|
| 290 | return; | 
|---|
| 291 | } | 
|---|
| 292 |  | 
|---|
| 293 | void pcap_printer_fn(void (*fn)(const char *, const char *, void *), void *pdata) | 
|---|
| 294 | { | 
|---|
| 295 | pcap_printer_fn_specific(pcap_cache, fn, pdata); | 
|---|
| 296 | } | 
|---|