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"));
294 * Determine SMTP server
296 if (mailhost == NULL) {
297 if ((cp = getenv("SMTPSERVER")) != NULL) {
300 mailhost = "localhost";
304 #if defined(HAVE_WIN32)
307 _setmode(0, _O_BINARY);
308 WSAStartup(MAKEWORD(2,2), &wsaData);
312 * Find out my own host name for HELO;
313 * if possible, get the fully qualified domain name
315 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
316 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
319 if ((hp = gethostbyname(my_hostname)) == NULL) {
320 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
324 strcpy(my_hostname, hp->h_name);
325 Dmsg1(20, "My hostname is: %s\n", my_hostname);
328 * Determine from address.
330 if (from_addr == NULL) {
331 #if defined(HAVE_WIN32)
332 DWORD dwSize = UNLEN + 1;
333 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
335 if (GetUserName(lpszBuffer, &dwSize)) {
336 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
338 sprintf(buf, "unknown-user@%s", my_hostname);
341 if ((pwd = getpwuid(getuid())) == 0) {
342 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
344 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
347 from_addr = bstrdup(buf);
349 Dmsg1(20, "From addr=%s\n", from_addr);
352 * Connect to smtp daemon on mailhost.
355 if ((hp = gethostbyname(mailhost)) == NULL) {
356 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
358 if (strcasecmp(mailhost, "localhost") != 0) {
359 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
360 mailhost = "localhost";
366 if (hp->h_addrtype != AF_INET) {
367 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
370 memset((char *)&sin, 0, sizeof(sin));
371 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
372 sin.sin_family = hp->h_addrtype;
373 sin.sin_port = htons(mailport);
374 #if defined(HAVE_WIN32)
375 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
376 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
380 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
381 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
385 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
386 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
389 Dmsg0(20, "Connected\n");
391 #if defined(HAVE_WIN32)
392 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
393 if (fdSocket == -1) {
394 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
398 int fdSocket2 = dup(fdSocket);
400 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
401 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
404 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
405 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
409 if ((r = dup(s)) < 0) {
410 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
413 if ((sfp = fdopen(s, "w")) == 0) {
414 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
417 if ((rfp = fdopen(r, "r")) == 0) {
418 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
424 * Send SMTP headers. Note, if any of the strings have a <
425 * in them already, we do not enclose the string in < >, otherwise
428 get_response(); /* banner */
429 chat("helo %s\r\n", my_hostname);
430 chat("mail from:%s\r\n", cleanup_addr(from_addr, buf, sizeof(buf)));
432 for (i = 0; i < argc; i++) {
433 Dmsg1(20, "rcpt to: %s\n", argv[i]);
434 chat("rcpt to:%s\r\n", cleanup_addr(argv[i], buf, sizeof(buf)));
438 chat("rcpt to:%s\r\n", cleanup_addr(cc_addr, buf, sizeof(buf)));
444 * Send message header
446 fprintf(sfp, "From: %s\r\n", from_addr);
447 Dmsg1(10, "From: %s\r\n", from_addr);
449 fprintf(sfp, "Subject: %s\r\n", subject);
450 Dmsg1(10, "Subject: %s\r\n", subject);
453 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
454 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
457 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
458 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
461 #if defined(HAVE_WIN32)
462 DWORD dwSize = UNLEN + 1;
463 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
465 if (GetUserName(lpszBuffer, &dwSize)) {
466 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
467 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
469 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
470 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
473 if ((pwd = getpwuid(getuid())) == 0) {
474 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
475 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
477 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
478 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
482 fprintf(sfp, "To: %s", argv[0]);
483 Dmsg1(10, "To: %s", argv[0]);
484 for (i = 1; i < argc; i++) {
485 fprintf(sfp, ",%s", argv[i]);
486 Dmsg1(10, ",%s", argv[i]);
489 fprintf(sfp, "\r\n");
492 fprintf(sfp, "Cc: %s\r\n", cc_addr);
493 Dmsg1(10, "Cc: %s\r\n", cc_addr);
497 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
498 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
501 get_date_string(buf, sizeof(buf));
502 fprintf(sfp, "Date: %s\r\n", buf);
503 Dmsg1(10, "Date: %s\r\n", buf);
505 fprintf(sfp, "\r\n");
511 while (fgets(buf, sizeof(buf), stdin)) {
512 if (maxlines > 0 && ++lines > maxlines) {
513 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
516 buf[sizeof(buf)-1] = '\0';
517 buf[strlen(buf)-1] = '\0';
518 if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
519 fputs("..\r\n", sfp);
520 } else { /* pass body through unchanged */
526 if (lines > maxlines) {
527 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
528 fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
532 * Send SMTP quit command
538 * Go away gracefully ...