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;
224 /* Unix/Linux code */
228 (void)gmtime_r(&now, &tm_utc);
229 tm_utc.tm_isdst = tm.tm_isdst;
230 return (long)difftime(mktime(&tm_utc), now) / 60;
234 static void get_date_string(char *buf, int buf_len)
236 time_t now = time(NULL);
238 char tzbuf[MAXSTRING];
241 /* Add RFC822 date */
242 (void)localtime_r(&now, &tm);
244 my_timezone = tz_offset(now, tm);
245 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
246 sprintf(tzbuf, " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60);
247 strcat(buf, tzbuf); /* add +0100 */
248 strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
249 strcat(buf, tzbuf); /* add (CEST) */
253 /*********************************************************************
255 * Program to send email
257 int main (int argc, char *argv[])
260 struct sockaddr_in sin;
263 unsigned long maxlines, lines;
264 #if defined(HAVE_WIN32)
272 setlocale(LC_ALL, "en_US");
273 bindtextdomain("bacula", LOCALEDIR);
274 textdomain("bacula");
276 my_name_is(argc, argv, "bsmtp");
279 while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
285 Dmsg1(20, "cc=%s\n", optarg);
289 case 'd': /* set debug level */
290 if (*optarg == 't') {
291 dbg_timestamp = true;
293 debug_level = atoi(optarg);
294 if (debug_level <= 0) {
298 Dmsg1(20, "Debug level = %d\n", debug_level);
305 case 'h': /* smtp host */
306 Dmsg1(20, "host=%s\n", optarg);
307 p = strchr(optarg, ':');
315 case 's': /* subject */
316 Dmsg1(20, "subject=%s\n", optarg);
320 case 'r': /* reply address */
325 Dmsg1(20, "maxlines=%s\n", optarg);
326 maxlines = (unsigned long) atol(optarg);
339 Pmsg0(0, _("Fatal error: no recipient given.\n"));
346 * Determine SMTP server
348 if (mailhost == NULL) {
349 if ((cp = getenv("SMTPSERVER")) != NULL) {
352 mailhost = "localhost";
356 #if defined(HAVE_WIN32)
359 _setmode(0, _O_BINARY);
360 WSAStartup(MAKEWORD(2,2), &wsaData);
364 * Find out my own host name for HELO;
365 * if possible, get the fully qualified domain name
367 if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
368 Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
371 if ((hp = gethostbyname(my_hostname)) == NULL) {
372 Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
376 strcpy(my_hostname, hp->h_name);
377 Dmsg1(20, "My hostname is: %s\n", my_hostname);
380 * Determine from address.
382 if (from_addr == NULL) {
383 #if defined(HAVE_WIN32)
384 DWORD dwSize = UNLEN + 1;
385 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
387 if (GetUserName(lpszBuffer, &dwSize)) {
388 sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
390 sprintf(buf, "unknown-user@%s", my_hostname);
393 if ((pwd = getpwuid(getuid())) == 0) {
394 sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
396 sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
399 from_addr = bstrdup(buf);
401 Dmsg1(20, "From addr=%s\n", from_addr);
404 * Connect to smtp daemon on mailhost.
407 if ((hp = gethostbyname(mailhost)) == NULL) {
408 Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
410 if (strcasecmp(mailhost, "localhost") != 0) {
411 Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
412 mailhost = "localhost";
418 if (hp->h_addrtype != AF_INET) {
419 Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
422 memset((char *)&sin, 0, sizeof(sin));
423 memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
424 sin.sin_family = hp->h_addrtype;
425 sin.sin_port = htons(mailport);
426 #if defined(HAVE_WIN32)
427 if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
428 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
432 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
433 Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
437 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
438 Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
441 Dmsg0(20, "Connected\n");
443 #if defined(HAVE_WIN32)
444 int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
445 if (fdSocket == -1) {
446 Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
450 int fdSocket2 = dup(fdSocket);
452 if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
453 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
456 if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
457 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
461 if ((r = dup(s)) < 0) {
462 Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
465 if ((sfp = fdopen(s, "w")) == 0) {
466 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
469 if ((rfp = fdopen(r, "r")) == 0) {
470 Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
476 * Send SMTP headers. Note, if any of the strings have a <
477 * in them already, we do not enclose the string in < >, otherwise
480 get_response(); /* banner */
481 chat("helo %s\r\n", my_hostname);
482 chat("mail from:%s\r\n", cleanup_addr(from_addr, buf, sizeof(buf)));
484 for (i = 0; i < argc; i++) {
485 Dmsg1(20, "rcpt to: %s\n", argv[i]);
486 chat("rcpt to:%s\r\n", cleanup_addr(argv[i], buf, sizeof(buf)));
490 chat("rcpt to:%s\r\n", cleanup_addr(cc_addr, buf, sizeof(buf)));
496 * Send message header
498 fprintf(sfp, "From: %s\r\n", from_addr);
499 Dmsg1(10, "From: %s\r\n", from_addr);
501 fprintf(sfp, "Subject: %s\r\n", subject);
502 Dmsg1(10, "Subject: %s\r\n", subject);
505 fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
506 Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
509 fprintf(sfp, "Errors-To: %s\r\n", err_addr);
510 Dmsg1(10, "Errors-To: %s\r\n", err_addr);
513 #if defined(HAVE_WIN32)
514 DWORD dwSize = UNLEN + 1;
515 LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
517 if (GetUserName(lpszBuffer, &dwSize)) {
518 fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
519 Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
521 fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
522 Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
525 if ((pwd = getpwuid(getuid())) == 0) {
526 fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
527 Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
529 fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
530 Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
534 fprintf(sfp, "To: %s", argv[0]);
535 Dmsg1(10, "To: %s", argv[0]);
536 for (i = 1; i < argc; i++) {
537 fprintf(sfp, ",%s", argv[i]);
538 Dmsg1(10, ",%s", argv[i]);
541 fprintf(sfp, "\r\n");
544 fprintf(sfp, "Cc: %s\r\n", cc_addr);
545 Dmsg1(10, "Cc: %s\r\n", cc_addr);
549 fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
550 Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
553 get_date_string(buf, sizeof(buf));
554 fprintf(sfp, "Date: %s\r\n", buf);
555 Dmsg1(10, "Date: %s\r\n", buf);
557 fprintf(sfp, "\r\n");
563 while (fgets(buf, sizeof(buf), stdin)) {
564 if (maxlines > 0 && ++lines > maxlines) {
565 Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
568 buf[sizeof(buf)-1] = '\0';
569 buf[strlen(buf)-1] = '\0';
570 if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
571 fputs("..\r\n", sfp);
572 } else { /* pass body through unchanged */
578 if (lines > maxlines) {
579 Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
580 fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
584 * Send SMTP quit command
590 * Go away gracefully ...