2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
27 * $TOG: MIMEPartial.C /main/6 1998/04/06 13:27:21 mgreess $
29 * RESTRICTED CONFIDENTIAL INFORMATION:
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
39 * Copyright 1993 Sun Microsystems, Inc. All rights reserved.
43 #include <EUSCompat.h>
49 #include <DtMail/DtMail.hh>
51 #include "str_utils.h"
53 static const char * contentType = "content-type";
54 static const char * partial = "message/partial";
57 // Get the named header out of out of the message.
60 getNamedHeader(DtMailEnv & error,
61 const char * headerName,
65 char * results = NULL;
66 RFCEnvelope * env = (RFCEnvelope *)message->getEnvelope(error);
68 if (error.isNotSet()) {
69 env->getHeader(error, headerName, DTM_FALSE, value);
71 if (error.isNotSet()) {
72 results = strdup(*(value[0]));
80 // Return the value to the right of the '=' in the named string.
84 getNamedValueInt(const char *string, const char *name)
86 int stringLen = strlen(string);
87 int nameLen = strlen(name);
89 register unsigned int offset;
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]);
103 // Return the value to the right of the '=' in the named string.
104 // As a const char *.
107 getNamedValueString(const char *string, const char *name)
109 int stringLen = strlen(string);
110 int nameLen = strlen(name);
111 const char * results;
113 register unsigned int offset;
115 for (offset = 0; offset < stringLen - nameLen; offset++) {
116 if (strncasecmp(&string[offset], name, nameLen) == 0) {
117 if (string[offset + nameLen] == '=') {
120 // We only want what is inside the '"' quotes.
122 results = strdup(&string[offset + nameLen + 1]);
123 if (*results == '"') {
125 stringEnd = const_cast <char *> (strchr(results, '"'));
126 if (stringEnd != NULL) {
139 RFCMailBox::_isPartial(DtMailEnv & error,
140 RFCMessage * message)
142 DtMailBoolean results = DTM_FALSE;
143 RFCMessage * messageArray = NULL;
148 && (type = getNamedHeader(error, contentType, message)) != NULL) {
150 if (error.isNotSet()) {
151 if (strncasecmp(type, partial, 15) == 0) {
154 // Add ourselves to the array.
156 _partialData * newData = new _partialData;
158 newData->id = getNamedValueString(type, "id");
159 newData->number = getNamedValueInt(type, "number");
160 newData->total = getNamedValueInt(type, "total");
161 newData->msg = message;
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.
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;
178 delete newData; // Not a valid message/partial.
191 RFCMailBox::_assemblePartial(DtMailEnv & error,
192 RFCMessage * message)
194 register unsigned int offset;
195 unsigned int totalParts = 0;
197 RFCMessage * msg = message;
199 _partialData * data = NULL;
201 if (message != NULL) {
202 char * type = getNamedHeader(error, contentType, message);
205 // Find ourselfs in the list.
207 for (offset = 0 ; offset < _partialListCount ; offset++) {
208 if (_partialList[offset]->msg == message) {
209 data = _partialList[offset];
215 // Now look for the total for this message-id.
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) {
229 // If we know how many parts we are to process - start looking.
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
235 if (totalParts > 0) {
237 char * dummy = (char *) calloc(1, totalParts * sizeof(RFCMessage *));
238 RFCMessage ** messages = (RFCMessage **)dummy;
241 // Now we go thru the list again, this time looking for
242 // all of the parts. Place them in the correct order
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;
253 // If all of the parts are IN, then assemble them into
256 int totalSize = 0; // Total size of all of the parts.
259 for (offset = 0 ; offset < totalParts ; offset++) {
260 if (messages[offset] == NULL) {
261 break; // Found a missing part, do not assemble.
263 messageLength = getNamedHeader(error, "Content-Length",
265 if (messageLength == NULL || error.isSet()) {
266 break; // No way to assemble if we do not know the size.
268 totalSize += atoi(messageLength); // Bump the total size.
271 if (offset == totalParts) { // Did we find all of the parts ?
274 // From RFC 1521, section 7.3.2:
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:
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.
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.
293 // (3) All of the header fields from the second and any subsequent
294 // messages will be ignored.
297 // Pick 10% more space for the headers, or a random size for
300 totalSize = (totalSize > 10*1024) ? (int) (totalSize*1.1) : (20*1024);
302 char * newMessage = (char *)malloc(totalSize);
304 newMessage[0] = '\0';
307 // Copy over all of the headers from the outer 1st message
308 // except the ones that we are not suppost to copy.
312 = (RFCEnvelope *)messages[0]->getEnvelope(error);
314 DtMailHeaderHandle headerHandle;
315 DtMailValueSeq headerValue;
317 const char * unixFromLine = NULL;
319 unsigned int duplicateCount;
321 int headerNumber = 0;
322 register unsigned int dupOffset;
324 if (error.isNotSet()) {
326 // Get the first header.
328 headerHandle = env->getFirstHeader(error, &headerName, headerValue);
330 unixFromLine = env->unixFrom(error, fromLen);
331 if (unixFromLine != NULL) {
332 strncat(newMessage, unixFromLine, fromLen);
333 newMessage[fromLen] = '\0';
335 if (error.isNotSet()) {
338 // Keep geting more (all) of the headers.
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)) {
349 // If there is more than one value for each type,
350 // then get all of the values.
352 duplicateCount = headerValue.length();
353 for (dupOffset=0; dupOffset < duplicateCount; dupOffset++ ) {
356 // Skip if the first line is a from.
358 if (unixFromLine != NULL && headerNumber == 0) {
361 strcat(newMessage, headerName);
362 strcat(newMessage, ": ");
364 strcat(newMessage, *(headerValue[dupOffset]));
365 strcat(newMessage, "\n");
370 // Clear out the current one, so that getNextHeader
375 headerHandle = env->getNextHeader(error,
382 // getNextHeader returns NULL where there are no more.
384 } while(error.isNotSet() && headerHandle != NULL);
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.
394 // We then delete the envelope as it is not longer needed.
397 = (RFCBodyPart *)messages[0]->getFirstBodyPart(error);
399 unsigned long length;
403 const void * contents;
404 const char * embHeader1St;
408 // Get the body part of the first message. It contains
409 // the headers that we need to merge with.
411 if (error.isNotSet()) {
412 bp->getContents(error, &contents, &length,
413 NULL, NULL, NULL, NULL);
415 if (error.isNotSet()) {
417 // Turn the 1st messages body into an envelope.
419 // Headers end with two new lines.
421 embHeader1StLen = (int) length;
422 embHeader1St = (const char *)contents;
423 endHeader = const_cast <char *> (strstr((const char *)contents, "\n\n"));
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));
432 if (error.isNotSet()) {
435 // Get the first header from the embeded message.
437 headerHandle = embEnv.getFirstHeader(error,
441 if (error.isNotSet()) {
444 // Keep geting more (all) of the headers.
447 if ((strncasecmp(headerName, "Content-", 8) == 0)
448 || (strcasecmp(headerName, "Message-ID") == 0)
449 || (strcasecmp(headerName, "Encrypted") == 0)
450 || (strcasecmp(headerName, "MIME-Version") == 0)) {
453 // If there is more than one value for each type,
454 // then get all of the values.
456 duplicateCount = headerValue.length();
458 ; dupOffset < duplicateCount
461 strcat(newMessage, headerName);
462 strcat(newMessage, ": ");
463 strcat(newMessage, *(headerValue[dupOffset]));
464 strcat(newMessage, "\n");
469 // Clear out the current one, so that getNextHeader
473 headerHandle = embEnv.getNextHeader(error,
478 // getNextHeader returns NULL where there are no more.
480 } while(error.isNotSet() && headerHandle != NULL);
483 strcat(newMessage, "\n"); // End of headers.
488 if (error.isNotSet()) {
490 // Now add the data parts into the one part.
492 size_t messageSize = strlen(newMessage);
494 length = embHeader1StLen - (&endHeader[2] - embHeader1St);
497 (const char *)&endHeader[2], // After \n\n.
500 messageSize += (size_t) length;
501 newMessage[messageSize] = '\0';
504 // Add in the 2nd -> Nth body parts.
506 for (offset = 1; offset < totalParts ; offset++) {
507 bp =(RFCBodyPart *)messages[offset]->getFirstBodyPart(error);
512 bp->getContents(error, &contents, &length,
513 NULL, NULL, NULL, NULL);
518 strncat(newMessage, (const char *)contents, (size_t) length);
519 messageSize += (size_t) length;
520 newMessage[messageSize] = '\0';
523 // Make the new Message, we strdup() so we only use
524 // as much space as needed (and free() old).
526 const char *msgResults = strdup(newMessage);
528 newMessage = (char *)msgResults;
530 msg = new RFCMessage(error, this,
531 (const char **)&msgResults,
532 (const char *)newMessage + messageSize);
534 for (offset = 0; offset < totalParts ; offset++) {
536 // Delete the old parts.
538 messages[offset]->setFlag(error, DtMailMessageDeletePending);
541 // TODO - CALL EXPUNGE to get rid of the message that
542 // we just marked for delete !!!!!
545 messages[offset] = NULL;
552 if (messages != NULL) {
558 return(msg); // This could be the new or the original message.