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];
155 /* Add RFC822 date */
156 (void)localtime_r(&now, &tm);
158 #if defined(HAVE_WIN32)
159 #if defined(HAVE_MINGW)
160 __MINGW_IMPORT long _dstbias;
163 my_timezone = _timezone;
164 my_timezone += _dstbias;
169 my_timezone = timezone / 60; /* timezone offset in mins */
170 if (tm.tm_isdst == 1) {
171 my_timezone -= 60; /* adjust for daylight savings */
174 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
175 sprintf(tzbuf, " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60);
176 strcat(buf, tzbuf); /* add +0100 */
177 strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
178 strcat(buf, tzbuf); /* add (CEST) */
182 /*********************************************************************
184 * Program to send email
186 int main (int argc, char *argv[])
189 struct sockaddr_in sin;
192 unsigned long maxlines, lines;
193 #if defined(HAVE_WIN32)
201 setlocale(LC_ALL, "en_US");
202 bindtextdomain("bacula", LOCALEDIR);
203 textdomain("bacula");
205 my_name_is(argc, argv, "bsmtp");
208 while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
214 Dmsg1(20, "cc=%s\n", optarg);
218 case 'd': /* set debug level */
219 debug_level = atoi(optarg);
220 if (debug_level <= 0) {
223 Dmsg1(20, "Debug level = %d\n", debug_level);
230 case 'h': /* smtp host */
231 Dmsg1(20, "host=%s\n", optarg);
232 p = strchr(optarg, ':');
240 case 's': /* subject */
241 Dmsg1(20, "subject=%s\n", optarg);
245 case 'r': /* reply address */
250 Dmsg1(20, "maxlines=%s\n", optarg);
251 maxlines = (unsigned long) atol(optarg);
264 Pmsg0(0, _("Fatal error: no recipient given.\n"));
269 #if defined(HAVE_WIN32)
270 _setmode(0, _O_BINARY);
274 * Determine SMTP server
276 if (mailhost == NULL) {
277 if ((cp = getenv("SMTPSERVER")) != NULL) {
280 mailhost = "localhost";
284 #if defined(HAVE_WIN32)
287 WSAStartup(MAKEWORD(2,2), &wsaData);
291 * Find out my own host name for HELO;
292 * if possible, get the fully qualified domain name
294 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
295 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
298 if ((hp = gethostbyname(my_hostname)) == NULL) {
299 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
303 strcpy(my_hostname, hp->h_name);
304 Dmsg1(20, "My hostname is: %s\n", my_hostname);
307 * Determine from address.
309 if (from_addr == NULL) {
310 #if defined(HAVE_WIN32)
311 DWORD dwSize = UNLEN + 1;
312 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
314 if (GetUserName(lpszBuffer, &dwSize)) {
315 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
317 sprintf(buf, "unknown-user@%s", my_hostname);
320 if ((pwd = getpwuid(getuid())) == 0) {
321 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
323 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
326 from_addr = bstrdup(buf);
328 Dmsg1(20, "From addr=%s\n", from_addr);
331 * Connect to smtp daemon on mailhost.
334 if ((hp = gethostbyname(mailhost)) == NULL) {
335 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
337 if (strcasecmp(mailhost, "localhost") != 0) {
338 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
339 mailhost = "localhost";
345 if (hp->h_addrtype != AF_INET) {
346 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
349 memset((char *)&sin, 0, sizeof(sin));
350 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
351 sin.sin_family = hp->h_addrtype;
352 sin.sin_port = htons(mailport);
353 #if defined(HAVE_WIN32)
354 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
355 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
359 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
360 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
364 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
365 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
368 Dmsg0(20, "Connected\n");
370 #if defined(HAVE_WIN32)
371 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
372 if (fdSocket == -1) {
373 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
377 int fdSocket2 = dup(fdSocket);
379 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
380 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
383 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
384 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
388 if ((r = dup(s)) < 0) {
389 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
392 if ((sfp = fdopen(s, "w")) == 0) {
393 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
396 if ((rfp = fdopen(r, "r")) == 0) {
397 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
403 * Send SMTP headers. Note, if any of the strings have a <
404 * in them already, we do not enclose the string in < >, otherwise
407 get_response(); /* banner */
408 chat("helo %s\r\n", my_hostname);
409 if (strchr(from_addr, '<') == NULL) {
410 chat("mail from:<%s>\r\n", from_addr);
412 chat("mail from:%s\r\n", from_addr);
415 for (i = 0; i < argc; i++) {
416 Dmsg1(20, "rcpt to: %s\n", argv[i]);
417 if (strchr(argv[i], '<') == NULL) {
418 chat("rcpt to:<%s>\r\n", argv[i]);
420 chat("rcpt to:%s\r\n", argv[i]);
425 if (strchr(cc_addr, '<') == NULL) {
426 chat("rcpt to:<%s>\r\n", cc_addr);
428 chat("rcpt to:%s\r\n", cc_addr);
435 * Send message header
437 fprintf(sfp, "From: %s\r\n", from_addr);
438 Dmsg1(10, "From: %s\r\n", from_addr);
440 fprintf(sfp, "Subject: %s\r\n", subject);
441 Dmsg1(10, "Subject: %s\r\n", subject);
444 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
445 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
448 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
449 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
452 #if defined(HAVE_WIN32)
453 DWORD dwSize = UNLEN + 1;
454 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
456 if (GetUserName(lpszBuffer, &dwSize)) {
457 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
458 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
460 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
461 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
464 if ((pwd = getpwuid(getuid())) == 0) {
465 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
466 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
468 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
469 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
473 fprintf(sfp, "To: %s", argv[0]);
474 Dmsg1(10, "To: %s", argv[0]);
475 for (i = 1; i < argc; i++) {
476 fprintf(sfp, ",%s", argv[i]);
477 Dmsg1(10, ",%s", argv[i]);
480 fprintf(sfp, "\r\n");
483 fprintf(sfp, "Cc: %s\r\n", cc_addr);
484 Dmsg1(10, "Cc: %s\r\n", cc_addr);
488 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
489 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
492 get_date_string(buf, sizeof(buf));
493 fprintf(sfp, "Date: %s\r\n", buf);
494 Dmsg1(10, "Date: %s\r\n", buf);
496 fprintf(sfp, "\r\n");
502 while (fgets(buf, sizeof(buf), stdin)) {
503 if (maxlines > 0 && ++lines > maxlines) {
504 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
507 buf[sizeof(buf)-1] = '\0';
508 buf[strlen(buf)-1] = '\0';
509 if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
510 fputs("..\r\n", sfp);
511 } else { /* pass body through unchanged */
517 if (lines > maxlines) {
518 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
519 fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
523 * Send SMTP quit command
529 * Go away gracefully ...