int env_truncate_and_unlink( struct envelope *env, SNET *snet_lock ) { char efile_fname[ MAXPATHLEN + 1 ]; if ( snet_lock != NULL ) { if ( ftruncate( snet_fd( snet_lock ), (off_t)0 ) == 0 ) { env_unlink( env ); return( 0 ); } sprintf( efile_fname, "%s/E%s", env->e_dir, env->e_id ); syslog( LOG_ERR, "Syserror: env_truncate_and_unlink ftruncate %s: %m", efile_fname ); } return( env_unlink( env )); }
int f_stor( SNET *sn, int ac, char *av[] ) { char *sizebuf; char xscriptdir[ MAXPATHLEN ]; char upload[ MAXPATHLEN ]; char buf[ 8192 ]; char *line; char *d_tran, *d_path; int fd; int zero = 0; off_t len; ssize_t rc; struct timeval tv; struct protoent *proto; if ( !prevstor ) { /* Turn off TCP_NODELAY for stores */ if (( proto = getprotobyname( "tcp" )) == NULL ) { syslog( LOG_ERR, "f_stor: getprotobyname: %m" ); return( -1 ); } if ( setsockopt( snet_fd( sn ), proto->p_proto, TCP_NODELAY, &zero, sizeof( zero )) != 0 ) { syslog( LOG_ERR, "f_stor: snet_setopt: %m" ); return( -1 ); } prevstor = 1; } if ( checkuser && ( !authorized )) { snet_writef( sn, "%d Not logged in\r\n", 551 ); exit( 1 ); } /* decode() uses static mem, so strdup() */ if (( d_tran = decode( av[ 2 ] )) == NULL ) { syslog( LOG_ERR, "f_stor: decode: buffer too small" ); snet_writef( sn, "%d Line too long\r\n", 540 ); return( 1 ); } if (( d_tran = strdup( d_tran )) == NULL ) { syslog( LOG_ERR, "f_stor: strdup: %s: %m", d_tran ); return( -1 ); } switch ( keyword( ac, av )) { case K_TRANSCRIPT: if ( snprintf( xscriptdir, MAXPATHLEN, "tmp/file/%s", d_tran ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stor: xscriptdir path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } if ( snprintf( upload, MAXPATHLEN, "tmp/transcript/%s", d_tran ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stor: upload path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } /* keep encoded transcript name, since it will just be * used later to compare in a stor file. */ if ( strlen( av[ 2 ] ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stor: upload_xscript path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } strcpy( upload_xscript, av[ 2 ] ); /* make the directory for the files of this xscript to live in. */ if ( mkdir( xscriptdir, 0777 ) < 0 ) { if ( errno == EEXIST ) { snet_writef( sn, "%d Transcript exists\r\n", 551 ); exit( 1 ); } snet_writef( sn, "%d %s: %s\r\n", 551, xscriptdir, strerror( errno )); exit( 1 ); } break; case K_FILE: /* client must have provided a transcript name before giving * files in that transcript */ if (( strcmp( upload_xscript, av[ 2 ] ) != 0 )) { snet_writef( sn, "%d Incorrect Transcript %s\r\n", 552, av[ 2 ] ); exit( 1 ); } /* decode() uses static mem, so strdup() */ if (( d_path = decode( av[ 3 ] )) == NULL ) { syslog( LOG_ERR, "f_stor: decode: buffer too small" ); snet_writef( sn, "%d Line too long\r\n", 540 ); return( 1 ); } if (( d_path = strdup( d_path )) == NULL ) { syslog( LOG_ERR, "f_stor: strdup: %s: %m", d_path ); return( -1 ); } if ( d_path[ 0 ] == '/' ) { if ( snprintf( upload, MAXPATHLEN, "tmp/file/%s%s", d_tran, d_path ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stor: upload path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } } else { if ( snprintf( upload, MAXPATHLEN, "tmp/file/%s/%s", d_tran, d_path ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stor: upload path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } } free( d_path ); free( d_tran ); break; default: snet_writef( sn, "%d STOR Syntax error\r\n", 550 ); exit( 1 ); } if (( fd = open( upload, O_CREAT|O_EXCL|O_WRONLY, 0666 )) < 0 ) { if ( mkdirs( upload ) < 0 ) { syslog( LOG_ERR, "f_stor: mkdir: %s: %m", upload ); snet_writef( sn, "%d %s: %s\r\n", 555, upload, strerror( errno )); exit( 1 ); } if (( fd = open( upload, O_CREAT|O_EXCL|O_WRONLY, 0666 )) < 0 ) { syslog( LOG_ERR, "f_stor: open: %s: %m", upload ); snet_writef( sn, "%d %s: %s\r\n", 555, upload, strerror( errno )); exit( 1 ); } } snet_writef( sn, "%d Storing file\r\n", 350 ); tv.tv_sec = 60; tv.tv_usec = 0; if ( ( sizebuf = snet_getline( sn, &tv ) ) == NULL ) { syslog( LOG_ERR, "f_stor: snet_getline: %m" ); return( -1 ); } /* Will there be a limit? */ len = strtoofft( sizebuf, NULL, 10 ); for ( ; len > 0; len -= rc ) { tv.tv_sec = 60; tv.tv_usec = 0; if (( rc = snet_read( sn, buf, MIN( len, sizeof( buf )), &tv )) <= 0 ) { if ( snet_eof( sn )) { syslog( LOG_ERR, "f_stor: snet_read: eof" ); } else { syslog( LOG_ERR, "f_stor: snet_read: %m" ); } return( -1 ); } if ( write( fd, buf, rc ) != rc ) { snet_writef( sn, "%d %s: %s\r\n", 555, upload, strerror( errno )); exit( 1 ); } } if ( len != 0 ) { syslog( LOG_ERR, "f_stor: len is %" PRIofft "d", len ); snet_writef( sn, "%d %s: internal error!\r\n", 555, upload ); exit( 1 ); } if ( close( fd ) < 0 ) { snet_writef( sn, "%d %s: %s\r\n", 555, upload, strerror( errno )); exit( 1 ); } syslog( LOG_DEBUG, "f_stor: file %s stored", upload ); tv.tv_sec = 60; tv.tv_usec = 0; if (( line = snet_getline( sn, &tv )) == NULL ) { syslog( LOG_ERR, "f_stor: snet_getline: %m" ); return( -1 ); } /* make sure client agrees we're at the end */ if ( strcmp( line, "." ) != 0 ) { syslog( LOG_ERR, "f_stor: line is: %s", line ); snet_writef( sn, "%d Length doesn't match sent data %s\r\n", 555, upload ); (void)unlink( upload ); exit( 1 ); } snet_writef( sn, "%d File stored\r\n", 250 ); return( 0 ); }
int read_scookie( char *path, struct sinfo *si, void *s ) { SNET *sn; struct stat st; char *p, *line; memset( si, 0, sizeof( struct sinfo )); if (( sn = snet_open( path, O_RDONLY, 0, 0 )) == NULL ) { if ( errno != ENOENT ) { perror( path ); } return( 1 ); } if ( fstat( snet_fd( sn ), &st ) != 0 ) { (void)snet_close( sn ); perror( path ); return( -1 ); } si->si_itime = st.st_mtime; while (( line = snet_getline( sn, NULL )) != NULL ) { p = line + 1; switch( line[0] ) { case 'v': errno = 0; si->si_protocol = strtol( p, (char **)NULL, 10 ); if ( errno ) { cosign_log( APLOG_NOTICE, s, "mod_cosign: read_scookie: " "invalid protocol version %s, " "falling back to protocol v0.", p ); si->si_protocol = 0; } break; case 'i': strcpy( si->si_ipaddr, p ); break; case 'p': strcpy( si->si_user, p ); break; case 'r': strcpy( si->si_realm, p ); break; case 'f': strcpy( si->si_factor, p ); break; #ifdef KRB case 'k': strcpy( si->si_krb5tkt, p ); break; #endif /* KRB */ default: cosign_log( APLOG_ERR, s, "mod_cosign: read_scookie: unknown key %c", line[0] ); (void)snet_close( sn ); return( -1 ); } } if ( snet_close( sn ) != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: read_scookie: %s", path ); return( -1 ); } return( 0 ); }
int env_read( int mode, struct envelope *env, SNET **s_lock ) { char *line; SNET *snet; char filename[ MAXPATHLEN + 1 ]; char *hostname; int ret = 1; ino_t dinode; int version; int exp_level; int jail; int line_no = 1; struct dll_entry *e_dll; switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read unknown mode: %d", mode ); return( 1 ); case READ_QUEUE_INFO: if ( s_lock != NULL ) { syslog( LOG_ERR, "Envelope.read no lock allowed in READ_QUEUE_INFO mode" ); return( 1 ); } break; case READ_DELIVER_INFO: case READ_JAIL_INFO: break; } sprintf( filename, "%s/E%s", env->e_dir, env->e_id ); if (( snet = snet_open( filename, O_RDWR, 0, 1024 * 1024 )) == NULL ) { if ( errno != ENOENT ) { syslog( LOG_ERR, "Syserror: env_read snet_open %s: %m", filename ); } return( 1 ); } switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read invalid mode change: %d", mode ); goto cleanup; case READ_QUEUE_INFO: /* test to see if env is locked by a q_runner */ if ( lockf( snet_fd( snet ), F_TEST, 0 ) != 0 ) { syslog( LOG_ERR, "Syserror: env_read lockf %s: %m", filename ); goto cleanup; } break; case READ_DELIVER_INFO: case READ_JAIL_INFO: if ( s_lock != NULL ) { *s_lock = snet; /* lock envelope fd */ if ( lockf( snet_fd( snet ), F_TLOCK, 0 ) != 0 ) { if ( errno != EAGAIN ) { /* file not locked by a diferent process */ syslog( LOG_ERR, "Syserror: env_read lockf %s: %m", filename ); } goto cleanup; } } break; } /* Vsimta-version */ if ((( line = snet_getline( snet, NULL )) == NULL ) || ( *line != 'V' )) { syslog( LOG_ERR, "Envelope.read %s %d: expected version syntax", filename, line_no ); goto cleanup; } sscanf( line + 1, "%d", &version ); if (( version < 1 ) || ( version > SIMTA_EFILE_VERSION )) { syslog( LOG_ERR, "Envelope.read %s %d: unsupported efile version %d", filename, line_no, version ); goto cleanup; } if ( version >= 2 ) { /* Emessage-id */ line_no++; if ((( line = snet_getline( snet, NULL )) == NULL ) || ( *line != 'E' )) { syslog( LOG_ERR, "Envelope.read %s %d: expected Equeue-id syntax", filename, line_no ); goto cleanup; } if ( strcmp( line + 1, env->e_id ) != 0 ) { syslog( LOG_WARNING, "Envelope.read %s %d: queue-id mismatch: %s", filename, line_no, line + 1 ); goto cleanup; } } line_no++; if (( line = snet_getline( snet, NULL )) == NULL ) { syslog( LOG_ERR, "Envelope.read %s %d: expected Dinode syntax", filename, line_no ); goto cleanup; } /* ignore optional M for now */ if ( *line == 'M' ) { line_no++; if (( line = snet_getline( snet, NULL )) == NULL ) { syslog( LOG_ERR, "Envelope.read %s %d: expected Dinode syntax", filename, line_no ); goto cleanup; } } /* Dinode info */ if ( *line != 'I' ) { syslog( LOG_ERR, "Envelope.read %s %d: expected Dinode syntax", filename, line_no ); goto cleanup; } sscanf( line + 1, "%lu", &dinode ); switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read invalid mode change: %d", mode ); goto cleanup; case READ_JAIL_INFO: case READ_DELIVER_INFO: if ( dinode != env->e_dinode ) { syslog( LOG_WARNING, "Envelope.read %s %d: Dinode reread mismatch: " "old %d new %d, ignoring", filename, line_no, (int)env->e_dinode, (int)dinode ); } break; case READ_QUEUE_INFO: if ( dinode == 0 ) { syslog( LOG_WARNING, "Envelope.read %s %d: Dinode is 0", filename, line_no ); } env->e_dinode = dinode; break; } /* expansion info */ if ( version >= 3 ) { line_no++; if ((( line = snet_getline( snet, NULL )) == NULL ) || ( *line != 'X' )) { syslog( LOG_ERR, "Envelope.read %s %d: expected Xpansion syntax", filename, line_no ); goto cleanup; } if ( sscanf( line + 1, "%d", &exp_level) != 1 ) { syslog( LOG_ERR, "Envelope.read %s %d: bad Xpansion syntax", filename, line_no ); goto cleanup; } switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read: invalid mode change: %d", mode ); goto cleanup; case READ_DELIVER_INFO: case READ_JAIL_INFO: if ( exp_level == env->e_n_exp_level ) { break; } syslog( LOG_WARNING, "Envelope.read %s %d: Xpansion mismatch: " "old %d new %d, ignoring", filename, line_no, env->e_n_exp_level, exp_level ); break; case READ_QUEUE_INFO: env->e_n_exp_level = exp_level; break; } } /* Jail info */ if ( version >= 5 ) { line_no++; if ((( line = snet_getline( snet, NULL )) == NULL ) || ( *line != 'J' )) { syslog( LOG_ERR, "Envelope.read %s %d: expected Jail syntax", filename, line_no ); goto cleanup; } if ( sscanf( line + 1, "%d", &jail) != 1 ) { syslog( LOG_ERR, "Envelope.read %s %d: bad Jail syntax", filename, line_no ); goto cleanup; } switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read: invalid mode change: %d", mode ); goto cleanup; case READ_JAIL_INFO: case READ_DELIVER_INFO: if ( env->e_jail == jail ) { break; } syslog( LOG_WARNING, "Envelope.read %s %d: Jail mismatch: " "old %d new %d, ignoring", filename, line_no, env->e_jail, jail ); break; case READ_QUEUE_INFO: env_jail_set( env, jail ); break; } } line_no++; if ((( line = snet_getline( snet, NULL )) == NULL ) || ( *line != 'H' )) { syslog( LOG_ERR, "Envelope.read %s %d: expected host syntax", filename, line_no ); goto cleanup; } hostname = line + 1; switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read: invalid mode change: %d", mode ); goto cleanup; case READ_DELIVER_INFO: case READ_JAIL_INFO: if ( env->e_hostname == NULL ) { if ( *hostname != '\0' ) { syslog( LOG_ERR, "Envelope.read %s %d: hostname reread mismatch, " "old \"\" new \"%s\"", filename, line_no, hostname ); goto cleanup; } } else if ( strcasecmp( hostname, env->e_hostname ) != 0 ) { syslog( LOG_ERR, "Envelope.read %s %d: hostname reread mismatch, " "old \"%s\" new \"%s\"", filename, line_no, env->e_hostname, hostname ); goto cleanup; } break; case READ_QUEUE_INFO: if ( env_hostname( env, hostname ) != 0 ) { goto cleanup; } break; } /* Dattributes */ if ( version >= 4 ) { line_no++; if (( line = snet_getline( snet, NULL )) == NULL ) { syslog( LOG_ERR, "Envelope.read %s: unexpected EOF", filename ); goto cleanup; } if ( *line != 'D' ) { syslog( LOG_ERR, "Envelope.read %s: expected Dattributes syntax", filename ); goto cleanup; } if ( sscanf( line + 1, "%d", &exp_level) != 1 ) { syslog( LOG_ERR, "Envelope.read %s: bad Dattributes syntax", filename ); goto cleanup; } if ( mode == READ_QUEUE_INFO ) { env->e_attributes = exp_level; } else if ( exp_level != env->e_attributes ) { syslog( LOG_WARNING, "Envelope.read %s: " "Dattributes reread mismatch old %d new %d", filename, env->e_attributes, exp_level ); } } /* Ffrom-address */ line_no++; if ((( line = snet_getline( snet, NULL )) == NULL ) || ( *line != 'F' )) { syslog( LOG_ERR, "Envelope.read %s %d: expected Ffrom syntax", filename, line_no ); goto cleanup; } switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read: invalid mode change: %d", mode ); goto cleanup; case READ_QUEUE_INFO: if ( env_sender( env, line + 1 ) == 0 ) { ret = 0; } goto cleanup; case READ_JAIL_INFO: case READ_DELIVER_INFO: if ( strcmp( env->e_mail, line + 1 ) != 0 ) { syslog( LOG_ERR, "Envelope.read %s %d: bad sender re-read: " "old <%s> new <%s>", filename, line_no, env->e_mail, line + 1 ); goto cleanup; } break; } /* Rto-addresses */ for ( line_no++; ( line = snet_getline( snet, NULL )) != NULL; line_no++ ) { if ( *line != 'R' ) { syslog( LOG_ERR, "Envelope.read %s %d: expected Recipient syntax", filename, line_no ); goto cleanup; } if ( env_recipient( env, line + 1 ) != 0 ) { goto cleanup; } } if ( env->e_rcpt == NULL ) { syslog( LOG_ERR, "Envelope.read %s %d: no recipients", filename, line_no ); goto cleanup; } ret = 0; /* close snet if no need to maintain lock */ if ( s_lock == NULL ) { cleanup: if ( snet_close( snet ) < 0 ) { syslog( LOG_ERR, "Liberror: env_read snet_close %s: %m", filename ); ret = 1; } } if (( simta_mid_list_enable != 0 ) && ( ret == 0 )) { if (( e_dll = dll_lookup_or_create( &simta_env_list, env->e_id, 0 )) == NULL ) { return( 1 ); } if ( e_dll->dll_data == NULL ) { e_dll->dll_data = env; env->e_env_list_entry = e_dll; } } if (( simta_sender_list_enable != 0 ) && ( ret == 0 )) { if ( sender_list_add( env ) != 0 ) { return( 1 ); } } return( ret ); }
int expand( struct envelope *unexpanded_env ) { struct expand exp; struct envelope *base_error_env; struct envelope *env_dead = NULL; struct envelope *env; struct envelope **env_p; struct recipient *rcpt; struct expand_output *host_stab = NULL; struct expand_output *eo; struct expand_output *eo_free; struct exp_addr *e_addr; struct exp_addr *next_e_addr; struct simta_red *hq_red; char *domain; SNET *snet = NULL; int n_rcpts; int return_value = 1; int env_out = 0; int fast_file_start; int sendermatch; char e_original[ MAXPATHLEN ]; char d_original[ MAXPATHLEN ]; char d_out[ MAXPATHLEN ]; /* RFC 5321 4.5.3.1.3. Path * The maximum total length of a reverse-path or forward-path is 256 * octets (including the punctuation and element separators). */ char header[ 270 ]; #ifdef HAVE_LDAP char *p; int loop_color = 1; struct exp_link *memonly; struct exp_link *parent; #endif /* HAVE_LDAP */ if ( unexpanded_env->e_hostname != NULL ) { syslog( LOG_INFO, "Expand env <%s>: already expanded for host %s", unexpanded_env->e_id, unexpanded_env->e_hostname ); return_value = 0; goto done; } memset( &exp, 0, sizeof( struct expand )); exp.exp_env = unexpanded_env; fast_file_start = simta_fast_files; /* call address_expand on each address in the expansion list. * * if an address is expandable, the address(es) that it expands to will * be added to the expansion list. These non-terminal addresses must * have their st_data set to NULL to specify that they are not to be * included in the terminal expansion list. * * Any address in the expansion list whose st_data is not NULL is * considered a terminal address and will be written out as one * of the addresses in expanded envelope(s). */ if (( base_error_env = address_bounce_create( &exp )) == NULL ) { syslog( LOG_ERR, "Expand env <%s>: address_bounce_create: %m", unexpanded_env->e_id ); goto done; } if ( env_recipient( base_error_env, unexpanded_env->e_mail ) != 0 ) { syslog( LOG_ERR, "Expand env <%s>: env_recipient: %m", unexpanded_env->e_id ); goto done; } /* add all of the original recipients to the expansion list */ for ( rcpt = unexpanded_env->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) { if ( add_address( &exp, rcpt->r_rcpt, base_error_env, ADDRESS_TYPE_EMAIL, exp.exp_env->e_mail ) != 0 ) { /* add_address syslogs errors */ goto cleanup1; } } /* process the expansion list */ for ( exp.exp_addr_cursor = exp.exp_addr_head; exp.exp_addr_cursor != NULL; exp.exp_addr_cursor = exp.exp_addr_cursor->e_addr_next ) { switch ( address_expand( &exp )) { case ADDRESS_EXCLUDE: exp.exp_addr_cursor->e_addr_terminal = 0; /* the address is not a terminal local address */ break; case ADDRESS_FINAL: exp.exp_addr_cursor->e_addr_terminal = 1; break; case ADDRESS_SYSERROR: goto cleanup1; default: panic( "Expand: address_expand out of range" ); } } #ifdef HAVE_LDAP /* Members-only processing */ for ( memonly = exp.exp_memonly; memonly != NULL; memonly = memonly->el_next ) { if ((( p = parent_permitted( memonly->el_exp_addr )) != NULL ) || ( sender_is_child( memonly->el_exp_addr->e_addr_children, loop_color++ ))) { if ( p != NULL ) { syslog( LOG_INFO, "Expand env <%s>: members-only group %s OK: " "parent %s permitted", unexpanded_env->e_id, memonly->el_exp_addr->e_addr, p ); } else { syslog( LOG_INFO, "Expand env <%s>: members-only group %s OK: " "sender is child", unexpanded_env->e_id, memonly->el_exp_addr->e_addr ); } memonly->el_exp_addr->e_addr_ldap_flags = ( memonly->el_exp_addr->e_addr_ldap_flags & ( ~STATUS_LDAP_MEMONLY )); if ( memonly->el_exp_addr->e_addr_env_moderated != NULL ) { env_free( memonly->el_exp_addr->e_addr_env_moderated ); memonly->el_exp_addr->e_addr_env_moderated = NULL; } } else { syslog( LOG_NOTICE, "Expand env <%s>: members-only group %s suppressed", unexpanded_env->e_id, memonly->el_exp_addr->e_addr ); memonly->el_exp_addr->e_addr_ldap_flags |= STATUS_LDAP_SUPPRESSOR; suppress_addrs( memonly->el_exp_addr->e_addr_children, loop_color++ ); } } #endif /* HAVE_LDAP */ sprintf( d_original, "%s/D%s", unexpanded_env->e_dir, unexpanded_env->e_id ); /* Create one expanded envelope for every host we expanded address for */ for ( e_addr = exp.exp_addr_head; e_addr != NULL; e_addr = e_addr->e_addr_next ) { #ifdef HAVE_LDAP if ((( e_addr->e_addr_ldap_flags & STATUS_LDAP_SUPPRESSED ) != 0 ) && ( !unblocked_path_to_root( e_addr, loop_color++ ))) { if ( simta_expand_debug != 0 ) { printf( "Suppressed: %s\n", e_addr->e_addr ); } continue; } if ( e_addr->e_addr_env_gmailfwd != NULL ) { e_addr->e_addr_env_gmailfwd->e_attributes = unexpanded_env->e_attributes | ENV_ATTR_ARCHIVE_ONLY; if ( simta_expand_debug != 0 ) { printf( "Group mail forwarding: %s\n", e_addr->e_addr ); env_stdout( e_addr->e_addr_env_gmailfwd ); continue; } sprintf( d_out, "%s/D%s", e_addr->e_addr_env_gmailfwd->e_dir, e_addr->e_addr_env_gmailfwd->e_id ); if ( env_dfile_copy( e_addr->e_addr_env_gmailfwd, d_original, NULL ) == 0 ) { syslog( LOG_ERR, "Expand env <%s>: %s: env_dfile_copy failed", unexpanded_env->e_id, e_addr->e_addr_env_gmailfwd->e_id ); goto cleanup3; } simta_debuglog( 2, "Expand env <%s>: group mail env %s dinode %d", unexpanded_env->e_id, e_addr->e_addr_env_gmailfwd->e_id, (int)e_addr->e_addr_env_gmailfwd->e_dinode ); sendermatch = !strcasecmp( unexpanded_env->e_mail, e_addr->e_addr_env_gmailfwd->e_mail ); n_rcpts = 0; for ( rcpt = e_addr->e_addr_env_gmailfwd->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) { n_rcpts++; if ( sendermatch ) { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s>", unexpanded_env->e_id, e_addr->e_addr_env_gmailfwd->e_id, rcpt->r_rcpt, e_addr->e_addr_env_gmailfwd->e_mail ); } else { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s> (%s)", unexpanded_env->e_id, e_addr->e_addr_env_gmailfwd->e_id, rcpt->r_rcpt, e_addr->e_addr_env_gmailfwd->e_mail, unexpanded_env->e_mail ); } } syslog( LOG_INFO, "Expand env <%s>: %s: Expanded %d group mail forwarders", unexpanded_env->e_id, e_addr->e_addr_env_gmailfwd->e_id, n_rcpts ); if ( env_outfile( e_addr->e_addr_env_gmailfwd ) != 0 ) { /* env_outfile syslogs errors */ if ( unlink( d_out ) != 0 ) { syslog( LOG_ERR, "Syserror: expand unlink %s: %m", d_out ); } goto cleanup3; } env_out++; queue_envelope( e_addr->e_addr_env_gmailfwd ); continue; } if ( e_addr->e_addr_env_moderated != NULL ) { e_addr->e_addr_env_moderated->e_attributes = unexpanded_env->e_attributes; if ( simta_expand_debug != 0 ) { printf( "Moderated: %s\n", e_addr->e_addr ); env_stdout( e_addr->e_addr_env_moderated ); continue; } sprintf( d_out, "%s/D%s", e_addr->e_addr_env_moderated->e_dir, e_addr->e_addr_env_moderated->e_id ); if ( env_dfile_copy( e_addr->e_addr_env_moderated, d_original, NULL ) == 0 ) { syslog( LOG_ERR, "Expand env <%s>: %s: env_dfile_copy failed", unexpanded_env->e_id, e_addr->e_addr_env_moderated->e_id ); goto cleanup3; } simta_debuglog( 2, "Expand env <%s>: %s: moderation env dinode %d", unexpanded_env->e_id, e_addr->e_addr_env_moderated->e_id, (int)e_addr->e_addr_env_moderated->e_dinode ); sendermatch = !strcasecmp( unexpanded_env->e_mail, e_addr->e_addr_env_moderated->e_mail ); n_rcpts = 0; for ( rcpt = e_addr->e_addr_env_moderated->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) { n_rcpts++; if ( sendermatch ) { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s>", unexpanded_env->e_id, e_addr->e_addr_env_moderated->e_id, rcpt->r_rcpt, e_addr->e_addr_env_moderated->e_mail ); } else { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s> (%s)", unexpanded_env->e_id, e_addr->e_addr_env_moderated->e_id, rcpt->r_rcpt, e_addr->e_addr_env_moderated->e_mail, unexpanded_env->e_mail ); } } syslog( LOG_INFO, "Expand env <%s>: %s: Expanded %d moderators", unexpanded_env->e_id, e_addr->e_addr_env_moderated->e_id, n_rcpts ); if ( env_outfile( e_addr->e_addr_env_moderated ) != 0 ) { /* env_outfile syslogs errors */ if ( unlink( d_out ) != 0 ) { syslog( LOG_ERR, "expand unlink %s: %m", d_out ); } goto cleanup3; } env_out++; queue_envelope( e_addr->e_addr_env_moderated ); continue; } else if ( e_addr->e_addr_ldap_flags & STATUS_LDAP_SUPPRESSOR ) { for ( parent = e_addr->e_addr_parents; parent != NULL; parent = parent->el_next ) { if ( parent->el_exp_addr == NULL ) { if ( bounce_text( base_error_env, TEXT_ERROR, "Members only group conditions not met: ", e_addr->e_addr, NULL ) != 0 ) { goto cleanup3; } if ( bounce_text( base_error_env, TEXT_ERROR, "If you have any questions, please contact the group owner: ", e_addr->e_addr_owner, NULL ) != 0 ) { goto cleanup3; } } else if (( e_addr->e_addr_ldap_flags & STATUS_LDAP_PRIVATE ) == 0 ) { if ( bounce_text( parent->el_exp_addr->e_addr_errors, TEXT_ERROR, "Members only group conditions not met: ", e_addr->e_addr, NULL ) != 0 ) { goto cleanup3; } if ( bounce_text( parent->el_exp_addr->e_addr_errors, TEXT_ERROR, "If you have any questions, please contact the group owner: ", e_addr->e_addr_owner, NULL ) != 0 ) { goto cleanup3; } } } continue; } #endif /* HAVE_LDAP */ if ( e_addr->e_addr_terminal == 0 ) { if ( simta_expand_debug != 0 ) { printf( "Non-terminal: %s\n", e_addr->e_addr ); } /* not a terminal expansion, do not add */ continue; } if ( simta_expand_debug != 0 ) { printf( "Terminal: %s\n", e_addr->e_addr ); } switch ( e_addr->e_addr_type ) { case ADDRESS_TYPE_EMAIL: if (( domain = strchr( e_addr->e_addr, '@' )) == NULL ) { syslog( LOG_ERR, "Expand env <%s>: strchr blivet", unexpanded_env->e_id ); goto cleanup3; } domain++; env = eo_lookup( host_stab, domain, e_addr->e_addr_from ); break; case ADDRESS_TYPE_DEAD: domain = NULL; env = env_dead; break; default: panic( "expand: address type out of range" ); } if ( env == NULL ) { /* Create envelope and add it to list */ if (( env = env_create( domain ? simta_dir_fast : simta_dir_dead, NULL, e_addr->e_addr_from, unexpanded_env )) == NULL ) { syslog( LOG_ERR, "Expand env <%s>: env_create: %m", unexpanded_env->e_id ); goto cleanup3; } simta_debuglog( 2, "Expand env <%s>: %s: expansion env dinode %d", unexpanded_env->e_id, env->e_id, (int)env->e_dinode ); /* fill in env */ env->e_attributes = unexpanded_env->e_attributes; if ( domain != NULL ) { if ( env_hostname( env, domain ) != 0 ) { env_free( env ); goto cleanup3; } } else { env_dead = env; } /* Add env to host_stab */ if ( eo_insert( &host_stab, env ) != 0 ) { syslog( LOG_ERR, "Expand env <%s>: eo_insert %s failed: %m", unexpanded_env->e_id, env->e_id ); env_free( env ); goto cleanup3; } } if ( env_recipient( env, e_addr->e_addr ) != 0 ) { goto cleanup3; } syslog( LOG_NOTICE, "Expand env <%s>: %s: recipient <%s> added to env for host %s", unexpanded_env->e_id, env->e_id, e_addr->e_addr, env->e_hostname ? env->e_hostname : "NULL" ); } /* Write out all expanded envelopes and place them in to the host_q */ for ( eo = host_stab; eo != NULL; eo = eo->eo_next ) { env = eo->eo_env; if ( simta_expand_debug == 0 ) { sprintf( d_out, "%s/D%s", env->e_dir, env->e_id ); /* RFC 5321 4.4 Trace Information * When the delivery SMTP server makes the "final delivery" of a * message, it inserts a return-path line at the beginning of the * mail data. This use of return-path is required; mail systems * MUST support it. The return-path line preserves the * information in the <reverse-path> from the MAIL command. * Here, final delivery means the message has left the SMTP * environment. */ if ((( hq_red = red_host_lookup( eo->eo_hostname )) != NULL ) && ( hq_red->red_deliver_type == RED_DELIVER_BINARY )) { if ( snprintf( header, 270, "Return-Path: <%s>", env->e_mail ) >= 270 ) { syslog( LOG_ERR, "Expand env <%s>: %s: return path is too large", unexpanded_env->e_id, env->e_id ); } if ( env_dfile_copy( env, d_original, header ) == 0 ) { syslog( LOG_ERR, "Expand env <%s>: %s: env_dfile_copy failed", unexpanded_env->e_id, env->e_id ); goto cleanup4; } } else { /* Dfile: link Dold_id env->e_dir/Dnew_id */ if ( link( d_original, d_out ) != 0 ) { syslog( LOG_ERR, "Syserror: expand link %s %s: %m", d_original, d_out ); goto cleanup4; } } sendermatch = !strcasecmp( unexpanded_env->e_mail, env->e_mail ); n_rcpts = 0; for ( rcpt = env->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) { n_rcpts++; if ( sendermatch ) { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s>", unexpanded_env->e_id, env->e_id, rcpt->r_rcpt, env->e_mail ); } else { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s> (%s)", unexpanded_env->e_id, env->e_id, rcpt->r_rcpt, env->e_mail, unexpanded_env->e_mail ); } } syslog( LOG_INFO, "Expand env <%s>: %s: Expanded %d recipients", unexpanded_env->e_id, env->e_id, n_rcpts ); /* Efile: write env->e_dir/Enew_id for all recipients at host */ syslog( LOG_NOTICE, "Expand env <%s>: %s: writing Efile for %s", unexpanded_env->e_id, env->e_id, env->e_hostname ? env->e_hostname : "NULL" ); if ( env_outfile( env ) != 0 ) { /* env_outfile syslogs errors */ if ( unlink( d_out ) != 0 ) { syslog( LOG_ERR, "Syserror: expand unlink %s: %m", d_out ); } goto cleanup4; } env_out++; queue_envelope( env ); } else { printf( "\n" ); env_stdout( env ); } } if ( env_out == 0 ) { syslog( LOG_NOTICE, "Expand env <%s>: no terminal recipients, " "deleting message", unexpanded_env->e_id ); } /* write errors out to disk */ env_p = &(exp.exp_errors); while (( env = *env_p ) != NULL ) { if ( simta_expand_debug == 0 ) { if ( env->e_error != 0 ) { env_p = &(env->e_next); if ( snet == NULL ) { if (( snet = snet_open( d_original, O_RDONLY, 0, 1024 * 1024 )) == NULL ) { syslog( LOG_ERR, "Liberror: expand snet_open %s: %m", d_original ); goto cleanup5; } } else { if ( lseek( snet_fd( snet ), (off_t)0, SEEK_SET ) != 0 ) { syslog( LOG_ERR, "Syserror: q_deliver lseek: %m" ); panic( "q_deliver lseek fail" ); } } /* write out error text, get Dfile inode */ if ( bounce_dfile_out( env, snet ) == 0 ) { if ( snet != NULL ) { if ( snet_close( snet ) != 0 ) { syslog( LOG_ERR, "Liberror: expand snet_close %s: %m", d_original ); } } goto cleanup5; } simta_debuglog( 2, "Expand env <%s>: %s: errors env dinode %d", unexpanded_env->e_id, env->e_id, (int)env->e_dinode ); line_file_free( env->e_err_text ); env->e_err_text = NULL; env->e_error = 0; if ( env_outfile( env ) != 0 ) { /* env_outfile syslogs errors */ sprintf( d_out, "%s/D%s", env->e_dir, env->e_id ); if ( unlink( d_out ) != 0 ) { syslog( LOG_ERR, "Syserror: expand unlink %s: %m", d_out ); } goto cleanup5; } sendermatch = !strcasecmp( unexpanded_env->e_mail, env->e_mail ); n_rcpts = 0; for ( rcpt = env->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) { n_rcpts++; if ( sendermatch ) { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s>", unexpanded_env->e_id, env->e_id, rcpt->r_rcpt, env->e_mail ); } else { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s> (%s)", unexpanded_env->e_id, env->e_id, rcpt->r_rcpt, env->e_mail, unexpanded_env->e_mail ); } } syslog( LOG_NOTICE, "Expand env <%s>: %s: Expanded %d bounces", unexpanded_env->e_id, env->e_id, n_rcpts ); queue_envelope( env ); } else { *env_p = env->e_next; env_free( env ); } } else { *env_p = env->e_next; bounce_stdout( env ); env_free( env ); } } if ( snet != NULL ) { if ( snet_close( snet ) != 0 ) { syslog( LOG_ERR, "Liberror: expand snet_close %s: %m", d_original ); sprintf( d_out, "%s/D%s", env->e_dir, env->e_id ); if ( unlink( d_out ) != 0 ) { syslog( LOG_ERR, "Syserror: expand unlink %s: %m", d_out ); } goto cleanup5; } snet = NULL; } syslog( LOG_INFO, "Expand env <%s>: Metric %d entries %d levels", unexpanded_env->e_id, exp.exp_entries, exp.exp_max_level ); if ( simta_expand_debug != 0 ) { return_value = 0; goto cleanup2; } if ( utime( d_original, NULL ) != 0 ) { syslog( LOG_ERR, "Syserror: expand utime %s: %m", d_original ); goto cleanup5; } if ( unexpanded_env->e_dir != simta_dir_fast ) { /* truncate orignal Efile */ sprintf( e_original, "%s/E%s", unexpanded_env->e_dir, unexpanded_env->e_id ); if ( truncate( e_original, (off_t)0 ) != 0 ) { syslog( LOG_ERR, "Syserror: expand truncate %s: %m", e_original ); goto cleanup5; } } /* delete original message */ if ( env_unlink( unexpanded_env ) != 0 ) { syslog( LOG_ERR, "Expand env <%s>: Expansion complete, can't delete message", unexpanded_env->e_id ); } else { syslog( LOG_INFO, "Expand env <%s>: Expansion complete, message deleted", unexpanded_env->e_id ); } return_value = 0; goto cleanup2; cleanup5: cleanup_envelope_list( &exp.exp_errors ); #ifdef HAVE_LDAP cleanup_envelope_list( &exp.exp_gmailfwding ); #endif /* HAVE_LDAP */ cleanup4: for ( eo = host_stab; eo != NULL; eo = eo->eo_next ) { env = eo->eo_env; eo->eo_env = NULL; if (( env->e_flags & ENV_FLAG_EFILE ) != 0 ) { queue_remove_envelope( env ); if ( env_unlink( env ) == 0 ) { syslog( LOG_WARNING, "Expand env <%s>: Message Deleted: " "System error, unwinding expansion", env->e_id ); } else { syslog( LOG_ERR, "Expand env <%s>: " "System error, can't unwind expansion", env->e_id ); } } env_free( env ); } cleanup3: #ifdef HAVE_LDAP for ( memonly = exp.exp_memonly; memonly != NULL; memonly = memonly->el_next ) { if (( memonly->el_exp_addr->e_addr_env_moderated != NULL ) && (( memonly->el_exp_addr->e_addr_env_moderated->e_flags & ENV_FLAG_EFILE ) != 0 )) { env_unlink( memonly->el_exp_addr->e_addr_env_moderated ); env_free( memonly->el_exp_addr->e_addr_env_moderated ); memonly->el_exp_addr->e_addr_env_moderated = NULL; } } #endif /* HAVE_LDAP */ if ( simta_fast_files != fast_file_start ) { syslog( LOG_ERR, "Expand env <%s>: could not unwind expansion", unexpanded_env->e_id ); return_value = 1; } cleanup2: /* free host_stab */ eo = host_stab; while ( eo != NULL ) { eo_free = eo; eo = eo->eo_next; free( eo_free ); } cleanup1: #ifdef HAVE_LDAP exp_addr_link_free( exp.exp_memonly ); #endif /* HAVE_LDAP */ /* free the expansion list */ for ( e_addr = exp.exp_addr_head; e_addr != NULL; e_addr = next_e_addr ) { next_e_addr = e_addr->e_addr_next; #ifdef HAVE_LDAP exp_addr_link_free( e_addr->e_addr_parents ); exp_addr_link_free( e_addr->e_addr_children ); permitted_destroy( e_addr ); if (( e_addr->e_addr_env_moderated != NULL ) && (( e_addr->e_addr_env_moderated->e_flags & ENV_FLAG_EFILE ) == 0 )) { env_free( e_addr->e_addr_env_moderated ); } if ( e_addr->e_addr_owner ) { free( e_addr->e_addr_owner ); } if ( e_addr->e_addr_dn ) { free( e_addr->e_addr_dn ); } #endif free( e_addr->e_addr ); free( e_addr->e_addr_from ); free( e_addr ); } done: if ( return_value != 0 ) { syslog( LOG_ERR, "Expand env <%s>: Expansion failed", unexpanded_env->e_id ); } return( return_value ); }