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