]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tools/bsmtp.c
Change to inner joins. The outer joins don't get us any more resulting records
[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 and included
11    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
293    /*
294     *  Determine SMTP server
295     */
296    if (mailhost == NULL) {
297       if ((cp = getenv("SMTPSERVER")) != NULL) {
298          mailhost = cp;
299       } else {
300          mailhost = "localhost";
301       }
302    }
303
304 #if defined(HAVE_WIN32)
305    WSADATA  wsaData;
306
307    _setmode(0, _O_BINARY);
308    WSAStartup(MAKEWORD(2,2), &wsaData);
309 #endif
310
311    /*
312     *  Find out my own host name for HELO;
313     *  if possible, get the fully qualified domain name
314     */
315    if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
316       Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno));
317       exit(1);
318    }
319    if ((hp = gethostbyname(my_hostname)) == NULL) {
320       Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname,
321          strerror(errno));
322       exit(1);
323    }
324    strcpy(my_hostname, hp->h_name);
325    Dmsg1(20, "My hostname is: %s\n", my_hostname);
326
327    /*
328     *  Determine from address.
329     */
330    if (from_addr == NULL) {
331 #if defined(HAVE_WIN32)
332       DWORD dwSize = UNLEN + 1;
333       LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
334
335       if (GetUserName(lpszBuffer, &dwSize)) {
336          sprintf(buf, "%s@%s", lpszBuffer, my_hostname);
337       } else {
338          sprintf(buf, "unknown-user@%s", my_hostname);
339       }
340 #else
341       if ((pwd = getpwuid(getuid())) == 0) {
342          sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
343       } else {
344          sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
345       }
346 #endif
347       from_addr = bstrdup(buf);
348    }
349    Dmsg1(20, "From addr=%s\n", from_addr);
350
351    /*
352     *  Connect to smtp daemon on mailhost.
353     */
354 hp:
355    if ((hp = gethostbyname(mailhost)) == NULL) {
356       Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost,
357          strerror(errno));
358       if (strcasecmp(mailhost, "localhost") != 0) {
359          Pmsg0(0, _("Retrying connection using \"localhost\".\n"));
360          mailhost = "localhost";
361          goto hp;
362       }
363       exit(1);
364    }
365
366    if (hp->h_addrtype != AF_INET) {
367       Pmsg1(0, _("Fatal error: Unknown address family for smtp host: %d\n"), hp->h_addrtype);
368       exit(1);
369    }
370    memset((char *)&sin, 0, sizeof(sin));
371    memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
372    sin.sin_family = hp->h_addrtype;
373    sin.sin_port = htons(mailport);
374 #if defined(HAVE_WIN32)
375    if ((s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
376       Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
377       exit(1);
378    }
379 #else
380    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
381       Pmsg1(0, _("Fatal socket error: ERR=%s\n"), strerror(errno));
382       exit(1);
383    }
384 #endif
385    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
386       Pmsg2(0, _("Fatal connect error to %s: ERR=%s\n"), mailhost, strerror(errno));
387       exit(1);
388    }
389    Dmsg0(20, "Connected\n");
390
391 #if defined(HAVE_WIN32)
392    int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY);
393    if (fdSocket == -1) {
394       Pmsg1(0, _("Fatal _open_osfhandle error: ERR=%s\n"), strerror(errno));
395       exit(1);
396    }
397
398    int fdSocket2 = dup(fdSocket);
399
400    if ((sfp = fdopen(fdSocket, "wb")) == NULL) {
401       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
402       exit(1);
403    }
404    if ((rfp = fdopen(fdSocket2, "rb")) == NULL) {
405       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
406       exit(1);
407    }
408 #else
409    if ((r = dup(s)) < 0) {
410       Pmsg1(0, _("Fatal dup error: ERR=%s\n"), strerror(errno));
411       exit(1);
412    }
413    if ((sfp = fdopen(s, "w")) == 0) {
414       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
415       exit(1);
416    }
417    if ((rfp = fdopen(r, "r")) == 0) {
418       Pmsg1(0, _("Fatal fdopen error: ERR=%s\n"), strerror(errno));
419       exit(1);
420    }
421 #endif
422
423    /*
424     *  Send SMTP headers.  Note, if any of the strings have a <
425     *   in them already, we do not enclose the string in < >, otherwise
426     *   we do.
427     */
428    get_response(); /* banner */
429    chat("helo %s\r\n", my_hostname);
430    chat("mail from:%s\r\n", cleanup_addr(from_addr, buf, sizeof(buf)));
431    
432    for (i = 0; i < argc; i++) {
433       Dmsg1(20, "rcpt to: %s\n", argv[i]);
434       chat("rcpt to:%s\r\n", cleanup_addr(argv[i], buf, sizeof(buf)));
435    }
436
437    if (cc_addr) {
438       chat("rcpt to:%s\r\n", cleanup_addr(cc_addr, buf, sizeof(buf)));
439    }
440    Dmsg0(20, "Data\n");
441    chat("data\r\n");
442
443    /*
444     *  Send message header
445     */
446    fprintf(sfp, "From: %s\r\n", from_addr);
447    Dmsg1(10, "From: %s\r\n", from_addr);
448    if (subject) {
449       fprintf(sfp, "Subject: %s\r\n", subject);
450       Dmsg1(10, "Subject: %s\r\n", subject);
451    }
452    if (reply_addr) {
453       fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
454       Dmsg1(10, "Reply-To: %s\r\n", reply_addr);
455    }
456    if (err_addr) {
457       fprintf(sfp, "Errors-To: %s\r\n", err_addr);
458       Dmsg1(10, "Errors-To: %s\r\n", err_addr);
459    }
460
461 #if defined(HAVE_WIN32)
462    DWORD dwSize = UNLEN + 1;
463    LPSTR lpszBuffer = (LPSTR)alloca(dwSize);
464
465    if (GetUserName(lpszBuffer, &dwSize)) {
466       fprintf(sfp, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
467       Dmsg2(10, "Sender: %s@%s\r\n", lpszBuffer, my_hostname);
468    } else {
469       fprintf(sfp, "Sender: unknown-user@%s\r\n", my_hostname);
470       Dmsg1(10, "Sender: unknown-user@%s\r\n", my_hostname);
471    }
472 #else
473    if ((pwd = getpwuid(getuid())) == 0) {
474       fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
475       Dmsg2(10, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
476    } else {
477       fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
478       Dmsg2(10, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
479    }
480 #endif
481
482    fprintf(sfp, "To: %s", argv[0]);
483    Dmsg1(10, "To: %s", argv[0]);
484    for (i = 1; i < argc; i++) {
485       fprintf(sfp, ",%s", argv[i]);
486       Dmsg1(10, ",%s", argv[i]);
487    }
488
489    fprintf(sfp, "\r\n");
490    Dmsg0(10, "\r\n");
491    if (cc_addr) {
492       fprintf(sfp, "Cc: %s\r\n", cc_addr);
493       Dmsg1(10, "Cc: %s\r\n", cc_addr);
494    }
495
496    if (content_utf8) {
497       fprintf(sfp, "Content-Type: text/plain; charset=UTF-8\r\n");
498       Dmsg0(10, "Content-Type: text/plain; charset=UTF-8\r\n");
499    }
500
501    get_date_string(buf, sizeof(buf));
502    fprintf(sfp, "Date: %s\r\n", buf);
503    Dmsg1(10, "Date: %s\r\n", buf);
504
505    fprintf(sfp, "\r\n");
506
507    /*
508     *  Send message body
509     */
510    lines = 0;
511    while (fgets(buf, sizeof(buf), stdin)) {
512       if (maxlines > 0 && ++lines > maxlines) {
513          Dmsg1(20, "skip line because of maxlines limit: %lu\n", maxlines);
514          break;
515       }
516       buf[sizeof(buf)-1] = '\0';
517       buf[strlen(buf)-1] = '\0';
518       if (buf[0] == '.' && buf[1] == '\0') { /* quote lone dots */
519          fputs("..\r\n", sfp);
520       } else {                     /* pass body through unchanged */
521          fputs(buf, sfp);
522          fputs("\r\n", sfp);
523       }
524    }
525
526    if (lines > maxlines) {
527       Dmsg1(10, "hit maxlines limit: %lu\n", maxlines);
528       fprintf(sfp, "\r\n[maximum of %lu lines exceeded, skipped %lu lines of output]\r\n", maxlines, lines-maxlines);
529    }
530
531    /*
532     *  Send SMTP quit command
533     */
534    chat(".\r\n");
535    chat("quit\r\n");
536
537    /*
538     *  Go away gracefully ...
539     */
540    exit(0);
541 }