]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tools/bsmtp.c
Make mark run *MUCH* faster in restore tree + update copyright on changed files
[bacula/bacula] / bacula / src / tools / bsmtp.c
1 /*
2    Copyright (C) 2000-2004 Kern Sibbald and John Walker
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 as
6    published by the Free Software Foundation; either version 2 of
7    the License, or (at your option) any later version.
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 GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public
15    License along with this program; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
17    MA 02111-1307, USA.
18
19  */
20
21 /*
22    Derived from a SMTPclient:
23
24        SMTPclient -- simple SMTP client
25
26        Copyright (C) 1997 Ralf S. Engelschall, All Rights Reserved.
27        rse@engelschall.com
28        www.engelschall.com
29
30    Kern Sibbald, July 2001
31   
32    Version $Id$
33     
34  */
35
36 #include "bacula.h"
37 #include "jcr.h"
38
39 #ifndef MAXSTRING
40 #define MAXSTRING 254
41 #endif
42
43 static FILE *sfp;
44 static FILE *rfp;
45
46 static char *from_addr = NULL;
47 static char *cc_addr = NULL;
48 static char *subject = NULL;
49 static char *err_addr = NULL;
50 static char *mailhost = NULL;
51 static char *reply_addr = NULL;
52 static int mailport = 25;
53 static char my_hostname[MAXSTRING];
54
55
56 /*
57  *  examine message from server 
58  */
59 static void get_response(void)
60 {
61     char buf[MAXSTRING];
62
63     Dmsg0(50, "Calling fgets on read socket rfp.\n");
64     while (fgets(buf, sizeof(buf), rfp)) {
65         buf[strlen(buf)-1] = 0;
66         Dmsg2(10, "%s --> %s\n", mailhost, buf);
67         if (!isdigit((int)buf[0]) || buf[0] > '3') {
68             Pmsg2(0, "Fatal malformed reply from %s: %s\n", mailhost, buf);
69             exit(1);
70         }
71         if (buf[4] != '-') {
72             break;
73         }
74     }
75     return;
76 }
77
78 /*
79  *  say something to server and check the response
80  */
81 static void chat(char *fmt, ...)
82 {
83     va_list ap;
84
85     va_start(ap, fmt);
86     vfprintf(sfp, fmt, ap);
87     if (debug_level >= 10) {
88        fprintf(stdout, "%s --> ", my_hostname); 
89        vfprintf(stdout, fmt, ap);
90     }
91     va_end(ap);
92   
93     fflush(sfp);
94     if (debug_level >= 10) {
95        fflush(stdout);
96     }
97     get_response();
98 }
99
100
101 static void usage()
102 {
103    fprintf(stderr,
104 "\n"
105 "Usage: bsmtp [-f from] [-h mailhost] [-s subject] [-c copy] [recepient ...]\n"
106 "       -c          set the Cc: field\n"
107 "       -dnn        set debug level to nn\n"
108 "       -f          set the From: field\n"
109 "       -h          use mailhost:port as the SMTP server\n"
110 "       -s          set the Subject: field\n"
111 "       -?          print this message.\n"  
112 "\n");
113
114    exit(1);
115 }
116
117
118 /*********************************************************************
119  *
120  *  Program to send email
121  */
122 int main (int argc, char *argv[])
123 {
124     char buf[MAXSTRING];
125     struct sockaddr_in sin;
126     struct hostent *hp;
127     int s, r, i, ch;
128     struct passwd *pwd;
129     char *cp, *p;
130     time_t now = time(NULL);
131     struct tm tm;
132
133    my_name_is(argc, argv, "bsmtp");
134
135    while ((ch = getopt(argc, argv, "c:d:f:h:r:s:?")) != -1) {
136       switch (ch) {
137       case 'c':                    
138          Dmsg1(20, "cc=%s\n", optarg);
139          cc_addr = optarg;
140          break;
141
142       case 'd':                    /* set debug level */
143          debug_level = atoi(optarg);
144          if (debug_level <= 0) {
145             debug_level = 1; 
146          }
147          Dmsg1(20, "Debug level = %d\n", debug_level);
148          break;
149
150       case 'f':                    /* from */
151          from_addr = optarg;
152          break;
153
154       case 'h':                    /* smtp host */
155          Dmsg1(20, "host=%s\n", optarg);
156          p = strchr(optarg, ':');
157          if (p) {
158             *p++ = 0;
159             mailport = atoi(p);
160          }
161          mailhost = optarg;
162          break;
163
164       case 's':                    /* subject */
165          Dmsg1(20, "subject=%s\n", optarg);
166          subject = optarg;
167          break;
168
169       case 'r':                    /* reply address */
170          reply_addr = optarg;
171          break;
172
173       case '?':
174       default:
175          usage();
176
177       }  
178    }
179    argc -= optind;
180    argv += optind;
181
182    if (argc < 1) {
183       Pmsg0(0, "Fatal error: no recipient given.\n");
184       usage();
185       exit(1);
186    }
187
188    /*
189     *  Determine SMTP server
190     */
191    if (mailhost == NULL) {
192       if ((cp = getenv("SMTPSERVER")) != NULL) {
193          mailhost = cp;
194       } else {
195          mailhost = "localhost";
196       }
197    }
198
199    /*
200     *  Find out my own host name for HELO; 
201     *  if possible, get the fully qualified domain name
202     */
203    if (gethostname(my_hostname, sizeof(my_hostname) - 1) < 0) {
204       Pmsg1(0, "Fatal gethostname error: ERR=%s\n", strerror(errno));
205       exit(1);
206    }
207    if ((hp = gethostbyname(my_hostname)) == NULL) {
208       Pmsg2(0, "Fatal gethostbyname for myself failed \"%s\": ERR=%s\n", my_hostname,
209          strerror(errno));
210       exit(1);
211    }
212    strcpy(my_hostname, hp->h_name);
213    Dmsg1(20, "My hostname is: %s\n", my_hostname);
214
215    /*
216     *  Determine from address.
217     */
218    if (from_addr == NULL) {
219       if ((pwd = getpwuid(getuid())) == 0) {
220          sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname);
221       } else {
222          sprintf(buf, "%s@%s", pwd->pw_name, my_hostname);
223       }
224       from_addr = bstrdup(buf);
225    }
226    Dmsg1(20, "From addr=%s\n", from_addr);
227
228    /*
229     *  Connect to smtp daemon on mailhost.
230     */
231 hp:
232    if ((hp = gethostbyname(mailhost)) == NULL) {
233       Pmsg2(0, "Error unknown mail host \"%s\": ERR=%s\n", mailhost,
234          strerror(errno));
235       if (strcasecmp(mailhost, "localhost") != 0) {
236          Pmsg0(0, "Retrying connection using \"localhost\".\n");
237          mailhost = "localhost";
238          goto hp;
239       }
240       exit(1);
241    }
242
243    if (hp->h_addrtype != AF_INET) {
244       Pmsg1(0, "Fatal error: Unknown address family for smtp host: %d\n", hp->h_addrtype);
245       exit(1);
246    }
247    memset((char *)&sin, 0, sizeof(sin));
248    memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
249    sin.sin_family = hp->h_addrtype;
250    sin.sin_port = htons(mailport);
251    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
252       Pmsg1(0, "Fatal socket error: ERR=%s\n", strerror(errno));
253       exit(1);
254    }
255    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
256       Pmsg2(0, "Fatal connect error to %s: ERR=%s\n", mailhost, strerror(errno));
257       exit(1);
258    }
259    Dmsg0(20, "Connected\n");
260    if ((r = dup(s)) < 0) {
261       Pmsg1(0, "Fatal dup error: ERR=%s\n", strerror(errno));
262       exit(1);
263    }
264    if ((sfp = fdopen(s, "w")) == 0) {
265       Pmsg1(0, "Fatal fdopen error: ERR=%s\n", strerror(errno));
266       exit(1);
267    }
268    if ((rfp = fdopen(r, "r")) == 0) {
269       Pmsg1(0, "Fatal fdopen error: ERR=%s\n", strerror(errno));
270       exit(1);
271    }
272
273    /* 
274     *  Send SMTP headers
275     */
276    get_response(); /* banner */
277    chat("helo %s\r\n", my_hostname);
278    chat("mail from: <%s>\r\n", from_addr);
279
280    for (i = 0; i < argc; i++) {
281       Dmsg1(20, "rcpt to: %s\n", argv[i]);
282       chat("rcpt to: <%s>\r\n", argv[i]);
283    }
284
285    if (cc_addr) {
286       chat("rcpt to: <%s>\r\n", cc_addr);
287    }
288    Dmsg0(20, "Data\n");
289    chat("data\r\n");
290
291    /* 
292     *  Send message header
293     */
294    fprintf(sfp, "From: %s\r\n", from_addr);
295    if (subject) {
296       fprintf(sfp, "Subject: %s\r\n", subject);
297    }
298    if (reply_addr) {
299       fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
300    }
301    if (err_addr) {
302       fprintf(sfp, "Errors-To: %s\r\n", err_addr);
303    }
304    if ((pwd = getpwuid(getuid())) == 0) {
305       fprintf(sfp, "Sender: userid-%d@%s\r\n", (int)getuid(), my_hostname);
306    } else {
307       fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_hostname);
308    }
309
310    fprintf(sfp, "To: %s", argv[0]);
311    for (i = 1; i < argc; i++) {
312       fprintf(sfp, ",%s", argv[i]);
313    }
314
315    fprintf(sfp, "\r\n");
316    if (cc_addr) {
317       fprintf(sfp, "Cc: %s\r\n", cc_addr);
318    }
319
320    /* Add RFC822 date */
321    localtime_r(&now, &tm);
322    strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", &tm);
323    fprintf(sfp, "Date: %s\r\n", buf);
324
325    fprintf(sfp, "\r\n");
326
327    /* 
328     *  Send message body 
329     */
330    while (fgets(buf, sizeof(buf), stdin)) {
331       buf[strlen(buf)-1] = 0;
332       if (strcmp(buf, ".") == 0) { /* quote lone dots */
333          fprintf(sfp, "..\r\n");
334       } else {                     /* pass body through unchanged */
335          fprintf(sfp, "%s\r\n", buf);
336       }
337    }
338
339    /* 
340     *  Send SMTP quit command
341     */
342    chat(".\r\n");
343    chat("quit\r\n");
344
345    /* 
346     *  Go away gracefully ...
347     */
348    exit(0);
349 }