sl@0
|
1 |
// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
|
sl@0
|
2 |
// All rights reserved.
|
sl@0
|
3 |
// This component and the accompanying materials are made available
|
sl@0
|
4 |
// under the terms of "Eclipse Public License v1.0"
|
sl@0
|
5 |
// which accompanies this distribution, and is available
|
sl@0
|
6 |
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
sl@0
|
7 |
//
|
sl@0
|
8 |
// Initial Contributors:
|
sl@0
|
9 |
// Nokia Corporation - initial contribution.
|
sl@0
|
10 |
//
|
sl@0
|
11 |
// Contributors:
|
sl@0
|
12 |
//
|
sl@0
|
13 |
// Description:
|
sl@0
|
14 |
//
|
sl@0
|
15 |
|
sl@0
|
16 |
// INCLUDE FILES
|
sl@0
|
17 |
#include <e32def.h> // First to avoid NULL redefine warning (no #ifndef NULL).
|
sl@0
|
18 |
#include <e32std.h>
|
sl@0
|
19 |
#include <string.h>
|
sl@0
|
20 |
#include <ezdecompressor.h> // Ezlib.lib, GZip decoders
|
sl@0
|
21 |
#include <uri8.h>
|
sl@0
|
22 |
#include <uri16.h>
|
sl@0
|
23 |
#include <uricommon.h>
|
sl@0
|
24 |
|
sl@0
|
25 |
#include <bafl/multipartparser.h>
|
sl@0
|
26 |
#include "gzipbufmgr.h"
|
sl@0
|
27 |
#include "tinternetdate.h"
|
sl@0
|
28 |
|
sl@0
|
29 |
#include <bafl/qpcodec.h>
|
sl@0
|
30 |
#include <bsul/clientmessage.h>
|
sl@0
|
31 |
|
sl@0
|
32 |
// CONSTANTS
|
sl@0
|
33 |
const char Multipart_Mixed[] = {"multipart/mixed"};
|
sl@0
|
34 |
const char Multipart_Related[] = {"multipart/related"};
|
sl@0
|
35 |
const char Multipart_Boundary_Text[] = {"boundary="};
|
sl@0
|
36 |
const char Multipart_Content_Base_Text[] = {"Content-Base:"};
|
sl@0
|
37 |
const char Multipart_Content_Location_Text[] = {"Content-Location:"};
|
sl@0
|
38 |
const char Multipart_Content_Type_Text[] = {"Content-Type:"};
|
sl@0
|
39 |
const char Multipart_Content_Transfer_Encoding_Text[] = {"Content-Transfer-Encoding:"};
|
sl@0
|
40 |
// violate RFC2045; but upon customer request
|
sl@0
|
41 |
const char Multipart_Content_Encoding_Text[] = {"Content-Encoding:"};
|
sl@0
|
42 |
const char Multipart_Content_ID_Text[] = {"Content-ID:"};
|
sl@0
|
43 |
const char Multipart_Hypens_Text[] = {"--"};
|
sl@0
|
44 |
const char Multipart_CRLF_Text[] = {"\r\n"};
|
sl@0
|
45 |
const char Multipart_LF_Text[] = {"\n"};
|
sl@0
|
46 |
const char Multipart_DoubleCRLF_Text[] = {"\r\n\r\n"};
|
sl@0
|
47 |
const char Multipart_DoubleLF_Text[] = {"\n\n"};
|
sl@0
|
48 |
const char Multipart_Charset_Text[] = {"charset="};
|
sl@0
|
49 |
const char Multipart_ContentTypeString_Delimiter_Text[] = {";"};
|
sl@0
|
50 |
const char Multipart_ContentTypeString_Quotes_Text[] = {"\""};
|
sl@0
|
51 |
const char Multipart_Content_Transfer_Encoding_Base64[] = {"base64"};
|
sl@0
|
52 |
const char Multipart_Content_Transfer_Encoding_QuotedPrintable[] = {"quoted-printable"};
|
sl@0
|
53 |
const char Multipart_Content_Transfer_Encoding_7bit[] = {"7bit"};
|
sl@0
|
54 |
const char Multipart_Content_Transfer_Encoding_8bit[] = {"8bit"};
|
sl@0
|
55 |
const char Multipart_Content_Transfer_Encoding_binary[] = {"binary"};
|
sl@0
|
56 |
const char Multipart_Content_Encoding_GZip[] = {"gzip"};
|
sl@0
|
57 |
const char Multipart_Content_Type_GZip[] = {"application/x-gzip"};
|
sl@0
|
58 |
|
sl@0
|
59 |
// MACROS
|
sl@0
|
60 |
#define MULTIPART_CONTENT_BASE_LENGTH 13
|
sl@0
|
61 |
#define MULTIPART_CONTENT_LOCATION_LENGTH 17
|
sl@0
|
62 |
#define MULTIPART_CONTENT_TYPE_LENGTH 13
|
sl@0
|
63 |
#define MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH 26
|
sl@0
|
64 |
// violates RFC2045; but upon vodafone request
|
sl@0
|
65 |
#define MULTIPART_CONTENT_ENCODING_LENGTH 17
|
sl@0
|
66 |
#define MULTIPART_CONTENT_LENGTH_LENGTH 15
|
sl@0
|
67 |
#define MULTIPART_LAST_MODIFIED_LENGTH 14
|
sl@0
|
68 |
#define MULTIPART_CONTENT_ID_LENGTH 11
|
sl@0
|
69 |
|
sl@0
|
70 |
#define MULTIPART_CONTENT_BASE 1
|
sl@0
|
71 |
#define MULTIPART_CONTENT_LOCATION 2
|
sl@0
|
72 |
#define MULTIPART_CONTENT_TRANSFER_ENCODING 3
|
sl@0
|
73 |
#define MULTIPART_CONTENT_TYPE 4
|
sl@0
|
74 |
#define MULTIPART_CONTENT_ID 5
|
sl@0
|
75 |
|
sl@0
|
76 |
#define MULTIPART_INTERNET_DATE_STRING_LENGTH 29
|
sl@0
|
77 |
|
sl@0
|
78 |
#define SLASH_CHAR '/'
|
sl@0
|
79 |
#define DOT_CHAR '.'
|
sl@0
|
80 |
#define AT_CHAR '@'
|
sl@0
|
81 |
#define COLON_CHAR ':'
|
sl@0
|
82 |
// <scheme>://
|
sl@0
|
83 |
#define SCHEME_SEPARATOR_LENGTH 3
|
sl@0
|
84 |
|
sl@0
|
85 |
|
sl@0
|
86 |
// ============================= LOCAL FUNCTIONS ===============================
|
sl@0
|
87 |
|
sl@0
|
88 |
|
sl@0
|
89 |
// ============================ MEMBER FUNCTIONS ===============================
|
sl@0
|
90 |
|
sl@0
|
91 |
|
sl@0
|
92 |
// -------------------------------------------------------------------------
|
sl@0
|
93 |
// Parse and put each body part to the body part array
|
sl@0
|
94 |
// -------------------------------------------------------------------------
|
sl@0
|
95 |
EXPORT_C void MultipartParser::ParseL( const TDesC8& aMultipartBody,
|
sl@0
|
96 |
const TDesC8& aContentType,
|
sl@0
|
97 |
const TDesC8& aBoundary,
|
sl@0
|
98 |
const TDesC16& aUrl,
|
sl@0
|
99 |
RPointerArray <CBodyPart>& aBodyPartsArray,
|
sl@0
|
100 |
TInt aMaxToParse )
|
sl@0
|
101 |
{
|
sl@0
|
102 |
// check on required parameters
|
sl@0
|
103 |
__ASSERT_ALWAYS( aMultipartBody.Ptr() != NULL,
|
sl@0
|
104 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
105 |
__ASSERT_ALWAYS( aContentType.Ptr() != NULL,
|
sl@0
|
106 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
107 |
__ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
|
sl@0
|
108 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
109 |
__ASSERT_ALWAYS( aUrl.Ptr() != NULL,
|
sl@0
|
110 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
111 |
|
sl@0
|
112 |
const TUint8* multipartBuffer = aMultipartBody.Ptr();
|
sl@0
|
113 |
TUint32 multipartLen = aMultipartBody.Length();
|
sl@0
|
114 |
__ASSERT_ALWAYS( multipartLen != 0,
|
sl@0
|
115 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
116 |
TUint8* bodyPartBuffer = NULL;
|
sl@0
|
117 |
TUint32 bodyPartBufferLength = 0;
|
sl@0
|
118 |
TUint32 startPosition = 0;
|
sl@0
|
119 |
char* singleEolChar = NULL;
|
sl@0
|
120 |
char* doubleEolChar = NULL;
|
sl@0
|
121 |
CBodyPart* bodyPart = NULL;
|
sl@0
|
122 |
|
sl@0
|
123 |
// currently only support mixed and related.
|
sl@0
|
124 |
char* contentType = (char*)aContentType.Ptr();
|
sl@0
|
125 |
if( strncasecmp( contentType, Multipart_Mixed, strlen(Multipart_Mixed) ) != 0 &&
|
sl@0
|
126 |
strncasecmp( contentType, Multipart_Related, strlen(Multipart_Related) ) != 0 )
|
sl@0
|
127 |
{
|
sl@0
|
128 |
User::Leave( KErrNotSupported );
|
sl@0
|
129 |
}
|
sl@0
|
130 |
|
sl@0
|
131 |
// get singleEol and doubleEol
|
sl@0
|
132 |
MultipartParser::SetEolCharacters( multipartBuffer,
|
sl@0
|
133 |
multipartLen,
|
sl@0
|
134 |
aBoundary,
|
sl@0
|
135 |
&singleEolChar,
|
sl@0
|
136 |
&doubleEolChar );
|
sl@0
|
137 |
|
sl@0
|
138 |
// get body parts one by one
|
sl@0
|
139 |
// null bodyPartBuffer indicates the end of the multipart body
|
sl@0
|
140 |
int counter = 0;
|
sl@0
|
141 |
do
|
sl@0
|
142 |
{
|
sl@0
|
143 |
// stop when we get required number of parse done
|
sl@0
|
144 |
if( aMaxToParse != -1 && counter >= aMaxToParse )
|
sl@0
|
145 |
{
|
sl@0
|
146 |
break;
|
sl@0
|
147 |
}
|
sl@0
|
148 |
// update counter
|
sl@0
|
149 |
counter++;
|
sl@0
|
150 |
|
sl@0
|
151 |
// get the next body part
|
sl@0
|
152 |
bodyPartBufferLength = MultipartParser::GetNextBodyPartBuffer( startPosition,
|
sl@0
|
153 |
multipartBuffer,
|
sl@0
|
154 |
multipartLen,
|
sl@0
|
155 |
aBoundary,
|
sl@0
|
156 |
singleEolChar,
|
sl@0
|
157 |
&bodyPartBuffer );
|
sl@0
|
158 |
// break if we are at end
|
sl@0
|
159 |
if( bodyPartBuffer == NULL )
|
sl@0
|
160 |
{
|
sl@0
|
161 |
break;
|
sl@0
|
162 |
}
|
sl@0
|
163 |
// update start position
|
sl@0
|
164 |
startPosition += bodyPartBufferLength;
|
sl@0
|
165 |
|
sl@0
|
166 |
// create new body part
|
sl@0
|
167 |
bodyPart = CBodyPart::NewL();
|
sl@0
|
168 |
// parse each body part buffer to fill in body part
|
sl@0
|
169 |
MultipartParser::ParseBodyPartL( bodyPartBuffer,
|
sl@0
|
170 |
bodyPartBufferLength,
|
sl@0
|
171 |
singleEolChar,
|
sl@0
|
172 |
doubleEolChar,
|
sl@0
|
173 |
aUrl,
|
sl@0
|
174 |
bodyPart );
|
sl@0
|
175 |
// add the body part to the array
|
sl@0
|
176 |
aBodyPartsArray.Append( bodyPart );
|
sl@0
|
177 |
}
|
sl@0
|
178 |
while( bodyPartBuffer != NULL );
|
sl@0
|
179 |
}
|
sl@0
|
180 |
|
sl@0
|
181 |
// -------------------------------------------------------------------------
|
sl@0
|
182 |
// Composes RFC1521 compliant multipart document with given bodyparts
|
sl@0
|
183 |
// Actual task of creating the document is delegated to specialized composer
|
sl@0
|
184 |
// for each of the subtypes
|
sl@0
|
185 |
// -------------------------------------------------------------------------
|
sl@0
|
186 |
EXPORT_C HBufC8* MultipartParser::ComposeL( RPointerArray<CBodyPart>& aBodyPartsArray,
|
sl@0
|
187 |
const TDesC8& aBoundary,
|
sl@0
|
188 |
TMultipartSubtype aSubtype,
|
sl@0
|
189 |
TInt aHeaderMask )
|
sl@0
|
190 |
{
|
sl@0
|
191 |
// check on required parameters
|
sl@0
|
192 |
if ( !aBoundary.Ptr() || !aBoundary.Length() )
|
sl@0
|
193 |
{
|
sl@0
|
194 |
User::Leave( KErrArgument );
|
sl@0
|
195 |
}
|
sl@0
|
196 |
|
sl@0
|
197 |
HBufC8* multipartDoc = NULL;
|
sl@0
|
198 |
switch(aSubtype)
|
sl@0
|
199 |
{
|
sl@0
|
200 |
case EMultipartSubtypeMixed:
|
sl@0
|
201 |
{
|
sl@0
|
202 |
multipartDoc = ComposeMixedL( aBodyPartsArray,
|
sl@0
|
203 |
aBoundary,
|
sl@0
|
204 |
aHeaderMask );
|
sl@0
|
205 |
}
|
sl@0
|
206 |
break;
|
sl@0
|
207 |
default:
|
sl@0
|
208 |
{
|
sl@0
|
209 |
User::Leave( KErrArgument );
|
sl@0
|
210 |
}
|
sl@0
|
211 |
}
|
sl@0
|
212 |
return multipartDoc;
|
sl@0
|
213 |
}
|
sl@0
|
214 |
|
sl@0
|
215 |
// -------------------------------------------------------------------------
|
sl@0
|
216 |
// Default constructor
|
sl@0
|
217 |
// -------------------------------------------------------------------------
|
sl@0
|
218 |
MultipartParser::MultipartParser()
|
sl@0
|
219 |
{
|
sl@0
|
220 |
}
|
sl@0
|
221 |
|
sl@0
|
222 |
// -------------------------------------------------------------------------
|
sl@0
|
223 |
// Returns with the next body part buffer from start position (offset)
|
sl@0
|
224 |
// -------------------------------------------------------------------------
|
sl@0
|
225 |
TUint32
|
sl@0
|
226 |
MultipartParser::GetNextBodyPartBuffer( TUint32 aStartPosition,
|
sl@0
|
227 |
const TUint8* aMultipartBody,
|
sl@0
|
228 |
TUint32 aMultipartLen,
|
sl@0
|
229 |
const TDesC8& aBoundary,
|
sl@0
|
230 |
char* aSingleEolChar,
|
sl@0
|
231 |
TUint8** aBodyPartBuffer )
|
sl@0
|
232 |
{
|
sl@0
|
233 |
// check on required parameters
|
sl@0
|
234 |
__ASSERT_ALWAYS( aMultipartBody != NULL,
|
sl@0
|
235 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
236 |
__ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
|
sl@0
|
237 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
238 |
__ASSERT_ALWAYS( aSingleEolChar != NULL,
|
sl@0
|
239 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
240 |
|
sl@0
|
241 |
long startOffset = -1;
|
sl@0
|
242 |
long endOffset = -1;
|
sl@0
|
243 |
TUint32 length = 0;
|
sl@0
|
244 |
TBool badContent = EFalse;
|
sl@0
|
245 |
TUint32 offset = 0;
|
sl@0
|
246 |
const char* boundaryStr = (const char*) aBoundary.Ptr();
|
sl@0
|
247 |
int boundaryLength = aBoundary.Length();
|
sl@0
|
248 |
int hypensLength = strlen( Multipart_Hypens_Text );
|
sl@0
|
249 |
int singleEolLength = strlen( aSingleEolChar );
|
sl@0
|
250 |
TUint32 i = aStartPosition;
|
sl@0
|
251 |
|
sl@0
|
252 |
// from RFC 1341 7.2
|
sl@0
|
253 |
// Overall, the body of a multipart entity may be specified as follows:
|
sl@0
|
254 |
// multipart-body := preamble 1*encapsulation close-delimiter epilogue
|
sl@0
|
255 |
// encapsulation := delimiter CRLF body-part
|
sl@0
|
256 |
// delimiter := CRLF "--" boundary ; taken from Content-Type field.
|
sl@0
|
257 |
// when content-type is multipart.
|
sl@0
|
258 |
// There must be no space between "--" and boundary.
|
sl@0
|
259 |
// close-delimiter := delimiter "--" ; Again, no space before
|
sl@0
|
260 |
// "--"
|
sl@0
|
261 |
|
sl@0
|
262 |
// boundary = 12xy
|
sl@0
|
263 |
// body: here comes some text that we ignore
|
sl@0
|
264 |
// --12xy
|
sl@0
|
265 |
// first body
|
sl@0
|
266 |
// --12xy
|
sl@0
|
267 |
// second body
|
sl@0
|
268 |
// --12xy--
|
sl@0
|
269 |
// closing boundary. we ignore this text here
|
sl@0
|
270 |
while( i < (aMultipartLen - hypensLength + 1 ) )
|
sl@0
|
271 |
{
|
sl@0
|
272 |
// get the first two hypens
|
sl@0
|
273 |
// using char comparison to compare "--"
|
sl@0
|
274 |
// hopefully this is faster
|
sl@0
|
275 |
if( (char)aMultipartBody[ i ] == '-' &&
|
sl@0
|
276 |
(char)aMultipartBody[ i+1 ] == '-' )
|
sl@0
|
277 |
{
|
sl@0
|
278 |
char* boundary = (char*)&aMultipartBody[ i + hypensLength ];
|
sl@0
|
279 |
// check if the body is long enough first and then check if boundary matches
|
sl@0
|
280 |
if( aMultipartLen >= i + hypensLength + boundaryLength )
|
sl@0
|
281 |
{
|
sl@0
|
282 |
if( strncasecmp( boundary, boundaryStr, boundaryLength ) == 0 )
|
sl@0
|
283 |
{
|
sl@0
|
284 |
// we've got the boundary
|
sl@0
|
285 |
offset = i + hypensLength + boundaryLength;
|
sl@0
|
286 |
// Next two chars must be either two hypens (closing boundary - 2 bytes),
|
sl@0
|
287 |
// or single Eol characters (new body).
|
sl@0
|
288 |
// Eol = CRLF (2 bytes - windows) or LF (1 byte - unix/mac).
|
sl@0
|
289 |
char* eolBuf = (char*)&aMultipartBody[ offset ];
|
sl@0
|
290 |
// Check if buffer is long enough for hypens [2 bytes], or eol [1 or 2 bytes]
|
sl@0
|
291 |
if( aMultipartLen >= offset + hypensLength )
|
sl@0
|
292 |
{
|
sl@0
|
293 |
if( strncmp( eolBuf, aSingleEolChar, singleEolLength ) == 0||
|
sl@0
|
294 |
eolBuf[0] == Multipart_LF_Text[0])
|
sl@0
|
295 |
|
sl@0
|
296 |
{
|
sl@0
|
297 |
// We found Eol, so this is a new multipart body (header and content)
|
sl@0
|
298 |
if( startOffset == -1 )
|
sl@0
|
299 |
{
|
sl@0
|
300 |
// this is the beginning.
|
sl@0
|
301 |
startOffset = offset;
|
sl@0
|
302 |
// let's looking for the end of this body part which either could
|
sl@0
|
303 |
// be closing boundary or an opening boundary for the next body part
|
sl@0
|
304 |
|
sl@0
|
305 |
// jump over the boundary information
|
sl@0
|
306 |
i = startOffset + singleEolLength;
|
sl@0
|
307 |
}
|
sl@0
|
308 |
else
|
sl@0
|
309 |
{
|
sl@0
|
310 |
// We found the next boundary marker, so this is the
|
sl@0
|
311 |
// beginning of the next body part
|
sl@0
|
312 |
endOffset = offset - boundaryLength - hypensLength;
|
sl@0
|
313 |
// we've got both start and end offset
|
sl@0
|
314 |
break;
|
sl@0
|
315 |
}
|
sl@0
|
316 |
}
|
sl@0
|
317 |
else if( strncmp( eolBuf, Multipart_Hypens_Text, hypensLength ) == 0 )
|
sl@0
|
318 |
{
|
sl@0
|
319 |
// We found the closing boundary marker
|
sl@0
|
320 |
endOffset = offset - boundaryLength - hypensLength;
|
sl@0
|
321 |
break;
|
sl@0
|
322 |
}
|
sl@0
|
323 |
else
|
sl@0
|
324 |
{
|
sl@0
|
325 |
// it's neither Eol nor two hypens "--"
|
sl@0
|
326 |
badContent = ETrue;
|
sl@0
|
327 |
break;
|
sl@0
|
328 |
}
|
sl@0
|
329 |
}
|
sl@0
|
330 |
else
|
sl@0
|
331 |
{
|
sl@0
|
332 |
// the buffer is too short and not closed properly
|
sl@0
|
333 |
endOffset = i;
|
sl@0
|
334 |
break;
|
sl@0
|
335 |
}
|
sl@0
|
336 |
}
|
sl@0
|
337 |
}
|
sl@0
|
338 |
else
|
sl@0
|
339 |
{
|
sl@0
|
340 |
// the buffer is far too short
|
sl@0
|
341 |
endOffset = i;
|
sl@0
|
342 |
break;
|
sl@0
|
343 |
}
|
sl@0
|
344 |
}
|
sl@0
|
345 |
i++;
|
sl@0
|
346 |
} // end of while loop
|
sl@0
|
347 |
|
sl@0
|
348 |
// missing closing boundary check
|
sl@0
|
349 |
if( endOffset == -1 )
|
sl@0
|
350 |
{
|
sl@0
|
351 |
// take the end of the body as closing boundary
|
sl@0
|
352 |
endOffset = i - 1;
|
sl@0
|
353 |
}
|
sl@0
|
354 |
|
sl@0
|
355 |
if( badContent )
|
sl@0
|
356 |
{
|
sl@0
|
357 |
endOffset = -1;
|
sl@0
|
358 |
startOffset = -1;
|
sl@0
|
359 |
}
|
sl@0
|
360 |
|
sl@0
|
361 |
if( startOffset != -1 && endOffset != -1 )
|
sl@0
|
362 |
{
|
sl@0
|
363 |
*aBodyPartBuffer = (TUint8*)&aMultipartBody[ startOffset ];
|
sl@0
|
364 |
length = endOffset - startOffset;
|
sl@0
|
365 |
}
|
sl@0
|
366 |
else
|
sl@0
|
367 |
{
|
sl@0
|
368 |
*aBodyPartBuffer = NULL;
|
sl@0
|
369 |
length = 0;
|
sl@0
|
370 |
}
|
sl@0
|
371 |
|
sl@0
|
372 |
return length;
|
sl@0
|
373 |
}
|
sl@0
|
374 |
|
sl@0
|
375 |
|
sl@0
|
376 |
// -------------------------------------------------------------------------
|
sl@0
|
377 |
// Set End-Of-Line characters. Look at the eol character after the boundary to
|
sl@0
|
378 |
// determine if it is a CRLF (2 bytes used by windows), or LF (1 byte used by
|
sl@0
|
379 |
// unix/mac).
|
sl@0
|
380 |
|
sl@0
|
381 |
// NOTE: CR = 0x0D = '\r', LF = 0x0A = '\n'
|
sl@0
|
382 |
|
sl@0
|
383 |
// Multipart entity is specified as follows:
|
sl@0
|
384 |
// --boundary123[eol]
|
sl@0
|
385 |
// Content-type: text/html[eol]
|
sl@0
|
386 |
// Content-location: http:\\www.example.com\index.html[eol]
|
sl@0
|
387 |
// [eol]
|
sl@0
|
388 |
// <html><body>...example...</html>[eol]
|
sl@0
|
389 |
// --boundary123--[eol]
|
sl@0
|
390 |
// -------------------------------------------------------------------------
|
sl@0
|
391 |
void
|
sl@0
|
392 |
MultipartParser::SetEolCharacters( const TUint8* aMultipartBody,
|
sl@0
|
393 |
TUint32 aMultipartLen,
|
sl@0
|
394 |
const TDesC8& aBoundary,
|
sl@0
|
395 |
char** aSingleEolChar,
|
sl@0
|
396 |
char** aDoubleEolChar )
|
sl@0
|
397 |
{
|
sl@0
|
398 |
// check on required parameters
|
sl@0
|
399 |
__ASSERT_ALWAYS( aMultipartBody != NULL,
|
sl@0
|
400 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
401 |
__ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
|
sl@0
|
402 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
403 |
__ASSERT_ALWAYS( aSingleEolChar != NULL,
|
sl@0
|
404 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
405 |
__ASSERT_ALWAYS( aDoubleEolChar != NULL,
|
sl@0
|
406 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
407 |
|
sl@0
|
408 |
TUint32 i = 0;
|
sl@0
|
409 |
const char* boundaryStr = (const char*) aBoundary.Ptr();
|
sl@0
|
410 |
int boundaryLength = aBoundary.Length();
|
sl@0
|
411 |
int hypensLength = strlen( Multipart_Hypens_Text );
|
sl@0
|
412 |
int lfLength = strlen( Multipart_LF_Text );
|
sl@0
|
413 |
|
sl@0
|
414 |
// Set the default eol (CRLF)
|
sl@0
|
415 |
*aSingleEolChar = (char *)Multipart_CRLF_Text;
|
sl@0
|
416 |
*aDoubleEolChar = (char *)Multipart_DoubleCRLF_Text;
|
sl@0
|
417 |
|
sl@0
|
418 |
while (i < (aMultipartLen - hypensLength + 1))
|
sl@0
|
419 |
{
|
sl@0
|
420 |
// Get the first two hypens
|
sl@0
|
421 |
char* bodyPart = (char*)&aMultipartBody[ i ];
|
sl@0
|
422 |
if (strncmp(bodyPart, Multipart_Hypens_Text, hypensLength) == 0)
|
sl@0
|
423 |
{
|
sl@0
|
424 |
char* boundary = (char*)&aMultipartBody[ i + hypensLength ];
|
sl@0
|
425 |
// Check if the body is long enough first and then check if boundary matches
|
sl@0
|
426 |
if (aMultipartLen >= i + hypensLength + boundaryLength )
|
sl@0
|
427 |
{
|
sl@0
|
428 |
if (strncasecmp(boundary, boundaryStr, boundaryLength) == 0)
|
sl@0
|
429 |
{
|
sl@0
|
430 |
// We've got the boundary
|
sl@0
|
431 |
i = i + hypensLength + boundaryLength;
|
sl@0
|
432 |
// Next two chars should be the single Eol characters.
|
sl@0
|
433 |
// Eol = CRLF (2 bytes - windows), or LF (1 byte - unix/mac).
|
sl@0
|
434 |
char* eolBuf = (char*)&aMultipartBody[ i ];
|
sl@0
|
435 |
// Check if buffer is long enough for eol [1 byte LF]
|
sl@0
|
436 |
if (aMultipartLen >= i + lfLength)
|
sl@0
|
437 |
{
|
sl@0
|
438 |
if (strncmp(eolBuf, Multipart_LF_Text, lfLength) == 0)
|
sl@0
|
439 |
{
|
sl@0
|
440 |
// We found LF Eol (unix/mac)
|
sl@0
|
441 |
*aSingleEolChar = (char *)Multipart_LF_Text;
|
sl@0
|
442 |
*aDoubleEolChar = (char *)Multipart_DoubleLF_Text;
|
sl@0
|
443 |
} // end of if compare eol to LF
|
sl@0
|
444 |
} // end of if buffer size ok
|
sl@0
|
445 |
|
sl@0
|
446 |
// Break in all cases, we will use the default CRLF if we don't
|
sl@0
|
447 |
// find eol=LF, or the remaining buffer is too small
|
sl@0
|
448 |
break;
|
sl@0
|
449 |
} // end of compare/found boundary
|
sl@0
|
450 |
}
|
sl@0
|
451 |
} // end of looking for first two hypens
|
sl@0
|
452 |
|
sl@0
|
453 |
++i;
|
sl@0
|
454 |
} // end of while
|
sl@0
|
455 |
}
|
sl@0
|
456 |
|
sl@0
|
457 |
|
sl@0
|
458 |
// -------------------------------------------------------------------------
|
sl@0
|
459 |
// parse body
|
sl@0
|
460 |
// The bodyPart parameter can contain the optional headers and response, or
|
sl@0
|
461 |
// just the response. In the case of both the (optional) header(s) and the
|
sl@0
|
462 |
// response, let's cut off header(s) and return the response body. The
|
sl@0
|
463 |
// header is seperated from the response by two End-of-line (Eol) characters,
|
sl@0
|
464 |
// i.e. two CRLF's (windows) or two LF's (unix/mac).
|
sl@0
|
465 |
// --boundary123 (omitted from bodyPart parameter, starts next line)
|
sl@0
|
466 |
// Content-type: text/html[eol]
|
sl@0
|
467 |
// Content-location: http:\\www.example.com\index.html[eol]
|
sl@0
|
468 |
// [eol]
|
sl@0
|
469 |
// <html><body>example</body></html>
|
sl@0
|
470 |
//
|
sl@0
|
471 |
// In the case of no headers, there may be only one (or more) Eol characters.
|
sl@0
|
472 |
// --boundary123 (omitted from bodyPart parameter, starts on next line)
|
sl@0
|
473 |
// [eol]
|
sl@0
|
474 |
// <html><body>example</body></html>
|
sl@0
|
475 |
// -------------------------------------------------------------------------
|
sl@0
|
476 |
void
|
sl@0
|
477 |
MultipartParser::ParseBodyPartL( TUint8* aBodyPartBuffer,
|
sl@0
|
478 |
TUint32 aBodyPartBufferLength,
|
sl@0
|
479 |
char* aSingleEolChar,
|
sl@0
|
480 |
char* aDoubleEolChar,
|
sl@0
|
481 |
const TDesC16& aResponseUrl,
|
sl@0
|
482 |
CBodyPart* aBodyPart )
|
sl@0
|
483 |
{
|
sl@0
|
484 |
// check on required parameters
|
sl@0
|
485 |
__ASSERT_ALWAYS( aBodyPartBuffer != NULL,
|
sl@0
|
486 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
487 |
__ASSERT_ALWAYS( aSingleEolChar != NULL,
|
sl@0
|
488 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
489 |
__ASSERT_ALWAYS( aDoubleEolChar != NULL,
|
sl@0
|
490 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
491 |
__ASSERT_ALWAYS( aBodyPart != NULL,
|
sl@0
|
492 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
493 |
__ASSERT_ALWAYS( aResponseUrl.Ptr() != NULL,
|
sl@0
|
494 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
495 |
|
sl@0
|
496 |
// headers look something like this
|
sl@0
|
497 |
// we need to return "text/html" if the requested header is "Content-type"
|
sl@0
|
498 |
// --boundary123
|
sl@0
|
499 |
// Content-type: text/html
|
sl@0
|
500 |
// Content-location: http:\\www.example.com\index.html
|
sl@0
|
501 |
//
|
sl@0
|
502 |
// <html>
|
sl@0
|
503 |
// <body>
|
sl@0
|
504 |
// example
|
sl@0
|
505 |
int contentHeaderValueCharLen = 0;
|
sl@0
|
506 |
TPtrC8 contentHeaderValuePtr( NULL, 0 );
|
sl@0
|
507 |
int contentHeaderNameLength = 0;
|
sl@0
|
508 |
|
sl@0
|
509 |
/*lint -e{668} Possibly passing a null pointer to function */
|
sl@0
|
510 |
int singleEolLength = strlen( aSingleEolChar );
|
sl@0
|
511 |
int doubleEolLength = strlen( aDoubleEolChar );
|
sl@0
|
512 |
// start looking for the header name
|
sl@0
|
513 |
for( TUint32 i = 0; i < aBodyPartBufferLength ; i++ )
|
sl@0
|
514 |
{
|
sl@0
|
515 |
int found = 0;
|
sl@0
|
516 |
const char* tempBodyPartBuffer = (char*)&aBodyPartBuffer[ i ];
|
sl@0
|
517 |
|
sl@0
|
518 |
// Did we find the Content Header Value
|
sl@0
|
519 |
if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Base_Text, MULTIPART_CONTENT_BASE_LENGTH ) == 0)
|
sl@0
|
520 |
{
|
sl@0
|
521 |
contentHeaderNameLength = MULTIPART_CONTENT_BASE_LENGTH;
|
sl@0
|
522 |
found = MULTIPART_CONTENT_BASE;
|
sl@0
|
523 |
}
|
sl@0
|
524 |
else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Location_Text, MULTIPART_CONTENT_LOCATION_LENGTH ) == 0)
|
sl@0
|
525 |
{
|
sl@0
|
526 |
contentHeaderNameLength = MULTIPART_CONTENT_LOCATION_LENGTH;
|
sl@0
|
527 |
found = MULTIPART_CONTENT_LOCATION;
|
sl@0
|
528 |
}
|
sl@0
|
529 |
else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Transfer_Encoding_Text, MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH ) == 0)
|
sl@0
|
530 |
{
|
sl@0
|
531 |
contentHeaderNameLength = MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH;
|
sl@0
|
532 |
found = MULTIPART_CONTENT_TRANSFER_ENCODING;
|
sl@0
|
533 |
}
|
sl@0
|
534 |
else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Encoding_Text, MULTIPART_CONTENT_ENCODING_LENGTH ) == 0)
|
sl@0
|
535 |
{
|
sl@0
|
536 |
contentHeaderNameLength = MULTIPART_CONTENT_ENCODING_LENGTH;
|
sl@0
|
537 |
found = MULTIPART_CONTENT_TRANSFER_ENCODING;
|
sl@0
|
538 |
}
|
sl@0
|
539 |
else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Type_Text, MULTIPART_CONTENT_TYPE_LENGTH ) == 0)
|
sl@0
|
540 |
{
|
sl@0
|
541 |
contentHeaderNameLength = MULTIPART_CONTENT_TYPE_LENGTH;
|
sl@0
|
542 |
found = MULTIPART_CONTENT_TYPE;
|
sl@0
|
543 |
}
|
sl@0
|
544 |
else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_ID_Text, MULTIPART_CONTENT_ID_LENGTH ) == 0)
|
sl@0
|
545 |
{
|
sl@0
|
546 |
contentHeaderNameLength = MULTIPART_CONTENT_ID_LENGTH;
|
sl@0
|
547 |
found = MULTIPART_CONTENT_ID;
|
sl@0
|
548 |
}
|
sl@0
|
549 |
|
sl@0
|
550 |
if (found)
|
sl@0
|
551 |
{
|
sl@0
|
552 |
// skip spaces
|
sl@0
|
553 |
int startPos = i + contentHeaderNameLength;
|
sl@0
|
554 |
while ( (char)aBodyPartBuffer[ startPos ] == ' ' )
|
sl@0
|
555 |
{
|
sl@0
|
556 |
startPos++;
|
sl@0
|
557 |
}
|
sl@0
|
558 |
|
sl@0
|
559 |
// used for finding '<' in Content-ID field
|
sl@0
|
560 |
char charFirst = aBodyPartBuffer[ startPos ];
|
sl@0
|
561 |
// content headers are closed with End-Of-Line (Eol) character
|
sl@0
|
562 |
for( TUint32 j = startPos; j < aBodyPartBufferLength - singleEolLength + 1; j++ )
|
sl@0
|
563 |
{
|
sl@0
|
564 |
char* tmpContentHeaderValue = (char*)&aBodyPartBuffer[ j ];
|
sl@0
|
565 |
if( strncmp( tmpContentHeaderValue, aSingleEolChar, singleEolLength ) == 0
|
sl@0
|
566 |
|| tmpContentHeaderValue[0] == Multipart_LF_Text[0])
|
sl@0
|
567 |
{
|
sl@0
|
568 |
if( found == MULTIPART_CONTENT_ID )
|
sl@0
|
569 |
{
|
sl@0
|
570 |
if( charFirst == '<' )
|
sl@0
|
571 |
{
|
sl@0
|
572 |
// length of the value excluding beginging '<' and ending '>'
|
sl@0
|
573 |
contentHeaderValueCharLen = j - startPos - 2;
|
sl@0
|
574 |
contentHeaderValuePtr.Set( (TUint8*)&aBodyPartBuffer[ startPos+1 ], contentHeaderValueCharLen );
|
sl@0
|
575 |
}
|
sl@0
|
576 |
}
|
sl@0
|
577 |
else
|
sl@0
|
578 |
{
|
sl@0
|
579 |
// length of the value
|
sl@0
|
580 |
contentHeaderValueCharLen = j - startPos;
|
sl@0
|
581 |
contentHeaderValuePtr.Set( (TUint8*)&aBodyPartBuffer[ startPos ], contentHeaderValueCharLen );
|
sl@0
|
582 |
}
|
sl@0
|
583 |
|
sl@0
|
584 |
// rewind so the double EOL will be checked against later
|
sl@0
|
585 |
i = j - 1;
|
sl@0
|
586 |
// break the inner loop
|
sl@0
|
587 |
break;
|
sl@0
|
588 |
}
|
sl@0
|
589 |
} // end of inner for loop
|
sl@0
|
590 |
|
sl@0
|
591 |
switch( found )
|
sl@0
|
592 |
{
|
sl@0
|
593 |
case MULTIPART_CONTENT_BASE:
|
sl@0
|
594 |
aBodyPart->SetContentBase( contentHeaderValuePtr );
|
sl@0
|
595 |
break;
|
sl@0
|
596 |
case MULTIPART_CONTENT_LOCATION:
|
sl@0
|
597 |
aBodyPart->SetContentLocation( contentHeaderValuePtr );
|
sl@0
|
598 |
break;
|
sl@0
|
599 |
case MULTIPART_CONTENT_TRANSFER_ENCODING:
|
sl@0
|
600 |
aBodyPart->SetContentTransferEncoding( contentHeaderValuePtr );
|
sl@0
|
601 |
break;
|
sl@0
|
602 |
case MULTIPART_CONTENT_TYPE:
|
sl@0
|
603 |
aBodyPart->SetContentType( contentHeaderValuePtr );
|
sl@0
|
604 |
break;
|
sl@0
|
605 |
case MULTIPART_CONTENT_ID:
|
sl@0
|
606 |
aBodyPart->SetContentID( contentHeaderValuePtr );
|
sl@0
|
607 |
break;
|
sl@0
|
608 |
default:
|
sl@0
|
609 |
break;
|
sl@0
|
610 |
}
|
sl@0
|
611 |
} // end of if (found)
|
sl@0
|
612 |
|
sl@0
|
613 |
// Did we get to the end of the Content Header. Many of the Content Header Values
|
sl@0
|
614 |
// are optional, so we could get to the end of the Content Header (double Eol) and
|
sl@0
|
615 |
// not find the Content Header Value we were searching for.
|
sl@0
|
616 |
// get the response body
|
sl@0
|
617 |
int aEolLength = strlen(Multipart_DoubleLF_Text);
|
sl@0
|
618 |
if (strncmp( tempBodyPartBuffer, aDoubleEolChar, doubleEolLength ) == 0 ||
|
sl@0
|
619 |
strncmp( tempBodyPartBuffer, Multipart_DoubleLF_Text,aEolLength)== 0)
|
sl@0
|
620 |
{
|
sl@0
|
621 |
if(strncmp( tempBodyPartBuffer, aDoubleEolChar, doubleEolLength)== 0)
|
sl@0
|
622 |
aEolLength = doubleEolLength;
|
sl@0
|
623 |
// set body
|
sl@0
|
624 |
TUint8* responseBody = (TUint8*) &tempBodyPartBuffer[aEolLength];
|
sl@0
|
625 |
int lengthBody = aBodyPartBufferLength - ( i + aEolLength );
|
sl@0
|
626 |
TPtrC8 body( responseBody, lengthBody );
|
sl@0
|
627 |
aBodyPart->SetBody( body );
|
sl@0
|
628 |
|
sl@0
|
629 |
// set headers when we have headers
|
sl@0
|
630 |
if( i != 0 )
|
sl@0
|
631 |
{
|
sl@0
|
632 |
// jump over the starting single EOL
|
sl@0
|
633 |
TUint8* responseHeaders = (TUint8*) &aBodyPartBuffer[ singleEolLength ];
|
sl@0
|
634 |
// // jump over the starting single EOL and the ending double EOL
|
sl@0
|
635 |
int lengthHeaders = aBodyPartBufferLength - lengthBody - singleEolLength - aEolLength;
|
sl@0
|
636 |
TPtrC8 headers( responseHeaders, lengthHeaders );
|
sl@0
|
637 |
aBodyPart->SetHeaders( headers );
|
sl@0
|
638 |
}
|
sl@0
|
639 |
|
sl@0
|
640 |
break;
|
sl@0
|
641 |
}
|
sl@0
|
642 |
} // end of outter for loop
|
sl@0
|
643 |
|
sl@0
|
644 |
// prepare more on body part
|
sl@0
|
645 |
|
sl@0
|
646 |
// Check to see if we have a Content-Transfer-Encoding.
|
sl@0
|
647 |
TUint8* contentTransferEncodingValue = (TUint8*) aBodyPart->ContentTransferEncoding().Ptr();
|
sl@0
|
648 |
// If we have Content-Transfer-Encoding, prepare to decode
|
sl@0
|
649 |
if( MultipartParser::IsEncoded(contentTransferEncodingValue) )
|
sl@0
|
650 |
{
|
sl@0
|
651 |
// Initialize the encoded body, input
|
sl@0
|
652 |
TPtrC8 encodedBody( aBodyPart->Body() );
|
sl@0
|
653 |
|
sl@0
|
654 |
// This will contain the "decoded" data.
|
sl@0
|
655 |
// The memory (decodedBody.Ptr) is owned by this method, but allocated by
|
sl@0
|
656 |
// the DecodeContentTransferEncoding method.
|
sl@0
|
657 |
TPtr8 decodedBody( NULL, 0, 0 );
|
sl@0
|
658 |
|
sl@0
|
659 |
// The decoded data will return in decodedBody.Ptr.
|
sl@0
|
660 |
// The memory allocated is owned by this method.
|
sl@0
|
661 |
TInt err = MultipartParser::DecodeContentTransferEncoding( contentTransferEncodingValue,
|
sl@0
|
662 |
encodedBody,
|
sl@0
|
663 |
decodedBody );
|
sl@0
|
664 |
User::LeaveIfError(err);
|
sl@0
|
665 |
|
sl@0
|
666 |
// The responseBody pointer is an offset into the response
|
sl@0
|
667 |
// buffer, do not delete. Substitute the decodedBody pointer.
|
sl@0
|
668 |
aBodyPart->SetBody( decodedBody );
|
sl@0
|
669 |
aBodyPart->SetIsDecodedBody( ETrue );
|
sl@0
|
670 |
} // end of if (contentTransferEncodingValue)
|
sl@0
|
671 |
|
sl@0
|
672 |
// Check to see if we have a Content-Type.
|
sl@0
|
673 |
TUint8* contentTypeValue = (TUint8*) aBodyPart->ContentType().Ptr();
|
sl@0
|
674 |
// parse contentType to get new contentType, charset, boundary info
|
sl@0
|
675 |
if( contentTypeValue )
|
sl@0
|
676 |
{
|
sl@0
|
677 |
MultipartParser::CutOffContentTypeAttributes( aBodyPart );
|
sl@0
|
678 |
// updated content type
|
sl@0
|
679 |
contentTypeValue = (TUint8*) aBodyPart->ContentType().Ptr();
|
sl@0
|
680 |
}
|
sl@0
|
681 |
|
sl@0
|
682 |
// If we have zipped Content-Type, prepare to unzip
|
sl@0
|
683 |
if( MultipartParser::IsZipped(contentTypeValue) )
|
sl@0
|
684 |
{
|
sl@0
|
685 |
// Initialize the zipped body, input
|
sl@0
|
686 |
TPtrC8 zippedBody( aBodyPart->Body() );
|
sl@0
|
687 |
|
sl@0
|
688 |
// This will contain the "unzipped" data.
|
sl@0
|
689 |
// The memory (unzippedBody.Ptr) is owned by this method, but allocated by
|
sl@0
|
690 |
// the Unzip method.
|
sl@0
|
691 |
TPtr8 unzippedBody( NULL, 0, 0 );
|
sl@0
|
692 |
|
sl@0
|
693 |
// The unzipped data will return in unzippedBody.Ptr.
|
sl@0
|
694 |
// The memory allocated is owned by this method.
|
sl@0
|
695 |
TInt err = MultipartParser::Unzip( contentTypeValue,
|
sl@0
|
696 |
zippedBody,
|
sl@0
|
697 |
unzippedBody );
|
sl@0
|
698 |
User::LeaveIfError(err);
|
sl@0
|
699 |
|
sl@0
|
700 |
if( aBodyPart->IsDecodedBody() )
|
sl@0
|
701 |
{
|
sl@0
|
702 |
// old body is not the original buffer, delete it
|
sl@0
|
703 |
delete (TUint8*) aBodyPart->Body().Ptr();
|
sl@0
|
704 |
}
|
sl@0
|
705 |
// unzip happend, use unzippedBody; delete decodedBody
|
sl@0
|
706 |
else
|
sl@0
|
707 |
{
|
sl@0
|
708 |
// The responseBody pointer is an offset into the response
|
sl@0
|
709 |
// buffer, do not delete. Substitute the decodedBody pointer.
|
sl@0
|
710 |
|
sl@0
|
711 |
aBodyPart->SetIsDecodedBody( ETrue );
|
sl@0
|
712 |
}
|
sl@0
|
713 |
|
sl@0
|
714 |
aBodyPart->SetBody( unzippedBody );
|
sl@0
|
715 |
}
|
sl@0
|
716 |
|
sl@0
|
717 |
// Get the url of the current body part
|
sl@0
|
718 |
HBufC16* responseUrl = MultipartParser::GetBodyPartUrlL( aBodyPart->ContentBase(),
|
sl@0
|
719 |
aBodyPart->ContentLocation(),
|
sl@0
|
720 |
aResponseUrl );
|
sl@0
|
721 |
aBodyPart->SetUrl( responseUrl );
|
sl@0
|
722 |
}
|
sl@0
|
723 |
|
sl@0
|
724 |
|
sl@0
|
725 |
// -------------------------------------------------------------------------
|
sl@0
|
726 |
// From RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
|
sl@0
|
727 |
// Section 6.2. Content-Transfer-Encodings Semantics
|
sl@0
|
728 |
//
|
sl@0
|
729 |
// The Content-Transfer-Encoding values "7bit", "8bit", and "binary" all
|
sl@0
|
730 |
// mean that the identity (i.e. NO) encoding transformation has been
|
sl@0
|
731 |
// performed. As such, they serve simply as indicators of the domain of
|
sl@0
|
732 |
// the body data, and provide useful information about the sort of
|
sl@0
|
733 |
// encoding that might be needed for transmission in a given transport
|
sl@0
|
734 |
// system. The terms "7bit data", "8bit data", and "binary data" are
|
sl@0
|
735 |
// all defined in Section 2.
|
sl@0
|
736 |
|
sl@0
|
737 |
// Returns true if contentTransferEncodingValue is neither NULL nor a domain.
|
sl@0
|
738 |
// -------------------------------------------------------------------------
|
sl@0
|
739 |
TBool MultipartParser::IsEncoded( TUint8* aContentTransferEncodingValue )
|
sl@0
|
740 |
{
|
sl@0
|
741 |
if( !aContentTransferEncodingValue )
|
sl@0
|
742 |
{
|
sl@0
|
743 |
return EFalse;
|
sl@0
|
744 |
}
|
sl@0
|
745 |
|
sl@0
|
746 |
char* encoding = (char*)aContentTransferEncodingValue;
|
sl@0
|
747 |
|
sl@0
|
748 |
if ( strncasecmp( encoding,
|
sl@0
|
749 |
Multipart_Content_Transfer_Encoding_7bit,
|
sl@0
|
750 |
strlen(Multipart_Content_Transfer_Encoding_7bit) ) == 0 )
|
sl@0
|
751 |
{
|
sl@0
|
752 |
return EFalse;
|
sl@0
|
753 |
}
|
sl@0
|
754 |
|
sl@0
|
755 |
if ( strncasecmp( encoding,
|
sl@0
|
756 |
Multipart_Content_Transfer_Encoding_8bit,
|
sl@0
|
757 |
strlen(Multipart_Content_Transfer_Encoding_8bit) ) == 0 )
|
sl@0
|
758 |
{
|
sl@0
|
759 |
return EFalse;
|
sl@0
|
760 |
}
|
sl@0
|
761 |
|
sl@0
|
762 |
if ( strncasecmp( encoding,
|
sl@0
|
763 |
Multipart_Content_Transfer_Encoding_binary,
|
sl@0
|
764 |
strlen(Multipart_Content_Transfer_Encoding_binary) ) == 0 )
|
sl@0
|
765 |
{
|
sl@0
|
766 |
return EFalse;
|
sl@0
|
767 |
}
|
sl@0
|
768 |
|
sl@0
|
769 |
return ETrue;
|
sl@0
|
770 |
}
|
sl@0
|
771 |
|
sl@0
|
772 |
|
sl@0
|
773 |
// ----------------------------------------------------------------------------
|
sl@0
|
774 |
// DecodeContentTransferEncoding
|
sl@0
|
775 |
//
|
sl@0
|
776 |
// Decodes the Content-Transfer-Encoding. The returned length of decodedBody
|
sl@0
|
777 |
// is zero if decoding failed.
|
sl@0
|
778 |
// NOTES:
|
sl@0
|
779 |
// 1. This method should be called with a non-null string, i.e.
|
sl@0
|
780 |
// aContentTransferEncodingValue.
|
sl@0
|
781 |
// 2. Memory is allocated in this method, but ownership is with the calling method.
|
sl@0
|
782 |
// ----------------------------------------------------------------------------
|
sl@0
|
783 |
TInt
|
sl@0
|
784 |
MultipartParser::DecodeContentTransferEncoding( TUint8* aContentTransferEncodingValue,
|
sl@0
|
785 |
const TDesC8& aEncodedBody,
|
sl@0
|
786 |
TPtr8& aDecodedBody )
|
sl@0
|
787 |
{
|
sl@0
|
788 |
// check on required parameters
|
sl@0
|
789 |
__ASSERT_ALWAYS( aContentTransferEncodingValue != NULL,
|
sl@0
|
790 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
791 |
__ASSERT_ALWAYS( aEncodedBody.Ptr() != NULL,
|
sl@0
|
792 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
793 |
|
sl@0
|
794 |
TInt status = KErrNone;
|
sl@0
|
795 |
char* contentTransferEncodingString = (char*)aContentTransferEncodingValue;
|
sl@0
|
796 |
|
sl@0
|
797 |
// Set the decodedBody.Length to zero, length > 0 if successful decode
|
sl@0
|
798 |
aDecodedBody.SetLength(0);
|
sl@0
|
799 |
|
sl@0
|
800 |
// Is the Content-Transfer-Encoding = "base64"
|
sl@0
|
801 |
if( strncasecmp( contentTransferEncodingString,
|
sl@0
|
802 |
Multipart_Content_Transfer_Encoding_Base64,
|
sl@0
|
803 |
strlen(Multipart_Content_Transfer_Encoding_Base64) ) == 0 )
|
sl@0
|
804 |
{
|
sl@0
|
805 |
// The decoded length of base64 is about half (use same) encoded length
|
sl@0
|
806 |
TUint maxBodyLength = aEncodedBody.Length();
|
sl@0
|
807 |
TUint8* decodedDataPtr = new TUint8[maxBodyLength];
|
sl@0
|
808 |
if( decodedDataPtr )
|
sl@0
|
809 |
{
|
sl@0
|
810 |
aDecodedBody.Set(decodedDataPtr, 0, maxBodyLength);
|
sl@0
|
811 |
|
sl@0
|
812 |
using namespace BSUL;
|
sl@0
|
813 |
// Decode the base64 Content-Transfer-Encoding
|
sl@0
|
814 |
Base64Codec::Decode(aEncodedBody, aDecodedBody);
|
sl@0
|
815 |
|
sl@0
|
816 |
if (aDecodedBody.Length() == 0)
|
sl@0
|
817 |
{
|
sl@0
|
818 |
status = KErrGeneral;
|
sl@0
|
819 |
}
|
sl@0
|
820 |
}
|
sl@0
|
821 |
else // decodedDataPtr is NULL
|
sl@0
|
822 |
{
|
sl@0
|
823 |
status = KErrNoMemory;
|
sl@0
|
824 |
}
|
sl@0
|
825 |
} // end of base64 decoding
|
sl@0
|
826 |
|
sl@0
|
827 |
// Is the Content-Transfer-Encoding = "quoted-printable"
|
sl@0
|
828 |
else if( strncasecmp( contentTransferEncodingString,
|
sl@0
|
829 |
Multipart_Content_Transfer_Encoding_QuotedPrintable,
|
sl@0
|
830 |
strlen(Multipart_Content_Transfer_Encoding_QuotedPrintable) ) == 0 )
|
sl@0
|
831 |
{
|
sl@0
|
832 |
// The decoded length of QP is the same as the encoded length
|
sl@0
|
833 |
TUint maxBodyLength = aEncodedBody.Length();
|
sl@0
|
834 |
TUint8* decodedDataPtr = new TUint8[maxBodyLength];
|
sl@0
|
835 |
if( decodedDataPtr )
|
sl@0
|
836 |
{
|
sl@0
|
837 |
aDecodedBody.Set(decodedDataPtr, 0, maxBodyLength);
|
sl@0
|
838 |
|
sl@0
|
839 |
// Decode the quoted-printable Content-Transfer-Encoding
|
sl@0
|
840 |
QuotedPrintableCodec::Decode(aEncodedBody, aDecodedBody);
|
sl@0
|
841 |
|
sl@0
|
842 |
if (aDecodedBody.Length() == 0)
|
sl@0
|
843 |
{
|
sl@0
|
844 |
status = KErrGeneral;
|
sl@0
|
845 |
}
|
sl@0
|
846 |
}
|
sl@0
|
847 |
else // decodedDataPtr is NULL
|
sl@0
|
848 |
{
|
sl@0
|
849 |
status = KErrNoMemory;
|
sl@0
|
850 |
}
|
sl@0
|
851 |
} // end of quoted-printed decoding
|
sl@0
|
852 |
|
sl@0
|
853 |
// Is the Content-Encoding = "gzip"
|
sl@0
|
854 |
else if( strncasecmp( contentTransferEncodingString,
|
sl@0
|
855 |
Multipart_Content_Encoding_GZip,
|
sl@0
|
856 |
strlen(Multipart_Content_Encoding_GZip) ) == 0 )
|
sl@0
|
857 |
{
|
sl@0
|
858 |
// Our GZip decoder parts
|
sl@0
|
859 |
GZipBufMgr* gZipBufMgr = NULL;
|
sl@0
|
860 |
CEZDecompressor* ezDecompressor = NULL;
|
sl@0
|
861 |
|
sl@0
|
862 |
// We have gzip, lets decompress the encoded data.
|
sl@0
|
863 |
// Set up the encoded data into a GZip buffer manager.
|
sl@0
|
864 |
TInt err = 0;
|
sl@0
|
865 |
TRAP(err, gZipBufMgr = GZipBufMgr::NewL( aEncodedBody ));
|
sl@0
|
866 |
|
sl@0
|
867 |
// Get the GZip decompressor
|
sl@0
|
868 |
if( gZipBufMgr )
|
sl@0
|
869 |
{
|
sl@0
|
870 |
TRAP(err, ezDecompressor = CEZDecompressor::NewL(*gZipBufMgr, -CEZDecompressor::EMaxWBits));
|
sl@0
|
871 |
|
sl@0
|
872 |
// Inflate the GZip data
|
sl@0
|
873 |
if( ezDecompressor )
|
sl@0
|
874 |
{
|
sl@0
|
875 |
TRAP(err, ezDecompressor->InflateL());
|
sl@0
|
876 |
// Set the finalize flag
|
sl@0
|
877 |
if (err == KErrNone)
|
sl@0
|
878 |
{
|
sl@0
|
879 |
TRAP(err, gZipBufMgr->FinalizeL(*ezDecompressor));
|
sl@0
|
880 |
// Get the inflated data, it is much larger then the encoded data
|
sl@0
|
881 |
if (err == KErrNone)
|
sl@0
|
882 |
{
|
sl@0
|
883 |
TPtrC8 output = ezDecompressor->OutputDescriptor();
|
sl@0
|
884 |
if (output.Length() != 0)
|
sl@0
|
885 |
{
|
sl@0
|
886 |
TInt size = output.Size();
|
sl@0
|
887 |
TUint8* outBuf = new TUint8[size];
|
sl@0
|
888 |
if( outBuf )
|
sl@0
|
889 |
{
|
sl@0
|
890 |
memcpy(outBuf, output.Ptr(), size);
|
sl@0
|
891 |
|
sl@0
|
892 |
aDecodedBody.Set((TUint8*)outBuf, size, size);
|
sl@0
|
893 |
}
|
sl@0
|
894 |
else // outBuf is NULL
|
sl@0
|
895 |
{
|
sl@0
|
896 |
status = KErrNoMemory;
|
sl@0
|
897 |
}
|
sl@0
|
898 |
}
|
sl@0
|
899 |
else
|
sl@0
|
900 |
{
|
sl@0
|
901 |
status = KErrGeneral;
|
sl@0
|
902 |
}
|
sl@0
|
903 |
}
|
sl@0
|
904 |
else
|
sl@0
|
905 |
{
|
sl@0
|
906 |
status = KErrGeneral;
|
sl@0
|
907 |
}
|
sl@0
|
908 |
}
|
sl@0
|
909 |
else
|
sl@0
|
910 |
{
|
sl@0
|
911 |
status = KErrGeneral;
|
sl@0
|
912 |
}
|
sl@0
|
913 |
}
|
sl@0
|
914 |
else // ezDecompressor is NULL
|
sl@0
|
915 |
{
|
sl@0
|
916 |
status = KErrNoMemory;
|
sl@0
|
917 |
}
|
sl@0
|
918 |
}
|
sl@0
|
919 |
else // gZipBufMgr is NULL
|
sl@0
|
920 |
{
|
sl@0
|
921 |
status = KErrNoMemory;
|
sl@0
|
922 |
}
|
sl@0
|
923 |
|
sl@0
|
924 |
// Clean up our memory
|
sl@0
|
925 |
delete gZipBufMgr;
|
sl@0
|
926 |
delete ezDecompressor;
|
sl@0
|
927 |
} // end of gzip
|
sl@0
|
928 |
|
sl@0
|
929 |
// We can add additional decodings here.
|
sl@0
|
930 |
// When adding additional decoding be aware of the decodedBody.Ptr()
|
sl@0
|
931 |
// max size. Do the realloc here, AND allow the decodedBody.Ptr()
|
sl@0
|
932 |
// ownership to be passed back to calling method.
|
sl@0
|
933 |
|
sl@0
|
934 |
return status;
|
sl@0
|
935 |
}
|
sl@0
|
936 |
|
sl@0
|
937 |
|
sl@0
|
938 |
// -------------------------------------------------------------------------
|
sl@0
|
939 |
// only support application/x-gzip
|
sl@0
|
940 |
// -------------------------------------------------------------------------
|
sl@0
|
941 |
TBool MultipartParser::IsZipped( TUint8* aContentTypeValue )
|
sl@0
|
942 |
{
|
sl@0
|
943 |
if( !aContentTypeValue )
|
sl@0
|
944 |
{
|
sl@0
|
945 |
return EFalse;
|
sl@0
|
946 |
}
|
sl@0
|
947 |
|
sl@0
|
948 |
char* contentType = (char*)aContentTypeValue;
|
sl@0
|
949 |
|
sl@0
|
950 |
if ( strncasecmp( contentType,
|
sl@0
|
951 |
Multipart_Content_Type_GZip,
|
sl@0
|
952 |
strlen(Multipart_Content_Type_GZip) ) == 0 )
|
sl@0
|
953 |
{
|
sl@0
|
954 |
return ETrue;
|
sl@0
|
955 |
}
|
sl@0
|
956 |
|
sl@0
|
957 |
return EFalse;
|
sl@0
|
958 |
}
|
sl@0
|
959 |
|
sl@0
|
960 |
|
sl@0
|
961 |
// ----------------------------------------------------------------------------
|
sl@0
|
962 |
// Unzip
|
sl@0
|
963 |
//
|
sl@0
|
964 |
// Unzip the .gz. The returned length of unzippedBody
|
sl@0
|
965 |
// is zero if unzip failed.
|
sl@0
|
966 |
// NOTES:
|
sl@0
|
967 |
// 1. This method should be called with a non-null string, i.e.
|
sl@0
|
968 |
// aContentType.
|
sl@0
|
969 |
// 2. Memory is allocated in this method, but ownership is with the calling method.
|
sl@0
|
970 |
// ----------------------------------------------------------------------------
|
sl@0
|
971 |
TInt
|
sl@0
|
972 |
MultipartParser::Unzip( TUint8* aContentType,
|
sl@0
|
973 |
const TDesC8& aZippedBody,
|
sl@0
|
974 |
TPtr8& aUnzippedBody )
|
sl@0
|
975 |
{
|
sl@0
|
976 |
// check on required parameters
|
sl@0
|
977 |
__ASSERT_ALWAYS( aContentType != NULL,
|
sl@0
|
978 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
979 |
__ASSERT_ALWAYS( aZippedBody.Ptr() != NULL,
|
sl@0
|
980 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
981 |
|
sl@0
|
982 |
TInt status = KErrNone;
|
sl@0
|
983 |
char* contentTypeStr = (char*)aContentType;
|
sl@0
|
984 |
|
sl@0
|
985 |
// Set the aUnzippedBody.Length to zero, length > 0 if successful decode
|
sl@0
|
986 |
aUnzippedBody.SetLength(0);
|
sl@0
|
987 |
|
sl@0
|
988 |
// Our GZip decoder parts
|
sl@0
|
989 |
GZipBufMgr* gZipBufMgr = NULL;
|
sl@0
|
990 |
CEZDecompressor* ezDecompressor = NULL;
|
sl@0
|
991 |
|
sl@0
|
992 |
// Is the Content-Type = "application/x-gzip"
|
sl@0
|
993 |
if( strncasecmp( contentTypeStr,
|
sl@0
|
994 |
Multipart_Content_Type_GZip,
|
sl@0
|
995 |
strlen(Multipart_Content_Type_GZip) ) == 0 )
|
sl@0
|
996 |
{
|
sl@0
|
997 |
// We have gzip, lets decompress the encoded data.
|
sl@0
|
998 |
// Set up the encoded data into a GZip buffer manager.
|
sl@0
|
999 |
TInt err = 0;
|
sl@0
|
1000 |
TRAP(err, gZipBufMgr = GZipBufMgr::NewL(aZippedBody));
|
sl@0
|
1001 |
|
sl@0
|
1002 |
// Get the GZip decompressor
|
sl@0
|
1003 |
if( gZipBufMgr )
|
sl@0
|
1004 |
{
|
sl@0
|
1005 |
TRAP(err, ezDecompressor = CEZDecompressor::NewL(*gZipBufMgr, -CEZDecompressor::EMaxWBits));
|
sl@0
|
1006 |
|
sl@0
|
1007 |
// Inflate the GZip data
|
sl@0
|
1008 |
if( ezDecompressor )
|
sl@0
|
1009 |
{
|
sl@0
|
1010 |
TRAP(err, ezDecompressor->InflateL());
|
sl@0
|
1011 |
// Set the finalize flag
|
sl@0
|
1012 |
if (err == KErrNone)
|
sl@0
|
1013 |
{
|
sl@0
|
1014 |
TRAP(err, gZipBufMgr->FinalizeL(*ezDecompressor));
|
sl@0
|
1015 |
// Get the inflated data, it is much larger then the encoded data
|
sl@0
|
1016 |
if (err == KErrNone)
|
sl@0
|
1017 |
{
|
sl@0
|
1018 |
TPtrC8 output = ezDecompressor->OutputDescriptor();
|
sl@0
|
1019 |
if (output.Length() != 0)
|
sl@0
|
1020 |
{
|
sl@0
|
1021 |
TInt size = output.Size();
|
sl@0
|
1022 |
TUint8* outBuf = new TUint8[size];
|
sl@0
|
1023 |
if( outBuf )
|
sl@0
|
1024 |
{
|
sl@0
|
1025 |
memcpy(outBuf, output.Ptr(), size);
|
sl@0
|
1026 |
|
sl@0
|
1027 |
aUnzippedBody.Set((TUint8*)outBuf, size, size);
|
sl@0
|
1028 |
}
|
sl@0
|
1029 |
else // outBuf is NULL
|
sl@0
|
1030 |
{
|
sl@0
|
1031 |
status = KErrNoMemory;
|
sl@0
|
1032 |
}
|
sl@0
|
1033 |
}
|
sl@0
|
1034 |
else
|
sl@0
|
1035 |
{
|
sl@0
|
1036 |
status = KErrGeneral;
|
sl@0
|
1037 |
}
|
sl@0
|
1038 |
}
|
sl@0
|
1039 |
else
|
sl@0
|
1040 |
{
|
sl@0
|
1041 |
status = KErrGeneral;
|
sl@0
|
1042 |
}
|
sl@0
|
1043 |
}
|
sl@0
|
1044 |
else
|
sl@0
|
1045 |
{
|
sl@0
|
1046 |
status = KErrGeneral;
|
sl@0
|
1047 |
}
|
sl@0
|
1048 |
}
|
sl@0
|
1049 |
else // ezDecompressor is NULL
|
sl@0
|
1050 |
{
|
sl@0
|
1051 |
status = KErrNoMemory;
|
sl@0
|
1052 |
}
|
sl@0
|
1053 |
}
|
sl@0
|
1054 |
else // gZipBufMgr is NULL
|
sl@0
|
1055 |
{
|
sl@0
|
1056 |
status = KErrNoMemory;
|
sl@0
|
1057 |
}
|
sl@0
|
1058 |
} // end of gzip
|
sl@0
|
1059 |
|
sl@0
|
1060 |
// Clean up our memory
|
sl@0
|
1061 |
delete gZipBufMgr;
|
sl@0
|
1062 |
delete ezDecompressor;
|
sl@0
|
1063 |
|
sl@0
|
1064 |
return status;
|
sl@0
|
1065 |
}
|
sl@0
|
1066 |
|
sl@0
|
1067 |
|
sl@0
|
1068 |
// ----------------------------------------------------------------------------
|
sl@0
|
1069 |
// It cuts off the charset value from the content type header
|
sl@0
|
1070 |
// content type string looks like as follows:
|
sl@0
|
1071 |
// text/plain; charset=us-ascii; boundary="abc"
|
sl@0
|
1072 |
// ----------------------------------------------------------------------------
|
sl@0
|
1073 |
void
|
sl@0
|
1074 |
MultipartParser::CutOffContentTypeAttributes( CBodyPart* aBodyPart )
|
sl@0
|
1075 |
{
|
sl@0
|
1076 |
// check on required parameters
|
sl@0
|
1077 |
__ASSERT_ALWAYS( aBodyPart != NULL,
|
sl@0
|
1078 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
1079 |
|
sl@0
|
1080 |
TPtrC8 aContentType( aBodyPart->ContentType() );
|
sl@0
|
1081 |
|
sl@0
|
1082 |
// check if there is a delimiter ';'
|
sl@0
|
1083 |
TInt lenCT = aContentType.Length();
|
sl@0
|
1084 |
TInt offset = aContentType.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text,
|
sl@0
|
1085 |
strlen(Multipart_ContentTypeString_Delimiter_Text) );
|
sl@0
|
1086 |
if (offset != KErrNotFound)
|
sl@0
|
1087 |
{
|
sl@0
|
1088 |
// ; is meant to be the end of the content type value.
|
sl@0
|
1089 |
// cut off content type unrelated part
|
sl@0
|
1090 |
aBodyPart->SetContentType( aContentType.Left( offset ) );
|
sl@0
|
1091 |
|
sl@0
|
1092 |
// extract boundary and charset info
|
sl@0
|
1093 |
if( lenCT > offset )
|
sl@0
|
1094 |
{
|
sl@0
|
1095 |
TPtrC8 unrelated = aContentType.Right( lenCT - offset );
|
sl@0
|
1096 |
TInt lenU = unrelated.Length();
|
sl@0
|
1097 |
|
sl@0
|
1098 |
// check the boundary information
|
sl@0
|
1099 |
TInt offsetB = unrelated.Find( (TUint8*)Multipart_Boundary_Text,
|
sl@0
|
1100 |
strlen(Multipart_Boundary_Text) );
|
sl@0
|
1101 |
if (offsetB != KErrNotFound)
|
sl@0
|
1102 |
{
|
sl@0
|
1103 |
// now, we are at the beginning of "boundary="abc" string.
|
sl@0
|
1104 |
// move to the "abc" part
|
sl@0
|
1105 |
TPtrC8 boundary = unrelated.Right( lenU -
|
sl@0
|
1106 |
offsetB -
|
sl@0
|
1107 |
strlen( Multipart_Boundary_Text ) );
|
sl@0
|
1108 |
TInt lenB = boundary.Length();
|
sl@0
|
1109 |
|
sl@0
|
1110 |
// look for where to end
|
sl@0
|
1111 |
TInt offsetQ = boundary.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text,
|
sl@0
|
1112 |
strlen(Multipart_ContentTypeString_Quotes_Text) );
|
sl@0
|
1113 |
if (offsetQ != KErrNotFound)
|
sl@0
|
1114 |
{
|
sl@0
|
1115 |
// skip the quote (") char
|
sl@0
|
1116 |
boundary.Set( boundary.Right( lenB - offsetQ ) );
|
sl@0
|
1117 |
}
|
sl@0
|
1118 |
|
sl@0
|
1119 |
// look for where to end
|
sl@0
|
1120 |
// check "
|
sl@0
|
1121 |
TInt offsetE = boundary.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text,
|
sl@0
|
1122 |
strlen(Multipart_ContentTypeString_Quotes_Text) );
|
sl@0
|
1123 |
if (offsetE == KErrNotFound)
|
sl@0
|
1124 |
{
|
sl@0
|
1125 |
// check ;
|
sl@0
|
1126 |
offsetE = boundary.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text,
|
sl@0
|
1127 |
strlen(Multipart_ContentTypeString_Delimiter_Text) );
|
sl@0
|
1128 |
}
|
sl@0
|
1129 |
if (offsetE != KErrNotFound)
|
sl@0
|
1130 |
{
|
sl@0
|
1131 |
boundary.Set( boundary.Left( offsetE ) );
|
sl@0
|
1132 |
}
|
sl@0
|
1133 |
|
sl@0
|
1134 |
// set it on to the input parameter
|
sl@0
|
1135 |
aBodyPart->SetBoundary( boundary );
|
sl@0
|
1136 |
} // end of if (offsetB != KErrNotFound)
|
sl@0
|
1137 |
|
sl@0
|
1138 |
// check the charset information
|
sl@0
|
1139 |
TInt offsetCh = unrelated.Find( (TUint8*)Multipart_Charset_Text,
|
sl@0
|
1140 |
strlen(Multipart_Charset_Text) );
|
sl@0
|
1141 |
if (offsetCh != KErrNotFound)
|
sl@0
|
1142 |
{
|
sl@0
|
1143 |
// now, we are at the beginning of "charset=us-ascii" string.
|
sl@0
|
1144 |
// move to the us-ascii part
|
sl@0
|
1145 |
TPtrC8 charset = unrelated.Right( lenU -
|
sl@0
|
1146 |
offsetCh -
|
sl@0
|
1147 |
strlen( Multipart_Charset_Text ) );
|
sl@0
|
1148 |
TInt lenCh = charset.Length();
|
sl@0
|
1149 |
|
sl@0
|
1150 |
// look for where to end
|
sl@0
|
1151 |
TInt offsetQ = charset.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text,
|
sl@0
|
1152 |
strlen(Multipart_ContentTypeString_Quotes_Text) );
|
sl@0
|
1153 |
if (offsetQ != KErrNotFound)
|
sl@0
|
1154 |
{
|
sl@0
|
1155 |
// skip the quote (") char
|
sl@0
|
1156 |
charset.Set( charset.Right( lenCh - offsetQ ) );
|
sl@0
|
1157 |
}
|
sl@0
|
1158 |
|
sl@0
|
1159 |
// look for where to end
|
sl@0
|
1160 |
// check "
|
sl@0
|
1161 |
TInt offsetE = charset.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text,
|
sl@0
|
1162 |
strlen(Multipart_ContentTypeString_Quotes_Text) );
|
sl@0
|
1163 |
if (offsetE == KErrNotFound)
|
sl@0
|
1164 |
{
|
sl@0
|
1165 |
// check ;
|
sl@0
|
1166 |
offsetE = charset.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text,
|
sl@0
|
1167 |
strlen(Multipart_ContentTypeString_Delimiter_Text) );
|
sl@0
|
1168 |
}
|
sl@0
|
1169 |
if (offsetE != KErrNotFound)
|
sl@0
|
1170 |
{
|
sl@0
|
1171 |
charset.Set( charset.Left( offsetE ) );
|
sl@0
|
1172 |
}
|
sl@0
|
1173 |
|
sl@0
|
1174 |
// set it on to the input parameter
|
sl@0
|
1175 |
aBodyPart->SetCharset( charset );
|
sl@0
|
1176 |
} // end of if (offsetCh != KErrNotFound)
|
sl@0
|
1177 |
|
sl@0
|
1178 |
} // end of if( lenCT > offset )
|
sl@0
|
1179 |
} // end of if (offset != KErrNotFound)
|
sl@0
|
1180 |
}
|
sl@0
|
1181 |
|
sl@0
|
1182 |
|
sl@0
|
1183 |
// ----------------------------------------------------------------------------
|
sl@0
|
1184 |
// MultipartParser::GetBodyPartUrl
|
sl@0
|
1185 |
//
|
sl@0
|
1186 |
// Builds up the URL which refers to this particular body part
|
sl@0
|
1187 |
// ----------------------------------------------------------------------------
|
sl@0
|
1188 |
HBufC16*
|
sl@0
|
1189 |
MultipartParser::GetBodyPartUrlL( const TDesC8& aContentBase,
|
sl@0
|
1190 |
const TDesC8& aContentLocation,
|
sl@0
|
1191 |
const TDesC16& aResponseUrl )
|
sl@0
|
1192 |
{
|
sl@0
|
1193 |
// check on required parameters
|
sl@0
|
1194 |
__ASSERT_ALWAYS( aResponseUrl.Ptr() != NULL,
|
sl@0
|
1195 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
1196 |
|
sl@0
|
1197 |
// Body url builds up as follows:
|
sl@0
|
1198 |
// Take global (respond header) Content Base header first.
|
sl@0
|
1199 |
// If local (in the body part) Content Base header is present, then
|
sl@0
|
1200 |
// take it as default Content Base header (by overruling global Content
|
sl@0
|
1201 |
// Base header). Check if the Content Location header is either an
|
sl@0
|
1202 |
// absolute or relative URL. If it is an absolute, then ignore the
|
sl@0
|
1203 |
// Content Base, otherwise concatenate them.
|
sl@0
|
1204 |
// Error cases:
|
sl@0
|
1205 |
// No Content Base header + Content Location is relative URL
|
sl@0
|
1206 |
// No Content Location header
|
sl@0
|
1207 |
// Content Base header is relative URL
|
sl@0
|
1208 |
TBool contentBaseInvalid = EFalse;
|
sl@0
|
1209 |
HBufC16* url = NULL;
|
sl@0
|
1210 |
|
sl@0
|
1211 |
if (aContentBase.Ptr())
|
sl@0
|
1212 |
{
|
sl@0
|
1213 |
// Check if it is a relative url
|
sl@0
|
1214 |
if ( MultipartParser::IsUrlRelativeL( aContentBase ) )
|
sl@0
|
1215 |
{
|
sl@0
|
1216 |
// Relative URL is not valid here as base location.
|
sl@0
|
1217 |
contentBaseInvalid = ETrue;
|
sl@0
|
1218 |
}
|
sl@0
|
1219 |
}
|
sl@0
|
1220 |
else
|
sl@0
|
1221 |
{
|
sl@0
|
1222 |
// no content base header
|
sl@0
|
1223 |
contentBaseInvalid = ETrue;
|
sl@0
|
1224 |
} // end of if (aContentBase)
|
sl@0
|
1225 |
|
sl@0
|
1226 |
if (contentBaseInvalid)
|
sl@0
|
1227 |
{
|
sl@0
|
1228 |
if( aResponseUrl.Ptr() )
|
sl@0
|
1229 |
{
|
sl@0
|
1230 |
// Copy response url
|
sl@0
|
1231 |
TInt lenU = aResponseUrl.Length();
|
sl@0
|
1232 |
url = HBufC::NewLC( lenU + 1 );
|
sl@0
|
1233 |
url->Des().Copy( aResponseUrl );
|
sl@0
|
1234 |
url->Des().ZeroTerminate();
|
sl@0
|
1235 |
}
|
sl@0
|
1236 |
}
|
sl@0
|
1237 |
else
|
sl@0
|
1238 |
{
|
sl@0
|
1239 |
// Copy global content "base" location
|
sl@0
|
1240 |
TInt lenCB = aContentBase.Length();
|
sl@0
|
1241 |
url = HBufC::NewLC( lenCB + 1 );
|
sl@0
|
1242 |
url->Des().Copy( aContentBase );
|
sl@0
|
1243 |
url->Des().ZeroTerminate();
|
sl@0
|
1244 |
} // end of if (contentBaseInvalid)
|
sl@0
|
1245 |
|
sl@0
|
1246 |
// Check if Content Localtion is valid
|
sl@0
|
1247 |
if( aContentLocation.Ptr() )
|
sl@0
|
1248 |
{
|
sl@0
|
1249 |
TInt lenCL = aContentLocation.Length();
|
sl@0
|
1250 |
|
sl@0
|
1251 |
// If the Content Location is an absolute URL, then Content Base value is ignored,
|
sl@0
|
1252 |
// otherwise the absolute path is going to be built unless Content Base is missing
|
sl@0
|
1253 |
if ( !MultipartParser::IsUrlRelativeL( aContentLocation ) )
|
sl@0
|
1254 |
{
|
sl@0
|
1255 |
// clean up memory
|
sl@0
|
1256 |
if( url )
|
sl@0
|
1257 |
{
|
sl@0
|
1258 |
CleanupStack::PopAndDestroy(); // url
|
sl@0
|
1259 |
}
|
sl@0
|
1260 |
|
sl@0
|
1261 |
// fill url with content location
|
sl@0
|
1262 |
url = HBufC::NewL( lenCL + 1 );
|
sl@0
|
1263 |
url->Des().Copy( aContentLocation );
|
sl@0
|
1264 |
url->Des().ZeroTerminate();
|
sl@0
|
1265 |
}
|
sl@0
|
1266 |
else
|
sl@0
|
1267 |
{
|
sl@0
|
1268 |
if( url )
|
sl@0
|
1269 |
{
|
sl@0
|
1270 |
HBufC16* urlN = MultipartParser::UrlRelToAbsL( *url, aContentLocation );
|
sl@0
|
1271 |
|
sl@0
|
1272 |
CleanupStack::PopAndDestroy(); // url
|
sl@0
|
1273 |
|
sl@0
|
1274 |
url = urlN;
|
sl@0
|
1275 |
}
|
sl@0
|
1276 |
}
|
sl@0
|
1277 |
} // end of if( aContentLocation
|
sl@0
|
1278 |
else
|
sl@0
|
1279 |
{
|
sl@0
|
1280 |
if( url )
|
sl@0
|
1281 |
{
|
sl@0
|
1282 |
CleanupStack::Pop(); // url
|
sl@0
|
1283 |
}
|
sl@0
|
1284 |
}
|
sl@0
|
1285 |
|
sl@0
|
1286 |
return url;
|
sl@0
|
1287 |
}
|
sl@0
|
1288 |
|
sl@0
|
1289 |
|
sl@0
|
1290 |
// ----------------------------------------------------------------------------
|
sl@0
|
1291 |
// MultipartParser::IsUrlRelativeL
|
sl@0
|
1292 |
//
|
sl@0
|
1293 |
// ----------------------------------------------------------------------------
|
sl@0
|
1294 |
TBool
|
sl@0
|
1295 |
MultipartParser::IsUrlRelativeL( const TDesC8& aUrl )
|
sl@0
|
1296 |
{
|
sl@0
|
1297 |
// check on required parameters
|
sl@0
|
1298 |
__ASSERT_ALWAYS( aUrl.Ptr() != NULL,
|
sl@0
|
1299 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
1300 |
|
sl@0
|
1301 |
TUriParser8 uriParser;
|
sl@0
|
1302 |
|
sl@0
|
1303 |
User::LeaveIfError(uriParser.Parse(aUrl));
|
sl@0
|
1304 |
|
sl@0
|
1305 |
if( uriParser.Extract(EUriScheme).Ptr() )
|
sl@0
|
1306 |
{
|
sl@0
|
1307 |
return EFalse;
|
sl@0
|
1308 |
}
|
sl@0
|
1309 |
else
|
sl@0
|
1310 |
{
|
sl@0
|
1311 |
return ETrue;
|
sl@0
|
1312 |
}
|
sl@0
|
1313 |
}
|
sl@0
|
1314 |
|
sl@0
|
1315 |
|
sl@0
|
1316 |
// ----------------------------------------------------------------------------
|
sl@0
|
1317 |
// MultipartParser::UrlRelToAbsL
|
sl@0
|
1318 |
//
|
sl@0
|
1319 |
// Absolute path is built as : Base + Relative
|
sl@0
|
1320 |
// ----------------------------------------------------------------------------
|
sl@0
|
1321 |
HBufC16*
|
sl@0
|
1322 |
MultipartParser::UrlRelToAbsL( TDesC16& aBase,
|
sl@0
|
1323 |
const TDesC8& aRelativeUrl )
|
sl@0
|
1324 |
{
|
sl@0
|
1325 |
// check on required parameters
|
sl@0
|
1326 |
__ASSERT_ALWAYS( aBase.Ptr() != NULL,
|
sl@0
|
1327 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
1328 |
__ASSERT_ALWAYS( aRelativeUrl.Ptr() != NULL,
|
sl@0
|
1329 |
User::Panic(_L("MultipartParser Panic"), KErrArgument) );
|
sl@0
|
1330 |
|
sl@0
|
1331 |
// length of absolute url
|
sl@0
|
1332 |
TInt len = 0;
|
sl@0
|
1333 |
// length of relative url
|
sl@0
|
1334 |
TInt lenR = 0;
|
sl@0
|
1335 |
TBool appendSlash = EFalse;
|
sl@0
|
1336 |
// path of absolute url
|
sl@0
|
1337 |
TPtrC16 path( NULL, 0 );
|
sl@0
|
1338 |
|
sl@0
|
1339 |
// must to have aRelativeUrl
|
sl@0
|
1340 |
User::LeaveIfNull( (TUint8*)aRelativeUrl.Ptr() );
|
sl@0
|
1341 |
|
sl@0
|
1342 |
TUriParser16 uriParser;
|
sl@0
|
1343 |
User::LeaveIfError( uriParser.Parse( aBase ) );
|
sl@0
|
1344 |
|
sl@0
|
1345 |
// <scheme>://
|
sl@0
|
1346 |
TPtrC16 scheme( uriParser.Extract( EUriScheme ) );
|
sl@0
|
1347 |
// must to have scheme
|
sl@0
|
1348 |
User::LeaveIfNull( (TUint16*)scheme.Ptr() );
|
sl@0
|
1349 |
len += scheme.Length() + SCHEME_SEPARATOR_LENGTH;
|
sl@0
|
1350 |
|
sl@0
|
1351 |
// <user>:<password>@
|
sl@0
|
1352 |
TPtrC16 user( uriParser.Extract( EUriUserinfo ) );
|
sl@0
|
1353 |
if( user.Ptr() )
|
sl@0
|
1354 |
{
|
sl@0
|
1355 |
len += user.Length() + 1;
|
sl@0
|
1356 |
}
|
sl@0
|
1357 |
|
sl@0
|
1358 |
// <host>
|
sl@0
|
1359 |
TPtrC16 host( uriParser.Extract( EUriHost ) );
|
sl@0
|
1360 |
// must to have host
|
sl@0
|
1361 |
User::LeaveIfNull( (TUint16*)host.Ptr() );
|
sl@0
|
1362 |
len += host.Length();
|
sl@0
|
1363 |
|
sl@0
|
1364 |
// :<port>
|
sl@0
|
1365 |
TPtrC16 port( uriParser.Extract( EUriPort ) );
|
sl@0
|
1366 |
if( port.Ptr() )
|
sl@0
|
1367 |
{
|
sl@0
|
1368 |
len += port.Length();
|
sl@0
|
1369 |
}
|
sl@0
|
1370 |
|
sl@0
|
1371 |
// If the relative url begins with "./", remove it
|
sl@0
|
1372 |
TPtrC8 relativeUrl( NULL, 0 );
|
sl@0
|
1373 |
TInt indexD = aRelativeUrl.Locate( DOT_CHAR );
|
sl@0
|
1374 |
TInt indexS = aRelativeUrl.Locate( SLASH_CHAR );
|
sl@0
|
1375 |
if ( indexD == 0 && indexS == 1)
|
sl@0
|
1376 |
{
|
sl@0
|
1377 |
// Found a dot-slash at beginning of relative url
|
sl@0
|
1378 |
relativeUrl.Set( aRelativeUrl.Mid( 2 ) );
|
sl@0
|
1379 |
}
|
sl@0
|
1380 |
else
|
sl@0
|
1381 |
{
|
sl@0
|
1382 |
relativeUrl.Set( aRelativeUrl );
|
sl@0
|
1383 |
}
|
sl@0
|
1384 |
|
sl@0
|
1385 |
lenR = relativeUrl.Length();
|
sl@0
|
1386 |
len += lenR;
|
sl@0
|
1387 |
// If the relative url begins with a slash, then it is an absolute path
|
sl@0
|
1388 |
// Does relative url start with slash?
|
sl@0
|
1389 |
indexS = relativeUrl.Locate( SLASH_CHAR );
|
sl@0
|
1390 |
// no, need to extract path from base url
|
sl@0
|
1391 |
if( indexS != 0 )
|
sl@0
|
1392 |
{
|
sl@0
|
1393 |
// <path>
|
sl@0
|
1394 |
path.Set( uriParser.Extract( EUriPath ) );
|
sl@0
|
1395 |
if( path.Ptr() )
|
sl@0
|
1396 |
{
|
sl@0
|
1397 |
// cut off the file path
|
sl@0
|
1398 |
if ( path.LocateReverse( DOT_CHAR ) )
|
sl@0
|
1399 |
{
|
sl@0
|
1400 |
// case: dir/index.html
|
sl@0
|
1401 |
if ( TInt indexS2 = path.LocateReverse( SLASH_CHAR ) )
|
sl@0
|
1402 |
{
|
sl@0
|
1403 |
// to keep the slash
|
sl@0
|
1404 |
path.Set( path.Left( indexS2 + 1 ) );
|
sl@0
|
1405 |
}
|
sl@0
|
1406 |
// case: index.html
|
sl@0
|
1407 |
else
|
sl@0
|
1408 |
{
|
sl@0
|
1409 |
path.Set( NULL, 0 );
|
sl@0
|
1410 |
}
|
sl@0
|
1411 |
}
|
sl@0
|
1412 |
|
sl@0
|
1413 |
// figure out the end slash
|
sl@0
|
1414 |
if( path.Ptr() )
|
sl@0
|
1415 |
{
|
sl@0
|
1416 |
if( path.LocateReverse( SLASH_CHAR ) != (path.Length() - 1) )
|
sl@0
|
1417 |
{
|
sl@0
|
1418 |
appendSlash = ETrue;
|
sl@0
|
1419 |
}
|
sl@0
|
1420 |
|
sl@0
|
1421 |
len += path.Length();
|
sl@0
|
1422 |
}
|
sl@0
|
1423 |
else
|
sl@0
|
1424 |
{
|
sl@0
|
1425 |
appendSlash = ETrue;
|
sl@0
|
1426 |
}
|
sl@0
|
1427 |
}
|
sl@0
|
1428 |
}
|
sl@0
|
1429 |
// yes, no need to extract path from base url
|
sl@0
|
1430 |
if( appendSlash )
|
sl@0
|
1431 |
{
|
sl@0
|
1432 |
++len;
|
sl@0
|
1433 |
}
|
sl@0
|
1434 |
|
sl@0
|
1435 |
// NULL terminator
|
sl@0
|
1436 |
// In certain operator cases, need to have an extra space(size of a 2-byte NULL terminator)
|
sl@0
|
1437 |
// for proper String Termination
|
sl@0
|
1438 |
len += 2;
|
sl@0
|
1439 |
|
sl@0
|
1440 |
// new absolute url
|
sl@0
|
1441 |
HBufC16* urlAbs = HBufC16::NewL( len );
|
sl@0
|
1442 |
TPtr16 urlAbsPtr = urlAbs->Des();
|
sl@0
|
1443 |
|
sl@0
|
1444 |
// copy base into absolute url
|
sl@0
|
1445 |
|
sl@0
|
1446 |
// scheme
|
sl@0
|
1447 |
urlAbsPtr.Copy( scheme );
|
sl@0
|
1448 |
urlAbsPtr.Append( COLON_CHAR );
|
sl@0
|
1449 |
urlAbsPtr.Append( SLASH_CHAR );
|
sl@0
|
1450 |
urlAbsPtr.Append( SLASH_CHAR );
|
sl@0
|
1451 |
|
sl@0
|
1452 |
// user
|
sl@0
|
1453 |
if( user.Ptr() )
|
sl@0
|
1454 |
{
|
sl@0
|
1455 |
urlAbsPtr.Append( user );
|
sl@0
|
1456 |
urlAbsPtr.Append( AT_CHAR );
|
sl@0
|
1457 |
}
|
sl@0
|
1458 |
|
sl@0
|
1459 |
// host
|
sl@0
|
1460 |
urlAbsPtr.Append( host );
|
sl@0
|
1461 |
|
sl@0
|
1462 |
// port
|
sl@0
|
1463 |
if( port.Ptr() )
|
sl@0
|
1464 |
{
|
sl@0
|
1465 |
urlAbsPtr.Append( COLON_CHAR );
|
sl@0
|
1466 |
urlAbsPtr.Append( port );
|
sl@0
|
1467 |
}
|
sl@0
|
1468 |
|
sl@0
|
1469 |
// path
|
sl@0
|
1470 |
if( path.Ptr() )
|
sl@0
|
1471 |
{
|
sl@0
|
1472 |
urlAbsPtr.Append( path );
|
sl@0
|
1473 |
}
|
sl@0
|
1474 |
|
sl@0
|
1475 |
// slash between path and relative url
|
sl@0
|
1476 |
if( appendSlash )
|
sl@0
|
1477 |
{
|
sl@0
|
1478 |
urlAbsPtr.Append( SLASH_CHAR );
|
sl@0
|
1479 |
}
|
sl@0
|
1480 |
|
sl@0
|
1481 |
// relative url
|
sl@0
|
1482 |
TUint16* relUrlInt = new TUint16[ lenR ];
|
sl@0
|
1483 |
TPtr16 relUrl16( relUrlInt, lenR );
|
sl@0
|
1484 |
relUrl16.Copy( relativeUrl );
|
sl@0
|
1485 |
urlAbsPtr.Append( relUrl16 );
|
sl@0
|
1486 |
delete[] relUrlInt;
|
sl@0
|
1487 |
|
sl@0
|
1488 |
// null terminate
|
sl@0
|
1489 |
urlAbsPtr.ZeroTerminate();
|
sl@0
|
1490 |
|
sl@0
|
1491 |
return urlAbs;
|
sl@0
|
1492 |
}
|
sl@0
|
1493 |
|
sl@0
|
1494 |
// -------------------------------------------------------------------------
|
sl@0
|
1495 |
// Composes multipart/mixed document
|
sl@0
|
1496 |
// -------------------------------------------------------------------------
|
sl@0
|
1497 |
HBufC8*
|
sl@0
|
1498 |
MultipartParser::ComposeMixedL( RPointerArray<CBodyPart>& aBodyArray,
|
sl@0
|
1499 |
const TDesC8& aBoundary,
|
sl@0
|
1500 |
TInt aHeaderMask )
|
sl@0
|
1501 |
{
|
sl@0
|
1502 |
// --(aBoundary)
|
sl@0
|
1503 |
_LIT8(KBoundary, "--%S\r\n");
|
sl@0
|
1504 |
HBufC8* boundary = HBufC8::NewLC( aBoundary.Length() + 4 );
|
sl@0
|
1505 |
boundary->Des().Format( KBoundary, &aBoundary );
|
sl@0
|
1506 |
|
sl@0
|
1507 |
// Calculate the size of this document.
|
sl@0
|
1508 |
TInt bodySize = 0;
|
sl@0
|
1509 |
// a. for each CBodyPart
|
sl@0
|
1510 |
// boundaries + CRLF between headers and body (constant addition)
|
sl@0
|
1511 |
bodySize += (boundary->Length() + strlen(Multipart_CRLF_Text)) * aBodyArray.Count() ;
|
sl@0
|
1512 |
for (TInt i = 0; i < aBodyArray.Count(); i++)
|
sl@0
|
1513 |
{
|
sl@0
|
1514 |
if (!(aBodyArray[i]->Headers().Length() +
|
sl@0
|
1515 |
aBodyArray[i]->Body().Length()))
|
sl@0
|
1516 |
{
|
sl@0
|
1517 |
// one less boundary
|
sl@0
|
1518 |
bodySize -= boundary->Length() + strlen(Multipart_CRLF_Text);
|
sl@0
|
1519 |
// skip empty bodypart
|
sl@0
|
1520 |
continue;
|
sl@0
|
1521 |
}
|
sl@0
|
1522 |
bodySize += aBodyArray[i]->Headers().Length();
|
sl@0
|
1523 |
// ensure there are only 2 CRLFs between header and body
|
sl@0
|
1524 |
if (aBodyArray[i]->Headers().Length() > 0)
|
sl@0
|
1525 |
{
|
sl@0
|
1526 |
TPtrC8 bodyHeaders(aBodyArray[i]->Headers().Ptr(), aBodyArray[i]->Headers().Length());
|
sl@0
|
1527 |
TUint newEnd = bodyHeaders.Length() - 1;
|
sl@0
|
1528 |
while( bodyHeaders[ newEnd ] == '\r' || bodyHeaders[ newEnd ] == '\n' )
|
sl@0
|
1529 |
{
|
sl@0
|
1530 |
--newEnd;
|
sl@0
|
1531 |
--bodySize;
|
sl@0
|
1532 |
}
|
sl@0
|
1533 |
// two CRLFs
|
sl@0
|
1534 |
bodySize += strlen(Multipart_CRLF_Text);
|
sl@0
|
1535 |
}
|
sl@0
|
1536 |
bodySize += aBodyArray[i]->Body().Length();
|
sl@0
|
1537 |
// CRLF (end of body, add one only if there is body AND does not end with CRLF)
|
sl@0
|
1538 |
TPtrC8 bodyBody(aBodyArray[i]->Body().Ptr(), aBodyArray[i]->Body().Length());
|
sl@0
|
1539 |
if (bodyBody.Length() > 0
|
sl@0
|
1540 |
&& bodyBody.Right(2) != TPtrC8((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text)))
|
sl@0
|
1541 |
{
|
sl@0
|
1542 |
bodySize += strlen(Multipart_CRLF_Text);
|
sl@0
|
1543 |
}
|
sl@0
|
1544 |
}
|
sl@0
|
1545 |
// end boundary (boundary - '\r\n' + "--")
|
sl@0
|
1546 |
bodySize += boundary->Length();
|
sl@0
|
1547 |
TInt docSize = bodySize;
|
sl@0
|
1548 |
// calculate the size of Headers
|
sl@0
|
1549 |
_LIT8(KContentType, "Content-Type: multipart/mixed; boundary=\"%S\"\r\n");
|
sl@0
|
1550 |
if ( aHeaderMask & EMultipartTopLevelHeaderContentType )
|
sl@0
|
1551 |
{
|
sl@0
|
1552 |
docSize += MULTIPART_CONTENT_TYPE_LENGTH + 1; // Content-Type: + empty space
|
sl@0
|
1553 |
docSize += KContentType().Length() - 2 + aBoundary.Length(); // multipart/mixed; boundary="{aBoundary}"
|
sl@0
|
1554 |
docSize += strlen(Multipart_CRLF_Text); // eol
|
sl@0
|
1555 |
}
|
sl@0
|
1556 |
if ( aHeaderMask & EMultipartTopLevelHeaderContentLength )
|
sl@0
|
1557 |
{
|
sl@0
|
1558 |
docSize += MULTIPART_CONTENT_LENGTH_LENGTH + 1; // Content-Length: + empty space
|
sl@0
|
1559 |
// calculate number of chars needed to represent bodySize
|
sl@0
|
1560 |
HBufC8* bodySizeSize = HBufC8::NewLC( 16 );
|
sl@0
|
1561 |
bodySizeSize->Des().Num( bodySize );
|
sl@0
|
1562 |
docSize += bodySizeSize->Length(); // content length (bodySize)
|
sl@0
|
1563 |
docSize += strlen(Multipart_CRLF_Text); // eol
|
sl@0
|
1564 |
CleanupStack::PopAndDestroy( bodySizeSize );
|
sl@0
|
1565 |
}
|
sl@0
|
1566 |
if ( aHeaderMask & EMultipartTopLevelHeaderLastModified )
|
sl@0
|
1567 |
{
|
sl@0
|
1568 |
docSize += MULTIPART_LAST_MODIFIED_LENGTH + 1;
|
sl@0
|
1569 |
docSize += MULTIPART_INTERNET_DATE_STRING_LENGTH; // timestamp (fixed length)
|
sl@0
|
1570 |
docSize += strlen(Multipart_CRLF_Text); // eol
|
sl@0
|
1571 |
}
|
sl@0
|
1572 |
// extra CRLF for separating header and body
|
sl@0
|
1573 |
docSize += strlen(Multipart_CRLF_Text);
|
sl@0
|
1574 |
// CALCULATION COMPLETE
|
sl@0
|
1575 |
// at this point, bodySize contains the size of bodyparts, i.e. Content-Length:
|
sl@0
|
1576 |
// and docSize contains the size of the entire document (use it to create HBufC8*
|
sl@0
|
1577 |
// of appropriate size)
|
sl@0
|
1578 |
|
sl@0
|
1579 |
// construct multipart document
|
sl@0
|
1580 |
HBufC8* document = HBufC8::NewLC(docSize);
|
sl@0
|
1581 |
TPtr8 docAppend(document->Des());
|
sl@0
|
1582 |
if ( aHeaderMask & EMultipartTopLevelHeaderContentType )
|
sl@0
|
1583 |
{
|
sl@0
|
1584 |
docAppend.Format( KContentType, &aBoundary );
|
sl@0
|
1585 |
}
|
sl@0
|
1586 |
if ( aHeaderMask & EMultipartTopLevelHeaderContentLength )
|
sl@0
|
1587 |
{
|
sl@0
|
1588 |
_LIT8( KContentLength, "Content-Length: %d\r\n" );
|
sl@0
|
1589 |
docAppend.AppendFormat( KContentLength, bodySize );
|
sl@0
|
1590 |
}
|
sl@0
|
1591 |
if ( aHeaderMask & EMultipartTopLevelHeaderLastModified )
|
sl@0
|
1592 |
{
|
sl@0
|
1593 |
_LIT8( KLastModified, "Last-Modified: %S\r\n" );
|
sl@0
|
1594 |
TTime current;
|
sl@0
|
1595 |
current.UniversalTime();
|
sl@0
|
1596 |
TInternetDate modDate(current.DateTime());
|
sl@0
|
1597 |
HBufC8* dateString = modDate.InternetDateTimeL( TInternetDate::ERfc1123Format );
|
sl@0
|
1598 |
docAppend.AppendFormat( KLastModified, dateString );
|
sl@0
|
1599 |
delete dateString;
|
sl@0
|
1600 |
}
|
sl@0
|
1601 |
// required CRLF
|
sl@0
|
1602 |
docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
|
sl@0
|
1603 |
// BodyParts
|
sl@0
|
1604 |
for (TInt i = 0; i < aBodyArray.Count(); i++)
|
sl@0
|
1605 |
{
|
sl@0
|
1606 |
if (!(aBodyArray[i]->Headers().Length() +
|
sl@0
|
1607 |
aBodyArray[i]->Body().Length()))
|
sl@0
|
1608 |
{
|
sl@0
|
1609 |
// skip empty bodypart
|
sl@0
|
1610 |
continue;
|
sl@0
|
1611 |
}
|
sl@0
|
1612 |
docAppend.Append( *boundary );
|
sl@0
|
1613 |
TInt headerLength = aBodyArray[i]->Headers().Length() - 1;
|
sl@0
|
1614 |
while ( headerLength > 0 &&
|
sl@0
|
1615 |
(aBodyArray[i]->Headers()[headerLength] == '\r'
|
sl@0
|
1616 |
|| aBodyArray[i]->Headers()[headerLength] == '\n' ))
|
sl@0
|
1617 |
{
|
sl@0
|
1618 |
--headerLength;
|
sl@0
|
1619 |
}
|
sl@0
|
1620 |
docAppend.Append( aBodyArray[i]->Headers().Ptr(), headerLength + 1 );
|
sl@0
|
1621 |
|
sl@0
|
1622 |
if ( headerLength > 0 )
|
sl@0
|
1623 |
{
|
sl@0
|
1624 |
docAppend.Append((TUint8*)Multipart_DoubleCRLF_Text, strlen(Multipart_DoubleCRLF_Text));
|
sl@0
|
1625 |
}
|
sl@0
|
1626 |
else
|
sl@0
|
1627 |
{
|
sl@0
|
1628 |
docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
|
sl@0
|
1629 |
}
|
sl@0
|
1630 |
// body
|
sl@0
|
1631 |
docAppend.Append(aBodyArray[i]->Body());
|
sl@0
|
1632 |
// CRLF only if body exists and doesn't end with CRLF
|
sl@0
|
1633 |
TPtrC8 bodyBody(aBodyArray[i]->Body().Ptr(), aBodyArray[i]->Body().Length());
|
sl@0
|
1634 |
if (bodyBody.Length() > 0
|
sl@0
|
1635 |
&& bodyBody.Right(2) != TPtrC8((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text)))
|
sl@0
|
1636 |
{
|
sl@0
|
1637 |
docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
|
sl@0
|
1638 |
}
|
sl@0
|
1639 |
}
|
sl@0
|
1640 |
// end boundary
|
sl@0
|
1641 |
_LIT8(KEndBoundary, "--%S--");
|
sl@0
|
1642 |
docAppend.AppendFormat(KEndBoundary, &aBoundary);
|
sl@0
|
1643 |
CleanupStack::Pop( document );
|
sl@0
|
1644 |
CleanupStack::PopAndDestroy( boundary );
|
sl@0
|
1645 |
return document;
|
sl@0
|
1646 |
}
|