2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 Derived from a SMTPclient:
31 ======== Original copyrights ==========
33 SMTPclient -- simple SMTP client
35 Copyright (c) 1997 Ralf S. Engelschall, All rights reserved.
37 This program is free software; it may be redistributed and/or modified
38 only under the terms of either the Artistic License or the GNU General
39 Public License, which may be found in the SMTP source distribution.
40 Look at the file COPYING.
42 This program is distributed in the hope that it will be useful, but
43 WITHOUT ANY WARRANTY; without even the implied warranty of
44 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45 GNU General Public License for more details.
47 ======================================================================
49 smtpclient_main.c -- program source
51 Based on smtp.c as of August 11, 1995 from
53 Eindhoven University of Technology,
54 Department of Mathematics and Computer Science,
55 Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands.
60 Kern Sibbald, July 2001
62 Note, the original W.Z. Venema smtp.c had no license and no
64 http://archives.neohapsis.com/archives/postfix/2000-05/1520.html
70 #define MY_NAME "bsmtp"
72 #if defined(HAVE_WIN32)
79 int generate_daemon_event(JCR *jcr, const char *event)
97 static char *from_addr = NULL;
98 static char *cc_addr = NULL;
99 static char *subject = NULL;
100 static char *err_addr = NULL;
101 static const char *mailhost = NULL;
102 static char *reply_addr = NULL;
103 static int mailport = 25;
104 static char my_hostname[MAXSTRING];
105 static bool content_utf8 = false;
106 static resolv_type default_resolv_type = RESOLV_PROTO_IPV4;
109 * Take input that may have names and other stuff and strip
110 * it down to the mail box address ... i.e. what is enclosed
111 * in < >. Otherwise add < >.
113 static char *cleanup_addr(char *addr, char *buf, int buf_len)
117 if ((p = strchr(addr, '<')) == NULL) {
118 snprintf(buf, buf_len, "<%s>", addr);
121 for (q=buf; *p && *p!='>'; ) {
129 Dmsg2(100, "cleanup in=%s out=%s\n", addr, buf);
134 * examine message from server
136 static void get_response(void)
140 Dmsg0(50, "Calling fgets on read socket rfp.\n");
142 while (fgets(buf, sizeof(buf), rfp)) {
143 int len = strlen(buf);
147 if (debug_level >= 10) {
148 fprintf(stderr, "%s <-- %s\n", mailhost, buf);
150 Dmsg2(10, "%s --> %s\n", mailhost, buf);
151 if (!isdigit((int)buf[0]) || buf[0] > '3') {
152 Pmsg2(0, _("Fatal malformed reply from %s: %s\n"), mailhost, buf);
160 fprintf(stderr, _("Fatal fgets error: ERR=%s\n"), strerror(errno));
166 * say something to server and check the response
168 static void chat(const char *fmt, ...)
173 vfprintf(sfp, fmt, ap);
175 if (debug_level >= 10) {
176 fprintf(stdout, "%s --> ", my_hostname);
178 vfprintf(stdout, fmt, ap);
183 if (debug_level >= 10) {
194 "Usage: %s [-f from] [-h mailhost] [-s subject] [-c copy] [recipient ...]\n"
195 " -4 forces bsmtp to use IPv4 addresses only.\n"
197 " -6 forces bsmtp to use IPv6 addresses only.\n"
199 " -8 set charset to UTF-8\n"
200 " -a use any ip protocol for address resolution\n"
201 " -c set the Cc: field\n"
202 " -d <nn> set debug level to <nn>\n"
203 " -dt print a timestamp in debug output\n"
204 " -f set the From: field\n"
205 " -h use mailhost:port as the SMTP server\n"
206 " -s set the Subject: field\n"
207 " -r set the Reply-To: field\n"
208 " -l set the maximum number of lines to send (default: unlimited)\n"
209 " -? print this message.\n"
216 * Return the offset west from localtime to UTC in minutes
217 * Same as timezone.tz_minuteswest
218 * Unix tz_offset coded by: Attila Fülöp
220 static long tz_offset(time_t lnow, struct tm &tm)
222 #if defined(HAVE_WIN32)
223 #if defined(HAVE_MINGW)
224 __MINGW_IMPORT long _dstbias;
227 # define _tzset tzset
239 /* Unix/Linux code */
243 (void)gmtime_r(&now, &tm_utc);
244 tm_utc.tm_isdst = tm.tm_isdst;
245 return (long)difftime(mktime(&tm_utc), now) / 60;
249 static void get_date_string(char *buf, int buf_len)
251 time_t now = time(NULL);
253 char tzbuf[MAXSTRING];
256 /* Add RFC822 date */
257 (void)localtime_r(&now, &tm);
259 my_timezone = tz_offset(now, tm);
260 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
261 snprintf(tzbuf, sizeof(tzbuf), " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60);
262 strcat(buf, tzbuf); /* add +0100 */
263 strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
264 strcat(buf, tzbuf); /* add (CEST) */
267 /*********************************************************************
269 * Program to send email
271 int main (int argc, char *argv[])
275 unsigned long maxlines, lines;
276 #if defined(HAVE_WIN32)
283 #ifdef HAVE_GETADDRINFO
285 struct addrinfo hints;
286 struct addrinfo *ai, *rp;
290 struct sockaddr_in sin;
293 const char *options = "468ac:d:f:h:r:s:l:?";
295 const char *options = "48ac:d:f:h:r:s:l:?";
298 setlocale(LC_ALL, "en_US");
299 bindtextdomain("bacula", LOCALEDIR);
300 textdomain("bacula");
302 my_name_is(argc, argv, "bsmtp");
305 while ((ch = getopt(argc, argv, options)) != -1) {
308 default_resolv_type = RESOLV_PROTO_IPV4;
313 default_resolv_type = RESOLV_PROTO_IPV6;
322 default_resolv_type = RESOLV_PROTO_ANY;
326 Dmsg1(20, "cc=%s\n", optarg);
330 case 'd': /* set debug level */
331 if (*optarg == 't') {
332 dbg_timestamp = true;
334 debug_level = atoi(optarg);
335 if (debug_level <= 0) {
339 Dmsg1(20, "Debug level = %d\n", debug_level);
346 case 'h': /* smtp host */
347 Dmsg1(20, "host=%s\n", optarg);
348 p = strchr(optarg, ':');
356 case 's': /* subject */
357 Dmsg1(20, "subject=%s\n", optarg);
361 case 'r': /* reply address */
366 Dmsg1(20, "maxlines=%s\n", optarg);
367 maxlines = (unsigned long) atol(optarg);
380 Pmsg0(0, _("Fatal error: no recipient given.\n"));
386 * Determine SMTP server
388 if (mailhost == NULL) {
389 if ((cp = getenv("SMTPSERVER")) != NULL) {
392 mailhost = "localhost";
396 #if defined(HAVE_WIN32)
399 _setmode(0, _O_BINARY);
400 WSAStartup(MAKEWORD(2,2), &wsaData);
404 * Find out my own host name for HELO;
405 * if possible, get the fully qualified domain name
407 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
408 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
411 #ifdef HAVE_GETADDRINFO
412 memset(&hints, 0, sizeof(struct addrinfo));
413 hints.ai_family = AF_UNSPEC;
414 hints.ai_socktype = 0;
415 hints.ai_protocol = 0;
416 hints.ai_flags = AI_CANONNAME;
418 if ((res = getaddrinfo(my_hostname, NULL, &hints, &ai)) != 0) {
419 Pmsg2(0, _("Fatal getaddrinfo for myself failed \"%s\": ERR=%s\n"),
420 my_hostname, gai_strerror(res));
423 strcpy(my_hostname, ai->ai_canonname);
426 if ((hp = gethostbyname(my_hostname)) == NULL) {
427 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"),
428 my_hostname, strerror(errno));
431 strcpy(my_hostname, hp->h_name);
433 Dmsg1(20, "My hostname is: %s\n", my_hostname);
436 * Determine from address.
438 if (from_addr == NULL) {
439 #if defined(HAVE_WIN32)
440 DWORD dwSize = UNLEN + 1;
441 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
443 if (GetUserName(lpszBuffer, &dwSize)) {
444 snprintf(buf, sizeof(buf), "%s@%s", lpszBuffer, my_hostname);
446 snprintf(buf, sizeof(buf), "unknown-user@%s", my_hostname);
449 if ((pwd = getpwuid(getuid())) == 0) {
450 snprintf(buf, sizeof(buf), "userid-%d@%s", (int)getuid(), my_hostname);
452 snprintf(buf, sizeof(buf), "%s@%s", pwd->pw_name, my_hostname);
455 from_addr = bstrdup(buf);
457 Dmsg1(20, "From addr=%s\n", from_addr);
460 * Connect to smtp daemon on mailhost.
463 #ifdef HAVE_GETADDRINFO
464 memset(&hints, 0, sizeof(struct addrinfo));
465 switch (default_resolv_type) {
466 case RESOLV_PROTO_ANY:
467 hints.ai_family = AF_UNSPEC;
469 case RESOLV_PROTO_IPV4:
470 hints.ai_family = AF_INET;
473 case RESOLV_PROTO_IPV6:
474 hints.ai_family = AF_INET6;
478 hints.ai_family = AF_UNSPEC;
481 hints.ai_socktype = SOCK_STREAM;
482 hints.ai_protocol = 0;
484 snprintf(mail_port, sizeof(mail_port), "%d", mailport);
486 if ((res = getaddrinfo(mailhost, mail_port, &hints, &ai)) != 0) {
487 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"),
488 mailhost, gai_strerror(res));
489 if (strcasecmp(mailhost, "localhost")) {
490 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
491 mailhost = "localhost";
497 for (rp = ai; rp != NULL; rp = rp->ai_next) {
498 #if defined(HAVE_WIN32)
499 s = WSASocket(rp->ai_family, rp->ai_socktype, rp->ai_protocol, NULL, 0, 0);
501 s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
507 if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) {
515 Pmsg1(0, _("Failed to connect to mailhost %s\n"), mailhost);
521 if ((hp = gethostbyname(mailhost)) == NULL) {
522 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
524 if (strcasecmp(mailhost, "localhost") != 0) {
525 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
526 mailhost = "localhost";
532 if (hp->h_addrtype != AF_INET) {
533 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
536 memset((char *)&sin, 0, sizeof(sin));
537 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
538 sin.sin_family = hp->h_addrtype;
539 sin.sin_port = htons(mailport);
540 #if defined(HAVE_WIN32)
541 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
542 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
546 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
547 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
551 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
552 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
555 Dmsg0(20, "Connected\n");
558 #if defined(HAVE_WIN32)
559 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
560 if (fdSocket == -1) {
561 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
565 int fdSocket2 = dup(fdSocket);
567 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
568 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
571 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
572 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
576 if ((r = dup(s)) < 0) {
577 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
580 if ((sfp = fdopen(s, "w")) == 0) {
581 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
584 if ((rfp = fdopen(r, "r")) == 0) {
585 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
591 * Send SMTP headers. Note, if any of the strings have a <
592 * in them already, we do not enclose the string in < >, otherwise
595 get_response(); /* banner */
596 chat("HELO %s\r\n", my_hostname);
597 chat("MAIL FROM:%s\r\n", cleanup_addr(from_addr, buf, sizeof(buf)));
599 for (i = 0; i < argc; i++) {
600 Dmsg1(20, "rcpt to: %s\n", argv[i]);
601 chat("RCPT TO:%s\r\n", cleanup_addr(argv[i], buf, sizeof(buf)));
605 chat("RCPT TO:%s\r\n", cleanup_addr(cc_addr, buf, sizeof(buf)));
611 * Send message header
613 fprintf(sfp, "From: %s\r\n", from_addr);
614 Dmsg1(10, "From: %s\r\n", from_addr);
616 fprintf(sfp, "Subject: %s\r\n", subject);
617 Dmsg1(10, "Subject: %s\r\n", subject);
620 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
621 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
624 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
625 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
628 #if defined(HAVE_WIN32)
629 DWORD dwSize = UNLEN + 1;
630 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
632 if (GetUserName(lpszBuffer, &dwSize)) {
633 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
634 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
636 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
637 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
640 if ((pwd = getpwuid(getuid())) == 0) {
641 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
642 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
644 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
645 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
649 fprintf(sfp, "To: %s", argv[0]);
650 Dmsg1(10, "To: %s", argv[0]);
651 for (i = 1; i < argc; i++) {
652 fprintf(sfp, ",%s", argv[i]);
653 Dmsg1(10, ",%s", argv[i]);
656 fprintf(sfp, "\r\n");
659 fprintf(sfp, "Cc: %s\r\n", cc_addr);
660 Dmsg1(10, "Cc: %s\r\n", cc_addr);
664 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
665 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
668 get_date_string(buf, sizeof(buf));
669 fprintf(sfp, "Date: %s\r\n", buf);
670 Dmsg1(10, "Date: %s\r\n", buf);
672 fprintf(sfp, "\r\n");
678 while (fgets(buf, sizeof(buf), stdin)) {
679 if (maxlines > 0 && ++lines > maxlines) {
680 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
681 while (fgets(buf, sizeof(buf), stdin)) {
686 buf[sizeof(buf)-1] = '\0';
687 buf[strlen(buf)-1] = '\0';
688 if (buf[0] == '.') { /* add extra . see RFC 2821 4.5.2 */
695 if (lines > maxlines) {
696 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
697 fprintf(sfp, "\r\n\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
701 * Send SMTP quit command
707 * Go away gracefully ...