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];
157 /* Add RFC822 date */
158 (void)localtime_r(&now, &tm);
160 #if defined(HAVE_WIN32)
161 #if defined(HAVE_MINGW)
162 __MINGW_IMPORT long _dstbias;
165 my_timezone = _timezone;
166 my_timezone += _dstbias;
170 gettimeofday(&tv, &tz);
171 my_timezone = tz.tz_minuteswest; /* timezone offset in mins */
173 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
174 sprintf(tzbuf, " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60);
175 strcat(buf, tzbuf); /* add +0100 */
176 strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
177 strcat(buf, tzbuf); /* add (CEST) */
181 /*********************************************************************
183 * Program to send email
185 int main (int argc, char *argv[])
188 struct sockaddr_in sin;
191 unsigned long maxlines, lines;
192 #if defined(HAVE_WIN32)
200 setlocale(LC_ALL, "en_US");
201 bindtextdomain("bacula", LOCALEDIR);
202 textdomain("bacula");
204 my_name_is(argc, argv, "bsmtp");
207 while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
213 Dmsg1(20, "cc=%s\n", optarg);
217 case 'd': /* set debug level */
218 debug_level = atoi(optarg);
219 if (debug_level <= 0) {
222 Dmsg1(20, "Debug level = %d\n", debug_level);
229 case 'h': /* smtp host */
230 Dmsg1(20, "host=%s\n", optarg);
231 p = strchr(optarg, ':');
239 case 's': /* subject */
240 Dmsg1(20, "subject=%s\n", optarg);
244 case 'r': /* reply address */
249 Dmsg1(20, "maxlines=%s\n", optarg);
250 maxlines = (unsigned long) atol(optarg);
263 Pmsg0(0, _("Fatal error: no recipient given.\n"));
268 #if defined(HAVE_WIN32)
269 _setmode(0, _O_BINARY);
273 * Determine SMTP server
275 if (mailhost == NULL) {
276 if ((cp = getenv("SMTPSERVER")) != NULL) {
279 mailhost = "localhost";
283 #if defined(HAVE_WIN32)
286 WSAStartup(MAKEWORD(2,2), &wsaData);
290 * Find out my own host name for HELO;
291 * if possible, get the fully qualified domain name
293 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
294 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
297 if ((hp = gethostbyname(my_hostname)) == NULL) {
298 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
302 strcpy(my_hostname, hp->h_name);
303 Dmsg1(20, "My hostname is: %s\n", my_hostname);
306 * Determine from address.
308 if (from_addr == NULL) {
309 #if defined(HAVE_WIN32)
310 DWORD dwSize = UNLEN + 1;
311 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
313 if (GetUserName(lpszBuffer, &dwSize)) {
314 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
316 sprintf(buf, "unknown-user@%s", my_hostname);
319 if ((pwd = getpwuid(getuid())) == 0) {
320 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
322 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
325 from_addr = bstrdup(buf);
327 Dmsg1(20, "From addr=%s\n", from_addr);
330 * Connect to smtp daemon on mailhost.
333 if ((hp = gethostbyname(mailhost)) == NULL) {
334 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
336 if (strcasecmp(mailhost, "localhost") != 0) {
337 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
338 mailhost = "localhost";
344 if (hp->h_addrtype != AF_INET) {
345 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
348 memset((char *)&sin, 0, sizeof(sin));
349 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
350 sin.sin_family = hp->h_addrtype;
351 sin.sin_port = htons(mailport);
352 #if defined(HAVE_WIN32)
353 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
354 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
358 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
359 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
363 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
364 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
367 Dmsg0(20, "Connected\n");
369 #if defined(HAVE_WIN32)
370 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
371 if (fdSocket == -1) {
372 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
376 int fdSocket2 = dup(fdSocket);
378 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
379 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
382 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
383 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
387 if ((r = dup(s)) < 0) {
388 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
391 if ((sfp = fdopen(s, "w")) == 0) {
392 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
395 if ((rfp = fdopen(r, "r")) == 0) {
396 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
402 * Send SMTP headers. Note, if any of the strings have a <
403 * in them already, we do not enclose the string in < >, otherwise
406 get_response(); /* banner */
407 chat("helo %s\r\n", my_hostname);
408 if (strchr(from_addr, '<') == NULL) {
409 chat("mail from:<%s>\r\n", from_addr);
411 chat("mail from:%s\r\n", from_addr);
414 for (i = 0; i < argc; i++) {
415 Dmsg1(20, "rcpt to: %s\n", argv[i]);
416 if (strchr(argv[i], '<') == NULL) {
417 chat("rcpt to:<%s>\r\n", argv[i]);
419 chat("rcpt to:%s\r\n", argv[i]);
424 if (strchr(cc_addr, '<') == NULL) {
425 chat("rcpt to:<%s>\r\n", cc_addr);
427 chat("rcpt to:%s\r\n", cc_addr);
434 * Send message header
436 fprintf(sfp, "From: %s\r\n", from_addr);
437 Dmsg1(10, "From: %s\r\n", from_addr);
439 fprintf(sfp, "Subject: %s\r\n", subject);
440 Dmsg1(10, "Subject: %s\r\n", subject);
443 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
444 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
447 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
448 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
451 #if defined(HAVE_WIN32)
452 DWORD dwSize = UNLEN + 1;
453 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
455 if (GetUserName(lpszBuffer, &dwSize)) {
456 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
457 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
459 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
460 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
463 if ((pwd = getpwuid(getuid())) == 0) {
464 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
465 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
467 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
468 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
472 fprintf(sfp, "To: %s", argv[0]);
473 Dmsg1(10, "To: %s", argv[0]);
474 for (i = 1; i < argc; i++) {
475 fprintf(sfp, ",%s", argv[i]);
476 Dmsg1(10, ",%s", argv[i]);
479 fprintf(sfp, "\r\n");
482 fprintf(sfp, "Cc: %s\r\n", cc_addr);
483 Dmsg1(10, "Cc: %s\r\n", cc_addr);
487 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
488 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
491 get_date_string(buf, sizeof(buf));
492 fprintf(sfp, "Date: %s\r\n", buf);
493 Dmsg1(10, "Date: %s\r\n", buf);
495 fprintf(sfp, "\r\n");
501 while (fgets(buf, sizeof(buf), stdin)) {
502 if (maxlines > 0 && ++lines > maxlines) {
503 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
506 buf[sizeof(buf)-1] = '\0';
507 buf[strlen(buf)-1] = '\0';
508 if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
509 fputs("..\r\n", sfp);
510 } else { /* pass body through unchanged */
516 if (lines > maxlines) {
517 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
518 fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
522 * Send SMTP quit command
528 * Go away gracefully ...