]> git.ipfire.org Git - thirdparty/cups.git/blob - notifier/mailto.c
Merge changes from CUPS 1.4svn-r7696.
[thirdparty/cups.git] / notifier / mailto.c
1 /*
2 * "$Id: mailto.c 7460 2008-04-16 02:19:54Z mike $"
3 *
4 * "mailto" notifier for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 1997-2005 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * Contents:
16 *
17 * main() - Main entry for the mailto notifier.
18 * email_message() - Email a notification message.
19 * load_configuration() - Load the mailto.conf file.
20 * pipe_sendmail() - Open a pipe to sendmail...
21 * print_attributes() - Print the attributes in a request...
22 */
23
24 /*
25 * Include necessary headers...
26 */
27
28 #include <cups/cups.h>
29 #include <cups/i18n.h>
30 #include <cups/string.h>
31 #include <errno.h>
32 #include <sys/wait.h>
33 #include <signal.h>
34
35
36 /*
37 * Globals...
38 */
39
40 char mailtoCc[1024]; /* Cc email address */
41 char mailtoFrom[1024]; /* From email address */
42 char mailtoReplyTo[1024]; /* Reply-To email address */
43 char mailtoSubject[1024]; /* Subject prefix */
44 char mailtoSMTPServer[1024]; /* SMTP server to use */
45 char mailtoSendmail[1024]; /* Sendmail program to use */
46
47
48 /*
49 * Local functions...
50 */
51
52 void email_message(const char *to, const char *subject,
53 const char *text);
54 int load_configuration(void);
55 cups_file_t *pipe_sendmail(const char *to);
56 void print_attributes(ipp_t *ipp, int indent);
57
58
59 /*
60 * 'main()' - Main entry for the mailto notifier.
61 */
62
63 int /* O - Exit status */
64 main(int argc, /* I - Number of command-line arguments */
65 char *argv[]) /* I - Command-line arguments */
66 {
67 int i; /* Looping var */
68 ipp_t *msg; /* Event message from scheduler */
69 ipp_state_t state; /* IPP event state */
70 char *subject, /* Subject for notification message */
71 *text; /* Text for notification message */
72 cups_lang_t *lang; /* Language info */
73 char temp[1024]; /* Temporary string */
74 int templen; /* Length of temporary string */
75 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
76 struct sigaction action; /* POSIX sigaction data */
77 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
78
79
80 /*
81 * Don't buffer stderr...
82 */
83
84 setbuf(stderr, NULL);
85
86 /*
87 * Ignore SIGPIPE signals...
88 */
89
90 #ifdef HAVE_SIGSET
91 sigset(SIGPIPE, SIG_IGN);
92 #elif defined(HAVE_SIGACTION)
93 memset(&action, 0, sizeof(action));
94 action.sa_handler = SIG_IGN;
95 sigaction(SIGPIPE, &action, NULL);
96 #else
97 signal(SIGPIPE, SIG_IGN);
98 #endif /* HAVE_SIGSET */
99
100 /*
101 * Validate command-line options...
102 */
103
104 if (argc != 3)
105 {
106 fputs("Usage: mailto mailto:user@domain.com notify-user-data\n", stderr);
107 return (1);
108 }
109
110 if (strncmp(argv[1], "mailto:", 7))
111 {
112 fprintf(stderr, "ERROR: Bad recipient \"%s\"!\n", argv[1]);
113 return (1);
114 }
115
116 fprintf(stderr, "DEBUG: argc=%d\n", argc);
117 for (i = 0; i < argc; i ++)
118 fprintf(stderr, "DEBUG: argv[%d]=\"%s\"\n", i, argv[i]);
119
120 /*
121 * Load configuration data...
122 */
123
124 if ((lang = cupsLangDefault()) == NULL)
125 return (1);
126
127 if (!load_configuration())
128 return (1);
129
130 /*
131 * Get the reply-to address...
132 */
133
134 templen = sizeof(temp);
135 httpDecode64_2(temp, &templen, argv[2]);
136
137 if (!strncmp(temp, "mailto:", 7))
138 strlcpy(mailtoReplyTo, temp + 7, sizeof(mailtoReplyTo));
139 else if (temp[0])
140 fprintf(stderr, "WARNING: Bad notify-user-data value (%d bytes) ignored!\n",
141 templen);
142
143 /*
144 * Loop forever until we run out of events...
145 */
146
147 for (;;)
148 {
149 /*
150 * Get the next event...
151 */
152
153 msg = ippNew();
154 while ((state = ippReadFile(0, msg)) != IPP_DATA)
155 {
156 if (state <= IPP_IDLE)
157 break;
158 }
159
160 fprintf(stderr, "DEBUG: state=%d\n", state);
161
162 if (state == IPP_ERROR)
163 fputs("DEBUG: ippReadFile() returned IPP_ERROR!\n", stderr);
164
165 if (state <= IPP_IDLE)
166 {
167 /*
168 * Out of messages, free memory and then exit...
169 */
170
171 ippDelete(msg);
172 return (0);
173 }
174
175 /*
176 * Get the subject and text for the message, then email it...
177 */
178
179 subject = cupsNotifySubject(lang, msg);
180 text = cupsNotifyText(lang, msg);
181
182 fprintf(stderr, "DEBUG: subject=\"%s\"\n", subject);
183 fprintf(stderr, "DEBUG: text=\"%s\"\n", text);
184
185 if (subject && text)
186 email_message(argv[1] + 7, subject, text);
187 else
188 {
189 fputs("ERROR: Missing attributes in event notification!\n", stderr);
190 print_attributes(msg, 4);
191 }
192
193 /*
194 * Free the memory used for this event...
195 */
196
197 if (subject)
198 free(subject);
199
200 if (text)
201 free(text);
202
203 ippDelete(msg);
204 }
205 }
206
207
208 /*
209 * 'email_message()' - Email a notification message.
210 */
211
212 void
213 email_message(const char *to, /* I - Recipient of message */
214 const char *subject, /* I - Subject of message */
215 const char *text) /* I - Text of message */
216 {
217 cups_file_t *fp; /* Pipe/socket to mail server */
218 const char *nl; /* Newline to use */
219 char response[1024]; /* SMTP response buffer */
220
221
222 /*
223 * Connect to the mail server...
224 */
225
226 if (mailtoSendmail[0])
227 {
228 /*
229 * Use the sendmail command...
230 */
231
232 fp = pipe_sendmail(to);
233
234 if (!fp)
235 return;
236
237 nl = "\n";
238 }
239 else
240 {
241 /*
242 * Use an SMTP server...
243 */
244
245 char hostbuf[1024]; /* Local hostname */
246
247
248 if (strchr(mailtoSMTPServer, ':'))
249 fp = cupsFileOpen(mailtoSMTPServer, "s");
250 else
251 {
252 char spec[1024]; /* Host:service spec */
253
254
255 snprintf(spec, sizeof(spec), "%s:smtp", mailtoSMTPServer);
256 fp = cupsFileOpen(spec, "s");
257 }
258
259 if (!fp)
260 {
261 fprintf(stderr, "ERROR: Unable to connect to SMTP server \"%s\"!\n",
262 mailtoSMTPServer);
263 return;
264 }
265
266 fprintf(stderr, "DEBUG: Connected to \"%s\"...\n", mailtoSMTPServer);
267
268 cupsFilePrintf(fp, "HELO %s\r\n",
269 httpGetHostname(NULL, hostbuf, sizeof(hostbuf)));
270 fprintf(stderr, "DEBUG: >>> HELO %s\n", hostbuf);
271
272 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
273 goto smtp_error;
274 fprintf(stderr, "DEBUG: <<< %s\n", response);
275
276 cupsFilePrintf(fp, "MAIL FROM:%s\r\n", mailtoFrom);
277 fprintf(stderr, "DEBUG: >>> MAIL FROM:%s\n", mailtoFrom);
278
279 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
280 goto smtp_error;
281 fprintf(stderr, "DEBUG: <<< %s\n", response);
282
283 cupsFilePrintf(fp, "RCPT TO:%s\r\n", to);
284 fprintf(stderr, "DEBUG: >>> RCPT TO:%s\n", to);
285
286 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
287 goto smtp_error;
288 fprintf(stderr, "DEBUG: <<< %s\n", response);
289
290 cupsFilePuts(fp, "DATA\r\n");
291 fputs("DEBUG: DATA\n", stderr);
292
293 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
294 goto smtp_error;
295 fprintf(stderr, "DEBUG: <<< %s\n", response);
296
297 nl = "\r\n";
298 }
299
300 /*
301 * Send the message...
302 */
303
304 cupsFilePrintf(fp, "Date: %s%s", httpGetDateString(time(NULL)), nl);
305 cupsFilePrintf(fp, "From: %s%s", mailtoFrom, nl);
306 cupsFilePrintf(fp, "Subject: %s %s%s", mailtoSubject, subject, nl);
307 if (mailtoReplyTo[0])
308 {
309 cupsFilePrintf(fp, "Sender: %s%s", mailtoReplyTo, nl);
310 cupsFilePrintf(fp, "Reply-To: %s%s", mailtoReplyTo, nl);
311 }
312 cupsFilePrintf(fp, "To: %s%s", to, nl);
313 if (mailtoCc[0])
314 cupsFilePrintf(fp, "Cc: %s%s", mailtoCc, nl);
315 cupsFilePrintf(fp, "Content-Type: text/plain%s", nl);
316 cupsFilePuts(fp, nl);
317 cupsFilePrintf(fp, "%s%s", text, nl);
318 cupsFilePrintf(fp, ".%s", nl);
319
320 /*
321 * Close the connection to the mail server...
322 */
323
324 if (mailtoSendmail[0])
325 {
326 /*
327 * Close the pipe and wait for the sendmail command to finish...
328 */
329
330 int status; /* Exit status */
331
332
333 cupsFileClose(fp);
334
335 if (wait(&status))
336 status = errno << 8;
337
338 /*
339 * Report any non-zero status...
340 */
341
342 if (status)
343 {
344 if (WIFEXITED(status))
345 fprintf(stderr, "ERROR: Sendmail command returned status %d!\n",
346 WEXITSTATUS(status));
347 else
348 fprintf(stderr, "ERROR: Sendmail command crashed on signal %d!\n",
349 WTERMSIG(status));
350 }
351 }
352 else
353 {
354 /*
355 * Finish up the SMTP submission and close the connection...
356 */
357
358 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
359 goto smtp_error;
360 fprintf(stderr, "DEBUG: <<< %s\n", response);
361
362 /*
363 * Process SMTP errors here...
364 */
365
366 smtp_error:
367
368 cupsFilePuts(fp, "QUIT\r\n");
369 fputs("DEBUG: QUIT\n", stderr);
370
371 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
372 goto smtp_error;
373 fprintf(stderr, "DEBUG: <<< %s\n", response);
374
375 cupsFileClose(fp);
376
377 fprintf(stderr, "DEBUG: Closed connection to \"%s\"...\n",
378 mailtoSMTPServer);
379 }
380 }
381
382
383 /*
384 * 'load_configuration()' - Load the mailto.conf file.
385 */
386
387 int /* I - 1 on success, 0 on failure */
388 load_configuration(void)
389 {
390 cups_file_t *fp; /* mailto.conf file */
391 const char *server_root, /* CUPS_SERVERROOT environment variable */
392 *server_admin; /* SERVER_ADMIN environment variable */
393 char line[1024], /* Line from file */
394 *value; /* Value for directive */
395 int linenum; /* Line number in file */
396
397
398 /*
399 * Initialize defaults...
400 */
401
402 mailtoCc[0] = '\0';
403
404 if ((server_admin = getenv("SERVER_ADMIN")) != NULL)
405 strlcpy(mailtoFrom, server_admin, sizeof(mailtoFrom));
406 else
407 snprintf(mailtoFrom, sizeof(mailtoFrom), "root@%s",
408 httpGetHostname(NULL, line, sizeof(line)));
409
410 strlcpy(mailtoSendmail, "/usr/sbin/sendmail", sizeof(mailtoSendmail));
411
412 mailtoSMTPServer[0] = '\0';
413
414 mailtoSubject[0] = '\0';
415
416 /*
417 * Try loading the config file...
418 */
419
420 if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
421 server_root = CUPS_SERVERROOT;
422
423 snprintf(line, sizeof(line), "%s/mailto.conf", server_root);
424
425 if ((fp = cupsFileOpen(line, "r")) == NULL)
426 {
427 fprintf(stderr, "ERROR: Unable to open \"%s\" - %s\n", line,
428 strerror(errno));
429 return (1);
430 }
431
432 linenum = 0;
433
434 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
435 {
436 if (!value)
437 {
438 fprintf(stderr, "ERROR: No value found for %s directive on line %d!\n",
439 line, linenum);
440 cupsFileClose(fp);
441 return (0);
442 }
443
444 if (!strcasecmp(line, "Cc"))
445 strlcpy(mailtoCc, value, sizeof(mailtoCc));
446 else if (!strcasecmp(line, "From"))
447 strlcpy(mailtoFrom, value, sizeof(mailtoFrom));
448 else if (!strcasecmp(line, "Sendmail"))
449 {
450 strlcpy(mailtoSendmail, value, sizeof(mailtoSendmail));
451 mailtoSMTPServer[0] = '\0';
452 }
453 else if (!strcasecmp(line, "SMTPServer"))
454 {
455 mailtoSendmail[0] = '\0';
456 strlcpy(mailtoSMTPServer, value, sizeof(mailtoSMTPServer));
457 }
458 else if (!strcasecmp(line, "Subject"))
459 strlcpy(mailtoSubject, value, sizeof(mailtoSubject));
460 else
461 {
462 fprintf(stderr,
463 "ERROR: Unknown configuration directive \"%s\" on line %d!\n",
464 line, linenum);
465 }
466 }
467
468 /*
469 * Close file and return...
470 */
471
472 cupsFileClose(fp);
473
474 return (1);
475 }
476
477
478 /*
479 * 'pipe_sendmail()' - Open a pipe to sendmail...
480 */
481
482 cups_file_t * /* O - CUPS file */
483 pipe_sendmail(const char *to) /* I - To: address */
484 {
485 cups_file_t *fp; /* CUPS file */
486 int pid; /* Process ID */
487 int pipefds[2]; /* Pipe file descriptors */
488 int argc; /* Number of arguments */
489 char *argv[100], /* Argument array */
490 line[1024], /* Sendmail command + args */
491 *lineptr; /* Pointer into line */
492
493
494 /*
495 * First break the mailtoSendmail string into arguments...
496 */
497
498 strlcpy(line, mailtoSendmail, sizeof(line));
499 argv[0] = line;
500 argc = 1;
501
502 for (lineptr = strchr(line, ' '); lineptr; lineptr = strchr(lineptr, ' '))
503 {
504 while (*lineptr == ' ')
505 *lineptr++ = '\0';
506
507 if (*lineptr)
508 {
509 /*
510 * Point to the next argument...
511 */
512
513 argv[argc ++] = lineptr;
514
515 /*
516 * Stop if we have too many...
517 */
518
519 if (argc >= (int)(sizeof(argv) / sizeof(argv[0]) - 2))
520 break;
521 }
522 }
523
524 argv[argc ++] = (char *)to;
525 argv[argc] = NULL;
526
527 /*
528 * Create the pipe...
529 */
530
531 if (pipe(pipefds))
532 {
533 perror("ERROR: Unable to create pipe");
534 return (NULL);
535 }
536
537 /*
538 * Then run the command...
539 */
540
541 if ((pid = fork()) == 0)
542 {
543 /*
544 * Child goes here - redirect stdin to the input side of the pipe,
545 * redirect stdout to stderr, and exec...
546 */
547
548 close(0);
549 dup(pipefds[0]);
550
551 close(1);
552 dup(2);
553
554 close(pipefds[0]);
555 close(pipefds[1]);
556
557 execvp(argv[0], argv);
558 exit(errno);
559 }
560 else if (pid < 0)
561 {
562 /*
563 * Unable to fork - error out...
564 */
565
566 perror("ERROR: Unable to fork command");
567
568 close(pipefds[0]);
569 close(pipefds[1]);
570
571 return (NULL);
572 }
573
574 /*
575 * Create a CUPS file using the output side of the pipe and close the
576 * input side...
577 */
578
579 close(pipefds[0]);
580
581 if ((fp = cupsFileOpenFd(pipefds[1], "w")) == NULL)
582 {
583 int status; /* Status of command */
584
585
586 close(pipefds[1]);
587 wait(&status);
588 }
589
590 return (fp);
591 }
592
593
594 /*
595 * 'print_attributes()' - Print the attributes in a request...
596 */
597
598 void
599 print_attributes(ipp_t *ipp, /* I - IPP request */
600 int indent) /* I - Indentation */
601 {
602 int i; /* Looping var */
603 ipp_tag_t group; /* Current group */
604 ipp_attribute_t *attr; /* Current attribute */
605 ipp_value_t *val; /* Current value */
606 static const char * const tags[] = /* Value/group tag strings */
607 {
608 "reserved-00",
609 "operation-attributes-tag",
610 "job-attributes-tag",
611 "end-of-attributes-tag",
612 "printer-attributes-tag",
613 "unsupported-attributes-tag",
614 "subscription-attributes-tag",
615 "event-attributes-tag",
616 "reserved-08",
617 "reserved-09",
618 "reserved-0A",
619 "reserved-0B",
620 "reserved-0C",
621 "reserved-0D",
622 "reserved-0E",
623 "reserved-0F",
624 "unsupported",
625 "default",
626 "unknown",
627 "no-value",
628 "reserved-14",
629 "not-settable",
630 "delete-attr",
631 "admin-define",
632 "reserved-18",
633 "reserved-19",
634 "reserved-1A",
635 "reserved-1B",
636 "reserved-1C",
637 "reserved-1D",
638 "reserved-1E",
639 "reserved-1F",
640 "reserved-20",
641 "integer",
642 "boolean",
643 "enum",
644 "reserved-24",
645 "reserved-25",
646 "reserved-26",
647 "reserved-27",
648 "reserved-28",
649 "reserved-29",
650 "reserved-2a",
651 "reserved-2b",
652 "reserved-2c",
653 "reserved-2d",
654 "reserved-2e",
655 "reserved-2f",
656 "octetString",
657 "dateTime",
658 "resolution",
659 "rangeOfInteger",
660 "begCollection",
661 "textWithLanguage",
662 "nameWithLanguage",
663 "endCollection",
664 "reserved-38",
665 "reserved-39",
666 "reserved-3a",
667 "reserved-3b",
668 "reserved-3c",
669 "reserved-3d",
670 "reserved-3e",
671 "reserved-3f",
672 "reserved-40",
673 "textWithoutLanguage",
674 "nameWithoutLanguage",
675 "reserved-43",
676 "keyword",
677 "uri",
678 "uriScheme",
679 "charset",
680 "naturalLanguage",
681 "mimeMediaType",
682 "memberName"
683 };
684
685
686 for (group = IPP_TAG_ZERO, attr = ipp->attrs; attr; attr = attr->next)
687 {
688 if ((attr->group_tag == IPP_TAG_ZERO && indent <= 8) || !attr->name)
689 {
690 group = IPP_TAG_ZERO;
691 fputc('\n', stderr);
692 continue;
693 }
694
695 if (group != attr->group_tag)
696 {
697 group = attr->group_tag;
698
699 fprintf(stderr, "DEBUG: %*s%s:\n\n", indent - 4, "", tags[group]);
700 }
701
702 fprintf(stderr, "DEBUG: %*s%s (", indent, "", attr->name);
703 if (attr->num_values > 1)
704 fputs("1setOf ", stderr);
705 fprintf(stderr, "%s):", tags[attr->value_tag]);
706
707 switch (attr->value_tag)
708 {
709 case IPP_TAG_ENUM :
710 case IPP_TAG_INTEGER :
711 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
712 fprintf(stderr, " %d", val->integer);
713 fputc('\n', stderr);
714 break;
715
716 case IPP_TAG_BOOLEAN :
717 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
718 fprintf(stderr, " %s", val->boolean ? "true" : "false");
719 fputc('\n', stderr);
720 break;
721
722 case IPP_TAG_RANGE :
723 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
724 fprintf(stderr, " %d-%d", val->range.lower, val->range.upper);
725 fputc('\n', stderr);
726 break;
727
728 case IPP_TAG_DATE :
729 {
730 time_t vtime; /* Date/Time value */
731 struct tm *vdate; /* Date info */
732 char vstring[256]; /* Formatted time */
733
734 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
735 {
736 vtime = ippDateToTime(val->date);
737 vdate = localtime(&vtime);
738 strftime(vstring, sizeof(vstring), "%c", vdate);
739 fprintf(stderr, " (%s)", vstring);
740 }
741 }
742 fputc('\n', stderr);
743 break;
744
745 case IPP_TAG_RESOLUTION :
746 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
747 fprintf(stderr, " %dx%d%s", val->resolution.xres,
748 val->resolution.yres,
749 val->resolution.units == IPP_RES_PER_INCH ? "dpi" : "dpc");
750 fputc('\n', stderr);
751 break;
752
753 case IPP_TAG_STRING :
754 case IPP_TAG_TEXTLANG :
755 case IPP_TAG_NAMELANG :
756 case IPP_TAG_TEXT :
757 case IPP_TAG_NAME :
758 case IPP_TAG_KEYWORD :
759 case IPP_TAG_URI :
760 case IPP_TAG_URISCHEME :
761 case IPP_TAG_CHARSET :
762 case IPP_TAG_LANGUAGE :
763 case IPP_TAG_MIMETYPE :
764 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
765 fprintf(stderr, " \"%s\"", val->string.text);
766 fputc('\n', stderr);
767 break;
768
769 case IPP_TAG_BEGIN_COLLECTION :
770 fputc('\n', stderr);
771
772 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
773 {
774 if (i)
775 fputc('\n', stderr);
776 print_attributes(val->collection, indent + 4);
777 }
778 break;
779
780 default :
781 fprintf(stderr, "UNKNOWN (%d values)\n", attr->num_values);
782 break;
783 }
784 }
785 }
786
787
788 /*
789 * End of "$Id: mailto.c 7460 2008-04-16 02:19:54Z mike $".
790 */