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