2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2009 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 three of the GNU Affero 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 Affero 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 Kern Sibbald.
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 to UTF-8\n"
189 " -c set the Cc: field\n"
190 " -d <nn> set debug level to <nn>\n"
191 " -dt print a 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 to send (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;
216 # define _tzset tzset
228 /* Unix/Linux code */
232 (void)gmtime_r(&now, &tm_utc);
233 tm_utc.tm_isdst = tm.tm_isdst;
234 return (long)difftime(mktime(&tm_utc), now) / 60;
238 static void get_date_string(char *buf, int buf_len)
240 time_t now = time(NULL);
242 char tzbuf[MAXSTRING];
245 /* Add RFC822 date */
246 (void)localtime_r(&now, &tm);
248 my_timezone = tz_offset(now, tm);
249 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
250 sprintf(tzbuf, " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60);
251 strcat(buf, tzbuf); /* add +0100 */
252 strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
253 strcat(buf, tzbuf); /* add (CEST) */
257 /*********************************************************************
259 * Program to send email
261 int main (int argc, char *argv[])
264 struct sockaddr_in sin;
267 unsigned long maxlines, lines;
268 #if defined(HAVE_WIN32)
276 setlocale(LC_ALL, "en_US");
277 bindtextdomain("bacula", LOCALEDIR);
278 textdomain("bacula");
280 my_name_is(argc, argv, "bsmtp");
283 while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
289 Dmsg1(20, "cc=%s\n", optarg);
293 case 'd': /* set debug level */
294 if (*optarg == 't') {
295 dbg_timestamp = true;
297 debug_level = atoi(optarg);
298 if (debug_level <= 0) {
302 Dmsg1(20, "Debug level = %d\n", debug_level);
309 case 'h': /* smtp host */
310 Dmsg1(20, "host=%s\n", optarg);
311 p = strchr(optarg, ':');
319 case 's': /* subject */
320 Dmsg1(20, "subject=%s\n", optarg);
324 case 'r': /* reply address */
329 Dmsg1(20, "maxlines=%s\n", optarg);
330 maxlines = (unsigned long) atol(optarg);
343 Pmsg0(0, _("Fatal error: no recipient given.\n"));
350 * Determine SMTP server
352 if (mailhost == NULL) {
353 if ((cp = getenv("SMTPSERVER")) != NULL) {
356 mailhost = "localhost";
360 #if defined(HAVE_WIN32)
363 _setmode(0, _O_BINARY);
364 WSAStartup(MAKEWORD(2,2), &wsaData);
368 * Find out my own host name for HELO;
369 * if possible, get the fully qualified domain name
371 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
372 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
375 if ((hp = gethostbyname(my_hostname)) == NULL) {
376 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
380 strcpy(my_hostname, hp->h_name);
381 Dmsg1(20, "My hostname is: %s\n", my_hostname);
384 * Determine from address.
386 if (from_addr == NULL) {
387 #if defined(HAVE_WIN32)
388 DWORD dwSize = UNLEN + 1;
389 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
391 if (GetUserName(lpszBuffer, &dwSize)) {
392 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
394 sprintf(buf, "unknown-user@%s", my_hostname);
397 if ((pwd = getpwuid(getuid())) == 0) {
398 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
400 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
403 from_addr = bstrdup(buf);
405 Dmsg1(20, "From addr=%s\n", from_addr);
408 * Connect to smtp daemon on mailhost.
411 if ((hp = gethostbyname(mailhost)) == NULL) {
412 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
414 if (strcasecmp(mailhost, "localhost") != 0) {
415 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
416 mailhost = "localhost";
422 if (hp->h_addrtype != AF_INET) {
423 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
426 memset((char *)&sin, 0, sizeof(sin));
427 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
428 sin.sin_family = hp->h_addrtype;
429 sin.sin_port = htons(mailport);
430 #if defined(HAVE_WIN32)
431 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
432 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
436 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
437 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
441 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
442 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
445 Dmsg0(20, "Connected\n");
447 #if defined(HAVE_WIN32)
448 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
449 if (fdSocket == -1) {
450 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
454 int fdSocket2 = dup(fdSocket);
456 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
457 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
460 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
461 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
465 if ((r = dup(s)) < 0) {
466 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
469 if ((sfp = fdopen(s, "w")) == 0) {
470 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
473 if ((rfp = fdopen(r, "r")) == 0) {
474 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
480 * Send SMTP headers. Note, if any of the strings have a <
481 * in them already, we do not enclose the string in < >, otherwise
484 get_response(); /* banner */
485 chat("HELO %s\r\n", my_hostname);
486 chat("MAIL FROM:%s\r\n", cleanup_addr(from_addr, buf, sizeof(buf)));
488 for (i = 0; i < argc; i++) {
489 Dmsg1(20, "rcpt to: %s\n", argv[i]);
490 chat("RCPT TO:%s\r\n", cleanup_addr(argv[i], buf, sizeof(buf)));
494 chat("RCPT TO:%s\r\n", cleanup_addr(cc_addr, buf, sizeof(buf)));
500 * Send message header
502 fprintf(sfp, "From: %s\r\n", from_addr);
503 Dmsg1(10, "From: %s\r\n", from_addr);
505 fprintf(sfp, "Subject: %s\r\n", subject);
506 Dmsg1(10, "Subject: %s\r\n", subject);
509 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
510 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
513 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
514 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
517 #if defined(HAVE_WIN32)
518 DWORD dwSize = UNLEN + 1;
519 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
521 if (GetUserName(lpszBuffer, &dwSize)) {
522 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
523 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
525 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
526 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
529 if ((pwd = getpwuid(getuid())) == 0) {
530 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
531 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
533 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
534 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
538 fprintf(sfp, "To: %s", argv[0]);
539 Dmsg1(10, "To: %s", argv[0]);
540 for (i = 1; i < argc; i++) {
541 fprintf(sfp, ",%s", argv[i]);
542 Dmsg1(10, ",%s", argv[i]);
545 fprintf(sfp, "\r\n");
548 fprintf(sfp, "Cc: %s\r\n", cc_addr);
549 Dmsg1(10, "Cc: %s\r\n", cc_addr);
553 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
554 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
557 get_date_string(buf, sizeof(buf));
558 fprintf(sfp, "Date: %s\r\n", buf);
559 Dmsg1(10, "Date: %s\r\n", buf);
561 fprintf(sfp, "\r\n");
567 while (fgets(buf, sizeof(buf), stdin)) {
568 if (maxlines > 0 && ++lines > maxlines) {
569 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
570 while (fgets(buf, sizeof(buf), stdin)) {
575 buf[sizeof(buf)-1] = '\0';
576 buf[strlen(buf)-1] = '\0';
577 if (buf[0] == '.') { /* add extra . see RFC 2821 4.5.2 */
584 if (lines > maxlines) {
585 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
586 fprintf(sfp, "\r\n\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
590 * Send SMTP quit command
596 * Go away gracefully ...