/* * Copyright (c) 2003-2010 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #include "nls.h" CGI_Application mailApp; TBL *Trcpts; #define SENDER_MAX 64 #define RCPT_MAX 32 #define SUBJECT_MAX 128 #define MESSAGE_MAX 65536 #define XVAL_MAX 128 #define FVAL_MAX 256 static void Destroy(void) { TBL_Destroy(Trcpts); } /* Verify the format of the sender e-mail address. */ static int ValidSender(const char *email) { regmatch_t rm[2]; regex_t reg; if (regcomp(®, "^[a-zA-Z0-9_.-]{1,64}@[a-zA-Z0-9.-]{2,60}" ".[a-zA-Z]{2,4}$", REG_EXTENDED) != 0) { CGI_Log(CGI_LOG_ERR, "regcomp failed"); return (0); } if (regexec(®, email, 2, rm, 0) != 0) { return (0); } regfree(®); return (1); } /* Check that the subject field contains only printing/space characters. */ static int ValidSubject(const char *subj) { const char *p; for (p = subj; *p != '\0'; p++) { if (!isprint(*p)) return (0); } return (1); } /* Feed the message to the mailer backend and archive it. */ static int Inject(CGI_Query *q, const char *sender, const char *rcpt, const char *subj, const char *message) { char path[MAXPATHLEN]; char stamp[FILENAME_MAX]; const time_t clock = time(NULL); struct tm *tm = localtime(&clock); TEXT msg; struct stat sta; int fd, pdes[2], status; CGI_KeySet *set; int i, eflag = 0; strlcpy(path, _PATH_MAIL_SENT, sizeof(path)); if (stat(path, &sta) == -1 && mkdir(path, 0700) == -1) { CGI_SetError("%s: %s", path, strerror(errno)); return (-1); } strftime(stamp, sizeof(stamp), "/%F.%T", tm); strlcat(path, stamp, sizeof(path)); /* Construct the header of the message. */ TEXT_Init(&msg, MESSAGE_MAX, q->cset); TEXT_Cat(&msg, "From: %s\n", sender); TEXT_Cat(&msg, "To: %s\n", rcpt); TEXT_Cat(&msg, "Subject: %s\n", subj); TEXT_Cat(&msg, "X-MailFCGI-IP: %s\n", getenv("REMOTE_ADDR")); /* Append custom header fields. */ set = CGI_GetKeySet(q, "x_"); for (i = 0; i < set->nents; i++) { char xkey[CGI_ARG_KEY_MAX]; const char *xval; xkey[0] = 'x'; xkey[1] = '_'; strlcpy(&xkey[2], set->ents[i], sizeof(xkey)-2); xval = CGI_Get(q, xkey, XVAL_MAX); TEXT_Cat(&msg, "X-%s: %s\n", set->ents[i], xval); } CGI_FreeKeySet(set); /* Append the message. */ TEXT_CatS(&msg, message); TEXT_CatC(&msg, '\n'); /* Append custom end of message fields. */ set = CGI_GetKeySet(q, "f_"); for (i = 0; i < set->nents; i++) { char fkey[CGI_ARG_KEY_MAX]; const char *fval; fkey[0] = 'f'; fkey[1] = '_'; strlcpy(&fkey[2], set->ents[i], sizeof(fkey)-2); if (!eflag) { eflag++; TEXT_CatS(&msg, "--\n"); } fval = CGI_Get(q, fkey, FVAL_MAX); TEXT_Cat(&msg, "%s: %s\n", set->ents[i], fval); } CGI_FreeKeySet(set); if ((fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0600)) == -1) { CGI_SetError("%s: %s", path, strerror(errno)); goto fail; } if (write(fd, msg.buf, msg.len) < msg.len) { CGI_SetError("%s: write error", path); close(fd); goto fail; } close(fd); if (pipe(pdes) == -1) { CGI_SetError("pipe: %s", strerror(errno)); goto fail; } if (fork()) { close(pdes[0]); if (write(pdes[1], msg.buf, msg.len) < msg.len || write(pdes[1], "\n", 1) < 1) { CGI_SetError("%s: write error", path); close(pdes[1]); goto fail; } close(pdes[1]); while (wait(&status) >= 0) ; if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { CGI_SetError("sendmail failure (%d)", WEXITSTATUS(status)); goto fail; } } else { if (pdes[0] != STDIN_FILENO) { dup2(pdes[0], STDIN_FILENO); close(pdes[0]); } close(pdes[1]); execl(_PATH_SENDMAIL, "sendmail", "-i", rcpt, (char *)NULL); _exit(EX_UNAVAILABLE); } TEXT_Destroy(&msg); return (0); fail: TEXT_Destroy(&msg); return (-1); } static int SendMessage(CGI_Query *q) { const char *sender, *rcptid, *subject, *message; const char *rcpt; if ((sender = CGI_Get(q, "sender", SENDER_MAX)) == NULL || (rcptid = CGI_Get(q, "rcpt", RCPT_MAX)) == NULL || (subject = CGI_Get(q, "subject", SUBJECT_MAX)) == NULL || (message = CGI_Get(q, "message", MESSAGE_MAX)) == NULL) goto fail; if (sender[0] == '\0' || !ValidSender(sender)) { CGI_SetError(_("The sender e-mail address is missing/invalid.")); goto fail; } if ((rcpt = TBL_LookupField(Trcpts, rcptid, 1)) == NULL) { CGI_SetError(_("The specified recipient does not exist.")); goto fail; } if (subject[0] == '\0' || !ValidSubject(subject)) { CGI_SetError(_("The subject field is missing/invalid.")); goto fail; } if (message[0] == '\0') { CGI_SetError(_("The message is empty.")); goto fail; } if (Inject(q, sender, rcpt, subject, message) == -1) goto fail; SetS("sender", sender); SetS("subject", subject); SetS("message", message); SetS("rcpt_name", TBL_LookupField(Trcpts, rcptid, 2)); HTML_Output(q, "success"); return (0); fail: HTML_OutputError(q, "%s", CGI_GetError()); return (-1); } int main(int argc, char *argv[]) { TBL_Entry *ent; CGI_Query q; CGI_Init(&mailApp); SetGlobal("sender_max", "%d", SENDER_MAX); SetGlobal("subject_max", "%d", SUBJECT_MAX); SetGlobal("message_max", "%d", MESSAGE_MAX); SetGlobal("xval_max", "%d", XVAL_MAX); SetGlobal("fval_max", "%d", FVAL_MAX); if ((Trcpts = TBL_Load("etc/recipients", 3)) == NULL) { CGI_Log(CGI_LOG_EMERG, "recipients: %s", CGI_GetError()); return (1); } while (FCGI_Accept() >= 0) { CGI_QueryInit(&q); if (CGI_QueryReadHTTP(&q) != 0) { continue; } if (CGI_Get(&q, "message", MESSAGE_MAX) != NULL) { SendMessage(&q); } else { VAR *Vrcpts; const char *rDef = CGI_Get(&q, "rcpt", RCPT_MAX); Vrcpts = SetS("recipients", NULL); TBL_FOREACH(ent, Trcpts) { const char *selected = ""; if (strcmp(ent->fields[0], q.lang) != 0) { continue; } if (rDef != NULL && strncmp(ent->key, rDef, strlen(ent->key)-3) == 0) { selected = " selected"; } Cat(Vrcpts, "\n", ent->key, selected, ent->fields[2]); } HTML_Output(&q, "default"); } out: CGI_QueryDestroy(&q); FCGI_Finish(); } CGI_Exit(EX_OK, NULL); return (0); fail: CGI_Exit(EX_TEMPFAIL, NULL); return (0); } CGI_Application mailApp = { N_("Mailer application"), N_("Copyright (c) 2003-2010 CubeSoft Communications, Inc."), { "en", "fr", NULL }, "main", CGI_HTML_ERRORS|CGI_PERSISTENT, Destroy, NULL /* log */ };