|
1 /* |
|
2 * Copyright (c) 2002-2006 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: |
|
15 * Factory class that creates SMIL Player media renderers |
|
16 * accoring to the MIME type of the media file. |
|
17 * |
|
18 */ |
|
19 |
|
20 |
|
21 #include "SmilPlayerMediaFactory.h" |
|
22 |
|
23 // DRM |
|
24 #include <DRMCommon.h> |
|
25 #include <DRMHelper.h> |
|
26 |
|
27 #include <centralrepository.h> // Central Repository |
|
28 #include <MusicPlayerInternalCRKeys.h> // for Central Repository keys |
|
29 |
|
30 #include <MsgMediaResolver.h> |
|
31 |
|
32 #include <MMediaFactoryFileInfo.h> |
|
33 |
|
34 #include "SmilAudioRenderer.h" |
|
35 #include "SmilImageRenderer.h" |
|
36 #include "SmilDefaultRenderer.h" |
|
37 #include "SmilTextRenderer.h" |
|
38 #include "SmilVideoRenderer.h" |
|
39 |
|
40 #include "SmilSVGRenderer.h" |
|
41 #ifdef FACTORY_DEBUG |
|
42 #include "SmilMediaLogging.h" |
|
43 #endif |
|
44 |
|
45 const TInt KRendererArrayGranularity = 10; |
|
46 _LIT( KSmilPlayerSpace, " " ); |
|
47 |
|
48 // --------------------------------------------------------- |
|
49 // CSmilPlayerMediaFactory::CSmilPlayerMediaFactory |
|
50 // C++ constructor. Initializes class member variables. |
|
51 // --------------------------------------------------------- |
|
52 // |
|
53 CSmilPlayerMediaFactory::CSmilPlayerMediaFactory( CCoeEnv& aCoeEnv, |
|
54 CGraphicsContext* aGc, |
|
55 MMediaFactoryFileInfo* aFileInfo ) : |
|
56 iCoeEnv( aCoeEnv ), |
|
57 iGc( aGc ), |
|
58 iFileInfo( aFileInfo ) |
|
59 { |
|
60 } |
|
61 |
|
62 // --------------------------------------------------------- |
|
63 // CSmilPlayerMediaFactory::ConstructL |
|
64 // --------------------------------------------------------- |
|
65 // |
|
66 void CSmilPlayerMediaFactory::ConstructL() |
|
67 { |
|
68 iRendererArray = new( ELeave ) CRendererArray( KRendererArrayGranularity ); |
|
69 |
|
70 iDrmCommon = DRMCommon::NewL(); |
|
71 iDrmHelper = CDRMHelper::NewL(); |
|
72 |
|
73 iMediaResolver = CMsgMediaResolver::NewL(); |
|
74 |
|
75 CRepository* repository = CRepository::NewLC( KCRUidMusicPlayerFeatures ); |
|
76 |
|
77 TInt value( 0 ); |
|
78 User::LeaveIfError( repository->Get( KRequireDRMInPlayback, value ) ); |
|
79 |
|
80 if ( value ) |
|
81 { |
|
82 iProhibitNonDrmMusic = ETrue; |
|
83 |
|
84 TBuf<1> tmp; // Magic: 1 char to get length of actual value |
|
85 TInt realLen = 0; |
|
86 TInt err = repository->Get( KPlaybackRestrictedMimeTypes, |
|
87 tmp, |
|
88 realLen ); |
|
89 |
|
90 if ( err == KErrOverflow ) |
|
91 { |
|
92 // Prepare list of blocked MIME types |
|
93 iProhibitMimeTypeBuffer = HBufC::NewL( realLen + KSmilPlayerSpace().Length() ); |
|
94 TPtr bufferPtr = iProhibitMimeTypeBuffer->Des(); |
|
95 |
|
96 User::LeaveIfError( repository->Get( KPlaybackRestrictedMimeTypes, bufferPtr ) ); |
|
97 bufferPtr.Append( KSmilPlayerSpace ); |
|
98 } |
|
99 else |
|
100 { |
|
101 User::LeaveIfError( err ); |
|
102 } |
|
103 } |
|
104 |
|
105 CleanupStack::PopAndDestroy( repository ); |
|
106 } |
|
107 |
|
108 // ---------------------------------------------------------------------------- |
|
109 // CSmilPlayerMediaFactory::NewL |
|
110 // ---------------------------------------------------------------------------- |
|
111 EXPORT_C CSmilPlayerMediaFactory* CSmilPlayerMediaFactory::NewL( CCoeEnv& aCoeEnv, |
|
112 CGraphicsContext* aGc, |
|
113 MMediaFactoryFileInfo* aFileInfo ) |
|
114 { |
|
115 CSmilPlayerMediaFactory* self = new( ELeave ) CSmilPlayerMediaFactory( aCoeEnv, |
|
116 aGc, |
|
117 aFileInfo ); |
|
118 CleanupStack::PushL( self ); |
|
119 self->ConstructL(); |
|
120 CleanupStack::Pop( self ); |
|
121 |
|
122 return self; |
|
123 } |
|
124 |
|
125 // --------------------------------------------------------- |
|
126 // CSmilPlayerMediaFactory::~CSmilPlayerMediaFactory |
|
127 // Destructor |
|
128 // --------------------------------------------------------- |
|
129 // |
|
130 CSmilPlayerMediaFactory::~CSmilPlayerMediaFactory() |
|
131 { |
|
132 delete iRendererArray; |
|
133 delete iDrmCommon; |
|
134 delete iDrmHelper; |
|
135 delete iProhibitMimeTypeBuffer; |
|
136 delete iMediaResolver; |
|
137 |
|
138 iFileInfo = NULL; // For LINT |
|
139 iGc = NULL; // For LINT |
|
140 } |
|
141 |
|
142 // --------------------------------------------------------- |
|
143 // CSmilPlayerMediaFactory::SetBaseUrlL |
|
144 // Sets Base URL that is not currently used. |
|
145 // --------------------------------------------------------- |
|
146 // |
|
147 void CSmilPlayerMediaFactory::SetBaseUrlL(const TDesC& aBaseUrl) |
|
148 { |
|
149 #ifdef FACTORY_DEBUG |
|
150 SMILUILOGGER_WRITEF( _L("CSmilPlayerMediaFactory ::SetBaseUrl(%S,...)"), &aBaseUrl ); |
|
151 #endif |
|
152 |
|
153 iBaseUrl.SetTextL(aBaseUrl); |
|
154 } |
|
155 |
|
156 // --------------------------------------------------------- |
|
157 // CSmilPlayerMediaFactory::RequestMediaL |
|
158 // Requests media from given URL. Not supported currently. |
|
159 // --------------------------------------------------------- |
|
160 // |
|
161 void CSmilPlayerMediaFactory::RequestMediaL( const TDesC& /*aUrl*/, |
|
162 MSmilMedia* /*aMedia*/ ) |
|
163 { |
|
164 } |
|
165 |
|
166 // --------------------------------------------------------- |
|
167 // CSmilPlayerMediaFactory::PrefetchMediaL |
|
168 // Called to prefetch media from given URL. Not supported currently. |
|
169 // --------------------------------------------------------- |
|
170 // |
|
171 void CSmilPlayerMediaFactory::PrefetchMediaL( const TDesC& /*aUrl*/ ) |
|
172 { |
|
173 |
|
174 } |
|
175 |
|
176 // --------------------------------------------------------- |
|
177 // CSmilPlayerMediaFactory::CreateRendererL |
|
178 // Creates given type media renderer. Retrieves media file handle |
|
179 // and MIME type for the given media URL from client. Initializes |
|
180 // media factory if not initialized already. Checks the given |
|
181 // MIME type against supported MIME types and if flag to prohibit |
|
182 // all non-DRM audio files is specified then checks if audio is |
|
183 // DRM protected. After this retrieves character set for text type |
|
184 // media renderers and tried to instantiate media renderer. If |
|
185 // there was error on instantiation then calls ResolveError to |
|
186 // handle error. |
|
187 // --------------------------------------------------------- |
|
188 // |
|
189 MSmilMediaFactory::TMediaFactoryError CSmilPlayerMediaFactory::CreateRendererL( const TDesC& aUrl, |
|
190 MSmilMedia* aMedia, |
|
191 MSmilMediaRenderer*& aRender ) |
|
192 { |
|
193 #ifdef FACTORY_DEBUG |
|
194 SMILUILOGGER_WRITEF( _L("CSmilPlayerMediaFactory::CreateRenderer(%S,...)"), &aUrl ); |
|
195 #endif |
|
196 |
|
197 if ( !aMedia ) |
|
198 { |
|
199 User::Leave( KErrArgument ); |
|
200 } |
|
201 |
|
202 TMediaFactoryError returnValue = ENoRenderer; |
|
203 TInt err( KErrNone ); |
|
204 |
|
205 RFile file; |
|
206 TRAP( err, iFileInfo->GetFileHandleL( aUrl, file ) ); |
|
207 |
|
208 if ( err != KErrNone || |
|
209 file.SubSessionHandle() == KNullHandle ) |
|
210 { |
|
211 file.Close(); |
|
212 |
|
213 // File not found. Don't create a renderer. |
|
214 return returnValue; |
|
215 } |
|
216 |
|
217 CleanupClosePushL( file ); |
|
218 |
|
219 TPtrC8 mimeType = iFileInfo->GetMimeTypeL( aUrl ); |
|
220 |
|
221 #ifdef FACTORY_DEBUG |
|
222 TBuf16<100> buffer; |
|
223 buffer.SetLength( mimeType.Length() ); |
|
224 buffer.Copy( mimeType ); |
|
225 SMILUILOGGER_WRITEF( _L("Mimetype = %S"), &buffer ); |
|
226 #endif |
|
227 |
|
228 TMsgMediaType mimeEnum = iMediaResolver->MediaType( mimeType ); |
|
229 |
|
230 #ifdef FACTORY_DEBUG |
|
231 SMILUILOGGER_WRITEF( _L("mimeEnum = %d"), mimeEnum ); |
|
232 #endif |
|
233 |
|
234 User::LeaveIfError( mimeEnum ); // if not found -1 == KErrNotFound |
|
235 |
|
236 //if audio and variatedfeature and ( !DrmProtected ) |
|
237 if( iProhibitNonDrmMusic && |
|
238 mimeEnum == EMsgMediaAudio ) |
|
239 { |
|
240 if ( !PlaybackAllowedL( mimeType, file ) ) |
|
241 { |
|
242 CleanupStack::PopAndDestroy( &file ); |
|
243 return returnValue; |
|
244 } |
|
245 } |
|
246 |
|
247 TUint charset( 0 ); |
|
248 if ( mimeEnum == EMsgMediaText ) |
|
249 { |
|
250 charset = iFileInfo->GetCharacterSetL( aUrl ); |
|
251 } |
|
252 |
|
253 TRAP( err, aRender =InstantiateRendererL( mimeEnum, |
|
254 file, |
|
255 aMedia, |
|
256 charset ) ); |
|
257 |
|
258 #ifdef FACTORY_DEBUG |
|
259 SMILUILOGGER_WRITEF( _L("InstantiateRendererL = %d"), err ); |
|
260 #endif |
|
261 |
|
262 if ( err != KErrNone ) |
|
263 { |
|
264 TRAPD( err2, ResolveErrorL( mimeEnum, file, err, aMedia, aRender ) ); |
|
265 if ( err2 != KErrNone ) |
|
266 { |
|
267 returnValue = ENoRenderer; |
|
268 } |
|
269 else |
|
270 { |
|
271 returnValue = EOk; |
|
272 } |
|
273 } |
|
274 else |
|
275 { |
|
276 returnValue = EOk; |
|
277 } |
|
278 |
|
279 CleanupStack::PopAndDestroy( &file ); |
|
280 return returnValue; |
|
281 } |
|
282 |
|
283 // --------------------------------------------------------- |
|
284 // CSmilPlayerMediaFactory::PrefetchStatus |
|
285 // Returns current prefetch status. Not supported currently. |
|
286 // --------------------------------------------------------- |
|
287 // |
|
288 MSmilMediaFactory::TPrefetchStatus CSmilPlayerMediaFactory::PrefetchStatus( |
|
289 const TDesC& /*aUrl*/, |
|
290 TInt& /*aDown*/, |
|
291 TInt& /*aSize*/ ) |
|
292 { |
|
293 return EPrefetchNone; |
|
294 } |
|
295 |
|
296 // --------------------------------------------------------- |
|
297 // CSmilPlayerMediaFactory::PresentationEnd |
|
298 // Notifies about presentation end. |
|
299 // --------------------------------------------------------- |
|
300 // |
|
301 void CSmilPlayerMediaFactory::PresentationEnd() |
|
302 { |
|
303 #ifdef FACTORY_DEBUG |
|
304 SMILUILOGGER_WRITEF( _L("CSmilPlayerMediaFactory::PresentationEnd()") ); |
|
305 #endif |
|
306 } |
|
307 |
|
308 // --------------------------------------------------------- |
|
309 // CSmilPlayerMediaFactory::RendererDeleted |
|
310 // Notifies about specific renderer deletion. |
|
311 // --------------------------------------------------------- |
|
312 // |
|
313 void CSmilPlayerMediaFactory::RendererDeleted( MSmilMediaRenderer* /*aRenderer*/ ) |
|
314 { |
|
315 #ifdef FACTORY_DEBUG |
|
316 SMILUILOGGER_WRITEF( _L("CSmilPlayerMediaFactory::RendererDeleted()") ); |
|
317 #endif |
|
318 } |
|
319 |
|
320 // --------------------------------------------------------- |
|
321 // CSmilPlayerMediaFactory::PresentationReady() |
|
322 // Notifies that presentation is ready. |
|
323 // --------------------------------------------------------- |
|
324 // |
|
325 void CSmilPlayerMediaFactory::PresentationReady() |
|
326 { |
|
327 #ifdef FACTORY_DEBUG |
|
328 SMILUILOGGER_WRITEF( _L("CSmilPlayerMediaFactory::PresentationReady()") ); |
|
329 #endif |
|
330 } |
|
331 |
|
332 // --------------------------------------------------------- |
|
333 // CSmilPlayerMediaFactory::QueryContentType |
|
334 // Checks if specific MIME type is supported. |
|
335 // --------------------------------------------------------- |
|
336 // |
|
337 TBool CSmilPlayerMediaFactory::QueryContentType( const TDesC& aMimeType ) const |
|
338 { |
|
339 #ifdef FACTORY_DEBUG |
|
340 SMILUILOGGER_WRITEF( _L("CSmilPlayerMediaFactory::QueryContentType(%S,...)"), &aMimeType ); |
|
341 #endif |
|
342 |
|
343 TBool result( EFalse ); |
|
344 |
|
345 HBufC8* mimeType8 = NULL; |
|
346 TRAPD( error, mimeType8 = HBufC8::NewL( aMimeType.Length() ) ); |
|
347 |
|
348 if ( error == KErrNone ) |
|
349 { |
|
350 TPtr8 mimeTypePtr8 = mimeType8->Des(); |
|
351 |
|
352 mimeTypePtr8.Copy( aMimeType ); |
|
353 |
|
354 if ( iMediaResolver->MediaType( *mimeType8 ) != EMsgMediaUnknown ) |
|
355 { |
|
356 result = ETrue; |
|
357 } |
|
358 |
|
359 delete mimeType8; |
|
360 } |
|
361 |
|
362 return result; |
|
363 } |
|
364 |
|
365 // --------------------------------------------------------- |
|
366 // CSmilPlayerMediaFactory::GetRenderers() |
|
367 // Returns an array containing all created renderers. |
|
368 // --------------------------------------------------------- |
|
369 EXPORT_C CRendererArray* CSmilPlayerMediaFactory::GetRenderers() |
|
370 { |
|
371 return iRendererArray; |
|
372 } |
|
373 |
|
374 // --------------------------------------------------------- |
|
375 // CSmilPlayerMediaFactory::InstantiateRendererL |
|
376 // Tries to instantiate specified type media renderer. |
|
377 // --------------------------------------------------------- |
|
378 MSmilMediaRenderer* CSmilPlayerMediaFactory::InstantiateRendererL( TMsgMediaType aMediaType, |
|
379 RFile& aFileHandle, |
|
380 MSmilMedia* aMedia, |
|
381 TUint aCharSet ) |
|
382 { |
|
383 #ifdef FACTORY_DEBUG |
|
384 SMILUILOGGER_ENTERFN( "[SMILUI] Factory: InstantiateRendererL" ) |
|
385 #endif |
|
386 |
|
387 CSmilMediaRendererBase* renderer = NULL; |
|
388 |
|
389 switch ( aMediaType ) |
|
390 { |
|
391 // audio types |
|
392 case EMsgMediaAudio: |
|
393 { |
|
394 #ifdef FACTORY_DEBUG |
|
395 SMILUILOGGER_WRITEF( _L("[SMILUI] Factory: creating audio renderer") ); |
|
396 #endif |
|
397 renderer = CSmilAudioRenderer::NewL( aFileHandle, |
|
398 aMedia, |
|
399 *iDrmCommon, |
|
400 *iDrmHelper ); |
|
401 break; |
|
402 } |
|
403 //image types |
|
404 case EMsgMediaImage: |
|
405 { |
|
406 #ifdef FACTORY_DEBUG |
|
407 SMILUILOGGER_WRITEF( _L("[SMILUI] Factory: creating image renderer") ); |
|
408 #endif |
|
409 |
|
410 renderer = CSmilImageRenderer::NewL( aFileHandle, |
|
411 aMedia, |
|
412 *iDrmCommon, |
|
413 *iDrmHelper ); |
|
414 break; |
|
415 } |
|
416 //text types |
|
417 case EMsgMediaText: |
|
418 case EMsgMediaXhtml: |
|
419 { |
|
420 #ifdef FACTORY_DEBUG |
|
421 SMILUILOGGER_WRITEF( _L("[SMILUI] Factory: creating text renderer") ); |
|
422 #endif |
|
423 |
|
424 renderer = CSmilTextRenderer::NewL( aFileHandle, |
|
425 aMedia, |
|
426 *iDrmCommon, |
|
427 *iDrmHelper, |
|
428 aCharSet, |
|
429 *iGc, |
|
430 aMediaType |
|
431 ); |
|
432 break; |
|
433 } |
|
434 //video types |
|
435 case EMsgMediaVideo: |
|
436 { |
|
437 #ifdef FACTORY_DEBUG |
|
438 SMILUILOGGER_WRITEF( _L("[SMILUI] Factory: creating video renderer") ); |
|
439 #endif |
|
440 |
|
441 renderer = CSmilVideoRenderer::NewL( aFileHandle, |
|
442 aMedia, |
|
443 *iDrmCommon, |
|
444 *iDrmHelper ); |
|
445 break; |
|
446 } |
|
447 // SVG types |
|
448 case EMsgMediaSvg: |
|
449 { |
|
450 #ifdef FACTORY_DEBUG |
|
451 SMILUILOGGER_WRITEF( _L("[SMILUI] Factory: creating svg renderer") ); |
|
452 #endif |
|
453 |
|
454 renderer = CSmilSVGRenderer::NewL( aFileHandle, |
|
455 aMedia, |
|
456 iFileInfo, |
|
457 *iDrmCommon, |
|
458 *iDrmHelper ); |
|
459 break; |
|
460 } |
|
461 case EMsgMediaUnknown: |
|
462 default: |
|
463 { |
|
464 #ifdef FACTORY_DEBUG |
|
465 SMILUILOGGER_WRITEF( _L("[SMILUI] Factory: creating default renderer") ); |
|
466 #endif |
|
467 |
|
468 renderer = CSmilDefaultRenderer::NewL( aFileHandle, |
|
469 aMedia, |
|
470 *iDrmCommon, |
|
471 *iDrmHelper, |
|
472 EMsgMediaUnknown, |
|
473 EFalse ); |
|
474 break; |
|
475 } |
|
476 } |
|
477 |
|
478 CleanupStack::PushL( renderer ); |
|
479 iRendererArray->AppendL( renderer ); |
|
480 CleanupStack::Pop( renderer ); |
|
481 |
|
482 #ifdef FACTORY_DEBUG |
|
483 SMILUILOGGER_LEAVEFN( "[SMILUI] Factory: InstantiateRendererL" ) |
|
484 #endif |
|
485 |
|
486 return renderer; |
|
487 } |
|
488 |
|
489 // --------------------------------------------------------- |
|
490 // CSmilPlayerMediaFactory::ResolveErrorL |
|
491 // Handles error on media renderer creation. |
|
492 // --------------------------------------------------------- |
|
493 // |
|
494 void CSmilPlayerMediaFactory::ResolveErrorL( TMsgMediaType aMediaType, |
|
495 RFile& aFileHandle, |
|
496 TInt aErrorCode, |
|
497 MSmilMedia* aMedia, |
|
498 MSmilMediaRenderer*& aRenderer ) const |
|
499 { |
|
500 #ifdef FACTORY_DEBUG |
|
501 SMILUILOGGER_WRITEF( _L("CSmilPlayerMediaFactory::ResolveErrorL(...,%d)"), aErrorCode ); |
|
502 #endif |
|
503 |
|
504 switch ( aMediaType ) |
|
505 { |
|
506 case EMsgMediaAudio: |
|
507 case EMsgMediaImage: |
|
508 case EMsgMediaText: |
|
509 case EMsgMediaVideo: |
|
510 case EMsgMediaSvg: |
|
511 case EMsgMediaXhtml: |
|
512 { |
|
513 switch( aErrorCode ) |
|
514 { |
|
515 case DRMCommon::EGeneralError: |
|
516 case DRMCommon::EUnknownMIME: |
|
517 case DRMCommon::EVersionNotSupported: |
|
518 case DRMCommon::ESessionError: |
|
519 case DRMCommon::ENoRights: |
|
520 case DRMCommon::ERightsDBCorrupted: |
|
521 case DRMCommon::EUnsupported: |
|
522 case DRMCommon::ERightsExpired: |
|
523 case DRMCommon::EInvalidRights: |
|
524 case DRMCommon::EPaddingFailed: |
|
525 case DRMCommon::EFileError: |
|
526 { |
|
527 aRenderer = CSmilDefaultRenderer::NewL( aFileHandle, |
|
528 aMedia, |
|
529 *iDrmCommon, |
|
530 *iDrmHelper, |
|
531 aMediaType, |
|
532 ETrue ); |
|
533 break; |
|
534 } |
|
535 default: |
|
536 { |
|
537 aRenderer = CSmilDefaultRenderer::NewL( aFileHandle, |
|
538 aMedia, |
|
539 *iDrmCommon, |
|
540 *iDrmHelper, |
|
541 aMediaType ); |
|
542 break; |
|
543 } |
|
544 } |
|
545 break; |
|
546 } |
|
547 default: |
|
548 { |
|
549 break; |
|
550 } |
|
551 } |
|
552 } |
|
553 |
|
554 // --------------------------------------------------------- |
|
555 // CSmilPlayerMediaFactory::PlaybackAllowedL |
|
556 // Determines whether file playing is allowed. |
|
557 // --------------------------------------------------------- |
|
558 // |
|
559 TBool CSmilPlayerMediaFactory::PlaybackAllowedL( const TDesC8& aMimeType, RFile& aFileHandle ) const |
|
560 { |
|
561 TBool result( EFalse ); |
|
562 |
|
563 // Prepare buffer for aMimeType |
|
564 HBufC* mimeBuffer = HBufC::NewLC( aMimeType.Length() + KSmilPlayerSpace().Length() ); |
|
565 TPtr mimeBufferPtr = mimeBuffer->Des(); |
|
566 mimeBufferPtr.Copy( aMimeType ); |
|
567 |
|
568 // FindF() would find "audio/3gpp" in "audio/3gpp2" without |
|
569 // the added space. |
|
570 mimeBufferPtr.Append( KSmilPlayerSpace ); |
|
571 |
|
572 // If result is not KErrNotFound, this MIME-type is indeed on blocked list. |
|
573 if ( iProhibitMimeTypeBuffer->FindF( mimeBufferPtr ) >= 0 ) |
|
574 { |
|
575 TBool value( EFalse ); |
|
576 iDrmCommon->IsProtectedFile( aFileHandle, value); |
|
577 |
|
578 if( value ) |
|
579 { |
|
580 result = ETrue; |
|
581 } |
|
582 } |
|
583 else |
|
584 { |
|
585 result = ETrue; |
|
586 } |
|
587 |
|
588 CleanupStack::PopAndDestroy( mimeBuffer ); |
|
589 |
|
590 return result; |
|
591 } |
|
592 |
|
593 |
|
594 // End of file |