Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/quit.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2012 The Regents of the University of California an*
5
* *
6
* Redistribution and use in source and binary forms, with or *
7
* without modification, are permitted provided that the following *
8
* conditions are met: *
9
* *
10
* 1. Redistributions of source code must retain the above *
11
* copyright notice, this list of conditions and the *
12
* following disclaimer. *
13
* *
14
* 2. Redistributions in binary form must reproduce the above *
15
* copyright notice, this list of conditions and the *
16
* following disclaimer in the documentation and/or other *
17
* materials provided with the distribution. *
18
* *
19
* 3. Neither the name of The Regents of the University of California*
20
* names of its contributors may be used to endorse or *
21
* promote products derived from this software without *
22
* specific prior written permission. *
23
* *
24
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
25
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
26
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
27
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
28
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS *
29
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
30
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED *
31
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
32
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *
33
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
34
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
35
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
36
* POSSIBILITY OF SUCH DAMAGE. *
37
* *
38
* Redistribution and use in source and binary forms, with or without *
39
* modification, are permitted provided that the following conditions *
40
* are met: *
41
* 1. Redistributions of source code must retain the above copyright *
42
* notice, this list of conditions and the following disclaimer. *
43
* 2. Redistributions in binary form must reproduce the above copyright *
44
* notice, this list of conditions and the following disclaimer in *
45
* the documentation and/or other materials provided with the *
46
* distribution. *
47
* 3. Neither the name of the University nor the names of its *
48
* contributors may be used to endorse or promote products derived *
49
* from this software without specific prior written permission. *
50
* *
51
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" *
52
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
53
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
54
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS *
55
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *
56
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
57
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *
58
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
59
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
60
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *
61
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *
62
* SUCH DAMAGE. *
63
* *
64
* Kurt Shoens (UCB) *
65
* gsf *
66
* *
67
***********************************************************************/
68
#pragma prototyped
69
/*
70
* Mail -- a mail program.
71
*
72
* Termination processing.
73
*/
74
75
#include "mailx.h"
76
77
/*
78
* Give names to all the temporary files that we will need.
79
*/
80
81
void
82
tempinit(void)
83
{
84
register char* cp;
85
86
if (!(state.tmp.dir = tempnam(NiL, NiL)) || !*state.tmp.dir || !(cp = strrchr(state.tmp.dir, '/')) && !(cp = strrchr(state.tmp.dir, '\\')))
87
state.tmp.dir = _PATH_TMP;
88
else {
89
*++cp = 0;
90
if (!(cp = (char*)malloc(cp - state.tmp.dir + 1)))
91
note(ERROR|FATAL, "Out of space");
92
strcpy(cp, state.tmp.dir);
93
state.tmp.dir = cp;
94
}
95
filetemp(state.tmp.edit, sizeof(state.tmp.edit), 'E', 0);
96
filetemp(state.tmp.mail, sizeof(state.tmp.mail), 'M', 0);
97
filetemp(state.tmp.mesg, sizeof(state.tmp.mesg), 'G', 0);
98
filetemp(state.tmp.more, sizeof(state.tmp.more), 'X', 0);
99
filetemp(state.tmp.quit, sizeof(state.tmp.quit), 'Q', 0);
100
}
101
102
/*
103
* The "quit" command.
104
*/
105
int
106
cmdquit(void)
107
{
108
/*
109
* If we are sourcing, then return 1 so execute() can handle it.
110
* Otherwise, return -1 to abort command loop.
111
*/
112
return state.sourcing ? 1 : -1;
113
}
114
115
/*
116
* Terminate an editing session by attempting to write out the user's
117
* file from the temporary. Save any new stuff appended to the file.
118
*/
119
static void
120
edstop(void)
121
{
122
register int gotcha;
123
register int c;
124
register struct msg* mp;
125
int update;
126
char* s;
127
char* temp;
128
char* move;
129
FILE* obuf;
130
FILE* ibuf;
131
FILE* news;
132
struct mhcontext mh;
133
struct stat st;
134
135
if (state.readonly)
136
return;
137
holdsigs();
138
news = state.var.news && *state.var.news ? fileopen(state.var.news, "w") : (FILE*)0;
139
update = MODIFY|MDELETE|MSTATUS;
140
if (!state.var.keepsave)
141
update |= MSAVE;
142
for (mp = state.msg.list, gotcha = 0; mp < state.msg.list + state.msg.count; mp++) {
143
if (!(mp->m_flag & MNONE)) {
144
if (mp->m_flag & MNEW)
145
msgflags(mp, MSTATUS, MNEW);
146
if (mp->m_flag & update)
147
gotcha++;
148
if (news && (mp->m_flag & (MREAD|MDELETE)) && (s = grab(mp, GNEWS, NiL)))
149
fprintf(news, "%s\n", s);
150
}
151
}
152
if (news) {
153
fileclose(news);
154
goto done;
155
}
156
if (!gotcha)
157
goto done;
158
if (state.folder == FMH) {
159
if (!state.incorporating)
160
note(PROMPT, "\"%s\" ", state.path.mail);
161
mhgetcontext(&mh, state.path.mail, 1);
162
mh.dot = state.msg.dot - state.msg.list + 1;
163
for (mp = state.msg.list; mp < state.msg.list + state.msg.count; mp++) {
164
if ((mp->m_flag & update) && !(mp->m_flag & MNONE)) {
165
sfprintf(state.path.temp, "%s/%d", state.path.mail, mp - state.msg.list + 1);
166
temp = struse(state.path.temp);
167
if (mp->m_flag & (MDELETE|MSAVE))
168
rm(temp);
169
else {
170
sfprintf(state.path.move, "%s/%d~", state.path.mail, mp - state.msg.list + 1);
171
move = struse(state.path.move);
172
if (obuf = fileopen(move, "Ew")) {
173
if (copy(mp, obuf, NiL, NiL, 0) < 0) {
174
note(SYSTEM, "%s", temp);
175
rm(move);
176
relsesigs();
177
reset(0);
178
}
179
rm(temp);
180
if (rename(move, temp))
181
note(SYSTEM, "%s", temp);
182
fileclose(obuf);
183
}
184
}
185
}
186
}
187
mhputcontext(&mh, state.path.mail);
188
if (!state.incorporating)
189
note(0, "complete");
190
}
191
else {
192
ibuf = 0;
193
if (stat(state.path.mail, &st) >= 0 && st.st_size > state.mailsize) {
194
temp = state.path.path;
195
filetemp(temp, sizeof(state.path.path), 'B', 0);
196
if (!(obuf = fileopen(temp, "Ew"))) {
197
relsesigs();
198
reset(0);
199
}
200
if (!(ibuf = fileopen(state.path.mail, "Er"))) {
201
fileclose(obuf);
202
rm(temp);
203
relsesigs();
204
reset(0);
205
}
206
fseek(ibuf, state.mailsize, SEEK_SET);
207
filecopy(NiL, ibuf, NiL, obuf, NiL, (off_t)0, NiL, NiL, 0);
208
fileclose(ibuf);
209
fileclose(obuf);
210
if (!(ibuf = fileopen(temp, "Er"))) {
211
rm(temp);
212
relsesigs();
213
reset(0);
214
}
215
rm(temp);
216
}
217
if (!state.incorporating)
218
note(PROMPT, "\"%s\" ", state.path.mail);
219
if (!(obuf = fileopen(state.path.mail, "Er+"))) {
220
relsesigs();
221
reset(0);
222
}
223
filetrunc(obuf);
224
update &= (MDELETE|MSAVE);
225
update |= MNONE;
226
c = 0;
227
for (mp = state.msg.list; mp < state.msg.list + state.msg.count; mp++)
228
if (!(mp->m_flag & update)) {
229
c = 1;
230
if (copy(mp, obuf, NiL, NiL, 0) < 0) {
231
note(SYSTEM, "%s", state.path.mail);
232
relsesigs();
233
reset(0);
234
}
235
}
236
gotcha = !c && !ibuf;
237
if (ibuf) {
238
filecopy(NiL, ibuf, NiL, obuf, NiL, (off_t)0, NiL, NiL, 0);
239
fileclose(ibuf);
240
}
241
if (fileclose(obuf)) {
242
note(SYSTEM, "%s", state.path.mail);
243
relsesigs();
244
reset(0);
245
}
246
if (!state.incorporating) {
247
if (gotcha) {
248
rm(state.path.mail);
249
note(0, "removed");
250
}
251
else
252
note(0, "complete");
253
}
254
}
255
done:
256
relsesigs();
257
}
258
259
/*
260
* Preserve all the appropriate messages back in the system
261
* mailbox, and print a nice message indicated how many were
262
* saved. On any error, just return -1. Else return 0.
263
* Incorporate the any new mail that we found.
264
*/
265
static int
266
writeback(register FILE* res)
267
{
268
register struct msg* mp;
269
register int p;
270
FILE* obuf;
271
272
p = 0;
273
if (!(obuf = fileopen(state.path.mail, "Er+")))
274
return -1;
275
#if !APPEND_MAILBOX
276
if (res)
277
filecopy(NiL, res, NiL, obuf, NiL, (off_t)0, NiL, NiL, 0);
278
#endif
279
for (mp = state.msg.list; mp < state.msg.list + state.msg.count; mp++)
280
if ((mp->m_flag&MPRESERVE) || !(mp->m_flag&MTOUCH)) {
281
p++;
282
if (copy(mp, obuf, NiL, NiL, 0) < 0) {
283
note(SYSTEM, "%s", state.path.mail);
284
fileclose(obuf);
285
return -1;
286
}
287
}
288
#if APPEND_MAILBOX
289
if (res && filecopy(NiL, res, state.path.mail, obuf, NiL, (off_t)0, NiL, NiL, 0)) {
290
fileclose(obuf);
291
return -1;
292
}
293
#endif
294
if (filetrunc(obuf)) {
295
note(SYSTEM, "%s", state.path.mail);
296
fileclose(obuf);
297
return -1;
298
}
299
fileclose(obuf);
300
if (res)
301
fileclose(res);
302
alter(state.path.mail);
303
note(0, "Held %d message%s in %s", p, p == 1 ? "" : "s", state.path.mail);
304
return 0;
305
}
306
307
/*
308
* Save all of the undetermined messages at the top of "mbox"
309
* Save all untouched messages back in the system mailbox.
310
* Remove the system mailbox, if none saved there.
311
*/
312
void
313
quit(void)
314
{
315
register struct msg* mp;
316
register int c;
317
off_t size;
318
char* mbox;
319
char* s;
320
char* xbox;
321
int anystat;
322
int holdbit;
323
int mcount;
324
int modify;
325
int nohold;
326
int p;
327
int x;
328
int set;
329
int clr;
330
FILE* abuf;
331
FILE* fbuf;
332
FILE* ibuf;
333
FILE* news;
334
FILE* obuf;
335
FILE* rbuf;
336
FILE* xbuf;
337
338
/*
339
* If we are read only, we can't do anything,
340
* so just return quickly.
341
*/
342
if (state.readonly)
343
return;
344
/*
345
* If editing (not reading system mail box), then do the work
346
* in edstop()
347
*/
348
if (state.edit) {
349
edstop();
350
return;
351
}
352
if (state.folder != FIMAP) {
353
354
/*
355
* See if there any messages to save in mbox. If no, we
356
* can save copying mbox to /tmp and back.
357
*
358
* Check also to see if any files need to be preserved.
359
* Delete all untouched messages to keep them out of mbox.
360
* If all the messages are to be preserved, just exit with
361
* a message.
362
*
363
* NOTE: filelock() requires open for r/w
364
*/
365
366
if (!(fbuf = fileopen(state.path.mail, "r+")))
367
goto newmail;
368
size = state.openstat.st_size;
369
filelock(state.path.mail, fbuf, 1);
370
rbuf = 0;
371
if (size > state.mailsize) {
372
note(0, "New mail has arrived");
373
if (!(rbuf = fileopen(state.tmp.more, "w")) || !fbuf)
374
goto newmail;
375
#if APPEND_MAILBOX
376
fseek(fbuf, state.mailsize, SEEK_SET);
377
filecopy(NiL, fbuf, NiL, rbuf, NiL, (off_t)0, NiL, NiL, 0);
378
#else
379
if (filecopy(NiL, fbuf, NiL, rbuf, NiL, (off_t)(size - state.mailsize), NiL, NiL, 0))
380
goto newmail;
381
#endif
382
fileclose(rbuf);
383
if (!(rbuf = fileopen(state.tmp.more, "r")))
384
goto newmail;
385
rm(state.tmp.more);
386
}
387
}
388
else
389
fbuf = 0;
390
391
/*
392
* Adjust the message flags in each message.
393
*/
394
395
anystat = 0;
396
holdbit = state.var.hold ? MPRESERVE : MBOX;
397
nohold = MBOX|MSAVE|MDELETE|MPRESERVE;
398
if (state.var.keepsave)
399
nohold &= ~MSAVE;
400
for (mp = state.msg.list; mp < state.msg.list + state.msg.count; mp++) {
401
set = clr = 0;
402
if (mp->m_flag & (MBOX|MDELETE|MPRESERVE|MSAVE))
403
clr |= MSPAM;
404
if (mp->m_flag & MNEW) {
405
set |= MSTATUS;
406
clr |= MNEW;
407
}
408
if ((mp->m_flag | set) & MSTATUS)
409
anystat++;
410
if (!(mp->m_flag & MTOUCH))
411
set |= MPRESERVE;
412
if (!(mp->m_flag & nohold))
413
set |= holdbit;
414
if (set || clr)
415
msgflags(mp, set, clr);
416
}
417
modify = 0;
418
news = state.var.news && *state.var.news ? fileopen(state.var.news, "w") : (FILE*)0;
419
for (c = p = x = 0, mp = state.msg.list; mp < state.msg.list + state.msg.count; mp++) {
420
if (mp->m_flag & MSPAM) {
421
msgflags(mp, MDELETE|MTOUCH, MBOX|MPRESERVE|MODIFY);
422
x++;
423
}
424
if (mp->m_flag & MBOX)
425
c++;
426
if (mp->m_flag & MPRESERVE)
427
p++;
428
if (mp->m_flag & MODIFY)
429
modify++;
430
if (news && (mp->m_flag & (MREAD|MDELETE)) &&
431
(s = grab(mp, GNEWS, NiL)))
432
fprintf(news, "%s\n", s);
433
}
434
if (news)
435
fileclose(news);
436
if (p == state.msg.count && !modify && !anystat) {
437
note(0, "Held %d message%s in %s", p, p == 1 ? "" : "s", state.path.mail);
438
if (fbuf)
439
fileclose(fbuf);
440
return;
441
}
442
xbuf = 0;
443
if (x && (!state.var.spamlog || !*state.var.spamlog || !(xbuf = fileopen(xbox = expand(state.var.spamlog, 1), "a"))))
444
x = 0;
445
if (state.folder == FIMAP) {
446
for (mp = state.msg.list; mp < state.msg.list + state.msg.count; mp++) {
447
if (mp->m_flag & MSAVE)
448
msgflags(mp, MDELETE, 0);
449
if (xbuf && (mp->m_flag & (MSPAM|MDELETE)) == (MSPAM|MDELETE)) {
450
if (copy(mp, xbuf, &state.saveignore, NiL, 0) < 0) {
451
msgflags(mp, MPRESERVE, MSPAM|MDELETE|MTOUCH);
452
x--;
453
}
454
}
455
}
456
if (xbuf) {
457
fileclose(xbuf);
458
if (x)
459
note(0, "Saved %d message%s in %s", x, x == 1 ? "" : "s", xbox);
460
}
461
}
462
if (state.msg.imap.state) {
463
imap_quit();
464
if (state.folder == FIMAP)
465
return;
466
}
467
if (!c && !x) {
468
if (p) {
469
writeback(rbuf);
470
fileclose(fbuf);
471
fileclose(xbuf);
472
return;
473
}
474
goto cream;
475
}
476
477
/*
478
* Create another temporary file and copy user's mbox file
479
* darin. If there is no mbox, copy nothing.
480
* If he has specified "append" don't copy his mailbox,
481
* just copy saveable entries at the end.
482
*/
483
484
mbox = expand("&", 1);
485
mcount = c;
486
if (state.var.append) {
487
if (!(obuf = fileopen(mbox, "Ea"))) {
488
fileclose(fbuf);
489
fileclose(xbuf);
490
return;
491
}
492
chmod(mbox, MAILMODE);
493
}
494
else {
495
if (!(obuf = fileopen(state.tmp.quit, "Ew"))) {
496
fileclose(fbuf);
497
fileclose(xbuf);
498
return;
499
}
500
if (!(ibuf = fileopen(state.tmp.quit, "Er"))) {
501
rm(state.tmp.quit);
502
fileclose(obuf);
503
fileclose(fbuf);
504
fileclose(xbuf);
505
return;
506
}
507
rm(state.tmp.quit);
508
if (abuf = fileopen(mbox, "r")) {
509
if (filecopy(mbox, abuf, state.tmp.quit, obuf, NiL, (off_t)0, NiL, NiL, 0)) {
510
fileclose(abuf);
511
fileclose(ibuf);
512
fileclose(obuf);
513
fileclose(fbuf);
514
fileclose(xbuf);
515
return;
516
}
517
fileclose(abuf);
518
}
519
if (fileclose(obuf)) {
520
note(SYSTEM, "%s", state.tmp.quit);
521
fileclose(ibuf);
522
fileclose(obuf);
523
fileclose(fbuf);
524
fileclose(xbuf);
525
return;
526
}
527
close(open(mbox, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY|O_cloexec, MAILMODE));
528
if (!(obuf = fileopen(mbox, "Er+"))) {
529
fileclose(ibuf);
530
fileclose(fbuf);
531
fileclose(xbuf);
532
return;
533
}
534
}
535
for (mp = state.msg.list; mp < state.msg.list + state.msg.count; mp++) {
536
if (mp->m_flag & MBOX) {
537
if (copy(mp, obuf, &state.saveignore, NiL, 0) < 0) {
538
note(SYSTEM, "%s", mbox);
539
fileclose(ibuf);
540
fileclose(obuf);
541
fileclose(fbuf);
542
fileclose(xbuf);
543
return;
544
}
545
}
546
else if (xbuf && (mp->m_flag & (MSPAM|MDELETE)) == (MSPAM|MDELETE)) {
547
if (copy(mp, xbuf, &state.saveignore, NiL, 0) < 0) {
548
msgflags(mp, MPRESERVE, MSPAM|MDELETE|MTOUCH);
549
x--;
550
}
551
}
552
}
553
if (xbuf) {
554
fileclose(xbuf);
555
if (x)
556
note(0, "Saved %d message%s in %s", x, x == 1 ? "" : "s", xbox);
557
}
558
559
/*
560
* Copy the user's old mbox contents back
561
* to the end of the stuff we just saved.
562
* If we are appending, this is unnecessary.
563
*/
564
565
if (!state.var.append) {
566
rewind(ibuf);
567
filecopy(NiL, ibuf, NiL, obuf, NiL, (off_t)0, NiL, NiL, 0);
568
fileclose(ibuf);
569
}
570
if (filetrunc(obuf)) {
571
note(SYSTEM, "%s", mbox);
572
fileclose(obuf);
573
fileclose(fbuf);
574
return;
575
}
576
fileclose(obuf);
577
if (mcount)
578
note(0, "Saved %d message%s in %s", mcount, mcount == 1 ? "" : "s", mbox);
579
580
/*
581
* Now we are ready to copy back preserved files to
582
* the system mailbox, if any were requested.
583
*/
584
585
if (p) {
586
writeback(rbuf);
587
fileclose(fbuf);
588
return;
589
}
590
591
/*
592
* Finally, remove his /usr/mail file.
593
* If new mail has arrived, copy it back.
594
*/
595
596
cream:
597
if (rbuf) {
598
if (!(abuf = fileopen(state.path.mail, "r+")))
599
goto newmail;
600
filecopy(NiL, rbuf, NiL, abuf, NiL, (off_t)0, NiL, NiL, 0);
601
fileclose(rbuf);
602
filetrunc(abuf);
603
fileclose(abuf);
604
alter(state.path.mail);
605
fileclose(fbuf);
606
return;
607
}
608
demail();
609
if (fbuf)
610
fileclose(fbuf);
611
return;
612
613
newmail:
614
note(0, "Thou hast new mail");
615
if (fbuf)
616
fileclose(fbuf);
617
}
618
619