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