]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tools/bsmtp.c
kes Rework bsmtp date editing for Win32.
[bacula/bacula] / bacula / src / tools / bsmtp.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2007 Free Software Foundation Europe e.V.
5
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 plus additions
11    that are listed in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29    Derived from a SMTPclient:
30
31        SMTPclient -- simple SMTP client
32
33        Copyright (C) 1997 Ralf S. Engelschall, All Rights Reserved.
34        rse@engelschall.com
35        www.engelschall.com
36
37    Kern Sibbald, July 2001
38
39    Version $Id$
40
41  */
42
43
44 #include "bacula.h"
45 #include "jcr.h"
46 #define MY_NAME "bsmtp"
47
48 #if defined(HAVE_WIN32)
49 #include <lmcons.h>
50 #endif
51
52 /* Dummy functions */
53 int generate_daemon_event(JCR *jcr, const char *event) 
54    { return 1; }
55
56 #ifndef MAXSTRING
57 #define MAXSTRING 254
58 #endif
59
60 static FILE *sfp;
61 static FILE *rfp;
62
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];
71 static bool content_utf8 = false;
72
73
74 /*
75  *  examine message from server
76  */
77 static void get_response(void)
78 {
79     char buf[MAXSTRING];
80
81     Dmsg0(50, "Calling fgets on read socket rfp.\n");
82     buf[3] = 0;
83     while (fgets(buf, sizeof(buf), rfp)) {
84         int len = strlen(buf);
85         if (len > 0) {
86            buf[len-1] = 0;
87         }
88         if (debug_level >= 10) {
89             fprintf(stderr, "%s <-- %s\n", mailhost, buf);
90         }
91         Dmsg2(10, "%s --> %s\n", mailhost, buf);
92         if (!isdigit((int)buf[0]) || buf[0] > '3') {
93             Pmsg2(0, _("Fatal malformed reply from %s: %s\n"), mailhost, buf);
94             exit(1);
95         }
96         if (buf[3] != '-') {
97             break;
98         }
99     }
100     if (ferror(rfp)) {
101         fprintf(stderr, _("Fatal fgets error: ERR=%s\n"), strerror(errno));
102     }
103     return;
104 }
105
106 /*
107  *  say something to server and check the response
108  */
109 static void chat(const char *fmt, ...)
110 {
111     va_list ap;
112
113     va_start(ap, fmt);
114     vfprintf(sfp, fmt, ap);
115     if (debug_level >= 10) {
116        fprintf(stdout, "%s --> ", my_hostname);
117        vfprintf(stdout, fmt, ap);
118     }
119     va_end(ap);
120
121     fflush(sfp);
122     if (debug_level >= 10) {
123        fflush(stdout);
124     }
125     get_response();
126 }
127
128
129 static void usage()
130 {
131    fprintf(stderr,
132 _("\n"
133 "Usage: %s [-f from] [-h mailhost] [-s subject] [-c copy] [recipient ...]\n"
134 "       -8          set charset utf-8\n"
135 "       -c          set the Cc: field\n"
136 "       -dnn        set debug level to nn\n"
137 "       -f          set the From: field\n"
138 "       -h          use mailhost:port as the SMTP server\n"
139 "       -s          set the Subject: field\n"
140 "       -r          set the Reply-To: field\n"
141 "       -l          set the maximum number of lines that should be sent (default: unlimited)\n"
142 "       -?          print this message.\n"
143 "\n"), MY_NAME);
144
145    exit(1);
146 }
147
148 static void get_date_string(char *buf, int buf_len)
149 {
150    time_t now = time(NULL);
151    struct tm tm;
152    char tzbuf[MAXSTRING];
153    long my_timezone;
154
155    /* Add RFC822 date */
156    (void)localtime_r(&now, &tm);
157
158 #if defined(HAVE_WIN32)
159 #if defined(HAVE_MINGW)
160 __MINGW_IMPORT long     _dstbias;
161 #endif
162    _tzset();
163    my_timezone = _timezone;
164    my_timezone += _dstbias;
165    my_timezone /= 60;
166
167 #else
168    tzset();
169    my_timezone = timezone / 60;     /* timezone offset in mins */
170    if (tm.tm_isdst == 1) {
171       my_timezone -= 60;            /* adjust for daylight savings */
172    }
173 #endif
174    strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm);
175    sprintf(tzbuf, " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60);
176    strcat(buf, tzbuf);              /* add +0100 */
177    strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm);
178    strcat(buf, tzbuf);              /* add (CEST) */
179 }
180
181
182 /*********************************************************************
183  *
184  *  Program to send email
185  */
186 int main (int argc, char *argv[])
187 {
188     char buf[MAXSTRING];
189     struct sockaddr_in sin;
190     struct hostent *hp;
191     int i, ch;
192     unsigned long maxlines, lines;
193 #if defined(HAVE_WIN32)
194     SOCKET s;
195 #else
196     int s, r;
197     struct passwd *pwd;
198 #endif
199     char *cp, *p;
200     
201    setlocale(LC_ALL, "en_US");
202    bindtextdomain("bacula", LOCALEDIR);
203    textdomain("bacula");
204
205    my_name_is(argc, argv, "bsmtp");
206    maxlines = 0;
207
208    while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
209       switch (ch) {
210       case '8':
211          content_utf8 = true;
212          break;
213       case 'c':
214          Dmsg1(20, "cc=%s\n", optarg);
215          cc_addr = optarg;
216          break;
217
218       case 'd':                    /* set debug level */
219          debug_level = atoi(optarg);
220          if (debug_level <= 0) {
221             debug_level = 1;
222          }
223          Dmsg1(20, "Debug level = %d\n", debug_level);
224          break;
225
226       case 'f':                    /* from */
227          from_addr = optarg;
228          break;
229
230       case 'h':                    /* smtp host */
231          Dmsg1(20, "host=%s\n", optarg);
232          p = strchr(optarg, ':');
233          if (p) {
234             *p++ = 0;
235             mailport = atoi(p);
236          }
237          mailhost = optarg;
238          break;
239
240       case 's':                    /* subject */
241          Dmsg1(20, "subject=%s\n", optarg);
242          subject = optarg;
243          break;
244
245       case 'r':                    /* reply address */
246          reply_addr = optarg;
247          break;
248
249       case 'l':
250          Dmsg1(20, "maxlines=%s\n", optarg);
251          maxlines = (unsigned long) atol(optarg);
252          break;
253
254       case '?':
255       default:
256          usage();
257
258       }
259    }
260    argc -= optind;
261    argv += optind;
262
263    if (argc < 1) {
264       Pmsg0(0, _("Fatal error: no recipient given.\n"));
265       usage();
266       exit(1);
267    }
268
269 #if defined(HAVE_WIN32)
270    _setmode(0, _O_BINARY);
271 #endif
272
273    /*
274     *  Determine SMTP server
275     */
276    if (mailhost == NULL) {
277       if ((cp = getenv("SMTPSERVER")) != NULL) {
278          mailhost = cp;
279       } else {
280          mailhost = "localhost";
281       }
282    }
283
284 #if defined(HAVE_WIN32)
285    WSADATA  wsaData;
286
287    WSAStartup(MAKEWORD(2,2), &wsaData);
288 #endif
289
290    /*
291     *  Find out my own host name for HELO;
292     *  if possible, get the fully qualified domain name
293     */
294    if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
295       Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
296       exit(1);
297    }
298    if ((hp = gethostbyname(my_hostname)) == NULL) {
299       Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
300          strerror(errno));
301       exit(1);
302    }
303    strcpy(my_hostname, hp->h_name);
304    Dmsg1(20, "My hostname is: %s\n", my_hostname);
305
306    /*
307     *  Determine from address.
308     */
309    if (from_addr == NULL) {
310 #if defined(HAVE_WIN32)
311       DWORD dwSize = UNLEN + 1;
312       LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
313
314       if (GetUserName(lpszBuffer, &dwSize)) {
315          sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
316       } else {
317          sprintf(buf, "unknown-user@%s", my_hostname);
318       }
319 #else
320       if ((pwd = getpwuid(getuid())) == 0) {
321          sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
322       } else {
323          sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
324       }
325 #endif
326       from_addr = bstrdup(buf);
327    }
328    Dmsg1(20, "From addr=%s\n", from_addr);
329
330    /*
331     *  Connect to smtp daemon on mailhost.
332     */
333 hp:
334    if ((hp = gethostbyname(mailhost)) == NULL) {
335       Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
336          strerror(errno));
337       if (strcasecmp(mailhost, "localhost") != 0) {
338          Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
339          mailhost = "localhost";
340          goto hp;
341       }
342       exit(1);
343    }
344
345    if (hp->h_addrtype != AF_INET) {
346       Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
347       exit(1);
348    }
349    memset((char *)&sin, 0, sizeof(sin));
350    memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
351    sin.sin_family = hp->h_addrtype;
352    sin.sin_port = htons(mailport);
353 #if defined(HAVE_WIN32)
354    if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
355       Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
356       exit(1);
357    }
358 #else
359    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
360       Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
361       exit(1);
362    }
363 #endif
364    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
365       Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
366       exit(1);
367    }
368    Dmsg0(20, "Connected\n");
369
370 #if defined(HAVE_WIN32)
371    int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
372    if (fdSocket == -1) {
373       Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
374       exit(1);
375    }
376
377    int fdSocket2 = dup(fdSocket);
378
379    if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
380       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
381       exit(1);
382    }
383    if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
384       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
385       exit(1);
386    }
387 #else
388    if ((r = dup(s)) < 0) {
389       Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
390       exit(1);
391    }
392    if ((sfp = fdopen(s, "w")) == 0) {
393       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
394       exit(1);
395    }
396    if ((rfp = fdopen(r, "r")) == 0) {
397       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
398       exit(1);
399    }
400 #endif
401
402    /*
403     *  Send SMTP headers.  Note, if any of the strings have a <
404     *   in them already, we do not enclose the string in < >, otherwise
405     *   we do.
406     */
407    get_response(); /* banner */
408    chat("helo %s\r\n", my_hostname);
409    if (strchr(from_addr, '<') == NULL) {
410       chat("mail from:<%s>\r\n", from_addr);
411    } else {
412       chat("mail from:%s\r\n", from_addr);
413    }
414
415    for (i = 0; i < argc; i++) {
416       Dmsg1(20, "rcpt to: %s\n", argv[i]);
417       if (strchr(argv[i], '<') == NULL) {
418          chat("rcpt to:<%s>\r\n", argv[i]);
419       } else {
420          chat("rcpt to:%s\r\n", argv[i]);
421       }
422    }
423
424    if (cc_addr) {
425       if (strchr(cc_addr, '<') == NULL) {
426          chat("rcpt to:<%s>\r\n", cc_addr);
427       } else {
428          chat("rcpt to:%s\r\n", cc_addr);
429       }
430    }
431    Dmsg0(20, "Data\n");
432    chat("data\r\n");
433
434    /*
435     *  Send message header
436     */
437    fprintf(sfp, "From: %s\r\n", from_addr);
438    Dmsg1(10, "From: %s\r\n", from_addr);
439    if (subject) {
440       fprintf(sfp, "Subject: %s\r\n", subject);
441       Dmsg1(10, "Subject: %s\r\n", subject);
442    }
443    if (reply_addr) {
444       fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
445       Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
446    }
447    if (err_addr) {
448       fprintf(sfp, "Errors-To: %s\r\n", err_addr);
449       Dmsg1(10, "Errors-To: %s\r\n", err_addr);
450    }
451
452 #if defined(HAVE_WIN32)
453    DWORD dwSize = UNLEN + 1;
454    LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
455
456    if (GetUserName(lpszBuffer, &dwSize)) {
457       fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
458       Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
459    } else {
460       fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
461       Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
462    }
463 #else
464    if ((pwd = getpwuid(getuid())) == 0) {
465       fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
466       Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
467    } else {
468       fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
469       Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
470    }
471 #endif
472
473    fprintf(sfp, "To: %s", argv[0]);
474    Dmsg1(10, "To: %s", argv[0]);
475    for (i = 1; i < argc; i++) {
476       fprintf(sfp, ",%s", argv[i]);
477       Dmsg1(10, ",%s", argv[i]);
478    }
479
480    fprintf(sfp, "\r\n");
481    Dmsg0(10, "\r\n");
482    if (cc_addr) {
483       fprintf(sfp, "Cc: %s\r\n", cc_addr);
484       Dmsg1(10, "Cc: %s\r\n", cc_addr);
485    }
486
487    if (content_utf8) {
488       fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
489       Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
490    }
491
492    get_date_string(buf, sizeof(buf));
493    fprintf(sfp, "Date: %s\r\n", buf);
494    Dmsg1(10, "Date: %s\r\n", buf);
495
496    fprintf(sfp, "\r\n");
497
498    /*
499     *  Send message body
500     */
501    lines = 0;
502    while (fgets(buf, sizeof(buf), stdin)) {
503       if (maxlines > 0 && ++lines > maxlines) {
504          Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
505          break;
506       }
507       buf[sizeof(buf)-1] = '\0';
508       buf[strlen(buf)-1] = '\0';
509       if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
510          fputs("..\r\n", sfp);
511       } else {                     /* pass body through unchanged */
512          fputs(buf, sfp);
513          fputs("\r\n", sfp);
514       }
515    }
516
517    if (lines > maxlines) {
518       Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
519       fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
520    }
521
522    /*
523     *  Send SMTP quit command
524     */
525    chat(".\r\n");
526    chat("quit\r\n");
527
528    /*
529     *  Go away gracefully ...
530     */
531    exit(0);
532 }