2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2007 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 two of the GNU 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 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 John Walker.
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 SMTPclient -- simple SMTP client
33 Copyright (C) 1997 Ralf S. Engelschall, All Rights Reserved.
37 Kern Sibbald, July 2001
46 #define MY_NAME "bsmtp"
48 #if defined(HAVE_WIN32)
53 int generate_daemon_event(JCR *jcr, const char *event)
63 static char *from_addr = NULL;
64 static char *cc_addr = NULL;
65 static char *subject = NULL;
66 static char *err_addr = NULL;
67 static const char *mailhost = NULL;
68 static char *reply_addr = NULL;
69 static int mailport = 25;
70 static char my_hostname[MAXSTRING];
71 static bool content_utf8 = false;
74 * Take input that may have names and other stuff and strip
75 * it down to the mail box address ... i.e. what is enclosed
76 * in < >. Otherwise add < >.
78 static char *cleanup_addr(char *addr, char *buf, int buf_len)
82 if ((p = strchr(from_addr, '<')) == NULL) {
83 snprintf(buf, buf_len, "<%s>", addr);
86 for (q=buf; *p && *p!='>'; ) {
94 Dmsg2(100, "cleanup in=%s out=%s\n", addr, buf);
99 * examine message from server
101 static void get_response(void)
105 Dmsg0(50, "Calling fgets on read socket rfp.\n");
107 while (fgets(buf, sizeof(buf), rfp)) {
108 int len = strlen(buf);
112 if (debug_level >= 10) {
113 fprintf(stderr, "%s <-- %s\n", mailhost, buf);
115 Dmsg2(10, "%s --> %s\n", mailhost, buf);
116 if (!isdigit((int)buf[0]) || buf[0] > '3') {
117 Pmsg2(0, _("Fatal malformed reply from %s: %s\n"), mailhost, buf);
125 fprintf(stderr, _("Fatal fgets error: ERR=%s\n"), strerror(errno));
131 * say something to server and check the response
133 static void chat(const char *fmt, ...)
138 vfprintf(sfp, fmt, ap);
140 if (debug_level >= 10) {
141 fprintf(stdout, "%s --> ", my_hostname);
143 vfprintf(stdout, fmt, ap);
148 if (debug_level >= 10) {
159 "Usage: %s [-f from] [-h mailhost] [-s subject] [-c copy] [recipient ...]\n"
160 " -8 set charset utf-8\n"
161 " -c set the Cc: field\n"
162 " -dnn set debug level to nn\n"
163 " -f set the From: field\n"
164 " -h use mailhost:port as the SMTP server\n"
165 " -s set the Subject: field\n"
166 " -r set the Reply-To: field\n"
167 " -l set the maximum number of lines that should be sent (default: unlimited)\n"
168 " -? print this message.\n"
175 * Return the offset west from localtime to UTC in minutes
176 * Same as timezone.tz_minuteswest
177 * Unix tz_offset coded by: Attila Fülöp
180 static long tz_offset(time_t lnow, struct tm &tm)
182 #if defined(HAVE_WIN32)
183 #if defined(HAVE_MINGW)
184 __MINGW_IMPORT long _dstbias;
195 /* Unix/Linux code */
199 (void)gmtime_r(&now, &tm_utc);
200 tm_utc.tm_isdst = tm.tm_isdst;
201 return (long)difftime(mktime(&tm_utc), now) / 60;
205 static void get_date_string(char *buf, int buf_len)
207 time_t now = time(NULL);
209 char tzbuf[MAXSTRING];
212 /* Add RFC822 date */
213 (void)localtime_r(&now, &tm);
215 my_timezone = tz_offset(now, tm);
216 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
217 sprintf(tzbuf, " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60);
218 strcat(buf, tzbuf); /* add +0100 */
219 strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
220 strcat(buf, tzbuf); /* add (CEST) */
224 /*********************************************************************
226 * Program to send email
228 int main (int argc, char *argv[])
231 struct sockaddr_in sin;
234 unsigned long maxlines, lines;
235 #if defined(HAVE_WIN32)
243 setlocale(LC_ALL, "en_US");
244 bindtextdomain("bacula", LOCALEDIR);
245 textdomain("bacula");
247 my_name_is(argc, argv, "bsmtp");
250 while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
256 Dmsg1(20, "cc=%s\n", optarg);
260 case 'd': /* set debug level */
261 debug_level = atoi(optarg);
262 if (debug_level <= 0) {
265 Dmsg1(20, "Debug level = %d\n", debug_level);
272 case 'h': /* smtp host */
273 Dmsg1(20, "host=%s\n", optarg);
274 p = strchr(optarg, ':');
282 case 's': /* subject */
283 Dmsg1(20, "subject=%s\n", optarg);
287 case 'r': /* reply address */
292 Dmsg1(20, "maxlines=%s\n", optarg);
293 maxlines = (unsigned long) atol(optarg);
306 Pmsg0(0, _("Fatal error: no recipient given.\n"));
313 * Determine SMTP server
315 if (mailhost == NULL) {
316 if ((cp = getenv("SMTPSERVER")) != NULL) {
319 mailhost = "localhost";
323 #if defined(HAVE_WIN32)
326 _setmode(0, _O_BINARY);
327 WSAStartup(MAKEWORD(2,2), &wsaData);
331 * Find out my own host name for HELO;
332 * if possible, get the fully qualified domain name
334 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
335 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
338 if ((hp = gethostbyname(my_hostname)) == NULL) {
339 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
343 strcpy(my_hostname, hp->h_name);
344 Dmsg1(20, "My hostname is: %s\n", my_hostname);
347 * Determine from address.
349 if (from_addr == NULL) {
350 #if defined(HAVE_WIN32)
351 DWORD dwSize = UNLEN + 1;
352 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
354 if (GetUserName(lpszBuffer, &dwSize)) {
355 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
357 sprintf(buf, "unknown-user@%s", my_hostname);
360 if ((pwd = getpwuid(getuid())) == 0) {
361 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
363 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
366 from_addr = bstrdup(buf);
368 Dmsg1(20, "From addr=%s\n", from_addr);
371 * Connect to smtp daemon on mailhost.
374 if ((hp = gethostbyname(mailhost)) == NULL) {
375 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
377 if (strcasecmp(mailhost, "localhost") != 0) {
378 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
379 mailhost = "localhost";
385 if (hp->h_addrtype != AF_INET) {
386 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
389 memset((char *)&sin, 0, sizeof(sin));
390 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
391 sin.sin_family = hp->h_addrtype;
392 sin.sin_port = htons(mailport);
393 #if defined(HAVE_WIN32)
394 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
395 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
399 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
400 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
404 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
405 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
408 Dmsg0(20, "Connected\n");
410 #if defined(HAVE_WIN32)
411 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
412 if (fdSocket == -1) {
413 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
417 int fdSocket2 = dup(fdSocket);
419 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
420 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
423 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
424 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
428 if ((r = dup(s)) < 0) {
429 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
432 if ((sfp = fdopen(s, "w")) == 0) {
433 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
436 if ((rfp = fdopen(r, "r")) == 0) {
437 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
443 * Send SMTP headers. Note, if any of the strings have a <
444 * in them already, we do not enclose the string in < >, otherwise
447 get_response(); /* banner */
448 chat("helo %s\r\n", my_hostname);
449 chat("mail from:%s\r\n", cleanup_addr(from_addr, buf, sizeof(buf)));
451 for (i = 0; i < argc; i++) {
452 Dmsg1(20, "rcpt to: %s\n", argv[i]);
453 chat("rcpt to:%s\r\n", cleanup_addr(argv[i], buf, sizeof(buf)));
457 chat("rcpt to:%s\r\n", cleanup_addr(cc_addr, buf, sizeof(buf)));
463 * Send message header
465 fprintf(sfp, "From: %s\r\n", from_addr);
466 Dmsg1(10, "From: %s\r\n", from_addr);
468 fprintf(sfp, "Subject: %s\r\n", subject);
469 Dmsg1(10, "Subject: %s\r\n", subject);
472 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
473 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
476 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
477 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
480 #if defined(HAVE_WIN32)
481 DWORD dwSize = UNLEN + 1;
482 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
484 if (GetUserName(lpszBuffer, &dwSize)) {
485 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
486 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
488 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
489 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
492 if ((pwd = getpwuid(getuid())) == 0) {
493 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
494 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
496 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
497 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
501 fprintf(sfp, "To: %s", argv[0]);
502 Dmsg1(10, "To: %s", argv[0]);
503 for (i = 1; i < argc; i++) {
504 fprintf(sfp, ",%s", argv[i]);
505 Dmsg1(10, ",%s", argv[i]);
508 fprintf(sfp, "\r\n");
511 fprintf(sfp, "Cc: %s\r\n", cc_addr);
512 Dmsg1(10, "Cc: %s\r\n", cc_addr);
516 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
517 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
520 get_date_string(buf, sizeof(buf));
521 fprintf(sfp, "Date: %s\r\n", buf);
522 Dmsg1(10, "Date: %s\r\n", buf);
524 fprintf(sfp, "\r\n");
530 while (fgets(buf, sizeof(buf), stdin)) {
531 if (maxlines > 0 && ++lines > maxlines) {
532 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
535 buf[sizeof(buf)-1] = '\0';
536 buf[strlen(buf)-1] = '\0';
537 if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
538 fputs("..\r\n", sfp);
539 } else { /* pass body through unchanged */
545 if (lines > maxlines) {
546 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
547 fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
551 * Send SMTP quit command
557 * Go away gracefully ...