2 Copyright (C) 2001-2006 Kern Sibbald
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 version 2 as amended with additional clauses defined in the
7 file LICENSE in the main source directory.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 the file LICENSE for additional details.
16 Derived from a SMTPclient:
18 SMTPclient -- simple SMTP client
20 Copyright (C) 1997 Ralf S. Engelschall, All Rights Reserved.
24 Kern Sibbald, July 2001
32 #define MY_NAME "bsmtp"
34 #if defined(HAVE_WIN32)
39 int generate_daemon_event(JCR *jcr, const char *event)
49 static char *from_addr = NULL;
50 static char *cc_addr = NULL;
51 static char *subject = NULL;
52 static char *err_addr = NULL;
53 static const char *mailhost = NULL;
54 static char *reply_addr = NULL;
55 static int mailport = 25;
56 static char my_hostname[MAXSTRING];
60 * examine message from server
62 static void get_response(void)
66 Dmsg0(50, "Calling fgets on read socket rfp.\n");
68 while (fgets(buf, sizeof(buf), rfp)) {
69 int len = strlen(buf);
73 if (debug_level >= 10) {
74 fprintf(stderr, "%s <-- %s\n", mailhost, buf);
76 Dmsg2(10, "%s --> %s\n", mailhost, buf);
77 if (!isdigit((int)buf[0]) || buf[0] > '3') {
78 Pmsg2(0, _("Fatal malformed reply from %s: %s\n"), mailhost, buf);
86 fprintf(stderr, _("Fatal fgets error: ERR=%s\n"), strerror(errno));
92 * say something to server and check the response
94 static void chat(const char *fmt, ...)
99 vfprintf(sfp, fmt, ap);
100 if (debug_level >= 10) {
101 fprintf(stdout, "%s --> ", my_hostname);
102 vfprintf(stdout, fmt, ap);
107 if (debug_level >= 10) {
118 "Usage: %s [-f from] [-h mailhost] [-s subject] [-c copy] [recipient ...]\n"
119 " -c set the Cc: field\n"
120 " -dnn set debug level to nn\n"
121 " -f set the From: field\n"
122 " -h use mailhost:port as the SMTP server\n"
123 " -s set the Subject: field\n"
124 " -r set the Reply-To: field\n"
125 " -l set the maximum number of lines that should be sent (default: unlimited)\n"
126 " -? print this message.\n"
133 /*********************************************************************
135 * Program to send email
137 int main (int argc, char *argv[])
140 struct sockaddr_in sin;
143 unsigned long maxlines, lines;
144 #if defined(HAVE_WIN32)
151 time_t now = time(NULL);
154 setlocale(LC_ALL, "en_US");
155 bindtextdomain("bacula", LOCALEDIR);
156 textdomain("bacula");
158 my_name_is(argc, argv, "bsmtp");
161 while ((ch = getopt(argc, argv, "c:d:f:h:r:s:l:?")) != -1) {
164 Dmsg1(20, "cc=%s\n", optarg);
168 case 'd': /* set debug level */
169 debug_level = atoi(optarg);
170 if (debug_level <= 0) {
173 Dmsg1(20, "Debug level = %d\n", debug_level);
180 case 'h': /* smtp host */
181 Dmsg1(20, "host=%s\n", optarg);
182 p = strchr(optarg, ':');
190 case 's': /* subject */
191 Dmsg1(20, "subject=%s\n", optarg);
195 case 'r': /* reply address */
200 Dmsg1(20, "maxlines=%s\n", optarg);
201 maxlines = (unsigned long) atol(optarg);
214 Pmsg0(0, _("Fatal error: no recipient given.\n"));
219 #if defined(HAVE_WIN32)
220 _setmode(0, _O_BINARY);
224 * Determine SMTP server
226 if (mailhost == NULL) {
227 if ((cp = getenv("SMTPSERVER")) != NULL) {
230 mailhost = "localhost";
234 #if defined(HAVE_WIN32)
237 WSAStartup(MAKEWORD(2,2), &wsaData);
241 * Find out my own host name for HELO;
242 * if possible, get the fully qualified domain name
244 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
245 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
248 if ((hp = gethostbyname(my_hostname)) == NULL) {
249 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
253 strcpy(my_hostname, hp->h_name);
254 Dmsg1(20, "My hostname is: %s\n", my_hostname);
257 * Determine from address.
259 if (from_addr == NULL) {
260 #if defined(HAVE_WIN32)
261 DWORD dwSize = UNLEN + 1;
262 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
264 if (GetUserName(lpszBuffer, &dwSize)) {
265 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
267 sprintf(buf, "unknown-user@%s", my_hostname);
270 if ((pwd = getpwuid(getuid())) == 0) {
271 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
273 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
276 from_addr = bstrdup(buf);
278 Dmsg1(20, "From addr=%s\n", from_addr);
281 * Connect to smtp daemon on mailhost.
284 if ((hp = gethostbyname(mailhost)) == NULL) {
285 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
287 if (strcasecmp(mailhost, "localhost") != 0) {
288 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
289 mailhost = "localhost";
295 if (hp->h_addrtype != AF_INET) {
296 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
299 memset((char *)&sin, 0, sizeof(sin));
300 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
301 sin.sin_family = hp->h_addrtype;
302 sin.sin_port = htons(mailport);
303 #if defined(HAVE_WIN32)
304 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
305 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
309 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
310 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
314 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
315 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
318 Dmsg0(20, "Connected\n");
320 #if defined(HAVE_WIN32)
321 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
322 if (fdSocket == -1) {
323 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
327 int fdSocket2 = dup(fdSocket);
329 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
330 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
333 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
334 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
338 if ((r = dup(s)) < 0) {
339 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
342 if ((sfp = fdopen(s, "w")) == 0) {
343 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
346 if ((rfp = fdopen(r, "r")) == 0) {
347 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
355 get_response(); /* banner */
356 chat("helo %s\r\n", my_hostname);
357 chat("mail from:<%s>\r\n", from_addr);
359 for (i = 0; i < argc; i++) {
360 Dmsg1(20, "rcpt to: %s\n", argv[i]);
361 chat("rcpt to:<%s>\r\n", argv[i]);
365 chat("rcpt to:<%s>\r\n", cc_addr);
371 * Send message header
373 fprintf(sfp, "From: %s\r\n", from_addr);
374 Dmsg1(10, "From: %s\r\n", from_addr);
376 fprintf(sfp, "Subject: %s\r\n", subject);
377 Dmsg1(10, "Subject: %s\r\n", subject);
380 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
381 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
384 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
385 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
388 #if defined(HAVE_WIN32)
389 DWORD dwSize = UNLEN + 1;
390 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
392 if (GetUserName(lpszBuffer, &dwSize)) {
393 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
394 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
396 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
397 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
400 if ((pwd = getpwuid(getuid())) == 0) {
401 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
402 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
404 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
405 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
409 fprintf(sfp, "To: %s", argv[0]);
410 Dmsg1(10, "To: %s", argv[0]);
411 for (i = 1; i < argc; i++) {
412 fprintf(sfp, ",%s", argv[i]);
413 Dmsg1(10, ",%s", argv[i]);
416 fprintf(sfp, "\r\n");
419 fprintf(sfp, "Cc: %s\r\n", cc_addr);
420 Dmsg1(10, "Cc: %s\r\n", cc_addr);
423 /* Add RFC822 date */
424 (void)localtime_r(&now, &tm);
425 #if defined(HAVE_WIN32)
426 #if defined(HAVE_MINGW)
427 __MINGW_IMPORT long _dstbias;
433 tzoffset = _timezone;
434 tzoffset += _dstbias;
437 size_t length = strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", &tm);
438 sprintf(&buf[length], " %+2.2ld%2.2u", -tzoffset / 60, abs(tzoffset) % 60);
440 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", &tm);
442 fprintf(sfp, "Date: %s\r\n", buf);
443 Dmsg1(10, "Date: %s\r\n", buf);
445 fprintf(sfp, "\r\n");
451 while (fgets(buf, sizeof(buf), stdin)) {
452 if (maxlines > 0 && ++lines > maxlines) {
453 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
456 buf[sizeof(buf)-1] = '\0';
457 buf[strlen(buf)-1] = '\0';
458 if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
459 fputs("..\r\n", sfp);
460 } else { /* pass body through unchanged */
466 if (lines > maxlines) {
467 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
468 fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
472 * Send SMTP quit command
478 * Go away gracefully ...