Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include "filterdet.hxx"
22 : #include "inc/pdfparse.hxx"
23 :
24 : #include <osl/diagnose.h>
25 : #include <osl/file.h>
26 : #include <osl/thread.h>
27 : #include <rtl/digest.h>
28 : #include <rtl/ref.hxx>
29 : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
30 : #include <com/sun/star/beans/XPropertySet.hpp>
31 : #include <com/sun/star/awt/XWindow.hpp>
32 : #include <com/sun/star/awt/XListBox.hpp>
33 : #include <com/sun/star/awt/XDialogEventHandler.hpp>
34 : #include <com/sun/star/awt/XDialogProvider2.hpp>
35 : #include <com/sun/star/awt/XControlContainer.hpp>
36 : #include <com/sun/star/uno/RuntimeException.hpp>
37 : #include <com/sun/star/io/XInputStream.hpp>
38 : #include <com/sun/star/io/XStream.hpp>
39 : #include <com/sun/star/io/XSeekable.hpp>
40 : #include <com/sun/star/io/TempFile.hpp>
41 :
42 : #include <boost/scoped_ptr.hpp>
43 : #include <string.h>
44 :
45 : using namespace com::sun::star;
46 :
47 : namespace pdfi
48 : {
49 :
50 : // TODO(T3): locking/thread safety
51 :
52 : namespace {
53 : typedef ::cppu::WeakComponentImplHelper1<
54 : com::sun::star::awt::XDialogEventHandler > ChooserDialogHandlerBase;
55 : class ChooserDialogHandler : private cppu::BaseMutex,
56 : public ChooserDialogHandlerBase
57 : {
58 : uno::Reference<awt::XListBox> m_xListbox;
59 : uno::Reference<awt::XWindow> m_xWriterText;
60 : uno::Reference<awt::XWindow> m_xDrawText;
61 : uno::Reference<awt::XWindow> m_xImpressText;
62 :
63 : enum{ DRAW_INDEX=0, IMPRESS_INDEX=1, WRITER_INDEX=2 };
64 : void selectionChanged( sal_Int32 nIndex ) const
65 : {
66 : sal_Bool bWriterState(sal_False);
67 : sal_Bool bDrawState(sal_False);
68 : sal_Bool bImpressState(sal_False);
69 : switch(nIndex)
70 : {
71 : default:
72 : OSL_FAIL("Unexpected case!");
73 : break;
74 : case DRAW_INDEX:
75 : bDrawState=sal_True;
76 : break;
77 : case IMPRESS_INDEX:
78 : bImpressState=sal_True;
79 : break;
80 : case WRITER_INDEX:
81 : bWriterState=sal_True;
82 : break;
83 : }
84 : m_xWriterText->setVisible(bWriterState);
85 : m_xDrawText->setVisible(bDrawState);
86 : m_xImpressText->setVisible(bImpressState);
87 : }
88 : public:
89 : ChooserDialogHandler() :
90 : ChooserDialogHandlerBase(m_aMutex),
91 : m_xListbox(),
92 : m_xWriterText(),
93 : m_xDrawText(),
94 : m_xImpressText()
95 : {}
96 :
97 : void initControls( const uno::Reference<awt::XControlContainer>& xControls,
98 : const OUString& rFilename )
99 : {
100 : m_xListbox.set(xControls->getControl(
101 : OUString( "ListBox" )),
102 : uno::UNO_QUERY_THROW );
103 : m_xWriterText.set(xControls->getControl(
104 : OUString( "InfoWriter" )),
105 : uno::UNO_QUERY_THROW );
106 : m_xImpressText.set(xControls->getControl(
107 : OUString( "InfoImpress" )),
108 : uno::UNO_QUERY_THROW );
109 : m_xDrawText.set(xControls->getControl(
110 : OUString( "InfoDraw" )),
111 : uno::UNO_QUERY_THROW );
112 :
113 : uno::Reference<awt::XWindow> xControl;
114 : xControl.set(xControls->getControl(
115 : OUString( "ListBoxWriter" )),
116 : uno::UNO_QUERY_THROW );
117 : xControl->setVisible(sal_False);
118 : xControl.set(xControls->getControl(
119 : OUString( "ListBoxImpress" )),
120 : uno::UNO_QUERY_THROW );
121 : xControl->setVisible(sal_False);
122 : xControl.set(xControls->getControl(
123 : OUString( "ListBoxDraw" )),
124 : uno::UNO_QUERY_THROW );
125 : xControl->setVisible(sal_False);
126 : uno::Reference<beans::XPropertySet> xPropSet(
127 : xControls->getControl(
128 : OUString( "ComboLabel" ))->getModel(),
129 : uno::UNO_QUERY_THROW );
130 : OUString aFilename( rFilename.copy(rFilename.lastIndexOf('/')+1) );
131 : OUString aLabel;
132 : xPropSet->getPropertyValue("Label") >>= aLabel;
133 : const char pFileName[] = "%FILENAME";
134 : aLabel = aLabel.replaceAt(
135 : aLabel.indexOfAsciiL(pFileName,SAL_N_ELEMENTS(pFileName)-1),
136 : SAL_N_ELEMENTS(pFileName)-1,
137 : aFilename );
138 : xPropSet->setPropertyValue("Label",
139 : uno::makeAny(aLabel));
140 :
141 : uno::Sequence<OUString> aListboxItems(3);
142 : aListboxItems[DRAW_INDEX] = OUString( "Drawing" );
143 : aListboxItems[IMPRESS_INDEX] = OUString( "Presentation" );
144 : aListboxItems[WRITER_INDEX] = OUString( "Text Document" );
145 :
146 : m_xListbox->addItems(aListboxItems,0);
147 : m_xListbox->selectItemPos(0,sal_True);
148 : selectionChanged(0);
149 : }
150 :
151 : sal_Int32 getSelectedItem() const
152 : {
153 : return m_xListbox->getSelectedItemPos();
154 : }
155 :
156 : virtual ::sal_Bool SAL_CALL callHandlerMethod( const uno::Reference< awt::XDialog >& /*xDialog*/,
157 : const uno::Any& /*EventObject*/,
158 : const OUString& MethodName ) throw (lang::WrappedTargetException, uno::RuntimeException)
159 : {
160 : (void)MethodName;
161 : OSL_ENSURE( MethodName.compareToAscii("SelectionChanged") == 0,
162 : "Invalid event name" );
163 : selectionChanged(getSelectedItem());
164 : return sal_True;
165 : }
166 :
167 : virtual uno::Sequence< OUString > SAL_CALL getSupportedMethodNames( ) throw (uno::RuntimeException)
168 : {
169 : uno::Sequence< OUString > aMethods(1);
170 : aMethods[0] = OUString( "SelectionChanged" );
171 : return aMethods;
172 : }
173 : };
174 : }
175 :
176 : class FileEmitContext : public pdfparse::EmitContext
177 : {
178 : private:
179 : oslFileHandle m_aReadHandle;
180 : unsigned int m_nReadLen;
181 : uno::Reference< io::XStream > m_xContextStream;
182 : uno::Reference< io::XSeekable > m_xSeek;
183 : uno::Reference< io::XOutputStream > m_xOut;
184 :
185 : public:
186 : FileEmitContext( const OUString& rOrigFile,
187 : const uno::Reference< uno::XComponentContext >& xContext,
188 : const pdfparse::PDFContainer* pTop );
189 : virtual ~FileEmitContext();
190 :
191 : virtual bool write( const void* pBuf, unsigned int nLen );
192 : virtual unsigned int getCurPos();
193 : virtual bool copyOrigBytes( unsigned int nOrigOffset, unsigned int nLen );
194 : virtual unsigned int readOrigBytes( unsigned int nOrigOffset, unsigned int nLen, void* pBuf );
195 :
196 0 : const uno::Reference< io::XStream >& getContextStream() const { return m_xContextStream; }
197 : };
198 :
199 0 : FileEmitContext::FileEmitContext( const OUString& rOrigFile,
200 : const uno::Reference< uno::XComponentContext >& xContext,
201 : const pdfparse::PDFContainer* pTop ) :
202 : pdfparse::EmitContext( pTop ),
203 : m_aReadHandle(NULL),
204 : m_nReadLen(0),
205 : m_xContextStream(),
206 : m_xSeek(),
207 0 : m_xOut()
208 : {
209 0 : m_xContextStream = uno::Reference< io::XStream >(
210 0 : io::TempFile::create(xContext), uno::UNO_QUERY_THROW );
211 0 : m_xOut = m_xContextStream->getOutputStream();
212 0 : m_xSeek = uno::Reference<io::XSeekable>(m_xOut, uno::UNO_QUERY_THROW );
213 :
214 0 : oslFileError aErr = osl_File_E_None;
215 0 : if( (aErr=osl_openFile( rOrigFile.pData,
216 : &m_aReadHandle,
217 0 : osl_File_OpenFlag_Read )) == osl_File_E_None )
218 : {
219 0 : if( (aErr=osl_setFilePos( m_aReadHandle,
220 : osl_Pos_End,
221 0 : 0 )) == osl_File_E_None )
222 : {
223 0 : sal_uInt64 nFileSize = 0;
224 0 : if( (aErr=osl_getFilePos( m_aReadHandle,
225 0 : &nFileSize )) == osl_File_E_None )
226 : {
227 0 : m_nReadLen = static_cast<unsigned int>(nFileSize);
228 : }
229 : }
230 0 : if( aErr != osl_File_E_None )
231 : {
232 0 : osl_closeFile( m_aReadHandle );
233 0 : m_aReadHandle = NULL;
234 : }
235 : }
236 0 : m_bDeflate = true;
237 0 : }
238 :
239 0 : FileEmitContext::~FileEmitContext()
240 : {
241 0 : if( m_aReadHandle )
242 0 : osl_closeFile( m_aReadHandle );
243 0 : }
244 :
245 0 : bool FileEmitContext::write( const void* pBuf, unsigned int nLen )
246 : {
247 0 : if( ! m_xOut.is() )
248 0 : return false;
249 :
250 0 : uno::Sequence< sal_Int8 > aSeq( nLen );
251 0 : memcpy( aSeq.getArray(), pBuf, nLen );
252 0 : m_xOut->writeBytes( aSeq );
253 0 : return true;
254 : }
255 :
256 0 : unsigned int FileEmitContext::getCurPos()
257 : {
258 0 : unsigned int nPos = 0;
259 0 : if( m_xSeek.is() )
260 : {
261 0 : nPos = static_cast<unsigned int>( m_xSeek->getPosition() );
262 : }
263 0 : return nPos;
264 : }
265 :
266 0 : bool FileEmitContext::copyOrigBytes( unsigned int nOrigOffset, unsigned int nLen )
267 : {
268 0 : if( nOrigOffset + nLen > m_nReadLen )
269 0 : return false;
270 :
271 0 : if( osl_setFilePos( m_aReadHandle, osl_Pos_Absolut, nOrigOffset ) != osl_File_E_None )
272 0 : return false;
273 :
274 0 : uno::Sequence< sal_Int8 > aSeq( nLen );
275 :
276 0 : sal_uInt64 nBytesRead = 0;
277 0 : if( osl_readFile( m_aReadHandle,
278 0 : aSeq.getArray(),
279 : nLen,
280 0 : &nBytesRead ) != osl_File_E_None
281 0 : || nBytesRead != static_cast<sal_uInt64>(nLen) )
282 : {
283 0 : return false;
284 : }
285 :
286 0 : m_xOut->writeBytes( aSeq );
287 0 : return true;
288 : }
289 :
290 0 : unsigned int FileEmitContext::readOrigBytes( unsigned int nOrigOffset, unsigned int nLen, void* pBuf )
291 : {
292 0 : if( nOrigOffset + nLen > m_nReadLen )
293 0 : return 0;
294 :
295 0 : if( osl_setFilePos( m_aReadHandle,
296 : osl_Pos_Absolut,
297 0 : nOrigOffset ) != osl_File_E_None )
298 : {
299 0 : return 0;
300 : }
301 :
302 0 : sal_uInt64 nBytesRead = 0;
303 0 : if( osl_readFile( m_aReadHandle,
304 : pBuf,
305 : nLen,
306 0 : &nBytesRead ) != osl_File_E_None )
307 : {
308 0 : return 0;
309 : }
310 0 : return static_cast<unsigned int>(nBytesRead);
311 : }
312 :
313 :
314 : ////////////////////////////////////////////////////////////////////////////////
315 :
316 :
317 0 : PDFDetector::PDFDetector( const uno::Reference< uno::XComponentContext >& xContext) :
318 : PDFDetectorBase( m_aMutex ),
319 0 : m_xContext( xContext )
320 0 : {}
321 :
322 : // XExtendedFilterDetection
323 0 : OUString SAL_CALL PDFDetector::detect( uno::Sequence< beans::PropertyValue >& rFilterData ) throw( uno::RuntimeException )
324 : {
325 0 : osl::MutexGuard const guard( m_aMutex );
326 0 : bool bSuccess = false;
327 :
328 : // get the InputStream carrying the PDF content
329 0 : uno::Reference< io::XInputStream > xInput;
330 0 : uno::Reference< io::XStream > xEmbedStream;
331 0 : OUString aOutFilterName, aOutTypeName;
332 0 : OUString aURL;
333 0 : OUString aPwd;
334 0 : const beans::PropertyValue* pAttribs = rFilterData.getConstArray();
335 0 : sal_Int32 nAttribs = rFilterData.getLength();
336 0 : sal_Int32 nFilterNamePos = -1;
337 0 : sal_Int32 nPwdPos = -1;
338 0 : for( sal_Int32 i = 0; i < nAttribs; i++ )
339 : {
340 : #if OSL_DEBUG_LEVEL > 1
341 : OUString aVal( "<no string>" );
342 : pAttribs[i].Value >>= aVal;
343 : OSL_TRACE( "doDetection: Attrib: %s = %s\n",
344 : OUStringToOString( pAttribs[i].Name, RTL_TEXTENCODING_UTF8 ).getStr(),
345 : OUStringToOString( aVal, RTL_TEXTENCODING_UTF8 ).getStr() );
346 : #endif
347 0 : if ( pAttribs[i].Name == "InputStream" )
348 0 : pAttribs[i].Value >>= xInput;
349 0 : else if ( pAttribs[i].Name == "URL" )
350 0 : pAttribs[i].Value >>= aURL;
351 0 : else if ( pAttribs[i].Name == "FilterName" )
352 0 : nFilterNamePos = i;
353 0 : else if ( pAttribs[i].Name == "Password" )
354 : {
355 0 : nPwdPos = i;
356 0 : pAttribs[i].Value >>= aPwd;
357 : }
358 : }
359 0 : if( xInput.is() )
360 : {
361 0 : uno::Reference< io::XSeekable > xSeek( xInput, uno::UNO_QUERY );
362 0 : if( xSeek.is() )
363 0 : xSeek->seek( 0 );
364 : // read the first 1024 byte (see PDF reference implementation note 12)
365 0 : const sal_Int32 nHeaderSize = 1024;
366 0 : uno::Sequence< sal_Int8 > aBuf( nHeaderSize );
367 0 : sal_uInt64 nBytes = 0;
368 0 : nBytes = xInput->readBytes( aBuf, nHeaderSize );
369 0 : if( nBytes > 5 )
370 : {
371 0 : const sal_Int8* pBytes = aBuf.getConstArray();
372 0 : for( unsigned int i = 0; i < nBytes-5; i++ )
373 : {
374 0 : if( pBytes[i] == '%' &&
375 0 : pBytes[i+1] == 'P' &&
376 0 : pBytes[i+2] == 'D' &&
377 0 : pBytes[i+3] == 'F' &&
378 0 : pBytes[i+4] == '-' )
379 : {
380 0 : bSuccess = true;
381 0 : break;
382 : }
383 : }
384 : }
385 :
386 : // check for hybrid PDF
387 0 : oslFileHandle aFile = NULL;
388 0 : if( bSuccess &&
389 0 : ( aURL.isEmpty() || !aURL.startsWith( "file:" ) )
390 : )
391 : {
392 0 : sal_uInt64 nWritten = 0;
393 0 : if( osl_createTempFile( NULL, &aFile, &aURL.pData ) != osl_File_E_None )
394 : {
395 0 : bSuccess = false;
396 : }
397 : else
398 : {
399 : #if OSL_DEBUG_LEVEL > 1
400 : OSL_TRACE( "created temp file %s\n",
401 : OUStringToOString( aURL, RTL_TEXTENCODING_UTF8 ).getStr() );
402 : #endif
403 0 : osl_writeFile( aFile, aBuf.getConstArray(), nBytes, &nWritten );
404 :
405 : OSL_ENSURE( nWritten == nBytes, "writing of header bytes failed" );
406 :
407 0 : if( nWritten == nBytes )
408 : {
409 0 : const sal_uInt32 nBufSize = 4096;
410 0 : aBuf = uno::Sequence<sal_Int8>(nBufSize);
411 : // copy the bytes
412 0 : do
413 : {
414 0 : nBytes = xInput->readBytes( aBuf, nBufSize );
415 0 : if( nBytes > 0 )
416 : {
417 0 : osl_writeFile( aFile, aBuf.getConstArray(), nBytes, &nWritten );
418 0 : if( nWritten != nBytes )
419 : {
420 0 : bSuccess = false;
421 0 : break;
422 : }
423 : }
424 : } while( nBytes == nBufSize );
425 : }
426 : }
427 0 : osl_closeFile( aFile );
428 : }
429 0 : OUString aEmbedMimetype;
430 0 : xEmbedStream = getAdditionalStream( aURL, aEmbedMimetype, aPwd, m_xContext, rFilterData, false );
431 0 : if( aFile )
432 0 : osl_removeFile( aURL.pData );
433 0 : if( !aEmbedMimetype.isEmpty() )
434 : {
435 0 : if( aEmbedMimetype.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "application/vnd.oasis.opendocument.text" ) )
436 0 : || aEmbedMimetype.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "application/vnd.oasis.opendocument.text-master" ) ) )
437 0 : aOutFilterName = OUString( "writer_pdf_addstream_import" );
438 0 : else if ( aEmbedMimetype == "application/vnd.oasis.opendocument.presentation" )
439 0 : aOutFilterName = OUString( "impress_pdf_addstream_import" );
440 0 : else if( aEmbedMimetype.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "application/vnd.oasis.opendocument.graphics" ) )
441 0 : || aEmbedMimetype.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "application/vnd.oasis.opendocument.drawing" ) ) )
442 0 : aOutFilterName = OUString( "draw_pdf_addstream_import" );
443 0 : else if ( aEmbedMimetype == "application/vnd.oasis.opendocument.spreadsheet" )
444 0 : aOutFilterName = OUString( "calc_pdf_addstream_import" );
445 0 : }
446 : }
447 :
448 0 : if( bSuccess )
449 : {
450 0 : if( !aOutFilterName.isEmpty() )
451 : {
452 0 : if( nFilterNamePos == -1 )
453 : {
454 0 : nFilterNamePos = nAttribs;
455 0 : rFilterData.realloc( ++nAttribs );
456 0 : rFilterData[ nFilterNamePos ].Name =
457 0 : OUString( "FilterName" );
458 : }
459 0 : aOutTypeName = OUString("pdf_Portable_Document_Format");
460 :
461 : OSL_TRACE( "setting filter name %s, input stream %s\n",
462 : OUStringToOString( aOutFilterName, RTL_TEXTENCODING_UTF8 ).getStr(),
463 : xEmbedStream.is() ? "present" : "not present" );
464 :
465 0 : rFilterData[nFilterNamePos].Value <<= aOutFilterName;
466 0 : if( xEmbedStream.is() )
467 : {
468 0 : rFilterData.realloc( ++nAttribs );
469 0 : rFilterData[nAttribs-1].Name = OUString( "EmbeddedSubstream" );
470 0 : rFilterData[nAttribs-1].Value <<= xEmbedStream;
471 : }
472 0 : if( !aPwd.isEmpty() )
473 : {
474 0 : if( nPwdPos == -1 )
475 : {
476 0 : nPwdPos = nAttribs;
477 0 : rFilterData.realloc( ++nAttribs );
478 0 : rFilterData[ nPwdPos ].Name = OUString( "Password" );
479 : }
480 0 : rFilterData[ nPwdPos ].Value <<= aPwd;
481 : }
482 : }
483 : else
484 : {
485 0 : if( nFilterNamePos == -1 )
486 : {
487 0 : nFilterNamePos = nAttribs;
488 0 : rFilterData.realloc( ++nAttribs );
489 0 : rFilterData[ nFilterNamePos ].Name = OUString( "FilterName" );
490 : }
491 :
492 0 : const sal_Int32 nDocumentType = 0; //const sal_Int32 nDocumentType = queryDocumentTypeDialog(m_xContext,aURL);
493 : if( nDocumentType < 0 )
494 : {
495 : return OUString();
496 : }
497 : else switch( nDocumentType )
498 : {
499 : case 0:
500 0 : rFilterData[nFilterNamePos].Value <<= OUString( "draw_pdf_import" );
501 0 : break;
502 :
503 : case 1:
504 : rFilterData[nFilterNamePos].Value <<= OUString( "impress_pdf_import" );
505 : break;
506 :
507 : case 2:
508 : rFilterData[nFilterNamePos].Value <<= OUString( "writer_pdf_import" );
509 : break;
510 :
511 : default:
512 : OSL_FAIL("Unexpected case");
513 : }
514 :
515 0 : aOutTypeName = OUString("pdf_Portable_Document_Format");
516 : }
517 : }
518 :
519 0 : return aOutTypeName;
520 : }
521 :
522 0 : bool checkDocChecksum( const OUString& rInPDFFileURL,
523 : sal_uInt32 nBytes,
524 : const OUString& rChkSum )
525 : {
526 0 : bool bRet = false;
527 0 : if( rChkSum.getLength() != 2* RTL_DIGEST_LENGTH_MD5 )
528 : {
529 : OSL_TRACE( "checksum of length %d, expected %d\n",
530 : rChkSum.getLength(), 2*RTL_DIGEST_LENGTH_MD5 );
531 0 : return false;
532 : }
533 :
534 : // prepare checksum to test
535 : sal_uInt8 nTestChecksum[ RTL_DIGEST_LENGTH_MD5 ];
536 0 : const sal_Unicode* pChar = rChkSum.getStr();
537 0 : for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ )
538 : {
539 0 : sal_uInt8 nByte = sal_uInt8( ( (*pChar >= '0' && *pChar <= '9') ? *pChar - '0' :
540 0 : ( (*pChar >= 'A' && *pChar <= 'F') ? *pChar - 'A' + 10 :
541 0 : ( (*pChar >= 'a' && *pChar <= 'f') ? *pChar - 'a' + 10 :
542 0 : 0 ) ) ) );
543 0 : nByte <<= 4;
544 0 : pChar++;
545 0 : nByte |= ( (*pChar >= '0' && *pChar <= '9') ? *pChar - '0' :
546 0 : ( (*pChar >= 'A' && *pChar <= 'F') ? *pChar - 'A' + 10 :
547 0 : ( (*pChar >= 'a' && *pChar <= 'f') ? *pChar - 'a' + 10 :
548 0 : 0 ) ) );
549 0 : pChar++;
550 0 : nTestChecksum[i] = nByte;
551 : }
552 :
553 : // open file and calculate actual checksum up to index nBytes
554 : sal_uInt8 nActualChecksum[ RTL_DIGEST_LENGTH_MD5 ];
555 0 : memset( nActualChecksum, 0, sizeof(nActualChecksum) );
556 0 : rtlDigest aActualDigest = rtl_digest_createMD5();
557 0 : oslFileHandle aRead = NULL;
558 0 : oslFileError aErr = osl_File_E_None;
559 0 : if( (aErr = osl_openFile(rInPDFFileURL.pData,
560 : &aRead,
561 0 : osl_File_OpenFlag_Read )) == osl_File_E_None )
562 : {
563 : sal_Int8 aBuf[4096];
564 0 : sal_uInt32 nCur = 0;
565 0 : sal_uInt64 nBytesRead = 0;
566 0 : while( nCur < nBytes )
567 : {
568 0 : sal_uInt32 nPass = (nBytes - nCur) > sizeof( aBuf ) ? sizeof( aBuf ) : nBytes - nCur;
569 0 : if( (aErr = osl_readFile( aRead, aBuf, nPass, &nBytesRead)) != osl_File_E_None
570 0 : || nBytesRead == 0 )
571 : {
572 0 : break;
573 : }
574 0 : nPass = static_cast<sal_uInt32>(nBytesRead);
575 0 : nCur += nPass;
576 0 : rtl_digest_updateMD5( aActualDigest, aBuf, nPass );
577 : }
578 0 : rtl_digest_getMD5( aActualDigest, nActualChecksum, sizeof(nActualChecksum) );
579 0 : osl_closeFile( aRead );
580 : }
581 0 : rtl_digest_destroyMD5( aActualDigest );
582 :
583 : // compare the contents
584 0 : bRet = (0 == memcmp( nActualChecksum, nTestChecksum, sizeof( nActualChecksum ) ));
585 : #if OSL_DEBUG_LEVEL > 1
586 : OSL_TRACE( "test checksum: " );
587 : for( unsigned int i = 0; i < sizeof(nTestChecksum); i++ )
588 : OSL_TRACE( "%.2X", int(nTestChecksum[i]) );
589 : OSL_TRACE( "\n" );
590 : OSL_TRACE( "file checksum: " );
591 : for( unsigned int i = 0; i < sizeof(nActualChecksum); i++ )
592 : OSL_TRACE( "%.2X", int(nActualChecksum[i]) );
593 : OSL_TRACE( "\n" );
594 : #endif
595 0 : return bRet;
596 : }
597 :
598 0 : uno::Reference< io::XStream > getAdditionalStream( const OUString& rInPDFFileURL,
599 : OUString& rOutMimetype,
600 : OUString& io_rPwd,
601 : const uno::Reference<uno::XComponentContext>& xContext,
602 : const uno::Sequence<beans::PropertyValue>& rFilterData,
603 : bool bMayUseUI )
604 : {
605 0 : uno::Reference< io::XStream > xEmbed;
606 0 : OString aPDFFile;
607 0 : OUString aSysUPath;
608 0 : if( osl_getSystemPathFromFileURL( rInPDFFileURL.pData, &aSysUPath.pData ) != osl_File_E_None )
609 0 : return xEmbed;
610 0 : aPDFFile = OUStringToOString( aSysUPath, osl_getThreadTextEncoding() );
611 :
612 0 : pdfparse::PDFReader aParser;
613 0 : boost::scoped_ptr<pdfparse::PDFEntry> pEntry( aParser.read( aPDFFile.getStr() ));
614 0 : if( pEntry )
615 : {
616 0 : pdfparse::PDFFile* pPDFFile = dynamic_cast<pdfparse::PDFFile*>(pEntry.get());
617 0 : if( pPDFFile )
618 : {
619 0 : unsigned int nElements = pPDFFile->m_aSubElements.size();
620 0 : while( nElements-- > 0 )
621 : {
622 0 : pdfparse::PDFTrailer* pTrailer = dynamic_cast<pdfparse::PDFTrailer*>(pPDFFile->m_aSubElements[nElements]);
623 0 : if( pTrailer && pTrailer->m_pDict )
624 : {
625 : // search document checksum entry
626 : boost::unordered_map< OString,
627 : pdfparse::PDFEntry*,
628 0 : OStringHash >::iterator chk;
629 0 : chk = pTrailer->m_pDict->m_aMap.find( "DocChecksum" );
630 0 : if( chk == pTrailer->m_pDict->m_aMap.end() )
631 : {
632 : OSL_TRACE( "no DocChecksum entry" );
633 0 : continue;
634 : }
635 0 : pdfparse::PDFName* pChkSumName = dynamic_cast<pdfparse::PDFName*>(chk->second);
636 0 : if( pChkSumName == NULL )
637 : {
638 : OSL_TRACE( "no name for DocChecksum entry" );
639 0 : continue;
640 : }
641 :
642 : // search for AdditionalStreams entry
643 : boost::unordered_map< OString,
644 : pdfparse::PDFEntry*,
645 0 : OStringHash >::iterator add_stream;
646 0 : add_stream = pTrailer->m_pDict->m_aMap.find( "AdditionalStreams" );
647 0 : if( add_stream == pTrailer->m_pDict->m_aMap.end() )
648 : {
649 : OSL_TRACE( "no AdditionalStreams entry" );
650 0 : continue;
651 : }
652 0 : pdfparse::PDFArray* pStreams = dynamic_cast<pdfparse::PDFArray*>(add_stream->second);
653 0 : if( ! pStreams || pStreams->m_aSubElements.size() < 2 )
654 : {
655 : OSL_TRACE( "AdditionalStreams array too small" );
656 0 : continue;
657 : }
658 :
659 : // check checksum
660 0 : OUString aChkSum = pChkSumName->getFilteredName();
661 0 : if( ! checkDocChecksum( rInPDFFileURL, pTrailer->m_nOffset, aChkSum ) )
662 0 : continue;
663 :
664 : // extract addstream and mimetype
665 0 : pdfparse::PDFName* pMimeType = dynamic_cast<pdfparse::PDFName*>(pStreams->m_aSubElements[0]);
666 0 : pdfparse::PDFObjectRef* pStreamRef = dynamic_cast<pdfparse::PDFObjectRef*>(pStreams->m_aSubElements[1]);
667 :
668 : OSL_ENSURE( pMimeType, "error: no mimetype element\n" );
669 : OSL_ENSURE( pStreamRef, "error: no stream ref element\n" );
670 :
671 0 : if( pMimeType && pStreamRef )
672 : {
673 0 : pdfparse::PDFObject* pObject = pPDFFile->findObject( pStreamRef->m_nNumber, pStreamRef->m_nGeneration );
674 : OSL_ENSURE( pObject, "object not found\n" );
675 0 : if( pObject )
676 : {
677 0 : if( pPDFFile->isEncrypted() )
678 : {
679 0 : bool bAuthenticated = false;
680 0 : if( !io_rPwd.isEmpty() )
681 : {
682 : OString aIsoPwd = OUStringToOString( io_rPwd,
683 0 : RTL_TEXTENCODING_ISO_8859_1 );
684 0 : bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() );
685 : }
686 0 : if( ! bAuthenticated )
687 : {
688 0 : const beans::PropertyValue* pAttribs = rFilterData.getConstArray();
689 0 : sal_Int32 nAttribs = rFilterData.getLength();
690 0 : uno::Reference< task::XInteractionHandler > xIntHdl;
691 0 : for( sal_Int32 i = 0; i < nAttribs; i++ )
692 : {
693 0 : if ( pAttribs[i].Name == "InteractionHandler" )
694 0 : pAttribs[i].Value >>= xIntHdl;
695 : }
696 0 : if( ! bMayUseUI || ! xIntHdl.is() )
697 : {
698 0 : rOutMimetype = pMimeType->getFilteredName();
699 0 : xEmbed.clear();
700 0 : break;
701 : }
702 :
703 0 : OUString aDocName( rInPDFFileURL.copy( rInPDFFileURL.lastIndexOf( sal_Unicode('/') )+1 ) );
704 :
705 0 : bool bEntered = false;
706 0 : do
707 : {
708 0 : bEntered = getPassword( xIntHdl, io_rPwd, ! bEntered, aDocName );
709 : OString aIsoPwd = OUStringToOString( io_rPwd,
710 0 : RTL_TEXTENCODING_ISO_8859_1 );
711 0 : bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() );
712 0 : } while( bEntered && ! bAuthenticated );
713 : }
714 :
715 : OSL_TRACE( "password: %s", bAuthenticated ? "matches" : "does not match" );
716 0 : if( ! bAuthenticated )
717 0 : continue;
718 : }
719 0 : rOutMimetype = pMimeType->getFilteredName();
720 : FileEmitContext aContext( rInPDFFileURL,
721 : xContext,
722 0 : pPDFFile );
723 0 : aContext.m_bDecrypt = pPDFFile->isEncrypted();
724 0 : pObject->writeStream( aContext, pPDFFile );
725 0 : xEmbed = aContext.getContextStream();
726 0 : break; // success
727 : }
728 0 : }
729 : }
730 : }
731 : }
732 : }
733 :
734 : OSL_TRACE( "extracted add stream: mimetype %s\n",
735 : OUStringToOString( rOutMimetype,
736 : RTL_TEXTENCODING_UTF8 ).getStr());
737 0 : return xEmbed;
738 : }
739 :
740 : }
741 :
742 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|