dthelp: Change to ANSI function definitions
[oweals/cde.git] / cde / programs / dtmail / libDtMail / RFC / MIMEPartial.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: MIMEPartial.C /main/6 1998/04/06 13:27:21 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 #include <EUSCompat.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <ctype.h>
48
49 #include <DtMail/DtMail.hh>
50 #include "RFCImpl.hh"
51 #include "str_utils.h"
52
53 static const char       * contentType = "content-type";
54 static const char       * partial = "message/partial";
55
56 //
57 // Get the named header out of out of the message.
58 //
59 static char     *
60 getNamedHeader(DtMailEnv                & error,
61                const char       * headerName,
62                RFCMessage       * message)
63 {
64   DtMailValueSeq          value;
65   char                  * results = NULL;
66   RFCEnvelope           * env = (RFCEnvelope *)message->getEnvelope(error);
67
68   if (error.isNotSet()) {
69     env->getHeader(error, headerName, DTM_FALSE, value);
70
71     if (error.isNotSet()) {
72       results = strdup(*(value[0]));
73     }
74   }
75
76   return(results);
77 }
78
79 //
80 // Return the value to the right of the '=' in the named string.
81 // As an INT.
82 //
83 static unsigned int
84 getNamedValueInt(const char *string, const char *name)
85 {
86   int                   stringLen = strlen(string);
87   int                   nameLen = strlen(name);
88   int                   results = 0;
89   register unsigned int offset;
90
91   for (offset = 0; offset < stringLen - nameLen; offset++) {
92     if (strncasecmp(&string[offset], name, nameLen) == 0) {
93       if (string[offset + nameLen] == '=') {
94         results = atoi(&string[offset + nameLen + 1]);
95         break;
96       }
97     }
98   }
99   return(results);
100 }
101
102 //
103 // Return the value to the right of the '=' in the named string.
104 // As a const char *.
105 //
106 static const char       *
107 getNamedValueString(const char *string, const char *name)
108 {
109   int                     stringLen = strlen(string);
110   int                     nameLen = strlen(name);
111   const char            * results;
112   char                  * stringEnd;
113   register unsigned int   offset;
114
115   for (offset = 0; offset < stringLen - nameLen; offset++) {
116     if (strncasecmp(&string[offset], name, nameLen) == 0) {
117       if (string[offset + nameLen] == '=') {
118
119         //
120         // We only want what is inside the '"' quotes.
121         //
122         results = strdup(&string[offset + nameLen + 1]);
123         if (*results == '"') {
124           results++;
125           stringEnd = const_cast <char *> (strchr(results, '"'));
126           if (stringEnd != NULL) {
127             *stringEnd = '\0';
128           }
129         }
130
131         break;
132       }
133     }
134   }
135   return(results);
136 }
137
138 DtMailBoolean
139 RFCMailBox::_isPartial(DtMailEnv        & error,
140                        RFCMessage       * message)
141 {
142   DtMailBoolean           results = DTM_FALSE;
143   RFCMessage            * messageArray = NULL;
144
145   char          * type;
146
147   if (message != NULL
148       && (type = getNamedHeader(error, contentType, message)) != NULL) {
149     
150     if (error.isNotSet()) {
151       if (strncasecmp(type, partial, 15) == 0) {
152
153         //
154         // Add ourselves to the array.
155         //
156         _partialData    * newData = new _partialData;
157
158         newData->id = getNamedValueString(type, "id");
159         newData->number = getNamedValueInt(type, "number");
160         newData->total =  getNamedValueInt(type, "total");
161         newData->msg = message;
162         //
163         // It has to have a number and an ID or it is not a valid
164         // message/partial and there would be no way to put it
165         // back together. Total is optional except for the last part
166         // where it is required.
167         //
168         if (newData->number > 0 && newData->id != NULL) {
169           _partialList = (_partialData **)realloc(_partialList,
170                                                   sizeof(_partialData *)
171                                                    *(_partialListCount+1));
172           if (_partialList != NULL) {
173             _partialList[_partialListCount] = newData;
174             _partialListCount++;
175             results = DTM_TRUE;
176           }
177         } else {
178           delete newData;       // Not a valid message/partial.
179         }
180       }
181       free(type);
182     }
183     else {
184         error.clear();
185     }
186   }
187   return(results);
188 }
189
190 RFCMessage      *
191 RFCMailBox::_assemblePartial(DtMailEnv  & error,
192                              RFCMessage * message)
193 {
194   register unsigned int   offset;
195   unsigned int            totalParts = 0;
196
197   RFCMessage            * msg = message;
198
199   _partialData          * data = NULL;
200
201   if (message != NULL) {
202     char                * type = getNamedHeader(error, contentType, message);
203
204     //
205     // Find ourselfs in the list.
206     //
207     for (offset = 0 ; offset < _partialListCount ; offset++) {
208       if (_partialList[offset]->msg == message) {
209         data = _partialList[offset];
210         break;
211       }
212     }
213
214     //
215     // Now look for the total for this message-id.
216     //
217     if (data != NULL) {
218       for (offset = 0 ; offset < _partialListCount ; offset++) {
219         if (strcasecmp(_partialList[offset]->id, data->id) == 0) {
220           totalParts =  _partialList[offset]->total;
221           if (totalParts > 0) {
222             break;
223           }
224         }
225       }
226     }
227
228     //
229     // If we know how many parts we are to process - start looking.
230     //
231     // If we do not know the total, then we know that not all of the
232     // parts have arrived. (the last partial message must have
233     // the total in it).
234     //
235     if (totalParts > 0) {
236
237       char              * dummy = (char *) calloc(1, totalParts * sizeof(RFCMessage *));
238       RFCMessage        ** messages = (RFCMessage **)dummy;
239
240       //
241       // Now we go thru the list again, this time looking for
242       // all of the parts. Place them in the correct order
243       // in the list.
244       //
245       for (offset = 0 ; offset < _partialListCount ; offset++) {
246         if (strcasecmp(_partialList[offset]->id, data->id) == 0) {
247           messages[_partialList[offset]->number - 1]
248             = _partialList[offset]->msg;
249         }
250       }
251
252       //
253       // If all of the parts are IN, then assemble them into
254       // a new message.
255       //
256       int               totalSize = 0;  // Total size of all of the parts.
257       char              *messageLength;
258
259       for (offset = 0 ; offset < totalParts ; offset++) {
260         if (messages[offset] == NULL) {
261           break;        // Found a missing part, do not assemble.
262         }
263         messageLength = getNamedHeader(error, "Content-Length",
264                                        messages[offset]);
265         if (messageLength == NULL || error.isSet()) {
266           break;        // No way to assemble if we do not know the size.
267         }
268         totalSize += atoi(messageLength);       // Bump the total size.
269         free(messageLength);
270       }
271       if (offset == totalParts) {       // Did we find all of the parts ?
272
273         //
274         // From RFC 1521, section 7.3.2:
275         //
276         // When generating and reassembling the parts of a message/partial
277         // message, the headers of the encapsulated message must be merged with
278         // the headers of the enclosing entities.  In this process the
279         // following rules must be observed:
280         //
281         // (1) All of the header fields from the initial enclosing entity
282         // (part one), except those that start with "Content-" and the
283         // specific header fields "Message-ID", "Encrypted", and
284         // "MIME-Version", must be copied, in order, to the new message.
285         //
286         // (2) Only those header fields in the enclosed message which start
287         // with "Content-" and "Message-ID", "Encrypted", and "MIME-Version"
288         // must be appended, in order, to the header fields of the new
289         // message.  Any header fields in the enclosed message which do not
290         // start with "Content-" (except for "Message-ID", "Encrypted", and
291         // "MIME-Version") will be ignored.
292         //
293         // (3) All of the header fields from the second and any subsequent
294         // messages will be ignored.
295
296         //
297         // Pick 10% more space for the headers, or a random size for
298         // the new message.
299         //
300         totalSize = (totalSize > 10*1024) ? (int) (totalSize*1.1) : (20*1024);
301
302         char            * newMessage = (char *)malloc(totalSize);
303
304         newMessage[0] = '\0';
305
306         //
307         // Copy over all of the headers from the outer 1st message
308         // except the ones that we are not suppost to copy.
309         //
310
311         RFCEnvelope             * env
312           = (RFCEnvelope *)messages[0]->getEnvelope(error);
313
314         DtMailHeaderHandle        headerHandle;
315         DtMailValueSeq            headerValue;
316         char                    * headerName;
317         const char              * unixFromLine = NULL;
318
319         unsigned int              duplicateCount;
320         int                       fromLen;
321         int                       headerNumber = 0;
322         register unsigned int     dupOffset;
323
324         if (error.isNotSet()) {
325           //
326           // Get the first header.
327           //
328           headerHandle = env->getFirstHeader(error, &headerName, headerValue);
329
330           unixFromLine = env->unixFrom(error, fromLen);
331           if (unixFromLine != NULL) {
332             strncat(newMessage, unixFromLine, fromLen);
333             newMessage[fromLen] = '\0';
334           }
335           if (error.isNotSet()) {
336
337             //
338             // Keep geting more (all) of the headers.
339             //
340             do {
341               if ((strncasecmp(headerName, "Content-", 8) != 0)
342                   && (strcasecmp(headerName, "Message-ID") != 0)
343                   && (strcasecmp(headerName, "Encrypted") != 0)
344                   && (strcasecmp(headerName, "MIME-Version") != 0)
345                   && (strcasecmp(headerName, "Status") != 0)) {
346                     
347
348                 //
349                 // If there is more than one value for each type,
350                 // then get all of the values.
351                 //
352                 duplicateCount = headerValue.length();
353                 for (dupOffset=0; dupOffset < duplicateCount; dupOffset++ ) {
354
355                   //
356                   // Skip if the first line is a from.
357                   //
358                   if (unixFromLine != NULL && headerNumber == 0) {
359                     continue;
360                   }
361                   strcat(newMessage, headerName);
362                   strcat(newMessage, ": ");
363                   
364                   strcat(newMessage, *(headerValue[dupOffset]));
365                   strcat(newMessage, "\n");
366                 }
367               }
368
369               //
370               // Clear out the current one, so that getNextHeader
371               // does not append.
372               //
373               headerValue.clear();
374
375               headerHandle = env->getNextHeader(error,
376                                                 headerHandle,
377                                                 &headerName,
378                                                 headerValue);
379
380               headerNumber++;
381               //
382               // getNextHeader returns NULL where there are no more.
383               //
384             } while(error.isNotSet() && headerHandle != NULL);
385
386             //
387             headerValue.clear();
388
389             //
390             // Now add in the headers from the imbeded message in part 1.
391             // To do this we need to convert the message to an envelope
392             // and then get the headers from that.
393             //
394             // We then delete the envelope as it is not longer needed.
395             //
396             RFCBodyPart         * bp
397               = (RFCBodyPart *)messages[0]->getFirstBodyPart(error);
398
399             unsigned long         length;
400
401             int                   embHeader1StLen;
402
403             const void          * contents;
404             const char          * embHeader1St;
405             char                * endHeader;
406
407             //
408             // Get the body part of the first message. It contains
409             // the headers that we need to merge with.
410             //
411             if (error.isNotSet()) {
412               bp->getContents(error, &contents, &length,
413                               NULL, NULL, NULL, NULL);
414
415               if (error.isNotSet()) {
416                 //
417                 // Turn the 1st messages body into an envelope.
418                 //
419                 // Headers end with two new lines.
420                 //
421                 embHeader1StLen = (int) length;
422                 embHeader1St = (const char *)contents;
423                 endHeader = const_cast <char *> (strstr((const char *)contents, "\n\n"));
424
425                 if (endHeader != NULL) {
426                   RFCEnvelope   embEnv(error,
427                                        (DtMail::Message *)NULL,
428                                        (const char *)contents,
429                                        (int)((unsigned long)endHeader
430                                              - (unsigned long)contents));
431
432                   if (error.isNotSet()) {
433                     
434                     //
435                     // Get the first header from the embeded message.
436                     //
437                     headerHandle = embEnv.getFirstHeader(error,
438                                                          &headerName,
439                                                          headerValue);
440
441                     if (error.isNotSet()) {
442
443                       //
444                       // Keep geting more (all) of the headers.
445                       //
446                       do {
447                         if ((strncasecmp(headerName, "Content-", 8) == 0)
448                             || (strcasecmp(headerName, "Message-ID") == 0)
449                             || (strcasecmp(headerName, "Encrypted") == 0)
450                             || (strcasecmp(headerName, "MIME-Version") == 0)) {
451                           
452                           //
453                           // If there is more than one value for each type,
454                           // then get all of the values.
455                           //
456                           duplicateCount = headerValue.length();
457                           for (dupOffset=0
458                                ; dupOffset < duplicateCount
459                                ; dupOffset++ ) {
460
461                             strcat(newMessage, headerName);
462                             strcat(newMessage, ": ");
463                             strcat(newMessage, *(headerValue[dupOffset]));
464                             strcat(newMessage, "\n");
465                           }
466                         }
467
468                         //
469                         // Clear out the current one, so that getNextHeader
470                         // does not append.
471                         //
472                         headerValue.clear();
473                         headerHandle = embEnv.getNextHeader(error,
474                                                             headerHandle,
475                                                             &headerName,
476                                                             headerValue);
477                         //
478                         // getNextHeader returns NULL where there are no more.
479                         //
480                       } while(error.isNotSet() && headerHandle != NULL);
481                       //
482                       headerValue.clear();
483                       strcat(newMessage, "\n"); // End of headers.
484                     }
485                   }
486                 }
487               }
488               if (error.isNotSet()) {
489                 //
490                 // Now add the data parts into the one part.
491                 //
492                 size_t          messageSize = strlen(newMessage);
493
494                 length = embHeader1StLen - (&endHeader[2] - embHeader1St);
495
496                 strncat(newMessage,
497                         (const char *)&endHeader[2],    // After \n\n.
498                         (size_t) length);
499
500                 messageSize += (size_t) length;
501                 newMessage[messageSize] = '\0';
502
503                 //
504                 // Add in the 2nd -> Nth body parts.
505                 //
506                 for (offset = 1; offset < totalParts ; offset++) {
507                   bp =(RFCBodyPart *)messages[offset]->getFirstBodyPart(error);
508                   if (error.isSet()) {
509                     break;
510                   }
511
512                   bp->getContents(error, &contents, &length,
513                                   NULL, NULL, NULL, NULL);
514                   if (error.isSet()) {
515                     break;
516                   }
517
518                   strncat(newMessage, (const char *)contents, (size_t) length);
519                   messageSize += (size_t) length;
520                   newMessage[messageSize] = '\0';
521                 }
522                 //
523                 // Make the new Message, we strdup() so we only use
524                 // as much space as needed (and free() old).
525                 //
526                 const char      *msgResults = strdup(newMessage);
527                 free(newMessage);
528                 newMessage = (char *)msgResults;
529
530                 msg = new RFCMessage(error, this,
531                                      (const char **)&msgResults,
532                                      (const char *)newMessage + messageSize);
533
534                 for (offset = 0; offset < totalParts ; offset++) {
535                   //
536                   // Delete the old parts.
537                   //
538                   messages[offset]->setFlag(error, DtMailMessageDeletePending);
539
540                   //
541                   // TODO - CALL EXPUNGE to get rid of the message that
542                   // we just marked for delete !!!!!
543                   //
544
545                   messages[offset] = NULL;
546                 }
547               }
548             }
549           }
550         }
551       }
552       if (messages != NULL) {
553         free(messages);
554       }
555     }
556     free(type);
557   }
558   return(msg);  // This could be the new or the original message.
559 }
560