15 |
along with this program; if not, write to the Free Software |
along with this program; if not, write to the Free Software |
16 |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
17 |
|
|
|
|
|
|
/* FIXME: Those headers should be in "mail.h". */ |
|
18 |
#include <sys/types.h> |
#include <sys/types.h> |
19 |
#include <sys/stat.h> |
#include <sys/stat.h> |
20 |
#include <fcntl.h> |
#include <fcntl.h> |
28 |
/* |
/* |
29 |
* m[ail] address... |
* m[ail] address... |
30 |
if address is starting with |
if address is starting with |
31 |
'/' it is consider a file and the message is save to a file. |
|
32 |
'.' by extension dot is consider relative path. |
'/' it is considered a file and the message is saveed to a file; |
33 |
'|' a pipe the message is writent to the pipe. |
'.' it is considered to be a relative path; |
34 |
|
'|' it is considered to be a pipe, and the message is written to |
35 |
|
there; |
36 |
|
|
37 |
example: |
example: |
38 |
mail joe '| cat >/tmp/save' |
|
39 |
mail will be send to joe and the message save in /tmp/save. |
mail joe '| cat >/tmp/save' |
40 |
*/ |
|
41 |
|
mail will be sent to joe and the message saved in /tmp/save. */ |
42 |
|
|
43 |
int |
int |
44 |
mail_send (int argc, char **argv) |
mail_send (int argc, char **argv) |
45 |
{ |
{ |
46 |
compose_env_t env; |
compose_env_t env; |
47 |
int status; |
int status; |
48 |
|
int save_to = isupper (argv[0][0]); |
49 |
compose_init (&env); |
compose_init (&env); |
50 |
|
|
51 |
if (argc < 2) |
if (argc < 2) |
52 |
compose_header_set (&env, MU_HEADER_TO, ml_readline ((char *)"To: "), |
compose_header_set (&env, MU_HEADER_TO, ml_readline ("To: "), |
53 |
COMPOSE_REPLACE); |
COMPOSE_REPLACE); |
54 |
else |
else |
55 |
{ |
{ |
56 |
while (--argc) |
while (--argc) |
57 |
{ |
{ |
58 |
char *p = alias_expand (*++argv); |
char *p = *++argv; |
59 |
if (isfilename(p)) |
if (isfilename (p)) |
60 |
{ |
{ |
61 |
env.outfiles = realloc (env.outfiles, |
env.outfiles = realloc (env.outfiles, |
62 |
(env.nfiles+1)*sizeof (*(env.outfiles))); |
(env.nfiles + 1) * |
63 |
|
sizeof (*(env.outfiles))); |
64 |
if (env.outfiles) |
if (env.outfiles) |
65 |
{ |
{ |
66 |
env.outfiles[env.nfiles] = p; |
env.outfiles[env.nfiles] = p; |
69 |
} |
} |
70 |
else |
else |
71 |
{ |
{ |
72 |
compose_header_set (&env, MU_HEADER_TO, p, |
compose_header_set (&env, MU_HEADER_TO, p, COMPOSE_SINGLE_LINE); |
|
COMPOSE_SINGLE_LINE); |
|
73 |
free (p); |
free (p); |
74 |
} |
} |
75 |
} |
} |
77 |
|
|
78 |
if (util_getenv (NULL, "askcc", Mail_env_boolean, 0) == 0) |
if (util_getenv (NULL, "askcc", Mail_env_boolean, 0) == 0) |
79 |
compose_header_set (&env, MU_HEADER_CC, |
compose_header_set (&env, MU_HEADER_CC, |
80 |
ml_readline ((char *)"Cc: "), |
ml_readline ("Cc: "), COMPOSE_REPLACE); |
|
COMPOSE_REPLACE); |
|
81 |
if (util_getenv (NULL, "askbcc", Mail_env_boolean, 0) == 0) |
if (util_getenv (NULL, "askbcc", Mail_env_boolean, 0) == 0) |
82 |
compose_header_set (&env, MU_HEADER_BCC, |
compose_header_set (&env, MU_HEADER_BCC, |
83 |
ml_readline ((char *)"Bcc: "), |
ml_readline ("Bcc: "), COMPOSE_REPLACE); |
|
COMPOSE_REPLACE); |
|
84 |
|
|
85 |
if (util_getenv (NULL, "asksub", Mail_env_boolean, 0) == 0) |
if (util_getenv (NULL, "asksub", Mail_env_boolean, 0) == 0) |
86 |
compose_header_set (&env, MU_HEADER_SUBJECT, |
compose_header_set (&env, MU_HEADER_SUBJECT, |
87 |
ml_readline ((char *)"Subject: "), |
ml_readline ("Subject: "), COMPOSE_REPLACE); |
|
COMPOSE_REPLACE); |
|
88 |
else |
else |
89 |
{ |
{ |
90 |
char *p; |
char *p; |
91 |
if (util_getenv (&p, "subject", Mail_env_string, 0) == 0) |
if (util_getenv (&p, "subject", Mail_env_string, 0) == 0) |
92 |
compose_header_set (&env, MU_HEADER_SUBJECT, |
compose_header_set (&env, MU_HEADER_SUBJECT, p, COMPOSE_REPLACE); |
|
p, |
|
|
COMPOSE_REPLACE); |
|
93 |
} |
} |
94 |
|
|
95 |
status = mail_send0 (&env, isupper(argv[0][0])); |
status = mail_send0 (&env, save_to); |
96 |
compose_destroy (&env); |
compose_destroy (&env); |
97 |
return status; |
return status; |
98 |
} |
} |
99 |
|
|
100 |
void |
void |
101 |
compose_init (compose_env_t *env) |
compose_init (compose_env_t * env) |
102 |
{ |
{ |
103 |
memset (env, 0, sizeof (*env)); |
memset (env, 0, sizeof (*env)); |
104 |
} |
} |
105 |
|
|
106 |
int |
int |
107 |
compose_header_set (compose_env_t *env, char *name, char *value, |
compose_header_set (compose_env_t * env, char *name, char *value, int mode) |
|
int mode) |
|
108 |
{ |
{ |
109 |
int status; |
int status; |
110 |
char *old_value; |
char *old_value; |
118 |
|
|
119 |
switch (mode) |
switch (mode) |
120 |
{ |
{ |
121 |
case COMPOSE_REPLACE: |
case COMPOSE_REPLACE: |
122 |
case COMPOSE_APPEND: |
case COMPOSE_APPEND: |
123 |
status = header_set_value (env->header, name, value, mode); |
status = header_set_value (env->header, name, value, mode); |
124 |
break; |
break; |
125 |
|
|
126 |
case COMPOSE_SINGLE_LINE: |
case COMPOSE_SINGLE_LINE: |
127 |
if (!value || value[0] == 0) |
if (!value || value[0] == 0) |
128 |
return EINVAL; |
return EINVAL; |
129 |
|
|
130 |
if (header_aget_value (env->header, name, &old_value) == 0 |
if (header_aget_value (env->header, name, &old_value) == 0 |
131 |
&& old_value[0]) |
&& old_value[0]) |
132 |
{ |
{ |
143 |
} |
} |
144 |
|
|
145 |
char * |
char * |
146 |
compose_header_get (compose_env_t *env, char *name, char *defval) |
compose_header_get (compose_env_t * env, char *name, char *defval) |
147 |
{ |
{ |
148 |
char *p; |
char *p; |
149 |
|
|
150 |
if (header_aget_value (env->header, name, &p)) |
if (header_aget_value (env->header, name, &p)) |
151 |
p = defval; |
p = defval; |
152 |
return p; |
return p; |
153 |
} |
} |
154 |
|
|
155 |
void |
void |
156 |
compose_destroy (compose_env_t *env) |
compose_destroy (compose_env_t * env) |
157 |
{ |
{ |
158 |
header_destroy (&env->header, NULL); |
header_destroy (&env->header, NULL); |
159 |
if (env->outfiles) |
if (env->outfiles) |
178 |
mail. You must manually set the default for this environment variable |
mail. You must manually set the default for this environment variable |
179 |
by editing ROOTDIR/etc/mailx.rc to specify the mail agent of your |
by editing ROOTDIR/etc/mailx.rc to specify the mail agent of your |
180 |
choice. The default is sendmail, but it can be any command that takes |
choice. The default is sendmail, but it can be any command that takes |
181 |
addresses on the command line and message contents on standard input. |
addresses on the command line and message contents on standard input. */ |
|
*/ |
|
182 |
|
|
183 |
int |
int |
184 |
mail_send0 (compose_env_t *env, int save_to) |
mail_send0 (compose_env_t * env, int save_to) |
185 |
{ |
{ |
186 |
int done = 0; |
int done = 0; |
187 |
int fd; |
int fd; |
210 |
while (!done) |
while (!done) |
211 |
{ |
{ |
212 |
char *buf; |
char *buf; |
213 |
buf = ml_readline ((char *)" \b"); |
buf = ml_readline (" \b"); |
214 |
|
|
215 |
if (ml_got_interrupt ()) |
if (ml_got_interrupt ()) |
216 |
{ |
{ |
248 |
&& util_getenv (NULL, "dot", Mail_env_boolean, 0) == 0) |
&& util_getenv (NULL, "dot", Mail_env_boolean, 0) == 0) |
249 |
done = 1; |
done = 1; |
250 |
else if (util_getenv (&escape, "escape", Mail_env_string, 0) == 0 |
else if (util_getenv (&escape, "escape", Mail_env_string, 0) == 0 |
251 |
&& buf[0] == escape[0]) |
&& buf[0] == escape[0]) |
252 |
{ |
{ |
253 |
if (buf[1] == buf[0]) |
if (buf[1] == buf[0]) |
254 |
fprintf (env->file, "%s\n", buf+1); |
fprintf (env->file, "%s\n", buf + 1); |
255 |
else if (buf[1] == '.') |
else if (buf[1] == '.') |
256 |
done = 1; |
done = 1; |
257 |
else if (buf[1] == 'x') |
else if (buf[1] == 'x') |
267 |
|
|
268 |
ofile = env->file; |
ofile = env->file; |
269 |
|
|
270 |
if (argcv_get (buf+1, "", NULL, &argc, &argv) == 0) |
if (argcv_get (buf + 1, "", NULL, &argc, &argv) == 0) |
271 |
{ |
{ |
272 |
struct mail_command_entry entry; |
struct mail_command_entry entry; |
273 |
entry = util_find_entry (mail_escape_table, argv[0]); |
entry = util_find_entry (mail_escape_table, argv[0]); |
274 |
|
|
275 |
if (entry.escfunc) |
if (entry.escfunc) |
276 |
status = (*entry.escfunc)(argc, argv, env); |
status = (*entry.escfunc) (argc, argv, env); |
277 |
else |
else |
278 |
util_error ("Unknown escape %s", argv[0]); |
util_error ("Unknown escape %s", argv[0]); |
279 |
} |
} |
283 |
} |
} |
284 |
argcv_free (argc, argv); |
argcv_free (argc, argv); |
285 |
|
|
286 |
ofile = env->ofile; |
ofile = env->ofile; |
287 |
} |
} |
288 |
} |
} |
289 |
else |
else |
299 |
{ |
{ |
300 |
FILE *fp = fopen (getenv ("DEAD"), |
FILE *fp = fopen (getenv ("DEAD"), |
301 |
util_getenv (NULL, "appenddeadletter", |
util_getenv (NULL, "appenddeadletter", |
302 |
Mail_env_boolean, 0) == 0 ? |
Mail_env_boolean, 0) == 0 ? |
303 |
"a" : "w"); |
"a" : "w"); |
304 |
|
|
305 |
if (!fp) |
if (!fp) |
306 |
{ |
{ |
307 |
util_error ("can't open file %s: %s", getenv ("DEAD"), |
util_error ("can't open file %s: %s", getenv ("DEAD"), |
308 |
strerror (errno)); |
strerror (errno)); |
309 |
} |
} |
310 |
else |
else |
311 |
{ |
{ |
325 |
return 1; |
return 1; |
326 |
} |
} |
327 |
|
|
328 |
fclose (env->file); /* FIXME: freopen would be better */ |
fclose (env->file); /* FIXME: freopen would be better */ |
329 |
|
|
330 |
file = fopen (filename, "r"); |
/* Prepare the header */ |
331 |
if (file != NULL) |
header_set_value (env->header, "X-Mailer", argp_program_version, 1); |
332 |
|
|
333 |
|
if (util_header_expand (&env->header) == 0) |
334 |
{ |
{ |
335 |
mailer_t mailer; |
file = fopen (filename, "r"); |
336 |
message_t msg = NULL; |
if (file != NULL) |
337 |
|
{ |
338 |
|
mailer_t mailer; |
339 |
|
message_t msg = NULL; |
340 |
|
|
341 |
|
message_create (&msg, NULL); |
342 |
|
|
343 |
message_create (&msg, NULL); |
message_set_header (msg, env->header, NULL); |
344 |
|
|
345 |
/* Fill the header */ |
/* Fill the body. */ |
|
header_set_value (env->header, "X-Mailer", argp_program_version, 1); |
|
|
message_set_header (msg, env->header, NULL); |
|
|
|
|
|
/* Fill the body. */ |
|
|
{ |
|
|
body_t body = NULL; |
|
|
stream_t stream = NULL; |
|
|
off_t offset = 0; |
|
|
char *buf = NULL; |
|
|
size_t n = 0; |
|
|
message_get_body (msg, &body); |
|
|
body_get_stream (body, &stream); |
|
|
while (getline (&buf, &n, file) >= 0) |
|
346 |
{ |
{ |
347 |
size_t len = strlen (buf); |
body_t body = NULL; |
348 |
stream_write (stream, buf, len, offset, &n); |
stream_t stream = NULL; |
349 |
offset += len; |
off_t offset = 0; |
350 |
|
char *buf = NULL; |
351 |
|
size_t n = 0; |
352 |
|
message_get_body (msg, &body); |
353 |
|
body_get_stream (body, &stream); |
354 |
|
while (getline (&buf, &n, file) >= 0) |
355 |
|
{ |
356 |
|
size_t len = strlen (buf); |
357 |
|
stream_write (stream, buf, len, offset, &n); |
358 |
|
offset += len; |
359 |
|
} |
360 |
|
|
361 |
|
if (offset == 0) |
362 |
|
util_error ("Null message body; hope that's ok\n"); |
363 |
|
if (buf) |
364 |
|
free (buf); |
365 |
} |
} |
366 |
|
|
367 |
if (offset == 0) |
fclose (file); |
|
util_error("Null message body; hope that's ok\n"); |
|
|
if (buf) |
|
|
free (buf); |
|
|
} |
|
|
|
|
|
fclose (file); |
|
368 |
|
|
369 |
/* Save outgoing message */ |
/* Save outgoing message */ |
370 |
if (save_to) |
if (save_to) |
|
{ |
|
|
savefile = compose_header_get (env, MU_HEADER_TO, NULL); |
|
|
if (savefile) |
|
371 |
{ |
{ |
372 |
char *p = strchr (savefile, '@'); |
char *tmp = compose_header_get (env, MU_HEADER_TO, NULL); |
373 |
if (p) |
address_t addr = NULL; |
374 |
*p = 0; |
|
375 |
|
address_create (&addr, tmp); |
376 |
|
address_aget_email (addr, 1, &savefile); |
377 |
|
address_destroy (&addr); |
378 |
|
if (savefile) |
379 |
|
{ |
380 |
|
char *p = strchr (savefile, '@'); |
381 |
|
if (p) |
382 |
|
*p = 0; |
383 |
|
} |
384 |
} |
} |
385 |
} |
util_save_outgoing (msg, savefile); |
386 |
util_save_outgoing (msg, savefile); |
if (savefile) |
387 |
if (savefile) |
free (savefile); |
|
free (savefile); |
|
388 |
|
|
389 |
/* Check if we need to save the message to files or pipes. */ |
/* Check if we need to save the message to files or pipes. */ |
390 |
if (env->outfiles) |
if (env->outfiles) |
|
{ |
|
|
int i; |
|
|
for (i = 0; i < env->nfiles; i++) |
|
391 |
{ |
{ |
392 |
/* Pipe to a cmd. */ |
int i; |
393 |
if (env->outfiles[i][0] == '|') |
for (i = 0; i < env->nfiles; i++) |
|
msg_to_pipe (&(env->outfiles[i][1]), msg); |
|
|
/* Save to a file. */ |
|
|
else |
|
394 |
{ |
{ |
395 |
int status; |
/* Pipe to a cmd. */ |
396 |
mailbox_t mbx = NULL; |
if (env->outfiles[i][0] == '|') |
397 |
status = mailbox_create_default (&mbx, env->outfiles[i]); |
msg_to_pipe (&(env->outfiles[i][1]), msg); |
398 |
if (status == 0) |
/* Save to a file. */ |
399 |
|
else |
400 |
{ |
{ |
401 |
status = mailbox_open (mbx, MU_STREAM_WRITE |
int status; |
402 |
| MU_STREAM_CREAT); |
mailbox_t mbx = NULL; |
403 |
|
status = mailbox_create_default (&mbx, env->outfiles[i]); |
404 |
if (status == 0) |
if (status == 0) |
405 |
{ |
{ |
406 |
mailbox_append_message (mbx, msg); |
status = mailbox_open (mbx, MU_STREAM_WRITE |
407 |
mailbox_close (mbx); |
| MU_STREAM_CREAT); |
408 |
|
if (status == 0) |
409 |
|
{ |
410 |
|
mailbox_append_message (mbx, msg); |
411 |
|
mailbox_close (mbx); |
412 |
|
} |
413 |
|
mailbox_destroy (&mbx); |
414 |
} |
} |
415 |
mailbox_destroy (&mbx); |
if (status) |
416 |
|
util_error ("can't create mailbox %s", env->outfiles[i]); |
417 |
} |
} |
|
if (status) |
|
|
util_error("can't create mailbox %s", env->outfiles[i]); |
|
418 |
} |
} |
419 |
} |
} |
|
} |
|
420 |
|
|
421 |
/* Do we need to Send the message on the wire? */ |
/* Do we need to Send the message on the wire? */ |
422 |
if (compose_header_get (env, "to", NULL) |
if (compose_header_get (env, MU_HEADER_TO, NULL) |
423 |
|| compose_header_get (env, "cc", NULL) |
|| compose_header_get (env, MU_HEADER_CC, NULL) |
424 |
|| compose_header_get (env, "bcc", NULL)) |
|| compose_header_get (env, MU_HEADER_BCC, NULL)) |
|
{ |
|
|
char *sendmail; |
|
|
if (util_getenv (&sendmail, "sendmail", Mail_env_string, 0) == 0) |
|
425 |
{ |
{ |
426 |
int status = mailer_create (&mailer, sendmail); |
char *sendmail; |
427 |
if (status == 0) |
if (util_getenv (&sendmail, "sendmail", Mail_env_string, 0) == 0) |
428 |
{ |
{ |
429 |
if (util_getenv (NULL, "verbose", Mail_env_boolean, 0) == 0) |
int status = mailer_create (&mailer, sendmail); |
|
{ |
|
|
mu_debug_t debug = NULL; |
|
|
mailer_get_debug (mailer, &debug); |
|
|
mu_debug_set_level (debug, MU_DEBUG_TRACE|MU_DEBUG_PROT); |
|
|
} |
|
|
status = mailer_open (mailer, MU_STREAM_RDWR); |
|
430 |
if (status == 0) |
if (status == 0) |
431 |
{ |
{ |
432 |
mailer_send_message (mailer, msg, NULL, NULL); |
if (util_getenv (NULL, "verbose", Mail_env_boolean, 0) |
433 |
mailer_close (mailer); |
== 0) |
434 |
|
{ |
435 |
|
mu_debug_t debug = NULL; |
436 |
|
mailer_get_debug (mailer, &debug); |
437 |
|
mu_debug_set_level (debug, |
438 |
|
MU_DEBUG_TRACE | MU_DEBUG_PROT); |
439 |
|
} |
440 |
|
status = mailer_open (mailer, MU_STREAM_RDWR); |
441 |
|
if (status == 0) |
442 |
|
{ |
443 |
|
mailer_send_message (mailer, msg, NULL, NULL); |
444 |
|
mailer_close (mailer); |
445 |
|
} |
446 |
|
mailer_destroy (&mailer); |
447 |
} |
} |
448 |
mailer_destroy (&mailer); |
if (status != 0) |
449 |
|
msg_to_pipe (sendmail, msg); |
450 |
} |
} |
451 |
if (status != 0) |
else |
452 |
msg_to_pipe (sendmail, msg); |
util_error ("variable sendmail not set: no mailer"); |
453 |
} |
} |
454 |
else |
message_destroy (&msg, NULL); |
455 |
util_error ("variable sendmail not set: no mailer"); |
remove (filename); |
456 |
|
free (filename); |
457 |
|
return 0; |
458 |
} |
} |
|
message_destroy (&msg, NULL); |
|
|
remove (filename); |
|
|
free (filename); |
|
|
return 0; |
|
459 |
} |
} |
460 |
|
|
461 |
remove (filename); |
remove (filename); |
475 |
} |
} |
476 |
|
|
477 |
/* FIXME: Should probably be in util.c. */ |
/* FIXME: Should probably be in util.c. */ |
478 |
/* Call pope(cmd) and write the message to it. */ |
/* Call popen(cmd) and write the message to it. */ |
479 |
static void |
static void |
480 |
msg_to_pipe (const char *cmd, message_t msg) |
msg_to_pipe (const char *cmd, message_t msg) |
481 |
{ |
{ |