/*
- * "$Id: dirsvc.c 6691 2007-07-19 19:09:46Z mike $"
+ * "$Id: dirsvc.c 7003 2007-10-01 23:10:13Z mike $"
*
* Directory services routines for the Common UNIX Printing System (CUPS).
*
- * Copyright 2007 by Apple Inc.
+ * Copyright 2007-2008 by Apple Inc.
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
*
* Contents:
*
- * cupsdDeregisterPrinter() - Stop sending broadcast information for a
- * local printer and remove any pending
- * references to remote printers.
- * cupsdLoadRemoteCache() - Load the remote printer cache.
- * cupsdRegisterPrinter() - Start sending broadcast information for a
- * printer update the broadcast contents.
- * cupsdSaveRemoteCache() - Save the remote printer cache.
- * cupsdSendBrowseList() - Send new browsing information as necessary.
- * cupsdStartBrowsing() - Start sending and receiving broadcast
- * information.
- * cupsdStartPolling() - Start polling servers as needed.
- * cupsdStopBrowsing() - Stop sending and receiving broadcast
- * information.
- * cupsdStopPolling() - Stop polling servers as needed.
- * cupsdUpdateDNSSDBrowse() - Handle DNS-SD queries.
- * cupsdUpdateLDAPBrowse() - Scan for new printers via LDAP...
- * cupsdUpdateSLPBrowse() - Get browsing information via SLP.
- * dnssdBuildTxtRecord() - Build a TXT record from printer info.
- * dnssdDeregisterPrinter() - Stop sending broadcast information for a
- * printer.
- * dnssdPackTxtRecord() - Pack an array of key/value pairs into the
- * TXT record format.
- * dnssdRegisterCallback() - DNSServiceRegister callback.
- * dnssdRegisterPrinter() - Start sending broadcast information for a
- * printer or update the broadcast contents.
- * dequote() - Remote quotes from a string.
- * process_browse_data() - Process new browse data.
- * process_implicit_classes() - Create/update implicit classes as needed.
- * send_cups_browse() - Send new browsing information using the
- * CUPS protocol.
- * send_ldap_browse() - Send LDAP printer registrations.
- * send_slp_browse() - Register the specified printer with SLP.
- * slp_attr_callback() - SLP attribute callback
- * slp_dereg_printer() - SLPDereg() the specified printer
- * slp_get_attr() - Get an attribute from an SLP registration.
- * slp_reg_callback() - Empty SLPRegReport.
- * slp_url_callback() - SLP service url callback
- * update_cups_browse() - Update the browse lists using the CUPS
- * protocol.
- * update_polling() - Read status messages from the poll daemons.
+ * cupsdDeregisterPrinter() - Stop sending broadcast information for a local
+ * printer and remove any pending references to
+ * remote printers.
+ * cupsdLoadRemoteCache() - Load the remote printer cache.
+ * cupsdRegisterPrinter() - Start sending broadcast information for a
+ * printer or update the broadcast contents.
+ * cupsdRestartPolling() - Restart polling servers as needed.
+ * cupsdSaveRemoteCache() - Save the remote printer cache.
+ * cupsdSendBrowseList() - Send new browsing information as necessary.
+ * cupsdStartBrowsing() - Start sending and receiving broadcast
+ * information.
+ * cupsdStartPolling() - Start polling servers as needed.
+ * cupsdStopBrowsing() - Stop sending and receiving broadcast
+ * information.
+ * cupsdStopPolling() - Stop polling servers as needed.
+ * cupsdUpdateDNSSDBrowse() - Handle DNS-SD queries.
+ * cupsdUpdateLDAPBrowse() - Scan for new printers via LDAP...
+ * cupsdUpdateSLPBrowse() - Get browsing information via SLP.
+ * dequote() - Remote quotes from a string.
+ * is_local_queue() - Determine whether the URI points at a local
+ * queue.
+ * process_browse_data() - Process new browse data.
+ * dnssdBuildTxtRecord() - Build a TXT record from printer info.
+ * dnssdDeregisterPrinter() - Stop sending broadcast information for a
+ * printer.
+ * dnssdPackTxtRecord() - Pack an array of key/value pairs into the TXT
+ * record format.
+ * dnssdRegisterCallback() - DNSServiceRegister callback.
+ * dnssdRegisterPrinter() - Start sending broadcast information for a
+ * printer or update the broadcast contents.
+ * process_implicit_classes() - Create/update implicit classes as needed.
+ * send_cups_browse() - Send new browsing information using the CUPS
+ * protocol.
+ * send_ldap_browse() - Send LDAP printer registrations.
+ * send_slp_browse() - Register the specified printer with SLP.
+ * slp_attr_callback() - SLP attribute callback
+ * slp_dereg_printer() - SLPDereg() the specified printer
+ * slp_get_attr() - Get an attribute from an SLP registration.
+ * slp_reg_callback() - Empty SLPRegReport.
+ * slp_url_callback() - SLP service url callback
+ * update_cups_browse() - Update the browse lists using the CUPS
+ * protocol.
+ * update_lpd() - Update the LPD configuration as needed.
+ * update_polling() - Read status messages from the poll daemons.
+ * update_smb() - Update the SMB configuration as needed.
*/
/*
ipp_pstate_t state, const char *location,
const char *info, const char *make_model,
int num_attrs, cups_option_t *attrs);
-static void process_implicit_classes(void);
+static void process_implicit_classes(int *write_printcap);
static void send_cups_browse(cupsd_printer_t *p);
#ifdef HAVE_LDAP
static void send_ldap_browse(cupsd_printer_t *p);
static void send_slp_browse(cupsd_printer_t *p);
#endif /* HAVE_LIBSLP */
static void update_cups_browse(void);
+static void update_lpd(int onoff);
static void update_polling(void);
+static void update_smb(int onoff);
#ifdef HAVE_OPENLDAP
* Announce the deletion...
*/
- if ((BrowseLocalProtocols & BROWSE_CUPS))
+ if ((BrowseLocalProtocols & BROWSE_CUPS) && BrowseSocket >= 0)
{
cups_ptype_t savedtype = p->type; /* Saved printer type */
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of remote.cache.", linenum);
- return;
+ break;
}
}
else if (!strcasecmp(line, "<Class") ||
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of remote.cache.", linenum);
- return;
+ break;
}
}
else if (!strcasecmp(line, "</Printer>") ||
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of remote.cache.", linenum);
- return;
+ break;
}
}
else if (!p)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of remote.cache.", linenum);
- return;
+ break;
}
else if (!strcasecmp(line, "Info"))
{
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of remote.cache.", linenum);
- return;
+ break;
}
}
else if (!strcasecmp(line, "Option") && value)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of remote.cache.", linenum);
- return;
+ break;
}
}
else if (!strcasecmp(line, "StateMessage"))
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of remote.cache.", linenum);
- return;
+ break;
}
}
else if (!strcasecmp(line, "Type"))
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of remote.cache.", linenum);
- return;
+ break;
}
}
else if (!strcasecmp(line, "BrowseTime"))
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of remote.cache.", linenum);
- return;
+ break;
}
}
else if (!strcasecmp(line, "JobSheets"))
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of remote.cache.", linenum);
- return;
+ break;
}
}
else if (!strcasecmp(line, "AllowUser"))
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of remote.cache.", linenum);
- return;
+ break;
}
}
else if (!strcasecmp(line, "DenyUser"))
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of remote.cache.", linenum);
- return;
+ break;
}
}
else
* Do auto-classing if needed...
*/
- process_implicit_classes();
+ process_implicit_classes(NULL);
}
cupsd_printer_t *p; /* Current printer */
time_t ut, /* Minimum update time */
to; /* Timeout time */
+ int write_printcap; /* Write the printcap file? */
if (!Browsing || !BrowseLocalProtocols || !Printers)
p->browse_time = time(NULL);
- if (BrowseLocalProtocols & BROWSE_CUPS)
+ if ((BrowseLocalProtocols & BROWSE_CUPS) && BrowseSocket >= 0)
send_cups_browse(p);
#ifdef HAVE_LIBSLP
* Loop through all of the printers and send local updates as needed...
*/
- for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
+ for (p = (cupsd_printer_t *)cupsArrayFirst(Printers), write_printcap = 0;
p;
p = (cupsd_printer_t *)cupsArrayNext(Printers))
{
cupsArraySave(Printers);
cupsdDeletePrinter(p, 1);
cupsArrayRestore(Printers);
+ write_printcap = 1;
}
}
}
+
+ if (write_printcap)
+ cupsdWritePrintcap();
}
}
#endif /* HAVE_OPENLDAP */
+ /*
+ * Enable LPD and SMB printer sharing as needed through external programs...
+ */
+
+ if (BrowseLocalProtocols & BROWSE_LPD)
+ update_lpd(1);
+
+ if (BrowseLocalProtocols & BROWSE_SMB)
+ update_smb(1);
+
/*
* Register the individual printers
*/
argv[1] = pollp->hostname;
if (cupsdStartProcess(polld, argv, envp, -1, -1, statusfds[1], -1, -1,
- 0, &(pollp->pid)) < 0)
+ 0, DefaultProfile, &(pollp->pid)) < 0)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"cupsdStartPolling: Unable to fork polling daemon - %s",
BrowseLDAPHandle = NULL;
}
#endif /* HAVE_OPENLDAP */
+
+ /*
+ * Disable LPD and SMB printer sharing as needed through external programs...
+ */
+
+ if (BrowseLocalProtocols & BROWSE_LPD)
+ update_lpd(0);
+
+ if (BrowseLocalProtocols & BROWSE_SMB)
+ update_smb(0);
}
cups_option_t *attrs) /* I - Attributes */
{
int i; /* Looping var */
- int update; /* Update printer attributes? */
+ int update, /* Update printer attributes? */
+ write_printcap; /* Write the printcap file? */
char finaluri[HTTP_MAX_URI], /* Final URI for printer */
name[IPP_MAX_NAME], /* Name of printer */
newname[IPP_MAX_NAME], /* New name of printer */
* See if we already have it listed in the Printers list, and add it if not...
*/
- type |= CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED;
- type &= ~CUPS_PRINTER_IMPLICIT;
- update = 0;
- hptr = strchr(host, '.');
- sptr = strchr(ServerName, '.');
+ type |= CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED;
+ type &= ~CUPS_PRINTER_IMPLICIT;
+ update = 0;
+ write_printcap = 0;
+ hptr = strchr(host, '.');
+ sptr = strchr(ServerName, '.');
if (!ServerNameIsIP && sptr != NULL && hptr != NULL)
{
if (hptr && !*hptr)
*hptr = '.'; /* Resource FQDN */
- if ((p = cupsdFindClass(name)) == NULL && BrowseShortNames)
+ if ((p = cupsdFindDest(name)) == NULL && BrowseShortNames)
{
- if ((p = cupsdFindClass(resource + 9)) != NULL)
+ if ((p = cupsdFindDest(resource + 9)) != NULL)
{
if (p->hostname && strcasecmp(p->hostname, host))
{
cupsdSetString(&p->device_uri, uri);
cupsdSetString(&p->hostname, host);
- update = 1;
+ update = 1;
+ write_printcap = 1;
}
}
else
if (hptr && !*hptr)
*hptr = '.'; /* Resource FQDN */
- if ((p = cupsdFindPrinter(name)) == NULL && BrowseShortNames)
+ if ((p = cupsdFindDest(name)) == NULL && BrowseShortNames)
{
- if ((p = cupsdFindPrinter(resource + 10)) != NULL)
+ if ((p = cupsdFindDest(resource + 10)) != NULL)
{
if (p->hostname && strcasecmp(p->hostname, host))
{
cupsdSetString(&p->uri, uri);
cupsdSetString(&p->device_uri, uri);
- update = 1;
+ write_printcap = 1;
+ update = 1;
}
}
if (info && (!p->info || strcmp(p->info, info)))
{
cupsdSetString(&p->info, info);
- update = 1;
+ update = 1;
+ write_printcap = 1;
}
if (!make_model || !make_model[0])
cupsdDeletePrinter(p, 1);
cupsdUpdateImplicitClasses();
+ write_printcap = 1;
}
else if (update)
{
if (p->type & CUPS_PRINTER_DEFAULT)
{
DefaultPrinter = p;
+ write_printcap = 1;
break;
}
}
* Do auto-classing if needed...
*/
- process_implicit_classes();
+ process_implicit_classes(&write_printcap);
/*
* Update the printcap file...
*/
- cupsdWritePrintcap();
+ if (write_printcap)
+ cupsdWritePrintcap();
}
const char *domain, /* I - Domain. ".local" for now */
void *context) /* I - User-defined context */
{
- (void)context;
+ cupsd_printer_t *p = (cupsd_printer_t *)context;
+ /* Current printer */
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "dnssdRegisterCallback(%s, %s)", name, regtype);
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterCallback(%s, %s) for %s",
+ name, regtype, p->name);
if (errorCode)
{
"DNSServiceRegister failed with error %d", (int)errorCode);
return;
}
+ else if (strcasecmp(name, p->reg_name))
+ {
+ cupsdLogMessage(CUPSD_LOG_INFO, "Using service name \"%s\" for \"%s\"",
+ name, p->name);
+
+ cupsdSetString(&p->reg_name, name);
+ LastEvent |= CUPSD_EVENT_PRINTER_MODIFIED;
+ }
}
*name; /* Service name */
int txt_len, /* TXT record length */
port; /* IPP port number */
- char str_buffer[1024];
+ char resource[1024], /* Resource path for printer */
+ str_buffer[1024];
/* C-string buffer */
const char *computerName; /* Computer name c-string ptr */
const char *regtype; /* Registration type */
+ const char *domain; /* Registration domain */
+ cupsd_location_t *location, /* Printer location */
+ *policy; /* Operation policy for Print-Job */
+ unsigned address[4]; /* INADDR_ANY address */
#ifdef HAVE_COREFOUNDATION_H
CFStringRef computerNameRef;/* Computer name CFString */
CFStringEncoding nameEncoding; /* Computer name encoding */
}
}
+ /*
+ * If 'Allow printing from the Internet' is enabled (i.e. from any address)
+ * let dnssd decide on the domain, otherwise restrict it to ".local".
+ */
+
+ if (p->type & CUPS_PRINTER_CLASS)
+ snprintf(resource, sizeof(resource), "/classes/%s", p->name);
+ else
+ snprintf(resource, sizeof(resource), "/printers/%s", p->name);
+
+ address[0] = address[1] = address[2] = address[3] = 0;
+ location = cupsdFindBest(resource, HTTP_POST);
+ policy = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
+
+ if ((location && !cupsdCheckAccess(address, "", 0, location)) ||
+ (policy && !cupsdCheckAccess(address, "", 0, policy)))
+ domain = "local.";
+ else
+ domain = NULL;
+
/*
* Use the _fax subtype for fax queues...
*/
regtype = (p->type & CUPS_PRINTER_FAX) ? dnssdIPPFaxRegType :
dnssdIPPRegType;
- cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterPrinter(%s) type is \"%s\"",
- p->name, regtype);
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "dnssdRegisterPrinter(%s) type, domain is \"%s\", \"%s\"",
+ p->name, regtype, domain ? domain : "(null)");
se = DNSServiceRegister(&p->dnssd_ipp_ref, 0, 0, name, regtype,
- NULL, NULL, htons(port), txt_len, txt_record,
+ domain, NULL, htons(port), txt_len, txt_record,
dnssdRegisterCallback, p);
/*
*/
static void
-process_implicit_classes(void)
+process_implicit_classes(
+ int *write_printcap) /* O - Write printcap file? */
{
int i; /* Looping var */
int update; /* Update printer attributes? */
update = 1;
+ if (write_printcap)
+ *write_printcap = 1;
+
cupsdLogMessage(CUPSD_LOG_DEBUG, "Added implicit class \"%s\"...",
name);
cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
* Access from localhost (127.0.0.1) is always allowed...
*/
- auth = AUTH_ALLOW;
+ auth = CUPSD_AUTH_ALLOW;
}
else
{
switch (BrowseACL->order_type)
{
default :
- auth = AUTH_DENY; /* anti-compiler-warning-code */
+ auth = CUPSD_AUTH_DENY; /* anti-compiler-warning-code */
break;
- case AUTH_ALLOW : /* Order Deny,Allow */
- auth = AUTH_ALLOW;
+ case CUPSD_AUTH_ALLOW : /* Order Deny,Allow */
+ auth = CUPSD_AUTH_ALLOW;
if (cupsdCheckAuth(address, srcname, len,
BrowseACL->num_deny, BrowseACL->deny))
- auth = AUTH_DENY;
+ auth = CUPSD_AUTH_DENY;
if (cupsdCheckAuth(address, srcname, len,
BrowseACL->num_allow, BrowseACL->allow))
- auth = AUTH_ALLOW;
+ auth = CUPSD_AUTH_ALLOW;
break;
- case AUTH_DENY : /* Order Allow,Deny */
- auth = AUTH_DENY;
+ case CUPSD_AUTH_DENY : /* Order Allow,Deny */
+ auth = CUPSD_AUTH_DENY;
if (cupsdCheckAuth(address, srcname, len,
BrowseACL->num_allow, BrowseACL->allow))
- auth = AUTH_ALLOW;
+ auth = CUPSD_AUTH_ALLOW;
if (cupsdCheckAuth(address, srcname, len,
BrowseACL->num_deny, BrowseACL->deny))
- auth = AUTH_DENY;
+ auth = CUPSD_AUTH_DENY;
break;
}
}
}
else
- auth = AUTH_ALLOW;
+ auth = CUPSD_AUTH_ALLOW;
- if (auth == AUTH_DENY)
+ if (auth == CUPSD_AUTH_DENY)
{
cupsdLogMessage(CUPSD_LOG_DEBUG,
"update_cups_browse: Refused %d bytes from %s", bytes,
}
+/*
+ * 'update_lpd()' - Update the LPD configuration as needed.
+ */
+
+static void
+update_lpd(int onoff) /* - 1 = turn on, 0 = turn off */
+{
+ if (!LPDConfigFile)
+ return;
+
+ if (!strncmp(LPDConfigFile, "xinetd:///", 10))
+ {
+ /*
+ * Enable/disable LPD via the xinetd.d config file for cups-lpd...
+ */
+
+ char newfile[1024]; /* New cups-lpd.N file */
+ cups_file_t *ofp, /* Original file pointer */
+ *nfp; /* New file pointer */
+ char line[1024]; /* Line from file */
+
+
+ snprintf(newfile, sizeof(newfile), "%s.N", LPDConfigFile + 9);
+
+ if ((ofp = cupsFileOpen(LPDConfigFile + 9, "r")) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\" - %s",
+ LPDConfigFile + 9, strerror(errno));
+ return;
+ }
+
+ if ((nfp = cupsFileOpen(newfile, "w")) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\" - %s",
+ newfile, strerror(errno));
+ cupsFileClose(ofp);
+ return;
+ }
+
+ /*
+ * Copy all of the lines from the cups-lpd file...
+ */
+
+ while (cupsFileGets(ofp, line, sizeof(line)))
+ {
+ if (line[0] == '{')
+ {
+ cupsFilePrintf(nfp, "%s\n", line);
+ snprintf(line, sizeof(line), "\tdisable = %s",
+ onoff ? "no" : "yes");
+ }
+ else if (!strstr(line, "disable ="))
+ cupsFilePrintf(nfp, "%s\n", line);
+ }
+
+ cupsFileClose(nfp);
+ cupsFileClose(ofp);
+ rename(newfile, LPDConfigFile + 9);
+ }
+#ifdef __APPLE__
+ else if (!strncmp(LPDConfigFile, "launchd:///", 11))
+ {
+ /*
+ * Enable/disable LPD via the launchctl command...
+ */
+
+ char *argv[5], /* Arguments for command */
+ *envp[MAX_ENV]; /* Environment for command */
+ int pid; /* Process ID */
+
+
+ cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
+ argv[0] = (char *)"launchctl";
+ argv[1] = (char *)(onoff ? "load" : "unload");
+ argv[2] = (char *)"-w";
+ argv[3] = LPDConfigFile + 10;
+ argv[4] = NULL;
+
+ cupsdStartProcess("/bin/launchctl", argv, envp, -1, -1, -1, -1, -1, 1,
+ NULL, &pid);
+ }
+#endif /* __APPLE__ */
+ else
+ cupsdLogMessage(CUPSD_LOG_INFO, "Unknown LPDConfigFile scheme!");
+}
+
+
/*
* 'update_polling()' - Read status messages from the poll daemons.
*/
/*
- * End of "$Id: dirsvc.c 6691 2007-07-19 19:09:46Z mike $".
+ * 'update_smb()' - Update the SMB configuration as needed.
+ */
+
+static void
+update_smb(int onoff) /* I - 1 = turn on, 0 = turn off */
+{
+ if (!SMBConfigFile)
+ return;
+
+ if (!strncmp(SMBConfigFile, "samba:///", 9))
+ {
+ /*
+ * Enable/disable SMB via the specified smb.conf config file...
+ */
+
+ char newfile[1024]; /* New smb.conf.N file */
+ cups_file_t *ofp, /* Original file pointer */
+ *nfp; /* New file pointer */
+ char line[1024]; /* Line from file */
+ int in_printers; /* In [printers] section? */
+
+
+ snprintf(newfile, sizeof(newfile), "%s.N", SMBConfigFile + 8);
+
+ if ((ofp = cupsFileOpen(SMBConfigFile + 8, "r")) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\" - %s",
+ SMBConfigFile + 8, strerror(errno));
+ return;
+ }
+
+ if ((nfp = cupsFileOpen(newfile, "w")) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\" - %s",
+ newfile, strerror(errno));
+ cupsFileClose(ofp);
+ return;
+ }
+
+ /*
+ * Copy all of the lines from the smb.conf file...
+ */
+
+ in_printers = 0;
+
+ while (cupsFileGets(ofp, line, sizeof(line)))
+ {
+ if (in_printers && strstr(line, "printable ="))
+ snprintf(line, sizeof(line), " printable = %s",
+ onoff ? "yes" : "no");
+
+ cupsFilePrintf(nfp, "%s\n", line);
+
+ if (line[0] == '[')
+ in_printers = !strcmp(line, "[printers]");
+ }
+
+ cupsFileClose(nfp);
+ cupsFileClose(ofp);
+ rename(newfile, SMBConfigFile + 8);
+ }
+ else
+ cupsdLogMessage(CUPSD_LOG_INFO, "Unknown SMBConfigFile scheme!");
+}
+
+
+/*
+ * End of "$Id: dirsvc.c 7003 2007-10-01 23:10:13Z mike $".
*/