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);
139 if (debug_level >= 10) {
140 fprintf(stdout, "%s --> ", my_hostname);
141 vfprintf(stdout, fmt, ap);
146 if (debug_level >= 10) {
157 "Usage: %s [-f from] [-h mailhost] [-s subject] [-c copy] [recipient ...]\n"
158 " -8 set charset utf-8\n"
159 " -c set the Cc: field\n"
160 " -dnn set debug level to nn\n"
161 " -f set the From: field\n"
162 " -h use mailhost:port as the SMTP server\n"
163 " -s set the Subject: field\n"
164 " -r set the Reply-To: field\n"
165 " -l set the maximum number of lines that should be sent (default: unlimited)\n"
166 " -? print this message.\n"
172 static void get_date_string(char *buf, int buf_len)
174 time_t now = time(NULL);
176 char tzbuf[MAXSTRING];
179 /* Add RFC822 date */
180 (void)localtime_r(&now, &tm);
182 #if defined(HAVE_WIN32)
183 #if defined(HAVE_MINGW)
184 __MINGW_IMPORT long _dstbias;
187 my_timezone = _timezone;
188 my_timezone += _dstbias;
194 gettimeofday(&tv, &tz);
195 my_timezone = tz.tz_minuteswest; /* timezone offset in mins */
197 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
198 sprintf(tzbuf, " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60);
199 strcat(buf, tzbuf); /* add +0100 */
200 strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
201 strcat(buf, tzbuf); /* add (CEST) */
205 /*********************************************************************
207 * Program to send email
209 int main (int argc, char *argv[])
212 struct sockaddr_in sin;
215 unsigned long maxlines, lines;
216 #if defined(HAVE_WIN32)
224 setlocale(LC_ALL, "en_US");
225 bindtextdomain("bacula", LOCALEDIR);
226 textdomain("bacula");
228 my_name_is(argc, argv, "bsmtp");
231 while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
237 Dmsg1(20, "cc=%s\n", optarg);
241 case 'd': /* set debug level */
242 debug_level = atoi(optarg);
243 if (debug_level <= 0) {
246 Dmsg1(20, "Debug level = %d\n", debug_level);
253 case 'h': /* smtp host */
254 Dmsg1(20, "host=%s\n", optarg);
255 p = strchr(optarg, ':');
263 case 's': /* subject */
264 Dmsg1(20, "subject=%s\n", optarg);
268 case 'r': /* reply address */
273 Dmsg1(20, "maxlines=%s\n", optarg);
274 maxlines = (unsigned long) atol(optarg);
287 Pmsg0(0, _("Fatal error: no recipient given.\n"));
292 #if defined(HAVE_WIN32)
293 _setmode(0, _O_BINARY);
297 * Determine SMTP server
299 if (mailhost == NULL) {
300 if ((cp = getenv("SMTPSERVER")) != NULL) {
303 mailhost = "localhost";
307 #if defined(HAVE_WIN32)
310 WSAStartup(MAKEWORD(2,2), &wsaData);
314 * Find out my own host name for HELO;
315 * if possible, get the fully qualified domain name
317 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
318 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
321 if ((hp = gethostbyname(my_hostname)) == NULL) {
322 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
326 strcpy(my_hostname, hp->h_name);
327 Dmsg1(20, "My hostname is: %s\n", my_hostname);
330 * Determine from address.
332 if (from_addr == NULL) {
333 #if defined(HAVE_WIN32)
334 DWORD dwSize = UNLEN + 1;
335 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
337 if (GetUserName(lpszBuffer, &dwSize)) {
338 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
340 sprintf(buf, "unknown-user@%s", my_hostname);
343 if ((pwd = getpwuid(getuid())) == 0) {
344 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
346 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
349 from_addr = bstrdup(buf);
351 Dmsg1(20, "From addr=%s\n", from_addr);
354 * Connect to smtp daemon on mailhost.
357 if ((hp = gethostbyname(mailhost)) == NULL) {
358 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
360 if (strcasecmp(mailhost, "localhost") != 0) {
361 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
362 mailhost = "localhost";
368 if (hp->h_addrtype != AF_INET) {
369 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
372 memset((char *)&sin, 0, sizeof(sin));
373 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
374 sin.sin_family = hp->h_addrtype;
375 sin.sin_port = htons(mailport);
376 #if defined(HAVE_WIN32)
377 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
378 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
382 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
383 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
387 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
388 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
391 Dmsg0(20, "Connected\n");
393 #if defined(HAVE_WIN32)
394 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
395 if (fdSocket == -1) {
396 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
400 int fdSocket2 = dup(fdSocket);
402 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
403 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
406 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
407 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
411 if ((r = dup(s)) < 0) {
412 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
415 if ((sfp = fdopen(s, "w")) == 0) {
416 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
419 if ((rfp = fdopen(r, "r")) == 0) {
420 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
426 * Send SMTP headers. Note, if any of the strings have a <
427 * in them already, we do not enclose the string in < >, otherwise
430 get_response(); /* banner */
431 chat("helo %s\r\n", my_hostname);
432 chat("mail from:%s\r\n", cleanup_addr(from_addr, buf, sizeof(buf)));
434 for (i = 0; i < argc; i++) {
435 Dmsg1(20, "rcpt to: %s\n", argv[i]);
436 chat("rcpt to:%s\r\n", cleanup_addr(argv[i], buf, sizeof(buf)));
440 chat("rcpt to:%s\r\n", cleanup_addr(cc_addr, buf, sizeof(buf)));
446 * Send message header
448 fprintf(sfp, "From: %s\r\n", from_addr);
449 Dmsg1(10, "From: %s\r\n", from_addr);
451 fprintf(sfp, "Subject: %s\r\n", subject);
452 Dmsg1(10, "Subject: %s\r\n", subject);
455 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
456 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
459 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
460 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
463 #if defined(HAVE_WIN32)
464 DWORD dwSize = UNLEN + 1;
465 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
467 if (GetUserName(lpszBuffer, &dwSize)) {
468 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
469 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
471 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
472 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
475 if ((pwd = getpwuid(getuid())) == 0) {
476 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
477 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
479 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
480 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
484 fprintf(sfp, "To: %s", argv[0]);
485 Dmsg1(10, "To: %s", argv[0]);
486 for (i = 1; i < argc; i++) {
487 fprintf(sfp, ",%s", argv[i]);
488 Dmsg1(10, ",%s", argv[i]);
491 fprintf(sfp, "\r\n");
494 fprintf(sfp, "Cc: %s\r\n", cc_addr);
495 Dmsg1(10, "Cc: %s\r\n", cc_addr);
499 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
500 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
503 get_date_string(buf, sizeof(buf));
504 fprintf(sfp, "Date: %s\r\n", buf);
505 Dmsg1(10, "Date: %s\r\n", buf);
507 fprintf(sfp, "\r\n");
513 while (fgets(buf, sizeof(buf), stdin)) {
514 if (maxlines > 0 && ++lines > maxlines) {
515 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
518 buf[sizeof(buf)-1] = '\0';
519 buf[strlen(buf)-1] = '\0';
520 if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
521 fputs("..\r\n", sfp);
522 } else { /* pass body through unchanged */
528 if (lines > maxlines) {
529 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
530 fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
534 * Send SMTP quit command
540 * Go away gracefully ...