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