|
1 /* |
|
2 * Copyright (c) 2002-2004 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Parse WebDAV PROPFIND method response body |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 // INCLUDE FILES |
|
20 #include <f32file.h> |
|
21 #include <escapeutils.h> |
|
22 #include <tinternetdate.h> |
|
23 |
|
24 #include "rsfwpropfindparser.h" |
|
25 //#include "rsfwdirent.h" |
|
26 #include "rsfwdirentattr.h" |
|
27 #include "mdebug.h" |
|
28 #include "uri8.h" |
|
29 |
|
30 // ============================ MEMBER FUNCTIONS ============================== |
|
31 CRsfwPropFindParser* CRsfwPropFindParser::NewLC() |
|
32 { |
|
33 CRsfwPropFindParser* self = new (ELeave) CRsfwPropFindParser; |
|
34 CleanupStack::PushL(self); |
|
35 self->ConstructL(); |
|
36 return self; |
|
37 } |
|
38 |
|
39 CRsfwPropFindParser* CRsfwPropFindParser::NewL() |
|
40 { |
|
41 CRsfwPropFindParser* self = NewLC(); |
|
42 CleanupStack::Pop(self); |
|
43 return self; |
|
44 } |
|
45 |
|
46 void CRsfwPropFindParser::ConstructL() |
|
47 { |
|
48 ClearDirEntryL(); |
|
49 iCurrentIsParent = EFalse; |
|
50 } |
|
51 |
|
52 CRsfwPropFindParser::~CRsfwPropFindParser() |
|
53 { |
|
54 delete iDirEntry; |
|
55 delete iContentString; |
|
56 } |
|
57 |
|
58 // ---------------------------------------------------------------------------- |
|
59 // CRsfwPropFindParser::OnStartDocumentL |
|
60 // This method is a callback to indicate the start of the document. |
|
61 // @param aDocParam Specifies the various parameters of the document. |
|
62 // @arg aDocParam.iCharacterSetName The character encoding of the document. |
|
63 // ---------------------------------------------------------------------------- |
|
64 // |
|
65 void CRsfwPropFindParser::OnStartDocumentL( |
|
66 const Xml::RDocumentParameters& /* aDocParam */, |
|
67 TInt aErrCode) |
|
68 { |
|
69 iError = KErrNone; // discard the old error |
|
70 if (!aErrCode) |
|
71 { |
|
72 iParseState = ELooking; |
|
73 } |
|
74 else |
|
75 { |
|
76 User::Leave(aErrCode); |
|
77 } |
|
78 |
|
79 } |
|
80 |
|
81 // ---------------------------------------------------------------------------- |
|
82 // CRsfwPropFindParser::OnStartDocumentL |
|
83 // This method is a callback to indicate the end of the document. |
|
84 // ---------------------------------------------------------------------------- |
|
85 // |
|
86 void CRsfwPropFindParser::OnEndDocumentL(TInt aErrCode) |
|
87 { |
|
88 if (aErrCode) |
|
89 { |
|
90 User::Leave(aErrCode); |
|
91 } |
|
92 } |
|
93 |
|
94 // ---------------------------------------------------------------------------- |
|
95 // CRsfwPropFindParser::OnStartElementL |
|
96 // This method is a callback to indicate an element has been parsed. |
|
97 // @param aElement is a handle to the element's details. |
|
98 // @param aAttributes contains the attributes for the element. |
|
99 // @param aErrorCode is the error code. |
|
100 // If this is not KErrNone then special action may be required. |
|
101 // ---------------------------------------------------------------------------- |
|
102 // |
|
103 void CRsfwPropFindParser::OnStartElementL( |
|
104 const Xml::RTagInfo& aElement, |
|
105 const Xml::RAttributeArray& /* aAttributes */, |
|
106 TInt aErrorCode) |
|
107 { |
|
108 _LIT8(KResponseHeader, "response"); |
|
109 _LIT8(KContentType, "getcontenttype"); |
|
110 _LIT8(KDate, "creationdate"); |
|
111 _LIT8(KModified, "getlastmodified"); |
|
112 _LIT8(KLength, "getcontentlength"); |
|
113 _LIT8(KResourceType, "resourcetype"); |
|
114 _LIT8(KEtag, "getetag"); |
|
115 |
|
116 if (aErrorCode) |
|
117 { |
|
118 User::Leave(aErrorCode); |
|
119 } |
|
120 |
|
121 switch (iParseState) |
|
122 { |
|
123 case EName: |
|
124 break; |
|
125 |
|
126 case EResponse: |
|
127 { |
|
128 _LIT8(KHRef, "href"); |
|
129 if (((aElement.LocalName()).DesC()).Compare(KHRef) == 0) |
|
130 { |
|
131 // href that follows response tag is the name of the file |
|
132 iParseState = EName; |
|
133 } |
|
134 } |
|
135 break; |
|
136 |
|
137 case EModified: |
|
138 break; |
|
139 |
|
140 case ELength: |
|
141 break; |
|
142 |
|
143 case EDate: |
|
144 break; |
|
145 |
|
146 case EResourceType: |
|
147 { |
|
148 _LIT8(KCollection, "collection"); |
|
149 if (((aElement.LocalName()).DesC()).Compare(KCollection) == 0) |
|
150 { |
|
151 iDirEntry->Attr()->SetAtt(KEntryAttDir); |
|
152 } |
|
153 } |
|
154 break; |
|
155 |
|
156 case EContentType: |
|
157 break; |
|
158 |
|
159 case EETag: |
|
160 break; |
|
161 |
|
162 case ELooking: // we are trying to find the next interesting tag |
|
163 if (((aElement.LocalName()).DesC()).Compare(KModified) == 0) |
|
164 { |
|
165 iParseState = EModified; |
|
166 } |
|
167 else if (((aElement.LocalName()).DesC()).Compare(KLength) == 0) |
|
168 { |
|
169 iParseState = ELength; |
|
170 } |
|
171 else if (((aElement.LocalName()).DesC()).Compare(KDate) == 0) |
|
172 { |
|
173 iParseState = EDate; |
|
174 } |
|
175 else if (((aElement.LocalName()).DesC()).Compare(KResourceType) == 0) |
|
176 { |
|
177 iParseState = EResourceType; |
|
178 } |
|
179 else if (((aElement.LocalName()).DesC()).Compare(KContentType) == 0) |
|
180 { |
|
181 iParseState = EContentType; |
|
182 } |
|
183 else if (((aElement.LocalName()).DesC()).Compare(KResponseHeader) == 0) |
|
184 { |
|
185 iParseState = EResponse; |
|
186 } |
|
187 else if (((aElement.LocalName()).DesC()).Compare(KEtag) == 0) |
|
188 { |
|
189 iParseState = EETag; |
|
190 } |
|
191 else |
|
192 { |
|
193 // lint |
|
194 } |
|
195 break; |
|
196 |
|
197 default: |
|
198 break; |
|
199 } |
|
200 } |
|
201 |
|
202 // ---------------------------------------------------------------------------- |
|
203 // CRsfwPropFindParser::OnEndElementL |
|
204 // This method is a callback to indicate that end of element has been reached. |
|
205 // @param aElement is a handle to the element's details. |
|
206 // @param aErrorCode is the error code. |
|
207 // If this is not KErrNone then special action may be required. |
|
208 // ---------------------------------------------------------------------------- |
|
209 // |
|
210 void CRsfwPropFindParser::OnEndElementL(const Xml::RTagInfo& aElement, |
|
211 TInt aErrorCode) |
|
212 { |
|
213 if (aErrorCode) |
|
214 { |
|
215 User::Leave(aErrorCode); |
|
216 } |
|
217 |
|
218 // If we reached </response> we can fill next entry in iDirEntArray |
|
219 _LIT8(KResponse, "*response"); |
|
220 if (((aElement.LocalName()).DesC()).Match(KResponse) != KErrNotFound) |
|
221 { |
|
222 // Save the entry if depth = 0 or depth = 1 and |
|
223 // this is not the parent directory |
|
224 switch (iDepth) |
|
225 { |
|
226 case 0: |
|
227 iDirEntArray->Append(iDirEntry); |
|
228 // ownership transferred |
|
229 iDirEntry = NULL; |
|
230 break; |
|
231 |
|
232 case 1: |
|
233 if (!iCurrentIsParent) |
|
234 { |
|
235 iDirEntArray->Append(iDirEntry); |
|
236 // ownership transferred |
|
237 iDirEntry = NULL; |
|
238 } |
|
239 break; |
|
240 |
|
241 default: |
|
242 break; |
|
243 } |
|
244 |
|
245 if (iCurrentIsParent) |
|
246 { |
|
247 // We have 'seen' the end tag of parent directory |
|
248 iCurrentIsParent = EFalse; |
|
249 } |
|
250 |
|
251 // In any case going through an entry is complete, |
|
252 // reset iDirEntry |
|
253 ClearDirEntryL(); |
|
254 delete iContentString; |
|
255 iContentString = NULL; |
|
256 iParseState = ELooking; |
|
257 } |
|
258 |
|
259 // otherwise we will continue reading |
|
260 // if we have some interesting content |
|
261 if ((iParseState != ELooking) && !iContentString) |
|
262 { |
|
263 iParseState = ELooking; |
|
264 return; |
|
265 } |
|
266 |
|
267 switch (iParseState) |
|
268 { |
|
269 case EName: |
|
270 { |
|
271 // Figure out whether the entry we are currently reading |
|
272 // is the directory itself. |
|
273 // The directory itself is the first one in the reply that |
|
274 // comes from server, |
|
275 // and the last one that our XML-parser passes to us |
|
276 |
|
277 // if the name is fully qualified URI, only take the path |
|
278 TPtrC8 uriPtr = iContentString->Des(); |
|
279 TPtrC8 pathPtr = uriPtr; |
|
280 TUriParser8 uriParser; |
|
281 if (uriParser.Parse(uriPtr) == KErrNone) |
|
282 { |
|
283 pathPtr.Set(uriParser.Extract(EUriPath)); |
|
284 } |
|
285 |
|
286 |
|
287 HBufC* name = DecodeL(pathPtr); |
|
288 CleanupStack::PushL(name); |
|
289 |
|
290 if (name->Compare(*iPropFindPath) == 0) |
|
291 { |
|
292 iCurrentIsParent = ETrue; |
|
293 } |
|
294 else |
|
295 { |
|
296 TPtrC namePtr = name->Des(); |
|
297 if ((namePtr.Length() > 1) && |
|
298 (namePtr[namePtr.Length() - 1] == '/')) |
|
299 { |
|
300 // strip off trailing '/' |
|
301 namePtr.Set(namePtr.Left(namePtr.Length() - 1)); |
|
302 } |
|
303 |
|
304 TInt pos = namePtr.LocateReverse('/'); |
|
305 // Shouldn't be negative as |
|
306 // the path should always start with '/' |
|
307 if ((pos >= 0) && (namePtr.Length() > 1)) |
|
308 { |
|
309 namePtr.Set((namePtr.Right(namePtr.Length() - (pos + 1)))); |
|
310 } |
|
311 iDirEntry->SetNameL(namePtr); |
|
312 } |
|
313 CleanupStack::PopAndDestroy(name); |
|
314 } |
|
315 break; |
|
316 |
|
317 case EModified: |
|
318 { |
|
319 // Webdav sends dates in RFC 822 format |
|
320 // (e.g., "Thu, 19 Dec 2002 13:51:16 GMT"). |
|
321 // We parse them as 8 bit data. |
|
322 TInternetDate inetDate; |
|
323 inetDate.SetDateL(*iContentString); |
|
324 iDirEntry->Attr()->SetModified(inetDate.DateTime()); |
|
325 } |
|
326 break; |
|
327 |
|
328 case ELength: |
|
329 { |
|
330 // Convert to int |
|
331 TLex8 lex(*iContentString); |
|
332 TInt len; |
|
333 User::LeaveIfError(lex.Val(len)); |
|
334 iDirEntry->Attr()->SetSize(len); |
|
335 } |
|
336 break; |
|
337 |
|
338 case EETag: |
|
339 // etag is stored for files |
|
340 if (!(iDirEntry->Attr()->Att() & KEntryAttDir)) |
|
341 { |
|
342 iDirEntry->Attr()->SetETagL(*iContentString); |
|
343 } |
|
344 |
|
345 break; |
|
346 |
|
347 case EContentType: |
|
348 { |
|
349 iDirEntry->Attr()->SetMimeTypeL(*iContentString); |
|
350 } |
|
351 break; |
|
352 |
|
353 default: |
|
354 break; |
|
355 } |
|
356 |
|
357 delete iContentString; |
|
358 iContentString = NULL; |
|
359 iParseState = ELooking; |
|
360 } |
|
361 |
|
362 // ---------------------------------------------------------------------------- |
|
363 // CRsfwPropFindParser::OnContentL |
|
364 // This method is a callback that sends the content of the element. |
|
365 // Not all the content may be returned in one go. |
|
366 // The data may be sent in chunks. |
|
367 // When an OnEndElementL is received there is no more content to be sent. |
|
368 // @param aBytes is the raw content data for the element. |
|
369 // The client is responsible for converting the data to the |
|
370 // required character set if necessary. |
|
371 // In some instances the content may be binary and must not be converted. |
|
372 // @param aErrorCode is the error code. |
|
373 // If this is not KErrNone then special action may be required. |
|
374 // |
|
375 void CRsfwPropFindParser::OnContentL(const TDesC8& aBytes, TInt aErrorCode) |
|
376 { |
|
377 if (aErrorCode) |
|
378 { |
|
379 User::Leave(aErrorCode); |
|
380 } |
|
381 |
|
382 // We want to add to contentstring only if we are in a state |
|
383 // where the content is interesting to us |
|
384 if ((iParseState == EName) || (iParseState == EModified) || |
|
385 (iParseState == ELength) || (iParseState ==EETag) || |
|
386 (iParseState == EContentType)) |
|
387 { |
|
388 if (!iContentString) |
|
389 { |
|
390 iContentString = HBufC8::NewL(aBytes.Length()); |
|
391 TPtr8 string = iContentString->Des(); |
|
392 string.Append(aBytes); |
|
393 } |
|
394 else |
|
395 { |
|
396 iContentString = |
|
397 iContentString->ReAllocL(iContentString->Length() + |
|
398 aBytes.Length()); |
|
399 TPtr8 string = iContentString->Des(); |
|
400 string.Append(aBytes); |
|
401 } |
|
402 } |
|
403 } |
|
404 |
|
405 // ---------------------------------------------------------------------------- |
|
406 // CRsfwPropFindParser::OnStartPrefixMappingL |
|
407 // This method is a notification of the beginning of the scope of a prefix-URI |
|
408 // Namespace mapping. |
|
409 // This method is always called before corresponding OnStartElementL method. |
|
410 // @param aPrefix is the Namespace prefix being declared. |
|
411 // @param aUri is the Namespace URI the prefix is mapped to. |
|
412 // @param aErrorCode is the error code. |
|
413 // If this is not KErrNone then special action may be required. |
|
414 // ---------------------------------------------------------------------------- |
|
415 // |
|
416 void CRsfwPropFindParser::OnStartPrefixMappingL(const RString& /* aPrefix */, |
|
417 const RString& /* aUri */, |
|
418 TInt aErrorCode) |
|
419 { |
|
420 if (aErrorCode) |
|
421 { |
|
422 User::Leave(aErrorCode); |
|
423 } |
|
424 } |
|
425 |
|
426 // ---------------------------------------------------------------------------- |
|
427 // CRsfwPropFindParser::OnEndPrefixMappingL |
|
428 // This method is a notification of end of the scope of a prefix-URI mapping. |
|
429 // This method is called after the corresponding DoEndElementL method. |
|
430 // @param aPrefix is the Namespace prefix that was mapped. |
|
431 // @param aErrorCode is the error code. |
|
432 // If this is not KErrNone then special action may be required. |
|
433 // ---------------------------------------------------------------------------- |
|
434 // |
|
435 void CRsfwPropFindParser::OnEndPrefixMappingL(const RString& /* aPrefix */, |
|
436 TInt aErrorCode) |
|
437 { |
|
438 if (aErrorCode) |
|
439 { |
|
440 User::Leave(aErrorCode); |
|
441 } |
|
442 } |
|
443 |
|
444 // ---------------------------------------------------------------------------- |
|
445 // CRsfwPropFindParser::OnIgnorableWhiteSpaceL |
|
446 // This method is a notification of ignorable whitespace in element content. |
|
447 // @param aBytes are the ignored bytes from the document being parsed. |
|
448 // @param aErrorCode is the error code. |
|
449 // If this is not KErrNone then special action may be required. |
|
450 // ---------------------------------------------------------------------------- |
|
451 // |
|
452 void CRsfwPropFindParser::OnIgnorableWhiteSpaceL(const TDesC8& /* aBytes */, |
|
453 TInt aErrorCode) |
|
454 { |
|
455 if (aErrorCode) |
|
456 { |
|
457 User::Leave(aErrorCode); |
|
458 } |
|
459 } |
|
460 |
|
461 // ---------------------------------------------------------------------------- |
|
462 // CRsfwPropFindParser::OnSkippedEntityL |
|
463 // This method is a notification of a skipped entity. |
|
464 // If the parser encounters an external entity it does not need to expand it - |
|
465 // it can return the entity as aName for the client to deal with. |
|
466 // @param aName is the name of the skipped entity. |
|
467 // @param aErrorCode is the error code. |
|
468 // If this is not KErrNone then special action may be required. |
|
469 // ---------------------------------------------------------------------------- |
|
470 // |
|
471 void CRsfwPropFindParser::OnSkippedEntityL(const RString& /* aName */, |
|
472 TInt aErrorCode) |
|
473 { |
|
474 if (aErrorCode) |
|
475 { |
|
476 User::Leave(aErrorCode); |
|
477 } |
|
478 } |
|
479 |
|
480 // ---------------------------------------------------------------------------- |
|
481 // CRsfwPropFindParser::OnProcessingInstructionL |
|
482 // This method is a receive notification of a processing instruction. |
|
483 // @param aTarget is the processing instruction target. |
|
484 // @param aData is the processing instruction data. If empty none was supplied. |
|
485 // @param aErrorCode is the error code. |
|
486 // If this is not KErrNone then special action may be required. |
|
487 // ---------------------------------------------------------------------------- |
|
488 // |
|
489 void CRsfwPropFindParser::OnProcessingInstructionL(const TDesC8& /* aTarget */, |
|
490 const TDesC8& /* aData */, |
|
491 TInt aErrorCode) |
|
492 { |
|
493 if (aErrorCode) |
|
494 { |
|
495 User::Leave(aErrorCode); |
|
496 } |
|
497 } |
|
498 |
|
499 // ---------------------------------------------------------------------------- |
|
500 // CRsfwPropFindParser::OnError |
|
501 // This method indicates an error has occurred. |
|
502 // @param aErrorCode is the error code |
|
503 // ---------------------------------------------------------------------------- |
|
504 // |
|
505 void CRsfwPropFindParser::OnError(TInt aErrorCode) |
|
506 { |
|
507 DEBUGSTRING(("CRsfwPropFindParser::OnError(%d)", aErrorCode)); |
|
508 iError = aErrorCode; |
|
509 } |
|
510 |
|
511 // ---------------------------------------------------------------------------- |
|
512 // CRsfwPropFindParser::GetExtendedInterface |
|
513 // This method obtains the interface matching the specified uid. |
|
514 // @return 0 if no interface matching the uid is found. |
|
515 // Otherwise, the this pointer cast to that interface. |
|
516 // @param aUid the uid identifying the required interface. |
|
517 // ---------------------------------------------------------------------------- |
|
518 // |
|
519 TAny* CRsfwPropFindParser::GetExtendedInterface(const TInt32 /* aUid */) |
|
520 { |
|
521 return NULL; |
|
522 } |
|
523 |
|
524 // ---------------------------------------------------------------------------- |
|
525 // CRsfwPropFindParser::SetDirEntArray |
|
526 // Set a pointer to the directory entry array to be filled. |
|
527 // ---------------------------------------------------------------------------- |
|
528 // |
|
529 void CRsfwPropFindParser::SetDirEntArray(RPointerArray<CRsfwDirEnt>* aDirEntArray) |
|
530 { |
|
531 iDirEntArray = aDirEntArray; |
|
532 } |
|
533 |
|
534 // ---------------------------------------------------------------------------- |
|
535 // CRsfwPropFindParser::SetTargetDirectory |
|
536 // Set the directory for which PROPFIND was targeted. |
|
537 // ---------------------------------------------------------------------------- |
|
538 // |
|
539 void CRsfwPropFindParser::SetTargetDirectory(const TDesC& aPropFindPath, |
|
540 TInt aDepth) |
|
541 { |
|
542 iPropFindPath = &aPropFindPath; |
|
543 iDepth = aDepth; |
|
544 } |
|
545 |
|
546 // ---------------------------------------------------------------------------- |
|
547 // CRsfwPropFindParser::ClearDirEntryL() |
|
548 // Clear directory entry for later use. |
|
549 // ---------------------------------------------------------------------------- |
|
550 // |
|
551 void CRsfwPropFindParser::ClearDirEntryL() |
|
552 { |
|
553 delete iDirEntry; |
|
554 iDirEntry = NULL; |
|
555 TPtrC noName; |
|
556 iDirEntry = CRsfwDirEnt::NewL(noName, NULL); |
|
557 // Will be changed to directory, if we ran into ´<collection> tag |
|
558 iDirEntry->Attr()->SetAtt(KEntryAttNormal); |
|
559 } |
|
560 |
|
561 // ---------------------------------------------------------------------------- |
|
562 // CRsfwPropFindParser::DecodeL() |
|
563 // First UTF-8 decode and then escape decode data. |
|
564 // ---------------------------------------------------------------------------- |
|
565 // |
|
566 HBufC* CRsfwPropFindParser::DecodeL(const TDesC8& aData) |
|
567 { |
|
568 HBufC8* utf8Data = EscapeUtils::EscapeDecodeL(aData); |
|
569 CleanupStack::PushL(utf8Data); |
|
570 HBufC* data = NULL; |
|
571 // if converting to unicode fails, just return the escapedecoded string. |
|
572 TRAPD(err, data = EscapeUtils::ConvertToUnicodeFromUtf8L(*utf8Data)); |
|
573 if (err) |
|
574 { |
|
575 data = HBufC::NewMaxL(utf8Data->Length()); |
|
576 TPtr dataPtr = data->Des(); |
|
577 dataPtr.Copy(*utf8Data); |
|
578 } |
|
579 CleanupStack::PopAndDestroy(utf8Data); |
|
580 return data; |
|
581 |
|
582 } |
|
583 |
|
584 TInt CRsfwPropFindParser::GetLastError() |
|
585 { |
|
586 return iError; |
|
587 } |
|
588 |
|
589 |
|
590 // End of File |