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