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"
149 /*********************************************************************
151 * Program to send email
153 int main (int argc, char *argv[])
156 struct sockaddr_in sin;
159 unsigned long maxlines, lines;
160 #if defined(HAVE_WIN32)
167 time_t now = time(NULL);
170 setlocale(LC_ALL, "en_US");
171 bindtextdomain("bacula", LOCALEDIR);
172 textdomain("bacula");
174 my_name_is(argc, argv, "bsmtp");
177 while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
183 Dmsg1(20, "cc=%s\n", optarg);
187 case 'd': /* set debug level */
188 debug_level = atoi(optarg);
189 if (debug_level <= 0) {
192 Dmsg1(20, "Debug level = %d\n", debug_level);
199 case 'h': /* smtp host */
200 Dmsg1(20, "host=%s\n", optarg);
201 p = strchr(optarg, ':');
209 case 's': /* subject */
210 Dmsg1(20, "subject=%s\n", optarg);
214 case 'r': /* reply address */
219 Dmsg1(20, "maxlines=%s\n", optarg);
220 maxlines = (unsigned long) atol(optarg);
233 Pmsg0(0, _("Fatal error: no recipient given.\n"));
238 #if defined(HAVE_WIN32)
239 _setmode(0, _O_BINARY);
243 * Determine SMTP server
245 if (mailhost == NULL) {
246 if ((cp = getenv("SMTPSERVER")) != NULL) {
249 mailhost = "localhost";
253 #if defined(HAVE_WIN32)
256 WSAStartup(MAKEWORD(2,2), &wsaData);
260 * Find out my own host name for HELO;
261 * if possible, get the fully qualified domain name
263 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
264 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
267 if ((hp = gethostbyname(my_hostname)) == NULL) {
268 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
272 strcpy(my_hostname, hp->h_name);
273 Dmsg1(20, "My hostname is: %s\n", my_hostname);
276 * Determine from address.
278 if (from_addr == NULL) {
279 #if defined(HAVE_WIN32)
280 DWORD dwSize = UNLEN + 1;
281 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
283 if (GetUserName(lpszBuffer, &dwSize)) {
284 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
286 sprintf(buf, "unknown-user@%s", my_hostname);
289 if ((pwd = getpwuid(getuid())) == 0) {
290 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
292 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
295 from_addr = bstrdup(buf);
297 Dmsg1(20, "From addr=%s\n", from_addr);
300 * Connect to smtp daemon on mailhost.
303 if ((hp = gethostbyname(mailhost)) == NULL) {
304 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
306 if (strcasecmp(mailhost, "localhost") != 0) {
307 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
308 mailhost = "localhost";
314 if (hp->h_addrtype != AF_INET) {
315 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
318 memset((char *)&sin, 0, sizeof(sin));
319 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
320 sin.sin_family = hp->h_addrtype;
321 sin.sin_port = htons(mailport);
322 #if defined(HAVE_WIN32)
323 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
324 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
328 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
329 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
333 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
334 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
337 Dmsg0(20, "Connected\n");
339 #if defined(HAVE_WIN32)
340 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
341 if (fdSocket == -1) {
342 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
346 int fdSocket2 = dup(fdSocket);
348 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
349 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
352 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
353 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
357 if ((r = dup(s)) < 0) {
358 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
361 if ((sfp = fdopen(s, "w")) == 0) {
362 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
365 if ((rfp = fdopen(r, "r")) == 0) {
366 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
372 * Send SMTP headers. Note, if any of the strings have a <
373 * in them already, we do not enclose the string in < >, otherwise
376 get_response(); /* banner */
377 chat("helo %s\r\n", my_hostname);
378 if (strchr(from_addr, '<') == NULL) {
379 chat("mail from:<%s>\r\n", from_addr);
381 chat("mail from:%s\r\n", from_addr);
384 for (i = 0; i < argc; i++) {
385 Dmsg1(20, "rcpt to: %s\n", argv[i]);
386 if (strchr(from_addr, '<') == NULL) {
387 chat("rcpt to:<%s>\r\n", argv[i]);
389 chat("rcpt to:%s\r\n", argv[i]);
394 if (strchr(from_addr, '<') == NULL) {
395 chat("rcpt to:<%s>\r\n", cc_addr);
397 chat("rcpt to:%s\r\n", cc_addr);
404 * Send message header
406 fprintf(sfp, "From: %s\r\n", from_addr);
407 Dmsg1(10, "From: %s\r\n", from_addr);
409 fprintf(sfp, "Subject: %s\r\n", subject);
410 Dmsg1(10, "Subject: %s\r\n", subject);
413 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
414 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
417 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
418 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
421 #if defined(HAVE_WIN32)
422 DWORD dwSize = UNLEN + 1;
423 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
425 if (GetUserName(lpszBuffer, &dwSize)) {
426 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
427 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
429 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
430 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
433 if ((pwd = getpwuid(getuid())) == 0) {
434 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
435 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
437 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
438 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
442 fprintf(sfp, "To: %s", argv[0]);
443 Dmsg1(10, "To: %s", argv[0]);
444 for (i = 1; i < argc; i++) {
445 fprintf(sfp, ",%s", argv[i]);
446 Dmsg1(10, ",%s", argv[i]);
449 fprintf(sfp, "\r\n");
452 fprintf(sfp, "Cc: %s\r\n", cc_addr);
453 Dmsg1(10, "Cc: %s\r\n", cc_addr);
457 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
458 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
461 /* Add RFC822 date */
462 (void)localtime_r(&now, &tm);
463 #if defined(HAVE_WIN32)
464 #if defined(HAVE_MINGW)
465 __MINGW_IMPORT long _dstbias;
471 tzoffset = _timezone;
472 tzoffset += _dstbias;
475 size_t length = strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", &tm);
476 sprintf(&buf[length], " %+2.2ld%2.2u", -tzoffset / 60, abs(tzoffset) % 60);
478 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", &tm);
480 fprintf(sfp, "Date: %s\r\n", buf);
481 Dmsg1(10, "Date: %s\r\n", buf);
483 fprintf(sfp, "\r\n");
489 while (fgets(buf, sizeof(buf), stdin)) {
490 if (maxlines > 0 && ++lines > maxlines) {
491 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
494 buf[sizeof(buf)-1] = '\0';
495 buf[strlen(buf)-1] = '\0';
496 if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
497 fputs("..\r\n", sfp);
498 } else { /* pass body through unchanged */
504 if (lines > maxlines) {
505 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
506 fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
510 * Send SMTP quit command
516 * Go away gracefully ...