]> git.sur5r.net Git - openldap/blob - clients/maildap/main.c
Apply Ian patch
[openldap] / clients / maildap / 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-2000 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                         NULL, 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_BADSCHEME:
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 ( myhosts == 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 ( mydomains == 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                                 do_addresses( vals, current_to, current_nto,
1234                                               togroups, ngroups, err, nerr,
1235                                               USER );
1236                                 resolved = 1;
1237                                 break;
1238                         case AS_SYNTAX_NATIVE_MB:
1239                                 /* We used to concatenate mailHost if set here */
1240                                 /*
1241                                  * We used to send a copy to the vacation host
1242                                  * if onVacation to uid@vacationhost
1243                                  */
1244                                 if ( as->as_param ) {
1245                                         for ( j=0; j<delivery_types_total; j++ ) {
1246                                                 if ( !strcasecmp( as->as_param, delivery_types[j] ) ) {
1247                                                         add_to( current_to, current_nto, vals );
1248                                                         resolved = 1;
1249                                                         break;
1250                                                 }
1251                                         }
1252                                 } else {
1253                                         add_to( current_to, current_nto, vals );
1254                                         resolved = 1;
1255                                 }
1256                                 break;
1257
1258                         case AS_SYNTAX_DN:
1259                                 if ( dn_search( vals, address,
1260                                                 current_to, current_nto,
1261                                                 togroups, ngroups,
1262                                                 err, nerr ) ) {
1263                                         resolved = 1;
1264                                 }
1265                                 break;
1266
1267                         case AS_SYNTAX_URL:
1268                                 if ( url_list_search( vals, address,
1269                                                  as->as_m_entries,
1270                                                  current_to, current_nto,
1271                                                  togroups, ngroups,
1272                                                  err, nerr, type ) ) {
1273                                         resolved = 1;
1274                                 }
1275                                 break;
1276
1277                         case AS_SYNTAX_BOOL_FILTER:
1278                                 if ( strcasecmp( vals[0], "true" ) ) {
1279                                         break;
1280                                 }
1281                                 substs[0].sub_char = 'D';
1282                                 substs[0].sub_value = dn;
1283                                 substs[1].sub_char = '\0';
1284                                 substs[1].sub_value = NULL;
1285                                 if ( url_list_search( vals, address,
1286                                                  as->as_m_entries,
1287                                                  current_to, current_nto,
1288                                                  togroups, ngroups,
1289                                                  err, nerr, type ) ) {
1290                                         resolved = 1;
1291                                 }
1292                                 break;
1293
1294                         default:
1295                                 syslog( LOG_ALERT,
1296                                         "Invalid syntax %d for kind %d",
1297                                         as->as_syntax, as->as_kind );
1298                                 break;
1299                         }
1300                         break;
1301
1302                 case AS_KIND_ERRORS:
1303                         cur_priority = as->as_priority;
1304                         /* This is a group with special processing */
1305                         if ( type & GROUP_ERRORS ) {
1306                                 switch (as->as_kind) {
1307                                 case AS_SYNTAX_RFC822:
1308                                         add_to( current_to, current_nto, vals );
1309                                         resolved = 1;
1310                                         break;
1311                                 case AS_SYNTAX_URL:
1312                                 default:
1313                                         syslog( LOG_ALERT,
1314                                                 "Invalid syntax %d for kind %d",
1315                                                 as->as_syntax, as->as_kind );
1316                                 }
1317                         } else {
1318                                 current_group = new_group( dn, togroups,
1319                                                            ngroups );
1320                                 current_to = &current_group->g_members;
1321                                 current_nto = &current_group->g_nmembers;
1322                                 split_address( address,
1323                                                &localpart, &domainpart );
1324                                 if ( domainpart ) {
1325                                         sprintf( buf, "%s-%s@%s",
1326                                                  localpart, ERRORS,
1327                                                  domainpart );
1328                                         free( localpart );
1329                                         free( domainpart );
1330                                 } else {
1331                                         sprintf( buf, "%s-%s@%s",
1332                                                  localpart, ERRORS,
1333                                                  host );
1334                                         free( localpart );
1335                                 }
1336                                 current_group->g_errorsto = strdup( buf );
1337                         }
1338                         break;
1339
1340                 case AS_KIND_REQUEST:
1341                         cur_priority = as->as_priority;
1342                         /* This is a group with special processing */
1343                         if ( type & GROUP_REQUEST ) {
1344                                 add_to( current_to, current_nto, vals );
1345                                 resolved = 1;
1346                         }
1347                         break;
1348
1349                 case AS_KIND_OWNER:
1350                         cur_priority = as->as_priority;
1351                         /* This is a group with special processing */
1352                         if ( type & GROUP_REQUEST ) {
1353                                 add_to( current_to, current_nto, vals );
1354                                 resolved = 1;
1355                         }
1356                         break;
1357
1358                 case AS_KIND_ROUTE_TO_HOST:
1359                         if ( !is_my_host( vals[0] ) ) {
1360                                 cur_priority = as->as_priority;
1361                                 if ( as->as_syntax == AS_SYNTAX_PRESENT ) {
1362                                         needs_mta_routing = 1;
1363                                 } else {
1364                                         route_to_host = strdup( vals[0] );
1365                                 }
1366                         }
1367                         break;
1368
1369                 case AS_KIND_ROUTE_TO_ADDR:
1370                         for ( j=0; j<own_addresses_total; j++ ) {
1371                                 if ( strcasecmp( vals[0], own_addresses[j] ) ) {
1372                                         cur_priority = as->as_priority;
1373                                         if ( as->as_syntax == AS_SYNTAX_PRESENT ) {
1374                                                 needs_mta_routing = 1;
1375                                         } else {
1376                                                 route_to_address = strdup( vals[0] );
1377                                         }
1378                                 }
1379                                 break;
1380                         }
1381
1382                 case AS_KIND_OWN_ADDR:
1383                         add_to( &own_addresses, &own_addresses_total, vals );
1384                         cur_priority = as->as_priority;
1385                         break;
1386
1387                 case AS_KIND_DELIVERY_TYPE:
1388                         add_to( &delivery_types, &delivery_types_total, vals );
1389                         cur_priority = as->as_priority;
1390                         break;
1391
1392                 default:
1393                         syslog( LOG_ALERT,
1394                                 "Invalid kind %d", as->as_kind );
1395                         /* Error, TBC */
1396                 }
1397                 ldap_value_free( vals );
1398         }
1399         /*
1400          * Now check if we are dealing with mail routing.  We support
1401          * two modes.
1402          *
1403          * The first mode and by far the most robust method is doing
1404          * routing at the MTA.  In this case, we just checked if the
1405          * routing attributes were present and did not seem like
1406          * pointing to ourselves.  The only thing we have to do here
1407          * is adding to the recipient list any of the RFC822 addresses
1408          * of this entry.  That means we needed to retrieve them from
1409          * the entry itself because we might have arrived here through
1410          * some directory search.  The address received as argument is
1411          * not the address of the entry we are processing, but rather
1412          * the RFC822 address we are expanding now.  Unfortunately,
1413          * this requires an MTA that understands LDAP routing.
1414          * Sendmail 8.10.0 does, if compiled properly.
1415          *
1416          * The second method, that is most emphatically not recommended
1417          * is routing in maildap.  This is going to require using the
1418          * percent hack.  Moreover, this may occasionally loop.
1419          */
1420         if ( needs_mta_routing ) {
1421                 if ( !own_addresses ) {
1422                         add_error( err, nerr, E_NOOWNADDRESS, address, e );
1423                         return( 0 );
1424                 }
1425                 nvals[0] = own_addresses[0];    /* Anyone will do */
1426                 nvals[1] = NULL;
1427                 add_to( current_to, current_nto, nvals );
1428                 resolved = 1;
1429         } else if ( route_to_host ) {
1430                 char *p;
1431                 if ( !route_to_address ) {
1432                         if ( !own_addresses ) {
1433                                 add_error( err, nerr, E_NOOWNADDRESS, address, e );
1434                                 return( 0 );
1435                         }
1436                         route_to_address = strdup( own_addresses[0] );
1437                 }
1438                 /* This makes use of the percent hack, but there's no choice */
1439                 p = strchr( route_to_address, '@' );
1440                 if ( p ) {
1441                         *p = '%';
1442                 }
1443                 sprintf( buf, "%s@%s", route_to_address, route_to_host );
1444                 nvals[0] = buf;
1445                 nvals[1] = NULL;
1446                 add_to( current_to, current_nto, nvals );
1447                 resolved = 1;
1448                 free( route_to_host );
1449                 free( route_to_address );
1450         } else if ( route_to_address ) {
1451                 nvals[0] = route_to_address;
1452                 nvals[1] = NULL;
1453                 add_to( current_to, current_nto, nvals );
1454                 resolved = 1;
1455                 free( route_to_address );
1456         }
1457         if ( own_addresses ) {
1458                 ldap_value_free( own_addresses );
1459         }
1460         if ( delivery_types ) {
1461                 ldap_value_free( delivery_types );
1462         }
1463                   
1464         return( resolved );
1465 }
1466
1467 static int
1468 search_bases(
1469         char    *filter,
1470         Subst   *substs,
1471         char    *name,
1472         char    ***to,
1473         int     *nto,
1474         Group   ***togroups,
1475         int     *ngroups,
1476         Error   **err,
1477         int     *nerr,
1478         int     type
1479 )
1480 {
1481         int             b, resolved = 0;
1482
1483         for ( b = 0; base[b] != NULL; b++ ) {
1484
1485                 if ( ! (base[b]->b_search & type) ) {
1486                         continue;
1487                 }
1488
1489                 resolved = search_ldap_url( base[b]->b_url, substs, name,
1490                                             base[b]->b_rdnpref,
1491                                             base[b]->b_m_entries,
1492                                             to, nto, togroups, ngroups,
1493                                             err, nerr, type );
1494                 if ( resolved )
1495                         break;
1496         }
1497         return( resolved );
1498 }
1499
1500 static void
1501 do_address(
1502         char    *name,
1503         char    ***to,
1504         int     *nto,
1505         Group   ***togroups,
1506         int     *ngroups,
1507         Error   **err,
1508         int     *nerr,
1509         int     type
1510 )
1511 {
1512         char            *localpart = NULL, *domainpart = NULL;
1513         char            *synthname = NULL;
1514         int             resolved;
1515         int             i;
1516         Subst           substs[6];
1517
1518         /*
1519          * Look up the name in X.500, add the appropriate addresses found
1520          * to the to list, or to the err list in case of error.  Groups are
1521          * handled by the do_group routine, individuals are handled here.
1522          * When looking up name, we follow the bases hierarchy, looking
1523          * in base[0] first, then base[1], etc.  For each base, there is
1524          * a set of search filters to try, in order.  If something goes
1525          * wrong here trying to contact X.500, we exit with EX_TEMPFAIL.
1526          * If the b_rdnpref flag is set, then we give preference to entries
1527          * that matched name because it's their rdn, otherwise not.
1528          */
1529
1530         split_address( name, &localpart, &domainpart );
1531         synthname = strdup( localpart );
1532         for ( i = 0; synthname[i] != '\0'; i++ ) {
1533                 if ( synthname[i] == '.' || synthname[i] == '_' )
1534                         synthname[i] = ' ';
1535         }
1536         substs[0].sub_char = 'm';
1537         substs[0].sub_value = name;
1538         substs[1].sub_char = 'h';
1539         substs[1].sub_value = host;
1540         substs[2].sub_char = 'l';
1541         substs[2].sub_value = localpart;
1542         substs[3].sub_char = 'd';
1543         substs[3].sub_value = domainpart;
1544         substs[4].sub_char = 's';
1545         substs[4].sub_value = synthname;
1546         substs[5].sub_char = '\0';
1547         substs[5].sub_value = NULL;
1548
1549         resolved = search_bases( NULL, substs, name,
1550                                  to, nto, togroups, ngroups,
1551                                  err, nerr, type );
1552
1553         if ( localpart ) {
1554                 free( localpart );
1555         }
1556         if ( domainpart ) {
1557                 free( domainpart );
1558         }
1559         if ( synthname ) {
1560                 free( synthname );
1561         }
1562
1563         if ( !resolved ) {
1564                 /* not resolved - bounce with user unknown */
1565                 if ( type == USER ) {
1566                         add_error( err, nerr, E_USERUNKNOWN, name, NULL );
1567                 } else {
1568                         add_error( err, nerr, E_GROUPUNKNOWN, name, NULL );
1569                 }
1570         }
1571 }
1572
1573 static void
1574 send_message( char **to )
1575 {
1576         int     pid;
1577 #ifndef HAVE_WAITPID
1578         WAITSTATUSTYPE  status;
1579 #endif
1580
1581         if ( debug ) {
1582                 char    buf[1024];
1583                 int     i;
1584
1585                 strcpy( buf, to[0] );
1586                 for ( i = 1; to[i] != NULL; i++ ) {
1587                         strcat( buf, " " );
1588                         strcat( buf, to[i] );
1589                 }
1590
1591                 syslog( LOG_ALERT, "send_message execing sendmail: (%s)", buf );
1592         }
1593
1594         /* parent */
1595         if ( (pid = fork()) != 0 ) {
1596 #ifdef HAVE_WAITPID
1597                 waitpid( pid, (int *) NULL, 0 );
1598 #else
1599                 wait4( pid, &status, WAIT_FLAGS, 0 );
1600 #endif
1601         /* child */
1602         } else {
1603                 /* to includes sendmailargs */
1604                 execv( MAIL500_SENDMAIL, to );
1605
1606                 syslog( LOG_ALERT, "execv failed" );
1607
1608                 exit( EX_TEMPFAIL );
1609         }
1610 }
1611
1612 static void
1613 send_group( Group **group, int ngroup )
1614 {
1615         int     i, pid;
1616         char    **argv;
1617         int     argc;
1618         char    *iargv[7];
1619 #ifndef HAVE_WAITPID
1620         WAITSTATUSTYPE  status;
1621 #endif
1622
1623         for ( i = 0; i < ngroup; i++ ) {
1624                 (void) rewind( stdin );
1625
1626                 iargv[0] = MAIL500_SENDMAIL;
1627                 iargv[1] = "-f";
1628                 iargv[2] = group[i]->g_errorsto;
1629                 iargv[3] = "-oMrX.500";
1630                 iargv[4] = "-odi";
1631                 iargv[5] = "-oi";
1632                 iargv[6] = NULL;
1633
1634                 argv = NULL;
1635                 argc = 0;
1636                 add_to( &argv, &argc, iargv );
1637                 add_to( &argv, &argc, group[i]->g_members );
1638
1639                 if ( debug ) {
1640                         char    buf[1024];
1641                         int     i;
1642
1643                         strcpy( buf, argv[0] );
1644                         for ( i = 1; i < argc; i++ ) {
1645                                 strcat( buf, " " );
1646                                 strcat( buf, argv[i] );
1647                         }
1648
1649                         syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
1650                 }
1651
1652                 /* parent */
1653                 if ( (pid = fork()) != 0 ) {
1654 #ifdef HAVE_WAITPID
1655                         waitpid( pid, (int *) NULL, 0 );
1656 #else
1657                         wait4( pid, &status, WAIT_FLAGS, 0 );
1658 #endif
1659                 /* child */
1660                 } else {
1661                         execv( MAIL500_SENDMAIL, argv );
1662
1663                         syslog( LOG_ALERT, "execv failed" );
1664
1665                         exit( EX_TEMPFAIL );
1666                 }
1667         }
1668 }
1669
1670 static void
1671 send_errors( Error *err, int nerr )
1672 {
1673         int     pid, i, namelen;
1674         FILE    *fp;
1675         int     fd[2];
1676         char    *argv[8];
1677         char    buf[1024];
1678 #ifndef HAVE_WAITPID
1679         WAITSTATUSTYPE  status;
1680 #endif
1681
1682         if ( strcmp( MAIL500_BOUNCEFROM, mailfrom ) == 0 ) {
1683             mailfrom = errorsfrom;
1684         }
1685
1686         argv[0] = MAIL500_SENDMAIL;
1687         argv[1] = "-oMrX.500";
1688         argv[2] = "-odi";
1689         argv[3] = "-oi";
1690         argv[4] = "-f";
1691         argv[5] = MAIL500_BOUNCEFROM;
1692         argv[6] = mailfrom;
1693         argv[7] = NULL;
1694
1695         if ( debug ) {
1696                 int     i;
1697
1698                 strcpy( buf, argv[0] );
1699                 for ( i = 1; argv[i] != NULL; i++ ) {
1700                         strcat( buf, " " );
1701                         strcat( buf, argv[i] );
1702                 }
1703
1704                 syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
1705         }
1706
1707         if ( pipe( fd ) == -1 ) {
1708                 syslog( LOG_ALERT, "cannot create pipe" );
1709                 exit( EX_TEMPFAIL );
1710         }
1711
1712         if ( (pid = fork()) != 0 ) {
1713                 if ( (fp = fdopen( fd[1], "w" )) == NULL ) {
1714                         syslog( LOG_ALERT, "cannot fdopen pipe" );
1715                         exit( EX_TEMPFAIL );
1716                 }
1717
1718                 fprintf( fp, "To: %s\n", mailfrom );
1719                 fprintf( fp, "From: %s\n", errorsfrom );
1720                 fprintf( fp, "Subject: undeliverable mail\n" );
1721                 fprintf( fp, "\n" );
1722                 fprintf( fp, "The following errors occurred when trying to deliver the attached mail:\n" );
1723                 for ( i = 0; i < nerr; i++ ) {
1724                         namelen = strlen( err[i].e_addr );
1725                         fprintf( fp, "\n" );
1726
1727                         switch ( err[i].e_code ) {
1728                         case E_USERUNKNOWN:
1729                                 fprintf( fp, "%s: User unknown\n", err[i].e_addr );
1730                                 break;
1731
1732                         case E_GROUPUNKNOWN:
1733                                 fprintf( fp, "%s: Group unknown\n", err[i].e_addr );
1734                                 break;
1735
1736                         case E_BADMEMBER:
1737                                 fprintf( fp, "%s: Group member does not exist\n",
1738                                     err[i].e_addr );
1739                                 fprintf( fp, "This could be because the distinguished name of the person has changed\n" );
1740                                 fprintf( fp, "If this is the case, the problem can be solved by removing and\n" );
1741                                 fprintf( fp, "then re-adding the person to the group.\n" );
1742                                 break;
1743
1744                         case E_NOREQUEST:
1745                                 fprintf( fp, "%s: Group exists but has no request address\n",
1746                                     err[i].e_addr );
1747                                 break;
1748
1749                         case E_NOERRORS:
1750                                 fprintf( fp, "%s: Group exists but has no errors-to address\n",
1751                                     err[i].e_addr );
1752                                 break;
1753
1754                         case E_NOOWNER:
1755                                 fprintf( fp, "%s: Group exists but has no owner\n",
1756                                     err[i].e_addr );
1757                                 break;
1758
1759                         case E_AMBIGUOUS:
1760                                 do_ambiguous( fp, &err[i], namelen );
1761                                 break;
1762
1763                         case E_NOEMAIL:
1764                                 do_noemail( fp, &err[i], namelen );
1765                                 break;
1766
1767                         case E_MEMBERNOEMAIL:
1768                                 fprintf( fp, "%s: Group member exists but does not have an email address\n",
1769                                     err[i].e_addr );
1770                                 break;
1771
1772                         case E_JOINMEMBERNOEMAIL:
1773                                 fprintf( fp, "%s: User has joined group but does not have an email address\n",
1774                                     err[i].e_addr );
1775                                 break;
1776
1777                         case E_LOOP:
1778                                 fprintf( fp, "%s: User has created a mail loop by adding address %s to their X.500 entry\n",
1779                                     err[i].e_addr, err[i].e_loop );
1780                                 break;
1781
1782                         case E_NOMEMBERS:
1783                                 fprintf( fp, "%s: Group has no members\n",
1784                                     err[i].e_addr );
1785                                 break;
1786
1787                         case E_NOOWNADDRESS:
1788                                 fprintf( fp, "%s: Not enough information to perform required routing\n",
1789                                     err[i].e_addr );
1790                                 break;
1791
1792                         default:
1793                                 syslog( LOG_ALERT, "unknown error %d", err[i].e_code );
1794                                 unbind_and_exit( EX_TEMPFAIL );
1795                                 break;
1796                         }
1797                 }
1798
1799                 fprintf( fp, "\n------- The original message sent:\n\n" );
1800
1801                 while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
1802                         fputs( buf, fp );
1803                 }
1804                 fclose( fp );
1805
1806 #ifdef HAVE_WAITPID
1807                 waitpid( pid, (int *) NULL, 0 );
1808 #else
1809                 wait4( pid, &status, WAIT_FLAGS, 0 );
1810 #endif
1811         } else {
1812                 dup2( fd[0], 0 );
1813
1814                 execv( MAIL500_SENDMAIL, argv );
1815
1816                 syslog( LOG_ALERT, "execv failed" );
1817
1818                 exit( EX_TEMPFAIL );
1819         }
1820 }
1821
1822 static void
1823 do_noemail( FILE *fp, Error *err, int namelen )
1824 {
1825         int             i, last;
1826         char            *dn, *rdn;
1827         char            **ufn, **vals;
1828
1829         fprintf(fp, "%s: User has no email address registered.\n",
1830             err->e_addr );
1831         fprintf( fp, "%*s  Name, title, postal address and phone for '%s':\n\n",
1832             namelen, " ", err->e_addr );
1833
1834         /* name */
1835         dn = ldap_get_dn( ld, err->e_msg );
1836         ufn = ldap_explode_dn( dn, 1 );
1837         rdn = strdup( ufn[0] );
1838         if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1839                 if ( (vals = ldap_get_values( ld, err->e_msg, "cn" ))
1840                     != NULL ) {
1841                         for ( i = 0; vals[i]; i++ ) {
1842                                 last = strlen( vals[i] ) - 1;
1843                                 if ( isdigit((unsigned char) vals[i][last]) ) {
1844                                         rdn = strdup( vals[i] );
1845                                         break;
1846                                 }
1847                         }
1848
1849                         ldap_value_free( vals );
1850                 }
1851         }
1852         fprintf( fp, "%*s  %s\n", namelen, " ", rdn );
1853         free( dn );
1854         free( rdn );
1855         ldap_value_free( ufn );
1856
1857         /* titles or descriptions */
1858         if ( (vals = ldap_get_values( ld, err->e_msg, "title" )) == NULL &&
1859             (vals = ldap_get_values( ld, err->e_msg, "description" ))
1860             == NULL ) {
1861                 fprintf( fp, "%*s  No title or description registered\n",
1862                     namelen, " " );
1863         } else {
1864                 for ( i = 0; vals[i] != NULL; i++ ) {
1865                         fprintf( fp, "%*s  %s\n", namelen, " ", vals[i] );
1866                 }
1867
1868                 ldap_value_free( vals );
1869         }
1870
1871         /* postal address */
1872         if ( (vals = ldap_get_values( ld, err->e_msg, "postalAddress" ))
1873             == NULL ) {
1874                 fprintf( fp, "%*s  No postal address registered\n", namelen,
1875                     " " );
1876         } else {
1877                 fprintf( fp, "%*s  ", namelen, " " );
1878                 for ( i = 0; vals[0][i] != '\0'; i++ ) {
1879                         if ( vals[0][i] == '$' ) {
1880                                 fprintf( fp, "\n%*s  ", namelen, " " );
1881                                 while ( isspace((unsigned char) vals[0][i+1]) )
1882                                         i++;
1883                         } else {
1884                                 fprintf( fp, "%c", vals[0][i] );
1885                         }
1886                 }
1887                 fprintf( fp, "\n" );
1888
1889                 ldap_value_free( vals );
1890         }
1891
1892         /* telephone number */
1893         if ( (vals = ldap_get_values( ld, err->e_msg, "telephoneNumber" ))
1894             == NULL ) {
1895                 fprintf( fp, "%*s  No phone number registered\n", namelen,
1896                     " " );
1897         } else {
1898                 for ( i = 0; vals[i] != NULL; i++ ) {
1899                         fprintf( fp, "%*s  %s\n", namelen, " ", vals[i] );
1900                 }
1901
1902                 ldap_value_free( vals );
1903         }
1904 }
1905
1906 /* ARGSUSED */
1907 static void
1908 do_ambiguous( FILE *fp, Error *err, int namelen )
1909 {
1910         int             i, last;
1911         char            *dn, *rdn;
1912         char            **ufn, **vals;
1913         LDAPMessage     *e;
1914
1915         i = ldap_result2error( ld, err->e_msg, 0 );
1916
1917         fprintf( fp, "%s: Ambiguous user.  %s%d matches found:\n\n",
1918             err->e_addr, i == LDAP_SIZELIMIT_EXCEEDED ? "First " : "",
1919             ldap_count_entries( ld, err->e_msg ) );
1920
1921         for ( e = ldap_first_entry( ld, err->e_msg ); e != NULL;
1922             e = ldap_next_entry( ld, e ) ) {
1923                 dn = ldap_get_dn( ld, e );
1924                 ufn = ldap_explode_dn( dn, 1 );
1925                 rdn = strdup( ufn[0] );
1926                 if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1927                         if ( (vals = ldap_get_values( ld, e, "cn" )) != NULL ) {
1928                                 for ( i = 0; vals[i]; i++ ) {
1929                                         last = strlen( vals[i] ) - 1;
1930                                         if (isdigit((unsigned char) vals[i][last])) {
1931                                                 rdn = strdup( vals[i] );
1932                                                 break;
1933                                         }
1934                                 }
1935
1936                                 ldap_value_free( vals );
1937                         }
1938                 }
1939
1940                 /* 
1941                 if ( isgroup( e ) ) {
1942                         vals = ldap_get_values( ld, e, "description" );
1943                 } else {
1944                         vals = ldap_get_values( ld, e, "title" );
1945                 }
1946                 */
1947                 vals = ldap_get_values( ld, e, "description" );
1948
1949                 fprintf( fp, "    %-20s %s\n", rdn, vals ? vals[0] : "" );
1950                 for ( i = 1; vals && vals[i] != NULL; i++ ) {
1951                         fprintf( fp, "                         %s\n", vals[i] );
1952                 }
1953
1954                 free( dn );
1955                 free( rdn );
1956                 ldap_value_free( ufn );
1957                 if ( vals != NULL )
1958                         ldap_value_free( vals );
1959         }
1960 }
1961
1962 static int
1963 count_values( char **list )
1964 {
1965         int     i;
1966
1967         for ( i = 0; list && list[i] != NULL; i++ )
1968                 ;       /* NULL */
1969
1970         return( i );
1971 }
1972
1973 static void
1974 add_to( char ***list, int *nlist, char **new )
1975 {
1976         int     i, nnew, oldnlist;
1977
1978         nnew = count_values( new );
1979
1980         oldnlist = *nlist;
1981         if ( *list == NULL || *nlist == 0 ) {
1982                 *list = (char **) malloc( (nnew + 1) * sizeof(char *) );
1983                 *nlist = nnew;
1984         } else {
1985                 *list = (char **) realloc( *list, *nlist * sizeof(char *) +
1986                     nnew * sizeof(char *) + sizeof(char *) );
1987                 *nlist += nnew;
1988         }
1989
1990         for ( i = 0; i < nnew; i++ )
1991                 (*list)[i + oldnlist] = strdup( new[i] );
1992         (*list)[*nlist] = NULL;
1993 }
1994
1995 static void
1996 add_single_to( char ***list, char *new )
1997 {
1998         int     nlist;
1999
2000         if ( *list == NULL ) {
2001                 nlist = 0;
2002                 *list = (char **) malloc( 2 * sizeof(char *) );
2003         } else {
2004                 nlist = count_values( *list );
2005                 *list = (char **) realloc( *list,
2006                                            ( nlist + 2 ) * sizeof(char *) );
2007         }
2008
2009         (*list)[nlist] = strdup( new );
2010         (*list)[nlist+1] = NULL;
2011 }
2012
2013 static int
2014 isgroup( LDAPMessage *e )
2015 {
2016         int     i, j;
2017         char    **oclist;
2018
2019         if ( !groupclasses ) {
2020                 return( 0 );
2021         }
2022
2023         oclist = ldap_get_values( ld, e, "objectClass" );
2024
2025         for ( i = 0; oclist[i] != NULL; i++ ) {
2026                 for ( j = 0; groupclasses[j] != NULL; j++ ) {
2027                         if ( strcasecmp( oclist[i], groupclasses[j] ) == 0 ) {
2028                                 ldap_value_free( oclist );
2029                                 return( 1 );
2030                         }
2031                 }
2032         }
2033         ldap_value_free( oclist );
2034
2035         return( 0 );
2036 }
2037
2038 static void
2039 add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg )
2040 {
2041         if ( *nerr == 0 ) {
2042                 *err = (Error *) malloc( sizeof(Error) );
2043         } else {
2044                 *err = (Error *) realloc( *err, (*nerr + 1) * sizeof(Error) );
2045         }
2046
2047         (*err)[*nerr].e_code = code;
2048         (*err)[*nerr].e_addr = strdup( addr );
2049         (*err)[*nerr].e_msg = msg;
2050         (*nerr)++;
2051 }
2052
2053 static void
2054 unbind_and_exit( int rc )
2055 {
2056         int     i;
2057
2058         if ( (i = ldap_unbind( ld )) != LDAP_SUCCESS )
2059                 syslog( LOG_ALERT, "ldap_unbind failed %d\n", i );
2060
2061         exit( rc );
2062 }