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