]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/conf.c
4dd7c318c4545999c849bb6f93283a1e135ed805
[thirdparty/cups.git] / scheduler / conf.c
1 /*
2 * "$Id: conf.c,v 1.77.2.40 2003/04/23 18:55:28 mike Exp $"
3 *
4 * Configuration routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2003 by Easy Software Products, all rights reserved.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * ReadConfiguration() - Read the cupsd.conf file.
27 * read_configuration() - Read a configuration file.
28 * read_location() - Read a <Location path> definition.
29 * get_address() - Get an address + port number from a line.
30 * get_addr_and_mask() - Get an IP address and netmask.
31 * CDSAGetServerCerts() - Convert a keychain name into the CFArrayRef
32 * required by SSLSetCertificate.
33 */
34
35 /*
36 * Include necessary headers...
37 */
38
39 #include "cupsd.h"
40 #include <stdarg.h>
41 #include <pwd.h>
42 #include <grp.h>
43
44 #ifdef HAVE_CDSASSL
45 # include <Security/SecureTransport.h>
46 # include <Security/SecIdentitySearch.h>
47 #endif /* HAVE_CDSASSL */
48
49 #ifdef HAVE_VSYSLOG
50 # include <syslog.h>
51 #endif /* HAVE_VSYSLOG */
52
53
54 /*
55 * Possibly missing network definitions...
56 */
57
58 #ifndef INADDR_NONE
59 # define INADDR_NONE 0xffffffff
60 #endif /* !INADDR_NONE */
61
62
63 /*
64 * Some OS's don't have hstrerror(), most notably Solaris...
65 */
66
67 #ifndef HAVE_HSTRERROR
68 const char * /* O - Error string */
69 cups_hstrerror(int error) /* I - Error number */
70 {
71 static const char * const errors[] =
72 {
73 "OK",
74 "Host not found.",
75 "Try again.",
76 "Unrecoverable lookup error.",
77 "No data associated with name."
78 };
79
80
81 if (error < 0 || error > 4)
82 return ("Unknown hostname lookup error.");
83 else
84 return (errors[error]);
85 }
86 #elif defined(_AIX)
87 /*
88 * AIX doesn't provide a prototype but does provide the function...
89 */
90 extern const char *hstrerror(int);
91 #endif /* !HAVE_HSTRERROR */
92
93
94 /*
95 * Configuration variable structure...
96 */
97
98 typedef struct
99 {
100 char *name; /* Name of variable */
101 void *ptr; /* Pointer to variable */
102 int type; /* Type (int, string, address) */
103 } var_t;
104
105 #define VAR_INTEGER 0
106 #define VAR_STRING 1
107 #define VAR_BOOLEAN 2
108
109
110 /*
111 * Local globals...
112 */
113
114 static var_t variables[] =
115 {
116 { "AccessLog", &AccessLog, VAR_STRING },
117 { "AutoPurgeJobs", &JobAutoPurge, VAR_BOOLEAN },
118 { "BrowseInterval", &BrowseInterval, VAR_INTEGER },
119 { "BrowsePort", &BrowsePort, VAR_INTEGER },
120 { "BrowseShortNames", &BrowseShortNames, VAR_BOOLEAN },
121 { "BrowseTimeout", &BrowseTimeout, VAR_INTEGER },
122 { "Browsing", &Browsing, VAR_BOOLEAN },
123 { "Classification", &Classification, VAR_STRING },
124 { "ClassifyOverride", &ClassifyOverride, VAR_BOOLEAN },
125 { "ConfigFilePerm", &ConfigFilePerm, VAR_INTEGER },
126 { "DataDir", &DataDir, VAR_STRING },
127 { "DefaultCharset", &DefaultCharset, VAR_STRING },
128 { "DefaultLanguage", &DefaultLanguage, VAR_STRING },
129 { "DocumentRoot", &DocumentRoot, VAR_STRING },
130 { "ErrorLog", &ErrorLog, VAR_STRING },
131 { "FaxRetryLimit", &FaxRetryLimit, VAR_INTEGER },
132 { "FaxRetryInterval", &FaxRetryInterval, VAR_INTEGER },
133 { "FileDevice", &FileDevice, VAR_BOOLEAN },
134 { "FilterLimit", &FilterLimit, VAR_INTEGER },
135 { "FilterNice", &FilterNice, VAR_INTEGER },
136 { "FontPath", &FontPath, VAR_STRING },
137 { "HideImplicitMembers", &HideImplicitMembers, VAR_BOOLEAN },
138 { "ImplicitClasses", &ImplicitClasses, VAR_BOOLEAN },
139 { "ImplicitAnyClasses", &ImplicitAnyClasses, VAR_BOOLEAN },
140 { "KeepAliveTimeout", &KeepAliveTimeout, VAR_INTEGER },
141 { "KeepAlive", &KeepAlive, VAR_BOOLEAN },
142 { "LimitRequestBody", &MaxRequestSize, VAR_INTEGER },
143 { "ListenBackLog", &ListenBackLog, VAR_INTEGER },
144 { "LogFilePerm", &LogFilePerm, VAR_INTEGER },
145 { "MaxActiveJobs", &MaxActiveJobs, VAR_INTEGER },
146 { "MaxClients", &MaxClients, VAR_INTEGER },
147 { "MaxClientsPerHost", &MaxClientsPerHost, VAR_INTEGER },
148 { "MaxCopies", &MaxCopies, VAR_INTEGER },
149 { "MaxJobs", &MaxJobs, VAR_INTEGER },
150 { "MaxJobsPerPrinter", &MaxJobsPerPrinter, VAR_INTEGER },
151 { "MaxJobsPerUser", &MaxJobsPerUser, VAR_INTEGER },
152 { "MaxLogSize", &MaxLogSize, VAR_INTEGER },
153 { "MaxPrinterHistory", &MaxPrinterHistory, VAR_INTEGER },
154 { "MaxRequestSize", &MaxRequestSize, VAR_INTEGER },
155 { "PageLog", &PageLog, VAR_STRING },
156 { "PreserveJobFiles", &JobFiles, VAR_BOOLEAN },
157 { "PreserveJobHistory", &JobHistory, VAR_BOOLEAN },
158 { "Printcap", &Printcap, VAR_STRING },
159 { "PrintcapGUI", &PrintcapGUI, VAR_STRING },
160 { "RemoteRoot", &RemoteRoot, VAR_STRING },
161 { "RequestRoot", &RequestRoot, VAR_STRING },
162 { "RIPCache", &RIPCache, VAR_STRING },
163 { "RunAsUser", &RunAsUser, VAR_BOOLEAN },
164 { "RootCertDuration", &RootCertDuration, VAR_INTEGER },
165 { "ServerAdmin", &ServerAdmin, VAR_STRING },
166 { "ServerBin", &ServerBin, VAR_STRING },
167 #ifdef HAVE_SSL
168 { "ServerCertificate", &ServerCertificate, VAR_STRING },
169 # if defined(HAVE_LIBSSL) || defined(HAVE_GNUTLS)
170 { "ServerKey", &ServerKey, VAR_STRING },
171 # endif /* HAVE_LIBSSL || HAVE_GNUTLS */
172 #endif /* HAVE_SSL */
173 { "ServerName", &ServerName, VAR_STRING },
174 { "ServerRoot", &ServerRoot, VAR_STRING },
175 { "TempDir", &TempDir, VAR_STRING },
176 { "Timeout", &Timeout, VAR_INTEGER }
177 };
178 #define NUM_VARS (sizeof(variables) / sizeof(variables[0]))
179
180
181 static unsigned ones[4] =
182 {
183 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
184 };
185 static unsigned zeros[4] =
186 {
187 0x00000000, 0x00000000, 0x00000000, 0x00000000
188 };
189
190 #ifdef HAVE_CDSASSL
191 static CFArrayRef CDSAGetServerCerts();
192 #endif /* HAVE_CDSASSL */
193
194
195 /*
196 * Local functions...
197 */
198
199 static int read_configuration(cups_file_t *fp);
200 static int read_location(cups_file_t *fp, char *name, int linenum);
201 static int get_address(const char *value, unsigned defaddress, int defport,
202 int deffamily, http_addr_t *address);
203 static int get_addr_and_mask(const char *value, unsigned *ip,
204 unsigned *mask);
205
206
207 /*
208 * 'ReadConfiguration()' - Read the cupsd.conf file.
209 */
210
211 int /* O - 1 on success, 0 otherwise */
212 ReadConfiguration(void)
213 {
214 int i; /* Looping var */
215 cups_file_t *fp; /* Configuration file */
216 int status; /* Return status */
217 char temp[1024], /* Temporary buffer */
218 *slash; /* Directory separator */
219 char type[MIME_MAX_SUPER + MIME_MAX_TYPE];
220 /* MIME type name */
221 char *language; /* Language string */
222 struct passwd *user; /* Default user */
223 struct group *group; /* Default group */
224 int run_user; /* User that will be running cupsd */
225 char *old_serverroot, /* Old ServerRoot */
226 *old_requestroot; /* Old RequestRoot */
227
228
229 /*
230 * Shutdown the server...
231 */
232
233 StopServer();
234
235 /*
236 * Save the old root paths...
237 */
238
239 old_serverroot = NULL;
240 SetString(&old_serverroot, ServerRoot);
241 old_requestroot = NULL;
242 SetString(&old_requestroot, RequestRoot);
243
244 /*
245 * Reset the server configuration data...
246 */
247
248 DeleteAllLocations();
249
250 if (NumBrowsers > 0)
251 {
252 free(Browsers);
253
254 NumBrowsers = 0;
255 }
256
257 if (NumPolled > 0)
258 {
259 free(Polled);
260
261 NumPolled = 0;
262 }
263
264 if (NumRelays > 0)
265 {
266 for (i = 0; i < NumRelays; i ++)
267 if (Relays[i].from.type == AUTH_NAME)
268 free(Relays[i].from.mask.name.name);
269
270 free(Relays);
271
272 NumRelays = 0;
273 }
274
275 if (NumListeners > 0)
276 {
277 free(Listeners);
278
279 NumListeners = 0;
280 }
281
282 /*
283 * String options...
284 */
285
286 gethostname(temp, sizeof(temp));
287 SetString(&ServerName, temp);
288 SetStringf(&ServerAdmin, "root@%s", temp);
289 SetString(&ServerBin, CUPS_SERVERBIN);
290 SetString(&RequestRoot, CUPS_REQUESTS);
291 SetString(&DocumentRoot, CUPS_DOCROOT);
292 SetString(&DataDir, CUPS_DATADIR);
293 SetString(&AccessLog, CUPS_LOGDIR "/access_log");
294 SetString(&ErrorLog, CUPS_LOGDIR "/error_log");
295 SetString(&PageLog, CUPS_LOGDIR "/page_log");
296 SetString(&Printcap, "/etc/printcap");
297 SetString(&PrintcapGUI, "/usr/bin/glpoptions");
298 SetString(&FontPath, CUPS_FONTPATH);
299 SetString(&RemoteRoot, "remroot");
300
301 strlcpy(temp, ConfigurationFile, sizeof(temp));
302 if ((slash = strrchr(temp, '/')) != NULL)
303 *slash = '\0';
304
305 SetString(&ServerRoot, temp);
306
307 ClearString(&Classification);
308 ClassifyOverride = 0;
309
310 #ifdef HAVE_SSL
311 # ifdef HAVE_CDSASSL
312 SetString(&ServerCertificate, "/var/root/Library/Keychains/CUPS");
313 # else
314 SetString(&ServerCertificate, "ssl/server.crt");
315 SetString(&ServerKey, "ssl/server.key");
316 # endif /* HAVE_CDSASSL */
317 #endif /* HAVE_SSL */
318
319 if ((language = DEFAULT_LANGUAGE) == NULL)
320 language = "en";
321 else if (strcmp(language, "C") == 0 || strcmp(language, "POSIX") == 0)
322 language = "en";
323
324 SetString(&DefaultLanguage, language);
325 SetString(&DefaultCharset, DEFAULT_CHARSET);
326
327 SetString(&RIPCache, "8m");
328
329 if (getenv("TMPDIR") == NULL)
330 SetString(&TempDir, CUPS_REQUESTS "/tmp");
331 else
332 SetString(&TempDir, getenv("TMPDIR"));
333
334 /*
335 * Find the default system group: "sys", "system", or "root"...
336 */
337
338 group = getgrnam(CUPS_DEFAULT_GROUP);
339 endgrent();
340
341 NumSystemGroups = 0;
342
343 if (group != NULL)
344 {
345 SetString(&SystemGroups[0], CUPS_DEFAULT_GROUP);
346 Group = group->gr_gid;
347 }
348 else
349 {
350 group = getgrgid(0);
351 endgrent();
352
353 if (group != NULL)
354 {
355 SetString(&SystemGroups[0], group->gr_name);
356 Group = 0;
357 }
358 else
359 {
360 SetString(&SystemGroups[0], "unknown");
361 Group = 0;
362 }
363 }
364
365 /*
366 * Find the default user...
367 */
368
369 if ((user = getpwnam(CUPS_DEFAULT_USER)) == NULL)
370 User = 1; /* Force to a non-priviledged account */
371 else
372 User = user->pw_uid;
373
374 endpwent();
375
376 /*
377 * Numeric options...
378 */
379
380 ConfigFilePerm = 0640;
381 LogFilePerm = 0644;
382
383 FaxRetryLimit = 5;
384 FaxRetryInterval = 300;
385 FileDevice = FALSE;
386 FilterLevel = 0;
387 FilterLimit = 0;
388 FilterNice = 0;
389 HostNameLookups = FALSE;
390 ImplicitClasses = TRUE;
391 ImplicitAnyClasses = FALSE;
392 HideImplicitMembers = TRUE;
393 KeepAlive = TRUE;
394 KeepAliveTimeout = DEFAULT_KEEPALIVE;
395 ListenBackLog = SOMAXCONN;
396 LogLevel = L_ERROR;
397 MaxClients = 100;
398 MaxClientsPerHost = 0;
399 MaxLogSize = 1024 * 1024;
400 MaxPrinterHistory = 10;
401 MaxRequestSize = 0;
402 RootCertDuration = 300;
403 RunAsUser = FALSE;
404 Timeout = DEFAULT_TIMEOUT;
405
406 BrowseInterval = DEFAULT_INTERVAL;
407 BrowsePort = ippPort();
408 BrowseProtocols = BROWSE_CUPS;
409 BrowseShortNames = TRUE;
410 BrowseTimeout = DEFAULT_TIMEOUT;
411 Browsing = TRUE;
412
413 JobHistory = DEFAULT_HISTORY;
414 JobFiles = DEFAULT_FILES;
415 JobAutoPurge = 0;
416 MaxJobs = 0;
417 MaxActiveJobs = 0;
418 MaxJobsPerPrinter = 0;
419 MaxJobsPerUser = 0;
420 MaxCopies = 100;
421
422 /*
423 * Read the configuration file...
424 */
425
426 if ((fp = cupsFileOpen(ConfigurationFile, "r")) == NULL)
427 return (0);
428
429 status = read_configuration(fp);
430
431 cupsFileClose(fp);
432
433 if (!status)
434 return (0);
435
436 if (RunAsUser)
437 run_user = User;
438 else
439 run_user = getuid();
440
441 /*
442 * Use the default system group if none was supplied in cupsd.conf...
443 */
444
445 if (NumSystemGroups == 0)
446 NumSystemGroups ++;
447
448 /*
449 * Get the access control list for browsing...
450 */
451
452 BrowseACL = FindLocation("CUPS_INTERNAL_BROWSE_ACL");
453
454 /*
455 * Open the system log for cupsd if necessary...
456 */
457
458 #ifdef HAVE_VSYSLOG
459 if (strcmp(AccessLog, "syslog") == 0 ||
460 strcmp(ErrorLog, "syslog") == 0 ||
461 strcmp(PageLog, "syslog") == 0)
462 openlog("cupsd", LOG_PID | LOG_NOWAIT | LOG_NDELAY, LOG_LPR);
463 #endif /* HAVE_VSYSLOG */
464
465 /*
466 * Log the configuration file that was used...
467 */
468
469 LogMessage(L_INFO, "Loaded configuration file \"%s\"", ConfigurationFile);
470
471 /*
472 * Update all relative filenames to include the full path from ServerRoot...
473 */
474
475 if (DocumentRoot[0] != '/')
476 SetStringf(&DocumentRoot, "%s/%s", ServerRoot, DocumentRoot);
477
478 if (RequestRoot[0] != '/')
479 SetStringf(&RequestRoot, "%s/%s", ServerRoot, RequestRoot);
480
481 if (ServerBin[0] != '/')
482 SetStringf(&ServerBin, "%s/%s", ServerRoot, ServerBin);
483
484 #ifdef HAVE_SSL
485 if (ServerCertificate[0] != '/')
486 SetStringf(&ServerCertificate, "%s/%s", ServerRoot, ServerCertificate);
487
488 # if defined(HAVE_LIBSSL) || defined(HAVE_GNUTLS)
489 chown(ServerCertificate, run_user, Group);
490 chmod(ServerCertificate, ConfigFilePerm);
491
492 if (ServerKey[0] != '/')
493 SetStringf(&ServerKey, "%s/%s", ServerRoot, ServerKey);
494
495 chown(ServerKey, run_user, Group);
496 chmod(ServerKey, ConfigFilePerm);
497 # endif /* HAVE_LIBSSL || HAVE_GNUTLS */
498 #endif /* HAVE_SSL */
499
500 /*
501 * Make sure that ServerRoot and the config files are owned and
502 * writable by the user and group in the cupsd.conf file...
503 */
504
505 chown(ServerRoot, run_user, Group);
506 chmod(ServerRoot, 0775);
507
508 snprintf(temp, sizeof(temp), "%s/certs", ServerRoot);
509 chown(temp, run_user, Group);
510 chmod(temp, 0711);
511
512 snprintf(temp, sizeof(temp), "%s/ppd", ServerRoot);
513 chown(temp, run_user, Group);
514 chmod(temp, 0755);
515
516 snprintf(temp, sizeof(temp), "%s/ssl", ServerRoot);
517 chown(temp, run_user, Group);
518 chmod(temp, 0700);
519
520 snprintf(temp, sizeof(temp), "%s/cupsd.conf", ServerRoot);
521 chown(temp, run_user, Group);
522 chmod(temp, ConfigFilePerm);
523
524 snprintf(temp, sizeof(temp), "%s/classes.conf", ServerRoot);
525 chown(temp, run_user, Group);
526 #ifdef __APPLE__
527 chmod(temp, 0600);
528 #else
529 chmod(temp, ConfigFilePerm);
530 #endif /* __APPLE__ */
531
532 snprintf(temp, sizeof(temp), "%s/printers.conf", ServerRoot);
533 chown(temp, run_user, Group);
534 #ifdef __APPLE__
535 chmod(temp, 0600);
536 #else
537 chmod(temp, ConfigFilePerm);
538 #endif /* __APPLE__ */
539
540 snprintf(temp, sizeof(temp), "%s/passwd.md5", ServerRoot);
541 chown(temp, User, Group);
542 chmod(temp, 0600);
543
544 /*
545 * Make sure the request and temporary directories have the right
546 * permissions...
547 */
548
549 chown(RequestRoot, run_user, Group);
550 chmod(RequestRoot, 0710);
551
552 if (strncmp(TempDir, RequestRoot, strlen(RequestRoot)) == 0)
553 {
554 /*
555 * Only update ownership and permissions if the CUPS temp directory
556 * is under the spool directory...
557 */
558
559 chown(TempDir, run_user, Group);
560 chmod(TempDir, 01770);
561 }
562
563 /*
564 * Check the MaxClients setting, and then allocate memory for it...
565 */
566
567 if (MaxClients > (MaxFDs / 3) || MaxClients <= 0)
568 {
569 if (MaxClients > 0)
570 LogMessage(L_INFO, "MaxClients limited to 1/3 of the file descriptor limit (%d)...",
571 MaxFDs);
572
573 MaxClients = MaxFDs / 3;
574 }
575
576 if ((Clients = calloc(sizeof(client_t), MaxClients)) == NULL)
577 {
578 LogMessage(L_ERROR, "ReadConfiguration: Unable to allocate memory for %d clients: %s",
579 MaxClients, strerror(errno));
580 exit(1);
581 }
582 else
583 LogMessage(L_INFO, "Configured for up to %d clients.", MaxClients);
584
585 /*
586 * Check the MaxActiveJobs setting; limit to 1/3 the available
587 * file descriptors, since we need a pipe for each job...
588 */
589
590 if (MaxActiveJobs > (MaxFDs / 3))
591 MaxActiveJobs = MaxFDs / 3;
592
593 if (Classification && strcasecmp(Classification, "none") == 0)
594 ClearString(&Classification);
595
596 if (Classification)
597 LogMessage(L_INFO, "Security set to \"%s\"", Classification);
598
599 /*
600 * Update the MaxClientsPerHost value, as needed...
601 */
602
603 if (MaxClientsPerHost <= 0)
604 MaxClientsPerHost = MaxClients;
605
606 if (MaxClientsPerHost > MaxClients)
607 MaxClientsPerHost = MaxClients;
608
609 LogMessage(L_INFO, "Allowing up to %d client connections per host.",
610 MaxClientsPerHost);
611
612 /*
613 * If we are doing a full reload or the server root has changed, flush
614 * the jobs, printers, etc. and start from scratch...
615 */
616
617 if (NeedReload == RELOAD_ALL ||
618 !old_serverroot || !ServerRoot || strcmp(old_serverroot, ServerRoot) ||
619 !old_requestroot || !RequestRoot || strcmp(old_requestroot, RequestRoot))
620 {
621 LogMessage(L_INFO, "Full reload is required.");
622
623 /*
624 * Free all memory...
625 */
626
627 FreeAllJobs();
628 DeleteAllClasses();
629 DeleteAllPrinters();
630
631 DefaultPrinter = NULL;
632
633 if (Devices)
634 {
635 ippDelete(Devices);
636 Devices = NULL;
637 }
638
639 if (PPDs)
640 {
641 ippDelete(PPDs);
642 PPDs = NULL;
643 }
644
645 if (MimeDatabase != NULL)
646 mimeDelete(MimeDatabase);
647
648 if (NumMimeTypes)
649 {
650 for (i = 0; i < NumMimeTypes; i ++)
651 free((void *)MimeTypes[i]);
652
653 free(MimeTypes);
654 }
655
656 /*
657 * Read the MIME type and conversion database...
658 */
659
660 snprintf(temp, sizeof(temp), "%s/filter", ServerBin);
661
662 MimeDatabase = mimeNew();
663 mimeMerge(MimeDatabase, ServerRoot, temp);
664
665 /*
666 * Create a list of MIME types for the document-format-supported
667 * attribute...
668 */
669
670 NumMimeTypes = MimeDatabase->num_types;
671 if (!mimeType(MimeDatabase, "application", "octet-stream"))
672 NumMimeTypes ++;
673
674 MimeTypes = calloc(NumMimeTypes, sizeof(const char *));
675
676 for (i = 0; i < MimeDatabase->num_types; i ++)
677 {
678 snprintf(type, sizeof(type), "%s/%s", MimeDatabase->types[i]->super,
679 MimeDatabase->types[i]->type);
680
681 MimeTypes[i] = strdup(type);
682 }
683
684 if (i < NumMimeTypes)
685 MimeTypes[i] = strdup("application/octet-stream");
686
687 /*
688 * Load banners...
689 */
690
691 snprintf(temp, sizeof(temp), "%s/banners", DataDir);
692 LoadBanners(temp);
693
694 /*
695 * Load printers and classes...
696 */
697
698 LoadAllPrinters();
699 LoadAllClasses();
700
701 /*
702 * Load devices and PPDs...
703 */
704
705 snprintf(temp, sizeof(temp), "%s/backend", ServerBin);
706 LoadDevices(temp);
707
708 snprintf(temp, sizeof(temp), "%s/model", DataDir);
709 LoadPPDs(temp);
710
711 /*
712 * Load queued jobs...
713 */
714
715 LoadAllJobs();
716
717 LogMessage(L_INFO, "Full reload complete.");
718 }
719 else
720 LogMessage(L_INFO, "Partial reload complete.");
721
722 /*
723 * Reset the reload state...
724 */
725
726 NeedReload = RELOAD_NONE;
727
728 ClearString(&old_serverroot);
729 ClearString(&old_requestroot);
730
731 /*
732 * Startup the server and return...
733 */
734
735 StartServer();
736
737 return (1);
738 }
739
740
741 /*
742 * 'read_configuration()' - Read a configuration file.
743 */
744
745 static int /* O - 1 on success, 0 on failure */
746 read_configuration(cups_file_t *fp) /* I - File to read from */
747 {
748 int i; /* Looping var */
749 int linenum; /* Current line number */
750 int len; /* Length of line */
751 char line[HTTP_MAX_BUFFER], /* Line from file */
752 name[256], /* Parameter name */
753 *nameptr, /* Pointer into name */
754 *value; /* Pointer to value */
755 int valuelen; /* Length of value */
756 var_t *var; /* Current variable */
757 unsigned ip[4], /* Address value */
758 mask[4]; /* Netmask value */
759 dirsvc_relay_t *relay; /* Relay data */
760 dirsvc_poll_t *poll; /* Polling data */
761 http_addr_t polladdr; /* Polling address */
762 location_t *location; /* Browse location */
763 cups_file_t *incfile; /* Include file */
764 char incname[1024]; /* Include filename */
765
766
767 /*
768 * Loop through each line in the file...
769 */
770
771 linenum = 0;
772
773 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
774 {
775 linenum ++;
776
777 /*
778 * Skip comment lines...
779 */
780
781 if (line[0] == '#')
782 continue;
783
784 /*
785 * Strip trailing whitespace, if any...
786 */
787
788 len = strlen(line);
789
790 while (len > 0 && isspace(line[len - 1]))
791 {
792 len --;
793 line[len] = '\0';
794 }
795
796 /*
797 * Extract the name from the beginning of the line...
798 */
799
800 for (value = line; isspace(*value); value ++);
801
802 for (nameptr = name; *value != '\0' && !isspace(*value) &&
803 nameptr < (name + sizeof(name) - 1);)
804 *nameptr++ = *value++;
805 *nameptr = '\0';
806
807 while (isspace(*value))
808 value ++;
809
810 if (name[0] == '\0')
811 continue;
812
813 /*
814 * Decode the directive...
815 */
816
817 if (strcasecmp(name, "Include") == 0)
818 {
819 /*
820 * Include filename
821 */
822
823 if (value[0] == '/')
824 strlcpy(incname, value, sizeof(incname));
825 else
826 snprintf(incname, sizeof(incname), "%s/%s", ServerRoot, value);
827
828 if ((incfile = cupsFileOpen(incname, "rb")) == NULL)
829 LogMessage(L_ERROR, "Unable to include config file \"%s\" - %s",
830 incname, strerror(errno));
831 else
832 {
833 read_configuration(incfile);
834 cupsFileClose(incfile);
835 }
836 }
837 else if (strcasecmp(name, "<Location") == 0)
838 {
839 /*
840 * <Location path>
841 */
842
843 if (line[len - 1] == '>')
844 {
845 line[len - 1] = '\0';
846
847 linenum = read_location(fp, value, linenum);
848 if (linenum == 0)
849 return (0);
850 }
851 else
852 {
853 LogMessage(L_ERROR, "ReadConfiguration() Syntax error on line %d.",
854 linenum);
855 return (0);
856 }
857 }
858 else if (strcasecmp(name, "Port") == 0 ||
859 strcasecmp(name, "Listen") == 0)
860 {
861 /*
862 * Add a listening address to the list...
863 */
864
865 listener_t *temp; /* New listeners array */
866
867
868 if (NumListeners == 0)
869 temp = malloc(sizeof(listener_t));
870 else
871 temp = realloc(Listeners, (NumListeners + 1) * sizeof(listener_t));
872
873 if (!temp)
874 {
875 LogMessage(L_ERROR, "Unable to allocate %s at line %d - %s.",
876 name, linenum, strerror(errno));
877 continue;
878 }
879
880 Listeners = temp;
881 temp += NumListeners;
882
883 if (get_address(value, INADDR_ANY, IPP_PORT, AF_INET, &(temp->address)))
884 {
885 httpAddrString(&(temp->address), line, sizeof(line));
886
887 #ifdef AF_INET6
888 if (temp->address.addr.sa_family == AF_INET6)
889 LogMessage(L_INFO, "Listening to %s:%d (IPv6)", line,
890 ntohs(temp->address.ipv6.sin6_port));
891 else
892 #endif /* AF_INET6 */
893 LogMessage(L_INFO, "Listening to %s:%d", line,
894 ntohs(temp->address.ipv4.sin_port));
895 NumListeners ++;
896 }
897 else
898 LogMessage(L_ERROR, "Bad %s address %s at line %d.", name,
899 value, linenum);
900 }
901 #ifdef HAVE_SSL
902 else if (strcasecmp(name, "SSLPort") == 0 ||
903 strcasecmp(name, "SSLListen") == 0)
904 {
905 /*
906 * Add a listening address to the list...
907 */
908
909 listener_t *temp; /* New listeners array */
910
911
912 if (NumListeners == 0)
913 temp = malloc(sizeof(listener_t));
914 else
915 temp = realloc(Listeners, (NumListeners + 1) * sizeof(listener_t));
916
917 if (!temp)
918 {
919 LogMessage(L_ERROR, "Unable to allocate %s at line %d - %s.",
920 name, linenum, strerror(errno));
921 continue;
922 }
923
924 Listeners = temp;
925 temp += NumListeners;
926
927 if (get_address(value, INADDR_ANY, IPP_PORT, AF_INET, &(temp->address)))
928 {
929 httpAddrString(&(temp->address), line, sizeof(line));
930
931 #ifdef AF_INET6
932 if (temp->address.addr.sa_family == AF_INET6)
933 LogMessage(L_INFO, "Listening to %s:%d (IPv6)", line,
934 ntohs(temp->address.ipv6.sin6_port));
935 else
936 #endif /* AF_INET6 */
937 LogMessage(L_INFO, "Listening to %s:%d", line,
938 ntohs(temp->address.ipv4.sin_port));
939 temp->encryption = HTTP_ENCRYPT_ALWAYS;
940 NumListeners ++;
941 }
942 else
943 LogMessage(L_ERROR, "Bad %s address %s at line %d.", name,
944 value, linenum);
945 }
946 #endif /* HAVE_SSL */
947 else if (strcasecmp(name, "BrowseAddress") == 0)
948 {
949 /*
950 * Add a browse address to the list...
951 */
952
953 dirsvc_addr_t *temp; /* New browse address array */
954
955
956 if (NumBrowsers == 0)
957 temp = malloc(sizeof(dirsvc_addr_t));
958 else
959 temp = realloc(Browsers, (NumBrowsers + 1) * sizeof(dirsvc_addr_t));
960
961 if (!temp)
962 {
963 LogMessage(L_ERROR, "Unable to allocate BrowseAddress at line %d - %s.",
964 linenum, strerror(errno));
965 continue;
966 }
967
968 Browsers = temp;
969 temp += NumBrowsers;
970
971 memset(temp, 0, sizeof(dirsvc_addr_t));
972
973 if (strcasecmp(value, "@LOCAL") == 0)
974 {
975 /*
976 * Send browse data to all local interfaces...
977 */
978
979 strcpy(temp->iface, "*");
980 NumBrowsers ++;
981 }
982 else if (strncasecmp(value, "@IF(", 4) == 0)
983 {
984 /*
985 * Send browse data to the named interface...
986 */
987
988 strlcpy(temp->iface, value + 4, sizeof(Browsers[0].iface));
989
990 nameptr = temp->iface + strlen(temp->iface) - 1;
991 if (*nameptr == ')')
992 *nameptr = '\0';
993
994 NumBrowsers ++;
995 }
996 else if (get_address(value, INADDR_NONE, BrowsePort, AF_INET, &(temp->to)))
997 {
998 httpAddrString(&(temp->to), line, sizeof(line));
999
1000 #ifdef AF_INET6
1001 if (temp->to.addr.sa_family == AF_INET6)
1002 LogMessage(L_INFO, "Sending browsing info to %s:%d (IPv6)", line,
1003 ntohs(temp->to.ipv6.sin6_port));
1004 else
1005 #endif /* AF_INET6 */
1006 LogMessage(L_INFO, "Sending browsing info to %s:%d", line,
1007 ntohs(temp->to.ipv4.sin_port));
1008
1009 NumBrowsers ++;
1010 }
1011 else
1012 LogMessage(L_ERROR, "Bad BrowseAddress %s at line %d.", value,
1013 linenum);
1014 }
1015 else if (strcasecmp(name, "BrowseOrder") == 0)
1016 {
1017 /*
1018 * "BrowseOrder Deny,Allow" or "BrowseOrder Allow,Deny"...
1019 */
1020
1021 if ((location = FindLocation("CUPS_INTERNAL_BROWSE_ACL")) == NULL)
1022 location = AddLocation("CUPS_INTERNAL_BROWSE_ACL");
1023
1024 if (location == NULL)
1025 LogMessage(L_ERROR, "Unable to initialize browse access control list!");
1026 else if (strncasecmp(value, "deny", 4) == 0)
1027 location->order_type = AUTH_ALLOW;
1028 else if (strncasecmp(value, "allow", 5) == 0)
1029 location->order_type = AUTH_DENY;
1030 else
1031 LogMessage(L_ERROR, "Unknown BrowseOrder value %s on line %d.",
1032 value, linenum);
1033 }
1034 else if (strcasecmp(name, "BrowseProtocols") == 0)
1035 {
1036 /*
1037 * "BrowseProtocol name [... name]"
1038 */
1039
1040 BrowseProtocols = 0;
1041
1042 for (; *value;)
1043 {
1044 for (valuelen = 0; value[valuelen]; valuelen ++)
1045 if (isspace(value[valuelen]) || value[valuelen] == ',')
1046 break;
1047
1048 if (value[valuelen])
1049 {
1050 value[valuelen] = '\0';
1051 valuelen ++;
1052 }
1053
1054 if (strcasecmp(value, "cups") == 0)
1055 BrowseProtocols |= BROWSE_CUPS;
1056 else if (strcasecmp(value, "slp") == 0)
1057 BrowseProtocols |= BROWSE_SLP;
1058 else if (strcasecmp(value, "ldap") == 0)
1059 BrowseProtocols |= BROWSE_LDAP;
1060 else if (strcasecmp(value, "all") == 0)
1061 BrowseProtocols |= BROWSE_ALL;
1062 else
1063 {
1064 LogMessage(L_ERROR, "Unknown browse protocol \"%s\" on line %d.",
1065 value, linenum);
1066 break;
1067 }
1068
1069 for (value += valuelen; *value; value ++)
1070 if (!isspace(*value) || *value != ',')
1071 break;
1072 }
1073 }
1074 else if (strcasecmp(name, "BrowseAllow") == 0 ||
1075 strcasecmp(name, "BrowseDeny") == 0)
1076 {
1077 /*
1078 * BrowseAllow [From] host/ip...
1079 * BrowseDeny [From] host/ip...
1080 */
1081
1082 if ((location = FindLocation("CUPS_INTERNAL_BROWSE_ACL")) == NULL)
1083 location = AddLocation("CUPS_INTERNAL_BROWSE_ACL");
1084
1085 if (location == NULL)
1086 LogMessage(L_ERROR, "Unable to initialize browse access control list!");
1087 else
1088 {
1089 if (strncasecmp(value, "from ", 5) == 0)
1090 {
1091 /*
1092 * Strip leading "from"...
1093 */
1094
1095 value += 5;
1096
1097 while (isspace(*value))
1098 value ++;
1099 }
1100
1101 /*
1102 * Figure out what form the allow/deny address takes:
1103 *
1104 * All
1105 * None
1106 * *.domain.com
1107 * .domain.com
1108 * host.domain.com
1109 * nnn.*
1110 * nnn.nnn.*
1111 * nnn.nnn.nnn.*
1112 * nnn.nnn.nnn.nnn
1113 * nnn.nnn.nnn.nnn/mm
1114 * nnn.nnn.nnn.nnn/mmm.mmm.mmm.mmm
1115 */
1116
1117 if (strcasecmp(value, "all") == 0)
1118 {
1119 /*
1120 * All hosts...
1121 */
1122
1123 if (strcasecmp(name, "BrowseAllow") == 0)
1124 AllowIP(location, zeros, zeros);
1125 else
1126 DenyIP(location, zeros, zeros);
1127 }
1128 else if (strcasecmp(value, "none") == 0)
1129 {
1130 /*
1131 * No hosts...
1132 */
1133
1134 if (strcasecmp(name, "BrowseAllow") == 0)
1135 AllowIP(location, ones, zeros);
1136 else
1137 DenyIP(location, ones, zeros);
1138 }
1139 else if (value[0] == '*' || value[0] == '.' || !isdigit(value[0]))
1140 {
1141 /*
1142 * Host or domain name...
1143 */
1144
1145 if (value[0] == '*')
1146 value ++;
1147
1148 if (strcasecmp(name, "BrowseAllow") == 0)
1149 AllowHost(location, value);
1150 else
1151 DenyHost(location, value);
1152 }
1153 else
1154 {
1155 /*
1156 * One of many IP address forms...
1157 */
1158
1159 if (!get_addr_and_mask(value, ip, mask))
1160 {
1161 LogMessage(L_ERROR, "Bad netmask value %s on line %d.",
1162 value, linenum);
1163 break;
1164 }
1165
1166 if (strcasecmp(name, "BrowseAllow") == 0)
1167 AllowIP(location, ip, mask);
1168 else
1169 DenyIP(location, ip, mask);
1170 }
1171 }
1172 }
1173 else if (strcasecmp(name, "BrowseRelay") == 0)
1174 {
1175 /*
1176 * BrowseRelay [from] source [to] destination
1177 */
1178
1179 if (NumRelays == 0)
1180 relay = malloc(sizeof(dirsvc_relay_t));
1181 else
1182 relay = realloc(Relays, (NumRelays + 1) * sizeof(dirsvc_relay_t));
1183
1184 if (!relay)
1185 {
1186 LogMessage(L_ERROR, "Unable to allocate BrowseRelay at line %d - %s.",
1187 linenum, strerror(errno));
1188 continue;
1189 }
1190
1191 Relays = relay;
1192 relay += NumRelays;
1193
1194 memset(relay, 0, sizeof(dirsvc_relay_t));
1195
1196 if (strncasecmp(value, "from ", 5) == 0)
1197 {
1198 /*
1199 * Strip leading "from"...
1200 */
1201
1202 value += 5;
1203
1204 while (isspace(*value))
1205 value ++;
1206 }
1207
1208 /*
1209 * Figure out what form the from address takes:
1210 *
1211 * *.domain.com
1212 * .domain.com
1213 * host.domain.com
1214 * nnn.*
1215 * nnn.nnn.*
1216 * nnn.nnn.nnn.*
1217 * nnn.nnn.nnn.nnn
1218 * nnn.nnn.nnn.nnn/mm
1219 * nnn.nnn.nnn.nnn/mmm.mmm.mmm.mmm
1220 */
1221
1222 if (value[0] == '*' || value[0] == '.' || !isdigit(value[0]))
1223 {
1224 /*
1225 * Host or domain name...
1226 */
1227
1228 if (value[0] == '*')
1229 value ++;
1230
1231 relay->from.type = AUTH_NAME;
1232 relay->from.mask.name.name = strdup(value);
1233 relay->from.mask.name.length = strlen(value);
1234 }
1235 else
1236 {
1237 /*
1238 * One of many IP address forms...
1239 */
1240
1241 if (!get_addr_and_mask(value, ip, mask))
1242 {
1243 LogMessage(L_ERROR, "Bad netmask value %s on line %d.",
1244 value, linenum);
1245 break;
1246 }
1247
1248 relay->from.type = AUTH_IP;
1249 memcpy(relay->from.mask.ip.address, ip,
1250 sizeof(relay->from.mask.ip.address));
1251 memcpy(relay->from.mask.ip.netmask, mask,
1252 sizeof(relay->from.mask.ip.netmask));
1253 }
1254
1255 /*
1256 * Skip value and trailing whitespace...
1257 */
1258
1259 for (; *value; value ++)
1260 if (isspace(*value))
1261 break;
1262
1263 while (isspace(*value))
1264 value ++;
1265
1266 if (strncasecmp(value, "to ", 3) == 0)
1267 {
1268 /*
1269 * Strip leading "to"...
1270 */
1271
1272 value += 3;
1273
1274 while (isspace(*value))
1275 value ++;
1276 }
1277
1278 /*
1279 * Get "to" address and port...
1280 */
1281
1282 if (get_address(value, INADDR_BROADCAST, BrowsePort, AF_INET, &(relay->to)))
1283 {
1284 httpAddrString(&(relay->to), line, sizeof(line));
1285
1286 if (relay->from.type == AUTH_IP)
1287 snprintf(name, sizeof(name), "%u.%u.%u.%u/%u.%u.%u.%u",
1288 relay->from.mask.ip.address[0],
1289 relay->from.mask.ip.address[1],
1290 relay->from.mask.ip.address[2],
1291 relay->from.mask.ip.address[3],
1292 relay->from.mask.ip.netmask[0],
1293 relay->from.mask.ip.netmask[1],
1294 relay->from.mask.ip.netmask[2],
1295 relay->from.mask.ip.netmask[3]);
1296 else
1297 {
1298 strncpy(name, relay->from.mask.name.name, sizeof(name) - 1);
1299 name[sizeof(name) - 1] = '\0';
1300 }
1301
1302 #ifdef AF_INET6
1303 if (relay->to.addr.sa_family == AF_INET6)
1304 LogMessage(L_INFO, "Relaying from %s to %s:%d", name, line,
1305 ntohs(relay->to.ipv6.sin6_port));
1306 else
1307 #endif /* AF_INET6 */
1308 LogMessage(L_INFO, "Relaying from %s to %s:%d", name, line,
1309 ntohs(relay->to.ipv4.sin_port));
1310
1311 NumRelays ++;
1312 }
1313 else
1314 {
1315 if (relay->from.type == AUTH_NAME)
1316 free(relay->from.mask.name.name);
1317
1318 LogMessage(L_ERROR, "Bad relay address %s at line %d.", value, linenum);
1319 }
1320 }
1321 else if (strcasecmp(name, "BrowsePoll") == 0)
1322 {
1323 /*
1324 * BrowsePoll address[:port]
1325 */
1326
1327 if (NumPolled == 0)
1328 poll = malloc(sizeof(dirsvc_poll_t));
1329 else
1330 poll = realloc(Polled, (NumPolled + 1) * sizeof(dirsvc_poll_t));
1331
1332 if (!poll)
1333 {
1334 LogMessage(L_ERROR, "Unable to allocate BrowsePoll at line %d - %s.",
1335 linenum, strerror(errno));
1336 continue;
1337 }
1338
1339 Polled = poll;
1340 poll += NumPolled;
1341
1342 /*
1343 * Get poll address and port...
1344 */
1345
1346 if (get_address(value, INADDR_NONE, ippPort(), AF_INET, &polladdr))
1347 {
1348 NumPolled ++;
1349 memset(poll, 0, sizeof(dirsvc_poll_t));
1350
1351 httpAddrString(&polladdr, poll->hostname, sizeof(poll->hostname));
1352
1353 #ifdef AF_INET6
1354 if (polladdr.addr.sa_family == AF_INET6)
1355 poll->port = ntohs(polladdr.ipv6.sin6_port);
1356 else
1357 #endif /* AF_INET6 */
1358 poll->port = ntohs(polladdr.ipv4.sin_port);
1359
1360 LogMessage(L_INFO, "Polling %s:%d", poll->hostname, poll->port);
1361 }
1362 else
1363 LogMessage(L_ERROR, "Bad poll address %s at line %d.", value, linenum);
1364 }
1365 else if (strcasecmp(name, "User") == 0)
1366 {
1367 /*
1368 * User ID to run as...
1369 */
1370
1371 if (isdigit(value[0]))
1372 User = atoi(value);
1373 else
1374 {
1375 struct passwd *p; /* Password information */
1376
1377 endpwent();
1378 p = getpwnam(value);
1379
1380 if (p != NULL)
1381 User = p->pw_uid;
1382 else
1383 LogMessage(L_WARN, "ReadConfiguration() Unknown username \"%s\"",
1384 value);
1385 }
1386 }
1387 else if (strcasecmp(name, "Group") == 0)
1388 {
1389 /*
1390 * Group ID to run as...
1391 */
1392
1393 if (isdigit(value[0]))
1394 Group = atoi(value);
1395 else
1396 {
1397 struct group *g; /* Group information */
1398
1399 endgrent();
1400 g = getgrnam(value);
1401
1402 if (g != NULL)
1403 Group = g->gr_gid;
1404 else
1405 LogMessage(L_WARN, "ReadConfiguration() Unknown groupname \"%s\"",
1406 value);
1407 }
1408 }
1409 else if (strcasecmp(name, "SystemGroup") == 0)
1410 {
1411 /*
1412 * System (admin) group(s)...
1413 */
1414
1415 char *valueptr; /* Pointer into value */
1416
1417
1418 for (i = NumSystemGroups; *value && i < MAX_SYSTEM_GROUPS; i ++)
1419 {
1420 for (valueptr = value; *valueptr; valueptr ++)
1421 if (isspace(*valueptr) || *valueptr == ',')
1422 break;
1423
1424 if (*valueptr)
1425 *valueptr++ = '\0';
1426
1427 SetString(SystemGroups + i, value);
1428
1429 value = valueptr;
1430
1431 while (*value == ',' || isspace(*value))
1432 value ++;
1433 }
1434
1435 if (i)
1436 NumSystemGroups = i;
1437 }
1438 else if (strcasecmp(name, "HostNameLookups") == 0)
1439 {
1440 /*
1441 * Do hostname lookups?
1442 */
1443
1444 if (strcasecmp(value, "off") == 0)
1445 HostNameLookups = 0;
1446 else if (strcasecmp(value, "on") == 0)
1447 HostNameLookups = 1;
1448 else if (strcasecmp(value, "double") == 0)
1449 HostNameLookups = 2;
1450 else
1451 LogMessage(L_WARN, "ReadConfiguration() Unknown HostNameLookups %s on line %d.",
1452 value, linenum);
1453 }
1454 else if (strcasecmp(name, "LogLevel") == 0)
1455 {
1456 /*
1457 * Amount of logging to do...
1458 */
1459
1460 if (strcasecmp(value, "debug2") == 0)
1461 LogLevel = L_DEBUG2;
1462 else if (strcasecmp(value, "debug") == 0)
1463 LogLevel = L_DEBUG;
1464 else if (strcasecmp(value, "info") == 0)
1465 LogLevel = L_INFO;
1466 else if (strcasecmp(value, "notice") == 0)
1467 LogLevel = L_NOTICE;
1468 else if (strcasecmp(value, "warn") == 0)
1469 LogLevel = L_WARN;
1470 else if (strcasecmp(value, "error") == 0)
1471 LogLevel = L_ERROR;
1472 else if (strcasecmp(value, "crit") == 0)
1473 LogLevel = L_CRIT;
1474 else if (strcasecmp(value, "alert") == 0)
1475 LogLevel = L_ALERT;
1476 else if (strcasecmp(value, "emerg") == 0)
1477 LogLevel = L_EMERG;
1478 else if (strcasecmp(value, "none") == 0)
1479 LogLevel = L_NONE;
1480 else
1481 LogMessage(L_WARN, "Unknown LogLevel %s on line %d.", value, linenum);
1482 }
1483 else if (strcasecmp(name, "PrintcapFormat") == 0)
1484 {
1485 /*
1486 * Format of printcap file?
1487 */
1488
1489 if (strcasecmp(value, "bsd") == 0)
1490 PrintcapFormat = PRINTCAP_BSD;
1491 else if (strcasecmp(value, "solaris") == 0)
1492 PrintcapFormat = PRINTCAP_SOLARIS;
1493 else
1494 LogMessage(L_WARN, "ReadConfiguration() Unknown PrintcapFormat %s on line %d.",
1495 value, linenum);
1496 }
1497 else
1498 {
1499 /*
1500 * Find a simple variable in the list...
1501 */
1502
1503 for (i = NUM_VARS, var = variables; i > 0; i --, var ++)
1504 if (strcasecmp(name, var->name) == 0)
1505 break;
1506
1507 if (i == 0)
1508 {
1509 /*
1510 * Unknown directive! Output an error message and continue...
1511 */
1512
1513 LogMessage(L_ERROR, "Unknown directive %s on line %d.", name,
1514 linenum);
1515 continue;
1516 }
1517
1518 switch (var->type)
1519 {
1520 case VAR_INTEGER :
1521 {
1522 int n; /* Number */
1523 char *units; /* Units */
1524
1525
1526 n = strtol(value, &units, 0);
1527
1528 if (units && *units)
1529 {
1530 if (tolower(units[0]) == 'g')
1531 n *= 1024 * 1024 * 1024;
1532 else if (tolower(units[0]) == 'm')
1533 n *= 1024 * 1024;
1534 else if (tolower(units[0]) == 'k')
1535 n *= 1024;
1536 else if (tolower(units[0]) == 't')
1537 n *= 262144;
1538 }
1539
1540 *((int *)var->ptr) = n;
1541 }
1542 break;
1543
1544 case VAR_BOOLEAN :
1545 if (strcasecmp(value, "true") == 0 ||
1546 strcasecmp(value, "on") == 0 ||
1547 strcasecmp(value, "enabled") == 0 ||
1548 strcasecmp(value, "yes") == 0 ||
1549 atoi(value) != 0)
1550 *((int *)var->ptr) = TRUE;
1551 else if (strcasecmp(value, "false") == 0 ||
1552 strcasecmp(value, "off") == 0 ||
1553 strcasecmp(value, "disabled") == 0 ||
1554 strcasecmp(value, "no") == 0 ||
1555 strcasecmp(value, "0") == 0)
1556 *((int *)var->ptr) = FALSE;
1557 else
1558 LogMessage(L_ERROR, "Unknown boolean value %s on line %d.",
1559 value, linenum);
1560 break;
1561
1562 case VAR_STRING :
1563 SetString((char **)var->ptr, value);
1564 break;
1565 }
1566 }
1567 }
1568
1569 return (1);
1570 }
1571
1572
1573 /*
1574 * 'read_location()' - Read a <Location path> definition.
1575 */
1576
1577 static int /* O - New line number or 0 on error */
1578 read_location(cups_file_t *fp, /* I - Configuration file */
1579 char *location, /* I - Location name/path */
1580 int linenum) /* I - Current line number */
1581 {
1582 int i; /* Looping var */
1583 location_t *loc, /* New location */
1584 *parent; /* Parent location */
1585 int len; /* Length of line */
1586 char line[HTTP_MAX_BUFFER], /* Line buffer */
1587 name[256], /* Configuration directive */
1588 *nameptr, /* Pointer into name */
1589 *value, /* Value for directive */
1590 *valptr; /* Pointer into value */
1591 unsigned ip[4], /* IP address components */
1592 mask[4]; /* IP netmask components */
1593
1594
1595 if ((parent = AddLocation(location)) == NULL)
1596 return (0);
1597
1598 parent->limit = AUTH_LIMIT_ALL;
1599 loc = parent;
1600
1601 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
1602 {
1603 linenum ++;
1604
1605 /*
1606 * Skip comment lines...
1607 */
1608
1609 if (line[0] == '#')
1610 continue;
1611
1612 /*
1613 * Strip trailing whitespace, if any...
1614 */
1615
1616 len = strlen(line);
1617
1618 while (len > 0 && isspace(line[len - 1]))
1619 {
1620 len --;
1621 line[len] = '\0';
1622 }
1623
1624 /*
1625 * Extract the name from the beginning of the line...
1626 */
1627
1628 for (value = line; isspace(*value); value ++);
1629
1630 for (nameptr = name; *value != '\0' && !isspace(*value) &&
1631 nameptr < (name + sizeof(name) - 1);)
1632 *nameptr++ = *value++;
1633 *nameptr = '\0';
1634
1635 while (isspace(*value))
1636 value ++;
1637
1638 if (name[0] == '\0')
1639 continue;
1640
1641 /*
1642 * Decode the directive...
1643 */
1644
1645 if (strcasecmp(name, "</Location>") == 0)
1646 return (linenum);
1647 else if (strcasecmp(name, "<Limit") == 0 ||
1648 strcasecmp(name, "<LimitExcept") == 0)
1649 {
1650 if ((loc = CopyLocation(&parent)) == NULL)
1651 return (0);
1652
1653 loc->limit = 0;
1654 while (*value)
1655 {
1656 for (valptr = value;
1657 !isspace(*valptr) && *valptr != '>' && *valptr;
1658 valptr ++);
1659
1660 if (*valptr)
1661 *valptr++ = '\0';
1662
1663 if (strcmp(value, "ALL") == 0)
1664 loc->limit = AUTH_LIMIT_ALL;
1665 else if (strcmp(value, "GET") == 0)
1666 loc->limit |= AUTH_LIMIT_GET;
1667 else if (strcmp(value, "HEAD") == 0)
1668 loc->limit |= AUTH_LIMIT_HEAD;
1669 else if (strcmp(value, "OPTIONS") == 0)
1670 loc->limit |= AUTH_LIMIT_OPTIONS;
1671 else if (strcmp(value, "POST") == 0)
1672 loc->limit |= AUTH_LIMIT_POST;
1673 else if (strcmp(value, "PUT") == 0)
1674 loc->limit |= AUTH_LIMIT_PUT;
1675 else if (strcmp(value, "TRACE") == 0)
1676 loc->limit |= AUTH_LIMIT_TRACE;
1677 else
1678 LogMessage(L_WARN, "Unknown request type %s on line %d!", value,
1679 linenum);
1680
1681 for (value = valptr; isspace(*value) || *value == '>'; value ++);
1682 }
1683
1684 if (strcasecmp(name, "<LimitExcept") == 0)
1685 loc->limit = AUTH_LIMIT_ALL ^ loc->limit;
1686
1687 parent->limit &= ~loc->limit;
1688 }
1689 else if (strcasecmp(name, "</Limit>") == 0)
1690 loc = parent;
1691 else if (strcasecmp(name, "Encryption") == 0)
1692 {
1693 /*
1694 * "Encryption xxx" - set required encryption level...
1695 */
1696
1697 if (strcasecmp(value, "never") == 0)
1698 loc->encryption = HTTP_ENCRYPT_NEVER;
1699 else if (strcasecmp(value, "always") == 0)
1700 {
1701 LogMessage(L_ERROR, "Encryption value \"%s\" on line %d is invalid in this context. "
1702 "Using \"required\" instead.", value, linenum);
1703
1704 loc->encryption = HTTP_ENCRYPT_REQUIRED;
1705 }
1706 else if (strcasecmp(value, "required") == 0)
1707 loc->encryption = HTTP_ENCRYPT_REQUIRED;
1708 else if (strcasecmp(value, "ifrequested") == 0)
1709 loc->encryption = HTTP_ENCRYPT_IF_REQUESTED;
1710 else
1711 LogMessage(L_ERROR, "Unknown Encryption value %s on line %d.",
1712 value, linenum);
1713 }
1714 else if (strcasecmp(name, "Order") == 0)
1715 {
1716 /*
1717 * "Order Deny,Allow" or "Order Allow,Deny"...
1718 */
1719
1720 if (strncasecmp(value, "deny", 4) == 0)
1721 loc->order_type = AUTH_ALLOW;
1722 else if (strncasecmp(value, "allow", 5) == 0)
1723 loc->order_type = AUTH_DENY;
1724 else
1725 LogMessage(L_ERROR, "Unknown Order value %s on line %d.",
1726 value, linenum);
1727 }
1728 else if (strcasecmp(name, "Allow") == 0 ||
1729 strcasecmp(name, "Deny") == 0)
1730 {
1731 /*
1732 * Allow [From] host/ip...
1733 * Deny [From] host/ip...
1734 */
1735
1736 if (strncasecmp(value, "from", 4) == 0)
1737 {
1738 /*
1739 * Strip leading "from"...
1740 */
1741
1742 value += 4;
1743
1744 while (isspace(*value))
1745 value ++;
1746 }
1747
1748 /*
1749 * Figure out what form the allow/deny address takes:
1750 *
1751 * All
1752 * None
1753 * *.domain.com
1754 * .domain.com
1755 * host.domain.com
1756 * nnn.*
1757 * nnn.nnn.*
1758 * nnn.nnn.nnn.*
1759 * nnn.nnn.nnn.nnn
1760 * nnn.nnn.nnn.nnn/mm
1761 * nnn.nnn.nnn.nnn/mmm.mmm.mmm.mmm
1762 */
1763
1764 if (strcasecmp(value, "all") == 0)
1765 {
1766 /*
1767 * All hosts...
1768 */
1769
1770 if (strcasecmp(name, "Allow") == 0)
1771 AllowIP(loc, zeros, zeros);
1772 else
1773 DenyIP(loc, zeros, zeros);
1774 }
1775 else if (strcasecmp(value, "none") == 0)
1776 {
1777 /*
1778 * No hosts...
1779 */
1780
1781 if (strcasecmp(name, "Allow") == 0)
1782 AllowIP(loc, ones, zeros);
1783 else
1784 DenyIP(loc, ones, zeros);
1785 }
1786 else if (value[0] == '*' || value[0] == '.' || !isdigit(value[0]))
1787 {
1788 /*
1789 * Host or domain name...
1790 */
1791
1792 if (value[0] == '*')
1793 value ++;
1794
1795 if (strcasecmp(name, "Allow") == 0)
1796 AllowHost(loc, value);
1797 else
1798 DenyHost(loc, value);
1799 }
1800 else
1801 {
1802 /*
1803 * One of many IP address forms...
1804 */
1805
1806 if (!get_addr_and_mask(value, ip, mask))
1807 {
1808 LogMessage(L_ERROR, "Bad netmask value %s on line %d.",
1809 value, linenum);
1810 break;
1811 }
1812
1813 if (strcasecmp(name, "Allow") == 0)
1814 AllowIP(loc, ip, mask);
1815 else
1816 DenyIP(loc, ip, mask);
1817 }
1818 }
1819 else if (strcasecmp(name, "AuthType") == 0)
1820 {
1821 /*
1822 * AuthType {none,basic,digest,basicdigest}
1823 */
1824
1825 if (strcasecmp(value, "none") == 0)
1826 {
1827 loc->type = AUTH_NONE;
1828 loc->level = AUTH_ANON;
1829 }
1830 else if (strcasecmp(value, "basic") == 0)
1831 {
1832 loc->type = AUTH_BASIC;
1833
1834 if (loc->level == AUTH_ANON)
1835 loc->level = AUTH_USER;
1836 }
1837 else if (strcasecmp(value, "digest") == 0)
1838 {
1839 loc->type = AUTH_DIGEST;
1840
1841 if (loc->level == AUTH_ANON)
1842 loc->level = AUTH_USER;
1843 }
1844 else if (strcasecmp(value, "basicdigest") == 0)
1845 {
1846 loc->type = AUTH_BASICDIGEST;
1847
1848 if (loc->level == AUTH_ANON)
1849 loc->level = AUTH_USER;
1850 }
1851 else
1852 LogMessage(L_WARN, "Unknown authorization type %s on line %d.",
1853 value, linenum);
1854 }
1855 else if (strcasecmp(name, "AuthClass") == 0)
1856 {
1857 /*
1858 * AuthClass anonymous, user, system, group
1859 */
1860
1861 if (strcasecmp(value, "anonymous") == 0)
1862 {
1863 loc->type = AUTH_NONE;
1864 loc->level = AUTH_ANON;
1865 }
1866 else if (strcasecmp(value, "user") == 0)
1867 loc->level = AUTH_USER;
1868 else if (strcasecmp(value, "group") == 0)
1869 loc->level = AUTH_GROUP;
1870 else if (strcasecmp(value, "system") == 0)
1871 {
1872 loc->level = AUTH_GROUP;
1873
1874 /*
1875 * Use the default system group if none is defined so far...
1876 */
1877
1878 if (NumSystemGroups == 0)
1879 NumSystemGroups = 1;
1880
1881 for (i = 0; i < NumSystemGroups; i ++)
1882 AddName(loc, SystemGroups[i]);
1883 }
1884 else
1885 LogMessage(L_WARN, "Unknown authorization class %s on line %d.",
1886 value, linenum);
1887 }
1888 else if (strcasecmp(name, "AuthGroupName") == 0)
1889 AddName(loc, value);
1890 else if (strcasecmp(name, "Require") == 0)
1891 {
1892 /*
1893 * Apache synonym for AuthClass and AuthGroupName...
1894 *
1895 * Get initial word:
1896 *
1897 * Require valid-user
1898 * Require group names
1899 * Require user names
1900 */
1901
1902 for (valptr = value;
1903 !isspace(*valptr) && *valptr != '>' && *valptr;
1904 valptr ++);
1905
1906 if (*valptr)
1907 *valptr++ = '\0';
1908
1909 if (strcasecmp(value, "valid-user") == 0 ||
1910 strcasecmp(value, "user") == 0)
1911 loc->level = AUTH_USER;
1912 else if (strcasecmp(value, "group") == 0)
1913 loc->level = AUTH_GROUP;
1914 else
1915 {
1916 LogMessage(L_WARN, "Unknown Require type %s on line %d.",
1917 value, linenum);
1918 continue;
1919 }
1920
1921 /*
1922 * Get the list of names from the line...
1923 */
1924
1925 for (value = valptr; *value;)
1926 {
1927 for (valptr = value; !isspace(*valptr) && *valptr; valptr ++);
1928
1929 if (*valptr)
1930 *valptr++ = '\0';
1931
1932 AddName(loc, value);
1933
1934 for (value = valptr; isspace(*value); value ++);
1935 }
1936 }
1937 else if (strcasecmp(name, "Satisfy") == 0)
1938 {
1939 if (strcasecmp(value, "all") == 0)
1940 loc->satisfy = AUTH_SATISFY_ALL;
1941 else if (strcasecmp(value, "any") == 0)
1942 loc->satisfy = AUTH_SATISFY_ANY;
1943 else
1944 LogMessage(L_WARN, "Unknown Satisfy value %s on line %d.", value,
1945 linenum);
1946 }
1947 else
1948 LogMessage(L_ERROR, "Unknown Location directive %s on line %d.",
1949 name, linenum);
1950 }
1951
1952 return (0);
1953 }
1954
1955
1956 /*
1957 * 'get_address()' - Get an address + port number from a line.
1958 */
1959
1960 static int /* O - 1 if address good, 0 if bad */
1961 get_address(const char *value, /* I - Value string */
1962 unsigned defaddress, /* I - Default address */
1963 int defport, /* I - Default port */
1964 int deffamily, /* I - Default family */
1965 http_addr_t *address) /* O - Socket address */
1966 {
1967 char hostname[256], /* Hostname or IP */
1968 portname[256]; /* Port number or name */
1969 struct hostent *host; /* Host address */
1970 struct servent *port; /* Port number */
1971
1972
1973 /*
1974 * Initialize the socket address to the defaults...
1975 */
1976
1977 memset(address, 0, sizeof(http_addr_t));
1978
1979 #ifdef AF_INET6
1980 if (deffamily == AF_INET6)
1981 {
1982 address->ipv6.sin6_family = AF_INET6;
1983 address->ipv6.sin6_addr.s6_addr32[0] = htonl(defaddress);
1984 address->ipv6.sin6_addr.s6_addr32[1] = htonl(defaddress);
1985 address->ipv6.sin6_addr.s6_addr32[2] = htonl(defaddress);
1986 address->ipv6.sin6_addr.s6_addr32[3] = htonl(defaddress);
1987 address->ipv6.sin6_port = htons(defport);
1988 }
1989 else
1990 #endif /* AF_INET6 */
1991 {
1992 address->ipv4.sin_family = AF_INET;
1993 address->ipv4.sin_addr.s_addr = htonl(defaddress);
1994 address->ipv4.sin_port = htons(defport);
1995 }
1996
1997 /*
1998 * Try to grab a hostname and port number...
1999 */
2000
2001 switch (sscanf(value, "%255[^:]:%255s", hostname, portname))
2002 {
2003 case 1 :
2004 if (strchr(hostname, '.') == NULL && defaddress == INADDR_ANY)
2005 {
2006 /*
2007 * Hostname is a port number...
2008 */
2009
2010 strlcpy(portname, hostname, sizeof(portname));
2011 hostname[0] = '\0';
2012 }
2013 else
2014 portname[0] = '\0';
2015 break;
2016
2017 case 2 :
2018 break;
2019
2020 default :
2021 LogMessage(L_ERROR, "Unable to decode address \"%s\"!", value);
2022 return (0);
2023 }
2024
2025 /*
2026 * Decode the hostname and port number as needed...
2027 */
2028
2029 if (hostname[0] && strcmp(hostname, "*") != 0)
2030 {
2031 if ((host = httpGetHostByName(hostname)) == NULL)
2032 {
2033 LogMessage(L_ERROR, "httpGetHostByName(\"%s\") failed - %s!", hostname,
2034 hstrerror(h_errno));
2035 return (0);
2036 }
2037
2038 httpAddrLoad(host, defport, 0, address);
2039 }
2040
2041 if (portname[0] != '\0')
2042 {
2043 if (isdigit(portname[0]))
2044 {
2045 #ifdef AF_INET6
2046 if (address->addr.sa_family == AF_INET6)
2047 address->ipv6.sin6_port = htons(atoi(portname));
2048 else
2049 #endif /* AF_INET6 */
2050 address->ipv4.sin_port = htons(atoi(portname));
2051 }
2052 else
2053 {
2054 if ((port = getservbyname(portname, NULL)) == NULL)
2055 {
2056 LogMessage(L_ERROR, "getservbyname(\"%s\") failed - %s!", portname,
2057 strerror(errno));
2058 return (0);
2059 }
2060 else
2061 {
2062 #ifdef AF_INET6
2063 if (address->addr.sa_family == AF_INET6)
2064 address->ipv6.sin6_port = htons(port->s_port);
2065 else
2066 #endif /* AF_INET6 */
2067 address->ipv4.sin_port = htons(port->s_port);
2068 }
2069 }
2070 }
2071
2072 return (1);
2073 }
2074
2075
2076 /*
2077 * 'get_addr_and_mask()' - Get an IP address and netmask.
2078 */
2079
2080 static int /* O - 1 on success, 0 on failure */
2081 get_addr_and_mask(const char *value, /* I - String from config file */
2082 unsigned *ip, /* O - Address value */
2083 unsigned *mask) /* O - Mask value */
2084 {
2085 int i, /* Looping var */
2086 family, /* Address family */
2087 ipcount; /* Count of fields in address */
2088 static unsigned netmasks[4][4] = /* Standard netmasks... */
2089 {
2090 { 0xffffffff, 0x00000000, 0x00000000, 0x00000000 },
2091 { 0xffffffff, 0xffffffff, 0x00000000, 0x00000000 },
2092 { 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000 },
2093 { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
2094 };
2095
2096
2097 /*
2098 * Get the address...
2099 */
2100
2101 memset(ip, 0, sizeof(unsigned) * 4);
2102 family = AF_INET;
2103 ipcount = sscanf(value, "%u.%u.%u.%u", ip + 0, ip + 1, ip + 2, ip + 3);
2104
2105 #ifdef AF_INET6
2106 /*
2107 * See if we have any values > 255; if so, this is an IPv6 address only.
2108 */
2109
2110 for (i = 0; i < ipcount; i ++)
2111 if (ip[0] > 255)
2112 {
2113 family = AF_INET6;
2114 break;
2115 }
2116 #endif /* AF_INET6 */
2117
2118 if ((value = strchr(value, '/')) != NULL)
2119 {
2120 /*
2121 * Get the netmask value(s)...
2122 */
2123
2124 value ++;
2125 memset(mask, 0, sizeof(unsigned) * 4);
2126 switch (sscanf(value, "%u.%u.%u.%u", mask + 0, mask + 1,
2127 mask + 2, mask + 3))
2128 {
2129 case 1 :
2130 #ifdef AF_INET6
2131 if (mask[0] >= 32)
2132 family = AF_INET6;
2133
2134 if (family == AF_INET6)
2135 {
2136 i = 128 - mask[0];
2137
2138 if (i <= 96)
2139 mask[0] = 0xffffffff;
2140 else
2141 mask[0] = (0xffffffff << (128 - mask[0])) & 0xffffffff;
2142
2143 if (i <= 64)
2144 mask[1] = 0xffffffff;
2145 else if (i >= 96)
2146 mask[1] = 0;
2147 else
2148 mask[1] = (0xffffffff << (96 - mask[0])) & 0xffffffff;
2149
2150 if (i <= 32)
2151 mask[1] = 0xffffffff;
2152 else if (i >= 64)
2153 mask[1] = 0;
2154 else
2155 mask[1] = (0xffffffff << (64 - mask[0])) & 0xffffffff;
2156
2157 if (i >= 32)
2158 mask[1] = 0;
2159 else
2160 mask[1] = (0xffffffff << (32 - mask[0])) & 0xffffffff;
2161 }
2162 else
2163 #endif /* AF_INET6 */
2164 {
2165 i = 32 - mask[0];
2166
2167 if (i <= 24)
2168 mask[0] = 0xffffffff;
2169 else
2170 mask[0] = (0xffffffff << (32 - mask[0])) & 0xffffffff;
2171
2172 if (i <= 16)
2173 mask[1] = 0xffffffff;
2174 else if (i >= 24)
2175 mask[1] = 0;
2176 else
2177 mask[1] = (0xffffffff << (24 - mask[0])) & 0xffffffff;
2178
2179 if (i <= 8)
2180 mask[1] = 0xffffffff;
2181 else if (i >= 16)
2182 mask[1] = 0;
2183 else
2184 mask[1] = (0xffffffff << (16 - mask[0])) & 0xffffffff;
2185
2186 if (i >= 8)
2187 mask[1] = 0;
2188 else
2189 mask[1] = (0xffffffff << (8 - mask[0])) & 0xffffffff;
2190 }
2191
2192 case 4 :
2193 break;
2194
2195 default :
2196 return (0);
2197 }
2198 }
2199 else
2200 memcpy(mask, netmasks[ipcount - 1], sizeof(unsigned) * 4);
2201
2202 return (1);
2203 }
2204
2205
2206 #ifdef HAVE_CDSASSL
2207 /*
2208 * 'CDSAGetServerCerts()' - Convert a keychain name into the CFArrayRef
2209 * required by SSLSetCertificate.
2210 *
2211 * For now we assumes that there is exactly one SecIdentity in the
2212 * keychain - i.e. there is exactly one matching cert/private key pair.
2213 * In the future we will search a keychain for a SecIdentity matching a
2214 * specific criteria. We also skip the operation of adding additional
2215 * non-signing certs from the keychain to the CFArrayRef.
2216 *
2217 * To create a self-signed certificate for testing use the certtool.
2218 * Executing the following as root will do it:
2219 *
2220 * certtool c c v k=CUPS
2221 */
2222
2223 static CFArrayRef
2224 CDSAGetServerCerts(void)
2225 {
2226 OSStatus err; /* Error info */
2227 SecKeychainRef kcRef; /* Keychain reference */
2228 SecIdentitySearchRef srchRef; /* Search reference */
2229 SecIdentityRef identity; /* Identity */
2230 CFArrayRef ca; /* Certificate array */
2231
2232
2233 kcRef = NULL;
2234 srchRef = NULL;
2235 identity = NULL;
2236 ca = NULL;
2237 err = SecKeychainOpen(ServerCertificate, &kcRef);
2238
2239 if (err)
2240 LogMessage(L_ERROR, "Cannot open keychain \"%s\", error %d.",
2241 ServerCertificate, err);
2242 else
2243 {
2244 /*
2245 * Search for "any" identity matching specified key use;
2246 * in this app, we expect there to be exactly one.
2247 */
2248
2249 err = SecIdentitySearchCreate(kcRef, CSSM_KEYUSE_SIGN, &srchRef);
2250
2251 if (err)
2252 LogMessage(L_ERROR,
2253 "Cannot find signing key in keychain \"%s\", error %d",
2254 ServerCertificate, err);
2255 else
2256 {
2257 err = SecIdentitySearchCopyNext(srchRef, &identity);
2258
2259 if (err)
2260 LogMessage(L_ERROR,
2261 "Cannot find signing key in keychain \"%s\", error %d",
2262 ServerCertificate, err);
2263 else
2264 {
2265 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
2266 LogMessage(L_ERROR, "SecIdentitySearchCopyNext CFTypeID failure!");
2267 else
2268 {
2269 /*
2270 * Found one. Place it in a CFArray.
2271 * TBD: snag other (non-identity) certs from keychain and add them
2272 * to array as well.
2273 */
2274
2275 ca = CFArrayCreate(NULL, (const void **)&identity, 1, NULL);
2276
2277 if (ca == nil)
2278 LogMessage(L_ERROR, "CFArrayCreate error");
2279 }
2280
2281 /*CFRelease(identity);*/
2282 }
2283
2284 /*CFRelease(srchRef);*/
2285 }
2286
2287 /*CFRelease(kcRef);*/
2288 }
2289
2290 return ca;
2291 }
2292 #endif /* HAVE_CDSASSL */
2293
2294
2295 /*
2296 * End of "$Id: conf.c,v 1.77.2.40 2003/04/23 18:55:28 mike Exp $".
2297 */