1
2 /* The outbound mail functions */
3
4 extern "C" {
5 #include <sys/types.h>
6 #include <stdio.h>
7 #include <errno.h>
8 #include <unistd.h>
9 #include <ctype.h>
10 #include <pwd.h>
11 #include <limits.h>
12 }
13 #include "mime.h"
14 #include "terminal.h"
15 #include "rcfile.h"
16 #include "tempfile.h"
17 #include "outgoing.h"
18
19 #ifndef MAILER
20 #define MAILER "/usr/lib/sendmail -t"
21 #endif
22 #define EDITOR "vi +%d"
23 #define SIGFILE ".signature"
24
25 extern int verbose;
26
27 /* Bare bones mail delivery */
SendMail(char * file)28 static int SendMail(char *file)
29 {
30 char cmdline[2*PATH_MAX];
31 char *sentpath;
32
33 /* Save a copy of the mail, if desired */
34 sentpath = ExpandPath(GetStartVar("SentFile"));
35 if ( sentpath ) {
36 sprintf(cmdline, "echo From you >>%s", sentpath);
37 system(cmdline);
38 sprintf(cmdline, "echo Date: `date \"+%%a, %%d %%b %%Y %%H:%%M:%%S\"` >>%s", sentpath);
39 system(cmdline);
40 sprintf(cmdline, "cat %s >>%s", file, sentpath);
41 system(cmdline);
42 delete[] sentpath;
43 }
44
45 sprintf(cmdline, "%s <%s", MAILER, file);
46 return(system(cmdline));
47 }
48
49 /***************************************************************************/
50 /* Helper routines for the mail functions */
51
LocalAddr(char * addr)52 static char *LocalAddr(char *addr)
53 {
54 struct passwd *pw;
55 char *fulladdr;
56 char *domain, domainbuf[BUFSIZ];
57
58 /* Inet address: user@host.net */
59 if ( strchr(addr, '@') != NULL )
60 return(NULL);
61
62 /* Local address, otherwise, so look in password file */
63 if ( (pw=getpwnam(addr)) == NULL )
64 return(NULL);
65
66 /* Slight optimization :-) */
67 domainbuf[0] = '@';
68 if ( (domain=GetStartVar("MailDomain")) == NULL ) {
69 if ( (getdomainname(&domainbuf[1], BUFSIZ-2) < 0) ||
70 (strcmp(&domainbuf[1], "(none)") == 0) )
71 domain = "";
72 else
73 domain = domainbuf;
74 } else {
75 domainbuf[1] = '\0';
76 strncat(domainbuf, domain, BUFSIZ-2);
77 domain = domainbuf;
78 }
79
80 /* Assemble the full name and address */
81 fulladdr = new char[1+strlen(pw->pw_gecos)+1+1+
82 strlen(addr)+strlen(domain)+1];
83 sprintf(fulladdr, "(%s) %s%s", pw->pw_gecos, addr, domain);
84 return(fulladdr);
85 }
86
87 /* Possible perform line wrapping? */
WriteAddrs(char * Field,char * Addrs,FILE * output)88 static int WriteAddrs(char *Field, char *Addrs, FILE *output)
89 {
90 MIME_body *aliases = NULL;
91 char *ptr, *alias;
92
93 /* Print the "To: " caption */
94 fprintf(output, Field);
95
96 /* Get any possible aliases we have */
97 if ( startup )
98 aliases = startup->ByName("aliases");
99
100 /* Run along the line, split it into addresses */
101 while ( *Addrs && isspace(*Addrs) )
102 ++Addrs;
103
104 ptr = Addrs;
105 while ( *Addrs ) {
106 if ( ! *ptr || (*ptr == ',') ) {
107 if ( *ptr ) {
108 *ptr = '\0';
109 do {
110 ++ptr;
111 } while ( *ptr && (isspace(*ptr) || *ptr == ',') );
112 }
113 if ( aliases &&
114 (alias=(char *)aliases->GetField(Addrs)) ) {
115 if ( fprintf(output, "%s", alias) <= 0 )
116 return(-1);
117 } else if ( (alias=LocalAddr(Addrs)) ) {
118 if ( fprintf(output, "%s", alias) <= 0 ) {
119 delete[] alias;
120 return(-1);
121 }
122 delete[] alias;
123 } else {
124 if ( fprintf(output, "%s", Addrs) <= 0 )
125 return(-1);
126 }
127 if ( *ptr != '\0' )
128 fprintf(output, ", ");
129 Addrs = ptr;
130 } else
131 ++ptr;
132 }
133 fprintf(output, "\n");
134 return(1);
135 }
136
137 /* Add any extra headers to the output */
AutoHeader(FILE * output)138 static int AutoHeader(FILE *output)
139 {
140 MIME_body *headers;
141 char *header_key;
142 char *header_val;
143 int written = 0;
144
145 if ( ! startup || ((headers=startup->ByName("headers")) == NULL) )
146 return(0);
147
148 headers->GetHeader(NULL);
149 while ( (header_val=headers->GetHeader(&header_key)) ) {
150 /* Don't include MIME Content headers */
151 if (!strncasecmp(header_key, "Content-", strlen("Content-")))
152 continue;
153
154 fprintf(output, "%s: %s\n", header_key, header_val);
155 ++written;
156 }
157 return(written);
158 }
159
160 /* Write the signature file to the message */
LoadSig(FILE * output)161 static void LoadSig(FILE *output)
162 {
163 FILE *input;
164 char buffer[BUFSIZ];
165 char *home;
166 char *filename;
167
168 if ( (home=getenv("HOME")) == NULL )
169 return;
170
171 /* Open the signature file */
172 filename = new char[strlen(home)+1+strlen(SIGFILE)+1];
173 sprintf(filename, "%s/%s", home, SIGFILE);
174 if ( (input=fopen(filename, "r")) == NULL ) {
175 delete[] filename;
176 return;
177 }
178 delete[] filename;
179
180 /* Copy it to the mail message */
181 while ( fgets(buffer, BUFSIZ-1, input) )
182 fputs(buffer, output);
183 fclose(input);
184 }
185
AttachFile(char * & message,char * file,int is_mime)186 static int AttachFile(char *&message, char *file, int is_mime)
187 {
188 IObottle *feeder;
189 MIME_body *body;
190 char *endings[] = { NULL }; /* No explicit boundaries */
191 int okay, olderr;
192
193 /* First open an IObottle on the message */
194 feeder = new IObottle(message);
195 if ( feeder->fail() || feeder->eof() ) {
196 delete feeder;
197 return(-1);
198 }
199
200 /* Now turn the message into a MIME body */
201 body = new MIME_body(feeder, endings);
202
203 /* Try adding the file */
204 if ( (okay = body->AddPart(file, is_mime)) > -1 ) {
205 if ( strcmp(message, body->RawPath()) != 0 ) {
206 /* We no longer need the original message */
207 feeder->temp(1);
208
209 /* Set up the message as the new raw file */
210 delete[] message;
211 message = new char[strlen(body->RawPath())+1];
212 strcpy(message, body->RawPath());
213 }
214 }
215 olderr = errno;
216 delete body;
217 delete feeder;
218 errno = olderr;
219 return(okay);
220 }
221
222 /* File tab-completion function */
223 extern char *file_complete(char *sofar); /* From handlemail.cc */
224
225 /* We pass in the outgoing file by reference, so that we can modify it */
SendMenu(char * & outgoing,char command,int offset,Terminal * tty)226 static int SendMenu(char *&outgoing, char command, int offset, Terminal *tty)
227 {
228 char buffer[BUFSIZ];
229 char *editor;
230 int done = 0;
231 int retval = 0;
232
233 /* Choose your editor */
234 if ( ((editor=getenv("EDITOR")) == NULL) &&
235 ((editor=getenv("VISUAL")) == NULL) )
236 editor = EDITOR;
237
238 /* Run the menu loop */
239 while ( ! done ) {
240 if ( command == '\0' ) {
241 command = tty->promptchar(0,
242 "[S]end, [e]dit, [a]dd attachment, [n]one of these: ");
243 }
244 switch (command) {
245 case 'E':
246 case 'e':
247 sprintf(buffer, "%s %s", editor, outgoing);
248 tty->system(0, buffer, offset);
249 break;
250 case 'A':
251 case 'a':
252 /* We don't support attachments yet */
253 tty->fillprompt(buffer, BUFSIZ,
254 "Enter file to attach: ", NULL, file_complete);
255 if ( buffer[0] == '\0' ) {
256 tty->status(0, "Attachment canceled.");
257 sleep(1);
258 break;
259 }
260 if ( AttachFile(outgoing, buffer, 0) < 0 ) {
261 tty->status(0, "Attachment failed: %s",
262 strerror(errno));
263 tty->waitchar();
264 }
265 break;
266 case 'N':
267 case 'n':
268 case 'q':
269 tty->status(0, "Message cancelled.");
270 sleep(1);
271 done = 1;
272 break;
273 case 'S':
274 case 's':
275 default:
276 /* Send mail and break out */
277 tty->status(0, "Sending mail...");
278 retval = SendMail(outgoing);
279 tty->status(0, "Mail sent!");
280 sleep(1);
281 done = 1;
282 break;
283 }
284 command = '\0';
285
286 }
287 return(retval);
288 }
289
290 /***************************************************************************/
291 /* Hot potato, we've got mail! */
292
293 /* Curses menu mailing */
NewMail(char * To,char * Cc,const char * InReplyTo,const char * Subject,int do_edit,FILE * incfile,Terminal * tty,int use_signature)294 int NewMail(char *To, char *Cc, const char *InReplyTo, const char *Subject,
295 int do_edit, FILE *incfile, Terminal *tty, int use_signature)
296 {
297 char *outbound;
298 char temp_name[PATH_MAX];
299 FILE *output;
300 int hlen, lines;
301 int retval = 0;
302
303 /* Open a temporary file for processing */
304 if ( (output=create_tempfile("w", temp_name)) == NULL ) {
305 tty->status(0, "Couldn't create temp file: %s", strerror(errno));
306 tty->waitchar();
307 return(-1);
308 }
309
310 /* Write the header and then one blank line to start */
311 hlen = 0;
312 if ( (lines=WriteAddrs("To: ", To, output)) < 0 ) {
313 tty->status(0, "Write error on tmpfile %s: %s",
314 temp_name, strerror(errno));
315 tty->waitchar();
316 (void) unlink(temp_name);
317 return(-1);
318 }
319 hlen += lines;
320 if ( Cc ) {
321 if ( (lines=WriteAddrs("Cc: ", Cc, output)) < 0 ) {
322 tty->status(0, "Write error on tmpfile %s: %s",
323 temp_name, strerror(errno));
324 tty->waitchar();
325 (void) unlink(temp_name);
326 return(-1);
327 }
328 hlen += lines;
329 }
330 if ( InReplyTo ) {
331 fprintf(output, "In-Reply-To: %s\n", InReplyTo);
332 ++hlen;
333 }
334 fprintf(output, "Subject: %s\n", Subject);
335 ++hlen;
336 hlen += AutoHeader(output);
337 fprintf(output, "\n");
338 ++hlen;
339
340 if ( incfile ) {
341 char buffer[BUFSIZ];
342 while ( fgets(buffer, BUFSIZ-1, incfile) ) {
343 if ( fputs(buffer, output) == EOF ) {
344 tty->status(0, "Write error on tmpfile %s: %s",
345 temp_name, strerror(errno));
346 tty->waitchar();
347 (void) unlink(temp_name);
348 return(-1);
349 }
350 }
351 }
352
353 /* Print an extra line for the body */
354 fprintf(output, "\n");
355
356 /* Load up a .signature file, if one exists */
357 if ( use_signature ) {
358 LoadSig(output);
359 }
360 fclose(output);
361
362 /* Save the name of the tmp_file for later deletion */
363 outbound = new char[strlen(temp_name)+1];
364 strcpy(outbound, temp_name);
365
366 /* Send (or abort) the mail */
367 SendMenu(outbound, (do_edit ? 'e' : '\0'), hlen+1, tty);
368
369 /* Clean up! We're done! :) */
370 (void) unlink(outbound);
371 delete[] outbound;
372 return(retval);
373 }
374
375 /* Command-line mailing */
NewMail(char * ToArray[],const char * Subject)376 int NewMail(char *ToArray[], const char *Subject)
377 {
378 Terminal *tty;
379 char *To;
380 int i, len;
381 int retval;
382
383 /* Create the "To: " line */
384 for ( i=0, len=0; ToArray[i]; ++i ) {
385 if ( i > 0 )
386 len += 2;
387 len += strlen(ToArray[i]);
388 }
389 To = new char[len+1];
390
391 *To = '\0';
392 for ( i=0; ToArray[i]; ++i ) {
393 if ( i > 0 )
394 strcat(To, ", ");
395 strcat(To, ToArray[i]);
396 }
397
398 /* Are we sending a file? */
399 if ( isatty(0) ) {
400 char subject[BUFSIZ];
401
402 tty = new Terminal(1);
403 if ( Subject == NULL ) {
404 tty->fillprompt(subject,BUFSIZ,"Subject: ",NULL,NULL);
405 Subject = subject;
406 }
407 retval = NewMail(To, NULL, NULL, Subject, 1, NULL, tty);
408 delete tty;
409 } else {
410 char temp_name[PATH_MAX];
411 FILE *output;
412 char line[BUFSIZ];
413
414 /* Manually create the message */
415 if ( (output=create_tempfile("w", temp_name)) == NULL ) {
416 fprintf(stderr, "Couldn't write to temp file: %s\n", strerror(errno));
417 return(-1);
418 }
419
420 /* Write the headers */
421 fprintf(output, "To: %s\n", To);
422 if ( ! Subject )
423 Subject = "No subject <file transmission>";
424 fprintf(output, "Subject: %s\n", Subject);
425 AutoHeader(output);
426 fprintf(output, "\n");
427
428 /* Write the file */
429 while ( fgets(line, BUFSIZ-1, stdin) )
430 fputs(line, output);
431 fclose(output);
432 printf("Sending mail...");
433 fflush(stdout);
434 retval = SendMail(temp_name);
435 printf("done.\n");
436 (void) unlink(temp_name);
437 }
438 return(retval);
439 }
440