1 /*
2 * "mailto" notifier for CUPS.
3 *
4 * Copyright © 2007-2018 by Apple Inc.
5 * Copyright © 1997-2005 by Easy Software Products.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
9 */
10
11 /*
12 * Include necessary headers...
13 */
14
15 #include <cups/cups-private.h>
16 #include <sys/wait.h>
17 #include <signal.h>
18
19
20 /*
21 * Globals...
22 */
23
24 char mailtoCc[1024]; /* Cc email address */
25 char mailtoFrom[1024]; /* From email address */
26 char mailtoReplyTo[1024]; /* Reply-To email address */
27 char mailtoSubject[1024]; /* Subject prefix */
28 char mailtoSMTPServer[1024]; /* SMTP server to use */
29 char mailtoSendmail[1024]; /* Sendmail program to use */
30
31
32 /*
33 * Local functions...
34 */
35
36 void email_message(const char *to, const char *subject, const char *text);
37 int load_configuration(void);
38 cups_file_t *pipe_sendmail(const char *to);
39 void print_attributes(ipp_t *ipp, int indent);
40
41
42 /*
43 * 'main()' - Main entry for the mailto notifier.
44 */
45
46 int /* O - Exit status */
main(int argc,char * argv[])47 main(int argc, /* I - Number of command-line arguments */
48 char *argv[]) /* I - Command-line arguments */
49 {
50 int i; /* Looping var */
51 ipp_t *msg; /* Event message from scheduler */
52 ipp_state_t state; /* IPP event state */
53 char *subject, /* Subject for notification message */
54 *text; /* Text for notification message */
55 cups_lang_t *lang; /* Language info */
56 char temp[1024]; /* Temporary string */
57 int templen; /* Length of temporary string */
58 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
59 struct sigaction action; /* POSIX sigaction data */
60 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
61
62
63 /*
64 * Don't buffer stderr...
65 */
66
67 setbuf(stderr, NULL);
68
69 /*
70 * Ignore SIGPIPE signals...
71 */
72
73 #ifdef HAVE_SIGSET
74 sigset(SIGPIPE, SIG_IGN);
75 #elif defined(HAVE_SIGACTION)
76 memset(&action, 0, sizeof(action));
77 action.sa_handler = SIG_IGN;
78 sigaction(SIGPIPE, &action, NULL);
79 #else
80 signal(SIGPIPE, SIG_IGN);
81 #endif /* HAVE_SIGSET */
82
83 /*
84 * Validate command-line options...
85 */
86
87 if (argc != 3)
88 {
89 fputs("Usage: mailto mailto:user@domain.com notify-user-data\n", stderr);
90 return (1);
91 }
92
93 if (strncmp(argv[1], "mailto:", 7))
94 {
95 fprintf(stderr, "ERROR: Bad recipient \"%s\"!\n", argv[1]);
96 return (1);
97 }
98
99 fprintf(stderr, "DEBUG: argc=%d\n", argc);
100 for (i = 0; i < argc; i ++)
101 fprintf(stderr, "DEBUG: argv[%d]=\"%s\"\n", i, argv[i]);
102
103 /*
104 * Load configuration data...
105 */
106
107 if ((lang = cupsLangDefault()) == NULL)
108 return (1);
109
110 if (!load_configuration())
111 return (1);
112
113 /*
114 * Get the reply-to address...
115 */
116
117 templen = sizeof(temp);
118 httpDecode64_2(temp, &templen, argv[2]);
119
120 if (!strncmp(temp, "mailto:", 7))
121 strlcpy(mailtoReplyTo, temp + 7, sizeof(mailtoReplyTo));
122 else if (temp[0])
123 fprintf(stderr, "WARNING: Bad notify-user-data value (%d bytes) ignored!\n",
124 templen);
125
126 /*
127 * Loop forever until we run out of events...
128 */
129
130 for (;;)
131 {
132 /*
133 * Get the next event...
134 */
135
136 msg = ippNew();
137 while ((state = ippReadFile(0, msg)) != IPP_DATA)
138 {
139 if (state <= IPP_IDLE)
140 break;
141 }
142
143 fprintf(stderr, "DEBUG: state=%d\n", state);
144
145 if (state == IPP_ERROR)
146 fputs("DEBUG: ippReadFile() returned IPP_ERROR!\n", stderr);
147
148 if (state <= IPP_IDLE)
149 {
150 /*
151 * Out of messages, free memory and then exit...
152 */
153
154 ippDelete(msg);
155 return (0);
156 }
157
158 /*
159 * Get the subject and text for the message, then email it...
160 */
161
162 subject = cupsNotifySubject(lang, msg);
163 text = cupsNotifyText(lang, msg);
164
165 fprintf(stderr, "DEBUG: subject=\"%s\"\n", subject);
166 fprintf(stderr, "DEBUG: text=\"%s\"\n", text);
167
168 if (subject && text)
169 email_message(argv[1] + 7, subject, text);
170 else
171 {
172 fputs("ERROR: Missing attributes in event notification!\n", stderr);
173 print_attributes(msg, 4);
174 }
175
176 /*
177 * Free the memory used for this event...
178 */
179
180 if (subject)
181 free(subject);
182
183 if (text)
184 free(text);
185
186 ippDelete(msg);
187 }
188 }
189
190
191 /*
192 * 'email_message()' - Email a notification message.
193 */
194
195 void
email_message(const char * to,const char * subject,const char * text)196 email_message(const char *to, /* I - Recipient of message */
197 const char *subject, /* I - Subject of message */
198 const char *text) /* I - Text of message */
199 {
200 cups_file_t *fp; /* Pipe/socket to mail server */
201 const char *nl; /* Newline to use */
202 char response[1024]; /* SMTP response buffer */
203
204
205 /*
206 * Connect to the mail server...
207 */
208
209 if (mailtoSendmail[0])
210 {
211 /*
212 * Use the sendmail command...
213 */
214
215 fp = pipe_sendmail(to);
216
217 if (!fp)
218 return;
219
220 nl = "\n";
221 }
222 else
223 {
224 /*
225 * Use an SMTP server...
226 */
227
228 char hostbuf[1024]; /* Local hostname */
229
230
231 if (strchr(mailtoSMTPServer, ':'))
232 {
233 fp = cupsFileOpen(mailtoSMTPServer, "s");
234 }
235 else
236 {
237 char spec[1024]; /* Host:service spec */
238
239
240 snprintf(spec, sizeof(spec), "%s:smtp", mailtoSMTPServer);
241 fp = cupsFileOpen(spec, "s");
242 }
243
244 if (!fp)
245 {
246 fprintf(stderr, "ERROR: Unable to connect to SMTP server \"%s\"!\n",
247 mailtoSMTPServer);
248 return;
249 }
250
251 fprintf(stderr, "DEBUG: Connected to \"%s\"...\n", mailtoSMTPServer);
252
253 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
254 goto smtp_error;
255 fprintf(stderr, "DEBUG: <<< %s\n", response);
256
257 cupsFilePrintf(fp, "HELO %s\r\n",
258 httpGetHostname(NULL, hostbuf, sizeof(hostbuf)));
259 fprintf(stderr, "DEBUG: >>> HELO %s\n", hostbuf);
260
261 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
262 goto smtp_error;
263 fprintf(stderr, "DEBUG: <<< %s\n", response);
264
265 cupsFilePrintf(fp, "MAIL FROM:%s\r\n", mailtoFrom);
266 fprintf(stderr, "DEBUG: >>> MAIL FROM:%s\n", mailtoFrom);
267
268 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
269 goto smtp_error;
270 fprintf(stderr, "DEBUG: <<< %s\n", response);
271
272 cupsFilePrintf(fp, "RCPT TO:%s\r\n", to);
273 fprintf(stderr, "DEBUG: >>> RCPT TO:%s\n", to);
274
275 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
276 goto smtp_error;
277 fprintf(stderr, "DEBUG: <<< %s\n", response);
278
279 cupsFilePuts(fp, "DATA\r\n");
280 fputs("DEBUG: DATA\n", stderr);
281
282 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
283 goto smtp_error;
284 fprintf(stderr, "DEBUG: <<< %s\n", response);
285
286 nl = "\r\n";
287 }
288
289 /*
290 * Send the message...
291 */
292
293 cupsFilePrintf(fp, "Date: %s%s", httpGetDateString(time(NULL)), nl);
294 cupsFilePrintf(fp, "From: %s%s", mailtoFrom, nl);
295 cupsFilePrintf(fp, "Subject: %s %s%s", mailtoSubject, subject, nl);
296 if (mailtoReplyTo[0])
297 {
298 cupsFilePrintf(fp, "Sender: %s%s", mailtoReplyTo, nl);
299 cupsFilePrintf(fp, "Reply-To: %s%s", mailtoReplyTo, nl);
300 }
301 cupsFilePrintf(fp, "To: %s%s", to, nl);
302 if (mailtoCc[0])
303 cupsFilePrintf(fp, "Cc: %s%s", mailtoCc, nl);
304 cupsFilePrintf(fp, "Content-Type: text/plain%s", nl);
305 cupsFilePuts(fp, nl);
306 cupsFilePrintf(fp, "%s%s", text, nl);
307 cupsFilePrintf(fp, ".%s", nl);
308
309 /*
310 * Close the connection to the mail server...
311 */
312
313 if (mailtoSendmail[0])
314 {
315 /*
316 * Close the pipe and wait for the sendmail command to finish...
317 */
318
319 int status; /* Exit status */
320
321
322 cupsFileClose(fp);
323
324 while (wait(&status))
325 {
326 if (errno != EINTR)
327 {
328 fprintf(stderr, "DEBUG: Unable to get child status: %s\n",
329 strerror(errno));
330 status = 0;
331 break;
332 }
333 }
334
335 /*
336 * Report any non-zero status...
337 */
338
339 if (status)
340 {
341 if (WIFEXITED(status))
342 fprintf(stderr, "ERROR: Sendmail command returned status %d!\n",
343 WEXITSTATUS(status));
344 else
345 fprintf(stderr, "ERROR: Sendmail command crashed on signal %d!\n",
346 WTERMSIG(status));
347 }
348 }
349 else
350 {
351 /*
352 * Finish up the SMTP submission and close the connection...
353 */
354
355 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
356 goto smtp_error;
357 fprintf(stderr, "DEBUG: <<< %s\n", response);
358
359 /*
360 * Process SMTP errors here...
361 */
362
363 smtp_error:
364
365 cupsFilePuts(fp, "QUIT\r\n");
366 fputs("DEBUG: QUIT\n", stderr);
367
368 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
369 fprintf(stderr, "ERROR: Got \"%s\" trying to QUIT connection.\n",
370 response);
371 else
372 fprintf(stderr, "DEBUG: <<< %s\n", response);
373
374 cupsFileClose(fp);
375
376 fprintf(stderr, "DEBUG: Closed connection to \"%s\"...\n",
377 mailtoSMTPServer);
378 }
379 }
380
381
382 /*
383 * 'load_configuration()' - Load the mailto.conf file.
384 */
385
386 int /* I - 1 on success, 0 on failure */
load_configuration(void)387 load_configuration(void)
388 {
389 cups_file_t *fp; /* mailto.conf file */
390 const char *server_root, /* CUPS_SERVERROOT environment variable */
391 *server_admin; /* SERVER_ADMIN environment variable */
392 char line[1024], /* Line from file */
393 *value; /* Value for directive */
394 int linenum; /* Line number in file */
395
396
397 /*
398 * Initialize defaults...
399 */
400
401 mailtoCc[0] = '\0';
402
403 if ((server_admin = getenv("SERVER_ADMIN")) != NULL)
404 strlcpy(mailtoFrom, server_admin, sizeof(mailtoFrom));
405 else
406 snprintf(mailtoFrom, sizeof(mailtoFrom), "root@%s",
407 httpGetHostname(NULL, line, sizeof(line)));
408
409 strlcpy(mailtoSendmail, "/usr/sbin/sendmail", sizeof(mailtoSendmail));
410
411 mailtoSMTPServer[0] = '\0';
412
413 mailtoSubject[0] = '\0';
414
415 /*
416 * Try loading the config file...
417 */
418
419 if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
420 server_root = CUPS_SERVERROOT;
421
422 snprintf(line, sizeof(line), "%s/mailto.conf", server_root);
423
424 if ((fp = cupsFileOpen(line, "r")) == NULL)
425 {
426 if (errno != ENOENT)
427 {
428 fprintf(stderr, "ERROR: Unable to open \"%s\" - %s\n", line,
429 strerror(errno));
430 return (1);
431 }
432 else
433 return (0);
434 }
435
436 linenum = 0;
437
438 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
439 {
440 if (!value)
441 {
442 fprintf(stderr, "ERROR: No value found for %s directive on line %d!\n",
443 line, linenum);
444 cupsFileClose(fp);
445 return (0);
446 }
447
448 if (!_cups_strcasecmp(line, "Cc"))
449 strlcpy(mailtoCc, value, sizeof(mailtoCc));
450 else if (!_cups_strcasecmp(line, "From"))
451 strlcpy(mailtoFrom, value, sizeof(mailtoFrom));
452 else if (!_cups_strcasecmp(line, "Sendmail"))
453 {
454 strlcpy(mailtoSendmail, value, sizeof(mailtoSendmail));
455 mailtoSMTPServer[0] = '\0';
456 }
457 else if (!_cups_strcasecmp(line, "SMTPServer"))
458 {
459 mailtoSendmail[0] = '\0';
460 strlcpy(mailtoSMTPServer, value, sizeof(mailtoSMTPServer));
461 }
462 else if (!_cups_strcasecmp(line, "Subject"))
463 strlcpy(mailtoSubject, value, sizeof(mailtoSubject));
464 else
465 {
466 fprintf(stderr,
467 "ERROR: Unknown configuration directive \"%s\" on line %d!\n",
468 line, linenum);
469 }
470 }
471
472 /*
473 * Close file and return...
474 */
475
476 cupsFileClose(fp);
477
478 return (1);
479 }
480
481
482 /*
483 * 'pipe_sendmail()' - Open a pipe to sendmail...
484 */
485
486 cups_file_t * /* O - CUPS file */
pipe_sendmail(const char * to)487 pipe_sendmail(const char *to) /* I - To: address */
488 {
489 cups_file_t *fp; /* CUPS file */
490 int pid; /* Process ID */
491 int pipefds[2]; /* Pipe file descriptors */
492 int argc; /* Number of arguments */
493 char *argv[100], /* Argument array */
494 line[1024], /* Sendmail command + args */
495 *lineptr; /* Pointer into line */
496
497
498 /*
499 * First break the mailtoSendmail string into arguments...
500 */
501
502 strlcpy(line, mailtoSendmail, sizeof(line));
503 argv[0] = line;
504 argc = 1;
505
506 for (lineptr = strchr(line, ' '); lineptr; lineptr = strchr(lineptr, ' '))
507 {
508 while (*lineptr == ' ')
509 *lineptr++ = '\0';
510
511 if (*lineptr)
512 {
513 /*
514 * Point to the next argument...
515 */
516
517 argv[argc ++] = lineptr;
518
519 /*
520 * Stop if we have too many...
521 */
522
523 if (argc >= (int)(sizeof(argv) / sizeof(argv[0]) - 2))
524 break;
525 }
526 }
527
528 argv[argc ++] = (char *)to;
529 argv[argc] = NULL;
530
531 /*
532 * Create the pipe...
533 */
534
535 if (pipe(pipefds))
536 {
537 perror("ERROR: Unable to create pipe");
538 return (NULL);
539 }
540
541 /*
542 * Then run the command...
543 */
544
545 if ((pid = fork()) == 0)
546 {
547 /*
548 * Child goes here - redirect stdin to the input side of the pipe,
549 * redirect stdout to stderr, and exec...
550 */
551
552 close(0);
553 dup(pipefds[0]);
554
555 close(1);
556 dup(2);
557
558 close(pipefds[0]);
559 close(pipefds[1]);
560
561 execvp(argv[0], argv);
562 exit(errno);
563 }
564 else if (pid < 0)
565 {
566 /*
567 * Unable to fork - error out...
568 */
569
570 perror("ERROR: Unable to fork command");
571
572 close(pipefds[0]);
573 close(pipefds[1]);
574
575 return (NULL);
576 }
577
578 /*
579 * Create a CUPS file using the output side of the pipe and close the
580 * input side...
581 */
582
583 close(pipefds[0]);
584
585 if ((fp = cupsFileOpenFd(pipefds[1], "w")) == NULL)
586 {
587 int status; /* Status of command */
588
589
590 close(pipefds[1]);
591 wait(&status);
592 }
593
594 return (fp);
595 }
596
597
598 /*
599 * 'print_attributes()' - Print the attributes in a request...
600 */
601
602 void
print_attributes(ipp_t * ipp,int indent)603 print_attributes(ipp_t *ipp, /* I - IPP request */
604 int indent) /* I - Indentation */
605 {
606 ipp_tag_t group; /* Current group */
607 ipp_attribute_t *attr; /* Current attribute */
608 char buffer[1024]; /* Value buffer */
609
610
611 for (group = IPP_TAG_ZERO, attr = ipp->attrs; attr; attr = attr->next)
612 {
613 if ((attr->group_tag == IPP_TAG_ZERO && indent <= 8) || !attr->name)
614 {
615 group = IPP_TAG_ZERO;
616 fputc('\n', stderr);
617 continue;
618 }
619
620 if (group != attr->group_tag)
621 {
622 group = attr->group_tag;
623
624 fprintf(stderr, "DEBUG: %*s%s:\n\n", indent - 4, "", ippTagString(group));
625 }
626
627 ippAttributeString(attr, buffer, sizeof(buffer));
628
629 fprintf(stderr, "DEBUG: %*s%s (%s%s) %s", indent, "", attr->name,
630 attr->num_values > 1 ? "1setOf " : "",
631 ippTagString(attr->value_tag), buffer);
632 }
633 }
634