File: | src/usr.bin/mail/collect.c |
Warning: | line 68, column 2 Value stored to 'lastlong' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: collect.c,v 1.34 2014/01/17 18:42:30 okan Exp $ */ |
2 | /* $NetBSD: collect.c,v 1.9 1997/07/09 05:25:45 mikel Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (c) 1980, 1993 |
6 | * The Regents of the University of California. All rights reserved. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * 3. Neither the name of the University nor the names of its contributors |
17 | * may be used to endorse or promote products derived from this software |
18 | * without specific prior written permission. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
30 | * SUCH DAMAGE. |
31 | */ |
32 | |
33 | /* |
34 | * Mail -- a mail program |
35 | * |
36 | * Collect input from standard input, handling |
37 | * ~ escapes. |
38 | */ |
39 | |
40 | #include "rcv.h" |
41 | #include "extern.h" |
42 | |
43 | /* |
44 | * Read a message from standard output and return a read file to it |
45 | * or NULL on error. |
46 | */ |
47 | |
48 | /* |
49 | * The following hokiness with global variables is so that on |
50 | * receipt of an interrupt signal, the partial message can be salted |
51 | * away on dead.letter. |
52 | */ |
53 | static FILE *collf; /* File for saving away */ |
54 | static int hadintr; /* Have seen one SIGINT so far */ |
55 | |
56 | FILE * |
57 | collect(struct header *hp, int printheaders) |
58 | { |
59 | FILE *fbuf; |
60 | int lc, cc, fd, c, t, lastlong, rc, sig; |
61 | int escape, eofcount, longline; |
62 | char getsub; |
63 | char linebuf[LINESIZE1024], tempname[PATHSIZE1024], *cp; |
64 | |
65 | collf = NULL((void *)0); |
66 | eofcount = 0; |
67 | hadintr = 0; |
68 | lastlong = 0; |
Value stored to 'lastlong' is never read | |
69 | longline = 0; |
70 | if ((cp = value("escape")) != NULL((void *)0)) |
71 | escape = *cp; |
72 | else |
73 | escape = ESCAPE'~'; |
74 | noreset++; |
75 | |
76 | (void)snprintf(tempname, sizeof(tempname), |
77 | "%s/mail.RsXXXXXXXXXX", tmpdir); |
78 | if ((fd = mkstemp(tempname)) == -1 || |
79 | (collf = Fdopen(fd, "w+")) == NULL((void *)0)) { |
80 | warn("%s", tempname); |
81 | goto err; |
82 | } |
83 | (void)rm(tempname); |
84 | |
85 | /* |
86 | * If we are going to prompt for a subject, |
87 | * refrain from printing a newline after |
88 | * the headers (since some people mind). |
89 | */ |
90 | t = GTO1|GSUBJECT2|GCC4|GNL16; |
91 | getsub = 0; |
92 | if (hp->h_subject == NULL((void *)0) && value("interactive") != NULL((void *)0) && |
93 | (value("ask") != NULL((void *)0) || value("asksub") != NULL((void *)0))) |
94 | t &= ~GNL16, getsub++; |
95 | if (printheaders) { |
96 | puthead(hp, stdout(&__sF[1]), t); |
97 | fflush(stdout(&__sF[1])); |
98 | } |
99 | if (getsub && gethfromtty(hp, GSUBJECT2) == -1) |
100 | goto err; |
101 | |
102 | if (0) { |
103 | cont: |
104 | /* Come here for printing the after-suspend message. */ |
105 | if (isatty(0)) { |
106 | puts("(continue)"); |
107 | fflush(stdout(&__sF[1])); |
108 | } |
109 | } |
110 | for (;;) { |
111 | c = readline(stdin(&__sF[0]), linebuf, LINESIZE1024, &sig); |
112 | |
113 | /* Act on any signal caught during readline() ignoring 'c' */ |
114 | switch (sig) { |
115 | case 0: |
116 | break; |
117 | case SIGINT2: |
118 | if (collabort()) |
119 | goto err; |
120 | continue; |
121 | case SIGHUP1: |
122 | rewind(collf); |
123 | savedeadletter(collf); |
124 | /* |
125 | * Let's pretend nobody else wants to clean up, |
126 | * a true statement at this time. |
127 | */ |
128 | exit(1); |
129 | default: |
130 | /* Stopped due to job control */ |
131 | (void)kill(0, sig); |
132 | goto cont; |
133 | } |
134 | |
135 | /* No signal, check for error */ |
136 | if (c < 0) { |
137 | if (value("interactive") != NULL((void *)0) && |
138 | value("ignoreeof") != NULL((void *)0) && ++eofcount < 25) { |
139 | puts("Use \".\" to terminate letter"); |
140 | continue; |
141 | } |
142 | break; |
143 | } |
144 | lastlong = longline; |
145 | longline = (c == LINESIZE1024 - 1); |
146 | eofcount = 0; |
147 | hadintr = 0; |
148 | if (linebuf[0] == '.' && linebuf[1] == '\0' && |
149 | value("interactive") != NULL((void *)0) && !lastlong && |
150 | (value("dot") != NULL((void *)0) || value("ignoreeof") != NULL((void *)0))) |
151 | break; |
152 | if (linebuf[0] != escape || value("interactive") == NULL((void *)0) || |
153 | lastlong) { |
154 | if (putline(collf, linebuf, !longline) < 0) |
155 | goto err; |
156 | continue; |
157 | } |
158 | c = (unsigned char)linebuf[1]; |
159 | switch (c) { |
160 | default: |
161 | /* |
162 | * On double escape, just send the single one. |
163 | * Otherwise, it's an error. |
164 | */ |
165 | if (c == escape) { |
166 | if (putline(collf, &linebuf[1], !longline) < 0) |
167 | goto err; |
168 | else |
169 | break; |
170 | } |
171 | puts("Unknown tilde escape."); |
172 | break; |
173 | case '!': |
174 | /* |
175 | * Shell escape, send the balance of the |
176 | * line to sh -c. |
177 | */ |
178 | shell(&linebuf[2]); |
179 | break; |
180 | case ':': |
181 | case '_': |
182 | /* |
183 | * Escape to command mode, but be nice! |
184 | */ |
185 | execute(&linebuf[2], 1); |
186 | goto cont; |
187 | case '.': |
188 | /* |
189 | * Simulate end of file on input. |
190 | */ |
191 | goto out; |
192 | case 'q': |
193 | /* |
194 | * Force a quit of sending mail. |
195 | * Act like an interrupt happened. |
196 | */ |
197 | hadintr++; |
198 | collabort(); |
199 | fputs("Interrupt\n", stderr(&__sF[2])); |
200 | goto err; |
201 | case 'x': |
202 | /* |
203 | * Force a quit of sending mail. |
204 | * Do not save the message. |
205 | */ |
206 | goto err; |
207 | case 'h': |
208 | /* |
209 | * Grab a bunch of headers. |
210 | */ |
211 | grabh(hp, GTO1|GSUBJECT2|GCC4|GBCC8); |
212 | goto cont; |
213 | case 't': |
214 | /* |
215 | * Add to the To list. |
216 | */ |
217 | hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO1)); |
218 | break; |
219 | case 's': |
220 | /* |
221 | * Set the Subject list. |
222 | */ |
223 | cp = &linebuf[2]; |
224 | while (isspace((unsigned char)*cp)) |
225 | cp++; |
226 | hp->h_subject = savestr(cp); |
227 | break; |
228 | case 'c': |
229 | /* |
230 | * Add to the CC list. |
231 | */ |
232 | hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC4)); |
233 | break; |
234 | case 'b': |
235 | /* |
236 | * Add stuff to blind carbon copies list. |
237 | */ |
238 | hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC8)); |
239 | break; |
240 | case 'd': |
241 | linebuf[2] = '\0'; |
242 | strlcat(linebuf, getdeadletter(), sizeof(linebuf)); |
243 | /* fall into . . . */ |
244 | case 'r': |
245 | case '<': |
246 | /* |
247 | * Invoke a file: |
248 | * Search for the file name, |
249 | * then open it and copy the contents to collf. |
250 | */ |
251 | cp = &linebuf[2]; |
252 | while (isspace((unsigned char)*cp)) |
253 | cp++; |
254 | if (*cp == '\0') { |
255 | puts("Interpolate what file?"); |
256 | break; |
257 | } |
258 | cp = expand(cp); |
259 | if (cp == NULL((void *)0)) |
260 | break; |
261 | if (isdir(cp)) { |
262 | printf("%s: Directory\n", cp); |
263 | break; |
264 | } |
265 | if ((fbuf = Fopen(cp, "r")) == NULL((void *)0)) { |
266 | warn("%s", cp); |
267 | break; |
268 | } |
269 | printf("\"%s\" ", cp); |
270 | fflush(stdout(&__sF[1])); |
271 | lc = 0; |
272 | cc = 0; |
273 | while ((rc = readline(fbuf, linebuf, LINESIZE1024, NULL((void *)0))) >= 0) { |
274 | if (rc != LINESIZE1024 - 1) |
275 | lc++; |
276 | if ((t = putline(collf, linebuf, |
277 | rc != LINESIZE1024-1)) < 0) { |
278 | (void)Fclose(fbuf); |
279 | goto err; |
280 | } |
281 | cc += t; |
282 | } |
283 | (void)Fclose(fbuf); |
284 | printf("%d/%d\n", lc, cc); |
285 | break; |
286 | case 'w': |
287 | /* |
288 | * Write the message on a file. |
289 | */ |
290 | cp = &linebuf[2]; |
291 | while (*cp == ' ' || *cp == '\t') |
292 | cp++; |
293 | if (*cp == '\0') { |
294 | fputs("Write what file!?\n", stderr(&__sF[2])); |
295 | break; |
296 | } |
297 | if ((cp = expand(cp)) == NULL((void *)0)) |
298 | break; |
299 | rewind(collf); |
300 | exwrite(cp, collf, 1); |
301 | break; |
302 | case 'm': |
303 | case 'M': |
304 | case 'f': |
305 | case 'F': |
306 | /* |
307 | * Interpolate the named messages, if we |
308 | * are in receiving mail mode. Does the |
309 | * standard list processing garbage. |
310 | * If ~f is given, we don't shift over. |
311 | */ |
312 | if (forward(linebuf + 2, collf, tempname, c) < 0) |
313 | goto err; |
314 | goto cont; |
315 | case '?': |
316 | if ((fbuf = Fopen(_PATH_TILDE"/usr/share/misc/mail.tildehelp", "r")) == NULL((void *)0)) { |
317 | warn(_PATH_TILDE"/usr/share/misc/mail.tildehelp"); |
318 | break; |
319 | } |
320 | while ((t = getc(fbuf)(!__isthreaded ? (--(fbuf)->_r < 0 ? __srget(fbuf) : (int )(*(fbuf)->_p++)) : (getc)(fbuf))) != EOF(-1)) |
321 | (void)putchar(t)(!__isthreaded ? __sputc(t, (&__sF[1])) : (putc)(t, (& __sF[1]))); |
322 | (void)Fclose(fbuf); |
323 | break; |
324 | case 'p': |
325 | /* |
326 | * Print out the current state of the |
327 | * message without altering anything. |
328 | */ |
329 | rewind(collf); |
330 | puts("-------\nMessage contains:"); |
331 | puthead(hp, stdout(&__sF[1]), GTO1|GSUBJECT2|GCC4|GBCC8|GNL16); |
332 | while ((t = getc(collf)(!__isthreaded ? (--(collf)->_r < 0 ? __srget(collf) : ( int)(*(collf)->_p++)) : (getc)(collf))) != EOF(-1)) |
333 | (void)putchar(t)(!__isthreaded ? __sputc(t, (&__sF[1])) : (putc)(t, (& __sF[1]))); |
334 | goto cont; |
335 | case '|': |
336 | /* |
337 | * Pipe message through command. |
338 | * Collect output as new message. |
339 | */ |
340 | rewind(collf); |
341 | mespipe(collf, &linebuf[2]); |
342 | goto cont; |
343 | case 'v': |
344 | case 'e': |
345 | /* |
346 | * Edit the current message. |
347 | * 'e' means to use EDITOR |
348 | * 'v' means to use VISUAL |
349 | */ |
350 | rewind(collf); |
351 | mesedit(collf, c); |
352 | goto cont; |
353 | } |
354 | } |
355 | |
356 | if (value("interactive") != NULL((void *)0)) { |
357 | if (value("askcc") != NULL((void *)0) || value("askbcc") != NULL((void *)0)) { |
358 | if (value("askcc") != NULL((void *)0)) { |
359 | if (gethfromtty(hp, GCC4) == -1) |
360 | goto err; |
361 | } |
362 | if (value("askbcc") != NULL((void *)0)) { |
363 | if (gethfromtty(hp, GBCC8) == -1) |
364 | goto err; |
365 | } |
366 | } else { |
367 | puts("EOT"); |
368 | (void)fflush(stdout(&__sF[1])); |
369 | } |
370 | } |
371 | goto out; |
372 | err: |
373 | if (collf != NULL((void *)0)) { |
374 | (void)Fclose(collf); |
375 | collf = NULL((void *)0); |
376 | } |
377 | out: |
378 | if (collf != NULL((void *)0)) |
379 | rewind(collf); |
380 | noreset--; |
381 | return(collf); |
382 | } |
383 | |
384 | /* |
385 | * Write a file, ex-like if f set. |
386 | */ |
387 | int |
388 | exwrite(char *name, FILE *fp, int f) |
389 | { |
390 | FILE *of; |
391 | int c; |
392 | ssize_t cc, lc; |
393 | struct stat junk; |
394 | |
395 | if (f) { |
396 | printf("\"%s\" ", name); |
397 | fflush(stdout(&__sF[1])); |
398 | } |
399 | if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)((junk.st_mode & 0170000) == 0100000)) { |
400 | if (!f) |
401 | fprintf(stderr(&__sF[2]), "%s: ", name); |
402 | fputs("File exists\n", stderr(&__sF[2])); |
403 | return(-1); |
404 | } |
405 | if ((of = Fopen(name, "w")) == NULL((void *)0)) { |
406 | warn(NULL((void *)0)); |
407 | return(-1); |
408 | } |
409 | lc = 0; |
410 | cc = 0; |
411 | while ((c = getc(fp)(!__isthreaded ? (--(fp)->_r < 0 ? __srget(fp) : (int)( *(fp)->_p++)) : (getc)(fp))) != EOF(-1)) { |
412 | cc++; |
413 | if (c == '\n') |
414 | lc++; |
415 | (void)putc(c, of)(!__isthreaded ? __sputc(c, of) : (putc)(c, of)); |
416 | if (ferror(of)(!__isthreaded ? (((of)->_flags & 0x0040) != 0) : (ferror )(of))) { |
417 | warn("%s", name); |
418 | (void)Fclose(of); |
419 | return(-1); |
420 | } |
421 | } |
422 | (void)Fclose(of); |
423 | printf("%lld/%lld\n", (long long)lc, (long long)cc); |
424 | fflush(stdout(&__sF[1])); |
425 | return(0); |
426 | } |
427 | |
428 | /* |
429 | * Edit the message being collected on fp. |
430 | * On return, make the edit file the new temp file. |
431 | */ |
432 | void |
433 | mesedit(FILE *fp, int c) |
434 | { |
435 | FILE *nf; |
436 | struct sigaction oact; |
437 | sigset_t oset; |
438 | |
439 | (void)ignoresig(SIGINT2, &oact, &oset); |
440 | nf = run_editor(fp, (off_t)-1, c, 0); |
441 | if (nf != NULL((void *)0)) { |
442 | fseek(nf, 0L, SEEK_END2); |
443 | collf = nf; |
444 | (void)Fclose(fp); |
445 | } |
446 | (void)sigprocmask(SIG_SETMASK3, &oset, NULL((void *)0)); |
447 | (void)sigaction(SIGINT2, &oact, NULL((void *)0)); |
448 | } |
449 | |
450 | /* |
451 | * Pipe the message through the command. |
452 | * Old message is on stdin of command; |
453 | * New message collected from stdout. |
454 | * Sh -c must return 0 to accept the new message. |
455 | */ |
456 | void |
457 | mespipe(FILE *fp, char *cmd) |
458 | { |
459 | FILE *nf; |
460 | int fd; |
461 | char *shell, tempname[PATHSIZE1024]; |
462 | struct sigaction oact; |
463 | sigset_t oset; |
464 | |
465 | (void)ignoresig(SIGINT2, &oact, &oset); |
466 | (void)snprintf(tempname, sizeof(tempname), |
467 | "%s/mail.ReXXXXXXXXXX", tmpdir); |
468 | if ((fd = mkstemp(tempname)) == -1 || |
469 | (nf = Fdopen(fd, "w+")) == NULL((void *)0)) { |
470 | warn("%s", tempname); |
471 | goto out; |
472 | } |
473 | (void)rm(tempname); |
474 | /* |
475 | * stdin = current message. |
476 | * stdout = new message. |
477 | */ |
478 | shell = value("SHELL"); |
479 | if (run_command(shell, |
480 | 0, fileno(fp)(!__isthreaded ? ((fp)->_file) : (fileno)(fp)), fileno(nf)(!__isthreaded ? ((nf)->_file) : (fileno)(nf)), "-c", cmd, NULL((void *)0)) < 0) { |
481 | (void)Fclose(nf); |
482 | goto out; |
483 | } |
484 | if (fsize(nf) == 0) { |
485 | fprintf(stderr(&__sF[2]), "No bytes from \"%s\" !?\n", cmd); |
486 | (void)Fclose(nf); |
487 | goto out; |
488 | } |
489 | /* |
490 | * Take new files. |
491 | */ |
492 | (void)fseek(nf, 0L, SEEK_END2); |
493 | collf = nf; |
494 | (void)Fclose(fp); |
495 | out: |
496 | (void)sigprocmask(SIG_SETMASK3, &oset, NULL((void *)0)); |
497 | (void)sigaction(SIGINT2, &oact, NULL((void *)0)); |
498 | } |
499 | |
500 | /* |
501 | * Interpolate the named messages into the current |
502 | * message, preceding each line with a tab. |
503 | * Return a count of the number of characters now in |
504 | * the message, or -1 if an error is encountered writing |
505 | * the message temporary. The flag argument is 'm' if we |
506 | * should shift over and 'f' if not. |
507 | */ |
508 | int |
509 | forward(char *ms, FILE *fp, char *fn, int f) |
510 | { |
511 | int *msgvec; |
512 | struct ignoretab *ig; |
513 | char *tabst; |
514 | |
515 | msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec)); |
516 | if (msgvec == NULL((void *)0)) |
517 | return(0); |
518 | if (getmsglist(ms, msgvec, 0) < 0) |
519 | return(0); |
520 | if (*msgvec == 0) { |
521 | *msgvec = first(0, MMNORM((1<<1)|(1<<2))); |
522 | if (*msgvec == 0) { |
523 | puts("No appropriate messages"); |
524 | return(0); |
525 | } |
526 | msgvec[1] = 0; |
527 | } |
528 | if (tolower(f) == 'f') |
529 | tabst = NULL((void *)0); |
530 | else if ((tabst = value("indentprefix")) == NULL((void *)0)) |
531 | tabst = "\t"; |
532 | ig = isupper(f) ? NULL((void *)0) : ignore; |
533 | fputs("Interpolating:", stdout(&__sF[1])); |
534 | for (; *msgvec != 0; msgvec++) { |
535 | struct message *mp = message + *msgvec - 1; |
536 | |
537 | touch(mp); |
538 | printf(" %d", *msgvec); |
539 | if (sendmessage(mp, fp, ig, tabst) < 0) { |
540 | warn("%s", fn); |
541 | return(-1); |
542 | } |
543 | } |
544 | putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n', (&__sF[1]))); |
545 | return(0); |
546 | } |
547 | |
548 | /* |
549 | * User aborted during message composition. |
550 | * Save the partial message in ~/dead.letter. |
551 | */ |
552 | int |
553 | collabort(void) |
554 | { |
555 | /* |
556 | * the control flow is subtle, because we can be called from ~q. |
557 | */ |
558 | if (hadintr == 0 && isatty(0)) { |
559 | if (value("ignore") != NULL((void *)0)) { |
560 | puts("@"); |
561 | fflush(stdout(&__sF[1])); |
562 | clearerr(stdin)(!__isthreaded ? ((void)(((&__sF[0]))->_flags &= ~ (0x0040|0x0020))) : (clearerr)((&__sF[0]))); |
563 | } else { |
564 | fflush(stdout(&__sF[1])); |
565 | fputs("\n(Interrupt -- one more to kill letter)\n", |
566 | stderr(&__sF[2])); |
567 | hadintr++; |
568 | } |
569 | return(0); |
570 | } |
571 | fflush(stdout(&__sF[1])); |
572 | rewind(collf); |
573 | if (value("nosave") == NULL((void *)0)) |
574 | savedeadletter(collf); |
575 | return(1); |
576 | } |
577 | |
578 | void |
579 | savedeadletter(FILE *fp) |
580 | { |
581 | FILE *dbuf; |
582 | int c; |
583 | char *cp; |
584 | |
585 | if (fsize(fp) == 0) |
586 | return; |
587 | cp = getdeadletter(); |
588 | c = umask(077); |
589 | dbuf = Fopen(cp, "a"); |
590 | (void)umask(c); |
591 | if (dbuf == NULL((void *)0)) |
592 | return; |
593 | while ((c = getc(fp)(!__isthreaded ? (--(fp)->_r < 0 ? __srget(fp) : (int)( *(fp)->_p++)) : (getc)(fp))) != EOF(-1)) |
594 | (void)putc(c, dbuf)(!__isthreaded ? __sputc(c, dbuf) : (putc)(c, dbuf)); |
595 | (void)Fclose(dbuf); |
596 | rewind(fp); |
597 | } |
598 | |
599 | int |
600 | gethfromtty(struct header *hp, int gflags) |
601 | { |
602 | |
603 | hadintr = 0; |
604 | while (grabh(hp, gflags) != 0) { |
605 | if (collabort()) |
606 | return(-1); |
607 | } |
608 | return(0); |
609 | } |