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