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