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