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