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 plus additions
11 that are listed in the file LICENSE.
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;
75 * examine message from server
77 static void get_response(void)
81 Dmsg0(50, "Calling fgets on read socket rfp.\n");
83 while (fgets(buf, sizeof(buf), rfp)) {
84 int len = strlen(buf);
88 if (debug_level >= 10) {
89 fprintf(stderr, "%s <-- %s\n", mailhost, buf);
91 Dmsg2(10, "%s --> %s\n", mailhost, buf);
92 if (!isdigit((int)buf[0]) || buf[0] > '3') {
93 Pmsg2(0, _("Fatal malformed reply from %s: %s\n"), mailhost, buf);
101 fprintf(stderr, _("Fatal fgets error: ERR=%s\n"), strerror(errno));
107 * say something to server and check the response
109 static void chat(const char *fmt, ...)
114 vfprintf(sfp, fmt, ap);
115 if (debug_level >= 10) {
116 fprintf(stdout, "%s --> ", my_hostname);
117 vfprintf(stdout, fmt, ap);
122 if (debug_level >= 10) {
133 "Usage: %s [-f from] [-h mailhost] [-s subject] [-c copy] [recipient ...]\n"
134 " -8 set charset utf-8\n"
135 " -c set the Cc: field\n"
136 " -dnn set debug level to nn\n"
137 " -f set the From: field\n"
138 " -h use mailhost:port as the SMTP server\n"
139 " -s set the Subject: field\n"
140 " -r set the Reply-To: field\n"
141 " -l set the maximum number of lines that should be sent (default: unlimited)\n"
142 " -? print this message.\n"
148 static void get_date_string(char *buf, int buf_len)
150 time_t now = time(NULL);
152 char tzbuf[MAXSTRING];
154 /* Add RFC822 date */
155 (void)localtime_r(&now, &tm);
157 #if defined(HAVE_WIN32)
158 #if defined(HAVE_MINGW)
159 __MINGW_IMPORT long _dstbias;
165 tzoffset = _timezone;
166 tzoffset += _dstbias;
169 size_t length = strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
170 sprintf(&buf[length], " %+2.2ld%2.2u", -tzoffset / 60, abs(tzoffset) % 60);
172 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
174 timezone /= 60; /* timezone offset in mins */
175 if (tm.tm_isdst == 1) {
176 timezone -= 60; /* adjust for daylight savings */
178 sprintf(tzbuf, " %+2.2ld%2.2u", -timezone / 60, abs(timezone) % 60);
179 strcat(buf, tzbuf); /* add +0100 */
180 strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
181 strcat(buf, tzbuf); /* add (CEST) */
186 /*********************************************************************
188 * Program to send email
190 int main (int argc, char *argv[])
193 struct sockaddr_in sin;
196 unsigned long maxlines, lines;
197 #if defined(HAVE_WIN32)
205 setlocale(LC_ALL, "en_US");
206 bindtextdomain("bacula", LOCALEDIR);
207 textdomain("bacula");
209 my_name_is(argc, argv, "bsmtp");
212 while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
218 Dmsg1(20, "cc=%s\n", optarg);
222 case 'd': /* set debug level */
223 debug_level = atoi(optarg);
224 if (debug_level <= 0) {
227 Dmsg1(20, "Debug level = %d\n", debug_level);
234 case 'h': /* smtp host */
235 Dmsg1(20, "host=%s\n", optarg);
236 p = strchr(optarg, ':');
244 case 's': /* subject */
245 Dmsg1(20, "subject=%s\n", optarg);
249 case 'r': /* reply address */
254 Dmsg1(20, "maxlines=%s\n", optarg);
255 maxlines = (unsigned long) atol(optarg);
268 Pmsg0(0, _("Fatal error: no recipient given.\n"));
273 #if defined(HAVE_WIN32)
274 _setmode(0, _O_BINARY);
278 * Determine SMTP server
280 if (mailhost == NULL) {
281 if ((cp = getenv("SMTPSERVER")) != NULL) {
284 mailhost = "localhost";
288 #if defined(HAVE_WIN32)
291 WSAStartup(MAKEWORD(2,2), &wsaData);
295 * Find out my own host name for HELO;
296 * if possible, get the fully qualified domain name
298 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
299 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
302 if ((hp = gethostbyname(my_hostname)) == NULL) {
303 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
307 strcpy(my_hostname, hp->h_name);
308 Dmsg1(20, "My hostname is: %s\n", my_hostname);
311 * Determine from address.
313 if (from_addr == NULL) {
314 #if defined(HAVE_WIN32)
315 DWORD dwSize = UNLEN + 1;
316 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
318 if (GetUserName(lpszBuffer, &dwSize)) {
319 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
321 sprintf(buf, "unknown-user@%s", my_hostname);
324 if ((pwd = getpwuid(getuid())) == 0) {
325 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
327 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
330 from_addr = bstrdup(buf);
332 Dmsg1(20, "From addr=%s\n", from_addr);
335 * Connect to smtp daemon on mailhost.
338 if ((hp = gethostbyname(mailhost)) == NULL) {
339 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
341 if (strcasecmp(mailhost, "localhost") != 0) {
342 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
343 mailhost = "localhost";
349 if (hp->h_addrtype != AF_INET) {
350 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
353 memset((char *)&sin, 0, sizeof(sin));
354 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
355 sin.sin_family = hp->h_addrtype;
356 sin.sin_port = htons(mailport);
357 #if defined(HAVE_WIN32)
358 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
359 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
363 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
364 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
368 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
369 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
372 Dmsg0(20, "Connected\n");
374 #if defined(HAVE_WIN32)
375 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
376 if (fdSocket == -1) {
377 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
381 int fdSocket2 = dup(fdSocket);
383 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
384 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
387 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
388 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
392 if ((r = dup(s)) < 0) {
393 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
396 if ((sfp = fdopen(s, "w")) == 0) {
397 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
400 if ((rfp = fdopen(r, "r")) == 0) {
401 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
407 * Send SMTP headers. Note, if any of the strings have a <
408 * in them already, we do not enclose the string in < >, otherwise
411 get_response(); /* banner */
412 chat("helo %s\r\n", my_hostname);
413 if (strchr(from_addr, '<') == NULL) {
414 chat("mail from:<%s>\r\n", from_addr);
416 chat("mail from:%s\r\n", from_addr);
419 for (i = 0; i < argc; i++) {
420 Dmsg1(20, "rcpt to: %s\n", argv[i]);
421 if (strchr(argv[i], '<') == NULL) {
422 chat("rcpt to:<%s>\r\n", argv[i]);
424 chat("rcpt to:%s\r\n", argv[i]);
429 if (strchr(cc_addr, '<') == NULL) {
430 chat("rcpt to:<%s>\r\n", cc_addr);
432 chat("rcpt to:%s\r\n", cc_addr);
439 * Send message header
441 fprintf(sfp, "From: %s\r\n", from_addr);
442 Dmsg1(10, "From: %s\r\n", from_addr);
444 fprintf(sfp, "Subject: %s\r\n", subject);
445 Dmsg1(10, "Subject: %s\r\n", subject);
448 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
449 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
452 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
453 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
456 #if defined(HAVE_WIN32)
457 DWORD dwSize = UNLEN + 1;
458 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
460 if (GetUserName(lpszBuffer, &dwSize)) {
461 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
462 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
464 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
465 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
468 if ((pwd = getpwuid(getuid())) == 0) {
469 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
470 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
472 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
473 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
477 fprintf(sfp, "To: %s", argv[0]);
478 Dmsg1(10, "To: %s", argv[0]);
479 for (i = 1; i < argc; i++) {
480 fprintf(sfp, ",%s", argv[i]);
481 Dmsg1(10, ",%s", argv[i]);
484 fprintf(sfp, "\r\n");
487 fprintf(sfp, "Cc: %s\r\n", cc_addr);
488 Dmsg1(10, "Cc: %s\r\n", cc_addr);
492 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
493 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
496 get_date_string(buf, sizeof(buf));
497 fprintf(sfp, "Date: %s\r\n", buf);
498 Dmsg1(10, "Date: %s\r\n", buf);
500 fprintf(sfp, "\r\n");
506 while (fgets(buf, sizeof(buf), stdin)) {
507 if (maxlines > 0 && ++lines > maxlines) {
508 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
511 buf[sizeof(buf)-1] = '\0';
512 buf[strlen(buf)-1] = '\0';
513 if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
514 fputs("..\r\n", sfp);
515 } else { /* pass body through unchanged */
521 if (lines > maxlines) {
522 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
523 fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
527 * Send SMTP quit command
533 * Go away gracefully ...