Index: ossp-pkg/petidomo/mailer.c RCS File: /v/ossp/cvs/ossp-pkg/petidomo/mailer.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/petidomo/mailer.c,v' | diff -u /dev/null - -L'ossp-pkg/petidomo/mailer.c' 2>/dev/null --- ossp-pkg/petidomo/mailer.c +++ - 2024-05-13 23:46:59.395423922 +0200 @@ -0,0 +1,261 @@ +/* + * $Source$ + * $Revision$ + * $Date$ + * + * Copyright (C) 1996 by CyberSolutions GmbH. + * All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef ARG_NUM_MAX +# define ARG_NUM_MAX 4096 +#endif +#ifndef ARG_MAX +# define ARG_MAX 4096 +#endif + +static char * +my_strcpy(char * dst, const char * src) +{ + while((*dst++ = *src++) != '\0') + ; + return dst-1; +} + +FILE * +OpenMailer(const char * envelope, const char * recipients[]) +{ + assert(1==0); + return NULL; +} + +FILE * +vOpenMailer(const char * envelope, ...) +{ + const struct PD_Config * MasterConfig; + va_list ap; + FILE * fh; + char * cmdline; + char * p; + const char * q; + const char * options; + unsigned int cmdline_len; + + MasterConfig = getMasterConfig(); + + debug((DEBUG_MAILER, 2, "MTA is \"%s\".", MasterConfig->mta)); + debug((DEBUG_MAILER, 2, "MTA options are \"%s\".", MasterConfig->mta_options)); + debug((DEBUG_MAILER, 2, "Envelope is \"%s\".", envelope)); + + /* Determine the length of the required buffer. */ + + cmdline_len = strlen(MasterConfig->mta); + cmdline_len += strlen(MasterConfig->mta_options); + cmdline_len += strlen(envelope); + va_start(ap, envelope); + while ((q = va_arg(ap, const char *)) != NULL) { + debug((DEBUG_MAILER, 2, "Recipient: \"%s\".", q)); + cmdline_len += strlen(q) + 1; + } + va_end(ap); + cmdline = xmalloc(cmdline_len+8); /* we don't take any risks :) */ + debug((DEBUG_MAILER, 3, "Command line will be %u byte long.", cmdline_len)); + + /* Copy the mta's path and name into the buffer. */ + + p = my_strcpy(cmdline, MasterConfig->mta); + *p++ = ' '; + + /* Copy the mta's options into the array, while replacing '%s' + with the envelope. */ + + for (options = MasterConfig->mta_options; *options != '\0'; ) { + debug((DEBUG_MAILER, 4, "Parsing '%c' character.", *options)); + if (options[0] == '%' && options[1] == 's') { + p = my_strcpy(p, envelope); + *p++ = ' '; + options += 2; + break; + } + else { + debug((DEBUG_MAILER, 4, "Wrote '%c' to aray.", *options)); + *p++ = *options++; + } + } + *p++ = ' '; + + /* Append the list of recipients. */ + + va_start(ap, envelope); + while ((q = va_arg(ap, const char *)) != NULL) { + p = my_strcpy(p, q); + *p++ = ' '; + } + p[-1] = '\0'; + va_end(ap); + + debug((DEBUG_MAILER, 1, "Starting up \"%s\".", cmdline)); + + fh = popen(cmdline, "w"); + if (fh == NULL) + syslog(LOG_ERR, "Failed opening pipe to \"%s\": %m", cmdline); + + free(cmdline); + return fh; +} + + +int +CloseMailer(FILE * fh) +{ + return pclose(fh); +} + + +static int +my_strlen(const char * p) +{ + u_int i; + for (i = 0; *p && !isspace((int)*p); p++) + i++; + return i; +} + +#define MYPIPE_READ fildes[0] +#define MYPIPE_WRITE fildes[1] + +int +ListMail(const char * envelope, const char * listname, const struct Mail * MailStruct) +{ + const struct PD_Config * MasterConfig = getMasterConfig(); + char ** arguments; + u_int arguments_num = 256; + char buffer[256]; + char * listfile; + char * nextAddress; + char * currAddress; + char * p; + u_int counter; + u_int len; + u_int address_byte; + u_int max_address_byte; + int fildes[2]; + pid_t child_pid; + int child_status; + + /* Initialize internal stuff. */ + + arguments = xmalloc((arguments_num+1) * sizeof(char *)); + max_address_byte = ARG_MAX - strlen(envelope) - strlen(MasterConfig->mta) - + strlen(MasterConfig->mta_options) - 8; + + /* Load the list of recipients. */ + + sprintf(buffer, "lists/%s/list", listname); + listfile = loadfile(buffer); + if (listfile == NULL) + return 1; + + /* Now go into delivery loop until we're finished. */ + + for(counter = 0, currAddress = listfile; *currAddress != '\0'; counter = 0) { + + /* Set up the call to the MTA, including options. */ + + arguments[counter++] = MasterConfig->mta; + debug((DEBUG_MAILER, 5, "MTA is \"%s\".", arguments[0])); + sprintf(buffer, MasterConfig->mta_options, envelope); + debug((DEBUG_MAILER, 5, "MTA options are \"%s\".", buffer)); + for (p = buffer, arguments[counter++] = buffer; *p != '\0'; p++) { + debug((DEBUG_MAILER, 9, "Left to parse: \"%s\".", p)); + if (isspace((int)*p)) { + *p++ = '\0'; + debug((DEBUG_MAILER, 9, "Left to parse: \"%s\".", p)); + while(*p != '\0' && isspace((int)*p)) + p++; + debug((DEBUG_MAILER, 9, "Left to parse: \"%s\".", p)); + arguments[counter++] = p; + } + } + if (strlen(arguments[counter-1]) == 0) + counter--; + + /* Append as many recipients as fit. */ + + for (address_byte = 0; *currAddress != '\0' ; currAddress = nextAddress) { + nextAddress = text_find_next_line(currAddress); + len = my_strlen(currAddress); + if (address_byte + len > max_address_byte) { + debug((DEBUG_MAILER, 1, "Sending early, command line exceeds %d characters.", ARG_MAX)); + break; + } + if (counter > ARG_NUM_MAX) { + debug((DEBUG_MAILER, 1, "Sending early, command line exceeds %d arguments.", ARG_NUM_MAX)); + break; + } + currAddress[len] = '\0'; + debug((DEBUG_MAILER, 8, "Address \"%s\" is %u byte long.", currAddress, len)); + address_byte += len; + arguments[counter++] = currAddress; + if (counter+8 >= arguments_num) { + debug((DEBUG_MAILER, 1, "Enlarging internal array.")); + arguments_num += 256; + arguments = realloc(arguments, (arguments_num+1) * sizeof(char *)); + if (arguments == NULL) + return -1; + } + } + + /* Deliver the mail. */ + + arguments[counter++] = NULL; + if (pipe(fildes) == -1) { + syslog(LOG_ERR, "Couldn't open a pipe to my child process: %m"); + return -1; + } + child_pid = fork(); + switch(child_pid) { + case 0: + /* Child */ + close(MYPIPE_WRITE); + if (dup2(MYPIPE_READ, STDIN_FILENO) == -1) { + syslog(LOG_ERR, "Child process couldn't read from pipe: %m"); + return -1; + } + close(MYPIPE_READ); + execv(MasterConfig->mta, arguments); + syslog(LOG_ERR, "Couldn't exec(\"%s\"): %m", MasterConfig->mta); + return -1; + case -1: + /* Error */ + syslog(LOG_ERR, "Couldn't fork: %m"); + return -1; + default: + /* everything is fine */ + close(MYPIPE_READ); + } + write(MYPIPE_WRITE, MailStruct->Header, strlen(MailStruct->Header)); + write(MYPIPE_WRITE, "\n", 1); + write(MYPIPE_WRITE, MailStruct->Body, strlen(MailStruct->Body)); + if (MailStruct->ListSignature != NULL) + write(MYPIPE_WRITE, MailStruct->ListSignature, strlen(MailStruct->ListSignature)); + close(MYPIPE_WRITE); + waitpid(child_pid, &child_status, 0); + if (!(WIFEXITED(child_status) && WEXITSTATUS(child_status) == 0)) { + syslog(LOG_ERR, "The executed mail agent return error %d, aborting.", + WEXITSTATUS(child_status)); + return -1; + } + } + return 0; +} Index: ossp-pkg/petidomo/main.c RCS File: /v/ossp/cvs/ossp-pkg/petidomo/main.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/petidomo/main.c,v' | diff -u /dev/null - -L'ossp-pkg/petidomo/main.c' 2>/dev/null --- ossp-pkg/petidomo/main.c +++ - 2024-05-13 23:46:59.398218113 +0200 @@ -0,0 +1,146 @@ +/* + * $Source$ + * $Revision$ + * $Date$ + * + * Copyright (C) 1996 by CyberSolutions GmbH. + * All rights reserved. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "version.h" + +#ifndef LOG_PERROR +# define LOG_PERROR 0 +#endif + +MODULE_TABLE /* defined in debug.h */ +static char * listname = NULL; +static argv_array_t debug; + +int +main(int argc, char * argv[]) +{ + const struct PD_Config * MasterConfig; + char * incoming_mail; + char * programname; + argv_t args[] = { +#ifdef DEBUG + {'d', "debug", ARGV_CHARP | ARGV_ARRAY , &debug, "debug", + "Set debug level per module."}, +#endif + {ARGV_MAYBE, 0L, ARGV_CHARP, &listname, "listname", "Default mailing list."}, + {ARGV_LAST} + }; + int fd; + + /* Determine the name we have been called under. */ + + programname = strrchr(argv[0], (int) '/'); + if (programname == NULL) + programname = argv[0]; + else + programname++; + + /* Init logging routines first of all, so that we can report + errors. */ + + openlog(programname, LOG_CONS | LOG_PID | LOG_PERROR, LOG_MAIL); + + /* Set our umask. */ + + umask(S_IRWXO); /* We don't care for "others". */ + + /* Switch real and effective uid/gid to 'petidomo'. */ + +#ifdef HAVE_SETREUID + setreuid(geteuid(), geteuid()); +#endif +#ifdef HAVE_SETREGID + setregid(getegid(), getegid()); +#endif + + /* Parse the command line. */ + + argv_help_string = "Petidomo Mailing List Server"; + argv_version_string = VERS; + argv_process(args, argc, argv); + + /* Set debug level according to the wishes of the user. */ + +#ifdef DEBUG + if (argvSetDebugLevel(debug) != 0) + exit(1); +#endif + + /* Init Petidomo's internal stuff. */ + + if (InitPetidomo() != 0) { + syslog(LOG_CRIT, "Failed to initialize my internals."); + exit(1); + } + MasterConfig = getMasterConfig(); + + /* Load the file from standard input and save it, so that it isn't + lost in case of an error. */ + + incoming_mail = LoadFromDescriptor(STDIN_FILENO); + if (incoming_mail == NULL) { + syslog(LOG_ERR, "Failed to read incoming mail from standard input."); + exit(1); + } + RescueMail(incoming_mail); + + /* Detach ourselves, if the configurator wished it so. */ + + if (MasterConfig->detach == TRUE) { + debug((DEBUG_MAIN, 3, "Detaching from control terminal and running asyncronously.")); + switch (fork()) { + case -1: + syslog(LOG_CRIT, "Can't fork(): %m"); + exit(1); + case 0: + setsid(); + fd = open("/dev/null", O_RDWR, 0); + if (fd != -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > 2) + close (fd); + } + break; + default: + _exit(0); + } + } + + /* Now decide what we actually do with the mail. */ + + if (strcasecmp("listserv", programname) == 0) + listserv_main(incoming_mail, listname); + else if (strcasecmp("hermes", programname) == 0) { + if (listname != NULL) + hermes_main(incoming_mail, listname); + else { + syslog(LOG_ERR, "Wrong command line syntax. \"hermes\" requires a parameter."); + exit(1); + } + } + else { + syslog(LOG_ERR, "I have been called under an unknown name \"%s\".", programname); + exit(1); + } + + /* Exit gracefully. */ + + RemoveRescueMail(); + return 0; +} Index: ossp-pkg/petidomo/members.c RCS File: /v/ossp/cvs/ossp-pkg/petidomo/members.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/petidomo/members.c,v' | diff -u /dev/null - -L'ossp-pkg/petidomo/members.c' 2>/dev/null --- ossp-pkg/petidomo/members.c +++ - 2024-05-13 23:46:59.400901633 +0200 @@ -0,0 +1,145 @@ +/* + * $Source$ + * $Revision$ + * $Date$ + * + * Copyright (C) 1997 by CyberSolutions GmbH. + * All rights reserved. + */ + +#include +#include +#include +#include + +#include +#include + +int +SendSubscriberList(struct Mail * MailStruct, + const char * param1, + const char * param2, + const char * defaultlist) +{ + const struct List_Config * ListConfig; + FILE * fh; + const char * address = NULL; + const char * listname = NULL; + char owner[4096]; + char envelope[4096]; + char * buffer; + char * p; + int i; + + debug((DEBUG_COMMAND, 3, "SendSubscriberList(\"%s\") with default list \"%s\".", + param1, defaultlist)); + + /* Try to find out, which parameter is what. */ + + if (param1 != NULL) { + if (isValidListName(param1) == TRUE) + listname = param1; + } + + address = (MailStruct->Reply_To) ? MailStruct->Reply_To : MailStruct->From; + if (listname == NULL && defaultlist != NULL) + listname = defaultlist; + + if (address == NULL || listname == NULL) { + syslog(LOG_NOTICE, "%s: members-command invalid: No list specified.", + MailStruct->From); + return 0; + } + + /* Initialize internal stuff. */ + + ListConfig = getListConfig(listname); + sprintf(owner, "%s-owner@%s", listname, ListConfig->fqdn); + sprintf(envelope, "%s-owner@%s", listname, ListConfig->fqdn); + + /* Check whether 'members' is allowed for this list. */ + + if (isValidAdminPassword(getPassword(), listname) == FALSE && + ListConfig->allowmembers == FALSE) { + + syslog(LOG_NOTICE, "MEMBERS command from \"%s\" has been denied.", address); + fh = vOpenMailer(envelope, address, owner, NULL); + if (fh != NULL) { + fprintf(fh, "From: %s-request@%s (Petidomo Mailing List Server)\n", + listname, ListConfig->fqdn); + fprintf(fh, "To: %s\n", address); + fprintf(fh, "Cc: %s\n", owner); + fprintf(fh, "Subject: Request \"members %s\"\n", listname); + if (MailStruct->Message_Id != NULL) + fprintf(fh, "In-Reply-To: %s\n", MailStruct->Message_Id); + fprintf(fh, "Precedence: junk\n"); + fprintf(fh, "Sender: %s\n", envelope); + fprintf(fh, "\n"); + buffer = text_easy_sprintf( +"The MEMBERS command has been disabled for this mailing list, I am " \ +"afraid. If there's a certain reason, why you need to know the list " \ +"of subscribed addresses, please contact the mailing list administrator " \ +"under the address \"%s\" instead.", owner); + text_wordwrap(buffer, 75); + fprintf(fh, "%s\n", buffer); + AppendSignature(fh); + CloseMailer(fh); + } + else + + syslog(LOG_ERR, "Failed to send mail to \"%s\"", address); + return 0; + } + + /* Okay, send the address list back. */ + + debug((DEBUG_COMMAND, 1, "Sending list of subscribed addresses for list " + "\"%s\" to \"%s\".", listname, address)); + + buffer = text_easy_sprintf("lists/%s/list", listname); + buffer = loadfile(buffer); + if (buffer == NULL) { + syslog(LOG_ERR, "Failed to open file \"~petidomo/lists/%s/list\"", listname); + return -1; + } + + fh = vOpenMailer(envelope, address, NULL); + if (fh != NULL) { + fprintf(fh, "From: %s-request@%s (Petidomo Mailing List Server)\n", + listname, ListConfig->fqdn); + fprintf(fh, "To: %s\n", address); + fprintf(fh, "Subject: Request \"members %s\"\n", listname); + if (MailStruct->Message_Id != NULL) + fprintf(fh, "In-Reply-To: %s\n", MailStruct->Message_Id); + fprintf(fh, "Precedence: junk\n"); + fprintf(fh, "Sender: %s\n", envelope); + fprintf(fh, "\n"); + fprintf(fh, "Subscribers of list \"%s\":\n", listname); + fprintf(fh, "======================="); + fflush(fh); + for (i = 0; i < strlen(listname); i++) { + fputc('=', fh); + } + fputc('\n', fh); + for (p = buffer; *p; p++) { + if (isspace((int)*p)) { + fputc('\n', fh); + while (*p != '\0' && *p != '\n') + p++; + } + else { + fputc(*p, fh); + } + } + AppendSignature(fh); + CloseMailer(fh); + } + else { + free(buffer); + syslog(LOG_ERR, "Failed to send email to \"%s\"!", address); + return -1; + } + + free(buffer); + return 0; +} Index: ossp-pkg/petidomo/parsearray.c RCS File: /v/ossp/cvs/ossp-pkg/petidomo/parsearray.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/petidomo/parsearray.c,v' | diff -u /dev/null - -L'ossp-pkg/petidomo/parsearray.c' 2>/dev/null --- ossp-pkg/petidomo/parsearray.c +++ - 2024-05-13 23:46:59.403519759 +0200 @@ -0,0 +1,28 @@ +/* + * $Source$ + * $Revision$ + * $Date$ + * + * Copyright (C) 1996 by CyberSolutions GmbH. + * All rights reserved. + */ + +#include "petidomo.h" + +struct Parse ParseArray[] = { + { "add", AddAddress }, + { "subscribe", AddAddress }, + { "delete", DeleteAddress }, + { "unsubscribe", DeleteAddress }, + { "remove", DeleteAddress }, + { "approve", setPassword }, + { "passwd", setPassword }, + { "password", setPassword }, + { "index", GenIndex }, + { "lists", GenIndex }, + { "longindex", GenIndex }, + { "help", SendHelp }, + { "who", SendSubscriberList }, + { "members", SendSubscriberList }, + { NULL, NULL } +}; Index: ossp-pkg/petidomo/password.c RCS File: /v/ossp/cvs/ossp-pkg/petidomo/password.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/petidomo/password.c,v' | diff -u /dev/null - -L'ossp-pkg/petidomo/password.c' 2>/dev/null --- ossp-pkg/petidomo/password.c +++ - 2024-05-13 23:46:59.406037626 +0200 @@ -0,0 +1,67 @@ +/* + * $Source$ + * $Revision$ + * $Date$ + * + * Copyright (C) 1996 by CyberSolutions GmbH. + * All rights reserved. + */ + +#include + +#include + +static const char * s_password = NULL; +extern char * g_currLine; + +int +setPassword(struct Mail * MailStruct, + const char * param1, + const char * param2, + const char * defaultlist) +{ + char * p; + char * q; + + debug((DEBUG_COMMAND, 3, "setPassword(\"%s\").", param1)); + + /* Find the beginning of the parameter. */ + + p = g_currLine; + while(*p && !isspace((int)*p)) + p++; + while(*p && isspace((int)*p)) + p++; + + /* If the rest is empty, there ain't no fucking password. */ + + if (*p == '\0' || strlen(p) == 0) + return 0; + + /* Cut trailing blanks. */ + + q = p + strlen(p); + while(isspace((int)q[-1])) + q--; + *q = '\0'; + + /* Okay, check for quotes and that's it then. */ + + if (*p == '\"' && q[-1] == '\"') { + p++; + q[-1] = '\0'; + } + + /* Store the result. */ + + debug((DEBUG_COMMAND, 2, "Setting current password to \"%s\".", p)); + s_password = p; + + return 0; +} + +const char * +getPassword(void) +{ + return s_password; +} Index: ossp-pkg/petidomo/rfcparse.c RCS File: /v/ossp/cvs/ossp-pkg/petidomo/rfcparse.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/petidomo/rfcparse.c,v' | diff -u /dev/null - -L'ossp-pkg/petidomo/rfcparse.c' 2>/dev/null --- ossp-pkg/petidomo/rfcparse.c +++ - 2024-05-13 23:46:59.408580491 +0200 @@ -0,0 +1,350 @@ +/* + * $Source$ + * $Revision$ + * $Date$ + * + * Copyright (C) 1996 by CyberSolutions GmbH. + * All rights reserved. + */ + +#include +#include +#include + +#include +#include +#include + +void +RemoveCarrigeReturns(char * buffer) +{ + char * src = buffer; + char * dst = buffer; + + while(*src != '\0') { + switch(*src) { + case '\n': + *dst++ = ' '; + while(isspace((int)*src)) + src++; + break; + default: + *dst++ = *src++; + } + } + *dst++ = '\0'; +} + +bool +isRFC822Address(const char * buffer) +{ + char * address; + int rc; + + rc = rfc822_parse_address(buffer, &address, NULL, NULL); + if (rc == RFC822_OK) { + debug((DEBUG_RFCPARSE, 3, "'%s' is a valid rfc address.", address)); + if (address) { + free(address); + } + return TRUE; + } + else { + debug((DEBUG_RFCPARSE, 4, "'%s' is not a valid rfc address", buffer)); + return FALSE; + } +} + +int +ParseAddressLine(char * buffer) +{ + struct rfc822_address_sep_state sep_state; + char * p, + * address; + int rc; + + /* Handle continuation lines. */ + + RemoveCarrigeReturns(buffer); + + /* Initialize the structure needed for address_sep(). */ + + sep_state.address_line = buffer; + sep_state.group_nest = 0; + + debug((DEBUG_RFCPARSE, 2, "Original address is \"%s\".", buffer)); + + /* We want only the first address, if multiple are there. */ + + while ((p = rfc822_address_sep(&sep_state)) != NULL) { + if (*p == '\0') + continue; + else + break; + } + + if (p == NULL) { + /* line is empty */ + return -1; + } + + debug((DEBUG_RFCPARSE, 2, "First part is \"%s\".", p)); + + rc = rfc822_parse_address(p, &address, NULL, NULL); + if (rc == RFC822_OK && address != NULL) { + debug((DEBUG_RFCPARSE, 2, "Parsed address is: '%s'", address)); + strcpy(buffer, address); + free(address); + return 0; + } + else + return -1; +} + +int +ParseReplyToLine(char * buffer) +{ + return ParseAddressLine(buffer); +} + +int +ParseFromLine(char * buffer) +{ + return ParseAddressLine(buffer); +} + +int +ParseMessageIdLine(char * buffer) +{ + int rc; + + debug((DEBUG_RFCPARSE, 2, "Unparsed Message-Id: '%s'", buffer)); + + rc = ParseAddressLine(buffer); + if (rc != 0) + return rc; /* Error! */ + + memmove(buffer+1, buffer, strlen(buffer)+1); + buffer[0] = '<'; + strcat(buffer, ">"); + + debug((DEBUG_RFCPARSE, 2, "Parsed Message-Id: '%s'", buffer)); + + return rc; +} + +int +ParseApproveLine(char * buffer) +{ + char * src; + char * dst; + + RemoveCarrigeReturns(buffer); + + src = buffer; + dst = buffer; + + /* Skip leading whitespace. */ + + while(isspace((int)*src)) + src++; + + /* Skip a quote if there is one. */ + + if (*src == '\"') + src++; + + /* Copy String. */ + + while((*dst++ = *src++) != '\0') + ; + dst--; + + /* Kill trailing whitespace. */ + + while(isspace((int)dst[-1])) + *(--dst) = '\0'; + + /* Kill a quote if there is one. */ + + if (dst[-1] == '\"') + *(--dst) = '\0'; + + return 0; +} + +void +CanonizeAddress(char ** buffer, const char * fqdn) +{ + const struct PD_Config * MasterConfig; + char * newbuf; + char * local, + * host; + int rc; + + assert(buffer != NULL); + assert(*buffer != NULL); + + if (buffer == NULL || *buffer == NULL) + return; + + debug((DEBUG_RFCPARSE, 3, "Check whether \"%s\" is a canon address.", *buffer)); + + rc = rfc822_parse_address(*buffer, NULL, &local, &host); + if (rc == RFC822_OK) { + if (local != NULL && host == NULL) { + debug((DEBUG_RFCPARSE, 3, "'%s' is a local address, appending my hostname.", + *buffer)); + if (fqdn == NULL) { + MasterConfig = getMasterConfig(); + fqdn = MasterConfig->fqdn; + } + newbuf = xmalloc(strlen(*buffer) + strlen(fqdn) + 3); + sprintf(newbuf, "%s@%s", local, fqdn); + free(local); + *buffer = newbuf; + debug((DEBUG_RFCPARSE, 3, "Canonized address: '%s'.", *buffer)); + } + else + debug((DEBUG_RFCPARSE, 3, "\"%s\" is a full address.", *buffer)); + } +} + +int +ParseMail(struct Mail **result, char * incoming_mail, const char * fqdn) +{ + struct Mail * MailStruct; + char * currLine; + char * nextLine; + int rc; + + /* Allocate structure. */ + + MailStruct = calloc(sizeof(struct Mail), 1); + if (MailStruct == NULL) { + syslog(LOG_ERR, "Failed to allocate %d byte of memory.", sizeof(struct Mail)); + return -1; + } + + /* Rescue the mail in its original state, before the parsing + routines have havoc in the buffer. */ + + MailStruct->Header = strdup(incoming_mail); + if (MailStruct->Header == NULL) { + syslog(LOG_ERR, "Failed to allocate %d byte of memory.", strlen(incoming_mail)); + return -1; + } + for (MailStruct->Body = MailStruct->Header; + *MailStruct->Body != '\n' && *MailStruct->Body != '\0'; + MailStruct->Body = text_find_next_line(MailStruct->Body)) + ; + if (*MailStruct->Body == '\n') { + *MailStruct->Body = '\0'; + MailStruct->Body++; + } + + /* Get the envelope. */ + + currLine = incoming_mail; + nextLine = text_find_next_line(incoming_mail); + if (strncasecmp("From ", currLine, strlen("From ")) == 0) { + if (nextLine[-1] == '\n') + nextLine[-1] = '\0'; + currLine += strlen("From "); + while (isspace((int)*currLine)) + currLine++; + MailStruct->Envelope = currLine; + while (!isspace((int)*currLine)) + currLine++; + *currLine = '\0'; + CanonizeAddress(&(MailStruct->Envelope), fqdn); + debug((DEBUG_RFCPARSE, 5, "Envelope is \"%s\".", MailStruct->Envelope)); + currLine = nextLine; + } + + /* Parse the incoming mail's header. */ + + for (nextLine = text_find_next_line(currLine); + *currLine != '\n' && *currLine != '\0'; + currLine = nextLine, nextLine = text_find_next_line(currLine)) { + + /* Find continuation lines. */ + + while (*nextLine == ' ' || *nextLine == '\t') + nextLine = text_find_next_line(nextLine); + + /* remove trailing \n */ + + if (nextLine[-1] == '\n') + nextLine[-1] = '\0'; + + /* Log contents of current line. */ + + debug((DEBUG_RFCPARSE, 6, "Parsing line \"%s\".", currLine)); + + /* Check whether it is a header we're interested in. */ + + if (strncasecmp("From:", currLine, strlen("From:")) == 0) { + if (MailStruct->From != NULL) { + syslog(LOG_NOTICE, "Received mail with multiple From: lines."); + continue; + } + MailStruct->From = &currLine[strlen("From:")]; + rc = ParseFromLine(MailStruct->From); + if (rc != 0) + return rc; + CanonizeAddress(&(MailStruct->From), fqdn); + debug((DEBUG_RFCPARSE, 5, "From: is \"%s\".", MailStruct->From)); + } else if (strncasecmp("Reply-To:", currLine, strlen("Reply-To:")) == 0) { + if (MailStruct->Reply_To != NULL) { + syslog(LOG_NOTICE, "Received mail with multiple Reply-To: lines."); + continue; + } + MailStruct->Reply_To = &currLine[strlen("Reply-To:")]; + rc = ParseReplyToLine(MailStruct->Reply_To); + if (rc != 0) + return rc; + CanonizeAddress(&(MailStruct->Reply_To), fqdn); + debug((DEBUG_RFCPARSE, 5, "Reply-To: is \"%s\".", MailStruct->Reply_To)); + } else if (strncasecmp("Message-Id:", currLine, strlen("Message-Id:")) == 0) { + if (MailStruct->Message_Id != NULL) { + syslog(LOG_NOTICE, "Received mail with multiple Message-Id: lines."); + continue; + } + MailStruct->Message_Id = &currLine[strlen("Message-Id:")]; + rc = ParseMessageIdLine(MailStruct->Message_Id); + if (rc != 0) + return rc; + debug((DEBUG_RFCPARSE, 5, "Message-Id: is \"%s\".", MailStruct->Message_Id)); + } + else if (strncasecmp("Approve:", currLine, strlen("Approve:")) == 0) { + if (MailStruct->Approve != NULL) + syslog(LOG_NOTICE, "Received mail with multiple Approve: lines."); + MailStruct->Approve = &currLine[strlen("Approve:")]; + rc = ParseApproveLine(MailStruct->Approve); + if (rc != 0) + return rc; + debug((DEBUG_RFCPARSE, 5, "Approve: is \"%s\".", MailStruct->Approve)); + } + else if (strncasecmp("Approved:", currLine, strlen("Approved:")) == 0) { + if (MailStruct->Approve != NULL) + syslog(LOG_NOTICE, "Received mail with multiple Approve: lines."); + MailStruct->Approve = &currLine[strlen("Approved:")]; + rc = ParseApproveLine(MailStruct->Approve); + if (rc != 0) + return rc; + debug((DEBUG_RFCPARSE, 5, "Approve: is \"%s\".", MailStruct->Approve)); + } + else if (strncasecmp("Subject:", currLine, strlen("Subject:")) == 0) { + if (MailStruct->Subject != NULL) + syslog(LOG_NOTICE, "Received mail with multiple Subject: lines."); + MailStruct->Subject = &currLine[strlen("Subject:")]; + if (*MailStruct->Subject == ' ') + MailStruct->Subject += 1; + debug((DEBUG_RFCPARSE, 5, "Subject: is \"%s\".", MailStruct->Subject)); + } + } + + *result = MailStruct; + return 0; +}