2 Derived from a SMTPclient:
4 SMTPclient -- simple SMTP client
6 Copyright (C) 1997 Ralf S. Engelschall, All Rights Reserved.
10 Kern Sibbald, July 2001
16 Bacula® - The Network Backup Solution
18 Copyright (C) 2001-2006 Free Software Foundation Europe e.V.
20 The main author of Bacula is Kern Sibbald, with contributions from
21 many others, a complete list can be found in the file AUTHORS.
22 This program is Free Software; you can redistribute it and/or
23 modify it under the terms of version two of the GNU General Public
24 License as published by the Free Software Foundation plus additions
25 that are listed in the file LICENSE.
27 This program is distributed in the hope that it will be useful, but
28 WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 General Public License for more details.
32 You should have received a copy of the GNU General Public License
33 along with this program; if not, write to the Free Software
34 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
37 Bacula® is a registered trademark of John Walker.
38 The licensor of Bacula is the Free Software Foundation Europe
39 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
40 Switzerland, email:ftf@fsfeurope.org.
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];
74 * examine message from server
76 static void get_response(void)
80 Dmsg0(50, "Calling fgets on read socket rfp.\n");
82 while (fgets(buf, sizeof(buf), rfp)) {
83 int len = strlen(buf);
87 if (debug_level >= 10) {
88 fprintf(stderr, "%s <-- %s\n", mailhost, buf);
90 Dmsg2(10, "%s --> %s\n", mailhost, buf);
91 if (!isdigit((int)buf[0]) || buf[0] > '3') {
92 Pmsg2(0, _("Fatal malformed reply from %s: %s\n"), mailhost, buf);
100 fprintf(stderr, _("Fatal fgets error: ERR=%s\n"), strerror(errno));
106 * say something to server and check the response
108 static void chat(const char *fmt, ...)
113 vfprintf(sfp, fmt, ap);
114 if (debug_level >= 10) {
115 fprintf(stdout, "%s --> ", my_hostname);
116 vfprintf(stdout, fmt, ap);
121 if (debug_level >= 10) {
132 "Usage: %s [-f from] [-h mailhost] [-s subject] [-c copy] [recipient ...]\n"
133 " -c set the Cc: field\n"
134 " -dnn set debug level to nn\n"
135 " -f set the From: field\n"
136 " -h use mailhost:port as the SMTP server\n"
137 " -s set the Subject: field\n"
138 " -r set the Reply-To: field\n"
139 " -l set the maximum number of lines that should be sent (default: unlimited)\n"
140 " -? print this message.\n"
147 /*********************************************************************
149 * Program to send email
151 int main (int argc, char *argv[])
154 struct sockaddr_in sin;
157 unsigned long maxlines, lines;
158 #if defined(HAVE_WIN32)
165 time_t now = time(NULL);
168 setlocale(LC_ALL, "en_US");
169 bindtextdomain("bacula", LOCALEDIR);
170 textdomain("bacula");
172 my_name_is(argc, argv, "bsmtp");
175 while ((ch = getopt(argc, argv, "c:d:f:h:r:s:l:?")) != -1) {
178 Dmsg1(20, "cc=%s\n", optarg);
182 case 'd': /* set debug level */
183 debug_level = atoi(optarg);
184 if (debug_level <= 0) {
187 Dmsg1(20, "Debug level = %d\n", debug_level);
194 case 'h': /* smtp host */
195 Dmsg1(20, "host=%s\n", optarg);
196 p = strchr(optarg, ':');
204 case 's': /* subject */
205 Dmsg1(20, "subject=%s\n", optarg);
209 case 'r': /* reply address */
214 Dmsg1(20, "maxlines=%s\n", optarg);
215 maxlines = (unsigned long) atol(optarg);
228 Pmsg0(0, _("Fatal error: no recipient given.\n"));
233 #if defined(HAVE_WIN32)
234 _setmode(0, _O_BINARY);
238 * Determine SMTP server
240 if (mailhost == NULL) {
241 if ((cp = getenv("SMTPSERVER")) != NULL) {
244 mailhost = "localhost";
248 #if defined(HAVE_WIN32)
251 WSAStartup(MAKEWORD(2,2), &wsaData);
255 * Find out my own host name for HELO;
256 * if possible, get the fully qualified domain name
258 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
259 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
262 if ((hp = gethostbyname(my_hostname)) == NULL) {
263 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
267 strcpy(my_hostname, hp->h_name);
268 Dmsg1(20, "My hostname is: %s\n", my_hostname);
271 * Determine from address.
273 if (from_addr == NULL) {
274 #if defined(HAVE_WIN32)
275 DWORD dwSize = UNLEN + 1;
276 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
278 if (GetUserName(lpszBuffer, &dwSize)) {
279 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
281 sprintf(buf, "unknown-user@%s", my_hostname);
284 if ((pwd = getpwuid(getuid())) == 0) {
285 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
287 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
290 from_addr = bstrdup(buf);
292 Dmsg1(20, "From addr=%s\n", from_addr);
295 * Connect to smtp daemon on mailhost.
298 if ((hp = gethostbyname(mailhost)) == NULL) {
299 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
301 if (strcasecmp(mailhost, "localhost") != 0) {
302 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
303 mailhost = "localhost";
309 if (hp->h_addrtype != AF_INET) {
310 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
313 memset((char *)&sin, 0, sizeof(sin));
314 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
315 sin.sin_family = hp->h_addrtype;
316 sin.sin_port = htons(mailport);
317 #if defined(HAVE_WIN32)
318 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
319 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
323 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
324 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
328 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
329 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
332 Dmsg0(20, "Connected\n");
334 #if defined(HAVE_WIN32)
335 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
336 if (fdSocket == -1) {
337 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
341 int fdSocket2 = dup(fdSocket);
343 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
344 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
347 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
348 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
352 if ((r = dup(s)) < 0) {
353 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
356 if ((sfp = fdopen(s, "w")) == 0) {
357 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
360 if ((rfp = fdopen(r, "r")) == 0) {
361 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
369 get_response(); /* banner */
370 chat("helo %s\r\n", my_hostname);
371 chat("mail from:<%s>\r\n", from_addr);
373 for (i = 0; i < argc; i++) {
374 Dmsg1(20, "rcpt to: %s\n", argv[i]);
375 chat("rcpt to:<%s>\r\n", argv[i]);
379 chat("rcpt to:<%s>\r\n", cc_addr);
385 * Send message header
387 fprintf(sfp, "From: %s\r\n", from_addr);
388 Dmsg1(10, "From: %s\r\n", from_addr);
390 fprintf(sfp, "Subject: %s\r\n", subject);
391 Dmsg1(10, "Subject: %s\r\n", subject);
394 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
395 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
398 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
399 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
402 #if defined(HAVE_WIN32)
403 DWORD dwSize = UNLEN + 1;
404 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
406 if (GetUserName(lpszBuffer, &dwSize)) {
407 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
408 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
410 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
411 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
414 if ((pwd = getpwuid(getuid())) == 0) {
415 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
416 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
418 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
419 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
423 fprintf(sfp, "To: %s", argv[0]);
424 Dmsg1(10, "To: %s", argv[0]);
425 for (i = 1; i < argc; i++) {
426 fprintf(sfp, ",%s", argv[i]);
427 Dmsg1(10, ",%s", argv[i]);
430 fprintf(sfp, "\r\n");
433 fprintf(sfp, "Cc: %s\r\n", cc_addr);
434 Dmsg1(10, "Cc: %s\r\n", cc_addr);
437 /* Add RFC822 date */
438 (void)localtime_r(&now, &tm);
439 #if defined(HAVE_WIN32)
440 #if defined(HAVE_MINGW)
441 __MINGW_IMPORT long _dstbias;
447 tzoffset = _timezone;
448 tzoffset += _dstbias;
451 size_t length = strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", &tm);
452 sprintf(&buf[length], " %+2.2ld%2.2u", -tzoffset / 60, abs(tzoffset) % 60);
454 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", &tm);
456 fprintf(sfp, "Date: %s\r\n", buf);
457 Dmsg1(10, "Date: %s\r\n", buf);
459 fprintf(sfp, "\r\n");
465 while (fgets(buf, sizeof(buf), stdin)) {
466 if (maxlines > 0 && ++lines > maxlines) {
467 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
470 buf[sizeof(buf)-1] = '\0';
471 buf[strlen(buf)-1] = '\0';
472 if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
473 fputs("..\r\n", sfp);
474 } else { /* pass body through unchanged */
480 if (lines > maxlines) {
481 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
482 fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
486 * Send SMTP quit command
492 * Go away gracefully ...