]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tools/bsmtp.c
ebl update debug message (last time) :)
[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
149 /*********************************************************************
150  *
151  *  Program to send email
152  */
153 int main (int argc, char *argv[])
154 {
155     char buf[MAXSTRING];
156     struct sockaddr_in sin;
157     struct hostent *hp;
158     int i, ch;
159     unsigned long maxlines, lines;
160 #if defined(HAVE_WIN32)
161     SOCKET s;
162 #else
163     int s, r;
164     struct passwd *pwd;
165 #endif
166     char *cp, *p;
167     time_t now = time(NULL);
168     struct tm tm;
169     
170    setlocale(LC_ALL, "en_US");
171    bindtextdomain("bacula", LOCALEDIR);
172    textdomain("bacula");
173
174    my_name_is(argc, argv, "bsmtp");
175    maxlines = 0;
176
177    while ((ch = getopt(argc, argv, "8c:d:f:h:r:s:l:?")) != -1) {
178       switch (ch) {
179       case '8':
180          content_utf8 = true;
181          break;
182       case 'c':
183          Dmsg1(20, "cc=%s\n", optarg);
184          cc_addr = optarg;
185          break;
186
187       case 'd':                    /* set debug level */
188          debug_level = atoi(optarg);
189          if (debug_level <= 0) {
190             debug_level = 1;
191          }
192          Dmsg1(20, "Debug level = %d\n", debug_level);
193          break;
194
195       case 'f':                    /* from */
196          from_addr = optarg;
197          break;
198
199       case 'h':                    /* smtp host */
200          Dmsg1(20, "host=%s\n", optarg);
201          p = strchr(optarg, ':');
202          if (p) {
203             *p++ = 0;
204             mailport = atoi(p);
205          }
206          mailhost = optarg;
207          break;
208
209       case 's':                    /* subject */
210          Dmsg1(20, "subject=%s\n", optarg);
211          subject = optarg;
212          break;
213
214       case 'r':                    /* reply address */
215          reply_addr = optarg;
216          break;
217
218       case 'l':
219          Dmsg1(20, "maxlines=%s\n", optarg);
220          maxlines = (unsigned long) atol(optarg);
221          break;
222
223       case '?':
224       default:
225          usage();
226
227       }
228    }
229    argc -= optind;
230    argv += optind;
231
232    if (argc < 1) {
233       Pmsg0(0, _("Fatal error: no recipient given.\n"));
234       usage();
235       exit(1);
236    }
237
238 #if defined(HAVE_WIN32)
239    _setmode(0, _O_BINARY);
240 #endif
241
242    /*
243     *  Determine SMTP server
244     */
245    if (mailhost == NULL) {
246       if ((cp = getenv("SMTPSERVER")) != NULL) {
247          mailhost = cp;
248       } else {
249          mailhost = "localhost";
250       }
251    }
252
253 #if defined(HAVE_WIN32)
254    WSADATA  wsaData;
255
256    WSAStartup(MAKEWORD(2,2), &wsaData);
257 #endif
258
259    /*
260     *  Find out my own host name for HELO;
261     *  if possible, get the fully qualified domain name
262     */
263    if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
264       Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
265       exit(1);
266    }
267    if ((hp = gethostbyname(my_hostname)) == NULL) {
268       Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
269          strerror(errno));
270       exit(1);
271    }
272    strcpy(my_hostname, hp->h_name);
273    Dmsg1(20, "My hostname is: %s\n", my_hostname);
274
275    /*
276     *  Determine from address.
277     */
278    if (from_addr == NULL) {
279 #if defined(HAVE_WIN32)
280       DWORD dwSize = UNLEN + 1;
281       LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
282
283       if (GetUserName(lpszBuffer, &dwSize)) {
284          sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
285       } else {
286          sprintf(buf, "unknown-user@%s", my_hostname);
287       }
288 #else
289       if ((pwd = getpwuid(getuid())) == 0) {
290          sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
291       } else {
292          sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
293       }
294 #endif
295       from_addr = bstrdup(buf);
296    }
297    Dmsg1(20, "From addr=%s\n", from_addr);
298
299    /*
300     *  Connect to smtp daemon on mailhost.
301     */
302 hp:
303    if ((hp = gethostbyname(mailhost)) == NULL) {
304       Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
305          strerror(errno));
306       if (strcasecmp(mailhost, "localhost") != 0) {
307          Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
308          mailhost = "localhost";
309          goto hp;
310       }
311       exit(1);
312    }
313
314    if (hp->h_addrtype != AF_INET) {
315       Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
316       exit(1);
317    }
318    memset((char *)&sin, 0, sizeof(sin));
319    memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
320    sin.sin_family = hp->h_addrtype;
321    sin.sin_port = htons(mailport);
322 #if defined(HAVE_WIN32)
323    if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
324       Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
325       exit(1);
326    }
327 #else
328    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
329       Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
330       exit(1);
331    }
332 #endif
333    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
334       Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
335       exit(1);
336    }
337    Dmsg0(20, "Connected\n");
338
339 #if defined(HAVE_WIN32)
340    int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
341    if (fdSocket == -1) {
342       Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
343       exit(1);
344    }
345
346    int fdSocket2 = dup(fdSocket);
347
348    if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
349       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
350       exit(1);
351    }
352    if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
353       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
354       exit(1);
355    }
356 #else
357    if ((r = dup(s)) < 0) {
358       Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
359       exit(1);
360    }
361    if ((sfp = fdopen(s, "w")) == 0) {
362       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
363       exit(1);
364    }
365    if ((rfp = fdopen(r, "r")) == 0) {
366       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
367       exit(1);
368    }
369 #endif
370
371    /*
372     *  Send SMTP headers.  Note, if any of the strings have a <
373     *   in them already, we do not enclose the string in < >, otherwise
374     *   we do.
375     */
376    get_response(); /* banner */
377    chat("helo %s\r\n", my_hostname);
378    if (strchr(from_addr, '<') == NULL) {
379       chat("mail from:<%s>\r\n", from_addr);
380    } else {
381       chat("mail from:%s\r\n", from_addr);
382    }
383
384    for (i = 0; i < argc; i++) {
385       Dmsg1(20, "rcpt to: %s\n", argv[i]);
386       if (strchr(argv[i], '<') == NULL) {
387          chat("rcpt to:<%s>\r\n", argv[i]);
388       } else {
389          chat("rcpt to:%s\r\n", argv[i]);
390       }
391    }
392
393    if (cc_addr) {
394       if (strchr(cc_addr, '<') == NULL) {
395          chat("rcpt to:<%s>\r\n", cc_addr);
396       } else {
397          chat("rcpt to:%s\r\n", cc_addr);
398       }
399    }
400    Dmsg0(20, "Data\n");
401    chat("data\r\n");
402
403    /*
404     *  Send message header
405     */
406    fprintf(sfp, "From: %s\r\n", from_addr);
407    Dmsg1(10, "From: %s\r\n", from_addr);
408    if (subject) {
409       fprintf(sfp, "Subject: %s\r\n", subject);
410       Dmsg1(10, "Subject: %s\r\n", subject);
411    }
412    if (reply_addr) {
413       fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
414       Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
415    }
416    if (err_addr) {
417       fprintf(sfp, "Errors-To: %s\r\n", err_addr);
418       Dmsg1(10, "Errors-To: %s\r\n", err_addr);
419    }
420
421 #if defined(HAVE_WIN32)
422    DWORD dwSize = UNLEN + 1;
423    LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
424
425    if (GetUserName(lpszBuffer, &dwSize)) {
426       fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
427       Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
428    } else {
429       fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
430       Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
431    }
432 #else
433    if ((pwd = getpwuid(getuid())) == 0) {
434       fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
435       Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
436    } else {
437       fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
438       Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
439    }
440 #endif
441
442    fprintf(sfp, "To: %s", argv[0]);
443    Dmsg1(10, "To: %s", argv[0]);
444    for (i = 1; i < argc; i++) {
445       fprintf(sfp, ",%s", argv[i]);
446       Dmsg1(10, ",%s", argv[i]);
447    }
448
449    fprintf(sfp, "\r\n");
450    Dmsg0(10, "\r\n");
451    if (cc_addr) {
452       fprintf(sfp, "Cc: %s\r\n", cc_addr);
453       Dmsg1(10, "Cc: %s\r\n", cc_addr);
454    }
455
456    if (content_utf8) {
457       fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
458       Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
459    }
460
461    /* Add RFC822 date */
462    (void)localtime_r(&now, &tm);
463 #if defined(HAVE_WIN32)
464 #if defined(HAVE_MINGW)
465 __MINGW_IMPORT long     _dstbias;
466 #endif
467    long tzoffset = 0;
468
469    _tzset();
470
471    tzoffset = _timezone;
472    tzoffset += _dstbias;
473    tzoffset /= 60;
474
475    size_t length = strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", &tm);
476    sprintf(&buf[length], " %+2.2ld%2.2u", -tzoffset / 60, abs(tzoffset) % 60);
477 #else
478    strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", &tm);
479 #endif
480    fprintf(sfp, "Date: %s\r\n", buf);
481    Dmsg1(10, "Date: %s\r\n", buf);
482
483    fprintf(sfp, "\r\n");
484
485    /*
486     *  Send message body
487     */
488    lines = 0;
489    while (fgets(buf, sizeof(buf), stdin)) {
490       if (maxlines > 0 && ++lines > maxlines) {
491          Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
492          break;
493       }
494       buf[sizeof(buf)-1] = '\0';
495       buf[strlen(buf)-1] = '\0';
496       if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
497          fputs("..\r\n", sfp);
498       } else {                     /* pass body through unchanged */
499          fputs(buf, sfp);
500          fputs("\r\n", sfp);
501       }
502    }
503
504    if (lines > maxlines) {
505       Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
506       fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
507    }
508
509    /*
510     *  Send SMTP quit command
511     */
512    chat(".\r\n");
513    chat("quit\r\n");
514
515    /*
516     *  Go away gracefully ...
517     */
518    exit(0);
519 }