]> git.ipfire.org Git - thirdparty/cups.git/blame - notifier/mailto.c
License change: Apache License, Version 2.0.
[thirdparty/cups.git] / notifier / mailto.c
CommitLineData
ef416fc2 1/*
503b54c9 2 * "mailto" notifier for CUPS.
ef416fc2 3 *
503b54c9
MS
4 * Copyright 2007-2011 by Apple Inc.
5 * Copyright 1997-2005 by Easy Software Products.
ef416fc2 6 *
e3101897 7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
ef416fc2 8 */
9
10/*
11 * Include necessary headers...
12 */
13
e60ec91f 14#include <cups/cups-private.h>
fa73b229 15#include <sys/wait.h>
16#include <signal.h>
17
18
19/*
20 * Globals...
21 */
22
23char mailtoCc[1024]; /* Cc email address */
24char mailtoFrom[1024]; /* From email address */
25char mailtoReplyTo[1024]; /* Reply-To email address */
26char mailtoSubject[1024]; /* Subject prefix */
27char mailtoSMTPServer[1024]; /* SMTP server to use */
28char mailtoSendmail[1024]; /* Sendmail program to use */
29
30
31/*
32 * Local functions...
33 */
34
35void email_message(const char *to, const char *subject,
36 const char *text);
37int load_configuration(void);
38cups_file_t *pipe_sendmail(const char *to);
39void print_attributes(ipp_t *ipp, int indent);
ef416fc2 40
41
42/*
43 * 'main()' - Main entry for the mailto notifier.
44 */
45
46int /* O - Exit status */
47main(int argc, /* I - Number of command-line arguments */
48 char *argv[]) /* I - Command-line arguments */
49{
fa73b229 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
195void
196email_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 fp = cupsFileOpen(mailtoSMTPServer, "s");
233 else
234 {
235 char spec[1024]; /* Host:service spec */
236
237
238 snprintf(spec, sizeof(spec), "%s:smtp", mailtoSMTPServer);
239 fp = cupsFileOpen(spec, "s");
240 }
241
242 if (!fp)
243 {
244 fprintf(stderr, "ERROR: Unable to connect to SMTP server \"%s\"!\n",
245 mailtoSMTPServer);
246 return;
247 }
248
249 fprintf(stderr, "DEBUG: Connected to \"%s\"...\n", mailtoSMTPServer);
250
757d2cad 251 cupsFilePrintf(fp, "HELO %s\r\n",
252 httpGetHostname(NULL, hostbuf, sizeof(hostbuf)));
fa73b229 253 fprintf(stderr, "DEBUG: >>> HELO %s\n", hostbuf);
254
255 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
256 goto smtp_error;
257 fprintf(stderr, "DEBUG: <<< %s\n", response);
258
259 cupsFilePrintf(fp, "MAIL FROM:%s\r\n", mailtoFrom);
260 fprintf(stderr, "DEBUG: >>> MAIL FROM:%s\n", mailtoFrom);
261
262 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
263 goto smtp_error;
264 fprintf(stderr, "DEBUG: <<< %s\n", response);
265
266 cupsFilePrintf(fp, "RCPT TO:%s\r\n", to);
267 fprintf(stderr, "DEBUG: >>> RCPT TO:%s\n", to);
268
269 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
270 goto smtp_error;
271 fprintf(stderr, "DEBUG: <<< %s\n", response);
272
273 cupsFilePuts(fp, "DATA\r\n");
274 fputs("DEBUG: DATA\n", stderr);
275
276 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
277 goto smtp_error;
278 fprintf(stderr, "DEBUG: <<< %s\n", response);
279
280 nl = "\r\n";
281 }
282
283 /*
284 * Send the message...
285 */
286
287 cupsFilePrintf(fp, "Date: %s%s", httpGetDateString(time(NULL)), nl);
288 cupsFilePrintf(fp, "From: %s%s", mailtoFrom, nl);
289 cupsFilePrintf(fp, "Subject: %s %s%s", mailtoSubject, subject, nl);
290 if (mailtoReplyTo[0])
291 {
292 cupsFilePrintf(fp, "Sender: %s%s", mailtoReplyTo, nl);
293 cupsFilePrintf(fp, "Reply-To: %s%s", mailtoReplyTo, nl);
294 }
295 cupsFilePrintf(fp, "To: %s%s", to, nl);
296 if (mailtoCc[0])
297 cupsFilePrintf(fp, "Cc: %s%s", mailtoCc, nl);
298 cupsFilePrintf(fp, "Content-Type: text/plain%s", nl);
299 cupsFilePuts(fp, nl);
300 cupsFilePrintf(fp, "%s%s", text, nl);
ae71f5de 301 cupsFilePrintf(fp, ".%s", nl);
fa73b229 302
303 /*
304 * Close the connection to the mail server...
305 */
306
307 if (mailtoSendmail[0])
308 {
309 /*
310 * Close the pipe and wait for the sendmail command to finish...
311 */
312
313 int status; /* Exit status */
314
315
316 cupsFileClose(fp);
317
e60ec91f
MS
318 while (wait(&status))
319 {
320 if (errno != EINTR)
321 {
322 fprintf(stderr, "DEBUG: Unable to get child status: %s\n",
323 strerror(errno));
324 status = 0;
325 break;
326 }
327 }
fa73b229 328
329 /*
330 * Report any non-zero status...
331 */
332
333 if (status)
334 {
335 if (WIFEXITED(status))
336 fprintf(stderr, "ERROR: Sendmail command returned status %d!\n",
337 WEXITSTATUS(status));
338 else
339 fprintf(stderr, "ERROR: Sendmail command crashed on signal %d!\n",
340 WTERMSIG(status));
341 }
342 }
343 else
344 {
345 /*
346 * Finish up the SMTP submission and close the connection...
347 */
348
349 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
350 goto smtp_error;
351 fprintf(stderr, "DEBUG: <<< %s\n", response);
352
353 /*
354 * Process SMTP errors here...
355 */
356
357 smtp_error:
358
359 cupsFilePuts(fp, "QUIT\r\n");
360 fputs("DEBUG: QUIT\n", stderr);
361
362 if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
c7017ecc
MS
363 fprintf(stderr, "ERROR: Got \"%s\" trying to QUIT connection.\n",
364 response);
365 else
366 fprintf(stderr, "DEBUG: <<< %s\n", response);
fa73b229 367
368 cupsFileClose(fp);
369
370 fprintf(stderr, "DEBUG: Closed connection to \"%s\"...\n",
371 mailtoSMTPServer);
372 }
373}
374
375
376/*
377 * 'load_configuration()' - Load the mailto.conf file.
378 */
379
380int /* I - 1 on success, 0 on failure */
381load_configuration(void)
382{
383 cups_file_t *fp; /* mailto.conf file */
384 const char *server_root, /* CUPS_SERVERROOT environment variable */
385 *server_admin; /* SERVER_ADMIN environment variable */
386 char line[1024], /* Line from file */
387 *value; /* Value for directive */
388 int linenum; /* Line number in file */
389
390
391 /*
392 * Initialize defaults...
393 */
394
395 mailtoCc[0] = '\0';
396
397 if ((server_admin = getenv("SERVER_ADMIN")) != NULL)
398 strlcpy(mailtoFrom, server_admin, sizeof(mailtoFrom));
399 else
400 snprintf(mailtoFrom, sizeof(mailtoFrom), "root@%s",
757d2cad 401 httpGetHostname(NULL, line, sizeof(line)));
fa73b229 402
403 strlcpy(mailtoSendmail, "/usr/sbin/sendmail", sizeof(mailtoSendmail));
404
405 mailtoSMTPServer[0] = '\0';
406
407 mailtoSubject[0] = '\0';
408
409 /*
410 * Try loading the config file...
411 */
412
413 if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
414 server_root = CUPS_SERVERROOT;
415
416 snprintf(line, sizeof(line), "%s/mailto.conf", server_root);
417
418 if ((fp = cupsFileOpen(line, "r")) == NULL)
419 {
e60ec91f
MS
420 if (errno != ENOENT)
421 {
422 fprintf(stderr, "ERROR: Unable to open \"%s\" - %s\n", line,
423 strerror(errno));
424 return (1);
425 }
426 else
427 return (0);
fa73b229 428 }
429
430 linenum = 0;
431
432 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
433 {
434 if (!value)
435 {
436 fprintf(stderr, "ERROR: No value found for %s directive on line %d!\n",
437 line, linenum);
438 cupsFileClose(fp);
439 return (0);
440 }
441
88f9aafc 442 if (!_cups_strcasecmp(line, "Cc"))
fa73b229 443 strlcpy(mailtoCc, value, sizeof(mailtoCc));
88f9aafc 444 else if (!_cups_strcasecmp(line, "From"))
fa73b229 445 strlcpy(mailtoFrom, value, sizeof(mailtoFrom));
88f9aafc 446 else if (!_cups_strcasecmp(line, "Sendmail"))
fa73b229 447 {
448 strlcpy(mailtoSendmail, value, sizeof(mailtoSendmail));
449 mailtoSMTPServer[0] = '\0';
450 }
88f9aafc 451 else if (!_cups_strcasecmp(line, "SMTPServer"))
fa73b229 452 {
453 mailtoSendmail[0] = '\0';
454 strlcpy(mailtoSMTPServer, value, sizeof(mailtoSMTPServer));
455 }
88f9aafc 456 else if (!_cups_strcasecmp(line, "Subject"))
fa73b229 457 strlcpy(mailtoSubject, value, sizeof(mailtoSubject));
458 else
459 {
460 fprintf(stderr,
461 "ERROR: Unknown configuration directive \"%s\" on line %d!\n",
462 line, linenum);
463 }
464 }
465
466 /*
467 * Close file and return...
468 */
469
470 cupsFileClose(fp);
471
472 return (1);
473}
474
475
476/*
477 * 'pipe_sendmail()' - Open a pipe to sendmail...
478 */
479
480cups_file_t * /* O - CUPS file */
481pipe_sendmail(const char *to) /* I - To: address */
482{
483 cups_file_t *fp; /* CUPS file */
484 int pid; /* Process ID */
485 int pipefds[2]; /* Pipe file descriptors */
486 int argc; /* Number of arguments */
487 char *argv[100], /* Argument array */
488 line[1024], /* Sendmail command + args */
489 *lineptr; /* Pointer into line */
490
491
492 /*
493 * First break the mailtoSendmail string into arguments...
494 */
495
496 strlcpy(line, mailtoSendmail, sizeof(line));
497 argv[0] = line;
498 argc = 1;
499
500 for (lineptr = strchr(line, ' '); lineptr; lineptr = strchr(lineptr, ' '))
501 {
502 while (*lineptr == ' ')
503 *lineptr++ = '\0';
504
505 if (*lineptr)
506 {
507 /*
508 * Point to the next argument...
509 */
510
511 argv[argc ++] = lineptr;
512
513 /*
514 * Stop if we have too many...
515 */
516
517 if (argc >= (int)(sizeof(argv) / sizeof(argv[0]) - 2))
518 break;
519 }
520 }
521
522 argv[argc ++] = (char *)to;
523 argv[argc] = NULL;
524
525 /*
526 * Create the pipe...
527 */
528
529 if (pipe(pipefds))
530 {
531 perror("ERROR: Unable to create pipe");
532 return (NULL);
533 }
534
535 /*
536 * Then run the command...
537 */
538
539 if ((pid = fork()) == 0)
540 {
541 /*
542 * Child goes here - redirect stdin to the input side of the pipe,
543 * redirect stdout to stderr, and exec...
544 */
545
546 close(0);
547 dup(pipefds[0]);
548
549 close(1);
550 dup(2);
551
552 close(pipefds[0]);
553 close(pipefds[1]);
554
555 execvp(argv[0], argv);
556 exit(errno);
557 }
558 else if (pid < 0)
559 {
560 /*
561 * Unable to fork - error out...
562 */
563
564 perror("ERROR: Unable to fork command");
565
566 close(pipefds[0]);
567 close(pipefds[1]);
568
569 return (NULL);
570 }
571
572 /*
573 * Create a CUPS file using the output side of the pipe and close the
574 * input side...
575 */
576
577 close(pipefds[0]);
578
579 if ((fp = cupsFileOpenFd(pipefds[1], "w")) == NULL)
580 {
581 int status; /* Status of command */
582
583
584 close(pipefds[1]);
585 wait(&status);
586 }
587
588 return (fp);
589}
590
591
592/*
593 * 'print_attributes()' - Print the attributes in a request...
594 */
595
596void
597print_attributes(ipp_t *ipp, /* I - IPP request */
598 int indent) /* I - Indentation */
599{
fa73b229 600 ipp_tag_t group; /* Current group */
601 ipp_attribute_t *attr; /* Current attribute */
e60ec91f 602 char buffer[1024]; /* Value buffer */
fa73b229 603
604
605 for (group = IPP_TAG_ZERO, attr = ipp->attrs; attr; attr = attr->next)
606 {
607 if ((attr->group_tag == IPP_TAG_ZERO && indent <= 8) || !attr->name)
608 {
609 group = IPP_TAG_ZERO;
610 fputc('\n', stderr);
611 continue;
612 }
613
614 if (group != attr->group_tag)
615 {
616 group = attr->group_tag;
617
e60ec91f 618 fprintf(stderr, "DEBUG: %*s%s:\n\n", indent - 4, "", ippTagString(group));
fa73b229 619 }
620
a2326b5b 621 ippAttributeString(attr, buffer, sizeof(buffer));
fa73b229 622
e60ec91f
MS
623 fprintf(stderr, "DEBUG: %*s%s (%s%s) %s", indent, "", attr->name,
624 attr->num_values > 1 ? "1setOf " : "",
625 ippTagString(attr->value_tag), buffer);
fa73b229 626 }
ef416fc2 627}