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