Revert manifest to default one
[external/cups.git] / notifier / mailto.c
1 /*
2  * "$Id: mailto.c 9793 2011-05-20 03:49:49Z mike $"
3  *
4  *   "mailto" notifier for CUPS.
5  *
6  *   Copyright 2007-2011 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-private.h>
29 #include <sys/wait.h>
30 #include <signal.h>
31
32
33 /*
34  * Globals...
35  */
36
37 char    mailtoCc[1024];                 /* Cc email address */
38 char    mailtoFrom[1024];               /* From email address */
39 char    mailtoReplyTo[1024];            /* Reply-To email address */
40 char    mailtoSubject[1024];            /* Subject prefix */
41 char    mailtoSMTPServer[1024];         /* SMTP server to use */
42 char    mailtoSendmail[1024];           /* Sendmail program to use */
43
44
45 /*
46  * Local functions...
47  */
48
49 void            email_message(const char *to, const char *subject,
50                               const char *text);
51 int             load_configuration(void);
52 cups_file_t     *pipe_sendmail(const char *to);
53 void            print_attributes(ipp_t *ipp, int indent);
54
55
56 /*
57  * 'main()' - Main entry for the mailto notifier.
58  */
59
60 int                                     /* O - Exit status */
61 main(int  argc,                         /* I - Number of command-line arguments */
62      char *argv[])                      /* I - Command-line arguments */
63 {
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
209 void
210 email_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
265     cupsFilePrintf(fp, "HELO %s\r\n",
266                    httpGetHostname(NULL, hostbuf, sizeof(hostbuf)));
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);
315   cupsFilePrintf(fp, ".%s", nl);
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
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     }
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)
377       fprintf(stderr, "ERROR: Got \"%s\" trying to QUIT connection.\n",
378               response);
379     else
380       fprintf(stderr, "DEBUG: <<< %s\n", response);
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
394 int                                     /* I - 1 on success, 0 on failure */
395 load_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",
415              httpGetHostname(NULL, line, sizeof(line)));
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   {
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);
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
456     if (!_cups_strcasecmp(line, "Cc"))
457       strlcpy(mailtoCc, value, sizeof(mailtoCc));
458     else if (!_cups_strcasecmp(line, "From"))
459       strlcpy(mailtoFrom, value, sizeof(mailtoFrom));
460     else if (!_cups_strcasecmp(line, "Sendmail"))
461     {
462       strlcpy(mailtoSendmail, value, sizeof(mailtoSendmail));
463       mailtoSMTPServer[0] = '\0';
464     }
465     else if (!_cups_strcasecmp(line, "SMTPServer"))
466     {
467       mailtoSendmail[0] = '\0';
468       strlcpy(mailtoSMTPServer, value, sizeof(mailtoSMTPServer));
469     }
470     else if (!_cups_strcasecmp(line, "Subject"))
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
494 cups_file_t *                           /* O - CUPS file */
495 pipe_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
610 void
611 print_attributes(ipp_t *ipp,            /* I - IPP request */
612                  int   indent)          /* I - Indentation */
613 {
614   ipp_tag_t             group;          /* Current group */
615   ipp_attribute_t       *attr;          /* Current attribute */
616   char                  buffer[1024];   /* Value buffer */
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
632       fprintf(stderr, "DEBUG: %*s%s:\n\n", indent - 4, "", ippTagString(group));
633     }
634
635     _ippAttrString(attr, buffer, sizeof(buffer));
636
637     fprintf(stderr, "DEBUG: %*s%s (%s%s) %s", indent, "", attr->name,
638             attr->num_values > 1 ? "1setOf " : "",
639             ippTagString(attr->value_tag), buffer);
640   }
641 }
642
643
644 /*
645  * End of "$Id: mailto.c 9793 2011-05-20 03:49:49Z mike $".
646  */