]> git.sur5r.net Git - openldap/blob - clients/mail500/main.c
Added a method to make delivery to the local mailbox conditional on
[openldap] / clients / mail500 / main.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright (c) 1990 Regents of the University of Michigan.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted
7  * provided that this notice is preserved and that due credit is given
8  * to the University of Michigan at Ann Arbor. The name of the University
9  * may not be used to endorse or promote products derived from this
10  * software without specific prior written permission. This software
11  * is provided ``as is'' without express or implied warranty.
12  *
13  * Copyright 1998,1999 The OpenLDAP Foundation
14  * COPYING RESTRICTIONS APPLY.  See COPYRIGHT File in top level directory
15  * of this package for details.
16  */
17
18 #include "portable.h"
19
20 #include <stdio.h>
21
22 #include <ac/stdlib.h>
23
24 #include <ac/ctype.h>
25 #include <ac/param.h>
26 #include <ac/signal.h>
27 #include <ac/string.h>
28 #include <ac/sysexits.h>
29 #include <ac/syslog.h>
30 #include <ac/time.h>
31 #include <ac/unistd.h>
32 #include <ac/wait.h>
33
34 #include <sys/stat.h>
35
36 #ifdef HAVE_SYS_RESOURCE_H
37 #include <sys/resource.h>
38 #endif
39
40 #include <ldap.h>
41
42 #include "ldap_defaults.h"
43
44 #ifndef MAIL500_BOUNCEFROM
45 #define MAIL500_BOUNCEFROM "<>"
46 #endif
47
48 #define USER            0x01
49 #define GROUP_ERRORS    0x02
50 #define GROUP_REQUEST   0x04
51 #define GROUP_MEMBERS   0x08
52 #define GROUP_OWNER     0x10
53
54 #define ERROR           "error"
55 #define ERRORS          "errors"
56 #define REQUEST         "request"
57 #define REQUESTS        "requests"
58 #define MEMBERS         "members"
59 #define OWNER           "owner"
60 #define OWNERS          "owners"
61
62 LDAP    *ld;
63 char    *vacationhost = NULL;
64 char    *errorsfrom = MAIL500_BOUNCEFROM;
65 char    *mailfrom = NULL;
66 char    *host = NULL;
67 char    *ldaphost = NULL;
68 int     hostlen = 0;
69 int     debug;
70
71 typedef struct errs {
72         int             e_code;
73 #define E_USERUNKNOWN           1
74 #define E_AMBIGUOUS             2
75 #define E_NOEMAIL               3
76 #define E_NOREQUEST             4
77 #define E_NOERRORS              5
78 #define E_BADMEMBER             6
79 #define E_JOINMEMBERNOEMAIL     7
80 #define E_MEMBERNOEMAIL         8
81 #define E_LOOP                  9
82 #define E_NOMEMBERS             10
83 #define E_NOOWNER               11
84 #define E_GROUPUNKNOWN          12
85 #define E_NOOWNADDRESS          13
86         char            *e_addr;
87         union e_union_u {
88                 char            *e_u_loop;
89                 LDAPMessage     *e_u_msg;
90         } e_union;
91 #define e_msg   e_union.e_u_msg
92 #define e_loop  e_union.e_u_loop
93 } Error;
94
95 typedef struct groupto {
96         char    *g_dn;
97         char    *g_errorsto;
98         char    **g_members;
99         int     g_nmembers;
100 } Group;
101
102 typedef struct baseinfo {
103         char    *b_url;
104         int     b_m_entries;
105         char    b_rdnpref;      /* give rdn's preference when searching? */
106         int     b_search;       /* ORed with the type of thing the address */
107                                 /*  looks like (USER, GROUP_ERRORS, etc.)  */
108                                 /*  to see if this should be searched      */
109 } Base;
110
111 Base    **base = NULL;
112
113 char    *sendmailargs[] = { MAIL500_SENDMAIL, "-oMrLDAP", "-odi", "-oi", "-f", NULL, NULL };
114
115 typedef struct attr_semantics {
116         char    *as_name;
117         int     as_m_valued;    /* Is multivalued? */
118         int     as_priority;    /* Priority level of this attribut type */
119         int     as_syntax;      /* How to interpret values */
120         int     as_m_entries;   /* Can resolve to several entries? */
121         int     as_kind;        /* Recipient, sender, etc. */
122         char    *as_param;      /* Extra info for filters and things alike */
123 } AttrSemantics;
124
125 #define AS_SYNTAX_UNKNOWN       0
126 #define AS_SYNTAX_NATIVE_MB     1       /* Unqualified mailbox name */
127 #define AS_SYNTAX_RFC822        2       /* RFC822 mail address */
128 #define AS_SYNTAX_HOST          3
129 #define AS_SYNTAX_DN            4       /* A directory entry */
130 #define AS_SYNTAX_RFC822_EXT    5
131 #define AS_SYNTAX_URL           6       /* mailto: or ldap: URL */
132 #define AS_SYNTAX_BOOL_FILTER   7       /* For joinable, filter in as_param */
133 #define AS_SYNTAX_PRESENT       8       /* Value irrelevant, only presence is
134                                          * considered. */
135
136 #define AS_KIND_UNKNOWN         0
137 #define AS_KIND_RECIPIENT       1
138 #define AS_KIND_ERRORS          2       /* For ErrorsTo and similar */
139 #define AS_KIND_REQUEST         3
140 #define AS_KIND_OWNER           4
141 #define AS_KIND_ROUTE_TO_HOST   5       /* Expand at some other host */
142 #define AS_KIND_ALLOWED_SENDER  6       /* Can send to group */
143 #define AS_KIND_MODERATOR       7
144 #define AS_KIND_ROUTE_TO_ADDR   8       /* Rewrite recipient address as */
145 #define AS_KIND_OWN_ADDR        9       /* RFC822 name of this entry */
146 #define AS_KIND_DELIVERY_TYPE   10      /* How to deliver mail to this entry */
147
148 AttrSemantics **attr_semantics = NULL;
149 int current_priority = 0;
150
151 typedef struct subst {
152         char    sub_char;
153         char    *sub_value;
154 } Subst;
155
156 char    **groupclasses = NULL;
157 char    **def_attr = NULL;
158 char    **myhosts = NULL;               /* FQDNs not to route elsewhere */
159 char    **mydomains = NULL;             /* If an RFC822 address points to one
160                                            of these domains, search it in the
161                                            directory instead of returning it
162                                            to hte MTA */
163
164 static void load_config( char *filespec );
165 static void split_address( char *address, char **localpart, char **domainpart);
166 static int entry_engine( LDAPMessage *e, char *dn, char *address, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type );
167 static void do_address( char *name, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type );
168 static void send_message( char **to );
169 static void send_errors( Error *err, int nerr );
170 static void do_noemail( FILE *fp, Error *err, int namelen );
171 static void do_ambiguous( FILE *fp, Error *err, int namelen );
172 static int count_values( char **list );
173 static void add_to( char ***list, int *nlist, char **new );
174 static void add_single_to( char ***list, char *new );
175 static int  isgroup( LDAPMessage *e );
176 static void add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg );
177 static void unbind_and_exit( int rc ) LDAP_GCCATTR((noreturn));
178 static void send_group( Group **group, int ngroup );
179
180 static int  connect_to_x500( void );
181
182
183 int
184 main ( int argc, char **argv )
185 {
186         char            *myname;
187         char            **tolist;
188         Error           *errlist;
189         Group           **togroups;
190         int             numto, ngroups, numerr, nargs;
191         int             i, j;
192         char            *conffile = NULL;
193
194         if ( (myname = strrchr( argv[0], '/' )) == NULL )
195                 myname = strdup( argv[0] );
196         else
197                 myname = strdup( myname + 1 );
198
199 #ifdef SIGPIPE
200         (void) SIGNAL( SIGPIPE, SIG_IGN );
201 #endif
202
203 #ifdef LOG_MAIL
204         openlog( myname, OPENLOG_OPTIONS, LOG_MAIL );
205 #elif LOG_DEBUG
206         openlog( myname, OPENLOG_OPTIONS );
207 #endif
208
209         while ( (i = getopt( argc, argv, "d:C:f:h:l:m:v:" )) != EOF ) {
210                 switch( i ) {
211                 case 'd':       /* turn on debugging */
212                         debug |= atoi( optarg );
213                         break;
214
215                 case 'C':       /* path to configuration file */
216                         conffile = strdup( optarg );
217                         break;
218
219                 case 'f':       /* who it's from & where errors should go */
220                         mailfrom = strdup( optarg );
221                         for ( j = 0; sendmailargs[j] != NULL; j++ ) {
222                                 if ( strcmp( sendmailargs[j], "-f" ) == 0 ) {
223                                         sendmailargs[j+1] = mailfrom;
224                                         break;
225                                 }
226                         }
227                         break;
228
229                 case 'h':       /* hostname */
230                         host = strdup( optarg );
231                         hostlen = strlen(host);
232                         break;
233
234                 case 'l':       /* ldap host */
235                         ldaphost = strdup( optarg );
236                         break;
237
238                                 /* mailer-daemon address - who we should */
239                 case 'm':       /* say errors come from */
240                         errorsfrom = strdup( optarg );
241                         break;
242
243                 case 'v':       /* vacation host */
244                         vacationhost = strdup( optarg );
245                         break;
246
247                 default:
248                         syslog( LOG_ALERT, "unknown option" );
249                         break;
250                 }
251         }
252
253         if ( mailfrom == NULL ) {
254                 syslog( LOG_ALERT, "required argument -f not present" );
255                 exit( EX_TEMPFAIL );
256         }
257         if ( errorsfrom == NULL ) {
258                 syslog( LOG_ALERT, "required argument -m not present" );
259                 exit( EX_TEMPFAIL );
260         }
261 /*      if ( host == NULL ) { */
262 /*              syslog( LOG_ALERT, "required argument -h not present" ); */
263 /*              exit( EX_TEMPFAIL ); */
264 /*      } */
265         if ( conffile == NULL ) {
266                 syslog( LOG_ALERT, "required argument -C not present" );
267                 exit( EX_TEMPFAIL );
268         }
269
270         load_config( conffile );
271
272         if ( connect_to_x500() != 0 )
273                 exit( EX_TEMPFAIL );
274
275         setuid( geteuid() );
276
277         if ( debug ) {
278                 char    buf[1024];
279                 int     i;
280
281                 syslog( LOG_ALERT, "running as %d", geteuid() );
282                 strcpy( buf, argv[0] );
283                 for ( i = 1; i < argc; i++ ) {
284                         strcat( buf, " " );
285                         strcat( buf, argv[i] );
286                 }
287
288                 syslog( LOG_ALERT, "args: (%s)", buf );
289         }
290
291         tolist = NULL;
292         numto = 0;
293         add_to( &tolist, &numto, sendmailargs );
294         nargs = numto;
295         ngroups = numerr = 0;
296         togroups = NULL;
297         errlist = NULL;
298         for ( i = optind; i < argc; i++ ) {
299                 char    *s;
300                 int     type;
301                 char    *localpart = NULL, *domainpart = NULL;
302                 char    address[1024];
303
304                 type = USER;
305                 split_address( argv[i], &localpart, &domainpart );
306                 if ( (s = strrchr( localpart, '-' )) != NULL ) {
307                         s++;
308
309                         if ((strcasecmp(s, ERROR) == 0) ||
310                                 (strcasecmp(s, ERRORS) == 0)) {
311                                 type = GROUP_ERRORS;
312                                 *(--s) = '\0';
313                         } else if ((strcasecmp(s, REQUEST) == 0) ||
314                                 (strcasecmp(s, REQUESTS) == 0)) {
315                                 type = GROUP_REQUEST;
316                                 *(--s) = '\0';
317                         } else if ( strcasecmp( s, MEMBERS ) == 0 ) {
318                                 type = GROUP_MEMBERS;
319                                 *(--s) = '\0';
320                         } else if ((strcasecmp(s, OWNER) == 0) ||
321                                 (strcasecmp(s, OWNERS) == 0)) {
322                                 type = GROUP_OWNER;
323                                 *(--s) = '\0';
324                         }
325                 }
326
327                 if ( domainpart ) {
328                         sprintf( address, "%s@%s", localpart, domainpart );
329                         free( localpart );
330                         free( domainpart );
331                 } else {
332                         sprintf( address, "%s@%s", localpart, domainpart );
333                         free( localpart );
334                 }
335                 do_address( address, &tolist, &numto, &togroups, &ngroups,
336                     &errlist, &numerr, type );
337         }
338
339         /*
340          * If we have both errors and successful deliveries to make or if
341          * if there are any groups to deliver to, we basically need to read
342          * the message twice.  So, we have to put it in a tmp file.
343          */
344
345         if ( numerr > 0 && numto > nargs || ngroups > 0 ) {
346                 FILE    *fp;
347                 char    buf[BUFSIZ];
348
349                 umask( 077 );
350                 if ( (fp = tmpfile()) == NULL ) {
351                         syslog( LOG_ALERT, "could not open tmp file" );
352                         unbind_and_exit( EX_TEMPFAIL );
353                 }
354
355                 /* copy the message to a temp file */
356                 while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
357                         if ( fputs( buf, fp ) == EOF ) {
358                                 syslog( LOG_ALERT, "error writing tmpfile" );
359                                 unbind_and_exit( EX_TEMPFAIL );
360                         }
361                 }
362
363                 if ( dup2( fileno( fp ), 0 ) == -1 ) {
364                         syslog( LOG_ALERT, "could not dup2 tmpfile" );
365                         unbind_and_exit( EX_TEMPFAIL );
366                 }
367
368                 fclose( fp );
369         }
370
371         /* deal with errors */
372         if ( numerr > 0 ) {
373                 if ( debug ) {
374                         syslog( LOG_ALERT, "sending errors" );
375                 }
376                 (void) rewind( stdin );
377                 send_errors( errlist, numerr );
378         }
379
380         (void) ldap_unbind( ld );
381
382         /* send to groups with errorsTo */
383         if ( ngroups > 0 ) {
384                 if ( debug ) {
385                         syslog( LOG_ALERT, "sending to groups with errorsto" );
386                 }
387                 (void) rewind( stdin );
388                 send_group( togroups, ngroups );
389         }
390
391         /* send to expanded aliases and groups w/o errorsTo */
392         if ( numto > nargs ) {
393                 if ( debug ) {
394                         syslog( LOG_ALERT, "sending to aliases and groups" );
395                 }
396                 (void) rewind( stdin );
397                 send_message( tolist );
398         }
399
400         return( EX_OK );
401 }
402
403 static char *
404 get_config_line( FILE *cf, int *lineno)
405 {
406         static char     buf[2048];
407         int             len;
408         int             pos;
409         int             room;
410
411         pos = 0;
412         room = sizeof( buf );
413         while ( fgets( &buf[pos], room, cf ) ) {
414                 (*lineno)++;
415                 if ( pos > 0 ) {
416                         /* Delete whitespace at the beginning of new data */
417                         if ( isspace( buf[pos] ) ) {
418                                 char *s, *d;
419                                 for ( s = buf+pos; isspace(*s); s++ )
420                                         ;
421                                 for ( d = buf+pos; *s; s++, d++ ) {
422                                         *d = *s;
423                                 }
424                                 *d = *s;
425                         }
426                 }
427                 len = strlen( buf );
428                 if ( buf[len-1] != '\n' ) {
429                         syslog( LOG_ALERT, "Definition too long at line %d",
430                                 *lineno );
431                         exit( EX_TEMPFAIL );
432                 }
433                 if ( buf[0] == '#' )
434                         continue;
435                 if ( strspn( buf, " \t\n" ) == len )
436                         continue;
437                 if ( buf[len-2] == '\\' ) {
438                         pos = len - 2;
439                         room = sizeof(buf) - pos;
440                         continue;
441                 }
442                 /* We have a real line, we will exit the loop */
443                 buf[len-1] = '\0';
444                 return( buf );
445         }
446         return( NULL );
447 }
448
449 static void
450 add_url ( char *url, int rdnpref, int typemask )
451 {
452         Base            **list_temp;
453         int             size;
454         Base            *b;
455
456         b = calloc(1, sizeof(Base));
457         if ( !b ) {
458                 syslog( LOG_ALERT, "Out of memory" );
459                 exit( EX_TEMPFAIL );
460         }
461         b->b_url = strdup( url );
462         b->b_rdnpref = rdnpref;
463         b->b_search   = typemask;
464
465         if ( base == NULL ) {
466                 base = calloc(2, sizeof(LDAPURLDesc *));
467                 if ( !base ) {
468                         syslog( LOG_ALERT, "Out of memory" );
469                         exit( EX_TEMPFAIL );
470                 }
471                 base[0] = b;
472         } else {
473                 for ( size = 0; base[size]; size++ )
474                         ;
475                 size += 2;
476                 list_temp = realloc( base, size*sizeof(LDAPURLDesc *) );
477                 if ( !list_temp ) {
478                         syslog( LOG_ALERT, "Out of memory" );
479                         exit( EX_TEMPFAIL );
480                 }
481                 base = list_temp;
482                 base[size-2] = b;
483                 base[size-1] = NULL;
484         }
485 }
486
487 static void
488 add_def_attr( char *s )
489 {
490         char *p, *q;
491
492         p = s;
493         while ( *p ) {
494                 p += strspn( p, "\t," );
495                 q = strpbrk( p, " \t," );
496                 if ( q ) {
497                         *q = '\0';
498                         add_single_to( &def_attr, p );
499                 } else {
500                         add_single_to( &def_attr, p );
501                         break;
502                 }
503                 p = q + 1;
504         }
505 }
506
507 static void
508 add_attr_semantics( char *s )
509 {
510         char *p, *q;
511         AttrSemantics *as;
512
513         as = calloc( 1, sizeof( AttrSemantics ) );
514         as->as_priority = current_priority;
515         p = s;
516         while ( isspace ( *p ) )
517                 p++;
518         q = p;
519         while ( !isspace ( *q ) && *q != '\0' )
520                 q++;
521         *q = '\0';
522         as->as_name = strdup( p );
523         p = q + 1;
524
525         while ( *p ) {
526                 while ( isspace ( *p ) )
527                         p++;
528                 q = p;
529                 while ( !isspace ( *q ) && *q != '\0' )
530                         q++;
531                 *q = '\0';
532                 if ( !strcasecmp( p, "multivalued" ) ) {
533                         as->as_m_valued = 1;
534                 } else if ( !strcasecmp( p, "multiple-entries" ) ) {
535                         as->as_m_entries = 1;
536                 } else if ( !strcasecmp( p, "local-native-mailbox" ) ) {
537                         as->as_syntax = AS_SYNTAX_NATIVE_MB;
538                 } else if ( !strcasecmp( p, "rfc822" ) ) {
539                         as->as_syntax = AS_SYNTAX_RFC822;
540                 } else if ( !strcasecmp( p, "rfc822-extended" ) ) {
541                         as->as_syntax = AS_SYNTAX_RFC822_EXT;
542                 } else if ( !strcasecmp( p, "dn" ) ) {
543                         as->as_syntax = AS_SYNTAX_DN;
544                 } else if ( !strcasecmp( p, "url" ) ) {
545                         as->as_syntax = AS_SYNTAX_URL;
546                 } else if ( !strcasecmp( p, "search-with-filter" ) ) {
547                         as->as_syntax = AS_SYNTAX_BOOL_FILTER;
548                 } else if ( !strncasecmp( p, "param=", 6 ) ) {
549                         q = strchr( p, '=' );
550                         if ( q ) {
551                                 p = q + 1;
552                                 while ( *q && !isspace( *q ) ) {
553                                         q++;
554                                 }
555                                 if ( *q ) {
556                                         *q = '\0';
557                                         as->as_param = strdup( p );
558                                         p = q + 1;
559                                 } else {
560                                         as->as_param = strdup( p );
561                                         p = q;
562                                 }
563                         }
564                 } else if ( !strcasecmp( p, "host" ) ) {
565                         as->as_kind = AS_SYNTAX_HOST;
566                 } else if ( !strcasecmp( p, "present" ) ) {
567                         as->as_kind = AS_SYNTAX_PRESENT;
568                 } else if ( !strcasecmp( p, "route-to-host" ) ) {
569                         as->as_kind = AS_KIND_ROUTE_TO_HOST;
570                 } else if ( !strcasecmp( p, "route-to-address" ) ) {
571                         as->as_kind = AS_KIND_ROUTE_TO_ADDR;
572                 } else if ( !strcasecmp( p, "own-address" ) ) {
573                         as->as_kind = AS_KIND_OWN_ADDR;
574                 } else if ( !strcasecmp( p, "recipient" ) ) {
575                         as->as_kind = AS_KIND_RECIPIENT;
576                 } else if ( !strcasecmp( p, "errors" ) ) {
577                         as->as_kind = AS_KIND_ERRORS;
578                 } else if ( !strcasecmp( p, "request" ) ) {
579                         as->as_kind = AS_KIND_REQUEST;
580                 } else if ( !strcasecmp( p, "owner" ) ) {
581                         as->as_kind = AS_KIND_OWNER;
582                 } else if ( !strcasecmp( p, "delivery-type" ) ) {
583                         as->as_kind = AS_KIND_DELIVERY_TYPE;
584                 } else {
585                         syslog( LOG_ALERT,
586                                 "Unknown semantics word %s", p );
587                         exit( EX_TEMPFAIL );
588                 }
589                 p = q + 1;
590         }
591         if ( attr_semantics == NULL ) {
592                 attr_semantics = calloc(2, sizeof(AttrSemantics *));
593                 if ( !attr_semantics ) {
594                         syslog( LOG_ALERT, "Out of memory" );
595                         exit( EX_TEMPFAIL );
596                 }
597                 attr_semantics[0] = as;
598         } else {
599                 int size;
600                 AttrSemantics **list_temp;
601                 for ( size = 0; attr_semantics[size]; size++ )
602                         ;
603                 size += 2;
604                 list_temp = realloc( attr_semantics,
605                                      size*sizeof(AttrSemantics *) );
606                 if ( !list_temp ) {
607                         syslog( LOG_ALERT, "Out of memory" );
608                         exit( EX_TEMPFAIL );
609                 }
610                 attr_semantics = list_temp;
611                 attr_semantics[size-2] = as;
612                 attr_semantics[size-1] = NULL;
613         }
614 }
615
616 static void
617 load_config( char *filespec )
618 {
619         FILE            *cf;
620         char            *line;
621         int             lineno = 0;
622         char            *p;
623         int             rdnpref;
624         int             typemask;
625
626         cf = fopen( filespec, "r" );
627         if ( !cf ) {
628                 perror( "Opening config file" );
629                 exit( EX_TEMPFAIL );
630         }
631
632         while ( ( line = get_config_line( cf,&lineno ) ) ) {
633                 p = strpbrk( line, " \t" );
634                 if ( !p ) {
635                         syslog( LOG_ALERT,
636                                 "Missing space at line %d", lineno );
637                         exit( EX_TEMPFAIL );
638                 }
639                 if ( !strncmp( line, "search", p-line ) ) {
640                         p += strspn( p, " \t" );
641                         /* TBC, get these */
642                         rdnpref = 0;
643                         typemask = 0xFF;
644                         add_url( p, rdnpref, typemask );
645                 } else if ( !strncmp(line, "attribute", p-line) ) {
646                         p += strspn(p, " \t");
647                         add_attr_semantics( p );
648                 } else if ( !strncmp(line, "default-attributes", p-line) ) {
649                         p += strspn(p, " \t");
650                         add_def_attr( p );
651                 } else if ( !strncmp(line, "group-classes", p-line) ) {
652                         p += strspn(p, " \t");
653                         add_single_to( &groupclasses, p );
654                 } else if ( !strncmp(line, "priority", p-line) ) {
655                         p += strspn(p, " \t");
656                         current_priority = atoi(p);
657                 } else if ( !strncmp(line, "domain", p-line) ) {
658                         p += strspn(p, " \t");
659                         add_single_to( &mydomains, p );
660                 } else if ( !strncmp(line, "host", p-line) ) {
661                         p += strspn(p, " \t");
662                         add_single_to( &myhosts, p );
663                 } else {
664                         syslog( LOG_ALERT,
665                                 "Unparseable config definition at line %d",
666                                 lineno );
667                         exit( EX_TEMPFAIL );
668                 }
669         }
670         fclose( cf );
671 }
672
673 static int
674 connect_to_x500( void )
675 {
676         int opt;
677
678         if ( (ld = ldap_init( ldaphost, 0 )) == NULL ) {
679                 syslog( LOG_ALERT, "ldap_init failed" );
680                 return( -1 );
681         }
682
683         /*  TBC: Set this only when it makes sense
684         opt = MAIL500_MAXAMBIGUOUS;
685         ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &opt);
686         */
687         opt = LDAP_DEREF_ALWAYS;
688         ldap_set_option(ld, LDAP_OPT_DEREF, &opt);
689
690         if ( ldap_simple_bind_s( ld, NULL, NULL ) != LDAP_SUCCESS ) {
691                 syslog( LOG_ALERT, "ldap_simple_bind_s failed" );
692                 return( -1 );
693         }
694
695         return( 0 );
696 }
697
698 static Group *
699 new_group( char *dn, Group ***list, int *nlist )
700 {
701         int     i;
702         Group   *this_group;
703
704         for ( i = 0; i < *nlist; i++ ) {
705                 if ( strcmp( dn, (*list)[i]->g_dn ) == 0 ) {
706                         syslog( LOG_ALERT, "group loop 2 detected (%s)", dn );
707                         return NULL;
708                 }
709         }
710
711         this_group = (Group *) malloc( sizeof(Group) );
712
713         if ( *nlist == 0 ) {
714                 *list = (Group **) malloc( sizeof(Group *) );
715         } else {
716                 *list = (Group **) realloc( *list, (*nlist + 1) *
717                     sizeof(Group *) );
718         }
719
720         this_group->g_errorsto = NULL;
721         this_group->g_members = NULL;
722         this_group->g_nmembers = 0;
723         /* save the group's dn so we can check for loops above */
724         this_group->g_dn = strdup( dn );
725
726         (*list)[*nlist] = this_group;
727         (*nlist)++;
728
729         return( this_group );
730 }
731
732 static void
733 split_address(
734         char    *address,
735         char    **localpart,
736         char    **domainpart
737 )
738 {
739         char            *p;
740
741         if ( ( p = strrchr( address, '@' ) ) == NULL ) {
742                 *localpart = strdup( address );
743                 *domainpart = NULL;
744         } else {
745                 *localpart = malloc( p - address + 1 );
746                 strncpy( *localpart, address, p - address );
747                 (*localpart)[p - address] = '\0';
748                 p++;
749                 *domainpart = strdup( p );
750         }
751 }
752
753 static int
754 dn_search(
755         char    **dnlist, 
756         char    *address,
757         char    ***to,
758         int     *nto,
759         Group   ***togroups,
760         int     *ngroups,
761         Error   **err,
762         int     *nerr
763 )
764 {
765         int             rc;
766         int             i;
767         int             resolved = 0;
768         LDAPMessage     *res, *e;
769         struct timeval  timeout;
770
771         timeout.tv_sec = MAIL500_TIMEOUT;
772         timeout.tv_usec = 0;
773         for ( i = 0; dnlist[i]; i++ ) {
774                 if ( (rc = ldap_search_st( ld, dnlist[i], LDAP_SCOPE_BASE,
775                         "(objectclass=*)", def_attr, 0,
776                          &timeout, &res )) != LDAP_SUCCESS ) {
777                         if ( rc == LDAP_NO_SUCH_OBJECT ) {
778                                 add_error( err, nerr, E_BADMEMBER, dnlist[i], NULL );
779                                 continue;
780                         } else {
781                                 syslog( LOG_ALERT, "member search return 0x%x", rc );
782
783                                 unbind_and_exit( EX_TEMPFAIL );
784                         }
785                 } else {
786                         if ( (e = ldap_first_entry( ld, res )) == NULL ) {
787                                 syslog( LOG_ALERT, "member search error parsing entry" );
788                                 unbind_and_exit( EX_TEMPFAIL );
789                         }
790                         if ( entry_engine( e, dnlist[i], address, to, nto,
791                                            togroups, ngroups, err, nerr,
792                                            USER | GROUP_MEMBERS ) ) {
793                                 resolved = 1;
794                         }
795                 }
796         }
797         return( resolved );
798 }
799
800 static int
801 search_ldap_url(
802         char    *url,
803         Subst   *substs,
804         char    *address,
805         int     rdnpref,
806         int     multi_entry,
807         char    ***to,
808         int     *nto,
809         Group   ***togroups,
810         int     *ngroups,
811         Error   **err,
812         int     *nerr,
813         int     type
814 )
815 {
816         LDAPURLDesc     *ludp;
817         char            *p, *s, *d;
818         int             i;
819         char            filter[1024];
820         LDAPMessage     *e, *res;
821         int             rc;
822         char            **attrlist;
823         struct timeval  timeout;
824         int             match;
825         int             resolved = 0;
826         char            *dn;
827
828         timeout.tv_sec = MAIL500_TIMEOUT;
829         timeout.tv_usec = 0;
830
831         rc = ldap_url_parse( url, &ludp );
832         if ( rc ) {
833                 switch ( rc ) {
834                 case LDAP_URL_ERR_NOTLDAP:
835                         syslog( LOG_ALERT,
836                                 "Not an LDAP URL: %s", url );
837                         break;
838                 case LDAP_URL_ERR_BADENCLOSURE:
839                         syslog( LOG_ALERT,
840                                 "Bad Enclosure in URL: %s", url );
841                         break;
842                 case LDAP_URL_ERR_BADURL:
843                         syslog( LOG_ALERT,
844                                 "Bad URL: %s", url );
845                         break;
846                 case LDAP_URL_ERR_BADHOST:
847                         syslog( LOG_ALERT,
848                                 "Host is invalid in URL: %s", url );
849                         break;
850                 case LDAP_URL_ERR_BADATTRS:
851                         syslog( LOG_ALERT,
852                                 "Attributes are invalid in URL: %s", url );
853                         break;
854                 case LDAP_URL_ERR_BADSCOPE:
855                         syslog( LOG_ALERT,
856                                 "Scope is invalid in URL: %s", url );
857                         break;
858                 case LDAP_URL_ERR_BADFILTER:
859                         syslog( LOG_ALERT,
860                                 "Filter is invalid in URL: %s", url );
861                         break;
862                 case LDAP_URL_ERR_BADEXTS:
863                         syslog( LOG_ALERT,
864                                 "Extensions are invalid in URL: %s", url );
865                         break;
866                 case LDAP_URL_ERR_MEM:
867                         syslog( LOG_ALERT,
868                                 "Out of memory parsing URL: %s", url );
869                         break;
870                 case LDAP_URL_ERR_PARAM:
871                         syslog( LOG_ALERT,
872                                 "bad parameter parsing URL: %s", url );
873                         break;
874                 default:
875                         syslog( LOG_ALERT,
876                                 "Unknown error %d parsing URL: %s",
877                                 rc, url );
878                         break;
879                 }
880                 add_error( err, nerr, E_BADMEMBER,
881                            url, NULL );
882                 return 0;
883         }
884
885         if ( substs ) {
886                 for ( s = ludp->lud_filter, d = filter; *s; s++,d++ ) {
887                         if ( *s == '%' ) {
888                                 s++;
889                                 if ( *s == '%' ) {
890                                         *d = '%';
891                                         continue;
892                                 }
893                                 for ( i = 0; substs[i].sub_char != '\0';
894                                       i++ ) {
895                                         if ( *s == substs[i].sub_char ) {
896                                                 for ( p = substs[i].sub_value;
897                                                       *p; p++,d++ ) {
898                                                         *d = *p;
899                                                 }
900                                                 d--;
901                                                 break;
902                                         }
903                                 }
904                                 if ( substs[i].sub_char == '\0' ) {
905                                         syslog( LOG_ALERT,
906                                                 "unknown format %c", *s );
907                                 }
908                         } else {
909                                 *d = *s;
910                         }
911                 }
912                 *d = *s;
913         } else {
914                 strncpy( filter, ludp->lud_filter, sizeof( filter ) - 1 );
915                 filter[ sizeof( filter ) - 1 ] = '\0';
916         }
917
918         if ( ludp->lud_attrs ) {
919                 attrlist = ludp->lud_attrs;
920         } else {
921                 attrlist = def_attr;
922         }
923         res = NULL;
924         /* TBC: we don't read the host, dammit */
925         rc = ldap_search_st( ld, ludp->lud_dn, ludp->lud_scope,
926                              filter, attrlist, 0,
927                              &timeout, &res );
928
929         /* some other trouble - try again later */
930         if ( rc != LDAP_SUCCESS &&
931              rc != LDAP_SIZELIMIT_EXCEEDED ) {
932                 syslog( LOG_ALERT, "return 0x%x from X.500",
933                         rc );
934                 unbind_and_exit( EX_TEMPFAIL );
935         }
936
937         match = ldap_count_entries( ld, res );
938
939         /* trouble - try again later */
940         if ( match == -1 ) {
941                 syslog( LOG_ALERT, "error parsing result from X.500" );
942                 unbind_and_exit( EX_TEMPFAIL );
943         }
944
945         if ( match == 1 || multi_entry ) {
946                 for ( e = ldap_first_entry( ld, res ); e != NULL;
947                       e = ldap_next_entry( ld, e ) ) {
948                         dn = ldap_get_dn( ld, e );
949                         resolved = entry_engine( e, dn, address, to, nto,
950                                                  togroups, ngroups,
951                                                  err, nerr, type );
952                         if ( !resolved ) {
953                                 add_error( err, nerr, E_NOEMAIL, address, res );
954                         }
955                 }
956                 return ( resolved );
957         }
958
959         /* more than one match - bounce with ambiguous user? */
960         if ( match > 1 ) {
961                 LDAPMessage     *next, *tmpres = NULL;
962                 char            *dn;
963                 char            **xdn;
964
965                 /* not giving rdn preference - bounce with ambiguous user */
966                 if ( rdnpref == 0 ) {
967                         add_error( err, nerr, E_AMBIGUOUS, address, res );
968                         return 0;
969                 }
970
971                 /*
972                  * giving rdn preference - see if any entries were matched
973                  * because of their rdn.  If so, collect them to deal with
974                  * later (== 1 we deliver, > 1 we bounce).
975                  */
976
977                 for ( e = ldap_first_entry( ld, res ); e != NULL; e = next ) {
978                         next = ldap_next_entry( ld, e );
979                         dn = ldap_get_dn( ld, e );
980                         xdn = ldap_explode_dn( dn, 1 );
981
982                         /* XXX bad, but how else can we do it? XXX */
983                         if ( strcasecmp( xdn[0], address ) == 0 ) {
984                                 ldap_delete_result_entry( &res, e );
985                                 ldap_add_result_entry( &tmpres, e );
986                         }
987
988                         ldap_value_free( xdn );
989                         free( dn );
990                 }
991
992                 /* nothing matched by rdn - go ahead and bounce */
993                 if ( tmpres == NULL ) {
994                         add_error( err, nerr, E_AMBIGUOUS, address, res );
995                         return 0;
996
997                 /* more than one matched by rdn - bounce with rdn matches */
998                 } else if ( (match = ldap_count_entries( ld, tmpres )) > 1 ) {
999                         add_error( err, nerr, E_AMBIGUOUS, address, tmpres );
1000                         return 0;
1001
1002                 /* trouble... */
1003                 } else if ( match < 0 ) {
1004                         syslog( LOG_ALERT, "error parsing result from X.500" );
1005                         unbind_and_exit( EX_TEMPFAIL );
1006                 }
1007
1008                 /* otherwise one matched by rdn - send to it */
1009                 ldap_msgfree( res );
1010                 res = tmpres;
1011
1012                 /* trouble */
1013                 if ( (e = ldap_first_entry( ld, res )) == NULL ) {
1014                         syslog( LOG_ALERT, "error parsing entry from X.500" );
1015                         unbind_and_exit( EX_TEMPFAIL );
1016                 }
1017
1018                 dn = ldap_get_dn( ld, e );
1019
1020                 resolved = entry_engine( e, dn, address, to, nto,
1021                                          togroups, ngroups,
1022                                          err, nerr, type );
1023                 if ( !resolved ) {
1024                         add_error( err, nerr, E_NOEMAIL, address, res );
1025                         /* Don't free res if we passed it to add_error */
1026                 } else {
1027                         ldap_msgfree( res );
1028                 }
1029         }
1030         return( resolved );
1031 }
1032
1033 static int
1034 url_list_search(
1035         char    **urllist, 
1036         char    *address,
1037         int     multi_entry,
1038         char    ***to,
1039         int     *nto,
1040         Group   ***togroups,
1041         int     *ngroups,
1042         Error   **err,
1043         int     *nerr,
1044         int     type
1045 )
1046 {
1047         int             i;
1048         int             resolved = 0;
1049
1050         for ( i = 0; urllist[i]; i++ ) {
1051
1052                 if ( !strncasecmp( urllist[i], "mail:", 5 ) ) {
1053                         char    *vals[2];
1054
1055                         vals[0] = urllist[i] + 5;
1056                         vals[1] = NULL;
1057                         add_to( to, nto, vals );
1058                         resolved = 1;
1059
1060                 } else if ( ldap_is_ldap_url( urllist[i] ) ) {
1061
1062                         resolved = search_ldap_url( urllist[i], NULL,
1063                                                     address, 0, multi_entry,
1064                                                     to, nto, togroups, ngroups,
1065                                                     err, nerr, type );
1066                 } else {
1067                         /* Produce some sensible error here */
1068                         resolved = 0;
1069                 }
1070         }
1071         return( resolved );
1072 }
1073
1074 /*
1075  * We should probably take MX records into account to cover all bases,
1076  * but really, routing belongs in the MTA.
1077  */
1078 static int
1079 is_my_host(
1080         char * host
1081 )
1082 {
1083         char **d;
1084
1085         if ( d == NULL )
1086                 return 0;
1087         for ( d = myhosts; *d; d++ ) {
1088                 if ( !strcasecmp(*d,host) ) {
1089                         return 1;
1090                 }
1091         }
1092         return 0;
1093 }
1094
1095 static int
1096 is_my_domain(
1097         char * address
1098 )
1099 {
1100         char **d;
1101         char *p;
1102
1103         if ( d == NULL )
1104                 return 0;
1105         p = strchr( address, '@' );
1106         if ( p == NULL)
1107                 return 0;
1108         for ( d = mydomains; *d; d++ ) {
1109                 if ( !strcasecmp(*d,p+1) ) {
1110                         return 1;
1111                 }
1112         }
1113         return 0;
1114 }
1115
1116 static void
1117 do_addresses(
1118         char    **addresses,
1119         char    ***to,
1120         int     *nto,
1121         Group   ***togroups,
1122         int     *ngroups,
1123         Error   **err,
1124         int     *nerr,
1125         int     type
1126 )
1127 {
1128         int     i, j;
1129         int     n;
1130
1131         /*
1132          * Well, this is tricky, every address in my_addresses will be
1133          * removed from the list while we shift the other values down
1134          * and we do it in a single scan of the address list and
1135          * without using additional memory.  We are going to be
1136          * modifying the value list in a way that the later
1137          * ldap_value_free works.
1138          */
1139         j = 0;
1140         for ( i = 0; addresses[i]; i++ ) {
1141                 if ( is_my_domain(addresses[i]) ) {
1142                         do_address( addresses[i], to, nto, togroups, ngroups,
1143                                     err, nerr, type );
1144                         ldap_memfree( addresses[i] );
1145                 } else {
1146                         if ( j < i ) {
1147                                 addresses[j] = addresses[i];
1148                         }
1149                         j++;
1150                 }
1151         }
1152         addresses[j] = NULL;
1153         if ( addresses[0] ) {
1154                 add_to( to, nto, addresses );
1155         }
1156 }
1157
1158 /*
1159  * The entry engine processes an entry.  Normally, each entry will resolve
1160  * to one or more values that will be added to the 'to' argument.  This
1161  * argument needs not be the global 'to' list, it may be the g_to field
1162  * in a group.  Groups have no special treatment, unless they require
1163  * a special sender.
1164  */
1165
1166 static int
1167 entry_engine(
1168         LDAPMessage *e,
1169         char    *dn,
1170         char    *address,
1171         char    ***to,
1172         int     *nto,
1173         Group   ***togroups,
1174         int     *ngroups,
1175         Error   **err,
1176         int     *nerr,
1177         int     type
1178 )
1179 {
1180         char    **vals;
1181         int     i;
1182         int     resolved = 0;
1183         char    ***current_to = to;
1184         int     *current_nto = nto;
1185         Group   *current_group = NULL;
1186         char    buf[1024];
1187         char    *localpart = NULL, *domainpart = NULL;
1188         Subst   substs[2];
1189         int     cur_priority = 0;
1190         char    *route_to_host = NULL;
1191         char    *route_to_address = NULL;
1192         int     needs_mta_routing = 0;
1193         char    **own_addresses = NULL;
1194         int     own_addresses_total = 0;
1195         char    **delivery_types = NULL;
1196         int     delivery_types_total = 0;
1197         char    *nvals[2];
1198
1199         for ( i=0; attr_semantics[i] != NULL; i++ ) {
1200                 AttrSemantics   *as = attr_semantics[i];
1201                 int             nent;
1202                 int             j;
1203
1204                 if ( as->as_priority < cur_priority ) {
1205                         /*
1206                          * We already got higher priority information,
1207                          * so no further work to do, ignore the rest.
1208                          */
1209                         break;
1210                 }
1211                 vals = ldap_get_values( ld, e, as->as_name );
1212                 if ( !vals || vals[0] == NULL ) {
1213                         continue;
1214                 }
1215                 nent = count_values( vals );
1216                 if ( nent > 1 && !as->as_m_valued ) {
1217                         add_error( err, nerr, E_AMBIGUOUS, address, e );
1218                         return( 0 );
1219                 }
1220                 switch ( as->as_kind ) {
1221                 case AS_KIND_RECIPIENT:
1222                         cur_priority = as->as_priority;
1223                         if ( ! ( type & ( USER | GROUP_MEMBERS ) ) )
1224                                 break;
1225                         switch ( as->as_syntax ) {
1226                         case AS_SYNTAX_RFC822:
1227                                 do_addresses( vals, current_to, current_nto,
1228                                               togroups, ngroups, err, nerr,
1229                                               USER );
1230                                 resolved = 1;
1231                                 break;
1232                         case AS_SYNTAX_RFC822_EXT:
1233                                 add_to( current_to, current_nto, vals );
1234                                 resolved = 1;
1235                                 break;
1236                         case AS_SYNTAX_NATIVE_MB:
1237                                 /* We used to concatenate mailHost if set here */
1238                                 /*
1239                                  * We used to send a copy to the vacation host
1240                                  * if onVacation to uid@vacationhost
1241                                  */
1242                                 if ( as->as_param ) {
1243                                         for ( j=0; j<delivery_types_total; j++ ) {
1244                                                 if ( !strcasecmp( as->as_param, delivery_types[j] ) ) {
1245                                                         add_to( current_to, current_nto, vals );
1246                                                         resolved = 1;
1247                                                         break;
1248                                                 }
1249                                         }
1250                                 } else {
1251                                         add_to( current_to, current_nto, vals );
1252                                         resolved = 1;
1253                                 }
1254                                 break;
1255
1256                         case AS_SYNTAX_DN:
1257                                 if ( dn_search( vals, address,
1258                                                 current_to, current_nto,
1259                                                 togroups, ngroups,
1260                                                 err, nerr ) ) {
1261                                         resolved = 1;
1262                                 }
1263                                 break;
1264
1265                         case AS_SYNTAX_URL:
1266                                 if ( url_list_search( vals, address,
1267                                                  as->as_m_entries,
1268                                                  current_to, current_nto,
1269                                                  togroups, ngroups,
1270                                                  err, nerr, type ) ) {
1271                                         resolved = 1;
1272                                 }
1273                                 break;
1274
1275                         case AS_SYNTAX_BOOL_FILTER:
1276                                 if ( strcasecmp( vals[0], "true" ) ) {
1277                                         break;
1278                                 }
1279                                 substs[0].sub_char = 'D';
1280                                 substs[0].sub_value = dn;
1281                                 substs[1].sub_char = '\0';
1282                                 substs[1].sub_value = NULL;
1283                                 if ( url_list_search( vals, address,
1284                                                  as->as_m_entries,
1285                                                  current_to, current_nto,
1286                                                  togroups, ngroups,
1287                                                  err, nerr, type ) ) {
1288                                         resolved = 1;
1289                                 }
1290                                 break;
1291
1292                         default:
1293                                 syslog( LOG_ALERT,
1294                                         "Invalid syntax %d for kind %d",
1295                                         as->as_syntax, as->as_kind );
1296                                 break;
1297                         }
1298                         break;
1299
1300                 case AS_KIND_ERRORS:
1301                         cur_priority = as->as_priority;
1302                         /* This is a group with special processing */
1303                         if ( type & GROUP_ERRORS ) {
1304                                 switch (as->as_kind) {
1305                                 case AS_SYNTAX_RFC822:
1306                                         add_to( current_to, current_nto, vals );
1307                                         resolved = 1;
1308                                         break;
1309                                 case AS_SYNTAX_URL:
1310                                 default:
1311                                         syslog( LOG_ALERT,
1312                                                 "Invalid syntax %d for kind %d",
1313                                                 as->as_syntax, as->as_kind );
1314                                 }
1315                         } else {
1316                                 current_group = new_group( dn, togroups,
1317                                                            ngroups );
1318                                 current_to = &current_group->g_members;
1319                                 current_nto = &current_group->g_nmembers;
1320                                 split_address( address,
1321                                                &localpart, &domainpart );
1322                                 if ( domainpart ) {
1323                                         sprintf( buf, "%s-%s@%s",
1324                                                  localpart, ERRORS,
1325                                                  domainpart );
1326                                         free( localpart );
1327                                         free( domainpart );
1328                                 } else {
1329                                         sprintf( buf, "%s-%s@%s",
1330                                                  localpart, ERRORS,
1331                                                  host );
1332                                         free( localpart );
1333                                 }
1334                                 current_group->g_errorsto = strdup( buf );
1335                         }
1336                         break;
1337
1338                 case AS_KIND_REQUEST:
1339                         cur_priority = as->as_priority;
1340                         /* This is a group with special processing */
1341                         if ( type & GROUP_REQUEST ) {
1342                                 add_to( current_to, current_nto, vals );
1343                                 resolved = 1;
1344                         }
1345                         break;
1346
1347                 case AS_KIND_OWNER:
1348                         cur_priority = as->as_priority;
1349                         /* This is a group with special processing */
1350                         if ( type & GROUP_REQUEST ) {
1351                                 add_to( current_to, current_nto, vals );
1352                                 resolved = 1;
1353                         }
1354                         break;
1355
1356                 case AS_KIND_ROUTE_TO_HOST:
1357                         if ( !is_my_host( vals[0] ) ) {
1358                                 cur_priority = as->as_priority;
1359                                 if ( as->as_syntax == AS_SYNTAX_PRESENT ) {
1360                                         needs_mta_routing = 1;
1361                                 } else {
1362                                         route_to_host = strdup( vals[0] );
1363                                 }
1364                         }
1365                         break;
1366
1367                 case AS_KIND_ROUTE_TO_ADDR:
1368                         for ( j=0; j<own_addresses_total; j++ ) {
1369                                 if ( strcasecmp( vals[0], own_addresses[j] ) ) {
1370                                         cur_priority = as->as_priority;
1371                                         if ( as->as_syntax == AS_SYNTAX_PRESENT ) {
1372                                                 needs_mta_routing = 1;
1373                                         } else {
1374                                                 route_to_address = strdup( vals[0] );
1375                                         }
1376                                 }
1377                                 break;
1378                         }
1379
1380                 case AS_KIND_OWN_ADDR:
1381                         add_to( &own_addresses, &own_addresses_total, vals );
1382                         cur_priority = as->as_priority;
1383                         break;
1384
1385                 case AS_KIND_DELIVERY_TYPE:
1386                         add_to( &delivery_types, &delivery_types_total, vals );
1387                         cur_priority = as->as_priority;
1388                         break;
1389
1390                 default:
1391                         syslog( LOG_ALERT,
1392                                 "Invalid kind %d", as->as_kind );
1393                         /* Error, TBC */
1394                 }
1395                 ldap_value_free( vals );
1396         }
1397         /*
1398          * Now check if we are dealing with mail routing.  We support
1399          * two modes.
1400          *
1401          * The first mode and by far the most robust method is doing
1402          * routing at the MTA.  In this case, we just checked if the
1403          * routing attributes were present and did not seem like
1404          * pointing to ourselves.  The only thing we have to do here
1405          * is adding to the recipient list any of the RFC822 addresses
1406          * of this entry.  That means we needed to retrieve them from
1407          * the entry itself because we might have arrived here through
1408          * some directory search.  The address received as argument is
1409          * not the address of the entry we are processing, but rather
1410          * the RFC822 address we are expanding now.  Unfortunately,
1411          * this requires an MTA that understands LDAP routing.
1412          * Sendmail 8.10.0 does, if compiled properly.
1413          *
1414          * The second method, that is most emphatically not recommended
1415          * is routing in mail500.  This is going to require using the
1416          * percent hack.  Moreover, this may occasionally loop.
1417          */
1418         if ( needs_mta_routing ) {
1419                 if ( !own_addresses ) {
1420                         add_error( err, nerr, E_NOOWNADDRESS, address, e );
1421                         return( 0 );
1422                 }
1423                 nvals[0] = own_addresses[0];    /* Anyone will do */
1424                 nvals[1] = NULL;
1425                 add_to( current_to, current_nto, nvals );
1426                 resolved = 1;
1427         } else if ( route_to_host ) {
1428                 char *p;
1429                 if ( !route_to_address ) {
1430                         if ( !own_addresses ) {
1431                                 add_error( err, nerr, E_NOOWNADDRESS, address, e );
1432                                 return( 0 );
1433                         }
1434                         route_to_address = strdup( own_addresses[0] );
1435                 }
1436                 /* This makes use of the percent hack, but there's no choice */
1437                 p = strchr( route_to_address, '@' );
1438                 if ( p ) {
1439                         *p = '%';
1440                 }
1441                 sprintf( buf, "%s@%s", route_to_address, route_to_host );
1442                 nvals[0] = buf;
1443                 nvals[1] = NULL;
1444                 add_to( current_to, current_nto, nvals );
1445                 resolved = 1;
1446                 free( route_to_host );
1447                 free( route_to_address );
1448         } else if ( route_to_address ) {
1449                 nvals[0] = route_to_address;
1450                 nvals[1] = NULL;
1451                 add_to( current_to, current_nto, nvals );
1452                 resolved = 1;
1453                 free( route_to_address );
1454         }
1455         if ( own_addresses ) {
1456                 ldap_value_free( own_addresses );
1457         }
1458         if ( delivery_types ) {
1459                 ldap_value_free( delivery_types );
1460         }
1461                   
1462         return( resolved );
1463 }
1464
1465 static int
1466 search_bases(
1467         char    *filter,
1468         Subst   *substs,
1469         char    *name,
1470         char    ***to,
1471         int     *nto,
1472         Group   ***togroups,
1473         int     *ngroups,
1474         Error   **err,
1475         int     *nerr,
1476         int     type
1477 )
1478 {
1479         int             b, resolved = 0;
1480
1481         for ( b = 0; base[b] != NULL; b++ ) {
1482
1483                 if ( ! (base[b]->b_search & type) ) {
1484                         continue;
1485                 }
1486
1487                 resolved = search_ldap_url( base[b]->b_url, substs, name,
1488                                             base[b]->b_rdnpref,
1489                                             base[b]->b_m_entries,
1490                                             to, nto, togroups, ngroups,
1491                                             err, nerr, type );
1492                 if ( resolved )
1493                         break;
1494         }
1495         return( resolved );
1496 }
1497
1498 static void
1499 do_address(
1500         char    *name,
1501         char    ***to,
1502         int     *nto,
1503         Group   ***togroups,
1504         int     *ngroups,
1505         Error   **err,
1506         int     *nerr,
1507         int     type
1508 )
1509 {
1510         char            *localpart = NULL, *domainpart = NULL;
1511         char            *synthname = NULL;
1512         int             resolved;
1513         int             i;
1514         Subst           substs[6];
1515
1516         /*
1517          * Look up the name in X.500, add the appropriate addresses found
1518          * to the to list, or to the err list in case of error.  Groups are
1519          * handled by the do_group routine, individuals are handled here.
1520          * When looking up name, we follow the bases hierarchy, looking
1521          * in base[0] first, then base[1], etc.  For each base, there is
1522          * a set of search filters to try, in order.  If something goes
1523          * wrong here trying to contact X.500, we exit with EX_TEMPFAIL.
1524          * If the b_rdnpref flag is set, then we give preference to entries
1525          * that matched name because it's their rdn, otherwise not.
1526          */
1527
1528         split_address( name, &localpart, &domainpart );
1529         synthname = strdup( localpart );
1530         for ( i = 0; synthname[i] != '\0'; i++ ) {
1531                 if ( synthname[i] == '.' || synthname[i] == '_' )
1532                         synthname[i] = ' ';
1533         }
1534         substs[0].sub_char = 'm';
1535         substs[0].sub_value = name;
1536         substs[1].sub_char = 'h';
1537         substs[1].sub_value = host;
1538         substs[2].sub_char = 'l';
1539         substs[2].sub_value = localpart;
1540         substs[3].sub_char = 'd';
1541         substs[3].sub_value = domainpart;
1542         substs[4].sub_char = 's';
1543         substs[4].sub_value = synthname;
1544         substs[5].sub_char = '\0';
1545         substs[5].sub_value = NULL;
1546
1547         resolved = search_bases( NULL, substs, name,
1548                                  to, nto, togroups, ngroups,
1549                                  err, nerr, type );
1550
1551         if ( localpart ) {
1552                 free( localpart );
1553         }
1554         if ( domainpart ) {
1555                 free( domainpart );
1556         }
1557         if ( synthname ) {
1558                 free( synthname );
1559         }
1560
1561         if ( !resolved ) {
1562                 /* not resolved - bounce with user unknown */
1563                 if ( type == USER ) {
1564                         add_error( err, nerr, E_USERUNKNOWN, name, NULL );
1565                 } else {
1566                         add_error( err, nerr, E_GROUPUNKNOWN, name, NULL );
1567                 }
1568         }
1569 }
1570
1571 static void
1572 send_message( char **to )
1573 {
1574         int     pid;
1575 #ifndef HAVE_WAITPID
1576         WAITSTATUSTYPE  status;
1577 #endif
1578
1579         if ( debug ) {
1580                 char    buf[1024];
1581                 int     i;
1582
1583                 strcpy( buf, to[0] );
1584                 for ( i = 1; to[i] != NULL; i++ ) {
1585                         strcat( buf, " " );
1586                         strcat( buf, to[i] );
1587                 }
1588
1589                 syslog( LOG_ALERT, "send_message execing sendmail: (%s)", buf );
1590         }
1591
1592         /* parent */
1593         if ( (pid = fork()) != 0 ) {
1594 #ifdef HAVE_WAITPID
1595                 waitpid( pid, (int *) NULL, 0 );
1596 #else
1597                 wait4( pid, &status, WAIT_FLAGS, 0 );
1598 #endif
1599         /* child */
1600         } else {
1601                 /* to includes sendmailargs */
1602                 execv( MAIL500_SENDMAIL, to );
1603
1604                 syslog( LOG_ALERT, "execv failed" );
1605
1606                 exit( EX_TEMPFAIL );
1607         }
1608 }
1609
1610 static void
1611 send_group( Group **group, int ngroup )
1612 {
1613         int     i, pid;
1614         char    **argv;
1615         int     argc;
1616         char    *iargv[7];
1617 #ifndef HAVE_WAITPID
1618         WAITSTATUSTYPE  status;
1619 #endif
1620
1621         for ( i = 0; i < ngroup; i++ ) {
1622                 (void) rewind( stdin );
1623
1624                 iargv[0] = MAIL500_SENDMAIL;
1625                 iargv[1] = "-f";
1626                 iargv[2] = group[i]->g_errorsto;
1627                 iargv[3] = "-oMrX.500";
1628                 iargv[4] = "-odi";
1629                 iargv[5] = "-oi";
1630                 iargv[6] = NULL;
1631
1632                 argv = NULL;
1633                 argc = 0;
1634                 add_to( &argv, &argc, iargv );
1635                 add_to( &argv, &argc, group[i]->g_members );
1636
1637                 if ( debug ) {
1638                         char    buf[1024];
1639                         int     i;
1640
1641                         strcpy( buf, argv[0] );
1642                         for ( i = 1; i < argc; i++ ) {
1643                                 strcat( buf, " " );
1644                                 strcat( buf, argv[i] );
1645                         }
1646
1647                         syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
1648                 }
1649
1650                 /* parent */
1651                 if ( (pid = fork()) != 0 ) {
1652 #ifdef HAVE_WAITPID
1653                         waitpid( pid, (int *) NULL, 0 );
1654 #else
1655                         wait4( pid, &status, WAIT_FLAGS, 0 );
1656 #endif
1657                 /* child */
1658                 } else {
1659                         execv( MAIL500_SENDMAIL, argv );
1660
1661                         syslog( LOG_ALERT, "execv failed" );
1662
1663                         exit( EX_TEMPFAIL );
1664                 }
1665         }
1666 }
1667
1668 static void
1669 send_errors( Error *err, int nerr )
1670 {
1671         int     pid, i, namelen;
1672         FILE    *fp;
1673         int     fd[2];
1674         char    *argv[8];
1675         char    buf[1024];
1676 #ifndef HAVE_WAITPID
1677         WAITSTATUSTYPE  status;
1678 #endif
1679
1680         if ( strcmp( MAIL500_BOUNCEFROM, mailfrom ) == 0 ) {
1681             mailfrom = errorsfrom;
1682         }
1683
1684         argv[0] = MAIL500_SENDMAIL;
1685         argv[1] = "-oMrX.500";
1686         argv[2] = "-odi";
1687         argv[3] = "-oi";
1688         argv[4] = "-f";
1689         argv[5] = MAIL500_BOUNCEFROM;
1690         argv[6] = mailfrom;
1691         argv[7] = NULL;
1692
1693         if ( debug ) {
1694                 int     i;
1695
1696                 strcpy( buf, argv[0] );
1697                 for ( i = 1; argv[i] != NULL; i++ ) {
1698                         strcat( buf, " " );
1699                         strcat( buf, argv[i] );
1700                 }
1701
1702                 syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
1703         }
1704
1705         if ( pipe( fd ) == -1 ) {
1706                 syslog( LOG_ALERT, "cannot create pipe" );
1707                 exit( EX_TEMPFAIL );
1708         }
1709
1710         if ( (pid = fork()) != 0 ) {
1711                 if ( (fp = fdopen( fd[1], "w" )) == NULL ) {
1712                         syslog( LOG_ALERT, "cannot fdopen pipe" );
1713                         exit( EX_TEMPFAIL );
1714                 }
1715
1716                 fprintf( fp, "To: %s\n", mailfrom );
1717                 fprintf( fp, "From: %s\n", errorsfrom );
1718                 fprintf( fp, "Subject: undeliverable mail\n" );
1719                 fprintf( fp, "\n" );
1720                 fprintf( fp, "The following errors occurred when trying to deliver the attached mail:\n" );
1721                 for ( i = 0; i < nerr; i++ ) {
1722                         namelen = strlen( err[i].e_addr );
1723                         fprintf( fp, "\n" );
1724
1725                         switch ( err[i].e_code ) {
1726                         case E_USERUNKNOWN:
1727                                 fprintf( fp, "%s: User unknown\n", err[i].e_addr );
1728                                 break;
1729
1730                         case E_GROUPUNKNOWN:
1731                                 fprintf( fp, "%s: Group unknown\n", err[i].e_addr );
1732                                 break;
1733
1734                         case E_BADMEMBER:
1735                                 fprintf( fp, "%s: Group member does not exist\n",
1736                                     err[i].e_addr );
1737                                 fprintf( fp, "This could be because the distinguished name of the person has changed\n" );
1738                                 fprintf( fp, "If this is the case, the problem can be solved by removing and\n" );
1739                                 fprintf( fp, "then re-adding the person to the group.\n" );
1740                                 break;
1741
1742                         case E_NOREQUEST:
1743                                 fprintf( fp, "%s: Group exists but has no request address\n",
1744                                     err[i].e_addr );
1745                                 break;
1746
1747                         case E_NOERRORS:
1748                                 fprintf( fp, "%s: Group exists but has no errors-to address\n",
1749                                     err[i].e_addr );
1750                                 break;
1751
1752                         case E_NOOWNER:
1753                                 fprintf( fp, "%s: Group exists but has no owner\n",
1754                                     err[i].e_addr );
1755                                 break;
1756
1757                         case E_AMBIGUOUS:
1758                                 do_ambiguous( fp, &err[i], namelen );
1759                                 break;
1760
1761                         case E_NOEMAIL:
1762                                 do_noemail( fp, &err[i], namelen );
1763                                 break;
1764
1765                         case E_MEMBERNOEMAIL:
1766                                 fprintf( fp, "%s: Group member exists but does not have an email address\n",
1767                                     err[i].e_addr );
1768                                 break;
1769
1770                         case E_JOINMEMBERNOEMAIL:
1771                                 fprintf( fp, "%s: User has joined group but does not have an email address\n",
1772                                     err[i].e_addr );
1773                                 break;
1774
1775                         case E_LOOP:
1776                                 fprintf( fp, "%s: User has created a mail loop by adding address %s to their X.500 entry\n",
1777                                     err[i].e_addr, err[i].e_loop );
1778                                 break;
1779
1780                         case E_NOMEMBERS:
1781                                 fprintf( fp, "%s: Group has no members\n",
1782                                     err[i].e_addr );
1783                                 break;
1784
1785                         case E_NOOWNADDRESS:
1786                                 fprintf( fp, "%s: Not enough information to perform required routing\n",
1787                                     err[i].e_addr );
1788                                 break;
1789
1790                         default:
1791                                 syslog( LOG_ALERT, "unknown error %d", err[i].e_code );
1792                                 unbind_and_exit( EX_TEMPFAIL );
1793                                 break;
1794                         }
1795                 }
1796
1797                 fprintf( fp, "\n------- The original message sent:\n\n" );
1798
1799                 while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
1800                         fputs( buf, fp );
1801                 }
1802                 fclose( fp );
1803
1804 #ifdef HAVE_WAITPID
1805                 waitpid( pid, (int *) NULL, 0 );
1806 #else
1807                 wait4( pid, &status, WAIT_FLAGS, 0 );
1808 #endif
1809         } else {
1810                 dup2( fd[0], 0 );
1811
1812                 execv( MAIL500_SENDMAIL, argv );
1813
1814                 syslog( LOG_ALERT, "execv failed" );
1815
1816                 exit( EX_TEMPFAIL );
1817         }
1818 }
1819
1820 static void
1821 do_noemail( FILE *fp, Error *err, int namelen )
1822 {
1823         int             i, last;
1824         char            *dn, *rdn;
1825         char            **ufn, **vals;
1826
1827         fprintf(fp, "%s: User has no email address registered.\n",
1828             err->e_addr );
1829         fprintf( fp, "%*s  Name, title, postal address and phone for '%s':\n\n",
1830             namelen, " ", err->e_addr );
1831
1832         /* name */
1833         dn = ldap_get_dn( ld, err->e_msg );
1834         ufn = ldap_explode_dn( dn, 1 );
1835         rdn = strdup( ufn[0] );
1836         if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1837                 if ( (vals = ldap_get_values( ld, err->e_msg, "cn" ))
1838                     != NULL ) {
1839                         for ( i = 0; vals[i]; i++ ) {
1840                                 last = strlen( vals[i] ) - 1;
1841                                 if ( isdigit((unsigned char) vals[i][last]) ) {
1842                                         rdn = strdup( vals[i] );
1843                                         break;
1844                                 }
1845                         }
1846
1847                         ldap_value_free( vals );
1848                 }
1849         }
1850         fprintf( fp, "%*s  %s\n", namelen, " ", rdn );
1851         free( dn );
1852         free( rdn );
1853         ldap_value_free( ufn );
1854
1855         /* titles or descriptions */
1856         if ( (vals = ldap_get_values( ld, err->e_msg, "title" )) == NULL &&
1857             (vals = ldap_get_values( ld, err->e_msg, "description" ))
1858             == NULL ) {
1859                 fprintf( fp, "%*s  No title or description registered\n",
1860                     namelen, " " );
1861         } else {
1862                 for ( i = 0; vals[i] != NULL; i++ ) {
1863                         fprintf( fp, "%*s  %s\n", namelen, " ", vals[i] );
1864                 }
1865
1866                 ldap_value_free( vals );
1867         }
1868
1869         /* postal address */
1870         if ( (vals = ldap_get_values( ld, err->e_msg, "postalAddress" ))
1871             == NULL ) {
1872                 fprintf( fp, "%*s  No postal address registered\n", namelen,
1873                     " " );
1874         } else {
1875                 fprintf( fp, "%*s  ", namelen, " " );
1876                 for ( i = 0; vals[0][i] != '\0'; i++ ) {
1877                         if ( vals[0][i] == '$' ) {
1878                                 fprintf( fp, "\n%*s  ", namelen, " " );
1879                                 while ( isspace((unsigned char) vals[0][i+1]) )
1880                                         i++;
1881                         } else {
1882                                 fprintf( fp, "%c", vals[0][i] );
1883                         }
1884                 }
1885                 fprintf( fp, "\n" );
1886
1887                 ldap_value_free( vals );
1888         }
1889
1890         /* telephone number */
1891         if ( (vals = ldap_get_values( ld, err->e_msg, "telephoneNumber" ))
1892             == NULL ) {
1893                 fprintf( fp, "%*s  No phone number registered\n", namelen,
1894                     " " );
1895         } else {
1896                 for ( i = 0; vals[i] != NULL; i++ ) {
1897                         fprintf( fp, "%*s  %s\n", namelen, " ", vals[i] );
1898                 }
1899
1900                 ldap_value_free( vals );
1901         }
1902 }
1903
1904 /* ARGSUSED */
1905 static void
1906 do_ambiguous( FILE *fp, Error *err, int namelen )
1907 {
1908         int             i, last;
1909         char            *dn, *rdn;
1910         char            **ufn, **vals;
1911         LDAPMessage     *e;
1912
1913         i = ldap_result2error( ld, err->e_msg, 0 );
1914
1915         fprintf( fp, "%s: Ambiguous user.  %s%d matches found:\n\n",
1916             err->e_addr, i == LDAP_SIZELIMIT_EXCEEDED ? "First " : "",
1917             ldap_count_entries( ld, err->e_msg ) );
1918
1919         for ( e = ldap_first_entry( ld, err->e_msg ); e != NULL;
1920             e = ldap_next_entry( ld, e ) ) {
1921                 dn = ldap_get_dn( ld, e );
1922                 ufn = ldap_explode_dn( dn, 1 );
1923                 rdn = strdup( ufn[0] );
1924                 if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1925                         if ( (vals = ldap_get_values( ld, e, "cn" )) != NULL ) {
1926                                 for ( i = 0; vals[i]; i++ ) {
1927                                         last = strlen( vals[i] ) - 1;
1928                                         if (isdigit((unsigned char) vals[i][last])) {
1929                                                 rdn = strdup( vals[i] );
1930                                                 break;
1931                                         }
1932                                 }
1933
1934                                 ldap_value_free( vals );
1935                         }
1936                 }
1937
1938                 /* 
1939                 if ( isgroup( e ) ) {
1940                         vals = ldap_get_values( ld, e, "description" );
1941                 } else {
1942                         vals = ldap_get_values( ld, e, "title" );
1943                 }
1944                 */
1945                 vals = ldap_get_values( ld, e, "description" );
1946
1947                 fprintf( fp, "    %-20s %s\n", rdn, vals ? vals[0] : "" );
1948                 for ( i = 1; vals && vals[i] != NULL; i++ ) {
1949                         fprintf( fp, "                         %s\n", vals[i] );
1950                 }
1951
1952                 free( dn );
1953                 free( rdn );
1954                 ldap_value_free( ufn );
1955                 if ( vals != NULL )
1956                         ldap_value_free( vals );
1957         }
1958 }
1959
1960 static int
1961 count_values( char **list )
1962 {
1963         int     i;
1964
1965         for ( i = 0; list && list[i] != NULL; i++ )
1966                 ;       /* NULL */
1967
1968         return( i );
1969 }
1970
1971 static void
1972 add_to( char ***list, int *nlist, char **new )
1973 {
1974         int     i, nnew, oldnlist;
1975
1976         nnew = count_values( new );
1977
1978         oldnlist = *nlist;
1979         if ( *list == NULL || *nlist == 0 ) {
1980                 *list = (char **) malloc( (nnew + 1) * sizeof(char *) );
1981                 *nlist = nnew;
1982         } else {
1983                 *list = (char **) realloc( *list, *nlist * sizeof(char *) +
1984                     nnew * sizeof(char *) + sizeof(char *) );
1985                 *nlist += nnew;
1986         }
1987
1988         for ( i = 0; i < nnew; i++ )
1989                 (*list)[i + oldnlist] = strdup( new[i] );
1990         (*list)[*nlist] = NULL;
1991 }
1992
1993 static void
1994 add_single_to( char ***list, char *new )
1995 {
1996         int     nlist;
1997
1998         if ( *list == NULL ) {
1999                 nlist = 0;
2000                 *list = (char **) malloc( 2 * sizeof(char *) );
2001         } else {
2002                 nlist = count_values( *list );
2003                 *list = (char **) realloc( *list,
2004                                            ( nlist + 2 ) * sizeof(char *) );
2005         }
2006
2007         (*list)[nlist] = strdup( new );
2008         (*list)[nlist+1] = NULL;
2009 }
2010
2011 static int
2012 isgroup( LDAPMessage *e )
2013 {
2014         int     i, j;
2015         char    **oclist;
2016
2017         if ( !groupclasses ) {
2018                 return( 0 );
2019         }
2020
2021         oclist = ldap_get_values( ld, e, "objectClass" );
2022
2023         for ( i = 0; oclist[i] != NULL; i++ ) {
2024                 for ( j = 0; groupclasses[j] != NULL; j++ ) {
2025                         if ( strcasecmp( oclist[i], groupclasses[j] ) == 0 ) {
2026                                 ldap_value_free( oclist );
2027                                 return( 1 );
2028                         }
2029                 }
2030         }
2031         ldap_value_free( oclist );
2032
2033         return( 0 );
2034 }
2035
2036 static void
2037 add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg )
2038 {
2039         if ( *nerr == 0 ) {
2040                 *err = (Error *) malloc( sizeof(Error) );
2041         } else {
2042                 *err = (Error *) realloc( *err, (*nerr + 1) * sizeof(Error) );
2043         }
2044
2045         (*err)[*nerr].e_code = code;
2046         (*err)[*nerr].e_addr = strdup( addr );
2047         (*err)[*nerr].e_msg = msg;
2048         (*nerr)++;
2049 }
2050
2051 static void
2052 unbind_and_exit( int rc )
2053 {
2054         int     i;
2055
2056         if ( (i = ldap_unbind( ld )) != LDAP_SUCCESS )
2057                 syslog( LOG_ALERT, "ldap_unbind failed %d\n", i );
2058
2059         exit( rc );
2060 }