1 /*
2  * MultiMail offline mail reader
3  * message editing (continuation of the LetterWindow class)
4 
5  Copyright 1996 Kolossvary Tamas <thomas@tvnet.hu>
6  Copyright 1997 John Zero <john@graphisoft.hu>
7  Copyright 1997-2019 William McBrine <wmcbrine@gmail.com>
8  Distributed under the GNU General Public License, version 3 or later. */
9 
10 #include "interfac.h"
11 
set_Letter_Params(net_address & nm,const char * to)12 void LetterWindow::set_Letter_Params(net_address &nm, const char *to)
13 {
14     NM = nm;
15     delete[] To;
16     To = strdupplus(to);
17 }
18 
QuoteText(FILE * reply)19 void LetterWindow::QuoteText(FILE *reply)
20 {
21     char TMP[81];
22     int i;
23     bool inet = !(!(mm.areaList->getType() & INTERNET));
24     const char *From = mm.letterList->getFrom();
25 
26     int width = mm.resourceObject->getInt(QuoteWrapCols);
27     if ((width < 20) || (width > 80))
28         width = 78;
29     width -= inet ? 2 : 6;
30 
31     char c;
32     const char *s, *quotestr = mm.resourceObject->get(inet ? InetQuote :
33                                                       QuoteHead);
34 
35     // Format header:
36 
37     while ((c = *quotestr++) != 0) {
38         if (c != '%')
39             fputc(c, reply);
40         else {
41             s = 0;
42             switch (tolower(*quotestr)) {
43             case 'f':
44                 s = From;
45                 break;
46             case 't':
47                 s = mm.letterList->getTo();
48                 break;
49             case 'd':
50                 s = mm.letterList->getDate();
51                 break;
52             case 's':
53                 s = mm.letterList->getSubject();
54                 break;
55             case 'a':
56                 s = mm.areaList->getDescription();
57                 break;
58             case 'n':
59                 s = "\n";
60                 break;
61             case '%':
62                 s = "%";
63             }
64             if (s) {
65                 sprintf(TMP, "%.80s", s);
66                 letterconv_in(TMP);
67                 fputs(TMP, reply);
68                 quotestr++;
69             }
70         }
71     }
72     fputs("\n\n", reply);
73 
74     // Set quote initials if necessary:
75 
76     if (!inet) {
77         char initials[4];
78 
79         // Start off with the first two letters of the name
80         strncpy(initials, From, 2);
81         initials[2] = '\0';
82         initials[3] = '\0';
83 
84         // Find the first letters of each of the second and last
85         // words in the name, if available
86         i = 1;
87         for (int j = 1; j < 3; j++) {
88             bool end = false;
89             while (From[i] && !end) {
90                 if ((From[i - 1] == ' ') && (From[i] != ' ')) {
91                     initials[j] = From[i];
92                     if (j == 1)
93                         end = true;
94                 }
95                 i++;
96             }
97         }
98 
99         letterconv_in(initials);
100         sprintf(TMP, " %s> ", initials);
101     } else
102         strcpy(TMP, "> ");
103 
104     // Output quoted text:
105 
106     MakeChain(width, true);
107 
108     for (i = 0; i < NumOfLines; i++) {
109         Line *curr = linelist[i];
110 
111         if (!((Sigline == curr->attr) ||
112             (!curr->length && (i < (NumOfLines - 1)) &&
113             (Sigline == linelist[i + 1]->attr) ))) {
114 
115             fputs(curr->length ? ((Quoted == curr->attr) ?
116                   (inet ? ">" : ((curr->text[0] == ' ') ? "" : " ")) :
117                   TMP) : (inet ? ">" : ""), reply);
118 
119             curr->out(reply);
120         }
121     }
122 
123     MakeChain(COLS);
124 }
125 
HeaderLine(ShadowedWin & win,char * buf,int limit,int pos,coltype color)126 int LetterWindow::HeaderLine(ShadowedWin &win, char *buf, int limit,
127                              int pos, coltype color)
128 {
129     areaconv_in(buf);
130     int getit = win.getstring(pos, 8, buf, limit, color, color);
131     areaconv_out(buf);
132     return getit;
133 }
134 
EnterHeader(char * FROM,char * TO,char * SUBJ,bool & privat)135 int LetterWindow::EnterHeader(char *FROM, char *TO, char *SUBJ, bool &privat)
136 {
137     static const char *noyes[] = { "No", "Yes" };
138 
139     char NETADD[100];
140     int result, current, maxitems = 2;
141     bool end = false;
142     bool hasNet = mm.areaList->isEmail();
143     bool hasNews = mm.areaList->isUsenet();
144     bool hasTo = hasNews || mm.areaList->hasTo();
145 
146     if (hasNet) {
147         sprintf(NETADD, "%.99s", (const char *) NM);
148         maxitems++;
149     } else
150         NM.isSet = false;
151 
152     if (hasTo)
153         maxitems++;
154 
155     ShadowedWin rep_header(maxitems + 2, COLS - 4, (LINES / 2) - 3, C_LETEXT);
156 
157     rep_header.put(1, 2, "From:");
158     if (hasTo)
159         rep_header.put(2, 2, "  To:");
160     if (hasNet)
161         rep_header.put(3, 2, "Addr:");
162     rep_header.put(maxitems, 2, "Subj:");
163 
164     rep_header.attrib(C_LEGET2);
165     areaconv_in(FROM);
166     rep_header.put(1, 8, FROM, 69);
167     areaconv_out(FROM);
168 
169     if (hasNet && !NM.isSet) {
170         //NM.isInternet = mm.areaList->isInternet();
171         TO[0] = '\0';
172         current = 1;
173     } else {
174         if (hasTo) {
175             rep_header.attrib(C_LEGET1);
176             areaconv_in(TO);
177             rep_header.put(2, 8, TO, 69);
178             areaconv_out(TO);
179         }
180         if (hasNet) {
181             rep_header.attrib(C_LEGET2);
182             areaconv_in(NETADD);
183             rep_header.put(3, 8, NETADD, 69);
184             areaconv_out(NETADD);
185         }
186         current = maxitems - 1;
187     }
188 
189     rep_header.update();
190 
191     do {
192         result = HeaderLine(rep_header, (current == (maxitems - 1)) ?
193             SUBJ : (((current == 2) && hasNet) ? NETADD : ((current &&
194             hasTo) ? TO : FROM)), ((current == 1) && hasNews) ? 512 :
195             ((current == (maxitems - 1)) ? mm.areaList->maxSubLen() :
196             (((current == 2) && NM.isInternet) ? 72 :
197             mm.areaList->maxToLen())), current + 1, (current & 1) ?
198             C_LEGET1 : C_LEGET2);
199 
200         switch (result) {
201         case 0:
202             end = true;
203             break;
204         case 1:
205             current++;
206             if (current == maxitems)
207                 end = true;
208             break;
209         case 2:
210             if (current > 0)
211                 current--;
212             break;
213         case 3:
214             if (current < maxitems - 1)
215                 current++;
216         }
217     } while (!end);
218 
219     if (result) {
220         int pmode = (!hasNet ? mm.areaList->hasPublic() : 0) +
221                     ((mm.areaList->hasPrivate() &&
222                     !mm.areaList->isUsenet()) ? 2 : 0);
223 
224         if (hasNet)
225             NM = NETADD;
226 
227         switch (pmode) {
228         case 1:
229             privat = false;
230             break;
231         case 2:
232             privat = true;
233             break;
234         case 3:
235             if (!ui->WarningWindow("Make letter private?", privat ? 0 : noyes))
236                 privat = !privat;
237         }
238     }
239     return result;
240 }
241 
reconvert(const char * reply_filename)242 long LetterWindow::reconvert(const char *reply_filename)
243 {
244     FILE *src, *dest;
245 
246     char *outname = mytmpnam();
247     src = fopen(reply_filename, "rt");
248     dest = fopen(outname, "wt");
249 
250     fseek(src, 0, SEEK_END);
251     long replen = ftell(src);
252     rewind(src);
253 
254     char *body = new char[((replen > MAXBLOCK) ? MAXBLOCK : replen) + 1];
255 
256     long totlen = replen;
257     replen = 0;
258     while (totlen > MAXBLOCK) {
259         long blklen = (long) fread(body, 1, MAXBLOCK, src);
260         body[blklen] = '\0';
261         areaconv_out(body);
262         replen += (long) fwrite(body, 1, blklen, dest);
263         totlen -= blklen;
264     }
265     totlen = (long) fread(body, 1, totlen, src);
266     fclose(src);
267 
268     body[totlen] = '\0';
269     areaconv_out(body);
270     while (body[totlen - 1] == '\n')
271         totlen--;
272 
273     replen += (long) fwrite(body, 1, totlen, dest);
274     fclose(dest);
275 
276     delete[] body;
277 
278     remove(reply_filename);
279     rename(outname, reply_filename);
280 
281     delete[] outname;
282 
283     return replen;
284 }
285 
setToFrom(char key,char * TO,char * FROM)286 void LetterWindow::setToFrom(char key, char *TO, char *FROM)
287 {
288     char format[7];
289     sprintf(format, "%%.%ds", mm.areaList->maxToLen());
290 
291     bool usealias = mm.areaList->getUseAlias();
292     if (usealias) {
293         const char *name = mm.packet->getAliasName();
294         sprintf(FROM, format, name ? name : "");
295         if (!FROM[0])
296             usealias = false;
297     }
298     if (!usealias) {
299         const char *name = mm.packet->getLoginName();
300         sprintf(FROM, format, name ? name : "");
301     }
302 
303     if (mm.areaList->isUsenet()) {
304         const char *newsgrps = 0;
305 
306         if (key != 'E') {
307             newsgrps = mm.letterList->getFollow();
308             if (!newsgrps)
309                 newsgrps = mm.letterList->getNewsgrps();
310         }
311         if (!newsgrps)
312             newsgrps = mm.areaList->getDescription();
313 
314         sprintf(TO, "%.512s", newsgrps);
315     } else
316         if (key == 'E')
317             strcpy(TO, (To ? To : "All"));
318         else
319             if (mm.areaList->isInternet()) {
320                 if (key == 'O') {
321                     sprintf(TO, format, fromName(mm.letterList->getTo()));
322                     NM = fromAddr(mm.letterList->getTo());
323                 } else {
324                     const char *rep = mm.letterList->getReply();
325 
326                     sprintf(TO, format, mm.letterList->getFrom());
327                     if (rep)
328                         NM = fromAddr(rep);
329                 }
330             } else
331                 sprintf(TO, format, (key == 'O') ? mm.letterList->getTo() :
332                         mm.letterList->getFrom());
333 }
334 
EnterLetter(int replyto_area,char key)335 void LetterWindow::EnterLetter(int replyto_area, char key)
336 {
337     FILE *reply;
338     char FROM[74], TO[514], SUBJ[514];
339     const char *orig_id;
340 
341     mystat fileStat;
342     time_t oldtime;
343 
344     long replen, replyto_num;
345     bool privat;
346 
347     mm.areaList->gotoArea(replyto_area);
348 
349     bool news = mm.areaList->isUsenet();
350     bool inet = news || mm.areaList->isInternet();
351 
352     // HEADER
353 
354     setToFrom(key, TO, FROM);
355 
356     if (key == 'E')
357         SUBJ[0] = '\0';  //we don't have subject yet
358     else {
359         const char *s = stripre(mm.letterList->getSubject());
360         int len = strlen(s);
361 
362         bool useRe = (inet || mm.resourceObject->getInt(ReOnReplies))
363                      && ((len + 4) <= mm.areaList->maxSubLen());
364         sprintf(SUBJ, useRe ? "Re: %.509s" : "%.512s", s);
365     }
366 
367     privat = (key == 'E') ? false : mm.letterList->getPrivate();
368 
369     if (!EnterHeader(FROM, TO, SUBJ, privat)) {
370         NM.isSet = false;
371         delete[] To;
372         To = 0;
373         ui->areas.Select();
374         ui->redraw();
375         return;
376     }
377 
378     // Don't use refnum if posting in different area:
379 
380     replyto_num = ((key == 'E') || (key == 'N') || (replyto_area !=
381         mm.letterList->getAreaID())) ? 0 : mm.letterList->getMsgNum();
382 
383     orig_id = replyto_num ? mm.letterList->getMsgID() : 0;
384 
385     // BODY
386 
387     char *reply_filename = mytmpnam();
388 
389     // Quote the old text
390 
391     if (key != 'E') {
392         reply = fopen(reply_filename, "wt");
393         QuoteText(reply);
394         fclose(reply);
395     }
396     fileStat.init(reply_filename);
397     oldtime = fileStat.fdate();
398 
399     // Edit the reply
400 
401     edit(reply_filename);
402     ui->areas.Select();
403     ui->redraw();
404 
405     // Check if modified
406 
407     fileStat.init(reply_filename);
408     if (fileStat.fdate() == oldtime)
409         if (ui->WarningWindow("Cancel this letter?")) {
410             remove(reply_filename);
411             ui->redraw();
412             return;
413         }
414 
415     // Mark original as replied
416 
417     if (key != 'E') {
418         int origatt = mm.letterList->getStatus();
419         mm.letterList->setStatus(origatt | MS_REPLIED);
420         if (!(origatt & MS_REPLIED))
421             ui->setAnyRead();
422     }
423 
424     reply = fopen(reply_filename, "at");
425 
426     // Signature
427 
428     bool sigset = false;
429 
430     const char *sg = mm.resourceObject->get(sigFile);
431     if (sg && *sg) {
432         FILE *s = fopen(sg, "rt");
433         if (s) {
434             int c;
435 
436             fputs(inet ? "\n-- \n" : "\n", reply);
437             while ((c = fgetc(s)) != EOF)
438                 if (c != '\r')
439                     fputc(c, reply);
440             fclose(s);
441 
442             sigset = true;
443         }
444     }
445 
446     // Tagline
447 
448     bool useTag = mm.resourceObject->getInt(UseTaglines) &&
449                   ui->taglines.NumOfItems() && ui->Tagwin();
450     if (useTag)
451         fprintf(reply, inet ? (sigset ? "\n%s\n" : "\n-- \n%s\n") :
452                 "\n... %s\n", ui->taglines.getCurrent());
453     else
454         if (!inet)
455             fprintf(reply, " \n");
456 
457     // Tearline (not for Blue Wave -- it does its own)
458 
459     if (!inet) {
460         const char *tear = mm.areaList->getTear();
461 
462         if (tear)
463             fprintf(reply, "%s\n", tear);
464     }
465 
466     fclose(reply);
467 
468     // Reconvert the text
469 
470     mm.areaList->gotoArea(replyto_area);
471     replen = reconvert(reply_filename);
472 
473     mm.areaList->enterLetter(replyto_area, FROM, news ? "All" : TO,
474         SUBJ, orig_id, news ? TO : 0, replyto_num, privat, NM,
475         reply_filename, replen);
476 
477     delete[] reply_filename;
478 
479     NM.isSet = false;
480     delete[] To;
481     To = 0;
482 
483     ui->areas.Select();
484     ui->setUnsaved();
485     ui->redraw();
486 }
487 
forward_header(FILE * fd,const char * FROM,const char * TO,const char * SUBJ,int replyto_area,bool is_reply)488 void LetterWindow::forward_header(FILE *fd, const char *FROM,
489     const char *TO, const char *SUBJ, int replyto_area, bool is_reply)
490 {
491     enum {area, date, from, to, subj, items};
492     static const char *names[items] = {"ly in", "ly on", "ly by",
493                                        "ly to", " subj"};
494     char Header[512], *p;
495     int j;
496 
497     int oldarea = mm.letterList->getAreaID();
498     mm.areaList->gotoArea(mm.letterList->getAreaID());
499 
500     const char *head[items] = {
501         mm.areaList->getDescription(), mm.letterList->getDate(),
502         mm.letterList->getFrom(), mm.letterList->getTo(),
503         mm.letterList->getSubject()
504     };
505 
506     const char *org[items] = {
507         0, 0, FROM, TO, SUBJ
508     };
509 
510     bool use[items];
511 
512     use[area] = (oldarea != replyto_area);
513     use[date] = !is_reply;
514     for (j = from; j < items; j++)
515         use[j] = !(!strcasecmp(org[j], head[j]));
516 
517     ui->areas.Select();
518 
519     bool anyused = false;
520 
521     for (j = 0; j < items; j++)
522         if (use[j]) {
523             p = Header + sprintf(Header, "%.511s", head[j]);
524             if (((j == from) && mm.areaList->isEmail())
525                 || ((j == to) && mm.areaList->isReplyArea()))
526 
527                 netAdd(p);
528             letterconv_in(Header);
529             fprintf(fd, " * Original%s: %s\n", names[j], Header);
530             anyused = true;
531         }
532 
533     if (anyused)
534         fprintf(fd, "\n");
535 }
536 
EditLetter(bool forwarding)537 void LetterWindow::EditLetter(bool forwarding)
538 {
539     FILE *reply;
540     char FROM[74], TO[514], SUBJ[514];
541     long siz;
542     int replyto_num, replyto_area;
543     bool privat, newsflag = false;
544 
545     bool is_reply_area = (mm.areaList->getAreaNo() == REPLY_AREA);
546 
547     NM = mm.letterList->getNetAddr();
548 
549     replyto_area = ui->areaMenu();
550     if (replyto_area == -1)
551         return;
552 
553     // The refnum is only good for the original area:
554 
555     replyto_num = (replyto_area != mm.letterList->getAreaID()) ?
556         0 : mm.letterList->getReplyTo();
557 
558     char *msgid = strdupplus(mm.letterList->getMsgID());
559 
560     mm.areaList->gotoArea(replyto_area);
561 
562     if (forwarding) {
563         if (mm.areaList->isEmail()) {
564             ui->areas.Select();
565             ui->addressbook();
566             mm.areaList->gotoArea(replyto_area);
567         } else {
568             NM.isSet = false;
569             newsflag = mm.areaList->isUsenet();
570         }
571         setToFrom('E', TO, FROM);
572         sprintf(SUBJ, "%.513s", mm.letterList->getSubject());
573         privat = false;
574     } else {
575         const char *newsgrps = mm.letterList->getNewsgrps();
576         newsflag = !(!newsgrps);
577 
578         strcpy(FROM, mm.letterList->getFrom());
579         sprintf(TO, "%.512s", newsflag ? newsgrps : mm.letterList->getTo());
580         sprintf(SUBJ, "%.512s", mm.letterList->getSubject());
581         privat = mm.letterList->getPrivate();
582     }
583 
584     if (!EnterHeader(FROM, TO, SUBJ, privat)) {
585         NM.isSet = false;
586         ui->areas.Select();
587         ui->redraw();
588         return;
589     }
590 
591     DestroyChain();  // current letter's chain reset
592 
593     char *reply_filename = mytmpnam();
594 
595     reply = fopen(reply_filename, "wt");
596     if (forwarding)
597         forward_header(reply, FROM, TO, SUBJ, replyto_area, is_reply_area);
598 
599     letter_body *msgBody = mm.letterList->getBody();
600 
601     while (msgBody) {
602         char *body = msgBody->getText();
603         int offset = 0;
604 
605         letterconv_in(body);
606 
607         // Can't use MakeChain here, or it will wrap the text; so:
608         if (!hidden)  // skip hidden lines at start
609             while (*body == 1) {
610                 do {
611                     body++;
612                     offset++;
613                 } while (*body && (*body != '\n'));
614                 while (*body == '\n') {
615                     body++;
616                     offset++;
617                 }
618             }
619 
620         fwrite(body, msgBody->getLength() - offset, 1, reply);
621 
622         msgBody = msgBody->next;
623     }
624     fclose(reply);
625 
626     edit(reply_filename);
627     siz = reconvert(reply_filename);
628 
629     if (!forwarding)
630         mm.areaList->killLetter(mm.letterList->getAreaID(),
631                                 mm.letterList->getMsgNum());
632 
633     mm.areaList->enterLetter(replyto_area, FROM, newsflag ? "All" :
634         TO, SUBJ, msgid, newsflag ? TO : 0, replyto_num, privat,
635         NM, reply_filename, siz);
636 
637     delete[] reply_filename;
638     delete[] msgid;
639 
640     if (is_reply_area) {
641         mm.letterList->rrefresh();
642         ui->letters.ResetActive();
643     }
644     ui->areas.Select();
645 
646     NM.isSet = false;
647 
648     ui->redraw();
649     ui->setUnsaved();
650 }
651 
SplitLetter(int lines)652 bool LetterWindow::SplitLetter(int lines)
653 {
654     static int eachmax = mm.resourceObject->getInt(MaxLines);
655 
656     if (!lines) {
657         char maxlinesA[55];
658 
659         sprintf(maxlinesA, "%d", eachmax);
660         if (!ui->savePrompt(
661             "Max lines per part? (WARNING: Split is not reversible!)",
662             maxlinesA) || !sscanf(maxlinesA, "%d", &eachmax))
663 
664             return false;
665     }
666     unsigned int maxlines = lines ? lines : eachmax;
667 
668     if (maxlines < 20) {
669         ui->nonFatalError("Split at less than 20 lines not allowed");
670         return false;
671     }
672 
673     MakeChain(80);
674     unsigned int orglines = NumOfLines;
675     unsigned int parts = (orglines - 1) / maxlines + 1;
676 
677     if (parts == 1)
678         return false;
679 
680     NM = mm.letterList->getNetAddr();
681 
682     int replyto_area = mm.letterList->getAreaID();
683     int replyto_num = mm.letterList->getReplyTo();
684 
685     char ORGSUBJ[514], *from, *to, *msgid, *newsgrps;
686 
687     from = strdupplus(mm.letterList->getFrom());
688     to = strdupplus(mm.letterList->getTo());
689     msgid = replyto_num ? strdupplus(mm.letterList->getMsgID()) : 0;
690     newsgrps = strdupplus(mm.letterList->getNewsgrps());
691 
692     sprintf(ORGSUBJ, "%.510s", mm.letterList->getSubject());
693 
694     unsigned int x = parts;
695     unsigned int padsize = 1;
696     while (x /= 10)
697         padsize++;
698 
699     bool privat = mm.letterList->getPrivate();
700 
701     unsigned int clines = 0;
702 
703     mm.areaList->killLetter(replyto_area, mm.letterList->getMsgNum());
704 
705     for (unsigned int partno = 1; partno <= parts; partno++) {
706         FILE *reply;
707         char SUBJ[514];
708 
709         char *reply_filename = mytmpnam();
710 
711         unsigned int endline = (orglines > maxlines) ? maxlines: orglines;
712 
713         unsigned long replen = 0;
714         reply = fopen(reply_filename, "wt");
715         for (unsigned int i = clines; i < (clines + endline); i++) {
716             linelist[i]->out(reply);
717             replen += linelist[i]->length + 1;
718         }
719         fclose(reply);
720 
721         sprintf(SUBJ, "%.499s (%0*d/%d)", ORGSUBJ, padsize, partno, parts);
722 
723         mm.areaList->enterLetter(replyto_area, from, to, SUBJ, msgid,
724             newsgrps, replyto_num, privat, NM, reply_filename, (long) replen);
725 
726         delete[] reply_filename;
727 
728         clines += endline;
729         orglines -= endline;
730     }
731     delete[] newsgrps;
732     delete[] msgid;
733     delete[] to;
734     delete[] from;
735 
736     NM.isSet = false;
737 
738     mm.letterList->rrefresh();
739 
740     if (!lines) {
741         ui->letters.ResetActive();
742         ui->areas.Select();
743         ui->setUnsaved();
744     }
745     return true;
746 }
747 
GetTagline()748 void LetterWindow::GetTagline()
749 {
750     ui->taglines.EnterTagline(tagline1);
751     ReDraw();
752 }
753 
EditOriginal()754 bool LetterWindow::EditOriginal()
755 {
756     int old_area = mm.letterList->getAreaID();
757     long old_mnum = mm.letterList->getMsgNum();
758     int orig_area = mm.areaList->getAreaNo();
759     int orig_mnum = mm.letterList->getCurrent();
760     int oldPos = position;
761     position = 0;
762 
763     letter_list *old_list = mm.letterList;
764     mm.areaList->gotoArea(REPLY_AREA);
765     mm.areaList->getLetterList();
766     ui->areas.ResetActive();
767 
768     bool found = mm.letterList->findReply(old_area, old_mnum);
769 
770     if (found && ui->WarningWindow("A reply exists. Re-edit it?"))
771         EditLetter(false);
772     else
773         found = false;
774 
775     position = oldPos;
776     delete mm.letterList;
777     mm.letterList = old_list;
778     mm.areaList->gotoArea(orig_area);
779     ui->areas.ResetActive();
780     mm.letterList->gotoLetter(orig_mnum);
781     ui->letters.ResetActive();
782 
783     return found;
784 }
785 
SplitAll(int lines)786 void LetterWindow::SplitAll(int lines)
787 {
788     letter_list *old_list = 0;
789     statetype state = ui->active();
790     bool list_is_active = (state == letter) || (state == letterlist);
791 
792     ui->areas.Select();
793     int orig_area = mm.areaList->getAreaNo();
794 
795     bool is_reply_area = (orig_area == REPLY_AREA) && list_is_active;
796 
797     if (!is_reply_area) {
798         if (list_is_active)
799             old_list = mm.letterList;
800 
801         mm.areaList->gotoArea(REPLY_AREA);
802         mm.areaList->getLetterList();
803         ui->areas.ResetActive();
804     }
805 
806     bool anysplit = false;
807     int last = mm.letterList->noOfActive();
808     for (int i = 0; i < last; i++) {
809         mm.letterList->gotoActive(i);
810         if (SplitLetter(lines)) {
811             i--;
812             last--;
813             anysplit = true;
814         }
815     }
816 
817     if (is_reply_area)
818         ui->letters.ResetActive();
819     else {
820         delete mm.letterList;
821         if (list_is_active)
822             mm.letterList = old_list;
823     }
824     mm.areaList->gotoArea(orig_area);
825     ui->areas.ResetActive();
826 
827     if ((state == letter) && !is_reply_area)
828         MakeChain(COLS);
829 
830     if (anysplit)
831         ui->nonFatalError("Some replies were split");
832 }
833