1 
2 //  ------------------------------------------------------------------
3 //  GoldED+
4 //  Copyright (C) 1990-1999 Odinn Sorensen
5 //  Copyright (C) 1999-2000 Alexander S. Aganichev
6 //  ------------------------------------------------------------------
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License as
9 //  published by the Free Software Foundation; either version 2 of the
10 //  License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 //  General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 //  MA 02111-1307 USA
21 //  ------------------------------------------------------------------
22 //  $Id: geline.cpp,v 1.82 2011/12/01 10:13:51 stas_degteff Exp $
23 //  ------------------------------------------------------------------
24 //  Conversion of a raw message to a linked list of lines.
25 //  ------------------------------------------------------------------
26 
27 #include <cstdarg>
28 #include <limits.h>
29 #include <golded.h>
30 #include <gstrmail.h>
31 #include <gutlcode.h>
32 #include <ghdrmime.h>
33 
34 #if defined(__USE_ALLOCA__)
35   #include <malloc.h>
36 #endif
37 
38 #ifdef HAS_ICONV
39   #include <iconv.h>
40 #endif
41 
42 
43 //  ------------------------------------------------------------------
44 
45 #ifdef __UNIX__
46 #define GOLDMARK '^' // xwindow fonts has no square sign :(
47 #else
48 #define GOLDMARK '\xFD'
49 #endif
50 
51 
52 //  ------------------------------------------------------------------
53 
54 const int BODYLINE = 0;
55 const int HEADERLINE = 1;
56 const int USERDEFINED = 2;
57 const int RFC_X = 3;
58 
59 enum {
60   MASK_FTS = 0x1000,
61   FTS_AREA,
62   FTS_INTL,
63   FTS_FMPT,
64   FTS_TOPT,
65   FTS_MSGID,
66   FTS_REPLY,
67   FTS_SEENBY,
68   FTS_PATH,
69   FTS_ZZZZ
70 };
71 
72 enum {
73   MASK_FSC = 0x2000,
74   FSC_CHARSET,
75   FSC_CHRC,
76   FSC_CHRS,
77   FSC_CODEPAGE,
78   FSC_DOMAIN,
79   FSC_EID,
80   FSC_ENC,
81   FSC_ENCLFILE,
82   FSC_FLAGS,
83   FSC_FWDFROM,
84   FSC_FWDORIG,
85   FSC_FWDTO,
86   FSC_FWDDEST,
87   FSC_FWDSUBJ,
88   FSC_FWDAREA,
89   FSC_FWDMSGID,
90   FSC_I51,
91   FSC_MSGTO,
92   FSC_PID,
93   FSC_PTH,
94   FSC_REPLYADDR,
95   FSC_REPLYTO,
96   FSC_SPLIT,
97   FSC_SPTH,
98   FSC_TID,
99   FSC_ZZZZ
100 };
101 
102 enum {
103   MASK_XXX = 0x8000,
104   XXX_ACUPDATE,
105   XXX_DESTADDR,
106   XXX_ENCRYPTION,
107   XXX_EOT,
108   XXX_GATECHK,
109   XXX_GID,
110   XXX_GIF,
111   XXX_GMD,
112   XXX_GROUP,
113   XXX_MOOD,
114   XXX_MSGSEQ,
115   XXX_NOTE,
116   XXX_ORIGID,
117   XXX_ORIGINAL,
118   XXX_ORIGREF,
119   XXX_RECD,
120   XXX_RFC,
121   XXX_RFD,
122   XXX_RID,
123   XXX_ROUTE,
124   XXX_SN,
125   XXX_SOT,
126   XXX_TCL1,
127   XXX_TCL2,
128   XXX_TZUTCINFO,
129   XXX_TZUTC,
130   XXX_TZ,
131   XXX_VIA,
132   XXX_XID,
133   XXX_ZZZZ
134 };
135 
136 enum {
137   MASK_RFC = 0x4000,
138   RFC_ALSO_CONTROL,
139   RFC_APPARENTLY_TO,
140   RFC_APPROVED,
141   RFC_ARTICLE_NAMES,
142   RFC_ARTICLE_UPDATES,
143   RFC_BCC,
144   RFC_CC,
145   RFC_COMMENT,
146   RFC_COMMENTS,
147   RFC_CONTENT_DESCRIPTION,
148   RFC_CONTENT_DISPOSITION,
149   RFC_CONTENT_ID,
150   RFC_CONTENT_LENGTH,
151   RFC_CONTENT_TRANSFER_ENCODING,
152   RFC_CONTENT_TYPE,
153   RFC_CONTROL,
154   RFC_DATE,
155   RFC_DELIVERED_TO,
156   RFC_DELIVERY_DATE,
157   RFC_DISTRIBUTION,
158   RFC_ENCRYPTED,
159   RFC_ERRORS_TO,
160   RFC_EXPIRES,
161   RFC_FOLLOWUP_TO,
162   RFC_FROM,
163   RFC_FROMX,
164   RFC_IN_REPLY_TO,
165   RFC_KEYWORDS,
166   RFC_LINES,
167   RFC_MAILING_LIST,                   // This one is actually _not_ RFC
168   RFC_MESSAGE_ID,
169   RFC_MIME_VERSION,
170   RFC_NEWSGROUPS,
171   RFC_NEWS_SOFTWARE,
172   RFC_NNTP_POSTING_DATE,
173   RFC_NNTP_POSTING_HOST,
174   RFC_NNTP_POSTING_USER,
175   RFC_OLD_DATE,
176   RFC_ORGANIZATION,
177   RFC_ORIGINATOR,
178   RFC_PATH,
179   RFC_PRECEDENCE,
180   RFC_PRIORITY,
181   RFC_RECEIVED,
182   RFC_REFERENCES,
183   RFC_REPLY_TO,
184   RFC_RETURN_PATH,
185   RFC_RETURN_RECEIPT_TO,
186   RFC_SEE_ALSO,
187   RFC_SENDER,
188   RFC_STATUS,
189   RFC_SUBJECT,
190   RFC_SUMMARY,
191   RFC_SUPERSEDES,
192   RFC_TO,
193   RFC_VERSION,
194   RFC_XREF,
195   RFC_X_CHARSET,
196   RFC_X_CHAR_ESC,
197   RFC_X_FTN_TO,
198   RFC_X_MAILER,
199   RFC_X_NEWSREADER,
200   RFC_X_TO,
201   RFC_RNEWS,
202   RFC_ZZZZ
203 };
204 
205 #define MASK_ALL (MASK_FTS|MASK_FSC|MASK_RFC|MASK_XXX)
206 
207 
208 //  ------------------------------------------------------------------
209 
210 struct Kludges {
211   char* key;
212   uint  num;
213   byte  req;
214 };
215 
216 
217 //  ------------------------------------------------------------------
218 
219 const byte KCRQ_NONE  = 0x0000;
220 const byte KCRQ_COLON = 0x0001;
221 const byte KCRQ_CASE  = 0x0002;
222 
223 
224 //  ------------------------------------------------------------------
225 
226 static const Kludges fts_list[] = {
227 
228   { "AREA"                       , FTS_AREA                      , KCRQ_CASE  },
229   { "INTL"                       , FTS_INTL                      , KCRQ_CASE  },
230   { "FMPT"                       , FTS_FMPT                      , KCRQ_CASE  },
231   { "TOPT"                       , FTS_TOPT                      , KCRQ_CASE  },
232   { "MSGID"                      , FTS_MSGID                     , KCRQ_CASE  },
233   { "REPLY"                      , FTS_REPLY                     , KCRQ_CASE  },
234   { "SEEN-BY"                    , FTS_SEENBY                    , KCRQ_CASE  },
235   { "PATH"                       , FTS_PATH                      , KCRQ_CASE  },
236   { ""                           , FTS_ZZZZ                      , KCRQ_NONE  },
237 };
238 
239 
240 //  ------------------------------------------------------------------
241 
242 static const Kludges fsc_list[] = {
243 
244   { "CHARSET"                    , FSC_CHARSET                   , KCRQ_CASE  },
245   { "CHRC"                       , FSC_CHRC                      , KCRQ_CASE  },
246   { "CHRS"                       , FSC_CHRS                      , KCRQ_CASE  },
247   { "CODEPAGE"                   , FSC_CODEPAGE                  , KCRQ_CASE  },
248   { "DOMAIN"                     , FSC_DOMAIN                    , KCRQ_CASE  },
249   { "EID"                        , FSC_EID                       , KCRQ_CASE  },
250   { "ENC"                        , FSC_ENC                       , KCRQ_CASE  },
251   { "ENCLFILE"                   , FSC_ENCLFILE                  , KCRQ_CASE  },
252   { "FLAGS"                      , FSC_FLAGS                     , KCRQ_CASE  },
253   { "FWDFROM"                    , FSC_FWDFROM                   , KCRQ_CASE  },
254   { "FWDORIG"                    , FSC_FWDORIG                   , KCRQ_CASE  },
255   { "FWDTO"                      , FSC_FWDTO                     , KCRQ_CASE  },
256   { "FWDDEST"                    , FSC_FWDDEST                   , KCRQ_CASE  },
257   { "FWDSUBJ"                    , FSC_FWDSUBJ                   , KCRQ_CASE  },
258   { "FWDAREA"                    , FSC_FWDAREA                   , KCRQ_CASE  },
259   { "FWDMSGID"                   , FSC_FWDMSGID                  , KCRQ_CASE  },
260   { "I51"                        , FSC_I51                       , KCRQ_CASE  },
261   { "MSGTO"                      , FSC_MSGTO                     , KCRQ_CASE  },
262   { "PID"                        , FSC_PID                       , KCRQ_CASE  },
263   { "PTH"                        , FSC_PTH                       , KCRQ_CASE  },
264   { "REPLYADDR"                  , FSC_REPLYADDR                 , KCRQ_CASE  },
265   { "REPLYTO"                    , FSC_REPLYTO                   , KCRQ_CASE  },
266   { "SPLIT"                      , FSC_SPLIT                     , KCRQ_CASE  },
267   { "SPTH"                       , FSC_SPTH                      , KCRQ_CASE  },
268   { "TID"                        , FSC_TID                       , KCRQ_CASE  },
269   { ""                           , FSC_ZZZZ                      , KCRQ_NONE  },
270 };
271 
272 
273 //  ------------------------------------------------------------------
274 
275 static const Kludges xxx_list[] = {
276 
277   { "ACUPDATE"                   , XXX_ACUPDATE                  , KCRQ_CASE  },
278   { "DESTADDR"                   , XXX_DESTADDR                  , KCRQ_CASE  },
279   { "ENCRYPTION"                 , XXX_ENCRYPTION                , KCRQ_CASE  },
280   { "EOT"                        , XXX_EOT                       , KCRQ_CASE  },
281   { "GATECHK"                    , XXX_GATECHK                   , KCRQ_CASE  },
282   { "GID"                        , XXX_GID                       , KCRQ_CASE  },
283   { "GIF"                        , XXX_GIF                       , KCRQ_CASE  },
284   { "GMD"                        , XXX_GMD                       , KCRQ_CASE  },
285   { "GROUP"                      , XXX_GROUP                     , KCRQ_CASE  },
286   { "MOOD"                       , XXX_MOOD                      , KCRQ_CASE  },
287   { "MSGSEQ"                     , XXX_MSGSEQ                    , KCRQ_CASE  },
288   { "NOTE"                       , XXX_NOTE                      , KCRQ_CASE  },
289   { "ORIGID"                     , XXX_ORIGID                    , KCRQ_CASE  },
290   { "Original"                   , XXX_ORIGINAL                  , KCRQ_NONE  },
291   { "ORIGREF"                    , XXX_ORIGREF                   , KCRQ_CASE  },
292   { "Recd"                       , XXX_RECD                      , KCRQ_CASE  },
293   { "RFC"                        , XXX_RFC                       , KCRQ_CASE  },
294   { "RFD"                        , XXX_RFD                       , KCRQ_CASE  },
295   { "RID"                        , XXX_RID                       , KCRQ_CASE  },
296   { "#ROUTE"                     , XXX_ROUTE                     , KCRQ_CASE  },
297   { "SN"                         , XXX_SN                        , KCRQ_CASE  },
298   { "SOT"                        , XXX_SOT                       , KCRQ_CASE  },
299   { "TCL1"                       , XXX_TCL1                      , KCRQ_CASE  },
300   { "TCL2"                       , XXX_TCL2                      , KCRQ_CASE  },
301   { "TZUTCINFO"                  , XXX_TZUTCINFO                 , KCRQ_CASE  },
302   { "TZUTC"                      , XXX_TZUTC                     , KCRQ_CASE  },
303   { "TZ"                         , XXX_TZ                        , KCRQ_CASE  },
304   { "Via"                        , XXX_VIA                       , KCRQ_NONE  },
305   { "XID"                        , XXX_XID                       , KCRQ_CASE  },
306   { ""                           , XXX_ZZZZ                      , KCRQ_NONE  }
307 };
308 
309 
310 //  ------------------------------------------------------------------
311 
312 static const Kludges rfc_list[] = {
313 
314   { "Also-Control"               , RFC_ALSO_CONTROL              , KCRQ_COLON },
315   { "Apparently-To"              , RFC_APPARENTLY_TO             , KCRQ_COLON },
316   { "Approved"                   , RFC_APPROVED                  , KCRQ_COLON },
317   { "Article-Names"              , RFC_ARTICLE_NAMES             , KCRQ_COLON },
318   { "Article-Updates"            , RFC_ARTICLE_UPDATES           , KCRQ_COLON },
319   { "Bcc"                        , RFC_BCC                       , KCRQ_COLON },
320   { "Cc"                         , RFC_CC                        , KCRQ_COLON },
321   { "Comment"                    , RFC_COMMENT                   , KCRQ_COLON },
322   { "Comments"                   , RFC_COMMENTS                  , KCRQ_COLON },
323   { "Content-Description"        , RFC_CONTENT_DESCRIPTION       , KCRQ_COLON },
324   { "Content-Disposition"        , RFC_CONTENT_DISPOSITION       , KCRQ_COLON },
325   { "Content-ID"                 , RFC_CONTENT_ID                , KCRQ_COLON },
326   { "Content-Length"             , RFC_CONTENT_LENGTH            , KCRQ_COLON },
327   { "Content-Transfer-Encoding"  , RFC_CONTENT_TRANSFER_ENCODING , KCRQ_COLON },
328   { "Content-Type"               , RFC_CONTENT_TYPE              , KCRQ_COLON },
329   { "Control"                    , RFC_CONTROL                   , KCRQ_COLON },
330   { "Date"                       , RFC_DATE                      , KCRQ_COLON },
331   { "Delivered-To"               , RFC_DELIVERED_TO              , KCRQ_COLON },
332   { "Delivery-Date"              , RFC_DELIVERY_DATE             , KCRQ_COLON },
333   { "Distribution"               , RFC_DISTRIBUTION              , KCRQ_COLON },
334   { "Encrypted"                  , RFC_ENCRYPTED                 , KCRQ_COLON },
335   { "Errors-To"                  , RFC_ERRORS_TO                 , KCRQ_COLON },
336   { "Expires"                    , RFC_EXPIRES                   , KCRQ_COLON },
337   { "Followup-To"                , RFC_FOLLOWUP_TO               , KCRQ_COLON },
338   { "From"                       , RFC_FROM                      , KCRQ_COLON },
339   { "From"                       , RFC_FROMX                     , KCRQ_NONE  },
340   { "In-Reply-To"                , RFC_IN_REPLY_TO               , KCRQ_COLON },
341   { "Keywords"                   , RFC_KEYWORDS                  , KCRQ_COLON },
342   { "Lines"                      , RFC_LINES                     , KCRQ_COLON },
343   { "Message-ID"                 , RFC_MESSAGE_ID                , KCRQ_COLON },
344   { "Mailing-List"               , RFC_MAILING_LIST              , KCRQ_COLON },
345   { "MIME-Version"               , RFC_MIME_VERSION              , KCRQ_COLON },
346   { "Newsgroups"                 , RFC_NEWSGROUPS                , KCRQ_COLON },
347   { "News-Software"              , RFC_NEWS_SOFTWARE             , KCRQ_COLON },
348   { "NNTP-Posting-Date"          , RFC_NNTP_POSTING_DATE         , KCRQ_COLON },
349   { "NNTP-Posting-Host"          , RFC_NNTP_POSTING_HOST         , KCRQ_COLON },
350   { "NNTP-Posting-User"          , RFC_NNTP_POSTING_USER         , KCRQ_COLON },
351   { "Old-Date"                   , RFC_OLD_DATE                  , KCRQ_COLON },
352   { "Organization"               , RFC_ORGANIZATION              , KCRQ_COLON },
353   { "Originator"                 , RFC_ORIGINATOR                , KCRQ_COLON },
354   { "Path"                       , RFC_PATH                      , KCRQ_COLON },
355   { "Precedence"                 , RFC_PRECEDENCE                , KCRQ_COLON },
356   { "Priority"                   , RFC_PRIORITY                  , KCRQ_COLON },
357   { "Received"                   , RFC_RECEIVED                  , KCRQ_COLON },
358   { "References"                 , RFC_REFERENCES                , KCRQ_COLON },
359   { "Reply-To"                   , RFC_REPLY_TO                  , KCRQ_COLON },
360   { "Return-Path"                , RFC_RETURN_PATH               , KCRQ_COLON },
361   { "Return-Receipt-To"          , RFC_RETURN_RECEIPT_TO         , KCRQ_COLON },
362   { "See-Also"                   , RFC_SEE_ALSO                  , KCRQ_COLON },
363   { "Sender"                     , RFC_SENDER                    , KCRQ_COLON },
364   { "Status"                     , RFC_STATUS                    , KCRQ_COLON },
365   { "Subject"                    , RFC_SUBJECT                   , KCRQ_COLON },
366   { "Summary"                    , RFC_SUMMARY                   , KCRQ_COLON },
367   { "Supersedes"                 , RFC_SUPERSEDES                , KCRQ_COLON },
368   { "To"                         , RFC_TO                        , KCRQ_COLON },
369   { "Version"                    , RFC_VERSION                   , KCRQ_COLON },
370   { "Xref"                       , RFC_XREF                      , KCRQ_COLON },
371   { "X-Charset"                  , RFC_X_CHARSET                 , KCRQ_COLON },
372   { "X-Char-Esc"                 , RFC_X_CHAR_ESC                , KCRQ_COLON },
373   { "X-FTN-To"                   , RFC_X_FTN_TO                  , KCRQ_COLON },
374   { "X-Mailer"                   , RFC_X_MAILER                  , KCRQ_COLON },
375   { "X-Mailreader"               , RFC_X_MAILER                  , KCRQ_COLON },
376   { "X-Newsreader"               , RFC_X_NEWSREADER              , KCRQ_COLON },
377   { "X-To"                       , RFC_X_TO                      , KCRQ_COLON },
378   { "#!"                         , RFC_RNEWS                     , KCRQ_NONE  },
379   { ""                           , RFC_ZZZZ                      , KCRQ_NONE  },
380 };
381 
382 
mime_header_decode(char * decoded,const char * encoded,char * charset)383 char* mime_header_decode(char* decoded, const char* encoded, char *charset) {
384 
385   char dbuf[200], cbuf[100], ebuf[50], tbuf[200];
386   char* dptr = decoded;
387   const char* eptr = encoded;
388   if(charset) *charset = NUL;
389   while(*eptr) {
390     if(*eptr == '=') {
391       const char* mptr = mime_crack_encoded_word(eptr, cbuf, ebuf, tbuf);
392       if(mptr) {
393         if(charset) {
394           strxcpy(charset, cbuf, 100);
395           charset = NULL;
396         }
397         bool okay = false;
398         strchg(tbuf, '_', ' ');
399         if(strieql(ebuf, "Q")) {
400           quoted_printable_engine qb;
401           qb.decode(dbuf, tbuf);
402           dptr = stpcpy(dptr, dbuf);
403           okay = true;
404         }
405         else if(strieql(ebuf, "B")) {
406           base64_engine b64;
407           b64.decode(dbuf, tbuf);
408           dptr = stpcpy(dptr, dbuf);
409           okay = true;
410         }
411         if(okay) {
412           eptr = mptr;
413           mptr = strskip_lwsp(mptr);
414           if(mime_crack_encoded_word(mptr, cbuf, ebuf, tbuf))
415             eptr = mptr;
416           continue;
417         }
418       }
419     }
420     *dptr++ = *eptr++;
421   }
422   *dptr = NUL;
423 
424   return decoded;
425 }
426 
427 
428 //  ------------------------------------------------------------------
429 
strxmimecpy(char * dest,const char * source,int level,int size,bool detect)430 char* strxmimecpy(char* dest, const char* source, int level, int size, bool detect) {
431 
432   ISub buf, buf2;
433   char charset[100];
434   int table = -1;
435 
436   strxcpy(buf, source, sizeof(buf));
437   mime_header_decode(buf2, buf, charset);
438 
439   if(charset[0] == NUL)
440     detect = false;
441 
442   if(detect) {
443     table = LoadCharset(NULL, NULL, 1);
444     level = LoadCharset(charset, CFG->xlatlocalset);
445     if(not level) {
446       level = LoadCharset(AA->Xlatimport(), CFG->xlatlocalset);
447     }
448   }
449 
450   XlatStr(buf, buf2, level, CharTable);
451 
452   if(detect) {
453     if(table == -1)
454       LoadCharset("N/A", "N/A");
455     else
456       LoadCharset(CFG->xlatcharset[table].imp, CFG->xlatcharset[table].exp);
457   }
458 
459   strxcpy(dest, buf, size);
460 
461   return dest;
462 }
463 
464 
465 //  ------------------------------------------------------------------
466 
KludgeAREA(GMsg * msg,const char * echoid)467 static void KludgeAREA(GMsg* msg, const char* echoid) {
468 
469   if(AA->Usearea()) {
470     Area* ap = AL.AreaEchoToPtr(echoid);
471     if(ap)
472       msg->areakludgeid = ap->echoid();
473   }
474 }
475 
476 
477 //  ------------------------------------------------------------------
478 
KludgeINTL(GMsg * msg,const char * ptr)479 static void KludgeINTL(GMsg* msg, const char* ptr) {
480 
481   char buf1[201], buf2[201];
482   word fmpt = msg->orig.point;
483   word topt = msg->dest.point;
484   sscanf(ptr, "%s %s", buf1, buf2);
485   msg->dest.set(buf1);
486   msg->orig.set(buf2);
487   msg->orig.point = fmpt;
488   msg->dest.point = topt;
489 }
490 
491 
492 //  ------------------------------------------------------------------
493 
KludgeFMPT(GMsg * msg,const char * ptr)494 static void KludgeFMPT(GMsg* msg, const char* ptr) {
495 
496   msg->orig.point = atow(ptr);
497 }
498 
499 
500 //  ------------------------------------------------------------------
501 
KludgeTOPT(GMsg * msg,const char * ptr)502 static void KludgeTOPT(GMsg* msg, const char* ptr) {
503 
504   msg->dest.point = atow(ptr);
505 }
506 
507 
508 //  ------------------------------------------------------------------
509 
KludgeMSGID(GMsg * msg,const char * ptr)510 static void KludgeMSGID(GMsg* msg, const char* ptr) {
511 
512   strxcpy(msg->msgids, ptr, sizeof(msg->msgids));
513   msg->msgid.reset(msg->msgids, msg->odom);
514 }
515 
516 
517 //  ------------------------------------------------------------------
518 
KludgeREPLY(GMsg * msg,const char * ptr)519 static void KludgeREPLY(GMsg* msg, const char* ptr) {
520 
521   strxcpy(msg->replys, ptr, sizeof(msg->replys));
522 }
523 
524 
525 //  ------------------------------------------------------------------
526 
KludgeDOMAIN(GMsg * msg,const char * ptr)527 static void KludgeDOMAIN(GMsg* msg, const char* ptr) {
528 
529   char buf1[201], buf2[201];
530   sscanf(ptr, "%s %s %s %s", msg->ddom, buf1, msg->odom, buf2);
531   msg->dest.reset(buf1);
532   msg->orig.reset(buf2);
533 }
534 
535 
536 //  ------------------------------------------------------------------
537 
KludgeFLAGS(GMsg * msg,const char * ptr)538 static void KludgeFLAGS(GMsg* msg, const char* ptr) {
539 
540   GetAttribstr(&msg->attr, ptr);
541 }
542 
543 
544 //  ------------------------------------------------------------------
545 
KludgeMSGTO(GMsg * msg,const char * ptr)546 static void KludgeMSGTO(GMsg* msg, const char* ptr) {
547 
548   msg->dest.reset(ptr, msg->ddom);
549 }
550 
551 
552 //  ------------------------------------------------------------------
553 
KludgePID(GMsg * msg,const char * ptr)554 static void KludgePID(GMsg* msg, const char* ptr) {
555 
556   strxcpy(msg->pid, ptr, sizeof(msg->pid));
557 
558   if(CFG->gedhandshake) {
559     // Recognize another GoldED msg
560     if(striinc(__gver_name__, ptr) or striinc(__gver_shortname__, ptr))
561       goldmark = GOLDMARK;
562   }
563 }
564 
565 
566 //  ------------------------------------------------------------------
567 
KludgeREPLYADDR(GMsg * msg,const char * ptr)568 static void KludgeREPLYADDR(GMsg* msg, const char* ptr) {
569 
570   INam name;
571   char *buf=throw_strdup(ptr);
572   *name = NUL;
573   ParseInternetAddr(buf, *msg->realby ? name : msg->realby, msg->iaddr);
574   if(*name)
575     strxcpy(msg->realby, name, sizeof(INam));
576   throw_free(buf);
577 }
578 
579 
580 //  ------------------------------------------------------------------
581 
KludgeREPLYTO(GMsg * msg,const char * ptr)582 static void KludgeREPLYTO(GMsg* msg, const char* ptr) {
583 
584   strcpy(msg->igate, ptr);
585 }
586 
587 
588 //  ------------------------------------------------------------------
589 
KludgeFROM(GMsg * msg,const char * ptr)590 static void KludgeFROM(GMsg* msg, const char* ptr) {
591 
592   INam _fromname;
593   IAdr _fromaddr;
594   char* buf = throw_strdup(ptr);
595   strxmimecpy(msg->ifrom, buf, 0, sizeof(msg->ifrom), true);
596   ParseInternetAddr(buf, _fromname, _fromaddr);
597   throw_free(buf);
598   if(*_fromaddr)
599     strcpy(msg->iorig, _fromaddr);
600   if(*_fromname)
601     strxcpy(msg->realby, _fromname, sizeof(msg->realby));
602 }
603 
604 
605 //  ------------------------------------------------------------------
606 
KludgeTO(GMsg * msg,const char * ptr)607 static void KludgeTO(GMsg* msg, const char* ptr) {
608 
609   INam _toname;
610   IAdr _toaddr;
611   char* buf = throw_strdup(ptr);
612   strxmimecpy(msg->ito, buf, 0, sizeof(msg->ito), true);
613   ParseInternetAddr(buf, _toname, _toaddr);
614   throw_free(buf);
615   if(*_toaddr)
616     strcpy(msg->idest, _toaddr);
617   if(*_toname)
618     strxcpy(msg->realto, _toname, sizeof(msg->realto));
619 }
620 
621 
622 //  ------------------------------------------------------------------
623 
KludgeBCC(GMsg * msg,const char * ptr)624 static void KludgeBCC(GMsg* msg, const char* ptr) {
625 
626   INam _toname;
627   IAdr _toaddr, buf;
628 
629   gstrarray bccs;
630   tokenize(bccs, ptr, ",");
631   for(int i=0; i < bccs.size(); i++) {
632     strxcpy(buf, strskip_wht(bccs[i].c_str()), sizeof(IAdr));
633     ParseInternetAddr(buf, _toname, _toaddr);
634     if(*_toaddr and not striinc(_toaddr, msg->ibcc)) {
635       if(*msg->ibcc)
636         strxcat(msg->ibcc, ", ", sizeof(msg->ibcc));
637       if(_toname[0] != NUL) {
638         IAdr buf2;
639         mime_header_encode(buf2, _toname, msg);
640         char quot[2] = "\"";
641         if ((buf2[0] == '\"') or (strpbrk(buf2, " \t") == NULL))
642           quot[0] = NUL;
643         gsprintf(PRINTF_DECLARE_BUFFER(buf), "%s%s%s <%s>", quot, buf2, quot, _toaddr);
644       }
645       else
646         gsprintf(PRINTF_DECLARE_BUFFER(buf), "%s", _toaddr);
647 
648       strxcat(msg->ibcc, buf, sizeof(msg->ibcc));
649     }
650   }
651 }
652 
653 
654 //  ------------------------------------------------------------------
655 
KludgeCC(GMsg * msg,const char * ptr)656 static void KludgeCC(GMsg* msg, const char* ptr) {
657 
658   INam _toname;
659   IAdr _toaddr, buf;
660 
661   gstrarray ccs;
662   tokenize(ccs, ptr, ",");
663   for(int i=0; i < ccs.size(); i++) {
664     strxcpy(buf, strskip_wht(ccs[i].c_str()), sizeof(IAdr));
665     ParseInternetAddr(buf, _toname, _toaddr);
666     if(*_toaddr and not striinc(_toaddr, msg->icc)) {
667       if(*msg->icc)
668         strxcat(msg->icc, ", ", sizeof(msg->icc));
669       if(_toname[0] != NUL) {
670         IAdr buf2;
671         mime_header_encode(buf2, _toname, msg);
672         char quot[2] = "\"";
673         if ((buf[0] == '\"') or (strpbrk(buf2, " \t") == NULL))
674           quot[0] = NUL;
675         gsprintf(PRINTF_DECLARE_BUFFER(buf), "%s%s%s <%s>", quot, buf2, quot, _toaddr);
676       }
677       else
678         gsprintf(PRINTF_DECLARE_BUFFER(buf), "%s", _toaddr);
679 
680       strxcat(msg->icc, buf, sizeof(msg->icc));
681     }
682   }
683 }
684 
685 
686 //  ------------------------------------------------------------------
687 
KludgeREPLY_TO(GMsg * msg,const char * ptr)688 static void KludgeREPLY_TO(GMsg* msg, const char* ptr) {
689 
690   INam _rtname;
691   IAdr _rtaddr;
692   char *buf=throw_strdup(ptr);
693   ParseInternetAddr(buf, _rtname, _rtaddr);
694   throw_free(buf);
695   if(*_rtaddr)
696     strcpy(msg->ireplyto, _rtaddr);
697 }
698 
699 
700 //  ------------------------------------------------------------------
701 
KludgeSUBJECT(GMsg * msg,const char * ptr)702 static void KludgeSUBJECT(GMsg* msg, const char* ptr) {
703 
704   if(not (msg->attr.frq() or msg->attr.att() or msg->attr.urq()))
705     strxmimecpy(msg->re, ptr, 0, sizeof(msg->re), true);
706 }
707 
708 
709 //  ------------------------------------------------------------------
710 
KludgeTZUTC(GMsg * msg,const char * ptr)711 static void KludgeTZUTC(GMsg* msg, const char* ptr) {
712 
713   msg->tzutc = atoi(ptr);
714 }
715 
716 
717 //  ------------------------------------------------------------------
718 
KludgeDATE(GMsg * msg,const char * ptr)719 void KludgeDATE(GMsg* msg, const char* ptr) {
720 
721   // RFC822 Date: BNF
722   //
723   // date-time   =  [ day "," ] date time        ; dd mm yy
724   //                                             ;  hh:mm:ss zzz
725   //
726   // day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
727   //             /  "Fri"  / "Sat" /  "Sun"
728   //
729   // date        =  1*2DIGIT month 2DIGIT        ; day month year
730   //                                             ;  e.g. 20 Jun 82
731   //
732   // month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
733   //             /  "May"  /  "Jun" /  "Jul"  /  "Aug"
734   //             /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
735   //
736   // time        =  hour zone                    ; ANSI and Military
737   //
738   // hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
739   //                                             ; 00:00:00 - 23:59:59
740   //
741   // zone        =  "UT"  / "GMT"                ; Universal Time
742   //                                             ; North American : UT
743   //             /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
744   //             /  "CST" / "CDT"                ;  Central:  - 6/ - 5
745   //             /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
746   //             /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
747   //             /  1ALPHA                       ; Military: Z = UT;
748   //                                             ;  A:-1; (J not used)
749   //                                             ;  M:-12; N:+1; Y:+12
750   //             / ( ("+" / "-") 4DIGIT )        ; Local differential
751   //                                             ;  hours+min. (HHMM)
752 
753   bool date_ok = false;
754   int year=0, month=0, day=0;
755   int hour=0, minute=0, second=0;
756 
757   ptr = strskip_wht(ptr);
758   if(not isdigit(*ptr)) {
759     // Skip past weekday string
760     ptr = strskip_wht(strskip_txt(ptr));
761   }
762   if(*ptr) {
763     if(isdigit(*ptr)) {
764       day = atoi(ptr);
765       ptr = strskip_wht(strskip_txt(ptr));
766       if(g_isalpha(*ptr)) {
767         month = str2mon(ptr);
768         if(month) {
769           ptr = strskip_wht(strskip_txt(ptr));
770           if(isdigit(*ptr)) {
771             year = atoi(ptr);
772             ptr = strskip_wht(strskip_txt(ptr));
773             if(isdigit(*ptr)) {
774               hour = atoi(ptr);
775               ptr = strskip_digits(ptr);
776               if(*ptr and isdigit(ptr[1])) {
777                 minute = atoi(++ptr);
778                 date_ok = true;
779                 // The seconds part is optional
780                 ptr = strskip_digits(ptr);
781                 if(*ptr and isdigit(ptr[1])) {
782                   second = atoi(++ptr);
783                   // Setting timezone
784                   ptr = strskip_wht(strskip_digits(ptr));
785                   if(*ptr) {
786                     if(*ptr == '(' /*)*/ ) ++ptr;
787                     msg->tzutc = atoi(ptr);
788                   }
789                 }
790               }
791             }
792           }
793         }
794       }
795     }
796   }
797 
798   if (date_ok)
799   {
800     struct tm t;
801     t.tm_year  = (year < 80) ? (year+100) : (year > 1900) ? (year-1900) : year;
802     t.tm_mon   = month - 1;
803     t.tm_mday  = day;
804     t.tm_hour  = hour;
805     t.tm_min   = minute;
806     t.tm_sec   = second;
807     t.tm_isdst = -1;
808     time32_t a = gmktime(&t);
809     struct tm tp; ggmtime(&tp, &a);
810     tp.tm_isdst = -1;
811     time32_t b = gmktime(&tp);
812     msg->written = a + a - b;
813   }
814 }
815 
816 
817 //  ------------------------------------------------------------------
818 
KludgeMESSAGE_ID(GMsg * msg,const char * ptr)819 static void KludgeMESSAGE_ID(GMsg* msg, const char* ptr) {
820 
821   char buf[201];
822   throw_free(msg->messageid);
823   msg->messageid = throw_strdup(ptr);
824   CvtMessageIDtoMSGID(ptr, buf, AA->echoid(), "MSGID");
825   strcpy(msg->msgids, buf+8);
826 }
827 
828 
829 //  ------------------------------------------------------------------
830 
KludgeREFERENCES(GMsg * msg,const char * ptr)831 static void KludgeREFERENCES(GMsg* msg, const char* ptr) {
832 
833   throw_free(msg->references);
834   msg->references = throw_strdup(ptr);
835 }
836 
837 
838 //  ------------------------------------------------------------------
839 
KludgeIN_REPLY_TO(GMsg * msg,const char * ptr)840 static void KludgeIN_REPLY_TO(GMsg* msg, const char* ptr) {
841 
842   throw_free(msg->inreplyto);
843   msg->inreplyto = throw_strdup(ptr);
844 }
845 
846 
847 //  ------------------------------------------------------------------
848 
KludgeORGANIZATION(GMsg * msg,const char * ptr)849 static void KludgeORGANIZATION(GMsg* msg, const char* ptr) {
850 
851   strxcpy(msg->organization, ptr, sizeof(msg->organization));
852 }
853 
854 
855 //  ------------------------------------------------------------------
856 
KludgeX_FTN_TO(GMsg * msg,const char * ptr)857 static void KludgeX_FTN_TO(GMsg* msg, const char* ptr) {
858 
859   strxmimecpy(msg->realto, ptr, 0, sizeof(msg->realto), true);
860 }
861 
862 
863 //  ------------------------------------------------------------------
864 
KludgeFWDFROM(GMsg * msg,const char * ptr)865 static void KludgeFWDFROM(GMsg* msg, const char* ptr) {
866 
867   if(AA->Usefwd())
868     strxcpy(msg->fwdfrom, ptr, sizeof(msg->fwdfrom));
869 }
870 
871 
872 //  ------------------------------------------------------------------
873 
KludgeFWDORIG(GMsg * msg,const char * ptr)874 static void KludgeFWDORIG(GMsg* msg, const char* ptr) {
875 
876   if(AA->Usefwd())
877     msg->fwdorig.reset(ptr);
878 }
879 
880 
881 //  ------------------------------------------------------------------
882 
KludgeFWDTO(GMsg * msg,const char * ptr)883 static void KludgeFWDTO(GMsg* msg, const char* ptr) {
884 
885   if(AA->Usefwd())
886     strxcpy(msg->fwdto, ptr, sizeof(msg->fwdto));
887 }
888 
889 
890 //  ------------------------------------------------------------------
891 
KludgeFWDDEST(GMsg * msg,const char * ptr)892 static void KludgeFWDDEST(GMsg* msg, const char* ptr) {
893 
894   if(AA->Usefwd())
895     msg->fwddest.reset(ptr);
896 }
897 
898 
899 //  ------------------------------------------------------------------
900 
KludgeFWDSUBJ(GMsg * msg,const char * ptr)901 static void KludgeFWDSUBJ(GMsg* msg, const char* ptr) {
902 
903   if(AA->Usefwd())
904     strxcpy(msg->fwdsubj, ptr, sizeof(msg->fwdsubj));
905 }
906 
907 
908 //  ------------------------------------------------------------------
909 
KludgeFWDAREA(GMsg * msg,const char * ptr)910 static void KludgeFWDAREA(GMsg* msg, const char* ptr) {
911 
912   if(AA->Usefwd())
913     strxcpy(msg->fwdarea, ptr, sizeof(msg->fwdarea));
914 }
915 
916 
917 //  ------------------------------------------------------------------
918 
KludgeFWDMSGID(GMsg * msg,const char * ptr)919 static void KludgeFWDMSGID(GMsg* msg, const char* ptr) {
920 
921   if(AA->Usefwd())
922     strxcpy(msg->fwdmsgid, ptr, sizeof(msg->fwdmsgid));
923 }
924 
925 
926 //  ------------------------------------------------------------------
927 
UnwrapLine(Line * line,const char * ptr,int addspace=false)928 char* UnwrapLine(Line* line, const char* ptr, int addspace = false) {
929 
930   if(line->type & GLINE_WRAP) {
931     uint len = strlen(ptr);
932     char* uptr = throw_strdup(ptr);
933     while(line and (line->type & GLINE_WRAP)) {
934       if(line->next) {
935         uint nextlen = line->next->txt.length();
936         uptr = (char*)throw_realloc(uptr, len+nextlen+2);
937         if(addspace and len and (uptr[len-1] != ' ')) {
938           strcat(uptr, " ");
939           len++;
940         }
941         strcpy(uptr+len, line->next->txt.c_str());
942         len += nextlen;
943       }
944       line = line->next;
945     }
946     return uptr;
947   }
948 
949   return NULL;
950 }
951 
952 
953 //  ------------------------------------------------------------------
954 
HandleKludges(GMsg * msg,Line * line,int kludgenum,const char * ptr,int getvalue)955 static int HandleKludges(GMsg* msg, Line* line, int kludgenum, const char* ptr, int getvalue) {
956 
957   switch(kludgenum) {
958 
959     case FTS_AREA:
960       line->kludge = GKLUD_AREA;
961       if(getvalue)
962         KludgeAREA(msg, ptr);
963       return true;
964 
965     case FTS_INTL:
966       line->kludge = GKLUD_INTL;
967       if(getvalue)
968         KludgeINTL(msg, ptr);
969       return true;
970 
971     case FTS_FMPT:
972       line->kludge = GKLUD_FMPT;
973       if(getvalue)
974         KludgeFMPT(msg, ptr);
975       return true;
976 
977     case FTS_TOPT:
978       line->kludge = GKLUD_TOPT;
979       if(getvalue)
980         KludgeTOPT(msg, ptr);
981       return true;
982 
983     case FTS_MSGID:
984       line->kludge = GKLUD_MSGID;
985       if(getvalue) {
986         char* tmp = UnwrapLine(line, ptr);
987         KludgeMSGID(msg, tmp ? tmp : ptr);
988         if(tmp)
989           throw_free(tmp);
990       }
991       return true;
992 
993     case FTS_REPLY:
994       line->kludge = GKLUD_REPLY;
995       if(getvalue) {
996         char* tmp = UnwrapLine(line, ptr);
997         KludgeREPLY(msg, tmp ? tmp : ptr);
998         if(tmp)
999           throw_free(tmp);
1000       }
1001       return true;
1002 
1003     case FTS_SEENBY:
1004       line->kludge = GKLUD_SEENBY;
1005       return true;
1006 
1007     case FTS_PATH:
1008       line->kludge = GKLUD_PATH;
1009       return true;
1010 
1011     ////////////////////////////////////////////////////////////
1012 
1013     case FSC_CHARSET:
1014       line->kludge = GKLUD_CHARSET;
1015       return true;
1016 
1017     case FSC_CHRS:
1018       line->kludge = GKLUD_CHARSET;
1019       return true;
1020 
1021     case FSC_CODEPAGE:
1022       line->kludge = GKLUD_CHARSET;
1023       return true;
1024 
1025     case FSC_DOMAIN:
1026       //line->kludge = GKLUD_DOMAIN;
1027       if(getvalue)
1028         KludgeDOMAIN(msg, ptr);
1029       return true;
1030 
1031     case FSC_ENCLFILE:
1032       if(getvalue)
1033         strxcpy(msg->re, ptr, sizeof(msg->re));
1034       return true;
1035 
1036     case FSC_FLAGS:
1037       line->kludge = GKLUD_FLAGS;
1038       if(getvalue)
1039         KludgeFLAGS(msg, ptr);
1040       return true;
1041 
1042     case FSC_I51:
1043       line->kludge = GKLUD_CHARSET;
1044       msg->i51 = true;
1045       return true;
1046 
1047     case FSC_MSGTO:
1048       //line->kludge = GKLUD_MSGTO;
1049       if(getvalue)
1050         KludgeMSGTO(msg, ptr);
1051       return true;
1052 
1053     case FSC_PID:
1054       line->kludge = GKLUD_PID;
1055       if(getvalue)
1056         KludgePID(msg, ptr);
1057       return true;
1058 
1059     case FSC_REPLYADDR:
1060       line->kludge = GKLUD_REPLYADDR;
1061       if(getvalue)
1062         KludgeREPLYADDR(msg, ptr);
1063       return true;
1064 
1065     case FSC_REPLYTO:
1066       line->kludge = GKLUD_REPLYTO;
1067       if(getvalue)
1068         KludgeREPLYTO(msg, ptr);
1069       return true;
1070 
1071     case FSC_FWDFROM:
1072       line->kludge = GKLUD_FWD;
1073       if(getvalue)
1074         KludgeFWDFROM(msg, ptr);
1075       return true;
1076 
1077     case FSC_FWDORIG:
1078       line->kludge = GKLUD_FWD;
1079       if(getvalue)
1080         KludgeFWDORIG(msg, ptr);
1081       return true;
1082 
1083     case FSC_FWDTO:
1084       line->kludge = GKLUD_FWD;
1085       if(getvalue)
1086         KludgeFWDTO(msg, ptr);
1087       return true;
1088 
1089     case FSC_FWDDEST:
1090       line->kludge = GKLUD_FWD;
1091       if(getvalue)
1092         KludgeFWDDEST(msg, ptr);
1093       return true;
1094 
1095     case FSC_FWDSUBJ:
1096       line->kludge = GKLUD_FWD;
1097       if(getvalue)
1098         KludgeFWDSUBJ(msg, ptr);
1099       return true;
1100 
1101     case FSC_FWDAREA:
1102       line->kludge = GKLUD_FWD;
1103       if(getvalue)
1104         KludgeFWDAREA(msg, ptr);
1105       return true;
1106 
1107     case FSC_FWDMSGID:
1108       line->kludge = GKLUD_FWD;
1109       if(getvalue)
1110         KludgeFWDMSGID(msg, ptr);
1111       return true;
1112 
1113     case FSC_CHRC:
1114     case FSC_EID:
1115     case FSC_ENC:
1116     case FSC_PTH:
1117     case FSC_SPLIT:
1118     case FSC_SPTH:
1119     case FSC_TID:
1120       // Recognized but not processed
1121       return true;
1122 
1123     ////////////////////////////////////////////////////////////
1124 
1125     case XXX_DESTADDR:
1126       //line->kludge = GKLUD_DESTADDR;
1127       if(getvalue)
1128         KludgeMSGTO(msg, ptr);
1129       return true;
1130 
1131     case XXX_TZUTCINFO:
1132     case XXX_TZUTC:
1133       line->kludge = GKLUD_KNOWN;
1134       if(getvalue)
1135         KludgeTZUTC(msg, ptr);
1136       return true;
1137 
1138     case XXX_ACUPDATE:
1139     case XXX_ENCRYPTION:
1140     case XXX_EOT:
1141     case XXX_GATECHK:
1142     case XXX_GID:
1143     case XXX_GIF:
1144     case XXX_GMD:
1145     case XXX_GROUP:
1146     case XXX_MOOD:
1147     case XXX_MSGSEQ:
1148     case XXX_NOTE:
1149     case XXX_ORIGID:
1150     case XXX_ORIGINAL:
1151     case XXX_ORIGREF:
1152     case XXX_RECD:
1153     case XXX_RFC:
1154     case XXX_RFD:
1155     case XXX_RID:
1156     case XXX_ROUTE:
1157     case XXX_SN:
1158     case XXX_SOT:
1159     case XXX_TCL1:
1160     case XXX_TCL2:
1161     case XXX_TZ:
1162     case XXX_VIA:
1163     case XXX_XID:
1164       // Recognized but not processed
1165       return true;
1166   }
1167 
1168   return false;
1169 }
1170 
1171 
1172 //  ------------------------------------------------------------------
1173 
HandleRFCs(GMsg * msg,Line * line,int kludgenum,const char * ptr,int getvalue)1174 int HandleRFCs(GMsg* msg, Line* line, int kludgenum, const char* ptr, int getvalue) {
1175 
1176   switch(kludgenum) {
1177 
1178     case RFC_FROM:
1179       line->kludge = GKLUD_RFC;
1180       if(getvalue) {
1181         char* tmp = UnwrapLine(line, ptr);
1182         KludgeFROM(msg, tmp ? tmp : ptr);
1183         if(tmp)
1184           throw_free(tmp);
1185       }
1186       return true;
1187 
1188     case RFC_TO:
1189     case RFC_X_TO:
1190       line->kludge = GKLUD_RFC;
1191       if(getvalue) {
1192         char* tmp = UnwrapLine(line, ptr);
1193         KludgeTO(msg, tmp ? tmp : ptr);
1194         if(tmp)
1195           throw_free(tmp);
1196       }
1197       return true;
1198 
1199     case RFC_BCC:
1200       line->kludge = GKLUD_RFC;
1201       if(true /* getvalue */) {
1202         char* tmp = UnwrapLine(line, ptr);
1203         KludgeBCC(msg, tmp ? tmp : ptr);
1204         if(tmp)
1205           throw_free(tmp);
1206       }
1207       return true;
1208 
1209     case RFC_CC:
1210       line->kludge = GKLUD_RFC;
1211       if(true /* getvalue */) {
1212         char* tmp = UnwrapLine(line, ptr);
1213         KludgeCC(msg, tmp ? tmp : ptr);
1214         if(tmp)
1215           throw_free(tmp);
1216       }
1217       return true;
1218 
1219     case RFC_DATE:
1220       line->kludge = GKLUD_RFC;
1221       if(getvalue)
1222         KludgeDATE(msg, ptr);
1223       return true;
1224 
1225     case RFC_SUBJECT:
1226       line->kludge = GKLUD_RFC;
1227       if(getvalue) {
1228         char* tmp = UnwrapLine(line, ptr);
1229         KludgeSUBJECT(msg, tmp ? tmp : ptr);
1230         if(tmp)
1231           throw_free(tmp);
1232       }
1233       return true;
1234 
1235     case RFC_REPLY_TO:
1236       line->kludge = GKLUD_RFC;
1237       if(getvalue) {
1238         char* tmp = UnwrapLine(line, ptr);
1239         KludgeREPLY_TO(msg, tmp ? tmp : ptr);
1240         if(tmp)
1241           throw_free(tmp);
1242       }
1243       return true;
1244 
1245     case RFC_MESSAGE_ID:
1246       line->kludge = GKLUD_RFC;
1247       if(getvalue) {
1248         char* tmp = UnwrapLine(line, ptr);
1249         KludgeMESSAGE_ID(msg, tmp ? tmp : ptr);
1250         if(tmp)
1251           throw_free(tmp);
1252       }
1253       return true;
1254 
1255     case RFC_REFERENCES:
1256       line->kludge = GKLUD_RFC;
1257       if(getvalue) {
1258         char* tmp = UnwrapLine(line, ptr);
1259         KludgeREFERENCES(msg, tmp ? tmp : ptr);
1260         if(tmp)
1261           throw_free(tmp);
1262       }
1263       return true;
1264 
1265     case RFC_IN_REPLY_TO:
1266       line->kludge = GKLUD_RFC;
1267       if(getvalue) {
1268         char* tmp = UnwrapLine(line, ptr);
1269         KludgeIN_REPLY_TO(msg, tmp ? tmp : ptr);
1270         if(tmp)
1271           throw_free(tmp);
1272       }
1273       return true;
1274 
1275     case RFC_ORGANIZATION:
1276       line->kludge = GKLUD_RFC;
1277       if(getvalue)
1278         KludgeORGANIZATION(msg, ptr);
1279       return true;
1280 
1281     case RFC_X_FTN_TO:
1282       line->kludge = GKLUD_RFC;
1283       if(getvalue)
1284         KludgeX_FTN_TO(msg, ptr);
1285       return true;
1286 
1287     case RFC_X_MAILER:
1288       line->kludge = GKLUD_RFC;
1289       if(getvalue)
1290         KludgePID(msg, ptr);
1291       return true;
1292 
1293     case RFC_X_NEWSREADER:
1294       line->kludge = GKLUD_RFC;
1295       if(getvalue)
1296         KludgePID(msg, ptr);
1297       return true;
1298 
1299     case RFC_NEWSGROUPS:
1300     case RFC_SENDER:
1301     case RFC_CONTENT_TRANSFER_ENCODING:
1302     case RFC_CONTENT_TYPE:
1303     case RFC_MIME_VERSION:
1304     case RFC_X_CHARSET:
1305     case RFC_X_CHAR_ESC:
1306       // Mark RFC's that we add ourselves
1307       line->kludge = GKLUD_RFC;
1308       return true;
1309 
1310     case RFC_ALSO_CONTROL:
1311     case RFC_APPARENTLY_TO:
1312     case RFC_APPROVED:
1313     case RFC_ARTICLE_NAMES:
1314     case RFC_ARTICLE_UPDATES:
1315     case RFC_COMMENT:
1316     case RFC_COMMENTS:
1317     case RFC_CONTENT_DESCRIPTION:
1318     case RFC_CONTENT_DISPOSITION:
1319     case RFC_CONTENT_ID:
1320     case RFC_CONTENT_LENGTH:
1321     case RFC_CONTROL:
1322     case RFC_DELIVERED_TO:
1323     case RFC_DELIVERY_DATE:
1324     case RFC_DISTRIBUTION:
1325     case RFC_ENCRYPTED:
1326     case RFC_ERRORS_TO:
1327     case RFC_EXPIRES:
1328     case RFC_FOLLOWUP_TO:
1329     case RFC_FROMX:
1330     case RFC_KEYWORDS:
1331     case RFC_LINES:
1332     case RFC_MAILING_LIST:
1333     case RFC_NEWS_SOFTWARE:
1334     case RFC_NNTP_POSTING_DATE:
1335     case RFC_NNTP_POSTING_HOST:
1336     case RFC_NNTP_POSTING_USER:
1337     case RFC_OLD_DATE:
1338     case RFC_ORIGINATOR:
1339     case RFC_PATH:
1340     case RFC_PRECEDENCE:
1341     case RFC_PRIORITY:
1342     case RFC_RECEIVED:
1343     case RFC_RETURN_PATH:
1344     case RFC_RETURN_RECEIPT_TO:
1345     case RFC_SEE_ALSO:
1346     case RFC_STATUS:
1347     case RFC_SUMMARY:
1348     case RFC_SUPERSEDES:
1349     case RFC_VERSION:
1350     case RFC_XREF:
1351     case RFC_RNEWS:
1352       // Recognized but not processed
1353       return true;
1354   }
1355 
1356   return false;
1357 }
1358 
1359 
1360 //  ------------------------------------------------------------------
1361 
ScanCtrlList(const Kludges * k,const char * kludge,char endchar)1362 int ScanCtrlList(const Kludges *k, const char *kludge, char endchar) {
1363 
1364   while(*k->key) {
1365     if((k->req & KCRQ_CASE) ? streql(kludge, k->key) : strieql(kludge, k->key)) {
1366       if(k->req & KCRQ_COLON) {
1367         if(endchar == ':')
1368           return k->num;
1369       }
1370       else {
1371         return k->num;
1372       }
1373     }
1374     k++;
1375   }
1376 
1377   return 0;
1378 }
1379 
1380 
1381 //  ------------------------------------------------------------------
1382 
ScanLine(GMsg * msg,Line * line,const char * ptr,int getvalue,int mask)1383 int ScanLine(GMsg* msg, Line* line, const char* ptr, int getvalue, int mask) {
1384 
1385   // Kludge number
1386   int kludgenum = 0;
1387 
1388   // Pointer to kludge id
1389   const char* kludge1 = ptr;
1390 
1391   // Skip past "RFC" string, if any
1392   if(strnieql(kludge1, "RFC", 3) and (kludge1[3] != ':')) {
1393     kludge1 += 3;
1394     if(not g_isalpha(*kludge1))
1395       kludge1++;
1396   }
1397 
1398   // Keep copy of id terminating char
1399   while((*ptr != ' ') and (*ptr != ':') and *ptr)
1400     ptr++;
1401   char endchar = *ptr;
1402 
1403 #if defined(__USE_ALLOCA__)
1404   char *kludge = (char*)alloca(ptr-kludge1+1);
1405 #else
1406   __extension__ char kludge[ptr-kludge1+1];
1407 #endif
1408   strxcpy(kludge, kludge1, ptr-kludge1+1);
1409 
1410   // Search for it in the known kludges list
1411   if(*kludge) {
1412     while(1) {
1413       if(mask & MASK_FTS) {
1414         kludgenum = ScanCtrlList(fts_list, kludge, endchar);
1415         if(kludgenum)
1416           break;
1417       }
1418       if(mask & MASK_FSC) {
1419         kludgenum = ScanCtrlList(fsc_list, kludge, endchar);
1420         if(kludgenum)
1421           break;
1422       }
1423       if(mask & MASK_RFC) {
1424         kludgenum = ScanCtrlList(rfc_list, kludge, endchar);
1425         if(kludgenum)
1426           break;
1427       }
1428       if(mask & MASK_XXX) {
1429         kludgenum = ScanCtrlList(xxx_list, kludge, endchar);
1430         if(kludgenum)
1431           break;
1432       }
1433       break;
1434     }
1435   }
1436 
1437   // Restore terminating char
1438   if(*ptr != ' ')
1439     ptr++;
1440 
1441   if(kludgenum) {
1442     ptr = strskip_wht(ptr);
1443     line->type |= GLINE_KLUD;
1444     if(kludgenum & (MASK_FTS|MASK_FSC|MASK_XXX))
1445       HandleKludges(msg, line, kludgenum, ptr, getvalue);
1446     else if(kludgenum & MASK_RFC)
1447       HandleRFCs(msg, line, kludgenum, ptr, getvalue);
1448   }
1449   else {
1450     gstrarray::iterator k;
1451     for(k = CFG->kludge.begin(); k != CFG->kludge.end(); k++) {
1452       if(strnieql(kludge, k->c_str(), k->length())) {
1453         line->type |= GLINE_KLUD;
1454         kludgenum = USERDEFINED;
1455         break;
1456       }
1457     }
1458     if(not (line->type & GLINE_KLUD))
1459       if((strnieql(kludge, "X-", 2) or strnieql(kludge, "Resent-", 7)) and (endchar == ':') and (mask & MASK_RFC)) {
1460         line->type |= GLINE_KLUD;
1461         kludgenum = RFC_X;
1462       }
1463     if(not (line->type & GLINE_KLUD))
1464       kludgenum = (endchar == ':') ? HEADERLINE : BODYLINE;
1465   }
1466 
1467   return kludgenum;
1468 }
1469 
1470 
1471 //  ------------------------------------------------------------------
1472 
next_non_empty(Line * line)1473 Line* next_non_empty(Line *line) {
1474   Line* nl = line;
1475 
1476   while(nl) {
1477     if(nl->txt.empty())
1478       nl = nl->next;
1479     else
1480       break;
1481   }
1482   return nl;
1483 }
1484 
1485 //  ------------------------------------------------------------------
1486 
ScanKludges(GMsg * msg,int getvalue)1487 void ScanKludges(GMsg* msg, int getvalue) {
1488 
1489   const char* ptr;
1490   int tearlineno = INT_MAX;
1491   int originlineno = INT_MAX;
1492   int gotorig=NO, gottear=NO, gottag=NO;
1493 
1494   // Scan for kludge-, tear- and originlines
1495 
1496   msg->tzutc = -32767; // Default value, means TZUTC kludge was not found
1497 
1498   if(getvalue)
1499     goldmark = ' ';   // Reset the "recognizer" code
1500 
1501   int lineno = 0;
1502   Line* line = LastLine(msg->lin);
1503 
1504   do {
1505     ptr = line->txt.c_str();
1506     if(*ptr == CTRL_A) {
1507 
1508       // Set kludge/hidden color
1509       line->color = C_READK;
1510 
1511       int kludgenum = ScanLine(msg, line, ptr+1, getvalue, MASK_ALL);
1512       if((kludgenum == BODYLINE) or (kludgenum == HEADERLINE)) {
1513         line->type |= GLINE_HIDD;
1514         line->color = C_READKH;
1515       }
1516     }
1517     else {
1518 
1519       if(strneql(ptr, "AREA:", 5) and (line->prev == NULL or *line->prev->txt.c_str() == CTRL_A)) {
1520         line->color = C_READK;
1521         line->kludge = GKLUD_AREA;
1522         line->type |= GLINE_KLUD;
1523         const char* areakludgeptr = ptr+5;   // Extract echoid from kludge
1524         areakludgeptr = strskip_wht(areakludgeptr);
1525         KludgeAREA(msg, areakludgeptr);
1526       }
1527 
1528       if(not (line->type & GLINE_KLUD)) {
1529 
1530         // Check if it is a tagline
1531         if(not gottag and (strneql("...", ptr, 3) or strneql("___", ptr, 3))) {
1532 
1533           // It's only a tagline if it's just above the tearline or origin.
1534           // Or if it's the last line in the message.
1535           if(not lineno or ((lineno-1) == tearlineno) or ((lineno-1) == originlineno)) {
1536               gottag = YES;
1537               line->type |= GLINE_TAGL;
1538               line->color = C_READG;
1539               if(AA->Taglinesupport())
1540                 strbtrim(strxcpy(msg->tagline, ptr+3, sizeof(msg->tagline)));
1541             }
1542           }
1543 
1544         // Check if it's a tearline
1545         else if(not (gottear or gottag) and strneql("---", ptr, 3) and (ptr[3] == ' ' or ptr[3] == NUL)) {
1546 
1547           Line* tearln = line;
1548           int tearlnno = lineno;
1549           while (tearln->type & GLINE_WRAP) {
1550             tearln = tearln->next;
1551             tearlnno--;
1552           }
1553           Line* nnel = next_non_empty(tearln->next);
1554           if(not tearlnno or ((tearlnno-1) == originlineno) or not nnel or nnel->type & GLINE_KLUDGE) {
1555             // Found Tearline
1556             gottear = YES;
1557             tearlineno = lineno;
1558             for (tearln = line; tearln->type & GLINE_WRAP; tearln = tearln->next) {
1559               tearln->type |= GLINE_TEAR;
1560               tearln->color = C_READT;
1561             }
1562             tearln->type |= GLINE_TEAR;
1563             tearln->color = C_READT;
1564             strbtrim(strxcpy(msg->tearline, ptr+3, sizeof(msg->tearline)));
1565 
1566             if(getvalue and CFG->gedhandshake) {
1567               char* tearid[] = {
1568                 "GoldED",
1569                 " GED ",
1570                 " GED2 ",
1571                 " GED3 ",
1572                 " GED386 ",
1573                 " GED/2 ",
1574                 " GED/386 ",
1575                 " GEDP16 ",
1576                 " GEDP32 ",
1577                 " GEDW32 ",
1578                 " GEDLNX ",
1579                 " GED/W32 ",
1580                 NULL
1581               };
1582               int n = 0;
1583               while(tearid[n]) {
1584                 if(striinc(tearid[n], ptr)) {
1585                   goldmark = GOLDMARK;          // Recognize another GoldED msg
1586                   break;
1587                 }
1588                 n++;
1589               }
1590             }
1591           }
1592         }
1593 
1594         // Check if it's an originline
1595         else if(not (gotorig or gottear or gottag) and strneql(" * Origin: ", ptr, 11)) {
1596 
1597           // Found Origin line
1598           bool cnd = line->next != NULL;
1599           Line* nnel = next_non_empty(line->next);
1600           bool nextkl = cnd ? not nnel or nnel->type & GLINE_KLUDGE : false;
1601           nnel = cnd ? next_non_empty(line->next->next) : NULL;
1602           bool nextor = cnd ? (not nnel or nnel->type & GLINE_KLUDGE) and (line->next->txt.find(/*(*/')') != line->next->txt.npos) : false;
1603           if(not line->next or nextkl or nextor) {
1604 
1605             gotorig = YES;
1606             originlineno = lineno;
1607             line->type |= GLINE_ORIG;
1608             line->color = C_READO;
1609             strxcpy(msg->origin, line->txt.c_str()+11, sizeof(msg->origin));
1610             if(nextor) {  // Get the next line too
1611               strcat(msg->origin, line->next->txt.c_str());
1612               line->next->color = C_READO;
1613               line->next->type |= GLINE_ORIG;     // Mark next line as Origin too
1614             }
1615           }
1616         }
1617 
1618         else if(strneql(ptr, "SEEN-BY:", 8)) {
1619           line->kludge = GKLUD_SEENBY;
1620           line->color = C_READK;
1621           line->type |= GLINE_KLUD;
1622         }
1623 
1624         // Check if it's a signature indicator
1625         else if(AA->isinternet() or *msg->ito or *msg->ifrom) {
1626           if(streql(ptr, "-- ")) {
1627             for(Line* q = line; q; q = q->next) {
1628               if((q->type & (GLINE_KLUDGE|GLINE_ORIG|GLINE_TEAR)) == 0) {
1629                 q->color = C_READS;
1630                 q->type |= GLINE_SIGN;
1631                 if(q != line)
1632                   q->type |= GLINE_HARD;
1633                 if(strneql("----", q->txt.c_str(), 4))
1634                   break;
1635               }
1636             }
1637           }
1638         }
1639       }
1640     }
1641     if(line->type & GLINE_WRAP) {
1642       Line* linep = line;
1643       while(linep and (linep->type & GLINE_WRAP)) {
1644         if(linep->next) {
1645           linep->next->type |= linep->type & GLINE_KLUDGE;
1646           linep->next->kludge = linep->kludge;
1647           linep->next->color = linep->color;
1648         }
1649         linep = linep->next;
1650       }
1651     }
1652 
1653     lineno++;
1654 
1655   } while((line = line->prev) != NULL);
1656 
1657   if(not gottag)
1658     *msg->tagline = NUL;
1659   if(not gottear)
1660     *msg->tearline = NUL;
1661   if(not gotorig)
1662     *msg->origin = NUL;
1663 
1664   if(getvalue) {
1665     // This is the new code (experimental)
1666     // It looks for an Origin before taking MSGID
1667     // Trust msg->orig if valid and we're in netmail area.
1668     // (msg->orig is already merged with INTL/FMPT/TOPT)
1669 
1670     if(not (AA->isnet() and msg->orig.valid())) {
1671       if(CFG->addresslookupfirst and msg->msgid.valid())
1672         msg->orig = msg->msgid;
1673       else if((ptr = strrchr(msg->origin, '(' /*)*/ )) != NULL) {
1674         while(not isdigit(*ptr) and *ptr)
1675           ptr++;
1676         msg->orig.reset(ptr);
1677       }
1678       if(msg->msgid.valid())
1679         msg->orig = msg->msgid;
1680     }
1681 
1682     if(msg->orig.zone == 0)
1683       msg->orig.zone = msg->msgid.zone ? msg->msgid.zone : AA->Aka().addr.zone;
1684 
1685     if(msg->dest.zone == 0)
1686       msg->dest.zone = msg->orig.zone;
1687   }
1688 }
1689 
1690 
1691 //  ------------------------------------------------------------------
1692 
Latin2Local(char * str)1693 void  Latin2Local(char *str)
1694 {
1695   if (!CFG->latin2local || !str) return;
1696 
1697   for (size_t i = 0; str[i]; i++)
1698   {
1699     byte chr = str[i];
1700     byte xch = CFG->latintolocal[chr];
1701 
1702     if (xch && (xch != chr))
1703     {
1704       byte left = i ? str[i-1] : 0;
1705       byte right = str[i+1];
1706 
1707       if (((left  >= 0x80) && g_isalpha(left )) ||
1708           ((right >= 0x80) && g_isalpha(right)))
1709       {
1710         str[i] = xch;
1711 
1712         for (size_t j = i-1; j < i; j--)
1713         {
1714           chr = str[j];
1715           xch = CFG->latintolocal[chr];
1716 
1717           if (xch && (xch != chr))
1718             str[j] = xch;
1719           else
1720             break;
1721         }
1722       }
1723     }
1724   }
1725 }
1726 
1727 
1728 //  ------------------------------------------------------------------
1729 
Latin2Local(std::string & str)1730 void  Latin2Local(std::string &str)
1731 {
1732 #if defined(__USE_ALLOCA__)
1733   char *temp = (char *)alloca(str.length()+1);
1734 #else
1735   __extension__ char temp[str.length()+1];
1736 #endif
1737   strcpy(temp, str.c_str());
1738   Latin2Local(temp);
1739   str = temp;
1740 }
1741 
1742 //  ------------------------------------------------------------------
1743 
1744 #ifdef HAS_ICONV
IconvClear(void)1745 void IconvClear(void){
1746   if( iconv_cd!=(iconv_t)(-1) ){
1747     iconv_close(iconv_cd);
1748     iconv_cd = (iconv_t)(-1);
1749   }
1750 }
1751 #endif
1752 
1753 //  ------------------------------------------------------------------
1754 
XlatStr(char * dest,const char * src,int level,Chs * chrtbl,int qpencoded,bool i51)1755 char* XlatStr(char* dest, const char* src, int level, Chs* chrtbl, int qpencoded, bool i51) {
1756 
1757   if( src==NULL )
1758     return dest;
1759 
1760   if( not chrtbl
1761 #ifdef HAS_ICONV
1762       && iconv_cd==(iconv_t)(-1)
1763 #endif
1764     )
1765     return stpcpy(dest, src);
1766 
1767   uint n;
1768   int clen;
1769   int translated;
1770   char* tptr;
1771   char* escp;
1772   const char* sptr = src;
1773   char* dptr = dest;
1774   char dochar;
1775   ChsTab* chrs = chrtbl ? chrtbl->t : (ChsTab*)NULL;
1776 
1777 #ifdef HAS_ICONV
1778   size_t iconvrc=(size_t)(-1);
1779   if( iconv_cd!=(iconv_t)(-1) ){
1780     iconvrc=iconv( iconv_cd, NULL, NULL, NULL, NULL ); // init iconv
1781   }
1782 #endif
1783   while(*sptr) {
1784     switch(*sptr) {
1785       case 0x02:
1786         if(i51 and I51Table) {
1787           for(n=0; n<I51Table->size; n++) {
1788             tptr = (char*)I51TP[n];
1789             if(*(sptr+1) == tptr[0]) {
1790               if(*(sptr+2) == tptr[1]) {
1791                 escp = &tptr[2];
1792                 if(*escp) {
1793                   *dptr++ = *escp++;
1794                   if(*escp) {
1795                     *dptr++ = *escp++;
1796                     if(*escp) {
1797                       *dptr++ = *escp;
1798                     }
1799                   }
1800                 }
1801                 sptr += 2;
1802                 n = (uint)-1;
1803                 break;
1804               }
1805             }
1806           }
1807           if(n != (uint)-1)   // I51 char not found, use fallback method
1808             sptr++;
1809         }
1810         sptr++;
1811         break;
1812 
1813       case 29:
1814         if(MNETable) {
1815           for(n=0; n<MNETable->size; n++) {
1816             tptr = (char*)MNETP[n];
1817             if(*(sptr+1) == tptr[0]) {
1818               if(*(sptr+2) == tptr[1]) {
1819                 escp = &tptr[2];
1820                 if(*escp) {
1821                   *dptr++ = *escp++;
1822                   if(*escp) {
1823                     *dptr++ = *escp++;
1824                     if(*escp) {
1825                       *dptr++ = *escp;
1826                     }
1827                   }
1828                 }
1829                 sptr += 2;
1830                 n = (uint)-1;
1831                 break;
1832               }
1833             }
1834           }
1835           if(n != (uint)-1)   // MNEMONIC char not found, use fallback method
1836             sptr++;
1837         }
1838         sptr++;
1839         break;
1840 
1841       case SOFTCR:
1842         translated = false;
1843         if (WideDispsoftcr)
1844           goto defaultchardo;
1845         else if (CompTable)
1846         {
1847           if(sptr > src) {
1848             if(not (isspace(*(sptr-1)) or isspace(*(sptr+1)))) {
1849               for(n=0; n<CompTable->size; n++) {
1850                 tptr = (char*)CompTP[n];
1851                 if(*(sptr-1) == tptr[0]) {
1852                   if(*(sptr+1) == tptr[1]) {
1853                     escp = &tptr[2];
1854                     if(*escp) {
1855                       dptr--;
1856                       *dptr++ = *escp++;
1857                       if(*escp) {
1858                         *dptr++ = *escp++;
1859                         if(*escp) {
1860                           *dptr++ = *escp;
1861                           translated = true;
1862                         }
1863                       }
1864                     }
1865                     sptr += 2;
1866                     break;
1867                   }
1868                 }
1869               }
1870             }
1871           }
1872         }
1873         if(not translated)
1874           *dptr++ = *sptr++;
1875         break;
1876 
1877       case '=':
1878         if(qpencoded) {
1879           if(isxdigit(sptr[1]) and isxdigit(sptr[2])) {
1880             dochar = (char)((xtoi(sptr[1]) << 4) | xtoi(sptr[2]));
1881             sptr += 3;
1882             goto chardo;
1883           }
1884         }
1885         goto defaultchardo;
1886 
1887       default:
1888       defaultchardo:
1889         dochar = *sptr++;
1890       chardo:
1891         #ifdef HAS_ICONV
1892         if( iconv_cd!=(iconv_t)(-1) ){
1893           unsigned srcleft=1;
1894           unsigned dstleft=3;
1895           char* tsptr = &dochar;
1896 
1897           iconvrc=iconv( iconv_cd, &tsptr, &srcleft, &dptr, &dstleft );
1898           if( iconvrc==((size_t)-1) ){
1899             switch( errno ){
1900               case EINVAL:
1901                 LOG.printf("An incomplete multibyte sequence has been encountered before:");
1902                 LOG.printf("%s",sptr);
1903               case EILSEQ:
1904                 LOG.printf("An invalid multibyte sequence has been encountered in the input before:");
1905                 LOG.printf("%s",sptr);
1906               case E2BIG:
1907                 LOG.printf("There is not sufficient destination size before '%s'", sptr);
1908               default:
1909                 LOG.printf("Unknown error %u in iconv() before %s", errno, sptr);
1910             }
1911           }
1912         }
1913         else
1914         #endif
1915 
1916         if ((level > 0) && chrs)
1917         {
1918           tptr = (char*)chrs[(byte)dochar];
1919           clen = *tptr++;
1920           while(clen--)
1921             *dptr++ = *tptr++;
1922         }
1923         else {
1924           *dptr++ = dochar;
1925         }
1926     }
1927   }
1928   *dptr = NUL;
1929   return dptr;
1930 }
1931 
1932 
1933 //  ------------------------------------------------------------------
1934 
cmp_quotes(char * q1,char * q2)1935 int cmp_quotes(char* q1, char* q2) {
1936 
1937   q1--;
1938   q2--;
1939 
1940   do {
1941     q1 = spanspaces(++q1);
1942     q2 = spanspaces(++q2);
1943     if(*q1 != *q2)
1944       return NO;
1945   } while(*q1 and *q2);
1946 
1947   return *q1 == *q2;
1948 }
1949 
1950 
1951 //  ------------------------------------------------------------------
1952 
TextToLines(int __line_width,bool getvalue,bool header_recode)1953 void GMsg::TextToLines(int __line_width, bool getvalue, bool header_recode) {
1954 
1955   MakeLineIndex(this, __line_width, getvalue, header_recode);
1956 }
1957 
1958 
1959 //  ------------------------------------------------------------------
1960 
check_multipart(const char * ptr,char * boundary)1961 static bool check_multipart(const char* ptr, char* boundary) {
1962 
1963   if(striinc("multipart", ptr)) {
1964     const char* boundptr = striinc("boundary=", ptr);
1965     if(boundptr) {
1966       boundptr += 9;
1967       const char* boundend;
1968       if(*boundptr == '\"') {
1969         boundptr++;
1970         boundend = strchr(boundptr, '\"');
1971       }
1972       else {
1973         boundend = strpbrk(boundptr, " \r\n");
1974       }
1975       if(boundend) {
1976         strxcpy(boundary, boundptr, 1+boundend-boundptr);
1977         return true;
1978       }
1979     }
1980   }
1981   return false;
1982 }
1983 
1984 
1985 //  ------------------------------------------------------------------
1986 
put_on_new_line(const char * ptr,const char * prev_ptr)1987 inline bool put_on_new_line(const char *ptr, const char *prev_ptr) {
1988 
1989   if((*ptr == CR) or
1990      (*ptr == CTRL_A) or
1991      is_quote(ptr) or
1992      ((ptr[0] == ptr[1]) and (ptr[0] == ptr[2])) or
1993      (strneql(ptr, "-- ", 3) and (ptr[3] == CR)) or
1994      strneql(ptr, " * Origin: ", 11) /*or
1995      (ptr[0] == prev_ptr[0]) and (ptr[1] == prev_ptr[1])*/)
1996     return true;
1997   // Put RFC kludges and SEEN-BY on new line
1998   while(*ptr and (isxalnum(*ptr) or (*ptr == '-')))
1999     ptr++;
2000   if(*ptr == ':')
2001     return true;
2002   return false;
2003 }
2004 
2005 
2006 //  ------------------------------------------------------------------
2007 
MakeLineIndex(GMsg * msg,int margin,bool getvalue,bool header_recode)2008 void MakeLineIndex(GMsg* msg, int margin, bool getvalue, bool header_recode) {
2009 
2010   uint idx=0, idxadjust=0;
2011   uint len;
2012   int level=0;
2013   uint n;
2014   char ch, chln = 0, dochar;
2015   Line* line;
2016   Line* nextline=NULL;
2017   char* bp;
2018   char* btmp=NULL;
2019   char* tptr;
2020   char* escp;
2021   char* bptr;
2022   ISub buf;
2023   char qbuf[MAXQUOTELEN], qbuf2[MAXQUOTELEN], chsbuf[100];
2024   char* ptr;
2025   char* qptr;
2026   char* tmp=NULL;
2027   char* linetmp=NULL;
2028   uint qlen=0, qlen2=0;
2029   int wraps=0, para=0, chslev;
2030   bool reflow = false, quoteflag = false;
2031   bool quotewraphard = AA->Quotewraphard();
2032   bool qpencoded = getvalue and (IsQuotedPrintable(AA->Xlatimport()) or AA->StripHTML());
2033   bool gotmime = false;
2034   bool gotmultipart = false;
2035   bool inheader = false;
2036   gstrarray boundary_set;
2037   char boundary[100];
2038 
2039   *buf = *qbuf = *qbuf2 = NUL;
2040 
2041   if(margin < 0) {
2042     margin = -margin;
2043     quoteflag = true;
2044   }
2045 
2046   // Free all previously allocated lines
2047   line = msg->lin;
2048   while(line) {
2049     nextline = line->next;
2050     throw_xdelete(line);
2051     line = nextline;
2052   }
2053 
2054   msg->icc[0] = NUL;
2055   msg->ibcc[0] = NUL;
2056 
2057   msg->lines = 0;
2058   msg->lin = NULL;
2059 
2060   if(AA->attr().hex()) {
2061 
2062     // Make a complete hexdump as a list of message lines
2063     w_info(LNG->GenHexdump);
2064     throw_release(msg->txt);
2065     line = AA->MakeDumpMsg(msg, LNG->Hexdumphead);
2066     if(line and msg->txt) {
2067       line = AddLine(line, "");
2068       line = AddLine(line, LNG->Hexdumptext);
2069       line = AddLine(line, "");
2070       ptr = msg->txt;
2071       uint _size = strlen(msg->txt);
2072       if ((AA->basetype() == "OPUS") || (AA->basetype() == "FTS1"))
2073         idxadjust = 190;
2074       for (idx=0; idx < _size; ptr+=16,idx+=16)
2075       {
2076         gsprintf(PRINTF_DECLARE_BUFFER(buf), "%04X   ", idx+idxadjust);
2077         HexDump16(buf+7, ptr, MinV((int)(_size-idx), 16), HEX_DUMP2);
2078         line = AddLine(line, buf);
2079       }
2080     }
2081 
2082     w_info(NULL);
2083   }
2084   else {  // Convert the message text to a list of separately allocated lines
2085 
2086     char prev_ptr[3] = {"\xFF\xFF"};
2087 
2088     if(AA->StripHTML())
2089       RemoveHTML(msg->txt);
2090     ptr = spanfeeds(msg->txt);
2091 
2092     // Set default conversion table for area
2093     if(getvalue) {
2094       if(not strieql(AA->Xlatimport(), CFG->xlatlocalset)) {
2095         strxcpy(msg->charset, AA->Xlatimport(), sizeof(msg->charset));
2096         level = msg->charsetlevel = LoadCharset(msg->charset, CFG->xlatlocalset);
2097       }
2098     }
2099 
2100     if(*ptr != NUL) {
2101       line = msg->lin = new Line();
2102       throw_xnew(line);
2103 
2104       // Alloc space for one line
2105       linetmp = (char*)throw_calloc(1, margin+512);
2106       while(*ptr) {
2107 
2108         bptr = linetmp;
2109         bp = bptr;
2110         len = 0;
2111 
2112         // Link previous line to this one
2113 
2114         if(line->prev) {
2115           line->prev->next = line;
2116           if((line->prev->type & (GLINE_HARD|GLINE_WRAP|GLINE_QUOT)) == (GLINE_HARD|GLINE_WRAP)) {
2117             line->prev->type &= ~GLINE_HARD;
2118             line->type |= GLINE_HARD;
2119           }
2120         }
2121 
2122         // Reflow quotes
2123 
2124         if(reflow) {
2125           len = qlen;
2126           qptr = qbuf;
2127           reflow = false;
2128           // Insert previous quotestring
2129           for(n=0; n<qlen; n++)
2130           {
2131             if ((level > 0) && ChsTP)
2132             {
2133               tptr = (char*)ChsTP[(byte)(*qptr++)];
2134               chln = *tptr++;
2135               while(chln--) {
2136                 *(++bp) = *tptr++;
2137               }
2138             }
2139             else {
2140               *(++bp) = *qptr++;
2141             }
2142           }
2143           if(quotewraphard) {
2144             *qbuf = NUL;
2145             qlen = 0;
2146           }
2147           ptr = spanfeeds(ptr);
2148           line->type |= GLINE_QUOT|GLINE_HARD;
2149         }
2150 
2151         // Get type of line
2152         int isq_flag1 = 0;
2153         bool isq_flag2 = false;
2154 
2155         if(wraps == 0) {
2156 
2157           if(gotmultipart) {
2158             if(*ptr == '-' and ptr[1] == '-') {
2159               gstrarray::iterator ib;
2160               for(ib = boundary_set.begin(); ib != boundary_set.end(); ib++)
2161               if(strneql(ptr+2, (*ib).c_str(), (*ib).length())) {
2162                 inheader = true;
2163                 break;
2164               }
2165             }
2166             else if(*ptr == '\n' or *ptr == '\r')
2167               inheader = false;
2168           }
2169 
2170           if(inheader and (*ptr != '-'))
2171             line->type |= GLINE_HIDD;
2172 
2173           para = 0;
2174           if(*ptr == CTRL_A or inheader) {  // Found kludge/hidden line
2175             para = GLINE_KLUD;
2176             line->type |= GLINE_HARD;
2177             if(getvalue and not CFG->ignorecharset) {
2178               tptr = ptr;
2179               char* kludge = ptr + (*ptr == CTRL_A ? 1 : 0);
2180               if(strnieql(kludge, "RFC", 3)) {
2181                 kludge += 3;
2182                 if(not g_isalpha(*kludge))
2183                   kludge++;
2184               }
2185               while((*ptr != ' ') and (*ptr != ':') and *ptr)
2186                 ptr++;
2187               char endchar = *ptr;
2188               *ptr = NUL;
2189               int kludgetype = -1;
2190               if(strieql(kludge, "I51"))
2191                 kludgetype = FSC_I51;
2192               else if(strieql(kludge, "CHRS") or strieql(kludge, "CHARSET"))
2193                 kludgetype = FSC_CHARSET;
2194               else if(strieql(kludge, "CODEPAGE"))
2195                 kludgetype = FSC_CODEPAGE;
2196               else if(strieql(kludge, "Content-Type"))
2197                 kludgetype = RFC_CONTENT_TYPE;
2198               else if(strieql(kludge, "Content-Transfer-Encoding"))
2199                 kludgetype = RFC_CONTENT_TRANSFER_ENCODING;
2200               else if(strieql(kludge, "X-Charset"))
2201                 kludgetype = RFC_X_CHARSET;
2202               else if(strieql(kludge, "X-Char-Esc"))
2203                 kludgetype = RFC_X_CHAR_ESC;
2204               *ptr = endchar;
2205               if(*ptr != ' ')
2206                 ptr++;
2207               ptr = strskip_wht(ptr);
2208               char* keptr = strpbrk(ptr, "\r\n");
2209               if(keptr) {
2210                 endchar = *keptr;
2211                 *keptr = NUL;
2212               }
2213               if(kludgetype == FSC_I51) {
2214                 msg->i51 = true;
2215                 // Convert FSC-0051.003 to FSC-0054.003
2216                 strxcpy(chsbuf, "LATIN-1", sizeof(chsbuf));
2217                 chslev = LoadCharset(chsbuf, CFG->xlatlocalset);
2218                 if(not chslev) {
2219                   strxcpy(chsbuf, AA->Xlatimport(), sizeof(chsbuf));
2220                   chslev = LoadCharset(chsbuf, CFG->xlatlocalset);
2221                 }
2222                 if(chslev) {
2223                   level = msg->charsetlevel = chslev;
2224                   strcpy(msg->charset, chsbuf);
2225                 }
2226               }
2227               else if((kludgetype == FSC_CHARSET) or (kludgetype == FSC_CODEPAGE)) {
2228                 *chsbuf = NUL;
2229                 qpencoded = IsQuotedPrintable(ptr);
2230                 if(kludgetype == FSC_CODEPAGE)
2231                   strxmerge(chsbuf, sizeof(chsbuf), "CP", ptr, NULL);
2232                 else
2233                   strxcpy(chsbuf, qpencoded ? ExtractPlainCharset(ptr) : ptr, sizeof(chsbuf));
2234                 // Workaround for buggy mailreaders which stores '_' in charset name
2235                 strchg(chsbuf,'_',' ');
2236                 chslev = LoadCharset(chsbuf, CFG->xlatlocalset);
2237                 if(not chslev) {
2238                   strxcpy(chsbuf, AA->Xlatimport(), sizeof(chsbuf));
2239                   chslev = LoadCharset(chsbuf, CFG->xlatlocalset);
2240                 }
2241                 if(chslev) {
2242                   level = msg->charsetlevel = chslev;
2243                   strcpy(msg->charset, chsbuf);
2244                 }
2245                 if(*msg->charset == NUL)
2246                   strcpy(msg->charset, chsbuf);
2247               }
2248               else if(kludgetype == RFC_CONTENT_TYPE) {
2249                 // Content-Type: text/plain; charset="us-ascii"
2250                 while(keptr and ((keptr[1] == ' ') or (keptr[1] == '\t'))) {
2251                   *keptr = endchar;
2252                   keptr = strpbrk(keptr+1, "\r\n");
2253                   if(keptr) {
2254                     endchar = *keptr;
2255                     *keptr = NUL;
2256                   }
2257                 }
2258                 const char *mime_charset = striinc("charset=", ptr);
2259                 if(mime_charset != NULL) {
2260                   if(mime_charset[8] == '\"') {
2261                     strxcpy(chsbuf, mime_charset+9, sizeof(chsbuf));
2262                     char *quote = strchr(chsbuf, '\"');
2263                     if(quote != NULL) *quote = NUL;
2264                   }
2265                   else
2266                     strxcpy(chsbuf, strrword(mime_charset+8), sizeof(chsbuf));
2267                   if(striinc("8859", chsbuf))
2268                     ISO2Latin(chsbuf, chsbuf);
2269                   chslev = LoadCharset(chsbuf, CFG->xlatlocalset);
2270                   if(not chslev) {
2271                     strxcpy(chsbuf, AA->Xlatimport(), sizeof(chsbuf));
2272                     chslev = LoadCharset(chsbuf, CFG->xlatlocalset);
2273                   }
2274                   if(chslev) {
2275                     level = msg->charsetlevel = chslev;
2276                     strcpy(msg->charset, chsbuf);
2277                   }
2278                   if(*msg->charset == NUL)
2279                     strcpy(msg->charset, chsbuf);
2280                   gotmime = true;
2281                 }
2282                 else if(check_multipart(ptr, boundary)) {
2283                   boundary_set.push_back(boundary);
2284                   gotmultipart = true;
2285                   gotmime = true;
2286                 }
2287               }
2288               else if(kludgetype == RFC_CONTENT_TRANSFER_ENCODING) {
2289                 if(striinc("quoted-printable", ptr)) {
2290                   qpencoded = true;
2291                   msg->charsetencoding |= GCHENC_QP;
2292                   strcpy(chsbuf, MakeQuotedPrintable(msg->charset));
2293                   chslev = LoadCharset(msg->charset, CFG->xlatlocalset);
2294                   if(not chslev) {
2295                     strxcpy(chsbuf, AA->Xlatimport(), sizeof(chsbuf));
2296                     chslev = LoadCharset(chsbuf, CFG->xlatlocalset);
2297                   }
2298                   if(chslev) {
2299                     level = msg->charsetlevel = chslev;
2300                     strcpy(msg->charset, chsbuf);
2301                   }
2302                 }
2303               }
2304               else if(kludgetype == RFC_X_CHARSET) {
2305                 if(not gotmime) {
2306                   if(striinc("8859", ptr))
2307                     ISO2Latin(chsbuf, ptr);
2308                   else
2309                     strxcpy(chsbuf, ptr, sizeof(chsbuf));
2310                   chslev = LoadCharset(chsbuf, CFG->xlatlocalset);
2311                   if(not chslev) {
2312                     strxcpy(chsbuf, AA->Xlatimport(), sizeof(chsbuf));
2313                     chslev = LoadCharset(chsbuf, CFG->xlatlocalset);
2314                   }
2315                   if(chslev) {
2316                     level = msg->charsetlevel = chslev;
2317                     strcpy(msg->charset, chsbuf);
2318                   }
2319                 }
2320               }
2321               else if(kludgetype == RFC_X_CHAR_ESC) {
2322                 if(not gotmime)
2323                   msg->charsetencoding |= GCHENC_MNE;
2324               }
2325               if(keptr)
2326                 *keptr = endchar;
2327               ptr = tptr;
2328             }
2329           }
2330           else if ((isq_flag1 = is_quote(ptr)) &&
2331                    (isq_flag2 = is_quote2(line, ptr)))
2332           {
2333             para = GLINE_QUOT;
2334             line->type |= GLINE_QUOT|GLINE_HARD;
2335             GetQuotestr(ptr, qbuf, &qlen);
2336           }
2337         }
2338 
2339         if (CFG->quoteusenewai && isq_flag1 && !isq_flag2)
2340         {
2341           uint bad_qlen;
2342           char bad_qbuf[MAXQUOTELEN];
2343           char *bad_ptr;
2344 
2345           GetQuotestr(bad_ptr = ptr, bad_qbuf, &bad_qlen);
2346 
2347           while (true)
2348           {
2349             for (; *bad_ptr && (*bad_ptr != CR); bad_ptr++);
2350             if (!*bad_ptr) break;
2351 
2352             char *bad_head = bad_ptr;
2353 
2354             bad_ptr++;
2355             if (*bad_ptr == LF) bad_ptr++;
2356 
2357             if (strneql(bad_qbuf, bad_ptr, bad_qlen))
2358             {
2359               *bad_head = ' ';
2360 
2361               memmove(bad_head+1, bad_ptr+bad_qlen, strlen(bad_ptr));
2362               bad_ptr = bad_head;
2363             }
2364             else
2365               break;
2366           }
2367         }
2368 
2369         // Get one line
2370 
2371         ch = '\0';
2372         tmp = NULL;
2373         while(*ptr and (len < (uint)margin)) {
2374           switch(*ptr) {
2375             case CR:
2376               do_cr:
2377               ch = CR;
2378               ptr = spanfeeds(ptr+1);
2379               if(wraps and not ((line->type & GLINE_HARD) and not (line->type & GLINE_QUOT))) {
2380                 if(para != GLINE_QUOT) {
2381                   if(quoteflag) {
2382                     if(para == GLINE_KLUD) {
2383                       wraps = 0;
2384                       break;
2385                     }
2386                     if(put_on_new_line(ptr, prev_ptr)) {
2387                       wraps = 0;
2388                       break;
2389                     }
2390                     char* lp = ptr;
2391                     while((*lp == ' ') or (*lp == '\t'))
2392                       lp++;
2393                     if(*lp == CR) {
2394                       wraps = 0;
2395                       break;
2396                     }
2397                     else {
2398                       ptr = lp;
2399                       if(*bp != ' ') {
2400                         *(++bp) = ' ';
2401                         len++;
2402                       }
2403                     }
2404                     ch = '\0';
2405                     continue;
2406                   }
2407                   else {
2408                     wraps=0;
2409                     break;
2410                   }
2411                 }
2412                 else {
2413                   GetQuotestr(ptr, qbuf2, &qlen2);
2414                   if((*ptr != CR) and cmp_quotes(qbuf2, qbuf)) {
2415                     char* lp = ptr + qlen2;
2416                     if(is_quote(lp)) {
2417                       wraps = 0;
2418                       para = 0;
2419                       break;
2420                     }
2421                     else {
2422                       ptr = lp;
2423                       para = GLINE_QUOT;
2424                       if((*ptr != ' ') and (*bp != ' ')) {
2425                         if(put_on_new_line(ptr, prev_ptr)) {
2426                           ptr -= qlen2;
2427                           wraps = 0;
2428                           break;
2429                         }
2430                         tmp = ptr-1;
2431                         btmp = bp;
2432                         *(++bp) = ' ';
2433                         len++;
2434                       }
2435                       else if(*ptr == ' ') {
2436                         ptr -= qlen2;
2437                         wraps = 0;
2438                         break;
2439                       }
2440                       ch = 0;
2441                       continue;
2442                     }
2443                   }
2444                   else {
2445                     wraps = 0;
2446                     para = 0;
2447                     break;
2448                   }
2449                 }
2450               }
2451               break;
2452             case 0x02:  // The I51 escape character
2453               if(msg->i51 and I51Table) {
2454                 for(n=0; n<I51Table->size; n++) {
2455                   tptr = (char*)I51TP[n];
2456                   if(*(ptr+1) == tptr[0]) {
2457                     if(*(ptr+2) == tptr[1]) {
2458                       escp = &tptr[2];
2459                       if(*escp) {
2460                         *(++bp) = *escp++;
2461                         if(*escp) {
2462                           *(++bp) = *escp++;
2463                           len++;
2464                           if(*escp) {
2465                             *(++bp) = *escp;
2466                             len++;
2467                           }
2468                         }
2469                       }
2470                       ptr += 2;
2471                       n = (uint)-1;
2472                       break;
2473                     }
2474                   }
2475                 }
2476                 if(n != (uint)-1)   // I51 char not found, use fallback method
2477                   ptr++;
2478               }
2479               ptr++;
2480               break;
2481             case 29:  // The MNE escape character
2482               if(MNETable) {
2483                 for(n=0; n<MNETable->size; n++) {
2484                   tptr = (char*)MNETP[n];
2485                   if(*(ptr+1) == tptr[0]) {
2486                     if(*(ptr+2) == tptr[1]) {
2487                       escp = &tptr[2];
2488                       if(*escp) {
2489                         *(++bp) = *escp++;
2490                         if(*escp) {
2491                           *(++bp) = *escp++;
2492                           len++;
2493                           if(*escp) {
2494                             *(++bp) = *escp;
2495                             len++;
2496                           }
2497                         }
2498                       }
2499                       ptr += 2;
2500                       n = (uint)-1;
2501                       break;
2502                     }
2503                   }
2504                 }
2505                 if(n != (uint)-1)   // MNE char not found, use fallback method
2506                   ptr++;
2507               }
2508               ptr++;
2509               break;
2510             case SOFTCR:
2511               if (WideDispsoftcr)
2512                 goto defaultchardo;
2513               else {
2514                 if(CompTable) {
2515                   if(not (isspace(*(ptr-1)) or isspace(*(ptr+1)))) {
2516                     for(n=0; n<CompTable->size; n++) {
2517                       tptr = (char*)CompTP[n];
2518                       if(*(ptr-1) == tptr[0]) {
2519                         if(*(ptr+1) == tptr[1]) {
2520                           escp = &tptr[2];
2521                           if(*escp) {
2522                             *(bp) = *escp++;
2523                             if(*escp) {
2524                               *(++bp) = *escp++;
2525                               len++;
2526                               if(*escp) {
2527                                 *(++bp) = *escp;
2528                                 len++;
2529                               }
2530                             }
2531                           }
2532                           ptr++;
2533                           break;
2534                         }
2535                       }
2536                     }
2537                   }
2538                   else if(para == GLINE_QUOT) {
2539                     *ptr-- = CR;   // Fake a CR
2540                   }
2541                 }
2542                 else if(para == GLINE_QUOT) {
2543                   *ptr-- = CR;   // Fake a CR
2544                 }
2545                 ptr++;
2546               }
2547               break;
2548             case LF:
2549               ptr++;
2550               break;
2551             case CTRL_A:
2552               *(++bp) = *ptr++;
2553               ++len;
2554               break;
2555             case '=':
2556               if(qpencoded) {
2557                 if(isxdigit(ptr[1]) and isxdigit(ptr[2])) {
2558                   // Decode the character
2559                   dochar = (char)((xtoi(ptr[1]) << 4) | xtoi(ptr[2]));
2560                   ptr += 3;
2561                   if(dochar == '\t') {
2562                     if(len >= qlen) {
2563                       tmp = ptr-3;
2564                       btmp = bp;
2565                     }
2566                     goto do_ht;
2567                   }
2568                   else if(dochar == CR)
2569                     goto do_cr;
2570                   goto chardo;
2571                 }
2572                 else if((ptr[1] == CR) or (ptr[1] == LF)) {
2573                   // Skip soft line break
2574                   ptr = spanfeeds(ptr+2);
2575                   break;
2576                 }
2577               }
2578               goto defaultchardo;
2579             case '\t':
2580               if(len >= qlen) {
2581                 tmp = ptr;
2582                 btmp = bp;
2583               }
2584               ptr++;
2585               do_ht:
2586               if(CFG->disptabsize) {
2587                 int llen = (int)(bp-linetmp);
2588                 for(n=0; n<(CFG->disptabsize-(llen%CFG->disptabsize)); n++) {
2589                   *(++bp) = ' ';
2590                   len++;
2591                 }
2592               }
2593               else {
2594                 *(++bp) = ' ';
2595                 len++;
2596               }
2597               break;
2598             case ' ':
2599               if(len >= qlen) {
2600                 tmp = ptr;
2601                 btmp = bp;
2602               }
2603             default:
2604             defaultchardo:
2605               dochar = *ptr++;
2606             chardo:
2607               if ((level > 0) && ChsTP)
2608               {
2609                 tptr = (char*)ChsTP[(byte)dochar];
2610                 chln = *tptr++;
2611                 while(chln--) {
2612                   *(++bp) = *tptr++;
2613                   ++len;
2614                 }
2615               }
2616               else {
2617                 *(++bp) = dochar;
2618                 ++len;
2619               }
2620               break;
2621           }
2622           if(ch == CR)
2623             break;
2624         }
2625 
2626         if(len == (uint)margin) {
2627           if(*ptr == CR) {
2628             line->type |= GLINE_HARD;
2629             wraps = 0;
2630             ptr++;
2631           }
2632           else {
2633             wraps++;
2634             if(para == GLINE_QUOT)
2635               reflow = true;
2636             line->type |= GLINE_WRAP;
2637             ptr = spanfeeds(ptr);
2638             if((*bp == ' ') or (isspace(*ptr) and (*ptr != LF)))
2639               ptr = spanspaces(ptr);
2640             else {
2641               if(tmp) {
2642                 bp = btmp+1;
2643                 ptr = tmp+1;
2644               }
2645             }
2646           }
2647         }
2648         else
2649           line->type |= GLINE_HARD;
2650 
2651         *(++bp) = NUL;
2652 
2653         // Get line length
2654         uint tmplinelength = (uint)(bp-bptr);
2655         if(tmplinelength > (uint)(margin + 512)) {
2656           LOG.ErrPointer();
2657           LOG.printf("! A message line length (%u bytes) exceeded an internal buffer limit of %u bytes", tmplinelength, margin+512);
2658           LOG.printf(": Message line text: %s", bptr+1);
2659           PointerErrorExit();
2660         }
2661 
2662         // Store line
2663         line->txt = linetmp+1;
2664         prev_ptr[0] = line->txt.empty() ? 0xFF : line->txt[0];
2665         prev_ptr[1] = line->txt.length() < 2 ? 0xFF : line->txt[1];
2666 
2667         // Set line color and type
2668         if((line->type & GLINE_QUOT) and not is_quote(line->txt.c_str()))
2669           line->type ^= GLINE_QUOT;
2670         if(line->type & GLINE_QUOT)
2671           line->color = quotecolor(line->txt.c_str());
2672         else if(inheader)
2673           line->color = C_READT;
2674         else
2675           line->color = C_READW;
2676 
2677         Line* prevline = line;
2678         line = new Line();
2679         throw_xnew(line);
2680         line->prev = prevline;
2681 
2682         ptr = spanfeeds(ptr);
2683       }
2684 
2685       throw_release(linetmp);
2686       throw_xdelete(line);
2687 
2688       // Charset translate header fields
2689       if(header_recode) {
2690         strxmimecpy(msg->by, msg->by, level, sizeof(INam), true);
2691         strxmimecpy(msg->to, msg->to, level, sizeof(INam), true);
2692         if(not (msg->attr.frq() or msg->attr.att() or msg->attr.urq()))
2693           strxmimecpy(msg->re, msg->re, level, sizeof(ISub), true);
2694       }
2695 
2696       // Scan msg body top for RFC headerlines
2697       int irfcbody = AA->Internetrfcbody();
2698       if(irfcbody != 0) {
2699         if(msg->lin) {
2700           Line* linep = msg->lin;
2701           int headerlines = 0;
2702           while(linep) {
2703             if(not linep->txt.empty()) {
2704               const char* tptr = linep->txt.c_str();
2705               if(*tptr != CTRL_A) {
2706                 int kludgetype = ScanLine(msg, linep, tptr, getvalue, MASK_RFC);
2707                 if(kludgetype) {
2708                   if(kludgetype == HEADERLINE) {
2709                     linep->type |= GLINE_HIDD;
2710                   }
2711                   headerlines++;
2712                   if(linep->type & GLINE_HIDD)
2713                     linep->color = C_READKH;
2714                   else
2715                     linep->color = C_READK;
2716                   if(linep->next) {
2717                     char lwsp = *linep->next->txt.c_str();
2718                     while((lwsp == ' ') or (lwsp == '\t') or (linep->type & GLINE_WRAP)) {
2719                       linep = linep->next;
2720                       linep->type |= linep->prev->type & (GLINE_KLUD|GLINE_HIDD);
2721                       linep->color = linep->prev->color;
2722                       if(linep->next)
2723                         lwsp = *linep->next->txt.c_str();
2724                       else
2725                         break;
2726                     }
2727                   }
2728                 }
2729                 else
2730                   break;
2731               }
2732             }
2733             else {
2734               if(headerlines) {
2735                 linep->type |= GLINE_KLUD;
2736                 linep->kludge = GKLUD_RFC;
2737                 linep->color = C_READK;
2738               }
2739               if(--irfcbody == 0)
2740                 break;
2741             }
2742             if(linep->type & GLINE_WRAP) {
2743               while(linep and (linep->type & GLINE_WRAP)) {
2744                 linep = linep->next;
2745                 if(linep) {
2746                   linep->type |= linep->prev->type & (GLINE_KLUD|GLINE_HIDD);
2747                   linep->color = linep->prev->color;
2748                 }
2749               }
2750             }
2751             if(linep)
2752               linep = linep->next;
2753           }
2754         }
2755       }
2756 
2757       // Scan for kludge-, tear- and originlines
2758       ScanKludges(msg, getvalue);
2759 
2760       // Try to fix the following situation:
2761       // Messagebase From field: fido7@da.ru
2762       // @REPLYADDR: fido7@da.ru
2763       // @REPLYTO: 2:5020/52 Sergey Kitsya
2764       // From: fido7@da.ru
2765       if(getvalue and streql(msg->by, msg->iaddr) and not *msg->realby and *msg->igate) {
2766         ptr = strchr(msg->igate, ' ');
2767         if(ptr) {
2768           ptr = strskip_wht(ptr);
2769           if(not isuucp(ptr))
2770             strcpy(msg->realby, ptr);
2771         }
2772       }
2773     }
2774   }
2775 
2776   // Make the index to the line index as allowed by config
2777   MsgLineReIndex(msg);
2778 }
2779 
2780 
2781 //  ------------------------------------------------------------------
2782 
MsgLineReIndex(GMsg * msg,int viewhidden,int viewkludge,int viewquote)2783 void MsgLineReIndex(GMsg* msg, int viewhidden, int viewkludge, int viewquote) {
2784 
2785   if(viewhidden == -1)
2786     viewhidden = AA->Viewhidden();
2787   if(viewkludge == -1)
2788     viewkludge = AA->Viewkludge();
2789   if(viewquote == -1)
2790     viewquote = AA->Viewquote();
2791 
2792   int x;
2793   Line* line;
2794 
2795   throw_xrelease(msg->line);
2796   line = msg->lin;
2797   msg->lines = 0;
2798   while(line) {
2799     msg->lines++;
2800     if(line->next) {
2801       if(line->type & GLINE_QUOT) {
2802         if(not (line->next->type & GLINE_QUOT) and not strblank(line->next->txt.c_str())) {
2803           if(CFG->switches.get(quotespacing)) {
2804             line = AddLine(line, "");
2805             msg->lines++;
2806           }
2807         }
2808       }
2809       else {
2810         const char *posn, *posn2 = line->txt.c_str();
2811         if(msg->attr.pos() and ((posn = striinc("@position", posn2)) != NULL)) {
2812           line->txt.erase(posn - posn2, 9);
2813           line->type |= GLINE_POSI;
2814         }
2815         if((line->next->type & GLINE_QUOT) and not strblank(line->txt.c_str())) {
2816           if(CFG->switches.get(quotespacing)) {
2817             line = AddLine(line, "");
2818             msg->lines++;
2819           }
2820         }
2821       }
2822     }
2823     line = line->next;
2824   }
2825 
2826   msg->line = (Line**)throw_xcalloc(msg->lines+2, sizeof(Line*)); // FIXME: Memory Leak
2827 
2828   x = 0;
2829   msg->lines = 0;
2830 
2831   char qbuf[MAXQUOTELEN];
2832   char qbuf0[MAXQUOTELEN];
2833   uint qlen = 0;
2834   int qmatches = 0;
2835 
2836   *qbuf0 = NUL;
2837   for(line = msg->lin; line != NULL; line = line->next) {
2838     if(line->type & GLINE_KLUD) {
2839       *qbuf0 = NUL;
2840       qmatches = 0;
2841       if(not viewkludge) {
2842         continue;
2843       }
2844     }
2845     else if(line->type & GLINE_HIDD) {
2846       *qbuf0 = NUL;
2847       qmatches = 0;
2848       if(not viewhidden) {
2849         continue;
2850       }
2851     }
2852     else if (line->type & GLINE_TXTH)
2853     {
2854       *qbuf0 = NUL;
2855       qmatches = 0;
2856       continue;
2857     }
2858     else if(line->type & GLINE_QUOT) {
2859       if(not viewquote) {
2860         GetQuotestr(line->txt.c_str(), qbuf, &qlen);
2861         if(not cmp_quotes(qbuf0, qbuf)) {
2862           strcpy(qbuf0, qbuf);
2863           qmatches = 0;
2864         }
2865         const char *p = line->txt.c_str()+qlen;
2866         while(*p != NUL) {
2867           if(isxalnum(*p)) {
2868             qmatches++;
2869             break;
2870           }
2871           ++p;
2872         }
2873         if(qmatches != 1) {
2874           continue;
2875         }
2876       }
2877     }
2878     else {
2879       *qbuf0 = NUL;
2880       qmatches = 0;
2881     }
2882     msg->line[x++] = line;
2883     msg->lines++;
2884   }
2885 
2886   msg->line[x] = NULL;  // Mark end of index
2887 
2888   // Calculate quote percent
2889   int quotes = 0, nonquotes = 0;
2890 
2891   line = msg->lin;
2892   while(line) {
2893     if(not (line->type & (GLINE_KLUDGE|GLINE_TEAR|GLINE_ORIG))) {
2894       if(line->txt.c_str()) {
2895         int n = line->txt.length();
2896         nonquotes += n;
2897         if(line->type & GLINE_QUOT)
2898            quotes += n;
2899       }
2900     }
2901     line = line->next;
2902   }
2903   msg->quotepct = 100-Pct(nonquotes, quotes);
2904 }
2905 
2906 
2907 //  ------------------------------------------------------------------
2908 
IsQuotedPrintable(const char * encoding)2909 bool IsQuotedPrintable(const char *encoding) {
2910 
2911   if(striinc("LATIN1QP", encoding))
2912     return true;
2913   else {
2914     const char *lencoding = strlword(encoding);
2915     int len = strlen(lencoding);
2916     if((len > 2) and strnieql("QP", lencoding+len-2, 2))
2917       return true;
2918   }
2919   return false;
2920 }
2921 
2922 
2923 //  ------------------------------------------------------------------
2924 
2925 static char qpencodingbuf[100];
2926 
MakeQuotedPrintable(const char * encoding)2927 char *MakeQuotedPrintable(const char *encoding) {
2928 
2929   strxmerge(qpencodingbuf, sizeof(qpencodingbuf), strlword(encoding), "QP", NULL);
2930   return qpencodingbuf;
2931 }
2932 
2933 //  ------------------------------------------------------------------
2934 
ExtractPlainCharset(const char * encoding)2935 char *ExtractPlainCharset(const char *encoding) {
2936 
2937   strxcpy(qpencodingbuf, strlword(encoding), sizeof(qpencodingbuf));
2938   int len = strlen(qpencodingbuf);
2939   if(len > 2)
2940     qpencodingbuf[len-2] = NUL;
2941   return qpencodingbuf;
2942 }
2943 
2944 
2945 //  ------------------------------------------------------------------
2946 
Latin2ISO(char * iso_encoding,const char * latin_encoding)2947 char *Latin2ISO(char *iso_encoding, const char *latin_encoding) {
2948 
2949   static const char *isono[] = { "15", "1", "2", "3", "4", "9", "10", "13", "14", "15" };
2950   int chsno = atoi(latin_encoding+5);
2951   if(chsno < 0) chsno = -chsno; // support for both latin-1 and latin1
2952   chsno = chsno > sizeof(isono)/sizeof(const char *) ? 0 : chsno;
2953   return strxmerge(iso_encoding, 12, "iso-8859-", isono[chsno], NULL);
2954 }
2955 
2956 
2957 //  ------------------------------------------------------------------
2958 
ISO2Latin(char * latin_encoding,const char * iso_encoding)2959 char *ISO2Latin(char *latin_encoding, const char *iso_encoding) {
2960 
2961   static const char *latinno[] = { NULL, "1", "2", "3", "4", NULL, NULL, NULL, NULL, "5", "6", NULL, NULL, "7", "8", "9" };
2962   int chsno = atoi(strstr(iso_encoding, "8859")+5);
2963   chsno = chsno > sizeof(latinno)/sizeof(const char *) ? 0 : chsno;
2964   if(latinno[chsno] == NULL)
2965     return strxmerge(latin_encoding, 12, iso_encoding, NULL);
2966   else
2967     return strxmerge(latin_encoding, 8, "latin-", latinno[chsno], NULL);
2968 }
2969 
2970 
2971 //  ------------------------------------------------------------------
2972 
CheckLevel(const char * imp,const char * imp2,int n,int & current_table)2973 static bool CheckLevel(const char* imp, const char* imp2, int n, int &current_table)
2974 {
2975   const char *ptr = striinc(imp2, imp);
2976   ptr += strlen(imp2);
2977   strskip_wht(ptr);
2978 
2979   int level = atoi(ptr);
2980 
2981   if (CharTable && (n == current_table) && (level <= CharTable->level))
2982     return true;
2983 
2984   gfile fp(AddPath(CFG->goldpath, CFG->xlatged), "rb", CFG->sharemode);
2985   if (fp.isopen())
2986   {
2987     if (!CharTable) CharTable = (Chs*)throw_calloc(1, sizeof(Chs));
2988     fp.FseekSet(n, sizeof(Chs));
2989     fp.Fread(CharTable, sizeof(Chs));
2990 
2991     ChsTP = CharTable->t;
2992     current_table = n;
2993 
2994     // Disable softcr translation unless DISPSOFTCR is enabled
2995     if (not WideDispsoftcr)
2996     {
2997       char* tptr = (char*)ChsTP[SOFTCR];
2998       *tptr++ = 1;
2999       *tptr = SOFTCR;
3000     }
3001 
3002     if (level <= CharTable->level) return true;
3003   }
3004 
3005   return false;
3006 }
3007 
3008 
3009 //  ------------------------------------------------------------------
3010 
LoadCharset(const char * imp,const char * exp,int query)3011 int LoadCharset(const char* imp, const char* exp, int query) {
3012 
3013   static int current_table = -1;
3014   int n;
3015 
3016   switch(query) {
3017     case 1:
3018       return current_table;
3019     default:
3020       break;
3021   }
3022 
3023 #ifdef HAS_ICONV
3024   if( iconv_cd != (iconv_t)(-1) )
3025     iconv_close(iconv_cd);
3026   iconv_cd = iconv_open(exp, imp);
3027   if(iconv_cd != (iconv_t)(-1) )
3028     LOG.printf("iconv is initialised to convert from %s to %s", imp, exp);
3029   else
3030     LOG.printf("Can't initialise iconv to convert from %s to %s", imp, exp);
3031 #endif
3032 
3033   // Find and load charset table
3034   std::vector<Map>::iterator xlt;
3035   for(n = 0, xlt = CFG->xlatcharset.begin(); xlt != CFG->xlatcharset.end(); xlt++, n++)
3036   {
3037     if (!striinc(xlt->exp, exp)) continue;
3038 
3039     bool imp_found = make_bool(strnieql(xlt->imp, imp, strlen(xlt->imp)));
3040     if (imp_found) imp_found = CheckLevel(imp, xlt->imp, n, current_table);
3041 
3042     std::vector< std::pair<std::string, gstrarray> >::iterator als;
3043     for (als = CFG->xlatcharsetalias.begin();
3044           !imp_found && (als != CFG->xlatcharsetalias.end()); als++)
3045     {
3046       if (strieql(xlt->imp, als->first.c_str()))
3047       {
3048         for (gstrarray::iterator it = als->second.begin(); !imp_found && (it != als->second.end()); it++)
3049           if (striinc(it->c_str(), imp))
3050             imp_found = CheckLevel(imp, it->c_str(), n, current_table);
3051       }
3052     }
3053 
3054     if (imp_found) return CharTable->level;
3055   }
3056 
3057   // No matching table found
3058   throw_release(CharTable);
3059   ChsTP = NULL;
3060   current_table = -1;
3061   return 0;
3062 }
3063 
3064 
3065 //  ------------------------------------------------------------------
3066 
DeleteLine(Line * line)3067 Line* DeleteLine(Line* line) {
3068 
3069   Line* nextline = NULL;
3070 
3071   // Link next and previous lines and release this line
3072 
3073   if(line) {
3074     if(line->prev) {
3075       line->prev->next = line->next;
3076       nextline = line->prev;
3077     }
3078     if(line->next) {
3079       line->next->prev = line->prev;
3080       nextline = line->next;
3081     }
3082     throw_xdelete(line);
3083   }
3084 
3085   return nextline;
3086 }
3087 
3088 
3089 //  ------------------------------------------------------------------
3090 
InsertLine(Line * newline,Line * oldline,int pos)3091 Line* InsertLine(Line* newline, Line* oldline, int pos) {
3092 
3093   if(oldline) {
3094     if(pos >= DIR_BELOW) {
3095       newline->prev = oldline;
3096       newline->next = oldline->next;
3097       if(oldline->next)
3098         oldline->next->prev = newline;
3099       oldline->next = newline;
3100     }
3101     else {
3102       newline->prev = oldline->prev;
3103       newline->next = oldline;
3104       if(oldline->prev)
3105         oldline->prev->next = newline;
3106       oldline->prev = newline;
3107     }
3108   }
3109   return newline;
3110 }
3111 
3112 
3113 //  ------------------------------------------------------------------
3114 
FirstLine(Line * line)3115 Line* FirstLine(Line* line) {
3116 
3117   if(line)
3118     while(line->prev)
3119       line = line->prev;
3120   return line;
3121 }
3122 
3123 
3124 //  ------------------------------------------------------------------
3125 
LastLine(Line * line)3126 Line* LastLine(Line* line) {
3127 
3128   if(line)
3129     while(line->next)
3130       line = line->next;
3131   return line;
3132 }
3133 
3134 
3135 //  ------------------------------------------------------------------
3136 
AddLine(Line * line,const char * buf,int where)3137 Line* AddLine(Line* line, const char* buf, int where) {
3138 
3139   Line* newline = new Line(buf);
3140   throw_xnew(newline);
3141   newline->type = GLINE_HARD;
3142   newline->color = C_READW;
3143 
3144   return InsertLine(newline, line, where);
3145 }
3146 
3147 
3148 //  ------------------------------------------------------------------
3149 
AddLine(Line * line,const char * buf)3150 Line* AddLine(Line* line, const char* buf) {
3151 
3152   return AddLine(line, buf, DIR_BELOW);
3153 }
3154 
3155 
3156 //  ------------------------------------------------------------------
3157 
AddLineFast(Line * oldline,char * text)3158 Line* AddLineFast(Line* oldline, char* text) {
3159 
3160   Line* newline = new Line(text);
3161   throw_xnew(newline);
3162   newline->type = GLINE_HARD;
3163   newline->color = C_READW;
3164   newline->prev = oldline;
3165   newline->next = oldline->next;
3166   if(oldline->next)
3167     oldline->next->prev = newline;
3168   oldline->next = newline;
3169   return newline;
3170 }
3171 
3172 
3173 //  ------------------------------------------------------------------
3174 
AddKludge(Line * line,char * buf,int where)3175 Line* AddKludge(Line* line, char* buf, int where) {
3176 
3177   Line* newline = new Line(buf);
3178   throw_xnew(newline);
3179   newline->type = GLINE_HARD|GLINE_KLUD;
3180   newline->color = C_READK;
3181   return InsertLine(newline, line, where);
3182 }
3183 
3184 
3185 //  ------------------------------------------------------------------
3186 
AddLineF(Line * & line,const char * format,...)3187 Line* AddLineF(Line*& line, const char* format, ...) {
3188 
3189   char buf[256];
3190   va_list argptr;
3191   va_start(argptr, format);
3192   vsprintf(buf, format, argptr);
3193   va_end(argptr);
3194   line = AddLine(line, buf);
3195   return line;
3196 }
3197 
3198 
3199 //  ------------------------------------------------------------------
3200 
AddHexdump(Line * & line,void * data,size_t datalen)3201 Line* AddHexdump(Line*& line, void* data, size_t datalen) {
3202 
3203   char buf[256];
3204   uint pos = 0;
3205   char* ptr = (char*)data;
3206 
3207   while (pos < datalen)
3208   {
3209     uint dataleft = datalen - pos;
3210     gsprintf(PRINTF_DECLARE_BUFFER(buf), "%04X   ", pos);
3211     HexDump16(buf+7, ptr, (dataleft < 16) ? dataleft : 16, HEX_DUMP2);
3212     line = AddLine(line, buf);
3213     ptr += 16;
3214     pos += 16;
3215   }
3216 
3217   return line;
3218 }
3219 
3220 
3221 //  ------------------------------------------------------------------
3222 
ParseInternetAddr(char * __string,char * __name,char * __addr,bool detect_charset)3223 char* ParseInternetAddr(char* __string, char* __name, char* __addr, bool detect_charset) {
3224 
3225   *__name = *__addr = NUL;
3226   char* commaptr = NULL;
3227 
3228   if(strchr(__string, ',')) {
3229     bool inquotes = false;
3230     commaptr = __string;
3231     while(commaptr) {
3232       if(*commaptr == '\"')
3233         inquotes = not inquotes;
3234       else if((*commaptr == ',') and not inquotes) {
3235         *commaptr = NUL;
3236         break;
3237       }
3238       if(not *commaptr)
3239         commaptr = NULL;
3240       else
3241         commaptr++;
3242     }
3243   }
3244 
3245   char* p;
3246   if((p = strrchr(__string, '>')) != NULL)
3247     *(++p) = NUL;
3248 
3249   char* endchar = __string + strlen(__string) - 1;
3250   if(*endchar == /*(*/ ')') {
3251     char* begchar = endchar;
3252     int pcnt = 0;
3253     while(begchar > __string) {
3254       if(*begchar == /*(*/ ')')
3255         pcnt++;
3256       else if(*begchar == '(' /*)*/)
3257         pcnt--;
3258       if(pcnt == 0)
3259         break;
3260       begchar--;
3261     }
3262     if(*begchar == '(' /*)*/)
3263       begchar++;
3264     strbtrim(strxcpy(__name, begchar, MinV((size_t)(endchar-begchar)+1, (size_t)sizeof(INam))));
3265     strbtrim(strxcpy(__addr, __string, MinV((size_t)(begchar-__string), (size_t)sizeof(IAdr))));
3266   }
3267   else if(*endchar == '>') {
3268     char* endaddr = endchar;
3269     while(*endchar != '<' and endchar > __string)
3270       endchar--;
3271     char* begaddr = endchar;
3272     if(*endchar == '<') {
3273       begaddr++;
3274       if (endchar > __string) endchar--;
3275     }
3276     __string = strskip_wht(__string);
3277     strbtrim(strxcpy(__name, __string, MinV((size_t)(endchar-__string)+1, (size_t)sizeof(INam))));
3278     strbtrim(strxcpy(__addr, begaddr, MinV((size_t)(endaddr-begaddr)+1, (size_t)sizeof(IAdr))));
3279   }
3280   else {
3281     strxcpy(__addr, __string, sizeof(IAdr));
3282   }
3283 
3284   if(*__addr == '@') {
3285     char* ptr = strchr(__addr, ':');
3286     if(ptr)
3287       memmove(__addr, ptr+1, strlen(ptr));
3288   }
3289 
3290   if(commaptr)
3291     *commaptr = ',';
3292 
3293   StripQuotes(__name);
3294 
3295   if(not strchr(__addr, '@'))
3296     *__addr = NUL;
3297 
3298   strxmimecpy(__name, __name, 0, strlen(__name)+1, detect_charset);
3299 
3300   return __name;
3301 }
3302 
3303 
3304 //  ------------------------------------------------------------------
3305 
InvalidateControlInfo(GMsg * msg)3306 void InvalidateControlInfo(GMsg* msg) {
3307 
3308   Line* line = msg->lin;
3309   char buf[256];
3310 
3311   while(line) {
3312 
3313     if(not (line->type & (GLINE_TEAR | GLINE_ORIG))) {
3314 
3315       strcpy(buf, line->txt.c_str());
3316 
3317       // Invalidate tearline
3318       if(not CFG->invalidate.tearline.first.empty())
3319         doinvalidate(buf, CFG->invalidate.tearline.first.c_str(), CFG->invalidate.tearline.second.c_str(), true);
3320       else
3321         doinvalidate(buf, "---", "-+-", true);
3322 
3323       // Invalidate originline
3324       if(not CFG->invalidate.origin.first.empty())
3325         doinvalidate(buf, CFG->invalidate.origin.first.c_str(), CFG->invalidate.origin.second.c_str());
3326       else
3327         doinvalidate(buf, " * Origin: ", " + Origin: ");
3328 
3329       // Invalidate SEEN-BY's
3330       if(not CFG->invalidate.seenby.first.empty())
3331         doinvalidate(buf, CFG->invalidate.seenby.first.c_str(), CFG->invalidate.seenby.second.c_str());
3332       else
3333         doinvalidate(buf, "SEEN-BY: ", "SEEN+BY: ");
3334 
3335       if(stricmp(buf, line->txt.c_str())) {
3336         line->type &= ~GLINE_KLUDGE;
3337         line->kludge = 0;
3338         line->color = C_READW;
3339         line->txt = buf;
3340       }
3341 
3342     }
3343     line = line->next;
3344   }
3345 }
3346 
3347 
3348 //  ------------------------------------------------------------------
3349