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