1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/types.h>
21 #include <sal/log.hxx>
22 
23 #include <services/desktop.hxx>
24 #include <protocols.h>
25 #include <general.h>
26 
27 #include <tools/diagnose_ex.h>
28 #include <vcl/svapp.hxx>
29 #include <unotools/tempfile.hxx>
30 #include <com/sun/star/beans/NamedValue.hpp>
31 #include <com/sun/star/beans/PropertyValue.hpp>
32 #include <com/sun/star/frame/theAutoRecovery.hpp>
33 #include <com/sun/star/frame/Desktop.hpp>
34 #include <com/sun/star/frame/FeatureStateEvent.hpp>
35 #include <com/sun/star/frame/XDispatch.hpp>
36 #include <com/sun/star/frame/XSessionManagerListener2.hpp>
37 #include <com/sun/star/frame/XSessionManagerClient.hpp>
38 #include <com/sun/star/frame/XStatusListener.hpp>
39 #include <com/sun/star/lang/EventObject.hpp>
40 #include <com/sun/star/lang/XInitialization.hpp>
41 #include <com/sun/star/util/URLTransformer.hpp>
42 #include <com/sun/star/util/XURLTransformer.hpp>
43 #include <com/sun/star/util/URL.hpp>
44 #include <cppuhelper/implbase.hxx>
45 #include <cppuhelper/supportsservice.hxx>
46 #include <unotools/pathoptions.hxx>
47 
48 #include <com/sun/star/uno/Any.hxx>
49 #include <com/sun/star/uno/Sequence.hxx>
50 
51 using namespace css;
52 using namespace com::sun::star::uno;
53 using namespace com::sun::star::util;
54 using namespace com::sun::star::beans;
55 using namespace framework;
56 
57 namespace {
58 
59 /// @HTML
60 /** @short  implements flat/deep detection of file/stream formats and provides
61             further read/write access to the global office type configuration.
62 
63     @descr  Using of this class makes it possible to get information about the
64             format type of a given URL or stream. The returned internal type name
65             can be used to get more information about this format. Further this
66             class provides full access to the configuration data and following
67             implementations will support some special query modes.
68 
69     @docdate    10.03.2003 by as96863
70 
71     @todo       <ul>
72                     <li>implementation of query mode</li>
73                     <li>simple restore mechanism of last consistent cache state,
74                         if flush failed</li>
75                 </ul>
76  */
77 typedef cppu::WeakImplHelper<
78     css::lang::XInitialization,
79     css::frame::XSessionManagerListener2,
80     css::frame::XStatusListener,
81     css::lang::XServiceInfo> SessionListener_BASE;
82 
83 class SessionListener : public SessionListener_BASE
84 {
85 private:
86     osl::Mutex m_aMutex;
87 
88     /** reference to the uno service manager, which created this service.
89         It can be used to create own needed helper services. */
90     css::uno::Reference< css::uno::XComponentContext > m_xContext;
91 
92     css::uno::Reference< css::frame::XSessionManagerClient > m_rSessionManager;
93 
94     // restore handling
95     bool m_bRestored;
96 
97     bool m_bSessionStoreRequested;
98 
99     bool m_bAllowUserInteractionOnQuit;
100     bool m_bTerminated;
101 
102     // in case of synchronous call the caller should do saveDone() call himself!
103     void StoreSession( bool bAsync );
104 
105     // let session quietly close the documents, remove lock files, store configuration and etc.
106     void QuitSessionQuietly();
107 
108 public:
109     explicit SessionListener(const css::uno::Reference< css::uno::XComponentContext >& xContext);
110 
111     virtual ~SessionListener() override;
112 
getImplementationName()113     virtual OUString SAL_CALL getImplementationName() override
114     {
115         return "com.sun.star.comp.frame.SessionListener";
116     }
117 
supportsService(OUString const & ServiceName)118     virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
119     {
120         return cppu::supportsService(this, ServiceName);
121     }
122 
getSupportedServiceNames()123     virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
124     {
125         return {"com.sun.star.frame.SessionListener"};
126     }
127 
128     virtual void SAL_CALL disposing(const css::lang::EventObject&) override;
129 
130     // XInitialization
131     virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any  >& args) override;
132 
133     // XSessionManagerListener
134     virtual void SAL_CALL doSave( sal_Bool bShutdown, sal_Bool bCancelable ) override;
135     virtual void SAL_CALL approveInteraction( sal_Bool bInteractionGranted ) override;
136    virtual void SAL_CALL shutdownCanceled() override;
137    virtual sal_Bool SAL_CALL doRestore() override;
138 
139     // XSessionManagerListener2
140     virtual void SAL_CALL doQuit() override;
141 
142     // XStatusListener
143     virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& event) override;
144 };
145 
SessionListener(const css::uno::Reference<css::uno::XComponentContext> & rxContext)146 SessionListener::SessionListener(const css::uno::Reference< css::uno::XComponentContext >& rxContext )
147         : m_xContext( rxContext )
148         , m_bRestored( false )
149         , m_bSessionStoreRequested( false )
150         , m_bAllowUserInteractionOnQuit( false )
151         , m_bTerminated( false )
152 {
153     SAL_INFO("fwk.session", "SessionListener::SessionListener");
154 }
155 
~SessionListener()156 SessionListener::~SessionListener()
157 {
158     SAL_INFO("fwk.session", "SessionListener::~SessionListener");
159     if (m_rSessionManager.is())
160     {
161         css::uno::Reference< XSessionManagerListener> me(this);
162         m_rSessionManager->removeSessionManagerListener(me);
163     }
164 }
165 
StoreSession(bool bAsync)166 void SessionListener::StoreSession( bool bAsync )
167 {
168     SAL_INFO("fwk.session", "SessionListener::StoreSession");
169     osl::MutexGuard g(m_aMutex);
170     try
171     {
172         // xd create SERVICENAME_AUTORECOVERY -> frame::XDispatch
173         // xd->dispatch("vnd.sun.star.autorecovery:/doSessionSave, async=bAsync
174         // on stop event m_rSessionManager->saveDone(this); in case of asynchronous call
175         // in case of synchronous call the caller should do saveDone() call himself!
176 
177         css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext );
178         css::uno::Reference< XURLTransformer > xURLTransformer = URLTransformer::create( m_xContext );
179         URL aURL;
180         aURL.Complete = "vnd.sun.star.autorecovery:/doSessionSave";
181         xURLTransformer->parseStrict(aURL);
182 
183         // in case of asynchronous call the notification will trigger saveDone()
184         if ( bAsync )
185             xDispatch->addStatusListener(this, aURL);
186 
187         Sequence< PropertyValue > args(1);
188         args[0] = PropertyValue("DispatchAsynchron",-1,makeAny(bAsync),PropertyState_DIRECT_VALUE);
189         xDispatch->dispatch(aURL, args);
190     } catch (const css::uno::Exception&) {
191         TOOLS_WARN_EXCEPTION("fwk.session", "");
192         // save failed, but tell manager to go on if we haven't yet dispatched the request
193         // in case of synchronous saving the notification is done by the caller
194         if ( bAsync && m_rSessionManager.is() )
195             m_rSessionManager->saveDone(this);
196     }
197 }
198 
QuitSessionQuietly()199 void SessionListener::QuitSessionQuietly()
200 {
201     SAL_INFO("fwk.session", "SessionListener::QuitSessionQuietly");
202     osl::MutexGuard g(m_aMutex);
203     try
204     {
205         // xd create SERVICENAME_AUTORECOVERY -> frame::XDispatch
206         // xd->dispatch("vnd.sun.star.autorecovery:/doSessionQuietQuit, async=false
207         // it is done synchronously to avoid conflict with normal quit process
208 
209         css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext );
210         css::uno::Reference< XURLTransformer > xURLTransformer = URLTransformer::create( m_xContext );
211         URL aURL;
212         aURL.Complete = "vnd.sun.star.autorecovery:/doSessionQuietQuit";
213         xURLTransformer->parseStrict(aURL);
214 
215         Sequence< PropertyValue > args(1);
216         args[0] = PropertyValue("DispatchAsynchron",-1,makeAny(false),PropertyState_DIRECT_VALUE);
217         xDispatch->dispatch(aURL, args);
218     } catch (const css::uno::Exception&) {
219         TOOLS_WARN_EXCEPTION("fwk.session", "");
220     }
221 }
222 
disposing(const css::lang::EventObject & Source)223 void SAL_CALL SessionListener::disposing(const css::lang::EventObject& Source)
224 {
225     SAL_INFO("fwk.session", "SessionListener::disposing");
226     if (Source.Source == m_rSessionManager) {
227         m_rSessionManager.clear();
228     }
229 }
230 
initialize(const Sequence<Any> & args)231 void SAL_CALL SessionListener::initialize(const Sequence< Any  >& args)
232 {
233     SAL_INFO("fwk.session", "SessionListener::initialize");
234 
235     OUString aSMgr("com.sun.star.frame.SessionManagerClient");
236     if ( (args.getLength() == 1) && (args[0] >>= m_bAllowUserInteractionOnQuit) )
237        ;// do nothing
238     else if (args.hasElements())
239     {
240         NamedValue v;
241         for (int i = 0; i < args.getLength(); i++)
242         {
243             if (args[i] >>= v)
244             {
245                 if ( v.Name == "SessionManagerName" )
246                     v.Value >>= aSMgr;
247                 else if ( v.Name == "SessionManager" )
248                     v.Value >>= m_rSessionManager;
249                 else if ( v.Name == "AllowUserInteractionOnQuit" )
250                     v.Value >>= m_bAllowUserInteractionOnQuit;
251             }
252         }
253     }
254 
255     SAL_INFO("fwk.session.debug", "  m_bAllowUserInteractionOnQuit = " << (m_bAllowUserInteractionOnQuit ? "true" : "false"));
256     if (!m_rSessionManager.is())
257         m_rSessionManager = css::uno::Reference< frame::XSessionManagerClient >
258             (m_xContext->getServiceManager()->createInstanceWithContext(aSMgr, m_xContext), UNO_QUERY);
259 
260     if (m_rSessionManager.is())
261     {
262         m_rSessionManager->addSessionManagerListener(this);
263     }
264 }
265 
statusChanged(const frame::FeatureStateEvent & event)266 void SAL_CALL SessionListener::statusChanged(const frame::FeatureStateEvent& event)
267 {
268     SAL_INFO("fwk.session", "SessionListener::statusChanged");
269 
270     SAL_INFO("fwk.session.debug", "  ev.Feature = " << event.FeatureURL.Complete <<
271                                   ", ev.Descript = " << event.FeatureDescriptor);
272     if ( event.FeatureURL.Complete == "vnd.sun.star.autorecovery:/doSessionRestore" )
273     {
274         if (event.FeatureDescriptor == "update")
275             m_bRestored = true; // a document was restored
276 
277     }
278     else if ( event.FeatureURL.Complete == "vnd.sun.star.autorecovery:/doAutoSave" )
279     {   // the "doSessionSave" was never set, look to framework/source/services/autorecovery.cxx
280         //   it always testing but never setting (enum AutoRecovery::E_SESSION_SAVE)
281         if (event.FeatureDescriptor == "update")
282         {
283             if (m_rSessionManager.is())
284                 m_rSessionManager->saveDone(this); // done with save
285         }
286     }
287 }
288 
doRestore()289 sal_Bool SAL_CALL SessionListener::doRestore()
290 {
291     SAL_INFO("fwk.session", "SessionListener::doRestore");
292     osl::MutexGuard g(m_aMutex);
293     m_bRestored = false;
294     try {
295         css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext );
296 
297         URL aURL;
298         aURL.Complete = "vnd.sun.star.autorecovery:/doSessionRestore";
299         css::uno::Reference< XURLTransformer > xURLTransformer(URLTransformer::create(m_xContext));
300         xURLTransformer->parseStrict(aURL);
301         Sequence< PropertyValue > args;
302         xDispatch->addStatusListener(this, aURL);
303         xDispatch->dispatch(aURL, args);
304         m_bRestored = true;
305 
306     } catch (const css::uno::Exception&) {
307         TOOLS_WARN_EXCEPTION("fwk.session", "");
308     }
309 
310     return m_bRestored;
311 }
312 
doSave(sal_Bool bShutdown,sal_Bool)313 void SAL_CALL SessionListener::doSave( sal_Bool bShutdown, sal_Bool /*bCancelable*/ )
314 {
315     SAL_INFO("fwk.session", "SessionListener::doSave");
316 
317     SAL_INFO("fwk.session.debug", "  m_bAllowUserInteractionOnQuit = " << (m_bAllowUserInteractionOnQuit ? "true" : "false") <<
318                                   ", bShutdown = " << (bShutdown ? "true" : "false"));
319     if (bShutdown)
320     {
321         m_bSessionStoreRequested = true; // there is no need to protect it with mutex
322         if ( m_bAllowUserInteractionOnQuit && m_rSessionManager.is() )
323             m_rSessionManager->queryInteraction( static_cast< css::frame::XSessionManagerListener* >( this ) );
324         else
325             StoreSession( true );
326     }
327     // we don't have anything to do so tell the session manager we're done
328     else if( m_rSessionManager.is() )
329         m_rSessionManager->saveDone( this );
330 }
331 
approveInteraction(sal_Bool bInteractionGranted)332 void SAL_CALL SessionListener::approveInteraction( sal_Bool bInteractionGranted )
333 {
334     SAL_INFO("fwk.session", "SessionListener::approveInteraction");
335     // do AutoSave as the first step
336     osl::MutexGuard g(m_aMutex);
337 
338     if ( bInteractionGranted )
339     {
340         // close the office documents in normal way
341         try
342         {
343             // first of all let the session be stored to be sure that we lose no information
344             StoreSession( false );
345 
346             css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext );
347             // honestly: how many implementations of XDesktop will we ever have?
348             // so casting this directly to the implementation
349             Desktop* pDesktop(dynamic_cast<Desktop*>(xDesktop.get()));
350             if(pDesktop)
351             {
352                 SAL_INFO("fwk.session", " XDesktop is a framework::Desktop -- good.");
353                 m_bTerminated = pDesktop->terminateQuickstarterToo();
354             }
355             else
356             {
357                 SAL_WARN("fwk.session", " XDesktop is not a framework::Desktop -- this should never happen.");
358                 m_bTerminated = xDesktop->terminate();
359             }
360 
361             if ( m_rSessionManager.is() )
362             {
363                 // false means that the application closing has been cancelled
364                 if ( !m_bTerminated )
365                     m_rSessionManager->cancelShutdown();
366                 else
367                     m_rSessionManager->interactionDone( this );
368             }
369         }
370         catch( const css::uno::Exception& )
371         {
372             StoreSession( true );
373             m_rSessionManager->interactionDone( this );
374         }
375 
376         if ( m_rSessionManager.is() && m_bTerminated )
377             m_rSessionManager->saveDone(this);
378     }
379     else
380     {
381         StoreSession( true );
382     }
383 }
384 
shutdownCanceled()385 void SessionListener::shutdownCanceled()
386 {
387     SAL_INFO("fwk.session", "SessionListener::shutdownCanceled");
388     // set the state back
389     m_bSessionStoreRequested = false; // there is no need to protect it with mutex
390 
391     if ( m_rSessionManager.is() )
392         m_rSessionManager->saveDone(this);
393 }
394 
doQuit()395 void SessionListener::doQuit()
396 {
397     SAL_INFO("fwk.session", "SessionListener::doQuit");
398     if ( m_bSessionStoreRequested && !m_bTerminated )
399     {
400         // let the session be closed quietly in this case
401         QuitSessionQuietly();
402     }
403 }
404 
405 }
406 
407 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_frame_SessionListener_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)408 com_sun_star_comp_frame_SessionListener_get_implementation(
409     css::uno::XComponentContext *context,
410     css::uno::Sequence<css::uno::Any> const &)
411 {
412     SAL_INFO("fwk.session", "com_sun_star_comp_frame_SessionListener_get_implementation");
413 
414     return cppu::acquire(new SessionListener(context));
415 }
416 
417 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
418