]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - notifier/mailto.c
Remove svn:keywords since they cause svn_load_dirs.pl to complain about every file.
[thirdparty/cups.git] / notifier / mailto.c
index 33ec692e90fb7c113896198b315063bbdc1cf262..38489caf29d1de089957867ecc81528255e2832d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: mailto.c 4829 2005-11-12 03:15:10Z mike $"
+ * "$Id: mailto.c 177 2006-06-21 00:20:03Z jlovell $"
  *
  *   "mailto" notifier for the Common UNIX Printing System (CUPS).
  *
  *
  * Contents:
  *
+ *   main()               - Main entry for the mailto notifier.
+ *   email_message()      - Email a notification message.
+ *   load_configuration() - Load the mailto.conf file.
+ *   pipe_sendmail()      - Open a pipe to sendmail...
+ *   print_attributes()   - Print the attributes in a request...
  */
 
 /*
  */
 
 #include <cups/cups.h>
-#include <cups/language.h>
+#include <cups/i18n.h>
 #include <cups/string.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+
+/*
+ * Globals...
+ */
+
+char   mailtoCc[1024];                 /* Cc email address */
+char   mailtoFrom[1024];               /* From email address */
+char   mailtoReplyTo[1024];            /* Reply-To email address */
+char   mailtoSubject[1024];            /* Subject prefix */
+char   mailtoSMTPServer[1024];         /* SMTP server to use */
+char   mailtoSendmail[1024];           /* Sendmail program to use */
+
+
+/*
+ * Local functions...
+ */
+
+void           email_message(const char *to, const char *subject,
+                             const char *text);
+int            load_configuration(void);
+cups_file_t    *pipe_sendmail(const char *to);
+void           print_attributes(ipp_t *ipp, int indent);
 
 
 /*
@@ -42,9 +73,727 @@ int                                 /* O - Exit status */
 main(int  argc,                                /* I - Number of command-line arguments */
      char *argv[])                     /* I - Command-line arguments */
 {
+  int          i;                      /* Looping var */
+  ipp_t                *msg;                   /* Event message from scheduler */
+  ipp_state_t  state;                  /* IPP event state */
+  char         *subject,               /* Subject for notification message */
+               *text;                  /* Text for notification message */
+  cups_lang_t  *lang;                  /* Language info */
+  char         temp[1024];             /* Temporary string */
+  int          templen;                /* Length of temporary string */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+  struct sigaction action;             /* POSIX sigaction data */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+  * Don't buffer stderr...
+  */
+
+  setbuf(stderr, NULL);
+
+ /*
+  * Ignore SIGPIPE signals...
+  */
+
+#ifdef HAVE_SIGSET
+  sigset(SIGPIPE, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+  memset(&action, 0, sizeof(action));
+  action.sa_handler = SIG_IGN;
+  sigaction(SIGPIPE, &action, NULL);
+#else
+  signal(SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGSET */
+
+ /*
+  * Validate command-line options...
+  */
+
+  if (argc != 3)
+  {
+    fputs("Usage: mailto mailto:user@domain.com notify-user-data\n", stderr);
+    return (1);
+  }
+
+  if (strncmp(argv[1], "mailto:", 7))
+  {
+    fprintf(stderr, "ERROR: Bad recipient \"%s\"!\n", argv[1]);
+    return (1);
+  }
+
+  fprintf(stderr, "DEBUG: argc=%d\n", argc);
+  for (i = 0; i < argc; i ++)
+    fprintf(stderr, "DEBUG: argv[%d]=\"%s\"\n", i, argv[i]);
+
+ /*
+  * Load configuration data...
+  */
+
+  if ((lang = cupsLangDefault()) == NULL)
+    return (1);
+
+  if (!load_configuration())
+    return (1);
+
+ /*
+  * Get the reply-to address...
+  */
+
+  templen = sizeof(temp);
+  httpDecode64_2(temp, &templen, argv[2]);
+
+  if (!strncmp(temp, "mailto:", 7))
+    strlcpy(mailtoReplyTo, temp + 7, sizeof(mailtoReplyTo));
+  else if (temp[0])
+    fprintf(stderr, "WARNING: Bad notify-user-data value (%d bytes) ignored!\n",
+            templen);
+
+ /*
+  * Loop forever until we run out of events...
+  */
+
+  for (;;)
+  {
+   /*
+    * Get the next event...
+    */
+
+    msg = ippNew();
+    while ((state = ippReadFile(0, msg)) != IPP_DATA)
+    {
+      if (state <= IPP_IDLE)
+        break;
+    }
+
+    fprintf(stderr, "DEBUG: state=%d\n", state);
+
+    if (state == IPP_ERROR)
+      fputs("DEBUG: ippReadFile() returned IPP_ERROR!\n", stderr);
+
+    if (state <= IPP_IDLE)
+    {
+     /*
+      * Out of messages, free memory and then exit...
+      */
+
+      ippDelete(msg);
+      return (0);
+    }
+
+   /*
+    * Get the subject and text for the message, then email it...
+    */
+
+    subject = cupsNotifySubject(lang, msg);
+    text    = cupsNotifyText(lang, msg);
+
+    fprintf(stderr, "DEBUG: subject=\"%s\"\n", subject);
+    fprintf(stderr, "DEBUG: text=\"%s\"\n", text);
+
+    if (subject && text)
+      email_message(argv[1] + 7, subject, text);
+    else
+    {
+      fputs("ERROR: Missing attributes in event notification!\n", stderr);
+      print_attributes(msg, 4);
+    }
+
+   /*
+    * Free the memory used for this event...
+    */
+
+    if (subject)
+      free(subject);
+
+    if (text)
+      free(text);
+
+    ippDelete(msg);
+  }
+}
+
+
+/*
+ * 'email_message()' - Email a notification message.
+ */
+
+void
+email_message(const char *to,          /* I - Recipient of message */
+              const char *subject,     /* I - Subject of message */
+             const char *text)         /* I - Text of message */
+{
+  cups_file_t  *fp;                    /* Pipe/socket to mail server */
+  const char   *nl;                    /* Newline to use */
+  char         response[1024];         /* SMTP response buffer */
+
+
+ /*
+  * Connect to the mail server...
+  */
+
+  if (mailtoSendmail[0])
+  {
+   /*
+    * Use the sendmail command...
+    */
+
+    fp = pipe_sendmail(to);
+
+    if (!fp)
+      return;
+
+    nl = "\n";
+  }
+  else
+  {
+   /*
+    * Use an SMTP server...
+    */
+
+    char       hostbuf[1024];          /* Local hostname */
+
+
+    if (strchr(mailtoSMTPServer, ':'))
+      fp = cupsFileOpen(mailtoSMTPServer, "s");
+    else
+    {
+      char     spec[1024];             /* Host:service spec */
+
+
+      snprintf(spec, sizeof(spec), "%s:smtp", mailtoSMTPServer);
+      fp = cupsFileOpen(spec, "s");
+    }
+
+    if (!fp)
+    {
+      fprintf(stderr, "ERROR: Unable to connect to SMTP server \"%s\"!\n",
+              mailtoSMTPServer);
+      return;
+    }
+
+    fprintf(stderr, "DEBUG: Connected to \"%s\"...\n", mailtoSMTPServer);
+
+    cupsFilePrintf(fp, "HELO %s\r\n",
+                   httpGetHostname(NULL, hostbuf, sizeof(hostbuf)));
+    fprintf(stderr, "DEBUG: >>> HELO %s\n", hostbuf);
+
+    if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
+      goto smtp_error;
+    fprintf(stderr, "DEBUG: <<< %s\n", response);
+
+    cupsFilePrintf(fp, "MAIL FROM:%s\r\n", mailtoFrom);
+    fprintf(stderr, "DEBUG: >>> MAIL FROM:%s\n", mailtoFrom);
+
+    if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
+      goto smtp_error;
+    fprintf(stderr, "DEBUG: <<< %s\n", response);
+
+    cupsFilePrintf(fp, "RCPT TO:%s\r\n", to);
+    fprintf(stderr, "DEBUG: >>> RCPT TO:%s\n", to);
+
+    if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
+      goto smtp_error;
+    fprintf(stderr, "DEBUG: <<< %s\n", response);
+
+    cupsFilePuts(fp, "DATA\r\n");
+    fputs("DEBUG: DATA\n", stderr);
+
+    if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
+      goto smtp_error;
+    fprintf(stderr, "DEBUG: <<< %s\n", response);
+
+    nl = "\r\n";
+  }
+
+ /*
+  * Send the message...
+  */
+
+  cupsFilePrintf(fp, "Date: %s%s", httpGetDateString(time(NULL)), nl);
+  cupsFilePrintf(fp, "From: %s%s", mailtoFrom, nl);
+  cupsFilePrintf(fp, "Subject: %s %s%s", mailtoSubject, subject, nl);
+  if (mailtoReplyTo[0])
+  {
+    cupsFilePrintf(fp, "Sender: %s%s", mailtoReplyTo, nl);
+    cupsFilePrintf(fp, "Reply-To: %s%s", mailtoReplyTo, nl);
+  }
+  cupsFilePrintf(fp, "To: %s%s", to, nl);
+  if (mailtoCc[0])
+    cupsFilePrintf(fp, "Cc: %s%s", mailtoCc, nl);
+  cupsFilePrintf(fp, "Content-Type: text/plain%s", nl);
+  cupsFilePuts(fp, nl);
+  cupsFilePrintf(fp, "%s%s", text, nl);
+  cupsFilePrintf(fp, ".\n", nl);
+
+ /*
+  * Close the connection to the mail server...
+  */
+
+  if (mailtoSendmail[0])
+  {
+   /*
+    * Close the pipe and wait for the sendmail command to finish...
+    */
+
+    int        status;                         /* Exit status */
+
+
+    cupsFileClose(fp);
+
+    if (wait(&status))
+      status = errno << 8;
+
+   /*
+    * Report any non-zero status...
+    */
+
+    if (status)
+    {
+      if (WIFEXITED(status))
+        fprintf(stderr, "ERROR: Sendmail command returned status %d!\n",
+               WEXITSTATUS(status));
+      else
+        fprintf(stderr, "ERROR: Sendmail command crashed on signal %d!\n",
+               WTERMSIG(status));
+    }
+  }
+  else
+  {
+   /*
+    * Finish up the SMTP submission and close the connection...
+    */
+
+    if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
+      goto smtp_error;
+    fprintf(stderr, "DEBUG: <<< %s\n", response);
+
+   /*
+    * Process SMTP errors here...
+    */
+
+    smtp_error:
+
+    cupsFilePuts(fp, "QUIT\r\n");
+    fputs("DEBUG: QUIT\n", stderr);
+
+    if (!cupsFileGets(fp, response, sizeof(response)) || atoi(response) >= 500)
+      goto smtp_error;
+    fprintf(stderr, "DEBUG: <<< %s\n", response);
+
+    cupsFileClose(fp);
+
+    fprintf(stderr, "DEBUG: Closed connection to \"%s\"...\n",
+            mailtoSMTPServer);
+  }
+}
+
+
+/*
+ * 'load_configuration()' - Load the mailto.conf file.
+ */
+
+int                                    /* I - 1 on success, 0 on failure */
+load_configuration(void)
+{
+  cups_file_t  *fp;                    /* mailto.conf file */
+  const char   *server_root,           /* CUPS_SERVERROOT environment variable */
+               *server_admin;          /* SERVER_ADMIN environment variable */
+  char         line[1024],             /* Line from file */
+               *value;                 /* Value for directive */
+  int          linenum;                /* Line number in file */
+
+
+ /*
+  * Initialize defaults...
+  */
+
+  mailtoCc[0] = '\0';
+
+  if ((server_admin = getenv("SERVER_ADMIN")) != NULL)
+    strlcpy(mailtoFrom, server_admin, sizeof(mailtoFrom));
+  else
+    snprintf(mailtoFrom, sizeof(mailtoFrom), "root@%s",
+             httpGetHostname(NULL, line, sizeof(line)));
+
+  strlcpy(mailtoSendmail, "/usr/sbin/sendmail", sizeof(mailtoSendmail));
+
+  mailtoSMTPServer[0] = '\0';
+
+  mailtoSubject[0] = '\0';
+
+ /*
+  * Try loading the config file...
+  */
+
+  if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
+    server_root = CUPS_SERVERROOT;
+
+  snprintf(line, sizeof(line), "%s/mailto.conf", server_root);
+
+  if ((fp = cupsFileOpen(line, "r")) == NULL)
+  {
+    fprintf(stderr, "ERROR: Unable to open \"%s\" - %s\n", line,
+            strerror(errno));
+    return (1);
+  }
+
+  linenum = 0;
+
+  while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
+  {
+    if (!value)
+    {
+      fprintf(stderr, "ERROR: No value found for %s directive on line %d!\n",
+              line, linenum);
+      cupsFileClose(fp);
+      return (0);
+    }
+
+    if (!strcasecmp(line, "Cc"))
+      strlcpy(mailtoCc, value, sizeof(mailtoCc));
+    else if (!strcasecmp(line, "From"))
+      strlcpy(mailtoFrom, value, sizeof(mailtoFrom));
+    else if (!strcasecmp(line, "Sendmail"))
+    {
+      strlcpy(mailtoSendmail, value, sizeof(mailtoSendmail));
+      mailtoSMTPServer[0] = '\0';
+    }
+    else if (!strcasecmp(line, "SMTPServer"))
+    {
+      mailtoSendmail[0] = '\0';
+      strlcpy(mailtoSMTPServer, value, sizeof(mailtoSMTPServer));
+    }
+    else if (!strcasecmp(line, "Subject"))
+      strlcpy(mailtoSubject, value, sizeof(mailtoSubject));
+    else
+    {
+      fprintf(stderr,
+              "ERROR: Unknown configuration directive \"%s\" on line %d!\n",
+              line, linenum);
+    }
+  }
+
+ /*
+  * Close file and return...
+  */
+
+  cupsFileClose(fp);
+
+  return (1);
+}
+
+
+/*
+ * 'pipe_sendmail()' - Open a pipe to sendmail...
+ */
+
+cups_file_t *                          /* O - CUPS file */
+pipe_sendmail(const char *to)          /* I - To: address */
+{
+  cups_file_t  *fp;                    /* CUPS file */
+  int          pid;                    /* Process ID */
+  int          pipefds[2];             /* Pipe file descriptors */
+  int          argc;                   /* Number of arguments */
+  char         *argv[100],             /* Argument array */
+               line[1024],             /* Sendmail command + args */
+               *lineptr;               /* Pointer into line */
+
+
+ /*
+  * First break the mailtoSendmail string into arguments...
+  */
+
+  strlcpy(line, mailtoSendmail, sizeof(line));
+  argv[0] = line;
+  argc    = 1;
+
+  for (lineptr = strchr(line, ' '); lineptr; lineptr = strchr(lineptr, ' '))
+  {
+    while (*lineptr == ' ')
+      *lineptr++ = '\0';
+
+    if (*lineptr)
+    {
+     /*
+      * Point to the next argument...
+      */
+
+      argv[argc ++] = lineptr;
+
+     /*
+      * Stop if we have too many...
+      */
+
+      if (argc >= (int)(sizeof(argv) / sizeof(argv[0]) - 2))
+        break;
+    }
+  }
+
+  argv[argc ++] = (char *)to;
+  argv[argc]    = NULL;
+
+ /*
+  * Create the pipe...
+  */
+
+  if (pipe(pipefds))
+  {
+    perror("ERROR: Unable to create pipe");
+    return (NULL);
+  }
+
+ /*
+  * Then run the command...
+  */
+
+  if ((pid = fork()) == 0)
+  {
+   /*
+    * Child goes here - redirect stdin to the input side of the pipe,
+    * redirect stdout to stderr, and exec...
+    */
+
+    close(0);
+    dup(pipefds[0]);
+
+    close(1);
+    dup(2);
+
+    close(pipefds[0]);
+    close(pipefds[1]);
+
+    execvp(argv[0], argv);
+    exit(errno);
+  }
+  else if (pid < 0)
+  {
+   /*
+    * Unable to fork - error out...
+    */
+
+    perror("ERROR: Unable to fork command");
+
+    close(pipefds[0]);
+    close(pipefds[1]);
+
+    return (NULL);
+  }
+
+ /*
+  * Create a CUPS file using the output side of the pipe and close the
+  * input side...
+  */
+
+  close(pipefds[0]);
+
+  if ((fp = cupsFileOpenFd(pipefds[1], "w")) == NULL)
+  {
+    int        status;                         /* Status of command */
+
+
+    close(pipefds[1]);
+    wait(&status);
+  }
+
+  return (fp);
+}
+
+
+/*
+ * 'print_attributes()' - Print the attributes in a request...
+ */
+
+void
+print_attributes(ipp_t *ipp,           /* I - IPP request */
+                 int   indent)         /* I - Indentation */
+{
+  int                  i;              /* Looping var */
+  ipp_tag_t            group;          /* Current group */
+  ipp_attribute_t      *attr;          /* Current attribute */
+  ipp_value_t          *val;           /* Current value */
+  static const char * const tags[] =   /* Value/group tag strings */
+                       {
+                         "reserved-00",
+                         "operation-attributes-tag",
+                         "job-attributes-tag",
+                         "end-of-attributes-tag",
+                         "printer-attributes-tag",
+                         "unsupported-attributes-tag",
+                         "subscription-attributes-tag",
+                         "event-attributes-tag",
+                         "reserved-08",
+                         "reserved-09",
+                         "reserved-0A",
+                         "reserved-0B",
+                         "reserved-0C",
+                         "reserved-0D",
+                         "reserved-0E",
+                         "reserved-0F",
+                         "unsupported",
+                         "default",
+                         "unknown",
+                         "no-value",
+                         "reserved-14",
+                         "not-settable",
+                         "delete-attr",
+                         "admin-define",
+                         "reserved-18",
+                         "reserved-19",
+                         "reserved-1A",
+                         "reserved-1B",
+                         "reserved-1C",
+                         "reserved-1D",
+                         "reserved-1E",
+                         "reserved-1F",
+                         "reserved-20",
+                         "integer",
+                         "boolean",
+                         "enum",
+                         "reserved-24",
+                         "reserved-25",
+                         "reserved-26",
+                         "reserved-27",
+                         "reserved-28",
+                         "reserved-29",
+                         "reserved-2a",
+                         "reserved-2b",
+                         "reserved-2c",
+                         "reserved-2d",
+                         "reserved-2e",
+                         "reserved-2f",
+                         "octetString",
+                         "dateTime",
+                         "resolution",
+                         "rangeOfInteger",
+                         "begCollection",
+                         "textWithLanguage",
+                         "nameWithLanguage",
+                         "endCollection",
+                         "reserved-38",
+                         "reserved-39",
+                         "reserved-3a",
+                         "reserved-3b",
+                         "reserved-3c",
+                         "reserved-3d",
+                         "reserved-3e",
+                         "reserved-3f",
+                         "reserved-40",
+                         "textWithoutLanguage",
+                         "nameWithoutLanguage",
+                         "reserved-43",
+                         "keyword",
+                         "uri",
+                         "uriScheme",
+                         "charset",
+                         "naturalLanguage",
+                         "mimeMediaType",
+                         "memberName"
+                       };
+
+
+  for (group = IPP_TAG_ZERO, attr = ipp->attrs; attr; attr = attr->next)
+  {
+    if ((attr->group_tag == IPP_TAG_ZERO && indent <= 8) || !attr->name)
+    {
+      group = IPP_TAG_ZERO;
+      fputc('\n', stderr);
+      continue;
+    }
+
+    if (group != attr->group_tag)
+    {
+      group = attr->group_tag;
+
+      fprintf(stderr, "DEBUG: %*s%s:\n\n", indent - 4, "", tags[group]);
+    }
+
+    fprintf(stderr, "DEBUG: %*s%s (", indent, "", attr->name);
+    if (attr->num_values > 1)
+      fputs("1setOf ", stderr);
+    fprintf(stderr, "%s):", tags[attr->value_tag]);
+
+    switch (attr->value_tag)
+    {
+      case IPP_TAG_ENUM :
+      case IPP_TAG_INTEGER :
+          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
+           fprintf(stderr, " %d", val->integer);
+          fputc('\n', stderr);
+          break;
+
+      case IPP_TAG_BOOLEAN :
+          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
+           fprintf(stderr, " %s", val->boolean ? "true" : "false");
+          fputc('\n', stderr);
+          break;
+
+      case IPP_TAG_RANGE :
+          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
+           fprintf(stderr, " %d-%d", val->range.lower, val->range.upper);
+         fputc('\n', stderr);
+          break;
+
+      case IPP_TAG_DATE :
+          {
+           time_t      vtime;          /* Date/Time value */
+           struct tm   *vdate;         /* Date info */
+           char        vstring[256];   /* Formatted time */
+
+           for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
+           {
+             vtime = ippDateToTime(val->date);
+             vdate = localtime(&vtime);
+             strftime(vstring, sizeof(vstring), "%c", vdate);
+             fprintf(stderr, " (%s)", vstring);
+           }
+          }
+         fputc('\n', stderr);
+          break;
+
+      case IPP_TAG_RESOLUTION :
+          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
+           fprintf(stderr, " %dx%d%s", val->resolution.xres,
+                   val->resolution.yres,
+                   val->resolution.units == IPP_RES_PER_INCH ? "dpi" : "dpc");
+         fputc('\n', stderr);
+          break;
+
+      case IPP_TAG_STRING :
+      case IPP_TAG_TEXTLANG :
+      case IPP_TAG_NAMELANG :
+      case IPP_TAG_TEXT :
+      case IPP_TAG_NAME :
+      case IPP_TAG_KEYWORD :
+      case IPP_TAG_URI :
+      case IPP_TAG_URISCHEME :
+      case IPP_TAG_CHARSET :
+      case IPP_TAG_LANGUAGE :
+      case IPP_TAG_MIMETYPE :
+          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
+           fprintf(stderr, " \"%s\"", val->string.text);
+         fputc('\n', stderr);
+          break;
+
+      case IPP_TAG_BEGIN_COLLECTION :
+         fputc('\n', stderr);
+
+          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
+         {
+           if (i)
+             fputc('\n', stderr);
+           print_attributes(val->collection, indent + 4);
+         }
+          break;
+
+      default :
+          fprintf(stderr, "UNKNOWN (%d values)\n", attr->num_values);
+          break;
+    }
+  }
 }
 
 
 /*
- * End of "$Id: mailto.c 4829 2005-11-12 03:15:10Z mike $".
+ * End of "$Id: mailto.c 177 2006-06-21 00:20:03Z jlovell $".
  */