1/* 2 * "$Id: mailto.c 11093 2013-07-03 20:48:42Z msweet $" 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 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); 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{ 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 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 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", 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 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{ 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 ippAttributeString(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 11093 2013-07-03 20:48:42Z msweet $". 646 */ 647