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