Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtmail / dtmail / DmxMessage.C
1 /* $TOG: DmxMessage.C /main/6 1998/07/24 16:18:17 mgreess $ */
2
3 /*
4  *+SNOTICE
5  *
6  *      $:$
7  *
8  *      RESTRICTED CONFIDENTIAL INFORMATION:
9  *      
10  *      The information in this document is subject to special
11  *      restrictions in a confidential disclosure agreement between
12  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
13  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
14  *      Sun's specific written approval.  This document and all copies
15  *      and derivative works thereof must be returned or destroyed at
16  *      Sun's request.
17  *
18  *      Copyright 1994 Sun Microsystems, Inc.  All rights reserved.
19  *
20  *+ENOTICE
21  */
22 /*
23  *                   Common Desktop Environment
24  *
25  *   (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
26  *   (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
27  *   (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
28  *   (c) Copyright 1993, 1994, 1995 Novell, Inc.
29  *   (c) Copyright 1995 Digital Equipment Corp.
30  *   (c) Copyright 1995 Fujitsu Limited
31  *   (c) Copyright 1995 Hitachi, Ltd.
32  *                                                                   
33  *
34  *                     RESTRICTED RIGHTS LEGEND                              
35  *
36  *Use, duplication, or disclosure by the U.S. Government is subject to
37  *restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in
38  *Technical Data and Computer Software clause in DFARS 252.227-7013.  Rights
39  *for non-DOD U.S. Government Departments and Agencies are as set forth in
40  *FAR 52.227-19(c)(1,2).
41
42  *Hewlett-Packard Company, 3000 Hanover Street, Palo Alto, CA 94304 U.S.A.
43  *International Business Machines Corp., Route 100, Somers, NY 10589 U.S.A. 
44  *Sun Microsystems, Inc., 2550 Garcia Avenue, Mountain View, CA 94043 U.S.A.
45  *Novell, Inc., 190 River Road, Summit, NJ 07901 U.S.A.
46  *Digital Equipment Corp., 111 Powdermill Road, Maynard, MA 01754, U.S.A.
47  *Fujitsu Limited, 1015, Kamikodanaka Nakahara-Ku, Kawasaki 211, Japan
48  *Hitachi, Ltd., 6, Kanda Surugadai 4-Chome, Chiyoda-ku, Tokyo 101, Japan
49  */
50
51 #include "Dmx.h"
52 // For CHARSET
53 #include <LocaleXlate.h>
54 #include <locale.h>
55 #if !defined(USL) && !defined(__uxp__)
56 #include <strings.h>
57 #else 
58 #include <EUSCompat.h>
59 #endif 
60
61 #include "Application.h"
62 #include "DtMail/DtMail.hh"
63 #include "DtMailTypes.h"
64 #include "MailSession.hh"
65 #include "RoamApp.h"
66 #include "str_utils.h"
67
68
69 DmxMsg::DmxMsg (void)
70 {
71         // initialize everything
72         message = NULL;
73         addlInfo = NULL;
74         numBPs = 0;
75         cachedValues = DTM_FALSE;
76
77         return;
78 }
79
80 void
81 DmxMsg::setHandle (DtMailMessageHandle &h)
82 {
83         msgHandle = h;
84         return;
85 }
86
87 void
88 DmxMsg::setHeader (DtMailHeaderLine &h)
89 {
90         msgHeader = h;
91         return;
92 }
93
94 void
95 DmxMsg::setMessage (DtMail::Message *m)
96 {
97         message = m;
98         return;
99 }
100
101 void
102 DmxMsg::setInfo (char *info)
103 {
104         addlInfo = strdup (info);
105         return;
106 }
107
108 char *
109 DmxMsg::getMessageHeader (DmxHeaders which)
110 {
111         int             i = 0, length = 0;
112         int             buflength = 0;
113         char            *rtn;
114         const char      *str;
115
116         if (which >= DMXNUMHDRS)
117           return (char *) NULL;
118
119         length = msgHeader.header_values[which].length ();
120         if (length == 0)
121             str = errorString(which);
122         else
123             str = *((msgHeader.header_values[which])[0]);
124
125         //need to free this after using it
126         rtn = strdup(str);
127         return (rtn);
128 }
129         
130
131         
132
133 char *
134 DmxMsg::getHeaders (DtMailBoolean abbreviated_only)
135 {
136     DtMailEnv           error;
137     DtMail::Session     *m_session = theRoamApp.session()->session(); 
138     DtMail::MailRc      *mail_rc = m_session->mailRc(error);
139     DtMail::Envelope    *env = message->getEnvelope(error);
140
141     DtMailHeaderHandle  hdr_hnd;
142     char                *name;
143     DtMailValueSeq      value;
144
145     // Code from MsgScrollingList - display_message().
146     // We're trying to reduce heap size by not allocating and 
147     // deleting space in every loop iteration.  So just have a 
148     // fixed size buffer initially.
149     // 
150
151     // Initial line size.  When not enough, allocate more.
152     int                 buffer_size = 2048;   
153     char                *buffer = new char [buffer_size];
154     int                 count = 0;
155     int                 hdr_num = 0;
156     char                *newline = "\n";
157     char                *separator = ": ";
158     int                 val = 0;
159
160     //
161     // Iterate through each header in the message and add it
162     // to the buffer.
163     //
164     for (hdr_hnd = env->getFirstHeader(error, &name, value), *buffer = '\0';
165          hdr_hnd && !error.isSet();
166          hdr_hnd = env->getNextHeader(error, hdr_hnd, &name, value), hdr_num++)
167     {
168         if (abbreviated_only == DTM_TRUE &&
169             (hdr_num != 0 || strcmp(name, "From") != 0))
170         {
171             DtMailEnv ierror;
172             if (mail_rc->ignore(ierror, name))
173             {
174                 free(name);
175                 value.clear();
176                 continue;
177             }
178         }
179             
180         for (val=0;  val<value.length();  val++)
181           count += strlen(name) +
182                    strlen(*(value[val])) +
183                    strlen(separator) +
184                    strlen(newline) + 1;
185
186         if (count > buffer_size)
187         {
188             // Need to increase buffer size.
189             char        *new_buffer;
190             
191             buffer_size *= 2;
192             new_buffer = new char [buffer_size];
193             memset(new_buffer, 0, buffer_size);
194
195             strcpy(new_buffer, buffer);
196             delete [] buffer;
197             buffer = new_buffer;
198         }
199
200         for (val=0;  val<value.length();  val++)
201         {
202             strcat(buffer, name);
203                 
204             if (hdr_num != 0 || strcmp(name, "From") != 0)
205               strcat(buffer, separator);
206             else
207               strcat(buffer, " ");
208                 
209             strcat(buffer, *(value[val]));
210             strcat(buffer, newline);
211         }
212         value.clear();
213         free(name);
214     }
215
216     //
217     // Need to free this after using;
218     //
219     return buffer;
220 }
221         
222
223         
224
225 char *
226 DmxMsg::getPrintedHeaders (DmxPrintHeadersEnum header_format)
227 {
228     char *newline = "\n";
229     char *buffer = NULL;
230
231     switch (header_format)
232     {
233         case DMX_PRINT_HEADERS_NONE:
234             buffer = new char [strlen(newline) + 1];
235             strcpy(buffer, (const char *) newline);
236             break;
237         case DMX_PRINT_HEADERS_STANDARD:
238             buffer = getStandardHeaders(msgHeader);
239             break;
240         case DMX_PRINT_HEADERS_ABBREV:
241             buffer = getHeaders(DTM_TRUE);
242             break;
243         case DMX_PRINT_HEADERS_ALL:
244             buffer = getHeaders(DTM_FALSE);
245             break;
246         default:
247             fprintf (stderr, "error in DmxMsg::display\n");
248             break;
249     }
250
251     //
252     // Need to free this after using;
253     //
254     return buffer;
255 }
256
257
258 void
259 DmxMsg::display (
260                 DmxPrintHeadersEnum header_format,
261                 DmxPrintOutputProc print_proc,
262                 XtPointer stream)
263 {
264     DtMailEnv           env;
265     DtMailBoolean       FirstIsText = DTM_FALSE;
266     DtMail::BodyPart    *firstPart = NULL, *nextpart = NULL;
267     char                *buf = NULL,
268                         *description = NULL,
269                         *name = NULL,
270                         *newline = NULL,
271                         *sunDataDescription = NULL,
272                         *type = NULL;
273
274     void                *contents = NULL;
275     unsigned long       length = 0;
276     int                 mode = 0;
277
278     // For CHARSET
279     char                v3_cs[64],
280                         *mime_cs = NULL,
281                         *from_cs = NULL,
282                         *to_cs = NULL;
283
284         
285     // read in body part info
286     if (cachedValues != DTM_TRUE)
287       parse ();
288
289     firstPart = bodyParts [0];
290
291     firstPart->getContents(env,
292                 (const void **) &contents, 
293                 &length,
294                 NULL,   //type
295                 NULL,   //name
296                 NULL,   //mode
297                 NULL);  //description
298
299     if (handleError(env, "getContents") == DTM_TRUE)
300       exit (1);
301
302     // For CHARSET
303     DtMailValueSeq      value;
304     DtMailBoolean       err = DTM_FALSE;
305
306     // Get the bodypart's charset - Try MIME first then V3
307     firstPart->getHeader(env, DtMailMessageContentType, DTM_TRUE, value);
308     if (env.isNotSet()) {
309         mime_cs = firstPart->csFromContentType(value);
310     } else {
311         env.clear();
312         value.clear();
313         firstPart->getHeader(env, DtMailMessageV3charset, DTM_TRUE, value);
314         if (env.isNotSet()) {
315             strcpy(v3_cs, *(value[0]));
316         } else {
317              err = DTM_TRUE;
318              env.clear();
319              value.clear();
320         }
321     }
322
323     // If cannot obtain bodypart's charset header, then maybe this message
324     // has only one bodypart, then in this case the charset header maybe
325     // among the message's envelope (main message headers).
326     // Get the envelope of the message (in order to access the headers)
327     DtMail::Envelope    *envelope = NULL;
328     if (err == DTM_TRUE) {
329         envelope = message->getEnvelope(env);
330         err = DTM_FALSE;
331 #ifdef DEBUG
332         env.logError(
333                 DTM_FALSE,
334                 "DEBUG dtmailpr: Looking at main message header\n");
335 #endif
336     }
337
338     //   Check for MIME charset header and then for V3 charset header
339     if (envelope != NULL) {
340         envelope->getHeader(env, DtMailMessageContentType, DTM_TRUE, value);
341         if (env.isNotSet()) {
342             mime_cs = firstPart->csFromContentType(value);
343         } else {
344             err = DTM_TRUE;
345             env.clear();
346         }
347         if (mime_cs == NULL || err == DTM_TRUE) {
348             value.clear();
349             envelope->getHeader(env, DtMailMessageV3charset, DTM_TRUE, value);
350             if (env.isNotSet()) {
351                 strcpy(v3_cs, *(value[0]));
352             } else {
353                 err = DTM_TRUE;
354                 env.clear();
355             }
356         }
357     } else {
358 #ifdef DEBUG
359         env.logError(DTM_FALSE, "DEBUG dtmailpr: envelope is null\n");
360 #endif
361         env.clear();
362     }
363
364     // Default codeset in case mime_cs and v3_cs are both null.
365     if ((mime_cs == NULL) && (strlen(v3_cs) == 0)) {
366         char *ret = NULL;
367         firstPart->DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
368                                         setlocale(LC_CTYPE, NULL),
369                                         NULL,
370                                         NULL,
371                                         &ret);
372         strcpy(v3_cs, "DEFAULT");
373         strcat(v3_cs, ".");
374         strcat(v3_cs, ret);
375         if (ret)
376           free(ret);
377     }
378
379     // Get iconv from and to codeset and do conversion.
380     int converted = 0;
381     if (mime_cs) {
382         from_cs = firstPart->csToConvName(mime_cs);
383 #ifdef DEBUG
384         env.logError(DTM_FALSE, "DEBUG dtmailpr: mime_cs = %s\n", mime_cs);
385 #endif
386     } else {
387        from_cs = firstPart->csToConvName(v3_cs);
388 #ifdef DEBUG
389        env.logError(DTM_FALSE, "DEBUG dtmailpr: v3_cs = %s\n", v3_cs);
390 #endif
391     }
392     to_cs = firstPart->locToConvName();
393
394 #ifdef DEBUG
395     if ( from_cs == NULL )
396       env.logError(DTM_FALSE, "DEBUG dtmailpr: from_cs is NULL\n");
397     else
398       env.logError(DTM_FALSE, "DEBUG dtmailpr: from_cs = %s\n", from_cs);
399
400     if ( to_cs == NULL )
401       env.logError(DTM_FALSE, "DEBUG dtmailpr: to_cs is NULL\n");
402     else
403       env.logError(DTM_FALSE, "DEBUG dtmailpr: to_cs = %s\n", to_cs);
404 #endif
405
406     if ( from_cs && to_cs ) {
407         if ( strcasecmp(from_cs, to_cs) != 0 ) {
408             converted = firstPart->csConvert(
409                                         (char **)&contents,
410                                         length,
411                                         0,
412                                         from_cs,
413                                         to_cs);
414 #ifdef DEBUG
415             env.logError(DTM_FALSE,
416                          "DEBUG dtmailpr: converted = %d\n", converted);
417 #endif
418         }
419     }
420     if ( mime_cs )
421       free ( mime_cs );
422     if ( from_cs )
423       free( from_cs );
424     if ( to_cs )
425       free ( to_cs );
426  
427     // End of For CHARSET
428
429     newline = new char [2];
430     newline[0] = '\n';
431     newline[1] = '\0';
432
433     //
434     // Print out the message headers.
435     //
436     buf = getPrintedHeaders(header_format);
437     print_proc(stream, buf);
438     print_proc(stream, newline);
439     delete buf;
440
441     //
442     // Print out the message body.
443     //
444     buf = new char [length + 1];
445     memset (buf, 0, (unsigned int) length + 1);
446     memmove (buf, contents, (unsigned int) length);
447     buf [length] = '\0';        // null-terminate that puppy
448     print_proc(stream, buf);
449     print_proc(stream, newline);
450     delete [] buf;
451
452     // For CHARSET
453     if (converted && contents)
454       free(contents);
455
456     // No attachments?  We're done.
457     if (numBPs < 2)
458         return;
459
460     int         i = 0, attbuflen = 0;
461     char        *attbuf = NULL;
462     char        *sunbuf = NULL;
463
464     print_proc(stream, newline);
465     for (i = 1; i < numBPs ; i++)
466     {
467         nextpart = bodyParts [i];
468
469         if (nextpart == NULL)
470           fprintf (stderr, "Error getting part!\n");
471
472         length = 0;
473         type = "";
474         sunDataDescription = "";
475         description = "";
476         name = "";
477         mode = -1;
478                 
479         nextpart->getContents(env,
480                         NULL,
481                         &length,
482                         &type,
483                         &name,
484                         &mode,
485                         &sunDataDescription);
486         if (handleError (env, "getContents") == DTM_TRUE)
487           exit (1);
488
489         if (type == NULL)
490           type = "(type unknown)";
491
492         if (name == NULL)
493           name = "(name unknown)";
494
495         if (sunDataDescription == NULL)
496         {
497             description = "";
498         } else {
499             // should add bracket or something
500             sunbuf = new char [strlen (sunDataDescription) + 10];
501             sprintf(sunbuf, " (%s)", sunDataDescription);
502             description = sunbuf;
503         }
504
505         attbuflen = strlen(name) + strlen(type) + strlen(description);
506         attbuf = new char [attbuflen + 64];
507         sprintf(attbuf,
508                 "[%d] \"%s\"%s, %s, %ld bytes",
509                 i,
510                 name,
511                 description,
512                 type,
513                 length);
514         print_proc(stream, attbuf);
515         print_proc(stream, newline);
516         delete [] attbuf;
517
518         if (sunbuf != NULL)
519           delete [] sunbuf;
520     }
521
522     return;
523 }
524
525 void
526 DmxMsg::parse (void)
527 {
528         // store the body parts for later reference
529
530         DtMailEnv                       env;
531         DtMailBoolean           FirstIsText = DTM_FALSE;
532         DtMail::BodyPart        *part = NULL, *nextpart = NULL;
533         char                    *type = NULL, *attr = NULL;
534
535         int     bc = message->getBodyCount (env);
536         if (handleError (env, "getBodyCount") == DTM_TRUE)
537                 exit (1);
538
539         part = message->getFirstBodyPart (env);
540         if (handleError (env, "getFirstBodyPart") == DTM_TRUE)
541                 exit (1);
542
543         part->getContents (env, NULL, NULL, &type, NULL, NULL, NULL);
544         if (handleError (env, "getContents") == DTM_TRUE)
545                 exit (1);
546
547         bodyParts = new (DtMail::BodyPart *[bc]);
548         cachedValues = DTM_TRUE;
549         
550         // cache values
551         bodyParts [0] = part;
552         numBPs++;
553
554
555         if (type != NULL)
556         {
557                 attr = DtDtsDataTypeToAttributeValue (type,
558                                                 DtDTS_DA_IS_TEXT,
559                                                 NULL);
560                 if (attr != NULL)
561                 {
562                         FirstIsText = DTM_TRUE;
563                 }
564                 //free (type);  // it's allocating some data for us
565         } else {
566                 FirstIsText = DTM_FALSE;
567         }
568
569         // No attachments?  We're done.
570         if (bc < 2)
571                 return;
572
573         int     i;
574
575         for (i = 1; i < bc; i++)
576         {
577                 nextpart = NULL;
578                 nextpart = message->getNextBodyPart (env,
579                                                 part);
580                 if (handleError (env, "getNextBodyPart") == DTM_TRUE)
581                         exit (1);
582
583                 if (nextpart == NULL)
584                         fprintf (stderr, "Error getting part!\n");
585
586
587                 bodyParts [i] = nextpart;
588                 numBPs++;
589
590                 part = nextpart;
591         }
592 }
593