#include <cups/cups.h>
#include <cups/i18n.h>
#include <cups/string.h>
#include <errno.h>
#include <sys/wait.h>
#include <signal.h>
char mailtoCc[1024];
char mailtoFrom[1024];
char mailtoReplyTo[1024];
char mailtoSubject[1024];
char mailtoSMTPServer[1024];
char mailtoSendmail[1024];
void email_message(const char *to, const char *subject,
const char *text);
int load_configuration(void);
cups_file_t *pipe_sendmail(const char *to);
void print_attributes(ipp_t *ipp, int indent);
int
main(int argc,
char *argv[])
{
int i;
ipp_t *msg;
ipp_state_t state;
char *subject,
*text;
cups_lang_t *lang;
char temp[1024];
int templen;
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action;
#endif
setbuf(stderr, NULL);
#ifdef HAVE_SIGSET
sigset(SIGPIPE, SIG_IGN);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
action.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &action, NULL);
#else
signal(SIGPIPE, SIG_IGN);
#endif
if (argc != 3)
{
fputs("Usage: mailto mailto:user@domain.com notify-user-data\n", stderr);
return (1);
}
if (strncmp(argv[1], "mailto:", 7))
{
fprintf(stderr, "ERROR: Bad recipient \"%s\"!\n", argv[1]);
return (1);
}
fprintf(stderr, "DEBUG: argc=%d\n", argc);
for (i = 0; i < argc; i ++)
fprintf(stderr, "DEBUG: argv[%d]=\"%s\"\n", i, argv[i]);
if ((lang = cupsLangDefault()) == NULL)
return (1);
if (!load_configuration())
return (1);
templen = sizeof(temp);
httpDecode64_2(temp, &templen, argv[2]);
if (!strncmp(temp, "mailto:", 7))
strlcpy(mailtoReplyTo, temp + 7, sizeof(mailtoReplyTo));
else if (temp[0])
fprintf(stderr, "WARNING: Bad notify-user-data value (%d bytes) ignored!\n",
templen);
for (;;)
{
msg = ippNew();
while ((state = ippReadFile(0, msg)) != IPP_DATA)
{
if (state <= IPP_IDLE)
break;
}
fprintf(stderr, "DEBUG: state=%d\n", state);
if (state == IPP_ERROR)
fputs("DEBUG: ippReadFile() returned IPP_ERROR!\n", stderr);
if (state <= IPP_IDLE)
{
ippDelete(msg);
return (0);
}
subject = cupsNotifySubject(lang, msg);
text = cupsNotifyText(lang, msg);
fprintf(stderr, "DEBUG: subject=\"%s\"\n", subject);
fprintf(stderr, "DEBUG: text=\"%s\"\n", text);
if (subject && text)
email_message(argv[1] + 7, subject, text);
else
{
fputs("ERROR: Missing attributes in event notification!\n", stderr);
print_attributes(msg, 4);
}
if (subject)
free(subject);
if (text)
free(text);
ippDelete(msg);
}
}
void
email_message(const char *to,
const char *subject,
const char *text)
{
cups_file_t *fp;
const char *nl;
char response[1024];
if (mailtoSendmail[0])
{
fp = pipe_sendmail(to);
if (!fp)
return;
nl = "\n";
}
else
{
char hostbuf[1024];
if (strchr(mailtoSMTPServer, ':'))
fp = cupsFileOpen(mailtoSMTPServer, "s");
else
{
char spec[1024];
snprintf(spec, sizeof(spec), "%s:smtp", mailtoSMTPServer);
fp = cupsFileOpen(spec, "s");
}
if (!fp)
{
fprintf(stderr, "ERROR: Unable to connect to SMTP server \"%s\"!\n",
mailtoSMTPServer);
return;
}
fprintf(stderr, "DEBUG: Connected to \"%s\"...\n", mailtoSMTPServer);
cupsFilePrintf(fp, "HELO %s\r\n",
httpGetHostname(NULL, hostbuf, sizeof(hostbuf)));
fprintf(stderr, "DEBUG: >>> HELO %s\n", hostbuf);
if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
goto smtp_error;
fprintf(stderr, "DEBUG: <<< %s\n", response);
cupsFilePrintf(fp, "MAIL FROM:%s\r\n", mailtoFrom);
fprintf(stderr, "DEBUG: >>> MAIL FROM:%s\n", mailtoFrom);
if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
goto smtp_error;
fprintf(stderr, "DEBUG: <<< %s\n", response);
cupsFilePrintf(fp, "RCPT TO:%s\r\n", to);
fprintf(stderr, "DEBUG: >>> RCPT TO:%s\n", to);
if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
goto smtp_error;
fprintf(stderr, "DEBUG: <<< %s\n", response);
cupsFilePuts(fp, "DATA\r\n");
fputs("DEBUG: DATA\n", stderr);
if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
goto smtp_error;
fprintf(stderr, "DEBUG: <<< %s\n", response);
nl = "\r\n";
}
cupsFilePrintf(fp, "Date: %s%s", httpGetDateString(time(NULL)), nl);
cupsFilePrintf(fp, "From: %s%s", mailtoFrom, nl);
cupsFilePrintf(fp, "Subject: %s %s%s", mailtoSubject, subject, nl);
if (mailtoReplyTo[0])
{
cupsFilePrintf(fp, "Sender: %s%s", mailtoReplyTo, nl);
cupsFilePrintf(fp, "Reply-To: %s%s", mailtoReplyTo, nl);
}
cupsFilePrintf(fp, "To: %s%s", to, nl);
if (mailtoCc[0])
cupsFilePrintf(fp, "Cc: %s%s", mailtoCc, nl);
cupsFilePrintf(fp, "Content-Type: text/plain%s", nl);
cupsFilePuts(fp, nl);
cupsFilePrintf(fp, "%s%s", text, nl);
cupsFilePrintf(fp, ".\n", nl);
if (mailtoSendmail[0])
{
int status;
cupsFileClose(fp);
if (wait(&status))
status = errno << 8;
if (status)
{
if (WIFEXITED(status))
fprintf(stderr, "ERROR: Sendmail command returned status %d!\n",
WEXITSTATUS(status));
else
fprintf(stderr, "ERROR: Sendmail command crashed on signal %d!\n",
WTERMSIG(status));
}
}
else
{
if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
goto smtp_error;
fprintf(stderr, "DEBUG: <<< %s\n", response);
smtp_error:
cupsFilePuts(fp, "QUIT\r\n");
fputs("DEBUG: QUIT\n", stderr);
if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
goto smtp_error;
fprintf(stderr, "DEBUG: <<< %s\n", response);
cupsFileClose(fp);
fprintf(stderr, "DEBUG: Closed connection to \"%s\"...\n",
mailtoSMTPServer);
}
}
int
load_configuration(void)
{
cups_file_t *fp;
const char *server_root,
*server_admin;
char line[1024],
*value;
int linenum;
mailtoCc[0] = '\0';
if ((server_admin = getenv("SERVER_ADMIN")) != NULL)
strlcpy(mailtoFrom, server_admin, sizeof(mailtoFrom));
else
snprintf(mailtoFrom, sizeof(mailtoFrom), "root@%s",
httpGetHostname(NULL, line, sizeof(line)));
strlcpy(mailtoSendmail, "/usr/sbin/sendmail", sizeof(mailtoSendmail));
mailtoSMTPServer[0] = '\0';
mailtoSubject[0] = '\0';
if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
server_root = CUPS_SERVERROOT;
snprintf(line, sizeof(line), "%s/mailto.conf", server_root);
if ((fp = cupsFileOpen(line, "r")) == NULL)
{
fprintf(stderr, "ERROR: Unable to open \"%s\" - %s\n", line,
strerror(errno));
return (1);
}
linenum = 0;
while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
{
if (!value)
{
fprintf(stderr, "ERROR: No value found for %s directive on line %d!\n",
line, linenum);
cupsFileClose(fp);
return (0);
}
if (!strcasecmp(line, "Cc"))
strlcpy(mailtoCc, value, sizeof(mailtoCc));
else if (!strcasecmp(line, "From"))
strlcpy(mailtoFrom, value, sizeof(mailtoFrom));
else if (!strcasecmp(line, "Sendmail"))
{
strlcpy(mailtoSendmail, value, sizeof(mailtoSendmail));
mailtoSMTPServer[0] = '\0';
}
else if (!strcasecmp(line, "SMTPServer"))
{
mailtoSendmail[0] = '\0';
strlcpy(mailtoSMTPServer, value, sizeof(mailtoSMTPServer));
}
else if (!strcasecmp(line, "Subject"))
strlcpy(mailtoSubject, value, sizeof(mailtoSubject));
else
{
fprintf(stderr,
"ERROR: Unknown configuration directive \"%s\" on line %d!\n",
line, linenum);
}
}
cupsFileClose(fp);
return (1);
}
cups_file_t *
pipe_sendmail(const char *to)
{
cups_file_t *fp;
int pid;
int pipefds[2];
int argc;
char *argv[100],
line[1024],
*lineptr;
strlcpy(line, mailtoSendmail, sizeof(line));
argv[0] = line;
argc = 1;
for (lineptr = strchr(line, ' '); lineptr; lineptr = strchr(lineptr, ' '))
{
while (*lineptr == ' ')
*lineptr++ = '\0';
if (*lineptr)
{
argv[argc ++] = lineptr;
if (argc >= (int)(sizeof(argv) / sizeof(argv[0]) - 2))
break;
}
}
argv[argc ++] = (char *)to;
argv[argc] = NULL;
if (pipe(pipefds))
{
perror("ERROR: Unable to create pipe");
return (NULL);
}
if ((pid = fork()) == 0)
{
close(0);
dup(pipefds[0]);
close(1);
dup(2);
close(pipefds[0]);
close(pipefds[1]);
execvp(argv[0], argv);
exit(errno);
}
else if (pid < 0)
{
perror("ERROR: Unable to fork command");
close(pipefds[0]);
close(pipefds[1]);
return (NULL);
}
close(pipefds[0]);
if ((fp = cupsFileOpenFd(pipefds[1], "w")) == NULL)
{
int status;
close(pipefds[1]);
wait(&status);
}
return (fp);
}
void
print_attributes(ipp_t *ipp,
int indent)
{
int i;
ipp_tag_t group;
ipp_attribute_t *attr;
ipp_value_t *val;
static const char * const tags[] =
{
"reserved-00",
"operation-attributes-tag",
"job-attributes-tag",
"end-of-attributes-tag",
"printer-attributes-tag",
"unsupported-attributes-tag",
"subscription-attributes-tag",
"event-attributes-tag",
"reserved-08",
"reserved-09",
"reserved-0A",
"reserved-0B",
"reserved-0C",
"reserved-0D",
"reserved-0E",
"reserved-0F",
"unsupported",
"default",
"unknown",
"no-value",
"reserved-14",
"not-settable",
"delete-attr",
"admin-define",
"reserved-18",
"reserved-19",
"reserved-1A",
"reserved-1B",
"reserved-1C",
"reserved-1D",
"reserved-1E",
"reserved-1F",
"reserved-20",
"integer",
"boolean",
"enum",
"reserved-24",
"reserved-25",
"reserved-26",
"reserved-27",
"reserved-28",
"reserved-29",
"reserved-2a",
"reserved-2b",
"reserved-2c",
"reserved-2d",
"reserved-2e",
"reserved-2f",
"octetString",
"dateTime",
"resolution",
"rangeOfInteger",
"begCollection",
"textWithLanguage",
"nameWithLanguage",
"endCollection",
"reserved-38",
"reserved-39",
"reserved-3a",
"reserved-3b",
"reserved-3c",
"reserved-3d",
"reserved-3e",
"reserved-3f",
"reserved-40",
"textWithoutLanguage",
"nameWithoutLanguage",
"reserved-43",
"keyword",
"uri",
"uriScheme",
"charset",
"naturalLanguage",
"mimeMediaType",
"memberName"
};
for (group = IPP_TAG_ZERO, attr = ipp->attrs; attr; attr = attr->next)
{
if ((attr->group_tag == IPP_TAG_ZERO && indent <= 8) || !attr->name)
{
group = IPP_TAG_ZERO;
fputc('\n', stderr);
continue;
}
if (group != attr->group_tag)
{
group = attr->group_tag;
fprintf(stderr, "DEBUG: %*s%s:\n\n", indent - 4, "", tags[group]);
}
fprintf(stderr, "DEBUG: %*s%s (", indent, "", attr->name);
if (attr->num_values > 1)
fputs("1setOf ", stderr);
fprintf(stderr, "%s):", tags[attr->value_tag]);
switch (attr->value_tag)
{
case IPP_TAG_ENUM :
case IPP_TAG_INTEGER :
for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
fprintf(stderr, " %d", val->integer);
fputc('\n', stderr);
break;
case IPP_TAG_BOOLEAN :
for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
fprintf(stderr, " %s", val->boolean ? "true" : "false");
fputc('\n', stderr);
break;
case IPP_TAG_RANGE :
for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
fprintf(stderr, " %d-%d", val->range.lower, val->range.upper);
fputc('\n', stderr);
break;
case IPP_TAG_DATE :
{
time_t vtime;
struct tm *vdate;
char vstring[256];
for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
{
vtime = ippDateToTime(val->date);
vdate = localtime(&vtime);
strftime(vstring, sizeof(vstring), "%c", vdate);
fprintf(stderr, " (%s)", vstring);
}
}
fputc('\n', stderr);
break;
case IPP_TAG_RESOLUTION :
for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
fprintf(stderr, " %dx%d%s", val->resolution.xres,
val->resolution.yres,
val->resolution.units == IPP_RES_PER_INCH ? "dpi" : "dpc");
fputc('\n', stderr);
break;
case IPP_TAG_STRING :
case IPP_TAG_TEXTLANG :
case IPP_TAG_NAMELANG :
case IPP_TAG_TEXT :
case IPP_TAG_NAME :
case IPP_TAG_KEYWORD :
case IPP_TAG_URI :
case IPP_TAG_URISCHEME :
case IPP_TAG_CHARSET :
case IPP_TAG_LANGUAGE :
case IPP_TAG_MIMETYPE :
for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
fprintf(stderr, " \"%s\"", val->string.text);
fputc('\n', stderr);
break;
case IPP_TAG_BEGIN_COLLECTION :
fputc('\n', stderr);
for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
{
if (i)
fputc('\n', stderr);
print_attributes(val->collection, indent + 4);
}
break;
default :
fprintf(stderr, "UNKNOWN (%d values)\n", attr->num_values);
break;
}
}
}