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