]> git.sur5r.net Git - openldap/blob - clients/mail500/main.c
cbe5bb80372f1ce7343e6af71f9ef0deae4b17b7
[openldap] / clients / mail500 / main.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright (c) 1990 Regents of the University of Michigan.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted
7  * provided that this notice is preserved and that due credit is given
8  * to the University of Michigan at Ann Arbor. The name of the University
9  * may not be used to endorse or promote products derived from this
10  * software without specific prior written permission. This software
11  * is provided ``as is'' without express or implied warranty.
12  *
13  * Copyright 1998-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 = NULL;
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         char            *e_addr;
86         union e_union_u {
87                 char            *e_u_loop;
88                 LDAPMessage     *e_u_msg;
89         } e_union;
90 #define e_msg   e_union.e_u_msg
91 #define e_loop  e_union.e_u_loop
92 } Error;
93
94 typedef struct groupto {
95         char    *g_dn;
96         char    *g_errorsto;
97         char    **g_members;
98 } Group;
99
100 typedef struct baseinfo {
101         char    *b_dn;          /* dn to start searching at */
102         char    b_rdnpref;      /* give rdn's preference when searching? */
103         int     b_search;       /* ORed with the type of thing the address */
104                                 /*  looks like (USER, GROUP_ERRORS, etc.)  */
105                                 /*  to see if this should be searched      */
106         char    *b_filter[3];   /* filter to apply - name substituted for %s */
107                                 /* (up to three of them) */
108 } Base;
109
110 Base    base[] = {
111         {"ou=People, dc=example, dc=com",
112                 0, USER,
113                 {"uid=%s", "cn=%s", NULL}},
114         {"ou=System Groups, ou=Groups, dc=example, dc=com",
115                 1, 0xff,
116                 {"(&(cn=%s)(associatedDomain=%h))", NULL, NULL}},
117         {"ou=User Groups, ou=Groups, dc=example, dc=com",
118                 1, 0xff,
119                 {"(&(cn=%s)(associatedDomain=%h))", NULL, NULL}},
120         {NULL}
121 };
122
123 char    *sendmailargs[] = { MAIL500_SENDMAIL, "-oMrLDAP", "-odi", "-oi", "-f", NULL, NULL };
124
125 static char     *attrs[] = { "objectClass", "title", "postaladdress",
126                         "telephoneNumber", "mail", "description", "owner",
127                         "errorsTo", "rfc822ErrorsTo", "requestsTo",
128                         "rfc822RequestsTo", "joinable", "cn", "member",
129                         "moderator", "onVacation", "uid",
130                         "suppressNoEmailError", NULL };
131
132 static void do_address( char *name, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr, int type );
133 static int  do_group( LDAPMessage *e, char *dn, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr );
134 static void do_group_members( LDAPMessage *e, char *dn, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr );
135 static void send_message( char **to );
136 static void send_errors( Error *err, int nerr );
137 static void do_noemail( FILE *fp, Error *err, int namelen );
138 static void do_ambiguous( FILE *fp, Error *err, int namelen );
139 static void add_to( char ***list, int *nlist, char **new );
140 static int  isgroup( LDAPMessage *e );
141 static void add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg );
142 static void add_group( char *dn, Group **list, int *nlist );
143 static void unbind_and_exit( int rc );
144 static int  group_loop( char *dn );
145 static void send_group( Group *group, int ngroup );
146 static int  has_attributes( LDAPMessage *e, char *attr1, char *attr2 );
147 static char **get_attributes_mail_dn( LDAPMessage *e, char *attr1, char *attr2 );
148 static char *canonical( char *s );
149 static int  connect_to_x500( void );
150
151 static void do_group_errors( LDAPMessage *e, char *dn, char ***to, int *nto, Error **err, int *nerr );
152 static void do_group_request( LDAPMessage *e, char *dn, char ***to, int *nto, Error **err, int *nerr );
153 static void do_group_owner( LDAPMessage *e, char *dn, char ***to, int *nto, Error **err, int *nerr );
154 static void add_member( char *gdn, char *dn, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr, char **suppress );
155
156 int
157 main ( int argc, char **argv )
158 {
159         char            *myname;
160         char            **tolist;
161         Error           *errlist;
162         Group           *togroups;
163         int             numto, ngroups, numerr, nargs;
164         int             i, j;
165
166         if ( (myname = strrchr( argv[0], *LDAP_DIRSEP )) == NULL )
167                 myname = strdup( argv[0] );
168         else
169                 myname = strdup( myname + 1 );
170
171 #ifdef SIGPIPE
172         (void) SIGNAL( SIGPIPE, SIG_IGN );
173 #endif
174
175 #ifdef LOG_MAIL
176         openlog( myname, OPENLOG_OPTIONS, LOG_MAIL );
177 #elif LOG_DEBUG
178         openlog( myname, OPENLOG_OPTIONS );
179 #endif
180
181         while ( (i = getopt( argc, argv, "d:f:h:l:m:v:" )) != EOF ) {
182                 switch( i ) {
183                 case 'd':       /* turn on debugging */
184                         debug = atoi( optarg );
185                         break;
186
187                 case 'f':       /* who it's from & where errors should go */
188                         mailfrom = strdup( optarg );
189                         for ( j = 0; sendmailargs[j] != NULL; j++ ) {
190                                 if ( strcmp( sendmailargs[j], "-f" ) == 0 ) {
191                                         sendmailargs[j+1] = mailfrom;
192                                         break;
193                                 }
194                         }
195                         break;
196
197                 case 'h':       /* hostname */
198                         host = strdup( optarg );
199                         hostlen = strlen(host);
200                         break;
201
202                 case 'l':       /* ldap host */
203                         ldaphost = strdup( optarg );
204                         break;
205
206                                 /* mailer-daemon address - who we should */
207                 case 'm':       /* say errors come from */
208                         errorsfrom = strdup( optarg );
209                         break;
210
211                 case 'v':       /* vacation host */
212                         vacationhost = strdup( optarg );
213                         break;
214
215                 default:
216                         syslog( LOG_ALERT, "unknown option" );
217                         break;
218                 }
219         }
220
221         if ( mailfrom == NULL ) {
222                 syslog( LOG_ALERT, "required argument -f not present" );
223                 exit( EX_TEMPFAIL );
224         }
225         if ( errorsfrom == NULL ) {
226                 syslog( LOG_ALERT, "required argument -m not present" );
227                 exit( EX_TEMPFAIL );
228         }
229         if ( host == NULL ) {
230                 syslog( LOG_ALERT, "required argument -h not present" );
231                 exit( EX_TEMPFAIL );
232         }
233
234         if ( connect_to_x500() != 0 )
235                 exit( EX_TEMPFAIL );
236
237         setuid( geteuid() );
238
239         if ( debug ) {
240                 char    buf[1024];
241                 int     i;
242
243                 syslog( LOG_ALERT, "running as %d", geteuid() );
244                 strcpy( buf, argv[0] );
245                 for ( i = 1; i < argc; i++ ) {
246                         strcat( buf, " " );
247                         strcat( buf, argv[i] );
248                 }
249
250                 syslog( LOG_ALERT, "args: (%s)", buf );
251         }
252
253         tolist = NULL;
254         numto = 0;
255         add_to( &tolist, &numto, sendmailargs );
256         nargs = numto;
257         ngroups = numerr = 0;
258         togroups = NULL;
259         errlist = NULL;
260         for ( i = optind; i < argc; i++ ) {
261                 char    *s;
262                 int     type;
263
264                 for ( j = 0; argv[i][j] != '\0'; j++ ) {
265                         if ( argv[i][j] == '.' || argv[i][j] == '_' )
266                                 argv[i][j] = ' ';
267                 }
268
269                 type = USER;
270                 if ( (s = strrchr( argv[i], '-' )) != NULL ) {
271                         s++;
272
273                         if ((strcasecmp(s, ERROR) == 0) ||
274                                 (strcasecmp(s, ERRORS) == 0)) {
275                                 type = GROUP_ERRORS;
276                                 *(--s) = '\0';
277                         } else if ((strcasecmp(s, REQUEST) == 0) ||
278                                 (strcasecmp(s, REQUESTS) == 0)) {
279                                 type = GROUP_REQUEST;
280                                 *(--s) = '\0';
281                         } else if ( strcasecmp( s, MEMBERS ) == 0 ) {
282                                 type = GROUP_MEMBERS;
283                                 *(--s) = '\0';
284                         } else if ((strcasecmp(s, OWNER) == 0) ||
285                                 (strcasecmp(s, OWNERS) == 0)) {
286                                 type = GROUP_OWNER;
287                                 *(--s) = '\0';
288                         }
289                 }
290
291                 do_address( argv[i], &tolist, &numto, &togroups, &ngroups,
292                     &errlist, &numerr, type );
293         }
294
295         /*
296          * If we have both errors and successful deliveries to make or if
297          * if there are any groups to deliver to, we basically need to read
298          * the message twice.  So, we have to put it in a tmp file.
299          */
300
301         if ( numerr > 0 && numto > nargs || ngroups > 0 ) {
302                 FILE    *fp;
303                 char    buf[BUFSIZ];
304
305                 umask( 077 );
306                 if ( (fp = tmpfile()) == NULL ) {
307                         syslog( LOG_ALERT, "could not open tmp file" );
308                         unbind_and_exit( EX_TEMPFAIL );
309                 }
310
311                 /* copy the message to a temp file */
312                 while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
313                         if ( fputs( buf, fp ) == EOF ) {
314                                 syslog( LOG_ALERT, "error writing tmpfile" );
315                                 unbind_and_exit( EX_TEMPFAIL );
316                         }
317                 }
318
319                 if ( dup2( fileno( fp ), 0 ) == -1 ) {
320                         syslog( LOG_ALERT, "could not dup2 tmpfile" );
321                         unbind_and_exit( EX_TEMPFAIL );
322                 }
323
324                 fclose( fp );
325         }
326
327         /* deal with errors */
328         if ( numerr > 0 ) {
329                 if ( debug ) {
330                         syslog( LOG_ALERT, "sending errors" );
331                 }
332                 (void) rewind( stdin );
333                 send_errors( errlist, numerr );
334         }
335
336         (void) ldap_unbind( ld );
337
338         /* send to groups with errorsTo */
339         if ( ngroups > 0 ) {
340                 if ( debug ) {
341                         syslog( LOG_ALERT, "sending to groups with errorsto" );
342                 }
343                 (void) rewind( stdin );
344                 send_group( togroups, ngroups );
345         }
346
347         /* send to expanded aliases and groups w/o errorsTo */
348         if ( numto > nargs ) {
349                 if ( debug ) {
350                         syslog( LOG_ALERT, "sending to aliases and groups" );
351                 }
352                 (void) rewind( stdin );
353                 send_message( tolist );
354         }
355
356         return( EX_OK );
357 }
358
359 static int
360 connect_to_x500( void )
361 {
362         int opt;
363
364         if ( (ld = ldap_init( ldaphost, 0 )) == NULL ) {
365                 syslog( LOG_ALERT, "ldap_init failed" );
366                 return( -1 );
367         }
368
369         opt = MAIL500_MAXAMBIGUOUS;
370         ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &opt);
371         opt = LDAP_DEREF_ALWAYS;
372         ldap_set_option(ld, LDAP_OPT_DEREF, &opt);
373
374         if ( ldap_simple_bind_s( ld, NULL, NULL ) != LDAP_SUCCESS ) {
375                 syslog( LOG_ALERT, "ldap_simple_bind_s failed" );
376                 return( -1 );
377         }
378
379         return( 0 );
380 }
381
382 static int
383 mailcmp( char *a, char *b )
384 {
385         int     i;
386
387         for ( i = 0; a[i] != '\0'; i++ ) {
388                 if ( a[i] != b[i] ) {
389                         switch ( a[i] ) {
390                         case ' ':
391                         case '.':
392                         case '_':
393                                 if ( b[i] == ' ' || b[i] == '.' || b[i] == '_' )
394                                         break;
395                                 return( 1 );
396
397                         default:
398                                 return( 1 );
399                         }
400                 }
401         }
402
403         return( 0 );
404 }
405
406 static void
407 do_address(
408         char    *name,
409         char    ***to,
410         int     *nto,
411         Group   **togroups,
412         int     *ngroups,
413         Error   **err,
414         int     *nerr,
415         int     type
416 )
417 {
418         int             rc, b, f, match;
419         LDAPMessage     *e, *res;
420         struct timeval  timeout;
421         char            *dn;
422         char            filter[1024];
423         char            realfilter[1024];
424         char            **mail, **onvacation = NULL, **uid = NULL;
425
426         /*
427          * Look up the name in X.500, add the appropriate addresses found
428          * to the to list, or to the err list in case of error.  Groups are
429          * handled by the do_group routine, individuals are handled here.
430          * When looking up name, we follow the bases hierarchy, looking
431          * in base[0] first, then base[1], etc.  For each base, there is
432          * a set of search filters to try, in order.  If something goes
433          * wrong here trying to contact X.500, we exit with EX_TEMPFAIL.
434          * If the b_rdnpref flag is set, then we give preference to entries
435          * that matched name because it's their rdn, otherwise not.
436          */
437
438         timeout.tv_sec = MAIL500_TIMEOUT;
439         timeout.tv_usec = 0;
440         for ( b = 0, match = 0; !match && base[b].b_dn != NULL; b++ ) {
441                 if ( ! (base[b].b_search & type) ) {
442                         continue;
443                 }
444                 for ( f = 0; base[b].b_filter[f] != NULL; f++ ) {
445                         char    *format, *p, *s, *d;
446                         char    *argv[3];
447                         int     argc;
448
449                         for ( argc = 0; argc < 3; argc++ ) {
450                                 argv[argc] = NULL;
451                         }
452
453                         format = strdup( base[b].b_filter[f] );
454                         for ( argc = 0, p = format; *p; p++ ) {
455                                 if ( *p == '%' ) {
456                                         switch ( *++p ) {
457                                         case 's':       /* %s is the name */
458                                                 argv[argc] = name;
459                                                 break;
460
461                                         case 'h':       /* %h is the host */
462                                                 *p = 's';
463                                                 argv[argc] = host;
464                                                 break;
465
466                                         default:
467                                                 syslog( LOG_ALERT,
468                                                     "unknown format %c", *p );
469                                                 break;
470                                         }
471
472                                         argc++;
473                                 }
474                         }
475
476                         /* three names ought to do... */
477                         sprintf( filter, format, argv[0], argv[1], argv[2] );
478                         free( format );
479                         for ( s = filter, d = realfilter; *s; s++, d++ ) {
480                                 if ( *s == '*' ) {
481                                         *d++ = '\\';
482                                 }
483                                 *d = *s;
484                         }
485                         *d = '\0';
486
487                         res = NULL;
488                         rc = ldap_search_st( ld, base[b].b_dn,
489                             LDAP_SCOPE_SUBTREE, realfilter, attrs, 0, &timeout,
490                             &res );
491
492                         /* some other trouble - try again later */
493                         if ( rc != LDAP_SUCCESS &&
494                             rc != LDAP_SIZELIMIT_EXCEEDED ) {
495                                 syslog( LOG_ALERT, "return 0x%x from X.500",
496                                     rc );
497                                 unbind_and_exit( EX_TEMPFAIL );
498                         }
499
500                         if ( (match = ldap_count_entries( ld, res )) != 0 )
501                                 break;
502
503                         ldap_msgfree( res );
504                 }
505
506                 if ( match )
507                         break;
508         }
509
510         /* trouble - try again later */
511         if ( match == -1 ) {
512                 syslog( LOG_ALERT, "error parsing result from X.500" );
513                 unbind_and_exit( EX_TEMPFAIL );
514         }
515
516         /* no matches - bounce with user unknown */
517         if ( match == 0 ) {
518                 if ( type == USER ) {
519                         add_error( err, nerr, E_USERUNKNOWN, name, NULL );
520                 } else {
521                         add_error( err, nerr, E_GROUPUNKNOWN, name, NULL );
522                 }
523                 return;
524         }
525
526         /* more than one match - bounce with ambiguous user? */
527         if ( match > 1 ) {
528                 LDAPMessage     *next, *tmpres = NULL;
529                 char            *dn;
530                 char            **xdn;
531
532                 /* not giving rdn preference - bounce with ambiguous user */
533                 if ( base[b].b_rdnpref == 0 ) {
534                         add_error( err, nerr, E_AMBIGUOUS, name, res );
535                         return;
536                 }
537
538                 /*
539                  * giving rdn preference - see if any entries were matched
540                  * because of their rdn.  If so, collect them to deal with
541                  * later (== 1 we deliver, > 1 we bounce).
542                  */
543
544                 for ( e = ldap_first_entry( ld, res ); e != NULL; e = next ) {
545                         next = ldap_next_entry( ld, e );
546                         dn = ldap_get_dn( ld, e );
547                         xdn = ldap_explode_dn( dn, 1 );
548
549                         /* XXX bad, but how else can we do it? XXX */
550                         if ( strcasecmp( xdn[0], name ) == 0 ) {
551                                 ldap_delete_result_entry( &res, e );
552                                 ldap_add_result_entry( &tmpres, e );
553                         }
554
555                         ldap_value_free( xdn );
556                         free( dn );
557                 }
558
559                 /* nothing matched by rdn - go ahead and bounce */
560                 if ( tmpres == NULL ) {
561                         add_error( err, nerr, E_AMBIGUOUS, name, res );
562                         return;
563
564                 /* more than one matched by rdn - bounce with rdn matches */
565                 } else if ( (match = ldap_count_entries( ld, tmpres )) > 1 ) {
566                         add_error( err, nerr, E_AMBIGUOUS, name, tmpres );
567                         return;
568
569                 /* trouble... */
570                 } else if ( match < 0 ) {
571                         syslog( LOG_ALERT, "error parsing result from X.500" );
572                         unbind_and_exit( EX_TEMPFAIL );
573                 }
574
575                 /* otherwise one matched by rdn - send to it */
576                 ldap_msgfree( res );
577                 res = tmpres;
578         }
579
580         /*
581          * if we get this far, it means that we found a single match for
582          * name.  for a user, we deliver to the mail attribute or bounce
583          * with address and phone if no mail attr.  for a group, we
584          * deliver to all members or bounce to rfc822ErrorsTo if no members.
585          */
586
587         /* trouble */
588         if ( (e = ldap_first_entry( ld, res )) == NULL ) {
589                 syslog( LOG_ALERT, "error parsing entry from X.500" );
590                 unbind_and_exit( EX_TEMPFAIL );
591         }
592
593         dn = ldap_get_dn( ld, e );
594
595         if ( type == GROUP_ERRORS ) {
596                 /* sent to group-errors - resend to [rfc822]ErrorsTo attr */
597                 do_group_errors( e, dn, to, nto, err, nerr );
598
599         } else if ( type == GROUP_REQUEST ) {
600                 /* sent to group-request - resend to [rfc822]RequestsTo attr */
601                 do_group_request( e, dn, to, nto, err, nerr );
602
603         } else if ( type == GROUP_MEMBERS ) {
604                 /* sent to group-members - expand */
605                 do_group_members( e, dn, to, nto, togroups, ngroups, err,
606                     nerr );
607
608         } else if ( type == GROUP_OWNER ) {
609                 /* sent to group-owner - resend to owner attr */
610                 do_group_owner( e, dn, to, nto, err, nerr );
611
612         } else if ( isgroup( e ) ) {
613                 /* 
614                  * sent to group - resend from [rfc822]ErrorsTo if it's there,
615                  * otherwise, expand the group
616                  */
617
618                 do_group( e, dn, to, nto, togroups, ngroups, err, nerr );
619
620                 ldap_msgfree( res );
621
622         } else {
623                 /*
624                  * sent to user - mail attribute => add it to the to list,
625                  * otherwise bounce
626                  */
627                 if ( (mail = ldap_get_values( ld, e, "mail" )) != NULL ) {
628                         char    buf[1024];
629                         char    *h;
630                         int     i, j;
631
632                         /* try to detect simple mail loops */
633                         sprintf( buf, "%s@%s", name, host );
634                         for ( i = 0; mail[i] != NULL; i++ ) {
635                                 /*
636                                  * address is the same as the one we're
637                                  * sending to - mail loop.  syslog the
638                                  * problem, bounce a message back to the
639                                  * sender (who else?), and delete the bogus
640                                  * addr from the list.
641                                  */
642
643                                 if ( (h = strchr( mail[i], '@' )) != NULL ) {
644                                         h++;
645                                         if ( strcasecmp( h, host ) == 0 ) {
646                                                 syslog( LOG_ALERT,
647                                             "potential loop detected (%s)",
648                                                     mail[i] );
649                                         }
650                                 }
651
652                                 if ( mailcmp( buf, mail[i] ) == 0 ) {
653                                         syslog( LOG_ALERT,
654                                             "loop detected (%s)", mail[i] );
655
656                                         /* remove the bogus address */
657                                         for ( j = i; mail[j] != NULL; j++ ) {
658                                                 mail[j] = mail[j+1];
659                                         }
660                                 }
661                         }
662                         if ( mail[0] != NULL ) {
663                                 add_to( to, nto, mail );
664                         } else {
665                                 add_error( err, nerr, E_NOEMAIL, name, res );
666                         }
667
668                         ldap_value_free( mail );
669                 } else {
670                         add_error( err, nerr, E_NOEMAIL, name, res );
671                 }
672
673                 /*
674                  * If the user is on vacation, send a copy of the mail to
675                  * the vacation server.  The address is constructed from
676                  * the vacationhost (set in a command line argument) and
677                  * the uid (XXX this should be more general XXX).
678                  */
679
680                 if ( vacationhost != NULL && (onvacation = ldap_get_values( ld,
681                     e, "onVacation" )) != NULL && strcasecmp( onvacation[0],
682                     "TRUE" ) == 0 ) {
683                         char    buf[1024];
684                         char    *vaddr[2];
685
686                         if ( (uid = ldap_get_values( ld, e, "uid" )) != NULL ) {
687                                 sprintf( buf, "%s@%s", uid[0], vacationhost );
688
689                                 vaddr[0] = buf;
690                                 vaddr[1] = NULL;
691
692                                 add_to( to, nto, vaddr );
693                         } else {
694                                 syslog( LOG_ALERT,
695                                     "user without a uid on vacation (%s)",
696                                     name );
697                         }
698                 }
699         }
700
701         if ( onvacation != NULL ) {
702                 ldap_value_free( onvacation );
703         }
704         if ( uid != NULL ) {
705                 ldap_value_free( uid );
706         }
707         free( dn );
708 }
709
710 static int
711 do_group(
712         LDAPMessage *e,
713         char    *dn,
714         char    ***to,
715         int     *nto,
716         Group   **togroups,
717         int     *ngroups,
718         Error   **err,
719         int     *nerr
720 )
721 {
722         int     i;
723         char    **moderator;
724
725         /*
726          * If this group has an rfc822ErrorsTo attribute, we need to
727          * arrange for errors involving this group to go there, not
728          * to the sender.  Since sendmail only has the concept of a
729          * single sender, we arrange for errors to go to groupname-errors,
730          * which we then handle specially when (if) it comes back to us
731          * by expanding to all the rfc822ErrorsTo addresses.  If it has no
732          * rfc822ErrorsTo attribute, we call do_group_members() to expand
733          * the group.
734          */
735
736         if ( group_loop( dn ) ) {
737                 return( -1 );
738         }
739
740         /*
741          * check for moderated groups - if the group has a moderator
742          * attribute, we check to see if the from address is one of
743          * the moderator values.  if so, continue on.  if not, arrange
744          * to send the mail to the moderator(s).  need to do this before
745          * we change the from below.
746          */
747
748         if ( (moderator = ldap_get_values( ld, e, "moderator" )) != NULL ) {
749                 /* check if it came from any of the group's moderators */
750                 for ( i = 0; moderator[i] != NULL; i++ ) {
751                         if ( strcasecmp( moderator[i], mailfrom ) == 0 )
752                                 break;
753                 }
754
755                 /* not from the moderator? */
756                 if ( moderator[i] == NULL ) {
757                         add_to( to, nto, moderator );
758                         ldap_value_free( moderator );
759
760                         return( 0 );
761                 }
762                 /* else from the moderator - fall through and deliver it */
763         }
764
765         if (strcmp(MAIL500_BOUNCEFROM, mailfrom) != 0 &&
766             has_attributes( e, "rfc822ErrorsTo", "errorsTo" ) ) {
767                 add_group( dn, togroups, ngroups );
768
769                 return( 0 );
770         }
771
772         do_group_members( e, dn, to, nto, togroups, ngroups, err, nerr );
773
774         return( 0 );
775 }
776
777 /* ARGSUSED */
778 static void
779 do_group_members(
780         LDAPMessage *e,
781         char    *dn,
782         char    ***to,
783         int     *nto,
784         Group   **togroups,
785         int     *ngroups,
786         Error   **err,
787         int     *nerr
788 )
789 {
790         int             i, rc, anymembers;
791         char            *ndn;
792         char            **mail, **member, **joinable, **suppress;
793         char            filter[1024];
794         LDAPMessage     *ee, *res;
795         struct timeval  timeout;
796         int             opt;
797
798         /*
799          * if all has gone according to plan, we've already arranged for
800          * errors to go to the [rfc822]ErrorsTo attributes (if they exist),
801          * so all we have to do here is arrange to send to the
802          * rfc822Mailbox attribute, the member attribute, and anyone who
803          * has joined the group by setting memberOfGroup equal to the
804          * group dn.
805          */
806
807         /* add members in the group itself - mail attribute */
808         anymembers = 0;
809         if ( (mail = ldap_get_values( ld, e, "mail" )) != NULL ) {
810                 anymembers = 1;
811                 add_to( to, nto, mail );
812
813                 ldap_value_free( mail );
814         }
815
816         /* add members in the group itself - member attribute */
817         if ( (member = ldap_get_values( ld, e, "member" )) != NULL ) {
818                 suppress = ldap_get_values( ld, e, "suppressNoEmailError" );
819                 anymembers = 1;
820                 for ( i = 0; member[i] != NULL; i++ ) {
821                         if ( strcasecmp( dn, member[i] ) == 0 ) {
822                                 syslog( LOG_ALERT, "group (%s) contains itself",
823                                     dn );
824                                 continue;
825                         }
826                         add_member( dn, member[i], to, nto, togroups,
827                             ngroups, err, nerr, suppress );
828                 }
829
830                 if ( suppress ) {
831                         ldap_value_free( suppress );
832                 }
833                 ldap_value_free( member );
834         }
835
836         /* add members who have joined by setting memberOfGroup */
837         if ( (joinable = ldap_get_values( ld, e, "joinable" )) != NULL ) {
838                 if ( strcasecmp( joinable[0], "FALSE" ) == 0 ) {
839                         if ( ! anymembers ) {
840                                 add_error( err, nerr, E_NOMEMBERS, dn,
841                                     NULL );
842                         }
843
844                         ldap_value_free( joinable );
845                         return;
846                 }
847                 ldap_value_free( joinable );
848
849                 sprintf( filter, "(memberOfGroup=%s)", dn );
850
851                 timeout.tv_sec = MAIL500_TIMEOUT;
852                 timeout.tv_usec = 0;
853
854                 /* for each subtree to look in... */
855                 opt = MAIL500_MAXAMBIGUOUS;
856                 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &opt);
857                 for ( i = 0; base[i].b_dn != NULL; i++ ) {
858                         /* find entries that have joined this group... */
859                         rc = ldap_search_st( ld, base[i].b_dn,
860                             LDAP_SCOPE_SUBTREE, filter, attrs, 0, &timeout,
861                             &res );
862
863                         if ( rc == LDAP_SIZELIMIT_EXCEEDED ||
864                             rc == LDAP_TIMELIMIT_EXCEEDED ) {
865                                 syslog( LOG_ALERT,
866                                     "group search limit exceeded %d", rc );
867                                 unbind_and_exit( EX_TEMPFAIL );
868                         }
869
870                         if ( rc != LDAP_SUCCESS ) {
871                                 syslog( LOG_ALERT, "group search return 0x%x",
872                                     rc );
873                                 unbind_and_exit( EX_TEMPFAIL );
874                         }
875
876                         /* for each entry that has joined... */
877                         for ( ee = ldap_first_entry( ld, res ); ee != NULL;
878                             ee = ldap_next_entry( ld, ee ) ) {
879                                 anymembers = 1;
880                                 if ( isgroup( ee ) ) {
881                                         ndn = ldap_get_dn( ld, ee );
882
883                                         if ( do_group( e, ndn, to, nto,
884                                             togroups, ngroups, err, nerr )
885                                             == -1 ) {
886                                                 syslog( LOG_ALERT,
887                                                     "group loop (%s) (%s)",
888                                                     dn, ndn );
889                                         }
890
891                                         free( ndn );
892
893                                         continue;
894                                 }
895
896                                 /* add them to the to list */
897                                 if ( (mail = ldap_get_values( ld, ee, "mail" ))
898                                     != NULL ) {
899                                         add_to( to, nto, mail );
900
901                                         ldap_value_free( mail );
902
903                                 /* else generate a bounce */
904                                 } else {
905                                         ndn = ldap_get_dn( ld, ee );
906
907                                         add_error( err, nerr,
908                                             E_JOINMEMBERNOEMAIL, ndn, NULL );
909
910                                         free( ndn );
911                                 }
912                         }
913
914                         ldap_msgfree( res );
915                 }
916                 opt = MAIL500_MAXAMBIGUOUS;
917                 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &opt);
918         }
919
920         if ( ! anymembers ) {
921                 add_error( err, nerr, E_NOMEMBERS, dn, NULL );
922         }
923 }
924
925 static void
926 add_member(
927         char    *gdn,
928         char    *dn,
929         char    ***to,
930         int     *nto,
931         Group   **togroups,
932         int     *ngroups,
933         Error   **err,
934         int     *nerr,
935         char    **suppress
936 )
937 {
938         char            *ndn;
939         char            **mail;
940         int             rc;
941         LDAPMessage     *res, *e;
942         struct timeval  timeout;
943
944         timeout.tv_sec = MAIL500_TIMEOUT;
945         timeout.tv_usec = 0;
946         if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
947             attrs, 0, &timeout, &res )) != LDAP_SUCCESS ) {
948                 if ( rc == LDAP_NO_SUCH_OBJECT ) {
949                         add_error( err, nerr, E_BADMEMBER, dn, NULL );
950
951                         return;
952                 } else {
953                         syslog( LOG_ALERT, "member search return 0x%x", rc );
954
955                         unbind_and_exit( EX_TEMPFAIL );
956                 }
957         }
958
959         if ( (e = ldap_first_entry( ld, res )) == NULL ) {
960                 syslog( LOG_ALERT, "member search error parsing entry" );
961
962                 unbind_and_exit( EX_TEMPFAIL );
963         }
964         ndn = ldap_get_dn( ld, e );
965
966         /* allow groups within groups */
967         if ( isgroup( e ) ) {
968                 if ( do_group( e, ndn, to, nto, togroups, ngroups, err, nerr )
969                     == -1 ) {
970                         syslog( LOG_ALERT, "group loop (%s) (%s)", gdn, ndn );
971                 }
972
973                 free( ndn );
974
975                 return;
976         }
977
978         /* send to the member's mail attribute */
979         if ( (mail = ldap_get_values( ld, e, "mail" )) != NULL ) {
980                 add_to( to, nto, mail );
981
982                 ldap_value_free( mail );
983
984         /* else generate a bounce */
985         } else {
986                 if ( suppress == NULL || strcasecmp( suppress[0], "FALSE" )
987                     == 0 ) {
988                         add_error( err, nerr, E_MEMBERNOEMAIL, ndn, NULL );
989                 }
990         }
991
992         free( ndn );
993 }
994
995 static void
996 do_group_request(
997         LDAPMessage *e,
998         char    *dn,
999         char    ***to,
1000         int     *nto,
1001         Error   **err,
1002         int     *nerr
1003 )
1004 {
1005         char            **requeststo;
1006
1007         if ( (requeststo = get_attributes_mail_dn( e, "rfc822RequestsTo",
1008             "requestsTo" )) != NULL ) {
1009                 add_to( to, nto, requeststo );
1010
1011                 ldap_value_free( requeststo );
1012         } else {
1013                 add_error( err, nerr, E_NOREQUEST, dn, NULL );
1014         }
1015 }
1016
1017 static void
1018 do_group_errors(
1019         LDAPMessage *e,
1020         char    *dn,
1021         char    ***to,
1022         int     *nto,
1023         Error   **err,
1024         int     *nerr
1025 )
1026 {
1027         char            **errorsto;
1028
1029         if ( (errorsto = get_attributes_mail_dn( e, "rfc822ErrorsTo",
1030             "errorsTo" )) != NULL ) {
1031                 add_to( to, nto, errorsto );
1032
1033                 ldap_value_free( errorsto );
1034         } else {
1035                 add_error( err, nerr, E_NOERRORS, dn, NULL );
1036         }
1037 }
1038
1039 static void
1040 do_group_owner(
1041         LDAPMessage *e,
1042         char    *dn,
1043         char    ***to,
1044         int     *nto,
1045         Error   **err,
1046         int     *nerr
1047 )
1048 {
1049         char            **owner;
1050
1051         if ( (owner = get_attributes_mail_dn( e, "", "owner" )) != NULL ) {
1052                 add_to( to, nto, owner );
1053                 ldap_value_free( owner );
1054         } else {
1055                 add_error( err, nerr, E_NOOWNER, dn, NULL );
1056         }
1057 }
1058
1059 static void
1060 send_message( char **to )
1061 {
1062         int     pid;
1063 #ifndef HAVE_WAITPID
1064         WAITSTATUSTYPE  status;
1065 #endif
1066
1067         if ( debug ) {
1068                 char    buf[1024];
1069                 int     i;
1070
1071                 strcpy( buf, to[0] );
1072                 for ( i = 1; to[i] != NULL; i++ ) {
1073                         strcat( buf, " " );
1074                         strcat( buf, to[i] );
1075                 }
1076
1077                 syslog( LOG_ALERT, "send_message execing sendmail: (%s)", buf );
1078         }
1079
1080         /* parent */
1081         if ( (pid = fork()) != 0 ) {
1082 #ifdef HAVE_WAITPID
1083                 waitpid( pid, (int *) NULL, 0 );
1084 #else
1085                 wait4( pid, &status, WAIT_FLAGS, 0 );
1086 #endif
1087         /* child */
1088         } else {
1089                 /* to includes sendmailargs */
1090                 execv( MAIL500_SENDMAIL, to );
1091
1092                 syslog( LOG_ALERT, "execv failed" );
1093
1094                 exit( EX_TEMPFAIL );
1095         }
1096 }
1097
1098 static void
1099 send_group( Group *group, int ngroup )
1100 {
1101         int     i, pid;
1102         char    **argv;
1103         int     argc;
1104         char    *iargv[7];
1105 #ifndef HAVE_WAITPID
1106         WAITSTATUSTYPE  status;
1107 #endif
1108
1109         for ( i = 0; i < ngroup; i++ ) {
1110                 (void) rewind( stdin );
1111
1112                 iargv[0] = MAIL500_SENDMAIL;
1113                 iargv[1] = "-f";
1114                 iargv[2] = group[i].g_errorsto;
1115                 iargv[3] = "-oMrX.500";
1116                 iargv[4] = "-odi";
1117                 iargv[5] = "-oi";
1118                 iargv[6] = NULL;
1119
1120                 argv = NULL;
1121                 argc = 0;
1122                 add_to( &argv, &argc, iargv );
1123                 add_to( &argv, &argc, group[i].g_members );
1124
1125                 if ( debug ) {
1126                         char    buf[1024];
1127                         int     i;
1128
1129                         strcpy( buf, argv[0] );
1130                         for ( i = 1; i < argc; i++ ) {
1131                                 strcat( buf, " " );
1132                                 strcat( buf, argv[i] );
1133                         }
1134
1135                         syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
1136                 }
1137
1138                 /* parent */
1139                 if ( (pid = fork()) != 0 ) {
1140 #ifdef HAVE_WAITPID
1141                         waitpid( pid, (int *) NULL, 0 );
1142 #else
1143                         wait4( pid, &status, WAIT_FLAGS, 0 );
1144 #endif
1145                 /* child */
1146                 } else {
1147                         execv( MAIL500_SENDMAIL, argv );
1148
1149                         syslog( LOG_ALERT, "execv failed" );
1150
1151                         exit( EX_TEMPFAIL );
1152                 }
1153         }
1154 }
1155
1156 static void
1157 send_errors( Error *err, int nerr )
1158 {
1159         int     pid, i, namelen;
1160         FILE    *fp;
1161         int     fd[2];
1162         char    *argv[8];
1163         char    buf[1024];
1164 #ifndef HAVE_WAITPID
1165         WAITSTATUSTYPE  status;
1166 #endif
1167
1168         if ( strcmp( MAIL500_BOUNCEFROM, mailfrom ) == 0 ) {
1169             mailfrom = errorsfrom;
1170         }
1171
1172         argv[0] = MAIL500_SENDMAIL;
1173         argv[1] = "-oMrX.500";
1174         argv[2] = "-odi";
1175         argv[3] = "-oi";
1176         argv[4] = "-f";
1177         argv[5] = MAIL500_BOUNCEFROM;
1178         argv[6] = mailfrom;
1179         argv[7] = NULL;
1180
1181         if ( debug ) {
1182                 int     i;
1183
1184                 strcpy( buf, argv[0] );
1185                 for ( i = 1; argv[i] != NULL; i++ ) {
1186                         strcat( buf, " " );
1187                         strcat( buf, argv[i] );
1188                 }
1189
1190                 syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
1191         }
1192
1193         if ( pipe( fd ) == -1 ) {
1194                 syslog( LOG_ALERT, "cannot create pipe" );
1195                 exit( EX_TEMPFAIL );
1196         }
1197
1198         if ( (pid = fork()) != 0 ) {
1199                 if ( (fp = fdopen( fd[1], "w" )) == NULL ) {
1200                         syslog( LOG_ALERT, "cannot fdopen pipe" );
1201                         exit( EX_TEMPFAIL );
1202                 }
1203
1204                 fprintf( fp, "To: %s\n", mailfrom );
1205                 fprintf( fp, "From: %s\n", errorsfrom );
1206                 fprintf( fp, "Subject: undeliverable mail\n" );
1207                 fprintf( fp, "\n" );
1208                 fprintf( fp, "The following errors occurred when trying to deliver the attached mail:\n" );
1209                 for ( i = 0; i < nerr; i++ ) {
1210                         namelen = strlen( err[i].e_addr );
1211                         fprintf( fp, "\n" );
1212
1213                         switch ( err[i].e_code ) {
1214                         case E_USERUNKNOWN:
1215                                 fprintf( fp, "%s: User unknown\n", err[i].e_addr );
1216                                 break;
1217
1218                         case E_GROUPUNKNOWN:
1219                                 fprintf( fp, "%s: Group unknown\n", err[i].e_addr );
1220                                 break;
1221
1222                         case E_BADMEMBER:
1223                                 fprintf( fp, "%s: Group member does not exist\n",
1224                                     err[i].e_addr );
1225                                 fprintf( fp, "This could be because the distinguished name of the person has changed\n" );
1226                                 fprintf( fp, "If this is the case, the problem can be solved by removing and\n" );
1227                                 fprintf( fp, "then re-adding the person to the group.\n" );
1228                                 break;
1229
1230                         case E_NOREQUEST:
1231                                 fprintf( fp, "%s: Group exists but has no request address\n",
1232                                     err[i].e_addr );
1233                                 break;
1234
1235                         case E_NOERRORS:
1236                                 fprintf( fp, "%s: Group exists but has no errors-to address\n",
1237                                     err[i].e_addr );
1238                                 break;
1239
1240                         case E_NOOWNER:
1241                                 fprintf( fp, "%s: Group exists but has no owner\n",
1242                                     err[i].e_addr );
1243                                 break;
1244
1245                         case E_AMBIGUOUS:
1246                                 do_ambiguous( fp, &err[i], namelen );
1247                                 break;
1248
1249                         case E_NOEMAIL:
1250                                 do_noemail( fp, &err[i], namelen );
1251                                 break;
1252
1253                         case E_MEMBERNOEMAIL:
1254                                 fprintf( fp, "%s: Group member exists but does not have an email address\n",
1255                                     err[i].e_addr );
1256                                 break;
1257
1258                         case E_JOINMEMBERNOEMAIL:
1259                                 fprintf( fp, "%s: User has joined group but does not have an email address\n",
1260                                     err[i].e_addr );
1261                                 break;
1262
1263                         case E_LOOP:
1264                                 fprintf( fp, "%s: User has created a mail loop by adding address %s to their X.500 entry\n",
1265                                     err[i].e_addr, err[i].e_loop );
1266                                 break;
1267
1268                         case E_NOMEMBERS:
1269                                 fprintf( fp, "%s: Group has no members\n",
1270                                     err[i].e_addr );
1271                                 break;
1272
1273                         default:
1274                                 syslog( LOG_ALERT, "unknown error %d", err[i].e_code );
1275                                 unbind_and_exit( EX_TEMPFAIL );
1276                                 break;
1277                         }
1278                 }
1279
1280                 fprintf( fp, "\n------- The original message sent:\n\n" );
1281
1282                 while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
1283                         fputs( buf, fp );
1284                 }
1285                 fclose( fp );
1286
1287 #ifdef HAVE_WAITPID
1288                 waitpid( pid, (int *) NULL, 0 );
1289 #else
1290                 wait4( pid, &status, WAIT_FLAGS, 0 );
1291 #endif
1292         } else {
1293                 dup2( fd[0], 0 );
1294
1295                 execv( MAIL500_SENDMAIL, argv );
1296
1297                 syslog( LOG_ALERT, "execv failed" );
1298
1299                 exit( EX_TEMPFAIL );
1300         }
1301 }
1302
1303 static void
1304 do_noemail( FILE *fp, Error *err, int namelen )
1305 {
1306         int             i, last;
1307         char            *dn, *rdn;
1308         char            **ufn, **vals;
1309
1310         fprintf(fp, "%s: User has no email address registered.\n",
1311             err->e_addr );
1312         fprintf( fp, "%*s  Name, title, postal address and phone for '%s':\n\n",
1313             namelen, " ", err->e_addr );
1314
1315         /* name */
1316         dn = ldap_get_dn( ld, err->e_msg );
1317         ufn = ldap_explode_dn( dn, 1 );
1318         rdn = strdup( ufn[0] );
1319         if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1320                 if ( (vals = ldap_get_values( ld, err->e_msg, "cn" ))
1321                     != NULL ) {
1322                         for ( i = 0; vals[i]; i++ ) {
1323                                 last = strlen( vals[i] ) - 1;
1324                                 if ( isdigit((unsigned char) vals[i][last]) ) {
1325                                         rdn = strdup( vals[i] );
1326                                         break;
1327                                 }
1328                         }
1329
1330                         ldap_value_free( vals );
1331                 }
1332         }
1333         fprintf( fp, "%*s  %s\n", namelen, " ", rdn );
1334         free( dn );
1335         free( rdn );
1336         ldap_value_free( ufn );
1337
1338         /* titles or descriptions */
1339         if ( (vals = ldap_get_values( ld, err->e_msg, "title" )) == NULL &&
1340             (vals = ldap_get_values( ld, err->e_msg, "description" ))
1341             == NULL ) {
1342                 fprintf( fp, "%*s  No title or description registered\n",
1343                     namelen, " " );
1344         } else {
1345                 for ( i = 0; vals[i] != NULL; i++ ) {
1346                         fprintf( fp, "%*s  %s\n", namelen, " ", vals[i] );
1347                 }
1348
1349                 ldap_value_free( vals );
1350         }
1351
1352         /* postal address */
1353         if ( (vals = ldap_get_values( ld, err->e_msg, "postalAddress" ))
1354             == NULL ) {
1355                 fprintf( fp, "%*s  No postal address registered\n", namelen,
1356                     " " );
1357         } else {
1358                 fprintf( fp, "%*s  ", namelen, " " );
1359                 for ( i = 0; vals[0][i] != '\0'; i++ ) {
1360                         if ( vals[0][i] == '$' ) {
1361                                 fprintf( fp, "\n%*s  ", namelen, " " );
1362                                 while ( isspace((unsigned char) vals[0][i+1]) )
1363                                         i++;
1364                         } else {
1365                                 fprintf( fp, "%c", vals[0][i] );
1366                         }
1367                 }
1368                 fprintf( fp, "\n" );
1369
1370                 ldap_value_free( vals );
1371         }
1372
1373         /* telephone number */
1374         if ( (vals = ldap_get_values( ld, err->e_msg, "telephoneNumber" ))
1375             == NULL ) {
1376                 fprintf( fp, "%*s  No phone number registered\n", namelen,
1377                     " " );
1378         } else {
1379                 for ( i = 0; vals[i] != NULL; i++ ) {
1380                         fprintf( fp, "%*s  %s\n", namelen, " ", vals[i] );
1381                 }
1382
1383                 ldap_value_free( vals );
1384         }
1385 }
1386
1387 /* ARGSUSED */
1388 static void
1389 do_ambiguous( FILE *fp, Error *err, int namelen )
1390 {
1391         int             i, last;
1392         char            *dn, *rdn;
1393         char            **ufn, **vals;
1394         LDAPMessage     *e;
1395
1396         i = ldap_result2error( ld, err->e_msg, 0 );
1397
1398         fprintf( fp, "%s: Ambiguous user.  %s%d matches found:\n\n",
1399             err->e_addr, i == LDAP_SIZELIMIT_EXCEEDED ? "First " : "",
1400             ldap_count_entries( ld, err->e_msg ) );
1401
1402         for ( e = ldap_first_entry( ld, err->e_msg ); e != NULL;
1403             e = ldap_next_entry( ld, e ) ) {
1404                 dn = ldap_get_dn( ld, e );
1405                 ufn = ldap_explode_dn( dn, 1 );
1406                 rdn = strdup( ufn[0] );
1407                 if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1408                         if ( (vals = ldap_get_values( ld, e, "cn" )) != NULL ) {
1409                                 for ( i = 0; vals[i]; i++ ) {
1410                                         last = strlen( vals[i] ) - 1;
1411                                         if (isdigit((unsigned char) vals[i][last])) {
1412                                                 rdn = strdup( vals[i] );
1413                                                 break;
1414                                         }
1415                                 }
1416
1417                                 ldap_value_free( vals );
1418                         }
1419                 }
1420
1421                 if ( isgroup( e ) ) {
1422                         vals = ldap_get_values( ld, e, "description" );
1423                 } else {
1424                         vals = ldap_get_values( ld, e, "title" );
1425                 }
1426
1427                 fprintf( fp, "    %-20s %s\n", rdn, vals ? vals[0] : "" );
1428                 for ( i = 1; vals && vals[i] != NULL; i++ ) {
1429                         fprintf( fp, "                         %s\n", vals[i] );
1430                 }
1431
1432                 free( dn );
1433                 free( rdn );
1434                 ldap_value_free( ufn );
1435                 if ( vals != NULL )
1436                         ldap_value_free( vals );
1437         }
1438 }
1439
1440 static int
1441 count_values( char **list )
1442 {
1443         int     i;
1444
1445         for ( i = 0; list && list[i] != NULL; i++ )
1446                 ;       /* NULL */
1447
1448         return( i );
1449 }
1450
1451 static void
1452 add_to( char ***list, int *nlist, char **new )
1453 {
1454         int     i, nnew, oldnlist;
1455
1456         nnew = count_values( new );
1457
1458         oldnlist = *nlist;
1459         if ( *list == NULL || *nlist == 0 ) {
1460                 *list = (char **) malloc( (nnew + 1) * sizeof(char *) );
1461                 *nlist = nnew;
1462         } else {
1463                 *list = (char **) realloc( *list, *nlist * sizeof(char *) +
1464                     nnew * sizeof(char *) + sizeof(char *) );
1465                 *nlist += nnew;
1466         }
1467
1468         for ( i = 0; i < nnew; i++ )
1469                 (*list)[i + oldnlist] = strdup( new[i] );
1470         (*list)[*nlist] = NULL;
1471 }
1472
1473 static int
1474 isgroup( LDAPMessage *e )
1475 {
1476         int     i;
1477         char    **oclist;
1478
1479         oclist = ldap_get_values( ld, e, "objectClass" );
1480
1481         for ( i = 0; oclist[i] != NULL; i++ ) {
1482                 if ( strcasecmp( oclist[i], "rfc822MailGroup" ) == 0 ) {
1483                         ldap_value_free( oclist );
1484                         return( 1 );
1485                 }
1486         }
1487         ldap_value_free( oclist );
1488
1489         return( 0 );
1490 }
1491
1492 static void
1493 add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg )
1494 {
1495         if ( *nerr == 0 ) {
1496                 *err = (Error *) malloc( sizeof(Error) );
1497         } else {
1498                 *err = (Error *) realloc( *err, (*nerr + 1) * sizeof(Error) );
1499         }
1500
1501         (*err)[*nerr].e_code = code;
1502         (*err)[*nerr].e_addr = strdup( addr );
1503         (*err)[*nerr].e_msg = msg;
1504         (*nerr)++;
1505 }
1506
1507 static void
1508 add_group( char *dn, Group **list, int *nlist )
1509 {
1510         int     i, namelen;
1511         char    **ufn;
1512
1513         for ( i = 0; i < *nlist; i++ ) {
1514                 if ( strcmp( dn, (*list)[i].g_dn ) == 0 ) {
1515                         syslog( LOG_ALERT, "group loop 2 detected (%s)", dn );
1516                         return;
1517                 }
1518         }
1519
1520         ufn = ldap_explode_dn( dn, 1 );
1521         namelen = strlen( ufn[0] );
1522
1523         if ( *nlist == 0 ) {
1524                 *list = (Group *) malloc( sizeof(Group) );
1525         } else {
1526                 *list = (Group *) realloc( *list, (*nlist + 1) *
1527                     sizeof(Group) );
1528         }
1529
1530         /* send errors to groupname-errors@host */
1531         (*list)[*nlist].g_errorsto = (char *) malloc( namelen + sizeof(ERRORS)
1532             + hostlen + 2 );
1533         sprintf( (*list)[*nlist].g_errorsto, "%s-%s@%s", ufn[0], ERRORS, host );
1534         (void) canonical( (*list)[*nlist].g_errorsto );
1535
1536         /* send to groupname-members@host - make it a list for send_group */
1537         (*list)[*nlist].g_members = (char **) malloc( 2 * sizeof(char *) );
1538         (*list)[*nlist].g_members[0] = (char *) malloc( namelen +
1539             sizeof(MEMBERS) + hostlen + 2 );
1540         sprintf( (*list)[*nlist].g_members[0], "%s-%s@%s", ufn[0], MEMBERS,
1541             host );
1542         (void) canonical( (*list)[*nlist].g_members[0] );
1543         (*list)[*nlist].g_members[1] = NULL;
1544
1545         /* save the group's dn so we can check for loops above */
1546         (*list)[*nlist].g_dn = strdup( dn );
1547
1548         (*nlist)++;
1549
1550         ldap_value_free( ufn );
1551 }
1552
1553 static void
1554 unbind_and_exit( int rc )
1555 {
1556         int     i;
1557
1558         if ( (i = ldap_unbind( ld )) != LDAP_SUCCESS )
1559                 syslog( LOG_ALERT, "ldap_unbind failed %d\n", i );
1560
1561         exit( rc );
1562 }
1563
1564 static char *
1565 canonical( char *s )
1566 {
1567         char    *saves = s;
1568
1569         for ( ; *s != '\0'; s++ ) {
1570                 if ( *s == ' ' )
1571                         *s = '.';
1572         }
1573
1574         return( saves );
1575 }
1576
1577 static int
1578 group_loop( char *dn )
1579 {
1580         int             i;
1581         static char     **groups;
1582         static int      ngroups;
1583
1584         for ( i = 0; i < ngroups; i++ ) {
1585                 if ( strcmp( dn, groups[i] ) == 0 )
1586                         return( 1 );
1587         }
1588
1589         if ( ngroups == 0 )
1590                 groups = (char **) malloc( sizeof(char *) );
1591         else
1592                 groups = (char **) realloc( groups,
1593                     (ngroups + 1) * sizeof(char *) );
1594
1595         groups[ngroups++] = strdup( dn );
1596
1597         return( 0 );
1598 }
1599
1600 static int
1601 has_attributes( LDAPMessage *e, char *attr1, char *attr2 )
1602 {
1603         char    **attr;
1604
1605         if ( (attr = ldap_get_values( ld, e, attr1 )) != NULL ) {
1606                 ldap_value_free( attr );
1607                 return( 1 );
1608         }
1609
1610         if ( (attr = ldap_get_values( ld, e, attr2 )) != NULL ) {
1611                 ldap_value_free( attr );
1612                 return( 1 );
1613         }
1614
1615         return( 0 );
1616 }
1617
1618 static char **
1619 get_attributes_mail_dn(
1620     LDAPMessage *e,
1621     char *attr1,
1622     char *attr2                 /* this one is dn-valued */
1623 )
1624 {
1625         LDAPMessage     *ee, *res;
1626         char            **vals, **dnlist, **mail, **grname;
1627         char            *dn;
1628         int             nto = 0, i, rc;
1629         struct timeval  timeout;
1630
1631         dn = ldap_get_dn( ld, e );
1632
1633         vals = ldap_get_values( ld, e, attr1 );
1634         for ( nto = 0; vals != NULL && vals[nto] != NULL; nto++ )
1635                 ;       /* NULL */
1636
1637         if ( (dnlist = ldap_get_values( ld, e, attr2 )) != NULL ) {
1638                 timeout.tv_sec = MAIL500_TIMEOUT;
1639                 timeout.tv_usec = 0;
1640
1641                 for ( i = 0; dnlist[i] != NULL; i++ ) {
1642                         if ( (rc = ldap_search_st( ld, dnlist[i],
1643                             LDAP_SCOPE_BASE, "(objectclass=*)", attrs, 0,
1644                             &timeout, &res )) != LDAP_SUCCESS ) {
1645                                 if ( rc != LDAP_NO_SUCH_OBJECT ) {
1646                                         unbind_and_exit( EX_TEMPFAIL );
1647                                 }
1648
1649                                 syslog( LOG_ALERT, "bad (%s) dn (%s)", attr2,
1650                                     dnlist[i] );
1651
1652                                 continue;
1653                         }
1654
1655                         if ( (ee = ldap_first_entry( ld, res )) == NULL ) {
1656                                 syslog( LOG_ALERT, "error parsing x500 entry" );
1657                                 continue;
1658                         }
1659
1660                         if ( isgroup(ee) ) {
1661                                 char    *graddr[2];
1662
1663                                 grname = ldap_explode_dn( dnlist[i], 1 );
1664
1665                                 /* groupname + host + @ + null */
1666                                 graddr[0] = (char *) malloc( strlen( grname[0] )
1667                                     + strlen( host ) + 2 );
1668                                 graddr[1] = NULL;
1669                                 sprintf( graddr[0], "%s@%s", grname[0], host);
1670                                 (void) canonical( graddr[0] );
1671
1672                                 add_to( &vals, &nto, graddr );
1673
1674                                 free( graddr[0] );
1675                                 ldap_value_free( grname );
1676                         } else if ( (mail = ldap_get_values( ld, ee, "mail" ))
1677                             != NULL ) {
1678                                 add_to( &vals, &nto, mail );
1679
1680                                 ldap_value_free( mail );
1681                         }
1682
1683                         ldap_msgfree( res );
1684                 }
1685         }
1686
1687         return( vals );
1688 }