BSD 4_4 release
[unix-history] / usr / src / usr.sbin / sendmail / src / udb.c
CommitLineData
814c4102
EA
1/*
2 * Copyright (c) 1983 Eric P. Allman
ad787160
C
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
814c4102 5 *
ad787160
C
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
814c4102
EA
33 */
34
cd47678d
EA
35#include "sendmail.h"
36
814c4102 37#ifndef lint
83b7c7b1 38#ifdef USERDB
ad787160 39static char sccsid [] = "@(#)udb.c 8.1 (Berkeley) 6/7/93 (with USERDB)";
83b7c7b1 40#else
ad787160 41static char sccsid [] = "@(#)udb.c 8.1 (Berkeley) 6/7/93 (without USERDB)";
83b7c7b1 42#endif
814c4102
EA
43#endif
44
814c4102
EA
45#ifdef USERDB
46
83b7c7b1 47#include <sys/time.h>
6ab0fb92 48#include <errno.h>
83b7c7b1 49#include <netdb.h>
814c4102
EA
50#include <db.h>
51
52/*
42fa5d67 53** UDB.C -- interface between sendmail and Berkeley User Data Base.
814c4102 54**
7a059839 55** This depends on the 4.4BSD db package.
814c4102
EA
56*/
57
dac6c90e 58
83b7c7b1
EA
59struct udbent
60{
61 char *udb_spec; /* string version of spec */
62 int udb_type; /* type of entry */
f61c3c40 63 char *udb_default; /* default host for outgoing mail */
83b7c7b1
EA
64 union
65 {
66 /* type UE_REMOTE -- do remote call for lookup */
67 struct
68 {
83b7c7b1
EA
69 struct sockaddr_in _udb_addr; /* address */
70 int _udb_timeout; /* timeout */
71 } udb_remote;
83b7c7b1
EA
72#define udb_addr udb_u.udb_remote._udb_addr
73#define udb_timeout udb_u.udb_remote._udb_timeout
74
75 /* type UE_FORWARD -- forward message to remote */
76 struct
77 {
78 char *_udb_fwdhost; /* name of forward host */
79 } udb_forward;
80#define udb_fwdhost udb_u.udb_forward._udb_fwdhost
81
f61c3c40 82 /* type UE_FETCH -- lookup in local database */
83b7c7b1
EA
83 struct
84 {
85 char *_udb_dbname; /* pathname of database */
86 DB *_udb_dbp; /* open database ptr */
87 } udb_lookup;
88#define udb_dbname udb_u.udb_lookup._udb_dbname
89#define udb_dbp udb_u.udb_lookup._udb_dbp
90 } udb_u;
91};
92
93#define UDB_EOLIST 0 /* end of list */
94#define UDB_SKIP 1 /* skip this entry */
95#define UDB_REMOTE 2 /* look up in remote database */
f61c3c40 96#define UDB_DBFETCH 3 /* look up in local database */
83b7c7b1
EA
97#define UDB_FORWARD 4 /* forward to remote host */
98
99#define MAXUDBENT 10 /* maximum number of UDB entries */
100
7a059839
EA
101
102struct option
103{
104 char *name;
105 char *val;
106};
107\f/*
108** UDBEXPAND -- look up user in database and expand
109**
110** Parameters:
111** a -- address to expand.
112** sendq -- pointer to head of sendq to put the expansions in.
113**
114** Returns:
6ab0fb92
EA
115** EX_TEMPFAIL -- if something "odd" happened -- probably due
116** to accessing a file on an NFS server that is down.
117** EX_OK -- otherwise.
7a059839
EA
118**
119** Side Effects:
120** Modifies sendq.
121*/
122
123int UdbPort = 1616;
124int UdbTimeout = 10;
125
d2836270
EA
126struct udbent UdbEnts[MAXUDBENT + 1];
127int UdbSock = -1;
128bool UdbInitialized = FALSE;
83b7c7b1 129
6ab0fb92 130int
a4076aed 131udbexpand(a, sendq, e)
814c4102
EA
132 register ADDRESS *a;
133 ADDRESS **sendq;
a4076aed 134 register ENVELOPE *e;
814c4102
EA
135{
136 int i;
137 register char *p;
814c4102
EA
138 DBT key;
139 DBT info;
83b7c7b1
EA
140 bool breakout;
141 register struct udbent *up;
dac6c90e 142 int keylen;
1c7897ef 143 int naddrs;
f43b04ce
EA
144 char keybuf[MAXKEY];
145 char buf[BUFSIZ];
814c4102
EA
146
147 if (tTd(28, 1))
f7eca811 148 printf("udbexpand(%s)\n", a->q_paddr);
814c4102
EA
149
150 /* make certain we are supposed to send to this address */
8bcce465 151 if (bitset(QDONTSEND|QVERIFIED, a->q_flags))
6ab0fb92 152 return EX_OK;
a4076aed 153 e->e_to = a->q_paddr;
814c4102 154
83b7c7b1 155 /* on first call, locate the database */
f61c3c40 156 if (!UdbInitialized)
814c4102 157 {
6ab0fb92 158 extern int _udbx_init();
dac6c90e 159
6ab0fb92
EA
160 if (_udbx_init() == EX_TEMPFAIL)
161 return EX_TEMPFAIL;
dac6c90e 162 }
83b7c7b1 163
d27b0e11
EA
164 /* short circuit the process if no chance of a match */
165 if (UdbSpec == NULL || UdbSpec[0] == '\0')
6ab0fb92 166 return EX_OK;
d27b0e11 167
dac6c90e
EA
168 /* if name is too long, assume it won't match */
169 if (strlen(a->q_user) > sizeof keybuf - 12)
6ab0fb92 170 return EX_OK;
83b7c7b1 171
dac6c90e
EA
172 /* if name begins with a colon, it indicates our metadata */
173 if (a->q_user[0] == ':')
6ab0fb92 174 return EX_OK;
dac6c90e
EA
175
176 /* build actual database key */
177 (void) strcpy(keybuf, a->q_user);
178 (void) strcat(keybuf, ":maildrop");
179 keylen = strlen(keybuf);
814c4102 180
83b7c7b1 181 breakout = FALSE;
dac6c90e 182 for (up = UdbEnts; !breakout; up++)
814c4102 183 {
83b7c7b1 184 char *user;
814c4102 185
83b7c7b1
EA
186 /*
187 ** Select action based on entry type.
188 **
189 ** On dropping out of this switch, "class" should
190 ** explain the type of the data, and "user" should
191 ** contain the user information.
192 */
814c4102 193
83b7c7b1
EA
194 switch (up->udb_type)
195 {
f61c3c40 196 case UDB_DBFETCH:
dac6c90e
EA
197 key.data = keybuf;
198 key.size = keylen;
bc3b6a08
EA
199 if (tTd(28, 80))
200 printf("udbexpand: trying %s\n", keybuf);
dac6c90e 201 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
6ab0fb92 202 if (i > 0 || info.size <= 0)
83b7c7b1 203 {
83b7c7b1 204 if (tTd(28, 2))
f7eca811 205 printf("udbexpand: no match on %s\n", keybuf);
83b7c7b1
EA
206 continue;
207 }
bc3b6a08
EA
208 if (tTd(28, 80))
209 printf("udbexpand: match %.*s: %.*s\n",
210 key.size, key.data, info.size, info.data);
814c4102 211
1c7897ef
EA
212 naddrs = 0;
213 a->q_flags &= ~QSELFREF;
256fe397
EA
214 while (i == 0 && key.size == keylen &&
215 bcmp(key.data, keybuf, keylen) == 0)
dac6c90e 216 {
fc14ab33 217 if (bitset(EF_VRFYONLY, e->e_flags))
8bcce465
EA
218 {
219 a->q_flags |= QVERIFIED;
a7dc53bd 220 e->e_nrcpts++;
fc14ab33 221 return EX_OK;
8bcce465 222 }
fc14ab33 223
256fe397 224 breakout = TRUE;
dac6c90e
EA
225 if (info.size < sizeof buf)
226 user = buf;
227 else
228 user = xalloc(info.size + 1);
229 bcopy(info.data, user, info.size);
230 user[info.size] = '\0';
231
b6edea3d 232 message("expanded to %s", user);
aa102c71
EA
233#ifdef LOG
234 if (LogLevel >= 10)
235 syslog(LOG_INFO, "%s: expand %s => %s",
236 e->e_id, e->e_to, user);
237#endif
dac6c90e 238 AliasLevel++;
1c7897ef 239 naddrs += sendtolist(user, a, sendq, e);
dac6c90e
EA
240 AliasLevel--;
241
242 if (user != buf)
243 free(user);
244
245 /* get the next record */
246 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
256fe397 247 }
bc3b6a08
EA
248
249 /* if nothing ever matched, try next database */
250 if (!breakout)
251 continue;
252
1c7897ef 253 if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
f7eca811
EA
254 {
255 if (tTd(28, 5))
256 {
257 printf("udbexpand: QDONTSEND ");
258 printaddr(a, FALSE);
259 }
260 a->q_flags |= QDONTSEND;
261 }
6ab0fb92
EA
262 if (i < 0)
263 {
49a282c6
EA
264 syserr("udbexpand: db-get %.*s stat %d",
265 key.size, key.data, i);
6ab0fb92
EA
266 return EX_TEMPFAIL;
267 }
102088d0
EA
268
269 /*
270 ** If this address has a -request address, reflect
271 ** it into the envelope.
272 */
273
274 (void) strcpy(keybuf, a->q_user);
275 (void) strcat(keybuf, ":mailsender");
276 keylen = strlen(keybuf);
277 key.data = keybuf;
278 key.size = keylen;
279 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
280 if (i != 0 || info.size <= 0)
281 break;
282 a->q_owner = xalloc(info.size + 1);
283 bcopy(info.data, a->q_owner, info.size);
284 a->q_owner[info.size] = '\0';
83b7c7b1
EA
285 break;
286
287 case UDB_REMOTE:
84a5cc29
EA
288 /* not yet implemented */
289 continue;
83b7c7b1
EA
290
291 case UDB_FORWARD:
fc14ab33
EA
292 if (bitset(EF_VRFYONLY, e->e_flags))
293 return EX_OK;
83b7c7b1
EA
294 i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1;
295 if (i < sizeof buf)
296 user = buf;
297 else
298 user = xalloc(i + 1);
299 (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost);
b6edea3d 300 message("expanded to %s", user);
1c7897ef 301 a->q_flags &= ~QSELFREF;
dac6c90e 302 AliasLevel++;
1c7897ef 303 naddrs = sendtolist(user, a, sendq, e);
dac6c90e 304 AliasLevel--;
1c7897ef 305 if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
f7eca811
EA
306 {
307 if (tTd(28, 5))
308 {
309 printf("udbexpand: QDONTSEND ");
310 printaddr(a, FALSE);
311 }
312 a->q_flags |= QDONTSEND;
313 }
dac6c90e
EA
314 if (user != buf)
315 free(user);
316 breakout = TRUE;
83b7c7b1
EA
317 break;
318
319 case UDB_EOLIST:
320 breakout = TRUE;
321 continue;
322
323 default:
324 /* unknown entry type */
325 continue;
326 }
dac6c90e 327 }
6ab0fb92 328 return EX_OK;
dac6c90e 329}
f61c3c40
EA
330\f/*
331** UDBSENDER -- return canonical external name of sender, given local name
332**
333** Parameters:
334** sender -- the name of the sender on the local machine.
335**
336** Returns:
337** The external name for this sender, if derivable from the
338** database.
339** NULL -- if nothing is changed from the database.
340**
341** Side Effects:
342** none.
343*/
344
345char *
346udbsender(sender)
347 char *sender;
348{
349 register char *p;
350 register struct udbent *up;
351 int i;
352 int keylen;
353 DBT key, info;
f43b04ce 354 char keybuf[MAXKEY];
f61c3c40
EA
355
356 if (tTd(28, 1))
357 printf("udbsender(%s)\n", sender);
358
359 if (!UdbInitialized)
360 {
361 if (_udbx_init() == EX_TEMPFAIL)
362 return NULL;
363 }
364
365 /* short circuit if no spec */
366 if (UdbSpec == NULL || UdbSpec[0] == '\0')
367 return NULL;
368
369 /* long names can never match and are a pain to deal with */
370 if (strlen(sender) > sizeof keybuf - 12)
371 return NULL;
372
373 /* names beginning with colons indicate metadata */
374 if (sender[0] == ':')
375 return NULL;
376
377 /* build database key */
378 (void) strcpy(keybuf, sender);
379 (void) strcat(keybuf, ":mailname");
380 keylen = strlen(keybuf);
381
382 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
383 {
384 /*
385 ** Select action based on entry type.
386 */
387
388 switch (up->udb_type)
389 {
390 case UDB_DBFETCH:
391 key.data = keybuf;
392 key.size = keylen;
393 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
394 if (i != 0 || info.size <= 0)
395 {
396 if (tTd(28, 2))
397 printf("udbsender: no match on %s\n",
398 keybuf);
399 continue;
400 }
401
402 p = xalloc(info.size + 1);
403 bcopy(info.data, p, info.size);
404 p[info.size] = '\0';
405 if (tTd(28, 1))
406 printf("udbsender ==> %s\n", p);
407 return p;
408 }
409 }
410
411 /*
412 ** Nothing yet. Search again for a default case. But only
413 ** use it if we also have a forward (:maildrop) pointer already
414 ** in the database.
415 */
416
417 /* build database key */
418 (void) strcpy(keybuf, sender);
419 (void) strcat(keybuf, ":maildrop");
420 keylen = strlen(keybuf);
421
422 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
423 {
424 switch (up->udb_type)
425 {
426 case UDB_DBFETCH:
427 /* get the default case for this database */
428 if (up->udb_default == NULL)
429 {
430 key.data = ":default:mailname";
431 key.size = strlen(key.data);
432 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
433 if (i != 0 || info.size <= 0)
434 {
435 /* no default case */
436 up->udb_default = "";
437 continue;
438 }
439
440 /* save the default case */
441 up->udb_default = xalloc(info.size + 1);
442 bcopy(info.data, up->udb_default, info.size);
443 up->udb_default[info.size] = '\0';
444 }
445 else if (up->udb_default[0] == '\0')
446 continue;
447
448 /* we have a default case -- verify user:maildrop */
449 key.data = keybuf;
450 key.size = keylen;
451 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
452 if (i != 0 || info.size <= 0)
453 {
454 /* nope -- no aliasing for this user */
455 continue;
456 }
457
458 /* they exist -- build the actual address */
459 p = xalloc(strlen(sender) + strlen(up->udb_default) + 2);
460 (void) strcpy(p, sender);
461 (void) strcat(p, "@");
462 (void) strcat(p, up->udb_default);
463 if (tTd(28, 1))
464 printf("udbsender ==> %s\n", p);
465 return p;
466 }
467 }
468
469 /* still nothing.... too bad */
470 return NULL;
471}
472\f/*
473** _UDBX_INIT -- parse the UDB specification, opening any valid entries.
474**
475** Parameters:
476** none.
477**
478** Returns:
479** EX_TEMPFAIL -- if it appeared it couldn't get hold of a
480** database due to a host being down or some similar
481** (recoverable) situation.
482** EX_OK -- otherwise.
483**
484** Side Effects:
485** Fills in the UdbEnts structure from UdbSpec.
486*/
83b7c7b1 487
7a059839
EA
488#define MAXUDBOPTS 27
489
d2836270 490int
dac6c90e
EA
491_udbx_init()
492{
493 register char *p;
494 int i;
495 register struct udbent *up;
f43b04ce 496 char buf[BUFSIZ];
83b7c7b1 497
f61c3c40
EA
498 if (UdbInitialized)
499 return EX_OK;
500
fe993526
EA
501# ifdef UDB_DEFAULT_SPEC
502 if (UdbSpec == NULL)
503 UdbSpec = UDB_DEFAULT_SPEC;
504# endif
505
dac6c90e
EA
506 p = UdbSpec;
507 up = UdbEnts;
b375cb3b 508 while (p != NULL)
dac6c90e
EA
509 {
510 char *spec;
511 auto int rcode;
7a059839 512 int nopts;
dac6c90e
EA
513 int nmx;
514 register struct hostent *h;
515 char *mxhosts[MAXMXHOSTS + 1];
7a059839 516 struct option opts[MAXUDBOPTS + 1];
dac6c90e
EA
517
518 while (*p == ' ' || *p == '\t' || *p == ',')
519 p++;
520 if (*p == '\0')
521 break;
522 spec = p;
f3d8f6d6 523 p = strchr(p, ',');
93f1f3e4 524 if (p != NULL)
dac6c90e 525 *p++ = '\0';
7a059839
EA
526
527 /* extract options */
528 nopts = _udb_parsespec(spec, opts, MAXUDBOPTS);
529
530 /*
531 ** Decode database specification.
532 **
533 ** In the sendmail tradition, the leading character
534 ** defines the semantics of the rest of the entry.
535 **
536 ** +hostname -- send a datagram to the udb server
537 ** on host "hostname" asking for the
538 ** home mail server for this user.
539 ** *hostname -- similar to +hostname, except that the
540 ** hostname is searched as an MX record;
541 ** resulting hosts are searched as for
542 ** +mxhostname. If no MX host is found,
543 ** this is the same as +hostname.
544 ** @hostname -- forward email to the indicated host.
545 ** This should be the last in the list,
546 ** since it always matches the input.
547 ** /dbname -- search the named database on the local
548 ** host using the Berkeley db package.
549 */
550
dac6c90e 551 switch (*spec)
83b7c7b1 552 {
dac6c90e 553 case '+': /* search remote database */
7a059839
EA
554 case '*': /* search remote database (expand MX) */
555 if (*spec == '*')
dac6c90e 556 {
823cd830 557#ifdef NAMED_BIND
c3577cd6 558 nmx = getmxrr(spec + 1, mxhosts, FALSE, &rcode);
823cd830
EA
559#else
560 mxhosts[0] = spec + 1;
561 nmx = 1;
562 rcode = 0;
563#endif
7a059839
EA
564 if (tTd(28, 16))
565 {
566 int i;
567
568 printf("getmxrr(%s): %d", spec + 1, nmx);
569 for (i = 0; i <= nmx; i++)
570 printf(" %s", mxhosts[i]);
571 printf("\n");
572 }
dac6c90e 573 }
7a059839 574 else
dac6c90e 575 {
7a059839
EA
576 nmx = 1;
577 mxhosts[0] = spec + 1;
dac6c90e 578 }
7a059839 579
dac6c90e
EA
580 for (i = 0; i < nmx; i++)
581 {
582 h = gethostbyname(mxhosts[i]);
583 if (h == NULL)
584 continue;
585 up->udb_type = UDB_REMOTE;
586 up->udb_addr.sin_family = h->h_addrtype;
dac6c90e
EA
587 bcopy(h->h_addr_list[0],
588 (char *) &up->udb_addr.sin_addr,
589 h->h_length);
590 up->udb_addr.sin_port = UdbPort;
591 up->udb_timeout = UdbTimeout;
592 up++;
593 }
594
595 /* set up a datagram socket */
596 if (UdbSock < 0)
597 {
598 UdbSock = socket(AF_INET, SOCK_DGRAM, 0);
599 (void) fcntl(UdbSock, F_SETFD, 1);
600 }
601 break;
602
603 case '@': /* forward to remote host */
604 up->udb_type = UDB_FORWARD;
605 up->udb_fwdhost = spec + 1;
606 up++;
607 break;
608
609 case '/': /* look up remote name */
834f8378 610 up->udb_dbname = spec;
6ab0fb92 611 errno = 0;
dac6c90e
EA
612 up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL);
613 if (up->udb_dbp == NULL)
6ab0fb92
EA
614 {
615 if (errno != ENOENT && errno != EACCES)
f61c3c40 616 {
73f6752a
EA
617#ifdef LOG
618 if (LogLevel > 2)
3f0792d1
EA
619 syslog(LOG_ERR, "dbopen(%s): %s",
620 spec, errstring(errno));
73f6752a 621#endif
f61c3c40
EA
622 up->udb_type = UDB_EOLIST;
623 goto tempfail;
624 }
dac6c90e 625 break;
6ab0fb92 626 }
f61c3c40 627 up->udb_type = UDB_DBFETCH;
dac6c90e
EA
628 up++;
629 break;
83b7c7b1 630 }
dac6c90e
EA
631 }
632 up->udb_type = UDB_EOLIST;
83b7c7b1 633
dac6c90e
EA
634 if (tTd(28, 4))
635 {
f61c3c40 636 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
83b7c7b1 637 {
dac6c90e
EA
638 switch (up->udb_type)
639 {
dac6c90e
EA
640 case UDB_REMOTE:
641 printf("REMOTE: addr %s, timeo %d\n",
5a972da1 642 anynet_ntoa((SOCKADDR *) &up->udb_addr),
dac6c90e
EA
643 up->udb_timeout);
644 break;
645
f61c3c40
EA
646 case UDB_DBFETCH:
647 printf("FETCH: file %s\n",
256fe397 648 up->udb_dbname);
dac6c90e
EA
649 break;
650
651 case UDB_FORWARD:
652 printf("FORWARD: host %s\n",
653 up->udb_fwdhost);
654 break;
655
656 default:
657 printf("UNKNOWN\n");
658 break;
659 }
83b7c7b1 660 }
814c4102 661 }
f61c3c40
EA
662
663 UdbInitialized = TRUE;
847ac56a 664 errno = 0;
f61c3c40
EA
665 return EX_OK;
666
667 /*
668 ** On temporary failure, back out anything we've already done
669 */
670
671 tempfail:
672 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
673 {
674 if (up->udb_type == UDB_DBFETCH)
675 {
676 (*up->udb_dbp->close)(up->udb_dbp);
677 }
678 }
679 return EX_TEMPFAIL;
83b7c7b1 680}
814c4102 681
7a059839
EA
682int
683_udb_parsespec(udbspec, opt, maxopts)
684 char *udbspec;
685 struct option opt[];
686 int maxopts;
687{
688 register char *spec;
689 register char *spec_end;
690 register int optnum;
691
f3d8f6d6 692 spec_end = strchr(udbspec, ':');
7a059839
EA
693 for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++)
694 {
695 register char *p;
696
2bee003d 697 while (isascii(*spec) && isspace(*spec))
7a059839 698 spec++;
f3d8f6d6 699 spec_end = strchr(spec, ':');
7a059839
EA
700 if (spec_end != NULL)
701 *spec_end++ = '\0';
702
703 opt[optnum].name = spec;
704 opt[optnum].val = NULL;
f3d8f6d6 705 p = strchr(spec, '=');
7a059839
EA
706 if (p != NULL)
707 opt[optnum].val = ++p;
708 }
709 return optnum;
710}
711
83b7c7b1
EA
712#else /* not USERDB */
713
6ab0fb92 714int
a4076aed 715udbexpand(a, sendq, e)
83b7c7b1
EA
716 ADDRESS *a;
717 ADDRESS **sendq;
a4076aed 718 ENVELOPE *e;
83b7c7b1 719{
6ab0fb92 720 return EX_OK;
814c4102
EA
721}
722
723#endif /* USERDB */