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