/* * Program: Synonym * File: synonym.c * Author: Ionut Nistor * Date: 20 Feb 2003 * * $Id: synonym.c,v 1.8.2.2 2004/01/19 12:20:30 diciu Exp $ * * Licensed under the Modulo Consulting Software License * (see file license.txt) * */ const char synonym_c_objid[]="$Id: synonym.c,v 1.8.2.2 2004/01/19 12:20:30 diciu Exp $"; #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "synonym.h" #include "config.h" #include "filtering.h" #include "milter_data.h" #ifdef DISCLAIMER_SUPPORT #include "body_parser.h" #endif extern sfsistat mlfi_cleanup(SMFICTX *, BOOL); _type_synonym_config synonym_config; void free_matched_action_list(matched_action * m_list); sfsistat mlfi_envfrom(ctx, envfrom) SMFICTX *ctx; char **envfrom; { /* We got a connection let's se where it is from and store the originator for further reference */ synonym_priv *priv; syslog(LOG_DEBUG, "Envfrom called"); priv = (synonym_priv*)malloc(sizeof(synonym_priv)); if(priv == NULL) { syslog(LOG_EMERG, "*** SERIOUS CONDITION - MEMORY ALLOCATION FAILED - SYSTEM UNSTABLE - TAKE IMMEDIATE ACTION ***"); priv->error_encountered = TRUE; return SMFIS_CONTINUE; } priv->resulted_actions = NULL; priv->headers = NULL; priv->error_encountered = FALSE; strncpy(priv->env_from, *envfrom, MAX_EMAIL); priv->env_from[MAX_EMAIL] = '\0'; priv->env_to=NULL; #ifdef DISCLAIMER_SUPPORT /* initialise disclaimer data */ syslog(LOG_DEBUG, "Initialising disclaimer data"); priv->output_stream = NULL; strcpy(priv->temp_filename, ""); priv->bp_state = NULL; #endif smfi_setpriv(ctx, priv); return SMFIS_CONTINUE; } sfsistat mlfi_envrcpt(ctx, envrcpt) SMFICTX *ctx; char **envrcpt; { synonym_priv * priv; to_list *new_to; priv = smfi_getpriv(ctx); if(priv == NULL) { syslog(LOG_ERR, "Cannot get mail context. Accepting message"); return SMFIS_CONTINUE; } if(priv->error_encountered) return SMFIS_CONTINUE; /* Allocate new 'to' element */ new_to = (to_list*)malloc(sizeof(to_list)); if(new_to == NULL) { syslog(LOG_EMERG, "*** SERIOUS CONDITION - MEMORY ALLOCATION FAILED - SYSTEM UNSTABLE - TAKE IMMEDIATE ACTION ***"); priv->error_encountered=TRUE; return SMFIS_CONTINUE; } strncpy(new_to->email, *envrcpt, MAX_EMAIL); new_to->email[MAX_EMAIL] = '\0'; new_to->next = NULL; if(priv->env_to == NULL) priv->env_to = new_to; else { while(priv->env_to->next != NULL) priv->env_to=priv->env_to->next; priv->env_to->next = new_to; } return SMFIS_CONTINUE; } sfsistat mlfi_header(ctx, headerf, headerv) SMFICTX *ctx; char *headerf; char *headerv; { synonym_header *new_header, *temp_header; synonym_priv * priv; priv = smfi_getpriv(ctx); if(priv == NULL) { syslog(LOG_ERR, "Cannot get mail context. Accepting message"); return SMFIS_CONTINUE; } if(priv->error_encountered) return SMFIS_CONTINUE; /* Store the headers */ /* Alloc the new header */ new_header = (synonym_header*) malloc(sizeof(synonym_header)); if(new_header == NULL) { syslog(LOG_CRIT, "CRITICAL: Memory allocation problem - cannot process the email"); priv->error_encountered = TRUE; return SMFIS_CONTINUE; } new_header->next = NULL; /* Set the values */ strncpy(new_header->header_name, headerf, MAX_HEADER_NAME_SIZE); new_header->header_name[MAX_HEADER_NAME_SIZE]='\0'; strncpy(new_header->header_value, headerv, MAX_HEADER_VALUE_SIZE); new_header->header_value[MAX_HEADER_VALUE_SIZE]='\0'; /* Link it to the end of the list */ if(priv->headers == NULL) priv->headers = new_header; else { temp_header=priv->headers; while(temp_header->next!=NULL) temp_header = temp_header->next; temp_header->next = new_header; } return SMFIS_CONTINUE; } sfsistat mlfi_eoh(ctx) SMFICTX *ctx; { synonym_priv * priv; sresult result; priv = smfi_getpriv(ctx); if(priv == NULL) { syslog(LOG_ERR, "Cannot get mail context. Accepting message"); return SMFIS_CONTINUE; } if(priv->error_encountered) return SMFIS_CONTINUE; if(synonym_config.rules == NULL) { syslog(LOG_ERR, "No rules appear to be configured. Accepting message"); return SMFIS_CONTINUE; } if(priv->headers == NULL) syslog(LOG_WARNING, "Message contains no headers. Weird"); /* Start processing rules - we should have all the required informtion at this point */ if((result = Synonym_Process_Rules(synonym_config.rules, priv->headers, priv->env_from, priv->env_to, &priv->resulted_actions)) != SYNONYM_OK) { syslog(LOG_ERR, "Failed to process the synonym rules - result code %d. Accepting message", result); return SMFIS_CONTINUE; } /* We got all the actions that matched header conditions */ return SMFIS_CONTINUE; } sfsistat mlfi_body(ctx, bodyp, bodylen) SMFICTX *ctx; u_char *bodyp; size_t bodylen; { synonym_priv *priv; #ifdef DISCLAIMER_SUPPORT int temp_fd; #endif priv = smfi_getpriv(ctx); if(priv == NULL) { syslog(LOG_ERR, "Cannot get mail context. Accepting message"); return SMFIS_CONTINUE; } if(priv->error_encountered) return SMFIS_CONTINUE; /* At some point there was an error so we proceed no longer */ syslog(LOG_DEBUG, "mlfi_body called"); if(priv!=NULL) { #ifdef DISCLAIMER_SUPPORT /* dump the body content into a temporary file */ if(priv->output_stream == NULL) { syslog(LOG_DEBUG, "Output stream was not yet initialised"); strcpy(priv->temp_filename, "/tmp/synonymXXXXXX"); if((temp_fd=mkstemp(priv->temp_filename))<0) { syslog(LOG_ERR, "Failed to open the temporary file"); priv->error_encountered = TRUE; return SMFIS_CONTINUE; } priv->output_stream = fdopen(temp_fd, "w"); if(priv->output_stream == NULL) { syslog(LOG_ERR, "Could not open file %s for write. ", priv->temp_filename); close(temp_fd); // Close the file descriptor cause is was open - if fdopen fails, the fd remains open priv->error_encountered = TRUE; return SMFIS_CONTINUE; } if(fwrite((char *)bodyp, 1, bodylen, priv->output_stream)!= bodylen) { syslog(LOG_ERR, "Failed to write to the temp file. Check disk space"); fclose(priv->output_stream); unlink(priv->temp_filename); priv->error_encountered = TRUE; return SMFIS_CONTINUE; } } else if(fwrite((char *)bodyp, 1, bodylen, priv->output_stream) != bodylen) { syslog(LOG_ERR, "Failed to add body data to the temp file"); fclose(priv->output_stream); unlink(priv->temp_filename); priv->error_encountered = TRUE; return SMFIS_CONTINUE; } #endif } return SMFIS_CONTINUE; } sfsistat mlfi_eom(ctx) SMFICTX *ctx; { synonym_priv *priv; matched_action *temp_action; char smtp_reply[MAX_SMTP_REPLY_SIZE+1]; BOOL reject_email=FALSE, delete_email=FALSE, add_copy=FALSE; char copyto_list[MAX_EMAIL*5 + 1 + sizeof(SYNONYM_COPIEDBY)]; /* We shall list max 5 addresses */ #ifdef DISCLAIMER_SUPPORT struct stat statstruct; long file_length = 0; int count; char fread_buffer[65000]; char filename[1000]; char newfilename[1000]; sfsistat rstat = SMFIS_CONTINUE; int retval = 0; int add_disclaimer = SUBTYPE_NONE; char disclaimer_text[MAX_DISCLAIMER_SIZE+1], disclaimer_html[MAX_DISCLAIMER_SIZE+1]; char *all_headers = NULL; synonym_header *temp_header; FILE * fd_processed; #endif priv = smfi_getpriv(ctx); if(priv == NULL) { syslog(LOG_ERR, "Cannot get mail context. Accepting message"); return SMFIS_CONTINUE; } if(priv->error_encountered) return SMFIS_CONTINUE; strcpy(copyto_list, SYNONYM_COPIEDBY); /* Perform the matched actions */ if(priv->resulted_actions != NULL) for(temp_action=priv->resulted_actions; temp_action != NULL; temp_action=temp_action->next) switch(temp_action->action.action_type) { case ACTION_COPY: add_copy = TRUE; smfi_addrcpt(ctx, temp_action->action.action_custom_field); if(strlen(copyto_list) + strlen(temp_action->action.action_custom_field) <= MAX_EMAIL*5 + 3) { strcat(copyto_list, temp_action->action.action_custom_field); strcat(copyto_list, ", "); } break; case ACTION_DELETE: delete_email=TRUE; break; case ACTION_REJECT: strncpy(smtp_reply, temp_action->action.action_custom_field, MAX_SMTP_REPLY_SIZE); smtp_reply[MAX_SMTP_REPLY_SIZE] = '\0'; reject_email=TRUE; break; case ACTION_DISCLAIMER: #ifdef DISCLAIMER_SUPPORT if(temp_action->action.action_text_disclaimer[0] !='\0') add_disclaimer = SUBTYPE_PLAIN; if(temp_action->action.action_html_disclaimer[0] != '\0') add_disclaimer = SUBTYPE_HTML; if(temp_action->action.action_text_disclaimer[0] !='\0' && temp_action->action.action_html_disclaimer[0] != '\0') add_disclaimer = SUBTYPE_BOTH; strcpy(disclaimer_text, temp_action->action.action_text_disclaimer); strcpy(disclaimer_html, temp_action->action.action_html_disclaimer); #else syslog(LOG_ERR, "Disclaimer matched though disclaimer support not available"); #endif break; default: syslog(LOG_ERR, "Unknown action %d. Ignoring", temp_action->action.action_type); break; } else syslog(LOG_DEBUG, "No matched actions for email"); #ifdef DISCLAIMER_SUPPORT if(add_disclaimer != SUBTYPE_NONE) { /* Cristi, call the c-client parser */ syslog(LOG_DEBUG, "Closing stream for body file and reopening for read access"); if(priv->output_stream != NULL) fclose(priv->output_stream); if(priv->temp_filename != NULL) { strcpy(filename, priv->temp_filename); strcpy(newfilename, priv->temp_filename); strcat(newfilename, ".new"); } else { syslog(LOG_ERR, "Error reading temp filename from priv."); return -1; } priv->output_stream = fopen(filename, "r"); if(priv->output_stream == NULL) { syslog(LOG_ERR, "Error opening old file for read"); return rstat; } file_length = stat(filename, &statstruct); if(file_length == -1) { unlink(filename); syslog(LOG_ERR, "Could not open body file for read"); return rstat; } syslog(LOG_DEBUG, "Mallocing body parser structure"); priv->bp_state = malloc(sizeof(body_parser_state)); if(priv->bp_state == NULL) { syslog(LOG_ERR, "Error on malloc of bp_state."); return mlfi_cleanup(ctx, TRUE); } syslog(LOG_DEBUG, "Mallocing disclaimer structure"); priv->bp_state->d_state = malloc(sizeof(disclaimer_state)); if(priv->bp_state->d_state == NULL) { syslog(LOG_ERR, "Error on malloc of bp_state."); return mlfi_cleanup(ctx, TRUE); } priv->bp_state->d_state->disclaimer_text = disclaimer_text; priv->bp_state->d_state->disclaimer_html = disclaimer_html; priv->bp_state->d_state->operation_mode = add_disclaimer; priv->bp_state->d_state->disclaimer_text_processed = 0; priv->bp_state->d_state->disclaimer_html_processed = 0; /* Initialise the output stream for the body parser to write in */ priv->bp_state->out_stream = fopen(newfilename, "w"); if(priv->bp_state->out_stream == NULL) { syslog(LOG_DEBUG, "body_parser_finalize: Failed to open new body file for write"); return BODY_PARSER_FAILURE; } syslog(LOG_DEBUG, "Parsing stream to locate text/plain"); /* All the headers are needed in a string for c-client to interpret the message body */ all_headers = malloc(10000); if(all_headers == NULL) { syslog(LOG_CRIT, "Failed to allocate memory for the headers"); priv->error_encountered = TRUE; return SMFIS_CONTINUE; } all_headers[0] = '\0'; syslog(LOG_DEBUG, "Writing headers in string."); for(temp_header = priv->headers; temp_header != NULL; temp_header = temp_header->next) { if(strlen(temp_header->header_name) + strlen(temp_header->header_value) + strlen(all_headers) + 1 < 10000) { strcat(all_headers, temp_header->header_name); strcat(all_headers, ": "); strcat(all_headers, temp_header->header_value); strcat(all_headers, "\n"); } else { syslog(LOG_ERR, "headers bigger than 10k. dropping."); break; } } syslog(LOG_DEBUG, "Finished writing headers in string."); retval = body_parser_init(all_headers, priv->output_stream, statstruct.st_size, priv->bp_state); if(retval != BODY_PARSER_SUCCESS) { syslog(LOG_ERR, "Error in body_parser_init"); } /*** NOT IMPLEMENTED YET ***/ //process_attachment_names(priv->bp_state->attachment_rule); retval = body_parser_finalize(priv->bp_state); if(retval == BODY_PARSER_SUCCESS) { fd_processed = fopen(newfilename, "r"); if(fd_processed == NULL) { syslog(LOG_ERR, "Failed to open new body for read"); } syslog(LOG_DEBUG, "Opened new body for read"); while((count = fread(fread_buffer, 1, 65000, fd_processed)) != 0) { retval = smfi_replacebody(ctx, fread_buffer, count); if(retval == MI_FAILURE) syslog(LOG_ERR, "Error in replace body."); } syslog(LOG_DEBUG, "Body was replaced, last count is %d", count); fclose(fd_processed); } free(all_headers); smfi_addheader(ctx, "X-Synonym", SYNONYM_DISCLAIMERBY); syslog(LOG_DEBUG, "Freeing disclaimer structure"); if(priv->bp_state->d_state != NULL) free(priv->bp_state->d_state); syslog(LOG_DEBUG, "Unlinking new body file"); unlink(newfilename); } if(priv->output_stream != NULL) fclose(priv->output_stream); if(priv->temp_filename != NULL) unlink(priv->temp_filename); #endif mlfi_cleanup(ctx, TRUE); if(reject_email) { if(smtp_reply[0]!='\0') if(smfi_setreply(ctx, "550", "5.7.1", smtp_reply)!= MI_SUCCESS) syslog(LOG_ERR, "Failed to set the smtp reply for message rejection"); return SMFIS_REJECT; } if(delete_email) return SMFIS_DISCARD; return SMFIS_CONTINUE; } sfsistat mlfi_close(ctx) SMFICTX *ctx; { return SMFIS_ACCEPT; } sfsistat mlfi_abort(ctx) SMFICTX *ctx; { return mlfi_cleanup(ctx, FALSE); } sfsistat mlfi_cleanup(ctx, ok) SMFICTX *ctx; BOOL ok; { sfsistat rstat = SMFIS_CONTINUE; synonym_priv *priv; to_list * clist, * plist; synonym_header *previous_header, *next_header; priv = smfi_getpriv(ctx); if (priv == NULL) { syslog(LOG_ERR, "This connection dropped before MAIL FROM:"); return rstat; } if (ok) ; /* release private memory */ next_header = previous_header = priv->headers; while(next_header != NULL) { previous_header = next_header; next_header = next_header->next; free(previous_header); } #ifdef DISCLAIMER_SUPPORT /* free the disclaimer state structure */ if(priv->bp_state != NULL) free(priv->bp_state); #endif /* Cristi, fix leak caused by matched action being abandoned */ syslog(LOG_DEBUG, "Freeing matched actions list"); if(priv->resulted_actions != NULL) free_matched_action_list(priv->resulted_actions); /* Free env to list */ clist = priv->env_to; plist = clist; while(clist != NULL) { /* all the members are statically allocated so nothing to do here */ plist = clist; clist = clist->next; free(plist); } free(priv); smfi_setpriv(ctx, NULL); /* return status */ return rstat; } struct smfiDesc smfilter = { "Synonym", /* filter name */ SMFI_VERSION, /* version code -- do not change */ #ifdef DISCLAIMER_SUPPORT SMFIF_ADDHDRS | SMFIF_ADDRCPT | SMFIF_DELRCPT | SMFIF_CHGBODY, /* flags */ #else SMFIF_ADDHDRS | SMFIF_ADDRCPT | SMFIF_DELRCPT, /* flags */ #endif NULL, /* connection info filter */ NULL, /* SMTP HELO command filter */ mlfi_envfrom, /* envelope sender filter */ mlfi_envrcpt, /* envelope recipient filter */ mlfi_header, /* header filter */ mlfi_eoh, /* end of header */ mlfi_body, /* body block filter */ mlfi_eom, /* end of message */ mlfi_abort, /* message aborted */ mlfi_close /* connection cleanup */ }; int main(argc, argv) int argc; char *argv[]; { int c; const char *args = "s:c:u:l:f:dh"; char socketname[256]; char configfile[256]; char runas_user[256]; BOOL daemon=FALSE; struct stat statbuf; char pid_str[1024]; int nread; FILE *f_pidfile; int pidno; pid_t fork_result_id; struct passwd *runas_pwent; int loglevel=DEFAULT_LOGLEVEL, logfacility=DEFAULT_LOGFACILITY; char loglevel_string[64], logfacility_string[64]; strcpy(loglevel_string, DEFAULT_LOGLEVEL_STRING); strcpy(logfacility_string, DEFAULT_LOGFACILITY_STRING); openlog("Synonym", LOG_NDELAY|LOG_PID, logfacility); setlogmask(LOG_UPTO(loglevel)); /* load some default values */ strcpy(socketname, DEFAULT_SOCKET); strcpy(configfile, DEFAULT_CONFIG); strcpy(runas_user, DEFAULT_USER); /* Process command line options */ while ((c = getopt(argc, argv, args)) != -1) { switch (c) { case 'f': /* Logging facility for syslog */ if (optarg == NULL || *optarg == '\0') { fprintf(stderr, "Invalid logging facility name\n"); exit(-1); } logfacility = -1; if(!strcasecmp(optarg, "LOG_LOCAL0")) logfacility=LOG_LOCAL0; if(!strcasecmp(optarg, "LOG_LOCAL1")) logfacility=LOG_LOCAL1; if(!strcasecmp(optarg, "LOG_LOCAL2")) logfacility=LOG_LOCAL2; if(!strcasecmp(optarg, "LOG_LOCAL3")) logfacility=LOG_LOCAL3; if(!strcasecmp(optarg, "LOG_LOCAL4")) logfacility=LOG_LOCAL4; if(!strcasecmp(optarg, "LOG_LOCAL5")) logfacility=LOG_LOCAL5; if(!strcasecmp(optarg, "LOG_LOCAL6")) logfacility=LOG_LOCAL6; if(!strcasecmp(optarg, "LOG_LOCAL7")) logfacility=LOG_LOCAL7; if(!strcasecmp(optarg, "LOG_DAEMON")) logfacility=LOG_DAEMON; if(!strcasecmp(optarg, "LOG_MAIL")) logfacility=LOG_MAIL; if(!strcasecmp(optarg, "LOG_USER")) logfacility=LOG_USER; if(logfacility == -1) { fprintf(stderr, "Invalid logging facility name - %s\n Choose one of: LOG_LOCAL0, LOG_LOCAL1, ... LOG_LOCAL7, LOG_DAEMON, LOG_MAIL, LOG_USER\n", optarg); exit(-1); } strcpy(logfacility_string, optarg); break; case 'l': /* Logging level for syslog */ if (optarg == NULL || *optarg == '\0') { fprintf(stderr, "Invalid logging level name\n"); exit(-1); } loglevel = -1; if(!strcasecmp(optarg, "LOG_DEBUG")) loglevel=LOG_DEBUG; if(!strcasecmp(optarg, "LOG_INFO")) loglevel=LOG_INFO; if(!strcasecmp(optarg, "LOG_NOTICE")) loglevel=LOG_NOTICE; if(!strcasecmp(optarg, "LOG_WARNING")) loglevel=LOG_WARNING; if(!strcasecmp(optarg, "LOG_ERR")) loglevel=LOG_ERR; if(!strcasecmp(optarg, "LOG_CRIT")) loglevel=LOG_CRIT; if(!strcasecmp(optarg, "LOG_ALERT")) loglevel=LOG_ALERT; if(!strcasecmp(optarg, "LOG_EMERG")) loglevel=LOG_EMERG; if(loglevel == -1) { fprintf(stderr, "Invalid logging level name - %s\n Choose one of: LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, LOG_CRIT, LOG_ALERT, LOG_EMERG\n", optarg); exit(-1); } strcpy(loglevel_string, optarg); break; case 's': /* Socket name (for comms with sendmail) */ if (optarg == NULL || *optarg == '\0') { fprintf(stderr, "Invalid socket name\n"); exit(-1); } strcpy(socketname, optarg); break; case 'c': /* synonym config file */ if (optarg == NULL || *optarg == '\0') { fprintf(stderr, "Invalid config file name\n"); exit(-1); } strcpy(configfile, optarg); break; case 'u': /* run-as username */ if (optarg == NULL || *optarg == '\0') { fprintf(stderr, "Invalid user name\n"); exit(-1); } strcpy(runas_user, optarg); break; case 'd': daemon = TRUE; break; case 'h': fprintf(stdout, "Synonym %s\nA flexible Milter-based extension for MTA level copy, drop and reject.\n\nUsage: synonym [-dh] [-c ] [-u ] [-s ]\n\ -d\tRun as daemon\n-h\tDisplay this help\n-c\tConfiguration file\n-r\tUsername that Synonym will run as\ \n-s\tMilter communication socket (should be the same as in the sendmail configuration file)\n\nLicensed under the Modulo Consulting Software License\n(see /usr/share/doc/synonym/license.txt)\n\n", SYNONYM_VERSION); exit(-1); break; } } closelog(); openlog("Synonym", LOG_NDELAY|LOG_PID, logfacility); setlogmask(LOG_UPTO(loglevel)); syslog(LOG_INFO, "Log facility is %s", logfacility_string); syslog(LOG_INFO, "Log level is %s", loglevel_string); syslog(LOG_INFO, "Socket is %s", socketname); syslog(LOG_INFO, "Config file is %s", configfile); syslog(LOG_INFO, "Synonym will attempt to run as user %s", runas_user); if((runas_pwent = getpwnam(runas_user)) == NULL) { fprintf(stderr, "Invalid user (cannot run as %s)\n", runas_user); exit(-1); } if((runas_pwent->pw_uid == 0) || (runas_pwent->pw_gid == 0)) { fprintf(stderr, "Synonym should not be run as root. Please specify a valid user.\n"); exit(-1); } if(setgid(runas_pwent->pw_gid)!=0) { fprintf(stderr, "Failed to drop privileges to gid %d\n", (int)runas_pwent->pw_gid); exit(-1); } if(setuid(runas_pwent->pw_uid)!=0) { fprintf(stderr, "Failed to drop privileges to uid %d\n", (int)runas_pwent->pw_uid); exit(-1); } if(Synonym_Get_Config(configfile, &synonym_config) != SYNONYM_OK) { fprintf(stderr, "Failed to load configuration (%s)\nPlease check the log file for more details\n", configfile); exit(-1); } if((f_pidfile = fopen(PIDFILE, "r")) != NULL) { nread = fread(pid_str, 1, 1023, f_pidfile); pid_str[nread] = '\0'; fclose(f_pidfile); if((pidno = atoi(pid_str)) != 0) { if((kill(pidno, 0) == -1) && (errno == ESRCH)) /* pid exists but process is not running anymore */ { syslog(LOG_WARNING, "Synonym was previously stopped uncleanly"); fprintf(stderr, "Synonym was previously stopped uncleanly\n"); unlink(PIDFILE); } else { syslog(LOG_ERR, "Synonym already running (%d)", pidno); fprintf(stderr, "Synonym already running (%d)\n", pidno); exit(-1); } } else { fprintf(stderr, "Failed to read the PID from %s", PIDFILE); exit(-1); } } if(daemon) { fork_result_id = fork(); if(fork_result_id == -1) /* Error; we should exit */ { syslog(LOG_ERR, "Failed to fork to background"); exit(-1); } if(fork_result_id != 0) exit(0); /* we're parent - close ourselves */ /* we're child, detached */ syslog(LOG_INFO, "Synonym started as daemon"); } f_pidfile = fopen(PIDFILE, "w"); if(f_pidfile == NULL) { syslog(LOG_ERR, "Failed to create pidfile %s", PIDFILE); fprintf(stderr, "Failed to create pidfile %s\n", PIDFILE); exit(-1); } sprintf(pid_str, "%d", (int)getpid()); if(fwrite(pid_str, 1, strlen(pid_str), f_pidfile) != strlen(pid_str)) { syslog(LOG_ERR, "Failed to write the pid in %s", PIDFILE); fprintf(stderr, "Failed to write the pid in %s\n", PIDFILE); fclose(f_pidfile); exit(-1); } fclose(f_pidfile); if(strncmp(socketname, "local:", 6) == 0) {/* Local socket already exists - we should delete it */ if(stat(&socketname[6], &statbuf) == 0) {/* The file exists so we have to remove it */ syslog(LOG_INFO, "The local socket already exists"); /* Delete the socket file */ if(unlink(&socketname[6]) != 0) { fprintf(stderr, "Can't remove the socket file. Please try removing it manually\n"); exit(-1); } } } if(smfi_setconn(socketname) == MI_FAILURE) { fprintf(stderr, "Can't create comms socket. (check permissions)\n"); exit(-1); } if (smfi_register(smfilter) == MI_FAILURE) { fprintf(stderr, "Unable to register as sendmail milter\n"); exit(EX_UNAVAILABLE); } fclose(stdin); fclose(stdout); fclose(stderr); smfi_main(); Synonym_Free_Config(&synonym_config); if(unlink(PIDFILE) == -1) syslog(LOG_ERR, "Failed to delete the pid file %s", PIDFILE); syslog(LOG_WARNING, "Synonym exiting"); closelog(); return 0; } void free_matched_action_list(matched_action * m_list) { matched_action *current_action, *previous_action; if(m_list == NULL) return; current_action = m_list; previous_action = current_action; while(current_action != NULL) { /* all the members are statically allocated so nothing to do here */ previous_action = current_action; current_action = current_action->next; free(previous_action); } m_list = NULL; }