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 " -d <nn> set debug level to <nn>\n"
191 " -dt print timestamp in debug output\n"
192 " -f set the From: field\n"
193 " -h use mailhost:port as the SMTP server\n"
194 " -s set the Subject: field\n"
195 " -r set the Reply-To: field\n"
196 " -l set the maximum number of lines that should be sent (default: unlimited)\n"
197 " -? print this message.\n"
204 * Return the offset west from localtime to UTC in minutes
205 * Same as timezone.tz_minuteswest
206 * Unix tz_offset coded by: Attila Fülöp
209 static long tz_offset(time_t lnow, struct tm &tm)
211 #if defined(HAVE_WIN32)
212 #if defined(HAVE_MINGW)
213 __MINGW_IMPORT long _dstbias;
226 /* Unix/Linux code */
230 (void)gmtime_r(&now, &tm_utc);
231 tm_utc.tm_isdst = tm.tm_isdst;
232 return (long)difftime(mktime(&tm_utc), now) / 60;
236 static void get_date_string(char *buf, int buf_len)
238 time_t now = time(NULL);
240 char tzbuf[MAXSTRING];
243 /* Add RFC822 date */
244 (void)localtime_r(&now, &tm);
246 my_timezone = tz_offset(now, tm);
247 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
248 sprintf(tzbuf, " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60);
249 strcat(buf, tzbuf); /* add +0100 */
250 strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
251 strcat(buf, tzbuf); /* add (CEST) */
255 /*********************************************************************
257 * Program to send email
259 int main (int argc, char *argv[])
262 struct sockaddr_in sin;
265 unsigned long maxlines, lines;
266 #if defined(HAVE_WIN32)
274 setlocale(LC_ALL, "en_US");
275 bindtextdomain("bacula", LOCALEDIR);
276 textdomain("bacula");
278 my_name_is(argc, argv, "bsmtp");
281 while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
287 Dmsg1(20, "cc=%s\n", optarg);
291 case 'd': /* set debug level */
292 if (*optarg == 't') {
293 dbg_timestamp = true;
295 debug_level = atoi(optarg);
296 if (debug_level <= 0) {
300 Dmsg1(20, "Debug level = %d\n", debug_level);
307 case 'h': /* smtp host */
308 Dmsg1(20, "host=%s\n", optarg);
309 p = strchr(optarg, ':');
317 case 's': /* subject */
318 Dmsg1(20, "subject=%s\n", optarg);
322 case 'r': /* reply address */
327 Dmsg1(20, "maxlines=%s\n", optarg);
328 maxlines = (unsigned long) atol(optarg);
341 Pmsg0(0, _("Fatal error: no recipient given.\n"));
348 * Determine SMTP server
350 if (mailhost == NULL) {
351 if ((cp = getenv("SMTPSERVER")) != NULL) {
354 mailhost = "localhost";
358 #if defined(HAVE_WIN32)
361 _setmode(0, _O_BINARY);
362 WSAStartup(MAKEWORD(2,2), &wsaData);
366 * Find out my own host name for HELO;
367 * if possible, get the fully qualified domain name
369 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
370 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
373 if ((hp = gethostbyname(my_hostname)) == NULL) {
374 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
378 strcpy(my_hostname, hp->h_name);
379 Dmsg1(20, "My hostname is: %s\n", my_hostname);
382 * Determine from address.
384 if (from_addr == NULL) {
385 #if defined(HAVE_WIN32)
386 DWORD dwSize = UNLEN + 1;
387 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
389 if (GetUserName(lpszBuffer, &dwSize)) {
390 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
392 sprintf(buf, "unknown-user@%s", my_hostname);
395 if ((pwd = getpwuid(getuid())) == 0) {
396 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
398 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
401 from_addr = bstrdup(buf);
403 Dmsg1(20, "From addr=%s\n", from_addr);
406 * Connect to smtp daemon on mailhost.
409 if ((hp = gethostbyname(mailhost)) == NULL) {
410 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
412 if (strcasecmp(mailhost, "localhost") != 0) {
413 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
414 mailhost = "localhost";
420 if (hp->h_addrtype != AF_INET) {
421 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
424 memset((char *)&sin, 0, sizeof(sin));
425 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
426 sin.sin_family = hp->h_addrtype;
427 sin.sin_port = htons(mailport);
428 #if defined(HAVE_WIN32)
429 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
430 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
434 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
435 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
439 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
440 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
443 Dmsg0(20, "Connected\n");
445 #if defined(HAVE_WIN32)
446 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
447 if (fdSocket == -1) {
448 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
452 int fdSocket2 = dup(fdSocket);
454 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
455 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
458 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
459 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
463 if ((r = dup(s)) < 0) {
464 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
467 if ((sfp = fdopen(s, "w")) == 0) {
468 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
471 if ((rfp = fdopen(r, "r")) == 0) {
472 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
478 * Send SMTP headers. Note, if any of the strings have a <
479 * in them already, we do not enclose the string in < >, otherwise
482 get_response(); /* banner */
483 chat("helo %s\r\n", my_hostname);
484 chat("mail from:%s\r\n", cleanup_addr(from_addr, buf, sizeof(buf)));
486 for (i = 0; i < argc; i++) {
487 Dmsg1(20, "rcpt to: %s\n", argv[i]);
488 chat("rcpt to:%s\r\n", cleanup_addr(argv[i], buf, sizeof(buf)));
492 chat("rcpt to:%s\r\n", cleanup_addr(cc_addr, buf, sizeof(buf)));
498 * Send message header
500 fprintf(sfp, "From: %s\r\n", from_addr);
501 Dmsg1(10, "From: %s\r\n", from_addr);
503 fprintf(sfp, "Subject: %s\r\n", subject);
504 Dmsg1(10, "Subject: %s\r\n", subject);
507 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
508 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
511 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
512 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
515 #if defined(HAVE_WIN32)
516 DWORD dwSize = UNLEN + 1;
517 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
519 if (GetUserName(lpszBuffer, &dwSize)) {
520 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
521 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
523 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
524 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
527 if ((pwd = getpwuid(getuid())) == 0) {
528 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
529 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
531 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
532 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
536 fprintf(sfp, "To: %s", argv[0]);
537 Dmsg1(10, "To: %s", argv[0]);
538 for (i = 1; i < argc; i++) {
539 fprintf(sfp, ",%s", argv[i]);
540 Dmsg1(10, ",%s", argv[i]);
543 fprintf(sfp, "\r\n");
546 fprintf(sfp, "Cc: %s\r\n", cc_addr);
547 Dmsg1(10, "Cc: %s\r\n", cc_addr);
551 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
552 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
555 get_date_string(buf, sizeof(buf));
556 fprintf(sfp, "Date: %s\r\n", buf);
557 Dmsg1(10, "Date: %s\r\n", buf);
559 fprintf(sfp, "\r\n");
565 while (fgets(buf, sizeof(buf), stdin)) {
566 if (maxlines > 0 && ++lines > maxlines) {
567 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
570 buf[sizeof(buf)-1] = '\0';
571 buf[strlen(buf)-1] = '\0';
572 if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
573 fputs("..\r\n", sfp);
574 } else { /* pass body through unchanged */
580 if (lines > maxlines) {
581 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
582 fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
586 * Send SMTP quit command
592 * Go away gracefully ...