dthelp: Change to ANSI function definitions
[oweals/cde.git] / cde / programs / dtmail / libDtMail / RFC / RFCEnvelope.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *+SNOTICE
25  *
26  *
27  *      $TOG: RFCEnvelope.C /main/7 1998/04/06 13:27:59 mgreess $
28  *
29  *      RESTRICTED CONFIDENTIAL INFORMATION:
30  *      
31  *      The information in this document is subject to special
32  *      restrictions in a confidential disclosure agreement bertween
33  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
34  *      document outside HP, IBM, Sun, USL, SCO, or Univel wihtout
35  *      Sun's specific written approval.  This documment and all copies
36  *      and derivative works thereof must be returned or destroyed at
37  *      Sun's request.
38  *
39  *      Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
40  *
41  *+ENOTICE
42  */
43
44 #ifndef I_HAVE_NO_IDENT
45 #endif
46
47 #include <EUSCompat.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 #include <string.h>
51 #include <ctype.h>
52 #include <sys/utsname.h>
53
54 #include <DtMail/DtMail.hh>
55 #include "RFCImpl.hh"
56 #include <DtMail/Threads.hh>
57 #include <DtMail/IO.hh>
58 #include "str_utils.h"
59
60 unsigned long RFCEnvelopeSignature = 0x55fd23ef;
61
62 // This constant defines how big the parsed header structure starts.
63 // it will grow dynamically over time if need be. The guess here is
64 // that most messages will have about 32 headers so we won't have
65 // to grow very often, but we also aren't wasting a lot of space.
66 //
67 static const int INITIAL_HEADER_COUNT = 32;
68
69 static const int NAME_MASK = 0x1;
70 static const int VALUE_MASK = 0x2;
71
72 RFCEnvelope::RFCEnvelope(DtMailEnv & error,
73                          DtMail::Message * parent,
74                          const char * start,
75                          const int len)
76 : DtMail::Envelope(error, parent), _parsed_headers(INITIAL_HEADER_COUNT)
77 {
78     error.clear();
79
80     _header_text = start;
81     _header_len = len;
82     _dirty = 0;
83     _header_lock = MutexInit();
84
85     if (_header_text) {
86         parseHeaders();
87     }
88
89     _object_signature = RFCEnvelopeSignature;
90     //fix for the defect 177527
91     _use_reply_to=DTM_TRUE;
92 }
93
94 RFCEnvelope::~RFCEnvelope(void)
95 {
96     if (_object_signature == RFCEnvelopeSignature) {
97         MutexLock lock_scope(_obj_mutex);
98         if (_object_signature == RFCEnvelopeSignature) {
99             for (int hdr = 0; hdr < _parsed_headers.length(); hdr++) {
100                 ParsedHeader * hdrp = _parsed_headers[hdr];
101                 delete _parsed_headers[hdr];
102             }
103             _object_signature = 0;
104         }
105     }
106 }
107
108 DtMailHeaderHandle
109 RFCEnvelope::getFirstHeader(DtMailEnv & error,
110                              char ** name,
111                              DtMailValueSeq & value)
112 {
113     MutexLock lock_header(_header_lock);
114
115     error.clear();
116
117     if (_parsed_headers.length() == 0) {
118         return(NULL); // No headers.
119     }
120
121     DtMailHeaderHandle handle = _parsed_headers[0];
122
123     *name = (char *)malloc(_parsed_headers[0]->name_len + 1);
124     strncpy(*name, _parsed_headers[0]->name_start, _parsed_headers[0]->name_len);
125     (*name)[_parsed_headers[0]->name_len] = 0;
126
127     makeValue(error, *_parsed_headers[0], value);
128
129     return(handle);
130 }
131
132 DtMailHeaderHandle
133 RFCEnvelope::getNextHeader(DtMailEnv & error,
134                             DtMailHeaderHandle last,
135                             char ** name,
136                             DtMailValueSeq & value)
137 {
138     MutexLock lock_header(_header_lock);
139
140     error.clear();
141
142     if (!last) {
143         error.setError(DTME_BadArg);
144         return(NULL);
145     }
146
147     int slot = _parsed_headers.indexof((ParsedHeader *)last);
148     if (slot < 0) {
149         return(NULL);
150     }
151
152     slot += 1;
153     if (slot >= _parsed_headers.length()) {
154         return(NULL);
155     }
156
157     ParsedHeader * hdr = _parsed_headers[slot];
158
159     *name = (char *)malloc(hdr->name_len + 1);
160     strncpy(*name, hdr->name_start, hdr->name_len);
161     (*name)[hdr->name_len] = 0;
162
163     makeValue(error, *hdr, value);
164
165     return(hdr);
166 }
167
168 struct AbstractMap {
169     const char *        abstract;
170     const char **       transports;
171 };
172
173 static const char * DtMailMessageToMap[] = {
174     "To", "Apparently-To", "Resent-To", NULL
175 };
176
177 static const char * DtMailMessageSenderMap[] = {
178     "Reply-To", "From", "Return-Path", "Resent-From", NULL
179 };
180
181 static const char * DtMailMessageCcMap[] = {
182     "Cc", NULL
183 };
184
185 static const char * DtMailMessageBccMap[] = {
186     "Bcc", NULL
187 };
188
189 static const char * DtMailMessageReceivedTimeMap[] = {
190     NULL
191 };
192
193 static const char * DtMailMessageSentTimeMap[] = {
194 "Date", NULL
195 };
196
197 static const char * DtMailMessageIdMap[] = {
198     "Message-Id", NULL
199 };
200
201 static const char * DtMailMessageSubjectMap[] = {
202     "Subject", NULL
203 };
204
205 static const char * DtMailMessageContentLengthMap[] = {
206     "Content-Length", NULL
207 };
208
209 static const char * DtMailMessageStatusMap[] = {
210     "Status", "X-Status", NULL
211 };
212
213 static const char * DtMailMessageV3charsetMap[] = {
214     "X-Sun-Charset", NULL
215 };
216
217 static const char * DtMailMessageContentTypeMap[] = {
218     "Content-Type", NULL
219 };
220
221 static const AbstractMap abstract_map[] = {
222 { DtMailMessageTo,              DtMailMessageToMap },
223 { DtMailMessageSender,          DtMailMessageSenderMap },
224 { DtMailMessageCc,              DtMailMessageCcMap },
225 { DtMailMessageBcc,             DtMailMessageBccMap },
226 { DtMailMessageReceivedTime,    DtMailMessageReceivedTimeMap },
227 { DtMailMessageSentTime,        DtMailMessageSentTimeMap },
228 { DtMailMessageMessageId,       DtMailMessageIdMap },
229 { DtMailMessageSubject,         DtMailMessageSubjectMap },
230 { DtMailMessageContentLength,   DtMailMessageContentLengthMap },
231 { DtMailMessageStatus,          DtMailMessageStatusMap },
232 { DtMailMessageV3charset,               DtMailMessageV3charsetMap },
233 { DtMailMessageContentType,             DtMailMessageContentTypeMap },
234 { NULL,                         NULL }
235 };
236
237 void
238 RFCEnvelope::getHeader(DtMailEnv & error,
239                         const char * name,
240                         const DtMailBoolean abstract,
241                         DtMailValueSeq & value)
242 {
243     MutexLock lock_header(_header_lock);
244
245     error.clear();
246
247     // If we are not in the abstract space, then simply get the
248     // transport header and return.
249     //
250     if (abstract == DTM_FALSE) {
251         getTransportHeader(error, name, value);
252     }
253     else {
254         // We need to handle reply to's with special care. We may
255         // need to strip the sender from the list.
256         //
257         if (strcmp(name, DtMailMessageToReply) == 0) {
258             makeReply(error, DtMailMessageTo, value);
259             return;
260         }
261
262         if (strcmp(name, DtMailMessageCcReply) == 0) {
263             makeReply(error, DtMailMessageCc, value);
264             return;
265         }
266
267         // Okay, we need to work out what the transport name
268         // might be. We do this by first finding the abstract
269         // name in the abstract->transport mapping table.
270         //
271         int abs = 0;
272         for (abs = 0; abstract_map[abs].abstract; abs++) {
273             if (strcmp(abstract_map[abs].abstract, name) == 0) {
274                 break;
275             }
276         }
277
278         // If we didn't find the abstract name, then feed it through
279         // as a transport name. This is useful behavior so intermediates
280         // like the RFCMailBox::getMessageSummary method can always
281         // assume abstract names, but allow its client to specify transport
282         // names.
283         //
284         if (!abstract_map[abs].abstract) {
285             getTransportHeader(error, name, value);
286             return;
287         }
288
289         // Now we need to go through the list of transport names until
290         // we find a match. The first match is taken because the map
291         // should be ordered based on preference.
292         //
293         for (int trans = 0; abstract_map[abs].transports[trans]; trans++) {
294             getTransportHeader(error,
295                                abstract_map[abs].transports[trans],
296                                value);
297         // If the abstract is  DtMailMessageSender we need do something
298         // special. If the request is for displaying the sender's
299         // name or e-mail address in the msg list scrolled window in the
300         // RMW, we should return the "From" value instead of the 
301         // "Reply-To" value. Otherwise, we should return the Reply-To
302         // value (such as "Reply to sender")
303         //  
304             if (error.isNotSet()) { // Found one!
305                if(!getUseReplyTo() && 
306                    strcmp(abstract_map[abs].transports[trans],"Reply-To") == 0)
307                  {
308                   value.clear();
309                   continue;
310                  }
311                 else
312                   return;
313             }
314             error.clear();
315         }
316
317         // If this isn't a request for the sender ("From"), then
318         // we don't have a known value for the header. Bail!
319         //
320         if (strcmp(DtMailMessageSender, name) && 
321             strcmp(DtMailMessageReceivedTime, name)) {
322             error.setError(DTME_NoObjectValue);
323             return;
324         }
325
326         if (strncmp(_parsed_headers[0]->name_start, "From", 4) == 0) {
327             if (strcmp(DtMailMessageSender, name) == 0) {
328                 parseUnixFrom(error, *_parsed_headers[0], value);
329             }
330             else if (strcmp(DtMailMessageReceivedTime, name) == 0) {
331                 parseUnixDate(error, *_parsed_headers[0], value);
332             }
333         }
334         else {
335             error.setError(DTME_NoObjectValue);
336             return;
337         }
338         return;
339     }
340
341     return;
342 }
343
344 void
345 RFCEnvelope::setHeader(DtMailEnv & error,
346                        const char * name, 
347                        const DtMailBoolean replace,
348                        const char * val)
349 {
350     MutexLock lock_header(_header_lock);
351
352     RFCMessage * msg = (RFCMessage *)_parent;
353
354     error.clear();
355
356     // First we need to see if we have this header. We are
357     // only interested in the first occurrence.
358     //
359     ParsedHeader * hdr;
360     const char * real_name;
361
362     // Find the header if it currently exists
363     //
364     int slot = lookupHeader(name);
365
366     // Determine if the value is really empty (just blanks)
367     //
368     DtMailBoolean valueIsEmpty = DTM_TRUE;
369     for (const char *cv = val; *cv; cv++) {
370       if (!isspace((unsigned char)*cv)) {
371         valueIsEmpty = DTM_FALSE;
372         break;
373       }
374     }
375     
376     // If the value to be set is empty (only spaces), then treat this
377     // set header request specially - either toss it if requesting to
378     // append an empty header, or remove an existing header if requesting
379     // to set an empty header
380     //
381     if (valueIsEmpty == DTM_TRUE) {
382       if (slot < 0) {
383         // The header was not found and the value is empty -
384         // Just return as there was no header to set anyway
385         return;
386       }
387       if (replace == DTM_TRUE) {
388         // The header was found and replacement was requested
389         // but the value is empty - request to replace existing
390         // header with empty header - treat as a remove header request
391         //
392         removeHeader(error, name);
393         return;
394       }
395       // The header was found, replacement is not requested, and the
396       // value is empty - request to append empty header - toss
397       //
398       return;
399     }
400     
401     if (slot < 0 || replace == DTM_FALSE) {
402         // Need to create a new header entry for this one.
403         //
404         hdr = new ParsedHeader;
405         slot = _parsed_headers.append(hdr);
406         real_name = mapName(name);
407         _dirty = 1;                             // new entry: header dirty
408     }
409     else {
410         hdr = _parsed_headers[slot];
411         real_name = name;
412     }
413
414     // First, see if we need to do something about the name.
415     //
416     if (!hdr->name_start) {
417         hdr->alloc_mask |= NAME_MASK;
418         hdr->name_start = strdup(real_name);
419         hdr->name_len = strlen(real_name);
420         _header_len += hdr->name_len;
421         _dirty = 1;                             // new name: header dirty
422     }
423
424     // Clean up the existing value if need be.
425     //
426     if (hdr->value_start) {
427       if ( (strlen(val) != hdr->value_len)
428         || (strncmp(hdr->value_start, val, hdr->value_len)!=0) )        // has value changed??
429           _dirty = 1;                           // yes: header dirty
430         if (hdr->alloc_mask & VALUE_MASK) {
431             free((char *)hdr->value_start);
432         }
433         hdr->value_start = NULL;
434         _header_len -= hdr->value_len;
435         hdr->value_len = 0;
436     }
437     else
438       _dirty = 1;                               // new value: header dirty
439
440     hdr->value_start = strdup(val);
441     hdr->value_len = strlen(hdr->value_start);
442     _header_len += hdr->value_len + 1;
443     hdr->alloc_mask |= VALUE_MASK;
444
445     if (msg) {
446       msg->markDirty(_dirty);
447     }
448 }
449
450 void
451 RFCEnvelope::removeHeader(DtMailEnv & error, const char * name)
452 {
453     MutexLock lock_header(_header_lock);
454
455     error.clear();
456
457     // Remove all versions of this header.
458     //
459     int slot = lookupHeader(name);
460     while (slot >= 0) {
461         _parsed_headers.remove(slot);
462         slot = lookupHeader(name);
463     }
464
465     RFCMessage * msg = (RFCMessage *)_parent;
466     if (msg) {
467         msg->markDirty(1);
468     }
469
470     if (!_dirty) {
471         _dirty += 1;
472     }
473
474     return;
475 }
476
477 void
478 RFCEnvelope::adjustHeaderLocation(char * headerStart, int headerLength)
479 {
480     MutexLock lock_header(_header_lock);
481
482     if (_dirty) {
483       // reparse headers in their new location
484       // destroy current headers
485       //
486       for (int hdr = 0; hdr < _parsed_headers.length(); hdr++) {
487         ParsedHeader * hdrp = _parsed_headers[hdr];
488         delete _parsed_headers[hdr];
489         _parsed_headers.remove(hdr);
490         hdr -=1;
491       }
492       
493       // parse headers from scratch
494       _header_text = headerStart;
495       _header_len = headerLength;
496       parseHeaders();
497     }
498     else {
499       // We must adjust the offset of every header in the parsed header
500       // structure. For those headers values that have been malloc()ed
501       // (e.g. NAME_MASK or VALUE_MASK are set), don't have to do anything
502       // as they are deallocated only when the header is destroyed.
503       //
504       for (int hdr = 0; hdr < _parsed_headers.length(); hdr++) {
505         ParsedHeader * h = _parsed_headers[hdr];
506         if (!(h->alloc_mask & NAME_MASK)) {
507           h->name_start = (h->name_start - _header_text) + headerStart;
508         }
509         if (!(h->alloc_mask & VALUE_MASK)) {
510           h->value_start = (h->value_start - _header_text) + headerStart;
511         }
512       }
513       _header_text = headerStart;
514     }
515     _dirty = 0;
516     return;
517 }
518
519 char *
520 RFCEnvelope::writeHeaders(char * new_loc)
521 {
522   MutexLock lock_header(_header_lock);
523
524   // Copy the headers to the new region
525   //
526   char * cur_loc = new_loc;
527   int first = 1;
528   for (int hdr = 0; hdr < _parsed_headers.length(); hdr++) {
529     ParsedHeader * h = _parsed_headers[hdr];
530     
531     const char * new_name = cur_loc;
532     memcpy(cur_loc, h->name_start, h->name_len);
533     cur_loc += h->name_len;
534     if (!first || strncmp(h->name_start, "From", h->name_len) != 0) {
535       *cur_loc++ = ':';
536     }
537     *cur_loc++ = ' ';
538     first = 0;
539     
540     // Copy the value
541     //
542     const char * new_value = cur_loc;
543     memcpy(cur_loc, h->value_start, h->value_len);
544     cur_loc += h->value_len;
545     
546     // Insert a trailing crlf if necessary.  We need this so that when
547     // we write into the file, the header lines are as in RFC822 format.
548     if (*(cur_loc - 1) != '\n') {
549       *cur_loc++ = '\n';
550     }
551   }
552
553   return(cur_loc - 1);
554 }
555
556
557 const char *
558 RFCEnvelope::unixFrom(DtMailEnv & error, int & length)
559 {
560     ParsedHeader * hdr = _parsed_headers[0];
561     const char * ufrom = NULL;
562
563     length = 0;
564     error.clear();
565
566     if (strncmp(hdr->name_start, "From ", 5) == 0) {
567         ufrom = hdr->name_start;
568         length = (hdr->value_start + hdr->value_len) - hdr->name_start + 1;
569     }
570     else {
571         error.setError(DTME_NoObjectValue);
572     }
573
574     return(ufrom);
575 }
576
577 void
578 RFCEnvelope::parseHeaders(void)
579 {
580     // Now we actually parse the headers. Each header either ends with
581     // a new line, or is continued if the next line begins with white
582     // space.
583     //
584     ParsedHeader * hdr = new ParsedHeader;
585     for (const char * scan = _header_text; scan < (_header_text + _header_len);) {
586         if ((scan == _header_text) && (strncmp(scan, "From ", 5) == 0)) {
587             // Unix "From" line. This header has a different structure.
588             // It is "From user@host <date>". It does not have a colon
589             // like all other RFC headers so we have to parse it specially.
590             //
591             hdr->name_start = scan;
592             hdr->name_len = 4;
593
594             // Look for the first non-blank after the "From ".
595             //
596             for (scan += 4; *scan && isspace((unsigned char)*scan); scan++) {
597                 continue;
598             }
599
600             hdr->value_start = scan;
601
602             // Find the new line.
603             for (; *scan && *scan != '\n'; scan++) {
604                 continue;
605             }
606
607             hdr->value_len = scan - hdr->value_start;
608             if (*(scan - 1) == '\r') {
609                 hdr->value_len -= 1;
610             }
611
612             scan += 1;
613             _parsed_headers.append(hdr);
614             hdr = new ParsedHeader;
615             continue;
616         }
617
618         // We should be at the start of a header. Let's look for a ":". If
619         // we find any white space first, then we have a problem.
620         //
621         hdr->name_start = scan;
622         for (;*scan && *scan != ':'; scan++) {
623             if (isspace((unsigned char)*scan)) {
624                 break;
625             }
626         }
627
628         if (*scan != ':') {
629             // Find the next new line, and try again.
630             //
631             for (;*scan && *scan != '\n'; scan++) {
632                 continue;
633             }
634             scan += 1;
635             continue;
636         }
637
638         hdr->name_len = scan - hdr->name_start;
639
640         // Look for the first non-blank after the colon.
641         //
642         for (scan += 1; scan < (_header_text + _header_len) 
643              && *scan != '\n' && isspace((unsigned char)*scan); scan++) {
644             continue;
645         }
646
647         if (*scan == '\n') {
648             // Null value!
649             hdr->value_start = scan;
650             hdr->value_len = 0;
651             _parsed_headers.append(hdr);
652             hdr = new ParsedHeader;
653             scan += 1;
654             continue;
655         }
656
657         // Okay, now we want scan looking for a new line that is
658         // not followed immediately by white space. That will give
659         // us the end of the header.
660         //
661         hdr->value_start = scan;
662         for (;scan < (_header_text + _header_len); scan++) {
663             if (*scan == '\n' && !isspace((unsigned char)*(scan + 1))) {
664                 break;
665             }
666         }
667
668         hdr->value_len = scan - hdr->value_start;
669         if (*(scan - 1) == '\r') {
670             hdr->value_len -= 1;
671         }
672
673         scan += 1;
674         _parsed_headers.append(hdr);
675         hdr = new ParsedHeader;
676     }
677
678     // Made one to many.
679     //
680     delete hdr;
681 }
682
683 void
684 RFCEnvelope::getTransportHeader(DtMailEnv & error,
685                                  const char * name,
686                                  DtMailValueSeq & value)
687 {
688     error.clear();
689
690     // First, let's try to find out how many times the header
691     // appears. It may appear 1, many, or not at all.
692     //
693     int appears = 0;
694     for (int hdr = 0; hdr < _parsed_headers.length(); hdr++) {
695         // We need to lock the object until we are done.
696         //
697         MutexLock lock_header(_parsed_headers[hdr]->mutex);
698
699         // Make sure we have a header!
700         //
701         if (!_parsed_headers[hdr]->name_start ||
702             !_parsed_headers[hdr]->value_start) {
703             continue;
704         }
705
706         // Unix From doesn't count. We only use it as a fall back.
707         // It will always appear as the first header, if it appears
708         // at all.
709         //
710         if (hdr == 0 && 
711             strncmp(_parsed_headers[hdr]->name_start, "From ", 5) == 0) {
712             continue;
713         }
714
715         if (matchName(*_parsed_headers[hdr], name) == DTM_TRUE) {
716             // If the header exists, make sure it has a value.
717             if (_parsed_headers[hdr]->value_len > 0)
718                 appears += 1;
719         }
720     }
721
722     if (appears == 0) { // Not here!
723         error.setError(DTME_NoObjectValue);
724         return;
725     }
726
727     // Second pass, find the headers and convert the values to the
728     // appropriate type.
729     //
730     int ent = 0;
731     for (int val = 0; val < _parsed_headers.length(); val++) {
732         if (val == 0 && 
733             strncmp(_parsed_headers[val]->name_start, "From ", 5) == 0) {
734             continue;
735         }
736
737         if (matchName(*_parsed_headers[val], name) == DTM_TRUE) {
738             RFCValue * new_value = new RFCValue(_parsed_headers[val]->value_start,
739                                                 _parsed_headers[val]->value_len, _parent->session());
740             value.append(new_value);
741         }
742     }
743
744     return;
745 }
746
747 void
748 RFCEnvelope::parseUnixFrom(DtMailEnv & error,
749                             const ParsedHeader & hdr,
750                             DtMailValueSeq & value)
751 {
752     error.clear();
753
754     // The value_start will point to the beginning of the address.
755     // The Unix From header doesn't maintain all of the strange
756     // quoting behavior so spaces don't appear.
757     //
758     const char *end;
759     for (end = hdr.value_start; *end && !isspace((unsigned char)*end); end++) {
760         continue;
761     }
762
763     int size = end - hdr.value_start;
764
765     RFCValue * new_value = new RFCValue(hdr.value_start, size, _parent->session());
766     value.append(new_value);
767
768     return;
769 }
770
771 void
772 RFCEnvelope::parseUnixDate(DtMailEnv & error,
773                            const ParsedHeader & hdr,
774                            DtMailValueSeq & value)
775 {
776     error.clear();
777
778     // The value_start will point to the beginning of the address.
779     // The Unix From header doesn't maintain all of the strange
780     // quoting behavior so spaces don't appear.
781     //
782     const char * end;
783     for (end = hdr.value_start; end < (hdr.value_start + hdr.value_len) 
784          && !isspace((unsigned char)*end); end++) {
785         continue;
786     }
787
788     // Now end points at the white space between the sender and
789     // the date it arrived.
790     //
791     for (; end < (hdr.value_start + hdr.value_len) &&
792          isspace((unsigned char)*end); end++) {
793         continue;
794     }
795
796     // Now we are at the start of the date.
797     //
798     int size = (hdr.value_start + hdr.value_len) - end;
799
800     RFCValue * new_value = new RFCValue(end, size, _parent->session());
801     value.append(new_value);
802
803     return;
804 }
805
806 void
807 RFCEnvelope::makeValue(DtMailEnv & error,
808                         const ParsedHeader & hdr,
809                         DtMailValueSeq & value)
810 {
811     error.clear();
812
813     RFCValue * new_value = new RFCValue(hdr.value_start, hdr.value_len, _parent->session());
814     value.append(new_value);
815 }
816
817 DtMailBoolean
818 RFCEnvelope::matchName(const ParsedHeader & hdr, const char * name)
819 {
820     if (hdr.name_len != strlen(name)) {
821         return(DTM_FALSE);
822     }
823
824     if (strncasecmp(hdr.name_start, name, hdr.name_len) == 0) {
825         return(DTM_TRUE);
826     }
827
828     return(DTM_FALSE);
829 }
830
831 int
832 RFCEnvelope::lookupHeader(const char * name, DtMailBoolean real_only)
833 {
834     // Look for the name in real space.
835     //
836     int hdr;
837     int len = strlen(name);
838     for (hdr = 0; hdr < _parsed_headers.length(); hdr++) {
839         if (_parsed_headers[hdr]->name_start &&
840             strncasecmp(_parsed_headers[hdr]->name_start, name, len) == 0) {
841             return(hdr);
842         }
843     }
844
845     if (real_only == DTM_TRUE) {
846         return(-1);
847     }
848
849     for (const AbstractMap * abs = abstract_map; abs->abstract; abs++) {
850         if (strcmp(abs->abstract, name) == 0) {
851             for (int trans = 0; abs->transports[trans]; trans++) {
852                 int slot = lookupHeader(abs->transports[trans], DTM_TRUE);
853                 if (slot >= 0) {
854                     return(slot);
855                 }
856             }
857         }
858     }
859
860     return(-1);
861 }
862
863 void
864 RFCEnvelope::makeReply(DtMailEnv & error,
865                        const char * name,
866                        DtMailValueSeq & value)
867 {
868     error.clear();
869
870     // We have an abstract name. We need to loop through the
871     // transport headers, gathering up the values.
872     //
873     int abs = 0;
874     for (abs = 0; abstract_map[abs].abstract; abs++) {
875         if (strcmp(abstract_map[abs].abstract, name) == 0) {
876             break;
877         }
878     }
879
880     if (!abstract_map[abs].abstract) {
881         error.setError(DTME_NoObjectValue);
882     }
883
884     // Now we need to go through the list of transport names until
885     // we find a match. The first match is taken because the map
886     // should be ordered based on preference.
887     //
888     DtMailBoolean found = DTM_FALSE;
889     DtMailValueSeq lvalue;
890     for (int trans = 0; abstract_map[abs].transports[trans]; trans++) {
891         getTransportHeader(error,
892                            abstract_map[abs].transports[trans],
893                            lvalue);
894         if (error.isNotSet()) { // Found one!
895             found = DTM_TRUE;
896             break;
897         }
898         error.clear();
899     }
900
901     if (found == DTM_FALSE) {
902         error.setError(DTME_NoObjectValue);
903         return;
904     }
905
906     if (_parent == NULL) {
907         // We're done. Copy the values from one to the other.
908         //
909         for (int nc = 0; nc < lvalue.length(); nc++) {
910             RFCValue * new_value = new RFCValue(*(lvalue[nc]), strlen(*(lvalue[nc])), _parent->session());
911             value.append(new_value);
912         }
913         return;
914     }
915
916     DtMail::MailRc * mailrc = _parent->session()->mailRc(error);
917
918     // If metoo is set, then we are also done.
919     //
920     const char * mval;
921     DtMailEnv lerror;
922     mailrc->getValue(lerror, "metoo", &mval);
923     if (lerror.isNotSet()) {
924         for (int nc = 0; nc < lvalue.length(); nc++) {
925             RFCValue * new_value = new RFCValue(*(lvalue[nc]), strlen(*(lvalue[nc])), _parent->session());
926             value.append(new_value);
927         }
928         return;
929     }
930
931     // Finally, the point of being here! We need to build a new
932     // value that has the user stripped from the reply list. This
933     // includes any alternates the user has specified.
934     //
935     passwd pw;
936     GetPasswordEntry(pw);
937     struct utsname uname_val;
938     uname(&uname_val);
939
940     char * my_addr = new char[strlen(pw.pw_name) + strlen(uname_val.nodename) + 5];
941     strcpy(my_addr, pw.pw_name);
942     strcat(my_addr, "@");
943     strcat(my_addr, uname_val.nodename);
944
945     DtMailAddressSeq alts;
946     DtMailValueAddress * me = new DtMailValueAddress(my_addr,
947                                                      pw.pw_gecos,
948                                                      DtMailAddressDefault);
949     alts.append(me);
950
951     // Fetch the alternates, if any and add them to the list to strip.
952     //
953     const char * others = mailrc->getAlternates(lerror);
954     if (others) {
955         RFCTransport::arpaPhrase(others, alts);
956     }
957
958     // Finally, we need to see if the user wants us to ignore the host
959     // component of the address when stripping.
960     //
961     DtMailBoolean allnet = DTM_FALSE;
962     mailrc->getValue(lerror, "allnet", &mval);
963     if (lerror.isNotSet()) {
964         allnet = DTM_TRUE;
965     }
966
967     DtMailAddressSeq keepers;
968
969     for (int nc = 0; nc < lvalue.length(); nc++) {
970         DtMailAddressSeq * cur_val = lvalue[nc]->toAddress();
971         for (int naddr = 0; naddr < cur_val->length(); naddr++) {
972             if (metooAddr(*(*cur_val)[naddr], alts, allnet) == DTM_FALSE) {
973                 DtMailValueAddress * kaddr = new DtMailValueAddress(*(*cur_val)[naddr]);
974                 keepers.append(kaddr);
975             }
976         }
977     }
978
979     // Finally, let's build a single string from the remaining list and
980     // set the value to that string.
981     //
982     int max_len = 0;
983     for (int cstr = 0; cstr < keepers.length(); cstr++) {
984         DtMailValueAddress * addr = keepers[cstr];
985         max_len += strlen(addr->dtm_address) + 3;
986     }
987
988     if (max_len > 0) {
989         char * str_addr = new char[max_len];
990         str_addr[0] = 0;
991         for (int copy = 0; copy < keepers.length(); copy++) {
992             DtMailValueAddress * addr = keepers[copy];
993             strcat(str_addr, addr->dtm_address);
994             if (copy != (keepers.length() - 1)) {
995                 strcat(str_addr, ", ");
996             }
997         }
998
999         RFCValue * new_val = new RFCValue(str_addr, strlen(str_addr), _parent->session());
1000         value.append(new_val);
1001         delete str_addr;
1002     }
1003 }
1004
1005 char *
1006 striphosts(char * addr)
1007 {
1008     char *cp, *cp2;
1009     
1010     if ((cp = strrchr(addr,'!')) != NULL)
1011         cp++;
1012     else
1013         cp = addr;
1014     /*
1015      * Now strip off all Internet-type
1016      * hosts.
1017      */
1018     if ((cp2 = strchr(cp, '%')) == NULL)
1019         cp2 = strchr(cp, '@');
1020     if (cp2 != NULL)
1021         *cp2 = '\0';
1022     return(cp);
1023 }
1024
1025 DtMailBoolean
1026 RFCEnvelope::metooAddr(DtMailValueAddress & addr,
1027                        DtMailAddressSeq & alts,
1028                        DtMailBoolean allnet)
1029 {
1030     char * str_addr_buf = strdup(addr.dtm_address);
1031     char * str_addr = str_addr_buf;
1032
1033     if (allnet) {
1034         str_addr = striphosts(str_addr_buf);
1035     }
1036
1037     for (int nalt = 0; nalt < alts.length(); nalt++) {
1038         char * cmp_addr_buf = strdup(alts[nalt]->dtm_address);
1039         char * cmp_addr = cmp_addr_buf;
1040
1041         if (allnet) {
1042             cmp_addr = striphosts(cmp_addr_buf);
1043         }
1044
1045         if (strcasecmp(str_addr, cmp_addr) == 0) {
1046             free(str_addr_buf);
1047             free(cmp_addr_buf);
1048             return(DTM_TRUE);
1049         }
1050
1051         free(cmp_addr_buf);
1052     }
1053
1054     free(str_addr_buf);
1055     return(DTM_FALSE);
1056 }
1057
1058
1059 // This method attempts to map an abstract name to a transport name.
1060 // When multiple transport names exist, the first is always used.
1061 //
1062 const char *
1063 RFCEnvelope::mapName(const char * name)
1064 {
1065     for (const AbstractMap * abs = abstract_map; abs->abstract; abs++) {
1066         if (strcmp(name, abs->abstract) == 0) {
1067             return(abs->transports[0]);
1068         }
1069     }
1070
1071     // Must be a real name.
1072     //
1073     return(name);
1074 }
1075
1076 RFCEnvelope::ParsedHeader::ParsedHeader(void)
1077 {
1078     mutex = MutexInit();
1079     alloc_mask = 0;
1080     name_start = NULL;
1081     name_len = 0;
1082     value_start = NULL;
1083     value_len = 0;
1084 }
1085
1086 RFCEnvelope::ParsedHeader::~ParsedHeader(void)
1087 {
1088     MutexLock lock_scope(mutex);
1089
1090     if (alloc_mask & NAME_MASK) {
1091         free((char *)name_start);
1092     }
1093
1094     if (alloc_mask & VALUE_MASK) {
1095         free((char *)value_start);
1096     }
1097     
1098     lock_scope.unlock_and_destroy();
1099 }