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)
91 static char *from_addr = NULL;
92 static char *cc_addr = NULL;
93 static char *subject = NULL;
94 static char *err_addr = NULL;
95 static const char *mailhost = NULL;
96 static char *reply_addr = NULL;
97 static int mailport = 25;
98 static char my_hostname[MAXSTRING];
99 static bool content_utf8 = false;
102 * Take input that may have names and other stuff and strip
103 * it down to the mail box address ... i.e. what is enclosed
104 * in < >. Otherwise add < >.
106 static char *cleanup_addr(char *addr, char *buf, int buf_len)
110 if ((p = strchr(addr, '<')) == NULL) {
111 snprintf(buf, buf_len, "<%s>", addr);
114 for (q=buf; *p && *p!='>'; ) {
122 Dmsg2(100, "cleanup in=%s out=%s\n", addr, buf);
127 * examine message from server
129 static void get_response(void)
133 Dmsg0(50, "Calling fgets on read socket rfp.\n");
135 while (fgets(buf, sizeof(buf), rfp)) {
136 int len = strlen(buf);
140 if (debug_level >= 10) {
141 fprintf(stderr, "%s <-- %s\n", mailhost, buf);
143 Dmsg2(10, "%s --> %s\n", mailhost, buf);
144 if (!isdigit((int)buf[0]) || buf[0] > '3') {
145 Pmsg2(0, _("Fatal malformed reply from %s: %s\n"), mailhost, buf);
153 fprintf(stderr, _("Fatal fgets error: ERR=%s\n"), strerror(errno));
159 * say something to server and check the response
161 static void chat(const char *fmt, ...)
166 vfprintf(sfp, fmt, ap);
168 if (debug_level >= 10) {
169 fprintf(stdout, "%s --> ", my_hostname);
171 vfprintf(stdout, fmt, ap);
176 if (debug_level >= 10) {
187 "Usage: %s [-f from] [-h mailhost] [-s subject] [-c copy] [recipient ...]\n"
188 " -8 set charset to UTF-8\n"
189 " -c set the Cc: field\n"
190 " -d <nn> set debug level to <nn>\n"
191 " -dt print a timestamp in debug output\n"
192 " -f set the From: field\n"
193 " -h use mailhost:port as the SMTP server\n"
194 " -s set the Subject: field\n"
195 " -r set the Reply-To: field\n"
196 " -l set the maximum number of lines to send (default: unlimited)\n"
197 " -? print this message.\n"
204 * Return the offset west from localtime to UTC in minutes
205 * Same as timezone.tz_minuteswest
206 * Unix tz_offset coded by: Attila Fülöp
208 static long tz_offset(time_t lnow, struct tm &tm)
210 #if defined(HAVE_WIN32)
211 #if defined(HAVE_MINGW)
212 __MINGW_IMPORT long _dstbias;
215 # define _tzset tzset
227 /* Unix/Linux code */
231 (void)gmtime_r(&now, &tm_utc);
232 tm_utc.tm_isdst = tm.tm_isdst;
233 return (long)difftime(mktime(&tm_utc), now) / 60;
237 static void get_date_string(char *buf, int buf_len)
239 time_t now = time(NULL);
241 char tzbuf[MAXSTRING];
244 /* Add RFC822 date */
245 (void)localtime_r(&now, &tm);
247 my_timezone = tz_offset(now, tm);
248 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
249 snprintf(tzbuf, sizeof(tzbuf), " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60);
250 strcat(buf, tzbuf); /* add +0100 */
251 strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
252 strcat(buf, tzbuf); /* add (CEST) */
255 /*********************************************************************
257 * Program to send email
259 int main (int argc, char *argv[])
263 unsigned long maxlines, lines;
264 #if defined(HAVE_WIN32)
271 #ifdef HAVE_GETADDRINFO
273 struct addrinfo hints;
274 struct addrinfo *ai, *rp;
278 struct sockaddr_in sin;
281 setlocale(LC_ALL, "en_US");
282 bindtextdomain("bacula", LOCALEDIR);
283 textdomain("bacula");
285 my_name_is(argc, argv, "bsmtp");
288 while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
294 Dmsg1(20, "cc=%s\n", optarg);
298 case 'd': /* set debug level */
299 if (*optarg == 't') {
300 dbg_timestamp = true;
302 debug_level = atoi(optarg);
303 if (debug_level <= 0) {
307 Dmsg1(20, "Debug level = %d\n", debug_level);
314 case 'h': /* smtp host */
315 Dmsg1(20, "host=%s\n", optarg);
316 p = strchr(optarg, ':');
324 case 's': /* subject */
325 Dmsg1(20, "subject=%s\n", optarg);
329 case 'r': /* reply address */
334 Dmsg1(20, "maxlines=%s\n", optarg);
335 maxlines = (unsigned long) atol(optarg);
348 Pmsg0(0, _("Fatal error: no recipient given.\n"));
354 * Determine SMTP server
356 if (mailhost == NULL) {
357 if ((cp = getenv("SMTPSERVER")) != NULL) {
360 mailhost = "localhost";
364 #if defined(HAVE_WIN32)
367 _setmode(0, _O_BINARY);
368 WSAStartup(MAKEWORD(2,2), &wsaData);
372 * Find out my own host name for HELO;
373 * if possible, get the fully qualified domain name
375 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
376 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
379 #ifdef HAVE_GETADDRINFO
380 memset(&hints, 0, sizeof(struct addrinfo));
381 hints.ai_family = AF_UNSPEC;
382 hints.ai_socktype = 0;
383 hints.ai_protocol = 0;
384 hints.ai_flags = AI_CANONNAME;
386 if ((res = getaddrinfo(my_hostname, NULL, &hints, &ai)) != 0) {
387 Pmsg2(0, _("Fatal getaddrinfo for myself failed \"%s\": ERR=%s\n"),
388 my_hostname, gai_strerror(res));
391 strcpy(my_hostname, ai->ai_canonname);
394 if ((hp = gethostbyname(my_hostname)) == NULL) {
395 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"),
396 my_hostname, strerror(errno));
399 strcpy(my_hostname, hp->h_name);
401 Dmsg1(20, "My hostname is: %s\n", my_hostname);
404 * Determine from address.
406 if (from_addr == NULL) {
407 #if defined(HAVE_WIN32)
408 DWORD dwSize = UNLEN + 1;
409 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
411 if (GetUserName(lpszBuffer, &dwSize)) {
412 snprintf(buf, sizeof(buf), "%s@%s", lpszBuffer, my_hostname);
414 snprintf(buf, sizeof(buf), "unknown-user@%s", my_hostname);
417 if ((pwd = getpwuid(getuid())) == 0) {
418 snprintf(buf, sizeof(buf), "userid-%d@%s", (int)getuid(), my_hostname);
420 snprintf(buf, sizeof(buf), "%s@%s", pwd->pw_name, my_hostname);
423 from_addr = bstrdup(buf);
425 Dmsg1(20, "From addr=%s\n", from_addr);
428 * Connect to smtp daemon on mailhost.
431 #ifdef HAVE_GETADDRINFO
432 memset(&hints, 0, sizeof(struct addrinfo));
433 hints.ai_family = AF_UNSPEC;
434 hints.ai_socktype = SOCK_STREAM;
435 hints.ai_protocol = IPPROTO_TCP;
437 snprintf(mail_port, sizeof(mail_port), "%d", mailport);
439 if ((res = getaddrinfo(mailhost, mail_port, &hints, &ai)) != 0) {
440 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"),
441 mailhost, gai_strerror(res));
442 if (!strcasecmp(mailhost, "localhost")) {
443 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
444 mailhost = "localhost";
450 for (rp = ai; rp != NULL; rp = rp->ai_next) {
451 #if defined(HAVE_WIN32)
452 s = WSASocket(rp->ai_family, rp->ai_socktype, rp->ai_protocol, NULL, 0, 0);
454 s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
460 if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) {
468 Pmsg1(0, _("Failed to connect to mailhost %s\n"), mailhost);
474 if ((hp = gethostbyname(mailhost)) == NULL) {
475 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
477 if (strcasecmp(mailhost, "localhost") != 0) {
478 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
479 mailhost = "localhost";
485 if (hp->h_addrtype != AF_INET) {
486 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
489 memset((char *)&sin, 0, sizeof(sin));
490 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
491 sin.sin_family = hp->h_addrtype;
492 sin.sin_port = htons(mailport);
493 #if defined(HAVE_WIN32)
494 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
495 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
499 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
500 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
504 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
505 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
508 Dmsg0(20, "Connected\n");
511 #if defined(HAVE_WIN32)
512 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
513 if (fdSocket == -1) {
514 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
518 int fdSocket2 = dup(fdSocket);
520 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
521 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
524 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
525 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
529 if ((r = dup(s)) < 0) {
530 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
533 if ((sfp = fdopen(s, "w")) == 0) {
534 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
537 if ((rfp = fdopen(r, "r")) == 0) {
538 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
544 * Send SMTP headers. Note, if any of the strings have a <
545 * in them already, we do not enclose the string in < >, otherwise
548 get_response(); /* banner */
549 chat("HELO %s\r\n", my_hostname);
550 chat("MAIL FROM:%s\r\n", cleanup_addr(from_addr, buf, sizeof(buf)));
552 for (i = 0; i < argc; i++) {
553 Dmsg1(20, "rcpt to: %s\n", argv[i]);
554 chat("RCPT TO:%s\r\n", cleanup_addr(argv[i], buf, sizeof(buf)));
558 chat("RCPT TO:%s\r\n", cleanup_addr(cc_addr, buf, sizeof(buf)));
564 * Send message header
566 fprintf(sfp, "From: %s\r\n", from_addr);
567 Dmsg1(10, "From: %s\r\n", from_addr);
569 fprintf(sfp, "Subject: %s\r\n", subject);
570 Dmsg1(10, "Subject: %s\r\n", subject);
573 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
574 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
577 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
578 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
581 #if defined(HAVE_WIN32)
582 DWORD dwSize = UNLEN + 1;
583 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
585 if (GetUserName(lpszBuffer, &dwSize)) {
586 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
587 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
589 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
590 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
593 if ((pwd = getpwuid(getuid())) == 0) {
594 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
595 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
597 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
598 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
602 fprintf(sfp, "To: %s", argv[0]);
603 Dmsg1(10, "To: %s", argv[0]);
604 for (i = 1; i < argc; i++) {
605 fprintf(sfp, ",%s", argv[i]);
606 Dmsg1(10, ",%s", argv[i]);
609 fprintf(sfp, "\r\n");
612 fprintf(sfp, "Cc: %s\r\n", cc_addr);
613 Dmsg1(10, "Cc: %s\r\n", cc_addr);
617 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
618 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
621 get_date_string(buf, sizeof(buf));
622 fprintf(sfp, "Date: %s\r\n", buf);
623 Dmsg1(10, "Date: %s\r\n", buf);
625 fprintf(sfp, "\r\n");
631 while (fgets(buf, sizeof(buf), stdin)) {
632 if (maxlines > 0 && ++lines > maxlines) {
633 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
634 while (fgets(buf, sizeof(buf), stdin)) {
639 buf[sizeof(buf)-1] = '\0';
640 buf[strlen(buf)-1] = '\0';
641 if (buf[0] == '.') { /* add extra . see RFC 2821 4.5.2 */
648 if (lines > maxlines) {
649 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
650 fprintf(sfp, "\r\n\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
654 * Send SMTP quit command
660 * Go away gracefully ...