/*
* 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 */
};