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