1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_extensions.hxx"
30 
31 #include "log_module.hxx"
32 #include "methodguard.hxx"
33 #include "loghandler.hxx"
34 
35 /** === begin UNO includes === **/
36 #include <com/sun/star/logging/XLogHandler.hpp>
37 #include <com/sun/star/lang/XServiceInfo.hpp>
38 #include <com/sun/star/ucb/AlreadyInitializedException.hpp>
39 #include <com/sun/star/lang/XInitialization.hpp>
40 #include <com/sun/star/lang/IllegalArgumentException.hpp>
41 #include <com/sun/star/util/XStringSubstitution.hpp>
42 /** === end UNO includes === **/
43 
44 #include <tools/diagnose_ex.h>
45 
46 #include <comphelper/componentcontext.hxx>
47 
48 #include <cppuhelper/compbase3.hxx>
49 #include <cppuhelper/basemutex.hxx>
50 
51 #include <osl/thread.h>
52 #include <osl/file.hxx>
53 
54 #include <rtl/strbuf.hxx>
55 
56 #include <memory>
57 
58 //........................................................................
59 namespace logging
60 {
61 //........................................................................
62 
63 	/** === begin UNO using === **/
64     using ::com::sun::star::uno::Reference;
65     using ::com::sun::star::logging::LogRecord;
66     using ::com::sun::star::uno::RuntimeException;
67     using ::com::sun::star::logging::XLogFormatter;
68     using ::com::sun::star::uno::Sequence;
69     using ::com::sun::star::uno::XInterface;
70     using ::com::sun::star::uno::XComponentContext;
71     using ::com::sun::star::logging::XLogHandler;
72     using ::com::sun::star::lang::XServiceInfo;
73     using ::com::sun::star::ucb::AlreadyInitializedException;
74     using ::com::sun::star::lang::XInitialization;
75     using ::com::sun::star::uno::Any;
76     using ::com::sun::star::uno::Exception;
77     using ::com::sun::star::lang::IllegalArgumentException;
78     using ::com::sun::star::uno::UNO_QUERY_THROW;
79     using ::com::sun::star::util::XStringSubstitution;
80     using ::com::sun::star::beans::NamedValue;
81 	/** === end UNO using === **/
82 
83 	//====================================================================
84 	//= FileHandler - declaration
85 	//====================================================================
86     typedef ::cppu::WeakComponentImplHelper3    <   XLogHandler
87                                                 ,   XServiceInfo
88                                                 ,   XInitialization
89                                                 >   FileHandler_Base;
90     class FileHandler   :public ::cppu::BaseMutex
91                         ,public FileHandler_Base
92 	{
93     private:
94         enum FileValidity
95         {
96             /// never attempted to open the file
97             eUnknown,
98             /// file is valid
99             eValid,
100             /// file is invalid
101             eInvalid
102         };
103 
104     private:
105         ::comphelper::ComponentContext  m_aContext;
106         LogHandlerHelper                m_aHandlerHelper;
107         ::rtl::OUString                 m_sFileURL;
108         ::std::auto_ptr< ::osl::File >  m_pFile;
109         FileValidity                    m_eFileValidity;
110 
111     protected:
112         FileHandler( const Reference< XComponentContext >& _rxContext );
113         virtual ~FileHandler();
114 
115         // XLogHandler
116         virtual ::rtl::OUString SAL_CALL getEncoding() throw (RuntimeException);
117         virtual void SAL_CALL setEncoding( const ::rtl::OUString& _encoding ) throw (RuntimeException);
118         virtual Reference< XLogFormatter > SAL_CALL getFormatter() throw (RuntimeException);
119         virtual void SAL_CALL setFormatter( const Reference< XLogFormatter >& _formatter ) throw (RuntimeException);
120         virtual ::sal_Int32 SAL_CALL getLevel() throw (RuntimeException);
121         virtual void SAL_CALL setLevel( ::sal_Int32 _level ) throw (RuntimeException);
122         virtual void SAL_CALL flush(  ) throw (RuntimeException);
123         virtual ::sal_Bool SAL_CALL publish( const LogRecord& Record ) throw (RuntimeException);
124 
125         // XInitialization
126         virtual void SAL_CALL initialize( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >& aArguments ) throw (::com::sun::star::uno::Exception, ::com::sun::star::uno::RuntimeException);
127 
128         // XServiceInfo
129 		virtual ::rtl::OUString SAL_CALL getImplementationName() throw(RuntimeException);
130         virtual ::sal_Bool SAL_CALL supportsService( const ::rtl::OUString& _rServiceName ) throw(RuntimeException);
131         virtual Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames() throw(RuntimeException);
132 
133         // OComponentHelper
134         virtual void SAL_CALL disposing();
135 
136     public:
137         // XServiceInfo - static version
138 		static ::rtl::OUString SAL_CALL getImplementationName_static();
139         static Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames_static();
140         static Reference< XInterface > Create( const Reference< XComponentContext >& _rxContext );
141 
142     public:
143         typedef ComponentMethodGuard< FileHandler > MethodGuard;
144         void    enterMethod( MethodGuard::Access );
145         void    leaveMethod( MethodGuard::Access );
146 
147     private:
148         /** prepares our output file for writing
149         */
150         bool    impl_prepareFile_nothrow();
151 
152         /// writes the given string to our file
153         void    impl_writeString_nothrow( const ::rtl::OString& _rEntry );
154 
155         /** does string substitution on a (usually externally provided) file url
156         */
157         void    impl_doStringsubstitution_nothrow( ::rtl::OUString& _inout_rURL );
158 	};
159 
160     //====================================================================
161 	//= FileHandler - implementation
162 	//====================================================================
163 	//--------------------------------------------------------------------
164     FileHandler::FileHandler( const Reference< XComponentContext >& _rxContext )
165         :FileHandler_Base( m_aMutex )
166         ,m_aContext( _rxContext )
167         ,m_aHandlerHelper( _rxContext, m_aMutex, rBHelper )
168         ,m_sFileURL( )
169         ,m_pFile( )
170         ,m_eFileValidity( eUnknown )
171     {
172     }
173 
174     //--------------------------------------------------------------------
175     FileHandler::~FileHandler()
176     {
177         if ( !rBHelper.bDisposed )
178         {
179             acquire();
180             dispose();
181         }
182     }
183 
184     //--------------------------------------------------------------------
185     bool FileHandler::impl_prepareFile_nothrow()
186     {
187         if ( m_eFileValidity == eUnknown )
188         {
189             m_pFile.reset( new ::osl::File( m_sFileURL ) );
190             // check whether the log file already exists
191             ::osl::DirectoryItem aFileItem;
192             ::osl::DirectoryItem::get( m_sFileURL, aFileItem );
193             ::osl::FileStatus aStatus( FileStatusMask_Validate );
194             if ( ::osl::FileBase::E_None == aFileItem.getFileStatus( aStatus ) )
195                 ::osl::File::remove( m_sFileURL );
196 
197             ::osl::FileBase::RC res = m_pFile->open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
198             m_eFileValidity =   res == ::osl::FileBase::E_None
199                             ?   eValid
200                             :   eInvalid;
201         #if OSL_DEBUG_LEVEL > 0
202             if ( m_eFileValidity == eInvalid )
203             {
204                 ::rtl::OStringBuffer sMessage;
205                 sMessage.append( "FileHandler::impl_prepareFile_nothrow: could not open the designated log file:" );
206                 sMessage.append( "\nURL: " );
207                 sMessage.append( ::rtl::OString( m_sFileURL.getStr(), m_sFileURL.getLength(), osl_getThreadTextEncoding() ) );
208                 sMessage.append( "\nerror code: " );
209                 sMessage.append( (sal_Int32)res );
210                 OSL_ENSURE( false, sMessage.makeStringAndClear() );
211             }
212         #endif
213             if ( m_eFileValidity == eValid )
214             {
215                 ::rtl::OString sHead;
216                 if ( m_aHandlerHelper.getEncodedHead( sHead ) )
217                     impl_writeString_nothrow( sHead );
218             }
219         }
220 
221         return m_eFileValidity == eValid;
222     }
223 
224 	//--------------------------------------------------------------------
225     void FileHandler::impl_writeString_nothrow( const ::rtl::OString& _rEntry )
226     {
227         OSL_PRECOND( m_pFile.get(), "FileHandler::impl_writeString_nothrow: no file!" );
228 
229         sal_uInt64 nBytesToWrite( _rEntry.getLength() );
230         sal_uInt64 nBytesWritten( 0 );
231     #if OSL_DEBUG_LEVEL > 0
232         ::osl::FileBase::RC res =
233     #endif
234         m_pFile->write( _rEntry.getStr(), nBytesToWrite, nBytesWritten );
235         OSL_ENSURE( ( res == ::osl::FileBase::E_None ) && ( nBytesWritten == nBytesToWrite ),
236             "FileHandler::impl_writeString_nothrow: could not write the log entry!" );
237     }
238 
239     //--------------------------------------------------------------------
240     void FileHandler::impl_doStringsubstitution_nothrow( ::rtl::OUString& _inout_rURL )
241     {
242         try
243         {
244             Reference< XStringSubstitution > xStringSubst;
245             if ( m_aContext.createComponent( "com.sun.star.util.PathSubstitution", xStringSubst ) )
246                 _inout_rURL = xStringSubst->substituteVariables( _inout_rURL, true );
247         }
248         catch( const Exception& )
249         {
250             DBG_UNHANDLED_EXCEPTION();
251         }
252     }
253 
254     //--------------------------------------------------------------------
255     void SAL_CALL FileHandler::disposing()
256     {
257         if ( m_eFileValidity == eValid )
258         {
259             ::rtl::OString sTail;
260             if ( m_aHandlerHelper.getEncodedTail( sTail ) )
261                 impl_writeString_nothrow( sTail );
262         }
263 
264         m_pFile.reset( NULL );
265         m_aHandlerHelper.setFormatter( NULL );
266     }
267 
268     //--------------------------------------------------------------------
269     void FileHandler::enterMethod( MethodGuard::Access )
270     {
271         m_aHandlerHelper.enterMethod();
272     }
273 
274     //--------------------------------------------------------------------
275     void FileHandler::leaveMethod( MethodGuard::Access )
276     {
277         m_aMutex.release();
278     }
279 
280     //--------------------------------------------------------------------
281     ::rtl::OUString SAL_CALL FileHandler::getEncoding() throw (RuntimeException)
282     {
283         MethodGuard aGuard( *this );
284         ::rtl::OUString sEncoding;
285         OSL_VERIFY( m_aHandlerHelper.getEncoding( sEncoding ) );
286         return sEncoding;
287     }
288 
289     //--------------------------------------------------------------------
290     void SAL_CALL FileHandler::setEncoding( const ::rtl::OUString& _rEncoding ) throw (RuntimeException)
291     {
292         MethodGuard aGuard( *this );
293         OSL_VERIFY( m_aHandlerHelper.setEncoding( _rEncoding ) );
294     }
295 
296     //--------------------------------------------------------------------
297     Reference< XLogFormatter > SAL_CALL FileHandler::getFormatter() throw (RuntimeException)
298     {
299         MethodGuard aGuard( *this );
300         return m_aHandlerHelper.getFormatter();
301     }
302 
303     //--------------------------------------------------------------------
304     void SAL_CALL FileHandler::setFormatter( const Reference< XLogFormatter >& _rxFormatter ) throw (RuntimeException)
305     {
306         MethodGuard aGuard( *this );
307         m_aHandlerHelper.setFormatter( _rxFormatter );
308     }
309 
310     //--------------------------------------------------------------------
311     ::sal_Int32 SAL_CALL FileHandler::getLevel() throw (RuntimeException)
312     {
313         MethodGuard aGuard( *this );
314         return m_aHandlerHelper.getLevel();
315     }
316 
317     //--------------------------------------------------------------------
318     void SAL_CALL FileHandler::setLevel( ::sal_Int32 _nLevel ) throw (RuntimeException)
319     {
320         MethodGuard aGuard( *this );
321         m_aHandlerHelper.setLevel( _nLevel );
322     }
323 
324     //--------------------------------------------------------------------
325     void SAL_CALL FileHandler::flush(  ) throw (RuntimeException)
326     {
327         MethodGuard aGuard( *this );
328         if(!m_pFile.get())
329         {
330             OSL_PRECOND(false, "FileHandler::flush: no file!");
331             return;
332         }
333         #if OSL_DEBUG_LEVEL > 0
334             ::osl::FileBase::RC res =
335         #endif
336                 m_pFile->sync();
337         OSL_ENSURE(res == ::osl::FileBase::E_None, "FileHandler::flush: Could not sync logfile to filesystem.");
338     }
339 
340     //--------------------------------------------------------------------
341     ::sal_Bool SAL_CALL FileHandler::publish( const LogRecord& _rRecord ) throw (RuntimeException)
342     {
343         MethodGuard aGuard( *this );
344 
345         if ( !impl_prepareFile_nothrow() )
346             return sal_False;
347 
348         ::rtl::OString sEntry;
349         if ( !m_aHandlerHelper.formatForPublishing( _rRecord, sEntry ) )
350             return sal_False;
351 
352         impl_writeString_nothrow( sEntry );
353         return sal_True;
354     }
355 
356     //--------------------------------------------------------------------
357     void SAL_CALL FileHandler::initialize( const Sequence< Any >& _rArguments ) throw (Exception, RuntimeException)
358     {
359         ::osl::MutexGuard aGuard( m_aMutex );
360 
361         if ( m_aHandlerHelper.getIsInitialized() )
362             throw AlreadyInitializedException();
363 
364         if ( _rArguments.getLength() != 1 )
365             throw IllegalArgumentException( ::rtl::OUString(), *this, 1 );
366 
367         Sequence< NamedValue > aSettings;
368         if ( _rArguments[0] >>= m_sFileURL )
369         {
370             // create( [in] string URL );
371             impl_doStringsubstitution_nothrow( m_sFileURL );
372         }
373         else if ( _rArguments[0] >>= aSettings )
374         {
375             // createWithSettings( [in] sequence< ::com::sun::star::beans::NamedValue > Settings )
376             ::comphelper::NamedValueCollection aTypedSettings( aSettings );
377             m_aHandlerHelper.initFromSettings( aTypedSettings );
378 
379             if ( aTypedSettings.get_ensureType( "FileURL", m_sFileURL ) )
380                 impl_doStringsubstitution_nothrow( m_sFileURL );
381         }
382         else
383             throw IllegalArgumentException( ::rtl::OUString(), *this, 1 );
384 
385         m_aHandlerHelper.setIsInitialized();
386     }
387 
388     //--------------------------------------------------------------------
389     ::rtl::OUString SAL_CALL FileHandler::getImplementationName() throw(RuntimeException)
390     {
391         return getImplementationName_static();
392     }
393 
394     //--------------------------------------------------------------------
395     ::sal_Bool SAL_CALL FileHandler::supportsService( const ::rtl::OUString& _rServiceName ) throw(RuntimeException)
396     {
397         const Sequence< ::rtl::OUString > aServiceNames( getSupportedServiceNames() );
398         for (   const ::rtl::OUString* pServiceNames = aServiceNames.getConstArray();
399                 pServiceNames != aServiceNames.getConstArray() + aServiceNames.getLength();
400                 ++pServiceNames
401             )
402             if ( _rServiceName == *pServiceNames )
403                 return sal_True;
404         return sal_False;
405     }
406 
407     //--------------------------------------------------------------------
408     Sequence< ::rtl::OUString > SAL_CALL FileHandler::getSupportedServiceNames() throw(RuntimeException)
409     {
410         return getSupportedServiceNames_static();
411     }
412 
413     //--------------------------------------------------------------------
414     ::rtl::OUString SAL_CALL FileHandler::getImplementationName_static()
415     {
416         return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.extensions.FileHandler" ) );
417     }
418 
419     //--------------------------------------------------------------------
420     Sequence< ::rtl::OUString > SAL_CALL FileHandler::getSupportedServiceNames_static()
421     {
422         Sequence< ::rtl::OUString > aServiceNames(1);
423         aServiceNames[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.logging.FileHandler" ) );
424         return aServiceNames;
425     }
426 
427     //--------------------------------------------------------------------
428     Reference< XInterface > FileHandler::Create( const Reference< XComponentContext >& _rxContext )
429     {
430         return *( new FileHandler( _rxContext ) );
431     }
432 
433     //--------------------------------------------------------------------
434     void createRegistryInfo_FileHandler()
435     {
436         static OAutoRegistration< FileHandler > aAutoRegistration;
437     }
438 
439 //........................................................................
440 } // namespace logging
441 //........................................................................
442