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