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 ======== Original copyrights ==========
33 SMTPclient -- simple SMTP client
35 Copyright (c) 1997 Ralf S. Engelschall, All rights reserved.
37 This program is free software; it may be redistributed and/or modified
38 only under the terms of either the Artistic License or the GNU General
39 Public License, which may be found in the SMTP source distribution.
40 Look at the file COPYING.
42 This program is distributed in the hope that it will be useful, but
43 WITHOUT ANY WARRANTY; without even the implied warranty of
44 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45 GNU General Public License for more details.
47 ======================================================================
49 smtpclient_main.c -- program source
51 Based on smtp.c as of August 11, 1995 from
53 Eindhoven University of Technology,
54 Department of Mathematics and Computer Science,
55 Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands.
60 Kern Sibbald, July 2001
62 Note, the original W.Z. Venema smtp.c had no license and no
64 http://archives.neohapsis.com/archives/postfix/2000-05/1520.html
74 #define MY_NAME "bsmtp"
76 #if defined(HAVE_WIN32)
81 int generate_daemon_event(JCR *jcr, const char *event)
91 static char *from_addr = NULL;
92 static char *cc_addr = NULL;
93 static char *subject = NULL;
94 static char *err_addr = NULL;
95 static const char *mailhost = NULL;
96 static char *reply_addr = NULL;
97 static int mailport = 25;
98 static char my_hostname[MAXSTRING];
99 static bool content_utf8 = false;
102 * Take input that may have names and other stuff and strip
103 * it down to the mail box address ... i.e. what is enclosed
104 * in < >. Otherwise add < >.
106 static char *cleanup_addr(char *addr, char *buf, int buf_len)
110 if ((p = strchr(addr, '<')) == NULL) {
111 snprintf(buf, buf_len, "<%s>", addr);
114 for (q=buf; *p && *p!='>'; ) {
122 Dmsg2(100, "cleanup in=%s out=%s\n", addr, buf);
127 * examine message from server
129 static void get_response(void)
133 Dmsg0(50, "Calling fgets on read socket rfp.\n");
135 while (fgets(buf, sizeof(buf), rfp)) {
136 int len = strlen(buf);
140 if (debug_level >= 10) {
141 fprintf(stderr, "%s <-- %s\n", mailhost, buf);
143 Dmsg2(10, "%s --> %s\n", mailhost, buf);
144 if (!isdigit((int)buf[0]) || buf[0] > '3') {
145 Pmsg2(0, _("Fatal malformed reply from %s: %s\n"), mailhost, buf);
153 fprintf(stderr, _("Fatal fgets error: ERR=%s\n"), strerror(errno));
159 * say something to server and check the response
161 static void chat(const char *fmt, ...)
166 vfprintf(sfp, fmt, ap);
168 if (debug_level >= 10) {
169 fprintf(stdout, "%s --> ", my_hostname);
171 vfprintf(stdout, fmt, ap);
176 if (debug_level >= 10) {
187 "Usage: %s [-f from] [-h mailhost] [-s subject] [-c copy] [recipient ...]\n"
188 " -8 set charset utf-8\n"
189 " -c set the Cc: field\n"
190 " -dnn set debug level to nn\n"
191 " -f set the From: field\n"
192 " -h use mailhost:port as the SMTP server\n"
193 " -s set the Subject: field\n"
194 " -r set the Reply-To: field\n"
195 " -l set the maximum number of lines that should be sent (default: unlimited)\n"
196 " -? print this message.\n"
203 * Return the offset west from localtime to UTC in minutes
204 * Same as timezone.tz_minuteswest
205 * Unix tz_offset coded by: Attila Fülöp
208 static long tz_offset(time_t lnow, struct tm &tm)
210 #if defined(HAVE_WIN32)
211 #if defined(HAVE_MINGW)
212 __MINGW_IMPORT long _dstbias;
223 /* Unix/Linux code */
227 (void)gmtime_r(&now, &tm_utc);
228 tm_utc.tm_isdst = tm.tm_isdst;
229 return (long)difftime(mktime(&tm_utc), now) / 60;
233 static void get_date_string(char *buf, int buf_len)
235 time_t now = time(NULL);
237 char tzbuf[MAXSTRING];
240 /* Add RFC822 date */
241 (void)localtime_r(&now, &tm);
243 my_timezone = tz_offset(now, tm);
244 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
245 sprintf(tzbuf, " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60);
246 strcat(buf, tzbuf); /* add +0100 */
247 strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
248 strcat(buf, tzbuf); /* add (CEST) */
252 /*********************************************************************
254 * Program to send email
256 int main (int argc, char *argv[])
259 struct sockaddr_in sin;
262 unsigned long maxlines, lines;
263 #if defined(HAVE_WIN32)
271 setlocale(LC_ALL, "en_US");
272 bindtextdomain("bacula", LOCALEDIR);
273 textdomain("bacula");
275 my_name_is(argc, argv, "bsmtp");
278 while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
284 Dmsg1(20, "cc=%s\n", optarg);
288 case 'd': /* set debug level */
289 debug_level = atoi(optarg);
290 if (debug_level <= 0) {
293 Dmsg1(20, "Debug level = %d\n", debug_level);
300 case 'h': /* smtp host */
301 Dmsg1(20, "host=%s\n", optarg);
302 p = strchr(optarg, ':');
310 case 's': /* subject */
311 Dmsg1(20, "subject=%s\n", optarg);
315 case 'r': /* reply address */
320 Dmsg1(20, "maxlines=%s\n", optarg);
321 maxlines = (unsigned long) atol(optarg);
334 Pmsg0(0, _("Fatal error: no recipient given.\n"));
341 * Determine SMTP server
343 if (mailhost == NULL) {
344 if ((cp = getenv("SMTPSERVER")) != NULL) {
347 mailhost = "localhost";
351 #if defined(HAVE_WIN32)
354 _setmode(0, _O_BINARY);
355 WSAStartup(MAKEWORD(2,2), &wsaData);
359 * Find out my own host name for HELO;
360 * if possible, get the fully qualified domain name
362 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
363 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
366 if ((hp = gethostbyname(my_hostname)) == NULL) {
367 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
371 strcpy(my_hostname, hp->h_name);
372 Dmsg1(20, "My hostname is: %s\n", my_hostname);
375 * Determine from address.
377 if (from_addr == NULL) {
378 #if defined(HAVE_WIN32)
379 DWORD dwSize = UNLEN + 1;
380 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
382 if (GetUserName(lpszBuffer, &dwSize)) {
383 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
385 sprintf(buf, "unknown-user@%s", my_hostname);
388 if ((pwd = getpwuid(getuid())) == 0) {
389 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
391 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
394 from_addr = bstrdup(buf);
396 Dmsg1(20, "From addr=%s\n", from_addr);
399 * Connect to smtp daemon on mailhost.
402 if ((hp = gethostbyname(mailhost)) == NULL) {
403 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
405 if (strcasecmp(mailhost, "localhost") != 0) {
406 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
407 mailhost = "localhost";
413 if (hp->h_addrtype != AF_INET) {
414 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
417 memset((char *)&sin, 0, sizeof(sin));
418 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
419 sin.sin_family = hp->h_addrtype;
420 sin.sin_port = htons(mailport);
421 #if defined(HAVE_WIN32)
422 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
423 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
427 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
428 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
432 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
433 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
436 Dmsg0(20, "Connected\n");
438 #if defined(HAVE_WIN32)
439 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
440 if (fdSocket == -1) {
441 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
445 int fdSocket2 = dup(fdSocket);
447 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
448 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
451 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
452 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
456 if ((r = dup(s)) < 0) {
457 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
460 if ((sfp = fdopen(s, "w")) == 0) {
461 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
464 if ((rfp = fdopen(r, "r")) == 0) {
465 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
471 * Send SMTP headers. Note, if any of the strings have a <
472 * in them already, we do not enclose the string in < >, otherwise
475 get_response(); /* banner */
476 chat("helo %s\r\n", my_hostname);
477 chat("mail from:%s\r\n", cleanup_addr(from_addr, buf, sizeof(buf)));
479 for (i = 0; i < argc; i++) {
480 Dmsg1(20, "rcpt to: %s\n", argv[i]);
481 chat("rcpt to:%s\r\n", cleanup_addr(argv[i], buf, sizeof(buf)));
485 chat("rcpt to:%s\r\n", cleanup_addr(cc_addr, buf, sizeof(buf)));
491 * Send message header
493 fprintf(sfp, "From: %s\r\n", from_addr);
494 Dmsg1(10, "From: %s\r\n", from_addr);
496 fprintf(sfp, "Subject: %s\r\n", subject);
497 Dmsg1(10, "Subject: %s\r\n", subject);
500 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
501 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
504 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
505 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
508 #if defined(HAVE_WIN32)
509 DWORD dwSize = UNLEN + 1;
510 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
512 if (GetUserName(lpszBuffer, &dwSize)) {
513 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
514 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
516 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
517 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
520 if ((pwd = getpwuid(getuid())) == 0) {
521 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
522 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
524 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
525 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
529 fprintf(sfp, "To: %s", argv[0]);
530 Dmsg1(10, "To: %s", argv[0]);
531 for (i = 1; i < argc; i++) {
532 fprintf(sfp, ",%s", argv[i]);
533 Dmsg1(10, ",%s", argv[i]);
536 fprintf(sfp, "\r\n");
539 fprintf(sfp, "Cc: %s\r\n", cc_addr);
540 Dmsg1(10, "Cc: %s\r\n", cc_addr);
544 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
545 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
548 get_date_string(buf, sizeof(buf));
549 fprintf(sfp, "Date: %s\r\n", buf);
550 Dmsg1(10, "Date: %s\r\n", buf);
552 fprintf(sfp, "\r\n");
558 while (fgets(buf, sizeof(buf), stdin)) {
559 if (maxlines > 0 && ++lines > maxlines) {
560 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
563 buf[sizeof(buf)-1] = '\0';
564 buf[strlen(buf)-1] = '\0';
565 if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
566 fputs("..\r\n", sfp);
567 } else { /* pass body through unchanged */
573 if (lines > maxlines) {
574 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
575 fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
579 * Send SMTP quit command
585 * Go away gracefully ...