Line data Source code
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 :
21 : #include "services/autorecovery.hxx"
22 : #include <loadenv/loadenv.hxx>
23 :
24 : #include <loadenv/targethelper.hxx>
25 : #include <pattern/frame.hxx>
26 : #include <threadhelp/readguard.hxx>
27 : #include <threadhelp/writeguard.hxx>
28 :
29 : #include <classes/resource.hrc>
30 : #include <classes/fwkresid.hxx>
31 : #include <protocols.h>
32 : #include <properties.h>
33 : #include <services.h>
34 :
35 : #include "helper/mischelper.hxx"
36 :
37 : #include <com/sun/star/ucb/NameClash.hpp>
38 : #include <com/sun/star/container/XNameAccess.hpp>
39 : #include <com/sun/star/frame/GlobalEventBroadcaster.hpp>
40 : #include <com/sun/star/frame/XLoadable.hpp>
41 : #include <com/sun/star/frame/XModel2.hpp>
42 : #include <com/sun/star/frame/ModuleManager.hpp>
43 : #include <com/sun/star/frame/XTitle.hpp>
44 : #include <com/sun/star/frame/XFrame.hpp>
45 : #include <com/sun/star/frame/XDispatchProvider.hpp>
46 : #include <com/sun/star/frame/DispatchResultState.hpp>
47 : #include <com/sun/star/frame/XNotifyingDispatch.hpp>
48 : #include <com/sun/star/frame/XController.hpp>
49 : #include <com/sun/star/frame/XModel.hpp>
50 : #include <com/sun/star/frame/XStorable.hpp>
51 : #include <com/sun/star/util/XModifiable.hpp>
52 : #include <com/sun/star/util/URLTransformer.hpp>
53 : #include <com/sun/star/util/XURLTransformer.hpp>
54 : #include <com/sun/star/frame/XDesktop.hpp>
55 : #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
56 : #include <com/sun/star/container/XNameContainer.hpp>
57 : #include <com/sun/star/util/XChangesNotifier.hpp>
58 : #include <com/sun/star/util/XChangesBatch.hpp>
59 : #include <com/sun/star/beans/XPropertySet.hpp>
60 : #include <com/sun/star/beans/PropertyAttribute.hpp>
61 : #include <com/sun/star/container/XContainerQuery.hpp>
62 : #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
63 : #include <com/sun/star/document/XDocumentRecovery.hpp>
64 : #include <com/sun/star/util/XCloseable.hpp>
65 : #include <com/sun/star/awt/XWindow2.hpp>
66 : #include <com/sun/star/task/XStatusIndicatorFactory.hpp>
67 :
68 : #include <comphelper/configurationhelper.hxx>
69 : #include <comphelper/mediadescriptor.hxx>
70 : #include <comphelper/namedvaluecollection.hxx>
71 : #include <comphelper/processfactory.hxx>
72 : #include <vcl/svapp.hxx>
73 : #include <unotools/pathoptions.hxx>
74 : #include <tools/diagnose_ex.h>
75 : #include <unotools/tempfile.hxx>
76 : #include <ucbhelper/content.hxx>
77 :
78 : #include <osl/time.h>
79 : #include <vcl/msgbox.hxx>
80 : #include <osl/file.hxx>
81 : #include <unotools/bootstrap.hxx>
82 : #include <unotools/configmgr.hxx>
83 : #include <svl/documentlockfile.hxx>
84 : #include <cppuhelper/exc_hlp.hxx>
85 :
86 : #include <tools/urlobj.hxx>
87 :
88 : #include <fwkdllapi.h>
89 :
90 : //_______________________________________________
91 : // namespaces
92 :
93 : using ::com::sun::star::uno::Sequence;
94 : using ::com::sun::star::uno::UNO_QUERY;
95 : using ::com::sun::star::uno::UNO_QUERY_THROW;
96 : using ::com::sun::star::uno::UNO_SET_THROW;
97 : using ::com::sun::star::uno::Reference;
98 : using ::com::sun::star::uno::Any;
99 : using ::com::sun::star::beans::PropertyValue;
100 : using ::com::sun::star::container::XEnumeration;
101 : using ::com::sun::star::document::XDocumentRecovery;
102 : using ::com::sun::star::frame::ModuleManager;
103 : using ::com::sun::star::frame::XModel2;
104 : using ::com::sun::star::frame::XModel;
105 : using ::com::sun::star::frame::XFrame;
106 : using ::com::sun::star::frame::XController2;
107 : using ::com::sun::star::frame::XLoadable;
108 : using ::com::sun::star::frame::XStorable;
109 : using ::com::sun::star::lang::XComponent;
110 :
111 :
112 : namespace framework
113 : {
114 :
115 : //-----------------------------------------------
116 : // recovery.xcu
117 : static const char CFG_PACKAGE_RECOVERY[] = "org.openoffice.Office.Recovery/";
118 : static const char CFG_ENTRY_RECOVERYLIST[] = "RecoveryList";
119 : static const char CFG_PATH_RECOVERYINFO[] = "RecoveryInfo";
120 : static const char CFG_ENTRY_CRASHED[] = "Crashed";
121 : static const char CFG_ENTRY_SESSIONDATA[] = "SessionData";
122 :
123 : static const char CFG_ENTRY_AUTOSAVE_ENABLED[] = "AutoSave/Enabled";
124 : static const char CFG_ENTRY_AUTOSAVE_TIMEINTERVALL[] = "AutoSave/TimeIntervall"; //sic!
125 :
126 : static const char CFG_PATH_AUTOSAVE[] = "AutoSave";
127 : static const char CFG_ENTRY_MINSPACE_DOCSAVE[] = "MinSpaceDocSave";
128 : static const char CFG_ENTRY_MINSPACE_CONFIGSAVE[] = "MinSpaceConfigSave";
129 :
130 : static const char CFG_PACKAGE_MODULES[] = "org.openoffice.Setup/Office/Factories";
131 : static const char CFG_ENTRY_REALDEFAULTFILTER[] = "ooSetupFactoryActualFilter";
132 :
133 : static const char CFG_ENTRY_PROP_TEMPURL[] = "TempURL";
134 : static const char CFG_ENTRY_PROP_ORIGINALURL[] = "OriginalURL";
135 : static const char CFG_ENTRY_PROP_TEMPLATEURL[] = "TemplateURL";
136 : static const char CFG_ENTRY_PROP_FACTORYURL[] = "FactoryURL";
137 : static const char CFG_ENTRY_PROP_MODULE[] = "Module";
138 : static const char CFG_ENTRY_PROP_DOCUMENTSTATE[] = "DocumentState";
139 : static const char CFG_ENTRY_PROP_FILTER[] = "Filter";
140 : static const char CFG_ENTRY_PROP_TITLE[] = "Title";
141 : static const char CFG_ENTRY_PROP_ID[] = "ID";
142 : static const char CFG_ENTRY_PROP_VIEWNAMES[] = "ViewNames";
143 :
144 : static const char FILTER_PROP_TYPE[] = "Type";
145 : static const char TYPE_PROP_EXTENSIONS[] = "Extensions";
146 :
147 : // setup.xcu
148 : static const char CFG_ENTRY_PROP_EMPTYDOCUMENTURL[] = "ooSetupFactoryEmptyDocumentURL";
149 : static const char CFG_ENTRY_PROP_FACTORYSERVICE[] = "ooSetupFactoryDocumentService";
150 :
151 : static const char EVENT_ON_NEW[] = "OnNew";
152 : static const char EVENT_ON_LOAD[] = "OnLoad";
153 : static const char EVENT_ON_UNLOAD[] = "OnUnload";
154 : static const char EVENT_ON_MODIFYCHANGED[] = "OnModifyChanged";
155 : static const char EVENT_ON_SAVE[] = "OnSave";
156 : static const char EVENT_ON_SAVEAS[] = "OnSaveAs";
157 : static const char EVENT_ON_SAVETO[] = "OnCopyTo";
158 : static const char EVENT_ON_SAVEDONE[] = "OnSaveDone";
159 : static const char EVENT_ON_SAVEASDONE[] = "OnSaveAsDone";
160 : static const char EVENT_ON_SAVETODONE[] = "OnCopyToDone";
161 : static const char EVENT_ON_SAVEFAILED[] = "OnSaveFailed";
162 : static const char EVENT_ON_SAVEASFAILED[] = "OnSaveAsFailed";
163 : static const char EVENT_ON_SAVETOFAILED[] = "OnCopyToFailed";
164 :
165 : static const char RECOVERY_ITEM_BASE_IDENTIFIER[] = "recovery_item_";
166 :
167 : static const char CMD_PROTOCOL[] = "vnd.sun.star.autorecovery:";
168 :
169 : static const char CMD_DO_AUTO_SAVE[] = "/doAutoSave"; // force AutoSave ignoring the AutoSave timer
170 : static const char CMD_DO_PREPARE_EMERGENCY_SAVE[] = "/doPrepareEmergencySave"; // prepare the office for the following EmergencySave step (hide windows etcpp.)
171 : static const char CMD_DO_EMERGENCY_SAVE[] = "/doEmergencySave"; // do EmergencySave on crash
172 : static const char CMD_DO_RECOVERY[] = "/doAutoRecovery"; // recover all crashed documents
173 : static const char CMD_DO_ENTRY_BACKUP[] = "/doEntryBackup"; // try to store a temp or original file to a user defined location
174 : static const char CMD_DO_ENTRY_CLEANUP[] = "/doEntryCleanUp"; // remove the specified entry from the recovery cache
175 : static const char CMD_DO_SESSION_SAVE[] = "/doSessionSave"; // save all open documents if e.g. a window manager closes an user session
176 : static const char CMD_DO_SESSION_QUIET_QUIT[] = "/doSessionQuietQuit"; // let the current session be quietly closed ( the saving should be done using doSessionSave previously ) if e.g. a window manager closes an user session
177 : static const char CMD_DO_SESSION_RESTORE[] = "/doSessionRestore"; // restore a saved user session from disc
178 : static const char CMD_DO_DISABLE_RECOVERY[] = "/disableRecovery"; // disable recovery and auto save (!) temp. for this office session
179 : static const char CMD_DO_SET_AUTOSAVE_STATE[] = "/setAutoSaveState"; // disable/enable auto save (not crash save) for this office session
180 :
181 : static const char REFERRER_USER[] = "private:user";
182 :
183 : static const char PROP_DISPATCH_ASYNCHRON[] = "DispatchAsynchron";
184 : static const char PROP_PROGRESS[] = "StatusIndicator";
185 : static const char PROP_SAVEPATH[] = "SavePath";
186 : static const char PROP_ENTRY_ID[] = "EntryID";
187 : static const char PROP_AUTOSAVE_STATE[] = "AutoSaveState";
188 :
189 : static const char OPERATION_START[] = "start";
190 : static const char OPERATION_STOP[] = "stop";
191 : static const char OPERATION_UPDATE[] = "update";
192 :
193 : static const sal_Int32 MIN_DISCSPACE_DOCSAVE = 5; // [MB]
194 : static const sal_Int32 MIN_DISCSPACE_CONFIGSAVE = 1; // [MB]
195 : static const sal_Int32 RETRY_STORE_ON_FULL_DISC_FOREVER = 300; // not forever ... but often enough .-)
196 : static const sal_Int32 RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL = 3; // in case FULL DISC does not seam the real problem
197 : static const sal_Int32 GIVE_UP_RETRY = 1; // in case FULL DISC does not seam the real problem
198 :
199 : #define SAVE_IN_PROGRESS sal_True
200 : #define SAVE_FINISHED sal_False
201 :
202 : #define LOCK_FOR_CACHE_ADD_REMOVE sal_True
203 : #define LOCK_FOR_CACHE_USE sal_False
204 :
205 : #define MIN_TIME_FOR_USER_IDLE 10000 // 10s user idle
206 :
207 : // enable the following defines in case you whish to simulate a full disc for debug purposes .-)
208 :
209 : // this define throws everytime a document is stored or a configuration change
210 : // should be flushed an exception ... so the special error handler for this scenario is triggered
211 : // #define TRIGGER_FULL_DISC_CHECK
212 :
213 : // force "return sal_False" for the method impl_enoughDiscSpace().
214 : // #define SIMULATE_FULL_DISC
215 :
216 : //-----------------------------------------------
217 : // #define ENABLE_RECOVERY_LOGGING
218 : #undef ENABLE_RECOVERY_LOGGING
219 : #ifdef ENABLE_RECOVERY_LOGGING
220 : #define LOGFILE_RECOVERY "recovery.log"
221 :
222 : #define LOG_RECOVERY(MSG) \
223 : { \
224 : WRITE_LOGFILE(LOGFILE_RECOVERY, MSG) \
225 : WRITE_LOGFILE(LOGFILE_RECOVERY, "\n") \
226 : }
227 : #else
228 : #undef LOGFILE_RECOVERY
229 : #define LOG_RECOVERY(MSG)
230 : #endif
231 :
232 : //-----------------------------------------------
233 : class CacheLockGuard
234 : {
235 : private:
236 :
237 : // holds the outside calli alive, so it's shared resources
238 : // are valid everytimes
239 : css::uno::Reference< css::uno::XInterface > m_xOwner;
240 :
241 : // mutex shared with outside calli !
242 : LockHelper& m_rSharedMutex;
243 :
244 : // this variable knows the state of the "cache lock"
245 : sal_Int32& m_rCacheLock;
246 :
247 : // to prevent increasing/decreasing of m_rCacheLock more then ones
248 : // we must know if THIS guard has an actual lock set there !
249 : sal_Bool m_bLockedByThisGuard;
250 :
251 : public:
252 :
253 : CacheLockGuard(AutoRecovery* pOwner ,
254 : LockHelper& rMutex ,
255 : sal_Int32& rCacheLock ,
256 : sal_Bool bLockForAddRemoveVectorItems);
257 : ~CacheLockGuard();
258 :
259 : void lock(sal_Bool bLockForAddRemoveVectorItems);
260 : void unlock();
261 : };
262 :
263 : //-----------------------------------------------
264 8 : CacheLockGuard::CacheLockGuard(AutoRecovery* pOwner ,
265 : LockHelper& rMutex ,
266 : sal_Int32& rCacheLock ,
267 : sal_Bool bLockForAddRemoveVectorItems)
268 8 : : m_xOwner (static_cast< css::frame::XDispatch* >(pOwner))
269 : , m_rSharedMutex (rMutex )
270 : , m_rCacheLock (rCacheLock )
271 8 : , m_bLockedByThisGuard(sal_False )
272 : {
273 8 : lock(bLockForAddRemoveVectorItems);
274 8 : }
275 :
276 : //-----------------------------------------------
277 16 : CacheLockGuard::~CacheLockGuard()
278 : {
279 8 : unlock();
280 8 : m_xOwner.clear();
281 8 : }
282 :
283 : //-----------------------------------------------
284 16 : void CacheLockGuard::lock(sal_Bool bLockForAddRemoveVectorItems)
285 : {
286 : // SAFE -> ----------------------------------
287 16 : WriteGuard aWriteLock(m_rSharedMutex);
288 :
289 16 : if (m_bLockedByThisGuard)
290 16 : return;
291 :
292 : // This cache lock is needed only to prevent us from removing/adding
293 : // items from/into the recovery cache ... during it's used at another code place
294 : // for iterating .-)
295 :
296 : // Modifying of item properties is allowed and sometimes needed!
297 : // So we should detect only the dangerous state of concurrent add/remove
298 : // requests and throw an exception then ... which can of course break the whole
299 : // operation. On the other side a crash reasoned by an invalid stl iterator
300 : // will have the same effect .-)
301 :
302 16 : if (
303 : (m_rCacheLock > 0 ) &&
304 : (bLockForAddRemoveVectorItems)
305 : )
306 : {
307 : OSL_FAIL("Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp.");
308 : throw css::uno::RuntimeException(
309 : ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp.")),
310 0 : m_xOwner);
311 : }
312 :
313 16 : ++m_rCacheLock;
314 16 : m_bLockedByThisGuard = sal_True;
315 :
316 16 : aWriteLock.unlock();
317 : // <- SAFE ----------------------------------
318 : }
319 :
320 : //-----------------------------------------------
321 24 : void CacheLockGuard::unlock()
322 : {
323 : // SAFE -> ----------------------------------
324 24 : WriteGuard aWriteLock(m_rSharedMutex);
325 :
326 24 : if ( ! m_bLockedByThisGuard)
327 24 : return;
328 :
329 16 : --m_rCacheLock;
330 16 : m_bLockedByThisGuard = sal_False;
331 :
332 16 : if (m_rCacheLock < 0)
333 : {
334 : OSL_FAIL("Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)");
335 : throw css::uno::RuntimeException(
336 : ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)")),
337 0 : m_xOwner);
338 : }
339 16 : aWriteLock.unlock();
340 : // <- SAFE ----------------------------------
341 : }
342 :
343 : //-----------------------------------------------
344 8 : DispatchParams::DispatchParams()
345 8 : : m_nWorkingEntryID(-1)
346 : {
347 8 : };
348 :
349 : //-----------------------------------------------
350 0 : DispatchParams::DispatchParams(const ::comphelper::SequenceAsHashMap& lArgs ,
351 0 : const css::uno::Reference< css::uno::XInterface >& xOwner)
352 : {
353 0 : m_nWorkingEntryID = lArgs.getUnpackedValueOrDefault(PROP_ENTRY_ID, (sal_Int32)-1 );
354 0 : m_xProgress = lArgs.getUnpackedValueOrDefault(PROP_PROGRESS, css::uno::Reference< css::task::XStatusIndicator >());
355 0 : m_sSavePath = lArgs.getUnpackedValueOrDefault(PROP_SAVEPATH, ::rtl::OUString() );
356 0 : m_xHoldRefForAsyncOpAlive = xOwner;
357 0 : };
358 :
359 : //-----------------------------------------------
360 0 : DispatchParams::DispatchParams(const DispatchParams& rCopy)
361 : {
362 0 : m_xProgress = rCopy.m_xProgress;
363 0 : m_sSavePath = rCopy.m_sSavePath;
364 0 : m_nWorkingEntryID = rCopy.m_nWorkingEntryID;
365 0 : m_xHoldRefForAsyncOpAlive = rCopy.m_xHoldRefForAsyncOpAlive;
366 0 : };
367 :
368 : //-----------------------------------------------
369 8 : DispatchParams::~DispatchParams()
370 8 : {};
371 :
372 : //-----------------------------------------------
373 0 : DispatchParams& DispatchParams::operator=(const DispatchParams& rCopy)
374 : {
375 0 : m_xProgress = rCopy.m_xProgress;
376 0 : m_sSavePath = rCopy.m_sSavePath;
377 0 : m_nWorkingEntryID = rCopy.m_nWorkingEntryID;
378 0 : m_xHoldRefForAsyncOpAlive = rCopy.m_xHoldRefForAsyncOpAlive;
379 0 : return *this;
380 : }
381 :
382 : //-----------------------------------------------
383 0 : void DispatchParams::forget()
384 : {
385 0 : m_sSavePath = ::rtl::OUString();
386 0 : m_nWorkingEntryID = -1;
387 0 : m_xProgress.clear();
388 0 : m_xHoldRefForAsyncOpAlive.clear();
389 0 : };
390 :
391 : //-----------------------------------------------
392 264 : DEFINE_XINTERFACE_10(AutoRecovery ,
393 : OWeakObject ,
394 : DIRECT_INTERFACE (css::lang::XTypeProvider ),
395 : DIRECT_INTERFACE (css::lang::XServiceInfo ),
396 : DIRECT_INTERFACE (css::frame::XDispatch ),
397 : DIRECT_INTERFACE (css::beans::XMultiPropertySet ),
398 : DIRECT_INTERFACE (css::beans::XFastPropertySet ),
399 : DIRECT_INTERFACE (css::beans::XPropertySet ),
400 : DIRECT_INTERFACE (css::document::XEventListener ),
401 : DIRECT_INTERFACE (css::util::XChangesListener ),
402 : DIRECT_INTERFACE (css::util::XModifyListener ),
403 : DERIVED_INTERFACE(css::lang::XEventListener, css::document::XEventListener))
404 :
405 : //-----------------------------------------------
406 0 : DEFINE_XTYPEPROVIDER_6(AutoRecovery ,
407 : css::lang::XTypeProvider ,
408 : css::lang::XServiceInfo ,
409 : css::frame::XDispatch ,
410 : css::beans::XMultiPropertySet,
411 : css::beans::XFastPropertySet ,
412 : css::beans::XPropertySet )
413 :
414 : //-----------------------------------------------
415 366 : DEFINE_XSERVICEINFO_ONEINSTANCESERVICE(AutoRecovery ,
416 : ::cppu::OWeakObject ,
417 : "com.sun.star.frame.AutoRecovery",
418 : IMPLEMENTATIONNAME_AUTORECOVERY)
419 :
420 : //-----------------------------------------------
421 8 : DEFINE_INIT_SERVICE(
422 : AutoRecovery,
423 : {
424 : /*Attention
425 : I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
426 : to create a new instance of this class by our own supported service factory.
427 : see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations!
428 : */
429 :
430 : // read configuration to know if autosave/recovery is on/off etcpp...
431 : implts_readConfig();
432 :
433 : implts_startListening();
434 :
435 : // establish callback for our internal used timer.
436 : // Note: Its only active, if the timer will be started ...
437 : m_aTimer.SetTimeoutHdl(LINK(this, AutoRecovery, implts_timerExpired));
438 : }
439 : )
440 :
441 : //-----------------------------------------------
442 8 : AutoRecovery::AutoRecovery(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR)
443 8 : : ThreadHelpBase (&Application::GetSolarMutex() )
444 8 : , ::cppu::OBroadcastHelper ( m_aLock.getShareableOslMutex() )
445 : , ::cppu::OPropertySetHelper( *(static_cast< ::cppu::OBroadcastHelper* >(this)) )
446 : , ::cppu::OWeakObject ( )
447 : , m_xSMGR (xSMGR )
448 : , m_bListenForDocEvents (sal_False )
449 : , m_bListenForConfigChanges (sal_False )
450 : , m_nAutoSaveTimeIntervall (0 )
451 : , m_eJob (AutoRecovery::E_NO_JOB )
452 : , m_aAsyncDispatcher ( LINK( this, AutoRecovery, implts_asyncDispatch ) )
453 : , m_eTimerType (E_DONT_START_TIMER )
454 : , m_nIdPool (0 )
455 8 : , m_lListener (m_aLock.getShareableOslMutex() )
456 : , m_nDocCacheLock (0 )
457 : , m_nMinSpaceDocSave (MIN_DISCSPACE_DOCSAVE )
458 32 : , m_nMinSpaceConfigSave (MIN_DISCSPACE_CONFIGSAVE )
459 :
460 : #if OSL_DEBUG_LEVEL > 1
461 : , m_dbg_bMakeItFaster (sal_False )
462 : #endif
463 : {
464 8 : }
465 :
466 : //-----------------------------------------------
467 24 : AutoRecovery::~AutoRecovery()
468 : {
469 8 : implts_stopTimer();
470 16 : }
471 :
472 : //-----------------------------------------------
473 8 : void SAL_CALL AutoRecovery::dispatch(const css::util::URL& aURL ,
474 : const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
475 : throw(css::uno::RuntimeException)
476 : {
477 : LOG_RECOVERY("AutoRecovery::dispatch() starts ...")
478 : LOG_RECOVERY(U2B(aURL.Complete).getStr())
479 :
480 : // valid request ?
481 8 : sal_Int32 eNewJob = AutoRecovery::implst_classifyJob(aURL);
482 8 : if (eNewJob == AutoRecovery::E_NO_JOB)
483 : return;
484 :
485 : // SAFE -> ----------------------------------
486 8 : WriteGuard aWriteLock(m_aLock);
487 :
488 : // still running operation ... ignoring AUTO_SAVE.
489 : // All other requests has higher prio!
490 8 : if (
491 : ( m_eJob != AutoRecovery::E_NO_JOB ) &&
492 : ((m_eJob & AutoRecovery::E_AUTO_SAVE ) != AutoRecovery::E_AUTO_SAVE)
493 : )
494 : {
495 : LOG_WARNING("AutoRecovery::dispatch()", "There is already an asynchronous dispatch() running. New request will be ignored!")
496 : return;
497 : }
498 :
499 8 : ::comphelper::SequenceAsHashMap lArgs(lArguments);
500 :
501 : // check if somewhere wish to disable recovery temp. for this office session
502 : // This can be done immediatly ... must not been done asynchronous.
503 8 : if ((eNewJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY)
504 : {
505 : // it's important to set a flag internaly, so AutoRecovery will be supressed - even if it's requested.
506 8 : m_eJob |= eNewJob;
507 8 : implts_stopTimer();
508 8 : implts_stopListening();
509 : return;
510 : }
511 :
512 : // disable/enable AutoSave for this office session only
513 : // independend from the configuration entry.
514 0 : if ((eNewJob & AutoRecovery::E_SET_AUTOSAVE_STATE) == AutoRecovery::E_SET_AUTOSAVE_STATE)
515 : {
516 0 : sal_Bool bOn = lArgs.getUnpackedValueOrDefault(PROP_AUTOSAVE_STATE, (sal_Bool)sal_True);
517 0 : if (bOn)
518 : {
519 : // dont enable AutoSave hardly !
520 : // reload configuration to know the current state.
521 0 : implts_readAutoSaveConfig();
522 0 : implts_updateTimer();
523 : // can it happen that might be the listener was stopped ? .-)
524 : // make sure it runs always ... even if AutoSave itself was disabled temporarly.
525 0 : implts_startListening();
526 : }
527 : else
528 : {
529 0 : implts_stopTimer();
530 0 : m_eJob &= ~AutoRecovery::E_AUTO_SAVE;
531 0 : m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
532 : }
533 : return;
534 : }
535 :
536 0 : m_eJob |= eNewJob;
537 :
538 0 : sal_Bool bAsync = lArgs.getUnpackedValueOrDefault(PROP_DISPATCH_ASYNCHRON, (sal_Bool)sal_False);
539 0 : DispatchParams aParams (lArgs, static_cast< css::frame::XDispatch* >(this));
540 :
541 : // Hold this instance alive till the asynchronous operation will be finished.
542 0 : if (bAsync)
543 0 : m_aDispatchParams = aParams;
544 :
545 0 : aWriteLock.unlock();
546 : // <- SAFE ----------------------------------
547 :
548 0 : if (bAsync)
549 0 : m_aAsyncDispatcher.Post(0);
550 : else
551 0 : implts_dispatch(aParams);
552 : }
553 :
554 0 : void AutoRecovery::ListenerInformer::start()
555 : {
556 : m_rRecovery.implts_informListener(m_eJob,
557 0 : AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_START, NULL));
558 0 : }
559 :
560 0 : void AutoRecovery::ListenerInformer::stop()
561 : {
562 0 : if (m_bStopped)
563 0 : return;
564 : m_rRecovery.implts_informListener(m_eJob,
565 0 : AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_STOP, NULL));
566 0 : m_bStopped = true;
567 : }
568 :
569 : //-----------------------------------------------
570 0 : void AutoRecovery::implts_dispatch(const DispatchParams& aParams)
571 : {
572 : // SAFE -> ----------------------------------
573 0 : WriteGuard aWriteLock(m_aLock);
574 0 : sal_Int32 eJob = m_eJob;
575 0 : aWriteLock.unlock();
576 : // <- SAFE ----------------------------------
577 :
578 : // in case a new dispatch overwrites a may ba active AutoSave session
579 : // we must restore this session later. see below ...
580 0 : sal_Bool bWasAutoSaveActive = ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE);
581 :
582 : // On the other side it make no sense to reactivate the AutoSave operation
583 : // if the new dispatch indicates a final decision ...
584 : // E.g. an EmergencySave/SessionSave indicates the end of life of the current office session.
585 : // It make no sense to reactivate an AutoSave then.
586 : // But a Recovery or SessionRestore should reactivate a may be already active AutoSave.
587 0 : sal_Bool bAllowAutoSaveReactivation = sal_True;
588 :
589 0 : implts_stopTimer();
590 0 : implts_stopListening();
591 :
592 0 : ListenerInformer aListenerInformer(*this, eJob);
593 0 : aListenerInformer.start();
594 :
595 : try
596 : {
597 : // Auto save is called from our internal timer ... not via dispatch() API !
598 : // else
599 0 : if (
600 : ((eJob & AutoRecovery::E_PREPARE_EMERGENCY_SAVE) == AutoRecovery::E_PREPARE_EMERGENCY_SAVE) &&
601 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY ) != AutoRecovery::E_DISABLE_AUTORECOVERY )
602 : )
603 : {
604 : LOG_RECOVERY("... prepare emergency save ...")
605 0 : bAllowAutoSaveReactivation = sal_False;
606 0 : implts_prepareEmergencySave();
607 : }
608 : else
609 0 : if (
610 : ((eJob & AutoRecovery::E_EMERGENCY_SAVE ) == AutoRecovery::E_EMERGENCY_SAVE ) &&
611 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
612 : )
613 : {
614 : LOG_RECOVERY("... do emergency save ...")
615 0 : bAllowAutoSaveReactivation = sal_False;
616 0 : implts_doEmergencySave(aParams);
617 : }
618 : else
619 0 : if (
620 : ((eJob & AutoRecovery::E_RECOVERY ) == AutoRecovery::E_RECOVERY ) &&
621 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
622 : )
623 : {
624 : LOG_RECOVERY("... do recovery ...")
625 0 : implts_doRecovery(aParams);
626 : }
627 : else
628 0 : if (
629 : ((eJob & AutoRecovery::E_SESSION_SAVE ) == AutoRecovery::E_SESSION_SAVE ) &&
630 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
631 : )
632 : {
633 : LOG_RECOVERY("... do session save ...")
634 0 : bAllowAutoSaveReactivation = sal_False;
635 0 : implts_doSessionSave(aParams);
636 : }
637 : else
638 0 : if (
639 : ((eJob & AutoRecovery::E_SESSION_QUIET_QUIT ) == AutoRecovery::E_SESSION_QUIET_QUIT ) &&
640 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
641 : )
642 : {
643 : LOG_RECOVERY("... do session quiet quit ...")
644 0 : bAllowAutoSaveReactivation = sal_False;
645 0 : implts_doSessionQuietQuit(aParams);
646 : }
647 : else
648 0 : if (
649 : ((eJob & AutoRecovery::E_SESSION_RESTORE ) == AutoRecovery::E_SESSION_RESTORE ) &&
650 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
651 : )
652 : {
653 : LOG_RECOVERY("... do session restore ...")
654 0 : implts_doSessionRestore(aParams);
655 : }
656 : else
657 0 : if (
658 : ((eJob & AutoRecovery::E_ENTRY_BACKUP ) == AutoRecovery::E_ENTRY_BACKUP ) &&
659 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
660 : )
661 0 : implts_backupWorkingEntry(aParams);
662 : else
663 0 : if (
664 : ((eJob & AutoRecovery::E_ENTRY_CLEANUP ) == AutoRecovery::E_ENTRY_CLEANUP ) &&
665 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
666 : )
667 0 : implts_cleanUpWorkingEntry(aParams);
668 : }
669 0 : catch(const css::uno::RuntimeException&)
670 : {
671 0 : throw;
672 : }
673 0 : catch(const css::uno::Exception&)
674 : {
675 : // TODO better error handling
676 : }
677 :
678 0 : aListenerInformer.stop();
679 :
680 : // SAFE -> ----------------------------------
681 0 : aWriteLock.lock();
682 0 : m_eJob = E_NO_JOB;
683 0 : if (
684 : (bAllowAutoSaveReactivation) &&
685 : (bWasAutoSaveActive )
686 : )
687 : {
688 0 : m_eJob |= AutoRecovery::E_AUTO_SAVE;
689 : }
690 :
691 0 : aWriteLock.unlock();
692 : // <- SAFE ----------------------------------
693 :
694 : // depends on bAllowAutoSaveReactivation implicitly by looking on m_eJob=E_AUTO_SAVE! see before ...
695 0 : implts_updateTimer();
696 :
697 0 : if (bAllowAutoSaveReactivation)
698 0 : implts_startListening();
699 0 : }
700 :
701 : //-----------------------------------------------
702 0 : void SAL_CALL AutoRecovery::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
703 : const css::util::URL& aURL )
704 : throw(css::uno::RuntimeException)
705 : {
706 0 : if (!xListener.is())
707 0 : throw css::uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Invalid listener reference.")), static_cast< css::frame::XDispatch* >(this));
708 : // container is threadsafe by using a shared mutex!
709 0 : m_lListener.addInterface(aURL.Complete, xListener);
710 :
711 : // REENTRANT !? -> --------------------------------
712 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
713 :
714 : // THREAD SAFE -> ----------------------------------
715 0 : ReadGuard aReadLock(m_aLock);
716 :
717 0 : AutoRecovery::TDocumentList::iterator pIt;
718 0 : for( pIt = m_lDocCache.begin();
719 0 : pIt != m_lDocCache.end() ;
720 : ++pIt )
721 : {
722 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
723 0 : css::frame::FeatureStateEvent aEvent = AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_UPDATE, &rInfo);
724 :
725 : // <- SAFE ------------------------------
726 0 : aReadLock.unlock();
727 0 : xListener->statusChanged(aEvent);
728 0 : aReadLock.lock();
729 : // SAFE -> ------------------------------
730 0 : }
731 :
732 0 : aReadLock.unlock();
733 : // <- SAFE ----------------------------------
734 0 : }
735 :
736 : //-----------------------------------------------
737 0 : void SAL_CALL AutoRecovery::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
738 : const css::util::URL& aURL )
739 : throw(css::uno::RuntimeException)
740 : {
741 0 : if (!xListener.is())
742 0 : throw css::uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Invalid listener reference.")), static_cast< css::frame::XDispatch* >(this));
743 : // container is threadsafe by using a shared mutex!
744 0 : m_lListener.removeInterface(aURL.Complete, xListener);
745 0 : }
746 :
747 : //-----------------------------------------------
748 0 : void SAL_CALL AutoRecovery::notifyEvent(const css::document::EventObject& aEvent)
749 : throw(css::uno::RuntimeException)
750 : {
751 0 : css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
752 :
753 : // new document => put it into the internal list
754 0 : if (
755 0 : (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_NEW))) ||
756 0 : (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_LOAD)))
757 : )
758 : {
759 0 : implts_registerDocument(xDocument);
760 : }
761 : // document modified => set its modify state new (means modified against the original file!)
762 0 : else if ( aEvent.EventName == EVENT_ON_MODIFYCHANGED )
763 : {
764 0 : implts_updateModifiedState(xDocument);
765 : }
766 : /* at least one document starts saving process =>
767 : Our application code isnt ready for multiple save requests
768 : at the same time. So we have to supress our AutoSave feature
769 : for the moment, till this other save requests will be finished.
770 : */
771 0 : else if (
772 0 : (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVE))) ||
773 0 : (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVEAS))) ||
774 0 : (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVETO)))
775 : )
776 : {
777 0 : implts_updateDocumentUsedForSavingState(xDocument, SAVE_IN_PROGRESS);
778 : }
779 : // document saved => remove tmp. files - but hold config entries alive!
780 0 : else if (
781 0 : (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVEDONE))) ||
782 0 : (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVEASDONE)))
783 : )
784 : {
785 0 : implts_markDocumentAsSaved(xDocument);
786 0 : implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
787 : }
788 : /* document saved as copy => mark it as "non used by concurrent save operation".
789 : so we can try to create a backup copy if next time AutoSave is started too.
790 : Dont remove temp. files or change the modified state of the document!
791 : It was not realy saved to the original file ...
792 : */
793 0 : else if ( aEvent.EventName == EVENT_ON_SAVETODONE )
794 : {
795 0 : implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
796 : }
797 : // If saving of a document failed by an error ... we have to save this document
798 : // by ourself next time AutoSave or EmergencySave is triggered.
799 : // But we can reset the state "used for other save requests". Otherwhise
800 : // these documents will never be saved!
801 0 : else if (
802 0 : (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVEFAILED))) ||
803 0 : (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVEASFAILED))) ||
804 0 : (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVETOFAILED)))
805 : )
806 : {
807 0 : implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
808 : }
809 : // document closed => remove temp. files and configuration entries
810 0 : else if ( aEvent.EventName == EVENT_ON_UNLOAD )
811 : {
812 0 : implts_deregisterDocument(xDocument, sal_True); // sal_True => stop listening for disposing() !
813 0 : }
814 0 : }
815 :
816 : //-----------------------------------------------
817 0 : void SAL_CALL AutoRecovery::changesOccurred(const css::util::ChangesEvent& aEvent)
818 : throw(css::uno::RuntimeException)
819 : {
820 0 : const css::uno::Sequence< css::util::ElementChange > lChanges (aEvent.Changes);
821 0 : const css::util::ElementChange* pChanges = lChanges.getConstArray();
822 :
823 0 : sal_Int32 c = lChanges.getLength();
824 0 : sal_Int32 i = 0;
825 :
826 : // SAFE -> ----------------------------------
827 0 : WriteGuard aWriteLock(m_aLock);
828 :
829 : // Changes of the configuration must be ignored if AutoSave/Recovery was disabled for this
830 : // office session. That can happen if e.g. the command line arguments "--norestore" or "--headless"
831 : // was set.
832 0 : if ((m_eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY)
833 0 : return;
834 :
835 0 : for (i=0; i<c; ++i)
836 : {
837 0 : ::rtl::OUString sPath;
838 0 : pChanges[i].Accessor >>= sPath;
839 :
840 0 : if ( sPath == CFG_ENTRY_AUTOSAVE_ENABLED )
841 : {
842 0 : sal_Bool bEnabled = sal_False;
843 0 : if (pChanges[i].Element >>= bEnabled)
844 : {
845 0 : if (bEnabled)
846 : {
847 0 : m_eJob |= AutoRecovery::E_AUTO_SAVE;
848 0 : m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
849 : }
850 : else
851 : {
852 0 : m_eJob &= ~AutoRecovery::E_AUTO_SAVE;
853 0 : m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
854 : }
855 : }
856 : }
857 : else
858 0 : if ( sPath == CFG_ENTRY_AUTOSAVE_TIMEINTERVALL )
859 0 : pChanges[i].Element >>= m_nAutoSaveTimeIntervall;
860 0 : }
861 :
862 0 : aWriteLock.unlock();
863 : // <- SAFE ----------------------------------
864 :
865 : // Note: This call stops the timer and starts it again.
866 : // But it checks the different timer states internaly and
867 : // may be supress the restart!
868 0 : implts_updateTimer();
869 : }
870 :
871 : //-----------------------------------------------
872 0 : void SAL_CALL AutoRecovery::modified(const css::lang::EventObject& aEvent)
873 : throw(css::uno::RuntimeException)
874 : {
875 0 : css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
876 0 : if (! xDocument.is())
877 0 : return;
878 :
879 0 : implts_markDocumentModifiedAgainstLastBackup(xDocument);
880 : }
881 :
882 : //-----------------------------------------------
883 0 : void SAL_CALL AutoRecovery::disposing(const css::lang::EventObject& aEvent)
884 : throw(css::uno::RuntimeException)
885 : {
886 : // SAFE -> ----------------------------------
887 0 : WriteGuard aWriteLock(m_aLock);
888 :
889 0 : if (aEvent.Source == m_xNewDocBroadcaster)
890 : {
891 0 : m_xNewDocBroadcaster.clear();
892 : return;
893 : }
894 :
895 0 : if (aEvent.Source == m_xRecoveryCFG)
896 : {
897 0 : m_xRecoveryCFG.clear();
898 : return;
899 : }
900 :
901 : // dispose from one of our cached documents ?
902 : // Normaly they should send a OnUnload message ...
903 : // But some stacktraces shows another possible use case .-)
904 0 : css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
905 0 : if (xDocument.is())
906 : {
907 0 : implts_deregisterDocument(xDocument, sal_False); // sal_False => dont call removeEventListener() .. because it's not needed here
908 : return;
909 0 : }
910 :
911 : // <- SAFE ----------------------------------
912 : }
913 :
914 : //-----------------------------------------------
915 16 : css::uno::Reference< css::container::XNameAccess > AutoRecovery::implts_openConfig()
916 : {
917 : // SAFE -> ----------------------------------
918 16 : WriteGuard aWriteLock(m_aLock);
919 :
920 16 : if (m_xRecoveryCFG.is())
921 8 : return m_xRecoveryCFG;
922 8 : css::uno::Reference< css::uno::XComponentContext > xContext = comphelper::getComponentContext(m_xSMGR);
923 :
924 8 : aWriteLock.unlock();
925 : // <- SAFE ----------------------------------
926 :
927 8 : rtl::OUString sCFG_PACKAGE_RECOVERY(RTL_CONSTASCII_USTRINGPARAM(CFG_PACKAGE_RECOVERY));
928 : // throws a RuntimeException if an error occure!
929 : css::uno::Reference< css::container::XNameAccess > xCFG(
930 : ::comphelper::ConfigurationHelper::openConfig(xContext, sCFG_PACKAGE_RECOVERY, ::comphelper::ConfigurationHelper::E_STANDARD),
931 8 : css::uno::UNO_QUERY);
932 :
933 8 : sal_Int32 nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE;
934 8 : sal_Int32 nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE;
935 :
936 : try
937 : {
938 8 : rtl::OUString sCFG_PATH_AUTOSAVE(CFG_PATH_AUTOSAVE);
939 : ::comphelper::ConfigurationHelper::readDirectKey(xContext,
940 : sCFG_PACKAGE_RECOVERY,
941 : sCFG_PATH_AUTOSAVE,
942 : rtl::OUString(CFG_ENTRY_MINSPACE_DOCSAVE),
943 8 : ::comphelper::ConfigurationHelper::E_STANDARD) >>= nMinSpaceDocSave;
944 :
945 : ::comphelper::ConfigurationHelper::readDirectKey(xContext,
946 : sCFG_PACKAGE_RECOVERY,
947 : sCFG_PATH_AUTOSAVE,
948 : rtl::OUString(CFG_ENTRY_MINSPACE_CONFIGSAVE),
949 8 : ::comphelper::ConfigurationHelper::E_STANDARD) >>= nMinSpaceConfigSave;
950 : }
951 0 : catch(const css::uno::Exception&)
952 : {
953 : // These config keys are not sooooo important, that
954 : // we are interested on errors here realy .-)
955 0 : nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE;
956 0 : nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE;
957 : }
958 :
959 : // SAFE -> ----------------------------------
960 8 : aWriteLock.lock();
961 8 : m_xRecoveryCFG = xCFG;
962 8 : m_nMinSpaceDocSave = nMinSpaceDocSave;
963 8 : m_nMinSpaceConfigSave = nMinSpaceConfigSave;
964 8 : aWriteLock.unlock();
965 : // <- SAFE ----------------------------------
966 :
967 8 : return xCFG;
968 : }
969 :
970 : //-----------------------------------------------
971 8 : void AutoRecovery::implts_readAutoSaveConfig()
972 : {
973 8 : css::uno::Reference< css::container::XHierarchicalNameAccess > xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY);
974 :
975 : // AutoSave [bool]
976 8 : sal_Bool bEnabled = sal_False;
977 8 : xCommonRegistry->getByHierarchicalName(rtl::OUString(CFG_ENTRY_AUTOSAVE_ENABLED)) >>= bEnabled;
978 :
979 : // SAFE -> ------------------------------
980 8 : WriteGuard aWriteLock(m_aLock);
981 8 : if (bEnabled)
982 : {
983 8 : m_eJob |= AutoRecovery::E_AUTO_SAVE;
984 8 : m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
985 : }
986 : else
987 : {
988 0 : m_eJob &= ~AutoRecovery::E_AUTO_SAVE;
989 0 : m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
990 : }
991 8 : aWriteLock.unlock();
992 : // <- SAFE ------------------------------
993 :
994 : // AutoSaveTimeIntervall [int] in min
995 8 : sal_Int32 nTimeIntervall = 15;
996 8 : xCommonRegistry->getByHierarchicalName(rtl::OUString(CFG_ENTRY_AUTOSAVE_TIMEINTERVALL)) >>= nTimeIntervall;
997 :
998 : // SAFE -> ----------------------------------
999 8 : aWriteLock.lock();
1000 8 : m_nAutoSaveTimeIntervall = nTimeIntervall;
1001 8 : aWriteLock.unlock();
1002 : // <- SAFE ----------------------------------
1003 8 : }
1004 :
1005 : //-----------------------------------------------
1006 8 : void AutoRecovery::implts_readConfig()
1007 : {
1008 8 : implts_readAutoSaveConfig();
1009 :
1010 8 : css::uno::Reference< css::container::XHierarchicalNameAccess > xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY);
1011 :
1012 : // REENTRANT -> --------------------------------
1013 8 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
1014 :
1015 : // THREADSAFE -> -------------------------------
1016 8 : WriteGuard aWriteLock(m_aLock);
1017 : // reset current cache load cache
1018 8 : m_lDocCache.clear();
1019 8 : m_nIdPool = 0;
1020 8 : aWriteLock.unlock();
1021 : // <- THREADSAFE -------------------------------
1022 :
1023 8 : aCacheLock.unlock();
1024 : // <- REENTRANT --------------------------------
1025 :
1026 8 : css::uno::Any aValue;
1027 :
1028 : // RecoveryList [set]
1029 8 : aValue = xCommonRegistry->getByHierarchicalName(rtl::OUString(CFG_ENTRY_RECOVERYLIST));
1030 8 : css::uno::Reference< css::container::XNameAccess > xList;
1031 8 : aValue >>= xList;
1032 8 : if (xList.is())
1033 : {
1034 8 : const rtl::OUString sRECOVERY_ITEM_BASE_IDENTIFIER(RECOVERY_ITEM_BASE_IDENTIFIER);
1035 8 : const css::uno::Sequence< ::rtl::OUString > lItems = xList->getElementNames();
1036 8 : const ::rtl::OUString* pItems = lItems.getConstArray();
1037 8 : sal_Int32 c = lItems.getLength();
1038 8 : sal_Int32 i = 0;
1039 :
1040 : // REENTRANT -> --------------------------
1041 8 : aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE);
1042 :
1043 8 : for (i=0; i<c; ++i)
1044 : {
1045 0 : css::uno::Reference< css::beans::XPropertySet > xItem;
1046 0 : xList->getByName(pItems[i]) >>= xItem;
1047 0 : if (!xItem.is())
1048 0 : continue;
1049 :
1050 0 : AutoRecovery::TDocumentInfo aInfo;
1051 0 : aInfo.NewTempURL = ::rtl::OUString();
1052 0 : aInfo.Document = css::uno::Reference< css::frame::XModel >();
1053 0 : xItem->getPropertyValue(rtl::OUString(CFG_ENTRY_PROP_ORIGINALURL)) >>= aInfo.OrgURL ;
1054 0 : xItem->getPropertyValue(rtl::OUString(CFG_ENTRY_PROP_TEMPURL)) >>= aInfo.OldTempURL ;
1055 0 : xItem->getPropertyValue(rtl::OUString(CFG_ENTRY_PROP_TEMPLATEURL)) >>= aInfo.TemplateURL ;
1056 0 : xItem->getPropertyValue(rtl::OUString(CFG_ENTRY_PROP_FILTER)) >>= aInfo.RealFilter ;
1057 0 : xItem->getPropertyValue(rtl::OUString(CFG_ENTRY_PROP_DOCUMENTSTATE)) >>= aInfo.DocumentState;
1058 0 : xItem->getPropertyValue(rtl::OUString(CFG_ENTRY_PROP_MODULE)) >>= aInfo.AppModule;
1059 0 : xItem->getPropertyValue(rtl::OUString(CFG_ENTRY_PROP_TITLE)) >>= aInfo.Title;
1060 0 : xItem->getPropertyValue(rtl::OUString(CFG_ENTRY_PROP_VIEWNAMES)) >>= aInfo.ViewNames;
1061 0 : implts_specifyAppModuleAndFactory(aInfo);
1062 0 : implts_specifyDefaultFilterAndExtension(aInfo);
1063 :
1064 0 : if (pItems[i].indexOf(sRECOVERY_ITEM_BASE_IDENTIFIER)==0)
1065 : {
1066 0 : ::rtl::OUString sID = pItems[i].copy(sRECOVERY_ITEM_BASE_IDENTIFIER.getLength());
1067 0 : aInfo.ID = sID.toInt32();
1068 : // SAFE -> ----------------------
1069 0 : aWriteLock.lock();
1070 0 : if (aInfo.ID > m_nIdPool)
1071 : {
1072 0 : m_nIdPool = aInfo.ID+1;
1073 : LOG_ASSERT(m_nIdPool>=0, "AutoRecovery::implts_readConfig()\nOverflow of IDPool detected!")
1074 : }
1075 0 : aWriteLock.unlock();
1076 : // <- SAFE ----------------------
1077 : }
1078 : #ifdef ENABLE_WARNINGS
1079 : else
1080 : LOG_WARNING("AutoRecovery::implts_readConfig()", "Who changed numbering of recovery items? Cache will be inconsistent then! I do not know, what will happen next time .-)")
1081 : #endif
1082 :
1083 : // THREADSAFE -> --------------------------
1084 0 : aWriteLock.lock();
1085 0 : m_lDocCache.push_back(aInfo);
1086 0 : aWriteLock.unlock();
1087 : // <- THREADSAFE --------------------------
1088 0 : }
1089 :
1090 8 : aCacheLock.unlock();
1091 : // <- REENTRANT --------------------------
1092 : }
1093 :
1094 8 : implts_updateTimer();
1095 8 : }
1096 :
1097 : //-----------------------------------------------
1098 0 : void AutoRecovery::implts_specifyDefaultFilterAndExtension(AutoRecovery::TDocumentInfo& rInfo)
1099 : {
1100 0 : if (rInfo.AppModule.isEmpty())
1101 : {
1102 : throw css::uno::RuntimeException(
1103 : ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Cant find out the default filter and its extension, if no application module is known!")),
1104 0 : static_cast< css::frame::XDispatch* >(this));
1105 : }
1106 :
1107 : // SAFE -> ----------------------------------
1108 0 : ReadGuard aReadLock(m_aLock);
1109 0 : css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1110 0 : css::uno::Reference< css::container::XNameAccess> xCFG = m_xModuleCFG;
1111 0 : aReadLock.unlock();
1112 : // <- SAFE ----------------------------------
1113 :
1114 : try
1115 : {
1116 0 : if (! xCFG.is())
1117 : {
1118 : // open module config on demand and cache the update access
1119 : xCFG = css::uno::Reference< css::container::XNameAccess >(
1120 : ::comphelper::ConfigurationHelper::openConfig(comphelper::getComponentContext(xSMGR), rtl::OUString(CFG_PACKAGE_MODULES),
1121 : ::comphelper::ConfigurationHelper::E_STANDARD),
1122 0 : css::uno::UNO_QUERY_THROW);
1123 :
1124 : // SAFE -> ----------------------------------
1125 0 : WriteGuard aWriteLock(m_aLock);
1126 0 : m_xModuleCFG = xCFG;
1127 0 : aWriteLock.unlock();
1128 : // <- SAFE ----------------------------------
1129 : }
1130 :
1131 : css::uno::Reference< css::container::XNameAccess > xModuleProps(
1132 0 : xCFG->getByName(rInfo.AppModule),
1133 0 : css::uno::UNO_QUERY_THROW);
1134 :
1135 0 : xModuleProps->getByName(rtl::OUString(CFG_ENTRY_REALDEFAULTFILTER)) >>= rInfo.DefaultFilter;
1136 :
1137 0 : css::uno::Reference< css::container::XNameAccess > xFilterCFG(xSMGR->createInstance(SERVICENAME_FILTERFACTORY), css::uno::UNO_QUERY_THROW);
1138 0 : css::uno::Reference< css::container::XNameAccess > xTypeCFG (xSMGR->createInstance(SERVICENAME_TYPEDETECTION), css::uno::UNO_QUERY_THROW);
1139 :
1140 0 : ::comphelper::SequenceAsHashMap lFilterProps (xFilterCFG->getByName(rInfo.DefaultFilter));
1141 0 : ::rtl::OUString sTypeRegistration = lFilterProps.getUnpackedValueOrDefault(rtl::OUString(FILTER_PROP_TYPE), ::rtl::OUString());
1142 0 : ::comphelper::SequenceAsHashMap lTypeProps (xTypeCFG->getByName(sTypeRegistration));
1143 0 : css::uno::Sequence< ::rtl::OUString > lExtensions = lTypeProps.getUnpackedValueOrDefault(rtl::OUString(TYPE_PROP_EXTENSIONS), css::uno::Sequence< ::rtl::OUString >());
1144 0 : if (lExtensions.getLength())
1145 : {
1146 0 : rInfo.Extension = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("."));
1147 0 : rInfo.Extension += lExtensions[0];
1148 : }
1149 : else
1150 0 : rInfo.Extension = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".unknown"));
1151 : }
1152 0 : catch(const css::uno::Exception&)
1153 : {
1154 0 : rInfo.DefaultFilter = ::rtl::OUString();
1155 0 : rInfo.Extension = ::rtl::OUString();
1156 0 : }
1157 0 : }
1158 :
1159 : //-----------------------------------------------
1160 0 : void AutoRecovery::implts_specifyAppModuleAndFactory(AutoRecovery::TDocumentInfo& rInfo)
1161 : {
1162 0 : ENSURE_OR_THROW2(
1163 : !rInfo.AppModule.isEmpty() || rInfo.Document.is(),
1164 : "Cant find out the application module nor its factory URL, if no application module (or a suitable) document is known!",
1165 : *this );
1166 :
1167 : // SAFE -> ----------------------------------
1168 0 : ReadGuard aReadLock(m_aLock);
1169 0 : css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1170 0 : aReadLock.unlock();
1171 : // <- SAFE ----------------------------------
1172 :
1173 0 : css::uno::Reference< css::frame::XModuleManager2 > xManager = ModuleManager::create( comphelper::getComponentContext(xSMGR) );
1174 :
1175 0 : if (rInfo.AppModule.isEmpty())
1176 0 : rInfo.AppModule = xManager->identify(rInfo.Document);
1177 :
1178 0 : ::comphelper::SequenceAsHashMap lModuleDescription(xManager->getByName(rInfo.AppModule));
1179 0 : lModuleDescription[rtl::OUString(CFG_ENTRY_PROP_EMPTYDOCUMENTURL)] >>= rInfo.FactoryURL;
1180 0 : lModuleDescription[rtl::OUString(CFG_ENTRY_PROP_FACTORYSERVICE)] >>= rInfo.FactoryService;
1181 0 : }
1182 :
1183 : //-----------------------------------------------
1184 0 : void AutoRecovery::implts_collectActiveViewNames( AutoRecovery::TDocumentInfo& i_rInfo )
1185 : {
1186 0 : ENSURE_OR_THROW2( i_rInfo.Document.is(), "need at document, at the very least", *this );
1187 :
1188 0 : i_rInfo.ViewNames.realloc(0);
1189 :
1190 : // obtain list of controllers of this document
1191 0 : ::std::vector< ::rtl::OUString > aViewNames;
1192 0 : const Reference< XModel2 > xModel( i_rInfo.Document, UNO_QUERY );
1193 0 : if ( xModel.is() )
1194 : {
1195 0 : const Reference< XEnumeration > xEnumControllers( xModel->getControllers() );
1196 0 : while ( xEnumControllers->hasMoreElements() )
1197 : {
1198 0 : const Reference< XController2 > xController( xEnumControllers->nextElement(), UNO_QUERY );
1199 0 : ::rtl::OUString sViewName;
1200 0 : if ( xController.is() )
1201 0 : sViewName = xController->getViewControllerName();
1202 : OSL_ENSURE( !sViewName.isEmpty(), "AutoRecovery::implts_collectActiveViewNames: (no XController2 ->) no view name -> no recovery of this view!" );
1203 :
1204 0 : if ( !sViewName.isEmpty() )
1205 0 : aViewNames.push_back( sViewName );
1206 0 : }
1207 : }
1208 : else
1209 : {
1210 0 : const Reference< XController2 > xController( xModel->getCurrentController(), UNO_QUERY );
1211 0 : ::rtl::OUString sViewName;
1212 0 : if ( xController.is() )
1213 0 : sViewName = xController->getViewControllerName();
1214 : OSL_ENSURE( !sViewName.isEmpty(), "AutoRecovery::implts_collectActiveViewNames: (no XController2 ->) no view name -> no recovery of this view!" );
1215 :
1216 0 : if ( !sViewName.isEmpty() )
1217 0 : aViewNames.push_back( sViewName );
1218 : }
1219 :
1220 0 : i_rInfo.ViewNames.realloc( aViewNames.size() );
1221 0 : ::std::copy( aViewNames.begin(), aViewNames.end(), i_rInfo.ViewNames.getArray() );
1222 0 : }
1223 :
1224 : //-----------------------------------------------
1225 0 : void AutoRecovery::implts_persistAllActiveViewNames()
1226 : {
1227 : // SAFE -> ----------------------------------
1228 0 : WriteGuard aWriteLock(m_aLock);
1229 :
1230 : // This list will be filled with every document
1231 0 : AutoRecovery::TDocumentList::iterator pIt;
1232 0 : for ( pIt = m_lDocCache.begin();
1233 0 : pIt != m_lDocCache.end() ;
1234 : ++pIt )
1235 : {
1236 0 : implts_collectActiveViewNames( *pIt );
1237 0 : implts_flushConfigItem( *pIt );
1238 0 : }
1239 0 : }
1240 :
1241 : //-----------------------------------------------
1242 0 : void AutoRecovery::implts_flushConfigItem(const AutoRecovery::TDocumentInfo& rInfo, sal_Bool bRemoveIt)
1243 : {
1244 0 : css::uno::Reference< css::container::XHierarchicalNameAccess > xCFG;
1245 :
1246 : try
1247 : {
1248 0 : xCFG = css::uno::Reference< css::container::XHierarchicalNameAccess >(implts_openConfig(), css::uno::UNO_QUERY_THROW);
1249 :
1250 0 : css::uno::Reference< css::container::XNameAccess > xCheck;
1251 0 : xCFG->getByHierarchicalName(rtl::OUString(CFG_ENTRY_RECOVERYLIST)) >>= xCheck;
1252 :
1253 0 : css::uno::Reference< css::container::XNameContainer > xModify(xCheck, css::uno::UNO_QUERY_THROW);
1254 0 : css::uno::Reference< css::lang::XSingleServiceFactory > xCreate(xCheck, css::uno::UNO_QUERY_THROW);
1255 :
1256 0 : ::rtl::OUStringBuffer sIDBuf;
1257 0 : sIDBuf.appendAscii(RTL_CONSTASCII_STRINGPARAM(RECOVERY_ITEM_BASE_IDENTIFIER));
1258 0 : sIDBuf.append((sal_Int32)rInfo.ID);
1259 0 : ::rtl::OUString sID = sIDBuf.makeStringAndClear();
1260 :
1261 : // remove
1262 0 : if (bRemoveIt)
1263 : {
1264 : // Catch NoSuchElementException.
1265 : // Its not a good idea inside multithreaded environments to call hasElement - removeElement.
1266 : // DO IT!
1267 : try
1268 : {
1269 0 : xModify->removeByName(sID);
1270 : }
1271 0 : catch(const css::container::NoSuchElementException&)
1272 : {
1273 0 : return;
1274 : }
1275 : }
1276 : else
1277 : {
1278 : // new/modify
1279 0 : css::uno::Reference< css::beans::XPropertySet > xSet;
1280 0 : sal_Bool bNew = (!xCheck->hasByName(sID));
1281 0 : if (bNew)
1282 0 : xSet = css::uno::Reference< css::beans::XPropertySet >(xCreate->createInstance(), css::uno::UNO_QUERY_THROW);
1283 : else
1284 0 : xCheck->getByName(sID) >>= xSet;
1285 :
1286 0 : xSet->setPropertyValue(rtl::OUString(CFG_ENTRY_PROP_ORIGINALURL), css::uno::makeAny(rInfo.OrgURL ));
1287 0 : xSet->setPropertyValue(rtl::OUString(CFG_ENTRY_PROP_TEMPURL), css::uno::makeAny(rInfo.OldTempURL ));
1288 0 : xSet->setPropertyValue(rtl::OUString(CFG_ENTRY_PROP_TEMPLATEURL), css::uno::makeAny(rInfo.TemplateURL ));
1289 0 : xSet->setPropertyValue(rtl::OUString(CFG_ENTRY_PROP_FILTER), css::uno::makeAny(rInfo.RealFilter));
1290 0 : xSet->setPropertyValue(rtl::OUString(CFG_ENTRY_PROP_DOCUMENTSTATE), css::uno::makeAny(rInfo.DocumentState));
1291 0 : xSet->setPropertyValue(rtl::OUString(CFG_ENTRY_PROP_MODULE), css::uno::makeAny(rInfo.AppModule));
1292 0 : xSet->setPropertyValue(rtl::OUString(CFG_ENTRY_PROP_TITLE), css::uno::makeAny(rInfo.Title));
1293 0 : xSet->setPropertyValue(rtl::OUString(CFG_ENTRY_PROP_VIEWNAMES), css::uno::makeAny(rInfo.ViewNames));
1294 :
1295 0 : if (bNew)
1296 0 : xModify->insertByName(sID, css::uno::makeAny(xSet));
1297 0 : }
1298 : }
1299 0 : catch(const css::uno::RuntimeException&)
1300 : {
1301 0 : throw;
1302 : }
1303 0 : catch(const css::uno::Exception&)
1304 : {
1305 : // ??? can it happen that a full disc let these set of operations fail too ???
1306 : }
1307 :
1308 0 : sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER;
1309 0 : do
1310 : {
1311 : try
1312 : {
1313 0 : css::uno::Reference< css::util::XChangesBatch > xFlush(xCFG, css::uno::UNO_QUERY_THROW);
1314 0 : xFlush->commitChanges();
1315 :
1316 : #ifdef TRIGGER_FULL_DISC_CHECK
1317 : throw css::uno::Exception();
1318 : #else // TRIGGER_FULL_DISC_CHECK
1319 0 : nRetry = 0;
1320 : #endif // TRIGGER_FULL_DISC_CHECK
1321 : }
1322 0 : catch(const css::uno::Exception&)
1323 : {
1324 : // a) FULL DISC seams to be the problem behind => show error and retry it forever (e.g. retry=300)
1325 : // b) unknown problem (may be locking problem) => reset RETRY value to more usefull value(!) (e.g. retry=3)
1326 : // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace !
1327 :
1328 : // SAFE ->
1329 0 : ReadGuard aReadLock(m_aLock);
1330 0 : sal_Int32 nMinSpaceConfigSave = m_nMinSpaceConfigSave;
1331 0 : aReadLock.unlock();
1332 : // <- SAFE
1333 :
1334 0 : if (! impl_enoughDiscSpace(nMinSpaceConfigSave))
1335 0 : AutoRecovery::impl_showFullDiscError();
1336 : else
1337 0 : if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL)
1338 0 : nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL;
1339 : else
1340 0 : if (nRetry <= GIVE_UP_RETRY)
1341 0 : throw; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!!
1342 :
1343 0 : --nRetry;
1344 : }
1345 : }
1346 0 : while(nRetry>0);
1347 : }
1348 :
1349 : //-----------------------------------------------
1350 8 : void AutoRecovery::implts_startListening()
1351 : {
1352 : // SAFE -> ----------------------------------
1353 8 : ReadGuard aReadLock(m_aLock);
1354 8 : css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1355 8 : css::uno::Reference< css::util::XChangesNotifier > xCFG (m_xRecoveryCFG, css::uno::UNO_QUERY);
1356 8 : css::uno::Reference< css::frame::XGlobalEventBroadcaster > xBroadcaster = m_xNewDocBroadcaster;
1357 8 : sal_Bool bListenForDocEvents = m_bListenForDocEvents;
1358 8 : aReadLock.unlock();
1359 : // <- SAFE ----------------------------------
1360 :
1361 16 : if (
1362 8 : ( xCFG.is() ) &&
1363 8 : (! m_bListenForConfigChanges)
1364 : )
1365 : {
1366 8 : m_xRecoveryCFGListener = new WeakChangesListener(this);
1367 8 : xCFG->addChangesListener(m_xRecoveryCFGListener);
1368 8 : m_bListenForConfigChanges = sal_True;
1369 : }
1370 :
1371 8 : if (!xBroadcaster.is())
1372 : {
1373 8 : xBroadcaster = css::frame::GlobalEventBroadcaster::create( comphelper::getComponentContext(xSMGR) );
1374 : // SAFE -> ----------------------------------
1375 8 : WriteGuard aWriteLock(m_aLock);
1376 8 : m_xNewDocBroadcaster = xBroadcaster;
1377 8 : aWriteLock.unlock();
1378 : // <- SAFE ----------------------------------
1379 : }
1380 :
1381 8 : if (
1382 8 : ( xBroadcaster.is() ) &&
1383 : (! bListenForDocEvents)
1384 : )
1385 : {
1386 8 : m_xNewDocBroadcasterListener = new WeakDocumentEventListener(this);
1387 8 : xBroadcaster->addEventListener(m_xNewDocBroadcasterListener);
1388 : // SAFE ->
1389 8 : WriteGuard aWriteLock(m_aLock);
1390 8 : m_bListenForDocEvents = sal_True;
1391 8 : aWriteLock.unlock();
1392 : // <- SAFE
1393 8 : }
1394 8 : }
1395 :
1396 : //-----------------------------------------------
1397 8 : void AutoRecovery::implts_stopListening()
1398 : {
1399 : // SAFE -> ----------------------------------
1400 8 : ReadGuard aReadLock(m_aLock);
1401 : // Attention: Dont reset our internal members here too.
1402 : // May be we must work with our configuration, but dont wish to be informed
1403 : // about changes any longer. Needed e.g. during EMERGENCY_SAVE!
1404 8 : css::uno::Reference< css::util::XChangesNotifier > xCFG (m_xRecoveryCFG , css::uno::UNO_QUERY);
1405 8 : css::uno::Reference< css::document::XEventBroadcaster > xGlobalEventBroadcaster(m_xNewDocBroadcaster, css::uno::UNO_QUERY);
1406 8 : aReadLock.unlock();
1407 : // <- SAFE ----------------------------------
1408 :
1409 8 : if (
1410 8 : (xGlobalEventBroadcaster.is()) &&
1411 : (m_bListenForDocEvents )
1412 : )
1413 : {
1414 8 : xGlobalEventBroadcaster->removeEventListener(m_xNewDocBroadcasterListener);
1415 8 : m_bListenForDocEvents = sal_False;
1416 : }
1417 :
1418 8 : if (
1419 8 : (xCFG.is() ) &&
1420 : (m_bListenForConfigChanges)
1421 : )
1422 : {
1423 8 : xCFG->removeChangesListener(m_xRecoveryCFGListener);
1424 8 : m_bListenForConfigChanges = sal_False;
1425 8 : }
1426 8 : }
1427 :
1428 : //-----------------------------------------------
1429 0 : void AutoRecovery::implts_startModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo)
1430 : {
1431 0 : if (rInfo.ListenForModify)
1432 0 : return;
1433 :
1434 0 : css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY);
1435 0 : if (xBroadcaster.is())
1436 : {
1437 0 : css::uno::Reference< css::util::XModifyListener > xThis(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY);
1438 0 : xBroadcaster->addModifyListener(xThis);
1439 0 : rInfo.ListenForModify = sal_True;
1440 0 : }
1441 : }
1442 :
1443 : //-----------------------------------------------
1444 0 : void AutoRecovery::implts_stopModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo)
1445 : {
1446 0 : if (! rInfo.ListenForModify)
1447 0 : return;
1448 :
1449 0 : css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY);
1450 0 : if (xBroadcaster.is())
1451 : {
1452 0 : css::uno::Reference< css::util::XModifyListener > xThis(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY);
1453 0 : xBroadcaster->removeModifyListener(xThis);
1454 0 : rInfo.ListenForModify = sal_False;
1455 0 : }
1456 : }
1457 :
1458 : //-----------------------------------------------
1459 8 : void AutoRecovery::implts_updateTimer()
1460 : {
1461 8 : implts_stopTimer();
1462 :
1463 : // SAFE -> ----------------------------------
1464 8 : WriteGuard aWriteLock(m_aLock);
1465 :
1466 8 : if (
1467 : (m_eJob == AutoRecovery::E_NO_JOB ) || // TODO may be superflous - E_DONT_START_TIMER should be used only
1468 : (m_eTimerType == AutoRecovery::E_DONT_START_TIMER)
1469 : )
1470 8 : return;
1471 :
1472 8 : sal_uLong nMilliSeconds = 0;
1473 8 : if (m_eTimerType == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL)
1474 : {
1475 8 : nMilliSeconds = (m_nAutoSaveTimeIntervall*60000); // [min] => 60.000 ms
1476 : #if OSL_DEBUG_LEVEL > 1
1477 : if (m_dbg_bMakeItFaster)
1478 : nMilliSeconds = m_nAutoSaveTimeIntervall; // [ms]
1479 : #endif
1480 : }
1481 : else
1482 0 : if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE)
1483 : {
1484 0 : nMilliSeconds = MIN_TIME_FOR_USER_IDLE;
1485 : #if OSL_DEBUG_LEVEL > 1
1486 : if (m_dbg_bMakeItFaster)
1487 : nMilliSeconds = 300; // let us some time, to finish this method .-)
1488 : #endif
1489 : }
1490 : else
1491 0 : if (m_eTimerType == AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED)
1492 0 : nMilliSeconds = 300; // there is a minimum time frame, where the user can loose some key input data!
1493 :
1494 8 : m_aTimer.SetTimeout(nMilliSeconds);
1495 8 : m_aTimer.Start();
1496 :
1497 8 : aWriteLock.unlock();
1498 : // <- SAFE ----------------------------------
1499 : }
1500 :
1501 : //-----------------------------------------------
1502 24 : void AutoRecovery::implts_stopTimer()
1503 : {
1504 : // SAFE -> ----------------------------------
1505 24 : WriteGuard aWriteLock(m_aLock);
1506 :
1507 24 : if (!m_aTimer.IsActive())
1508 24 : return;
1509 8 : m_aTimer.Stop();
1510 :
1511 : // <- SAFE ----------------------------------
1512 : }
1513 :
1514 : //-----------------------------------------------
1515 0 : IMPL_LINK_NOARG(AutoRecovery, implts_timerExpired)
1516 : {
1517 : try
1518 : {
1519 : // This method is called by using a pointer to us.
1520 : // But we must be aware that we can be destroyed hardly
1521 : // if our uno reference will be gone!
1522 : // => Hold this object alive till this method finish its work.
1523 0 : css::uno::Reference< css::uno::XInterface > xSelfHold(static_cast< css::lang::XTypeProvider* >(this));
1524 :
1525 : // Needed! Otherwise every reschedule request allow a new triggered timer event :-(
1526 0 : implts_stopTimer();
1527 :
1528 : // The timer must be ignored if AutoSave/Recovery was disabled for this
1529 : // office session. That can happen if e.g. the command line arguments "--norestore" or "--headless"
1530 : // was set. But normaly the timer was disabled if recovery was disabled ...
1531 : // But so we are more "safe" .-)
1532 : // SAFE -> ----------------------------------
1533 0 : ReadGuard aReadLock(m_aLock);
1534 0 : if ((m_eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY)
1535 0 : return 0;
1536 0 : aReadLock.unlock();
1537 : // <- SAFE ----------------------------------
1538 :
1539 : // check some "states", where its not allowed (better: not a good idea) to
1540 : // start an AutoSave. (e.g. if the user makes drag & drop ...)
1541 : // Then we poll till this "disallowed" state is gone.
1542 0 : sal_Bool bAutoSaveNotAllowed = Application::IsUICaptured();
1543 0 : if (bAutoSaveNotAllowed)
1544 : {
1545 : // SAFE -> ------------------------------
1546 0 : WriteGuard aWriteLock(m_aLock);
1547 0 : m_eTimerType = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED;
1548 0 : aWriteLock.unlock();
1549 : // <- SAFE ------------------------------
1550 0 : implts_updateTimer();
1551 0 : return 0;
1552 : }
1553 :
1554 : // analyze timer type.
1555 : // If we poll for an user idle period, may be we must
1556 : // do nothing here and start the timer again.
1557 : // SAFE -> ----------------------------------
1558 0 : WriteGuard aWriteLock(m_aLock);
1559 :
1560 0 : if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE)
1561 : {
1562 0 : sal_Bool bUserIdle = (Application::GetLastInputInterval()>MIN_TIME_FOR_USER_IDLE);
1563 0 : if (!bUserIdle)
1564 : {
1565 0 : implts_updateTimer();
1566 0 : return 0;
1567 : }
1568 : }
1569 :
1570 0 : aWriteLock.unlock();
1571 : // <- SAFE ----------------------------------
1572 :
1573 : implts_informListener(AutoRecovery::E_AUTO_SAVE,
1574 0 : AutoRecovery::implst_createFeatureStateEvent(AutoRecovery::E_AUTO_SAVE, OPERATION_START, NULL));
1575 :
1576 : // force save of all currently open documents
1577 : // The called method returns an info, if and how this
1578 : // timer must be restarted.
1579 0 : sal_Bool bAllowUserIdleLoop = sal_True;
1580 0 : AutoRecovery::ETimerType eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_False);
1581 :
1582 : // If timer isnt used for "short callbacks" (means polling
1583 : // for special states) ... reset the handle state of all
1584 : // cache items. Such handle state indicates, that a document
1585 : // was already saved during the THIS(!) AutoSave session.
1586 : // Of course NEXT AutoSave session must be started without
1587 : // any "handle" state ...
1588 0 : if (
1589 : (eSuggestedTimer == AutoRecovery::E_DONT_START_TIMER ) ||
1590 : (eSuggestedTimer == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL)
1591 : )
1592 : {
1593 0 : implts_resetHandleStates(sal_False);
1594 : }
1595 :
1596 : implts_informListener(AutoRecovery::E_AUTO_SAVE,
1597 0 : AutoRecovery::implst_createFeatureStateEvent(AutoRecovery::E_AUTO_SAVE, OPERATION_STOP, NULL));
1598 :
1599 : // restart timer - because it was disabled before ...
1600 : // SAFE -> ----------------------------------
1601 0 : aWriteLock.lock();
1602 0 : m_eTimerType = eSuggestedTimer;
1603 0 : aWriteLock.unlock();
1604 : // <- SAFE ----------------------------------
1605 :
1606 0 : implts_updateTimer();
1607 : }
1608 0 : catch(const css::uno::Exception&)
1609 : {
1610 : }
1611 :
1612 0 : return 0;
1613 : }
1614 :
1615 : //-----------------------------------------------
1616 0 : IMPL_LINK_NOARG(AutoRecovery, implts_asyncDispatch)
1617 : {
1618 : // SAFE ->
1619 0 : WriteGuard aWriteLock(m_aLock);
1620 0 : DispatchParams aParams = m_aDispatchParams;
1621 0 : css::uno::Reference< css::uno::XInterface > xHoldRefForMethodAlive = aParams.m_xHoldRefForAsyncOpAlive;
1622 0 : m_aDispatchParams.forget(); // clears all members ... including the ref-hold object .-)
1623 0 : aWriteLock.unlock();
1624 : // <- SAFE
1625 :
1626 : try
1627 : {
1628 0 : implts_dispatch(aParams);
1629 : }
1630 0 : catch (...)
1631 : {
1632 : }
1633 0 : return 0;
1634 : }
1635 :
1636 : //-----------------------------------------------
1637 0 : void AutoRecovery::implts_registerDocument(const css::uno::Reference< css::frame::XModel >& xDocument)
1638 : {
1639 : // ignore corrupted events, where no document is given ... Runtime Error ?!
1640 0 : if (!xDocument.is())
1641 : return;
1642 :
1643 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1644 :
1645 : // notification for already existing document !
1646 : // Can happen if events came in asynchronous on recovery time.
1647 : // Then our cache was filled from the configuration ... but now we get some
1648 : // asynchronous events from the global event broadcaster. We must be sure that
1649 : // we dont add the same document more then once.
1650 0 : AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1651 0 : if (pIt != m_lDocCache.end())
1652 : {
1653 : // Normaly nothing must be done for this "late" notification.
1654 : // But may be the modified state was changed inbetween.
1655 : // Check it ...
1656 0 : implts_updateModifiedState(xDocument);
1657 : return;
1658 : }
1659 :
1660 0 : aCacheLock.unlock();
1661 :
1662 0 : ::comphelper::MediaDescriptor lDescriptor(xDocument->getArgs());
1663 :
1664 : // check if this document must be ignored for recovery !
1665 : // Some use cases dont wish support for AutoSave/Recovery ... as e.g. OLE-Server / ActiveX Control etcpp.
1666 0 : sal_Bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_NOAUTOSAVE(), (sal_Bool)(sal_False));
1667 0 : if (bNoAutoSave)
1668 : return;
1669 :
1670 : // Check if doc is well known on the desktop. Otherwhise ignore it!
1671 : // Other frames mostly are used from external programs - e.g. the bean ...
1672 0 : css::uno::Reference< css::frame::XController > xController = xDocument->getCurrentController();
1673 0 : if (!xController.is())
1674 : return;
1675 :
1676 0 : css::uno::Reference< css::frame::XFrame > xFrame = xController->getFrame();
1677 0 : css::uno::Reference< css::frame::XDesktop > xDesktop (xFrame->getCreator(), css::uno::UNO_QUERY);
1678 0 : if (!xDesktop.is())
1679 : return;
1680 :
1681 : // if the document doesn't support the XDocumentRecovery interface, we're not interested in it.
1682 0 : Reference< XDocumentRecovery > xDocRecovery( xDocument, UNO_QUERY );
1683 0 : if ( !xDocRecovery.is() )
1684 : return;
1685 :
1686 : // get all needed informations of this document
1687 : // We need it to update our cache or to locate already existing elements there!
1688 0 : AutoRecovery::TDocumentInfo aNew;
1689 0 : aNew.Document = xDocument;
1690 :
1691 : // TODO replace getLocation() with getURL() ... its a workaround currently only!
1692 0 : css::uno::Reference< css::frame::XStorable > xDoc(aNew.Document, css::uno::UNO_QUERY_THROW);
1693 0 : aNew.OrgURL = xDoc->getLocation();
1694 :
1695 0 : css::uno::Reference< css::frame::XTitle > xTitle(aNew.Document, css::uno::UNO_QUERY_THROW);
1696 0 : aNew.Title = xTitle->getTitle ();
1697 :
1698 : // SAFE -> ----------------------------------
1699 0 : ReadGuard aReadLock(m_aLock);
1700 0 : css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1701 0 : aReadLock.unlock();
1702 : // <- SAFE ----------------------------------
1703 :
1704 : // classify the used application module, which is used by this document.
1705 0 : implts_specifyAppModuleAndFactory(aNew);
1706 :
1707 : // Hack! Check for "illegal office documents" ... as e.g. the Basic IDE
1708 : // Its not realy a full featured office document. It doesnt provide an URL, any filter, a factory URL etcpp.
1709 : // TODO file bug to Basci IDE developers. They must remove the office document API from its service.
1710 0 : if (
1711 0 : (aNew.OrgURL.isEmpty()) &&
1712 0 : (aNew.FactoryURL.isEmpty())
1713 : )
1714 : {
1715 : OSL_FAIL( "AutoRecovery::implts_registerDocument: this should not happen anymore!" );
1716 : // nowadays, the Basic IDE should already die on the "supports XDocumentRecovery" check. And no other known
1717 : // document type fits in here ...
1718 : return;
1719 : }
1720 :
1721 : // By the way - get some information about the default format for saving!
1722 : // and save an information about the real used filter by this document.
1723 : // We save this document with DefaultFilter ... and load it with the RealFilter.
1724 0 : implts_specifyDefaultFilterAndExtension(aNew);
1725 0 : aNew.RealFilter = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_FILTERNAME() , ::rtl::OUString());
1726 :
1727 : // Further we must know, if this document base on a template.
1728 : // Then we must load it in a different way.
1729 0 : css::uno::Reference< css::document::XDocumentPropertiesSupplier > xSupplier(aNew.Document, css::uno::UNO_QUERY);
1730 0 : if (xSupplier.is()) // optional interface!
1731 : {
1732 0 : css::uno::Reference< css::document::XDocumentProperties > xDocProps(xSupplier->getDocumentProperties(), css::uno::UNO_QUERY_THROW);
1733 0 : aNew.TemplateURL = xDocProps->getTemplateURL();
1734 : }
1735 :
1736 0 : css::uno::Reference< css::util::XModifiable > xModifyCheck(xDocument, css::uno::UNO_QUERY_THROW);
1737 0 : if (xModifyCheck->isModified())
1738 : {
1739 0 : aNew.DocumentState |= AutoRecovery::E_MODIFIED;
1740 : }
1741 :
1742 0 : aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE);
1743 :
1744 : // SAFE -> ----------------------------------
1745 0 : WriteGuard aWriteLock(m_aLock);
1746 :
1747 : // create a new cache entry ... this document isn't known.
1748 0 : ++m_nIdPool;
1749 0 : aNew.ID = m_nIdPool;
1750 : LOG_ASSERT(m_nIdPool>=0, "AutoRecovery::implts_registerDocument()\nOverflow of ID pool detected.")
1751 0 : m_lDocCache.push_back(aNew);
1752 :
1753 0 : AutoRecovery::TDocumentList::iterator pIt1 = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1754 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt1;
1755 :
1756 0 : aWriteLock.unlock();
1757 : // <- SAFE ----------------------------------
1758 :
1759 0 : implts_flushConfigItem(rInfo);
1760 0 : implts_startModifyListeningOnDoc(rInfo);
1761 :
1762 0 : aCacheLock.unlock();
1763 : }
1764 :
1765 : //-----------------------------------------------
1766 0 : void AutoRecovery::implts_deregisterDocument(const css::uno::Reference< css::frame::XModel >& xDocument ,
1767 : sal_Bool bStopListening)
1768 : {
1769 :
1770 : // SAFE -> ----------------------------------
1771 0 : WriteGuard aWriteLock(m_aLock);
1772 :
1773 : // Attention: Dont leave SAFE section, if you work with pIt!
1774 : // Because it points directly into the m_lDocCache list ...
1775 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1776 :
1777 0 : AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1778 0 : if (pIt == m_lDocCache.end())
1779 : return; // unknown document => not a runtime error! Because we register only a few documents. see registration ...
1780 :
1781 0 : AutoRecovery::TDocumentInfo aInfo = *pIt;
1782 :
1783 0 : aCacheLock.unlock();
1784 :
1785 : // Sometimes we close documents by ourself.
1786 : // And these documents cant be deregistered.
1787 : // Otherwhise we loos our configuration data ... but need it !
1788 : // see SessionSave !
1789 0 : if (aInfo.IgnoreClosing)
1790 : return;
1791 :
1792 0 : CacheLockGuard aCacheLock2(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
1793 0 : pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1794 0 : if (pIt != m_lDocCache.end())
1795 0 : m_lDocCache.erase(pIt);
1796 0 : pIt = m_lDocCache.end(); // otherwhise its not specified what pIt means!
1797 0 : aCacheLock2.unlock();
1798 :
1799 0 : aWriteLock.unlock();
1800 : // <- SAFE ----------------------------------
1801 :
1802 : /* This method is called within disposing() of the document too. But there it's not a good idea to
1803 : deregister us as listener. Furter it make no sense - because the broadcaster dies.
1804 : So we supress deregistration in such case ...
1805 : */
1806 0 : if (bStopListening)
1807 0 : implts_stopModifyListeningOnDoc(aInfo);
1808 :
1809 0 : AutoRecovery::st_impl_removeFile(aInfo.OldTempURL);
1810 0 : AutoRecovery::st_impl_removeFile(aInfo.NewTempURL);
1811 0 : implts_flushConfigItem(aInfo, sal_True); // sal_True => remove it from config
1812 : }
1813 :
1814 : //-----------------------------------------------
1815 0 : void AutoRecovery::implts_markDocumentModifiedAgainstLastBackup(const css::uno::Reference< css::frame::XModel >& xDocument)
1816 : {
1817 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1818 :
1819 : // SAFE -> ----------------------------------
1820 0 : WriteGuard aWriteLock(m_aLock);
1821 :
1822 0 : AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1823 0 : if (pIt != m_lDocCache.end())
1824 : {
1825 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
1826 :
1827 : /* Now we know, that this document was modified again and must be saved next time.
1828 : But we dont need this information for every e.g. key input of the user.
1829 : So we stop listening here.
1830 : But if the document was saved as temp. file we start listening for this event again.
1831 : */
1832 0 : implts_stopModifyListeningOnDoc(rInfo);
1833 : }
1834 :
1835 0 : aWriteLock.unlock();
1836 : // <- SAFE ----------------------------------
1837 0 : }
1838 :
1839 : //-----------------------------------------------
1840 0 : void AutoRecovery::implts_updateModifiedState(const css::uno::Reference< css::frame::XModel >& xDocument)
1841 : {
1842 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1843 :
1844 : // SAFE -> ----------------------------------
1845 0 : WriteGuard aWriteLock(m_aLock);
1846 :
1847 0 : AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1848 0 : if (pIt != m_lDocCache.end())
1849 : {
1850 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
1851 :
1852 : // use sal_True as fallback ... so we recognize every document on EmergencySave/AutoRecovery!
1853 0 : sal_Bool bModified = sal_True;
1854 0 : css::uno::Reference< css::util::XModifiable > xModify(xDocument, css::uno::UNO_QUERY);
1855 0 : if (xModify.is())
1856 0 : bModified = xModify->isModified();
1857 0 : if (bModified)
1858 : {
1859 0 : rInfo.DocumentState |= AutoRecovery::E_MODIFIED;
1860 : }
1861 : else
1862 : {
1863 0 : rInfo.DocumentState &= ~AutoRecovery::E_MODIFIED;
1864 0 : }
1865 : }
1866 :
1867 0 : aWriteLock.unlock();
1868 : // <- SAFE ----------------------------------
1869 0 : }
1870 :
1871 : //-----------------------------------------------
1872 0 : void AutoRecovery::implts_updateDocumentUsedForSavingState(const css::uno::Reference< css::frame::XModel >& xDocument ,
1873 : sal_Bool bSaveInProgress)
1874 : {
1875 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1876 :
1877 : // SAFE -> ----------------------------------
1878 0 : WriteGuard aWriteLock(m_aLock);
1879 :
1880 0 : AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1881 0 : if (pIt == m_lDocCache.end())
1882 0 : return;
1883 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
1884 0 : rInfo.UsedForSaving = bSaveInProgress;
1885 :
1886 0 : aWriteLock.unlock();
1887 : // <- SAFE ----------------------------------
1888 : }
1889 :
1890 : //-----------------------------------------------
1891 0 : void AutoRecovery::implts_markDocumentAsSaved(const css::uno::Reference< css::frame::XModel >& xDocument)
1892 : {
1893 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1894 :
1895 : // SAFE -> ----------------------------------
1896 0 : WriteGuard aWriteLock(m_aLock);
1897 :
1898 0 : AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1899 0 : if (pIt == m_lDocCache.end())
1900 0 : return;
1901 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
1902 :
1903 0 : rInfo.DocumentState = AutoRecovery::E_UNKNOWN;
1904 : // TODO replace getLocation() with getURL() ... its a workaround currently only!
1905 0 : css::uno::Reference< css::frame::XStorable > xDoc(rInfo.Document, css::uno::UNO_QUERY);
1906 0 : rInfo.OrgURL = xDoc->getLocation();
1907 :
1908 0 : ::rtl::OUString sRemoveURL1 = rInfo.OldTempURL;
1909 0 : ::rtl::OUString sRemoveURL2 = rInfo.NewTempURL;
1910 0 : rInfo.OldTempURL = ::rtl::OUString();
1911 0 : rInfo.NewTempURL = ::rtl::OUString();
1912 :
1913 0 : ::comphelper::MediaDescriptor lDescriptor(rInfo.Document->getArgs());
1914 0 : rInfo.RealFilter = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_FILTERNAME(), ::rtl::OUString());
1915 :
1916 0 : css::uno::Reference< css::frame::XTitle > xDocTitle(xDocument, css::uno::UNO_QUERY);
1917 0 : if (xDocTitle.is ())
1918 0 : rInfo.Title = xDocTitle->getTitle ();
1919 : else
1920 : {
1921 0 : rInfo.Title = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_TITLE() , ::rtl::OUString());
1922 0 : if (rInfo.Title.isEmpty())
1923 0 : rInfo.Title = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_DOCUMENTTITLE(), ::rtl::OUString());
1924 : }
1925 :
1926 0 : rInfo.UsedForSaving = sal_False;
1927 :
1928 0 : aWriteLock.unlock();
1929 : // <- SAFE ----------------------------------
1930 :
1931 0 : implts_flushConfigItem(rInfo);
1932 :
1933 0 : aCacheLock.unlock();
1934 :
1935 0 : AutoRecovery::st_impl_removeFile(sRemoveURL1);
1936 0 : AutoRecovery::st_impl_removeFile(sRemoveURL2);
1937 : }
1938 :
1939 : //-----------------------------------------------
1940 0 : AutoRecovery::TDocumentList::iterator AutoRecovery::impl_searchDocument( AutoRecovery::TDocumentList& rList ,
1941 : const css::uno::Reference< css::frame::XModel >& xDocument)
1942 : {
1943 0 : AutoRecovery::TDocumentList::iterator pIt;
1944 0 : for ( pIt = rList.begin();
1945 0 : pIt != rList.end() ;
1946 : ++pIt )
1947 : {
1948 0 : const AutoRecovery::TDocumentInfo& rInfo = *pIt;
1949 0 : if (rInfo.Document == xDocument)
1950 0 : break;
1951 : }
1952 0 : return pIt;
1953 : }
1954 :
1955 : //-----------------------------------------------
1956 : namespace
1957 : {
1958 0 : void lcl_changeVisibility( const css::uno::Reference< css::frame::XFramesSupplier >& i_rFrames, sal_Bool i_bVisible )
1959 : {
1960 0 : css::uno::Reference< css::container::XIndexAccess > xFramesContainer( i_rFrames->getFrames(), css::uno::UNO_QUERY );
1961 0 : const sal_Int32 count = xFramesContainer->getCount();
1962 :
1963 0 : Any aElement;
1964 0 : for ( sal_Int32 i=0; i < count; ++i )
1965 : {
1966 0 : aElement = xFramesContainer->getByIndex(i);
1967 : // check for sub frames
1968 0 : css::uno::Reference< css::frame::XFramesSupplier > xFramesSupp( aElement, css::uno::UNO_QUERY );
1969 0 : if ( xFramesSupp.is() )
1970 0 : lcl_changeVisibility( xFramesSupp, i_bVisible );
1971 :
1972 0 : css::uno::Reference< css::frame::XFrame > xFrame( aElement, css::uno::UNO_QUERY );
1973 0 : if ( !xFrame.is() )
1974 0 : continue;
1975 :
1976 0 : css::uno::Reference< css::awt::XWindow > xWindow( xFrame->getContainerWindow(), UNO_SET_THROW );
1977 0 : xWindow->setVisible( i_bVisible );
1978 0 : }
1979 0 : }
1980 : }
1981 :
1982 : //-----------------------------------------------
1983 0 : void AutoRecovery::implts_changeAllDocVisibility(sal_Bool bVisible)
1984 : {
1985 : // SAFE -> ----------------------------------
1986 0 : ReadGuard aReadLock(m_aLock);
1987 0 : css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1988 0 : aReadLock.unlock();
1989 : // <- SAFE ----------------------------------
1990 :
1991 0 : css::uno::Reference< css::frame::XFramesSupplier > xDesktop(xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY);
1992 0 : lcl_changeVisibility( xDesktop, bVisible );
1993 :
1994 0 : aReadLock.unlock();
1995 : // <- SAFE ----------------------------------
1996 0 : }
1997 :
1998 : //-----------------------------------------------
1999 : /* Currently the document is not closed in case of crash,
2000 : so the lock file must be removed explicitly
2001 : */
2002 0 : void lc_removeLockFile(AutoRecovery::TDocumentInfo& rInfo)
2003 : {
2004 0 : if ( rInfo.Document.is() )
2005 : {
2006 : try
2007 : {
2008 0 : css::uno::Reference< css::frame::XStorable > xStore(rInfo.Document, css::uno::UNO_QUERY_THROW);
2009 0 : ::rtl::OUString aURL = xStore->getLocation();
2010 0 : if ( !aURL.isEmpty() )
2011 : {
2012 0 : ::svt::DocumentLockFile aLockFile( aURL );
2013 0 : aLockFile.RemoveFile();
2014 0 : }
2015 : }
2016 0 : catch( const css::uno::Exception& )
2017 : {
2018 : }
2019 : }
2020 0 : }
2021 :
2022 :
2023 : //-----------------------------------------------
2024 0 : void AutoRecovery::implts_prepareSessionShutdown()
2025 : {
2026 : LOG_RECOVERY("AutoRecovery::implts_prepareSessionShutdown() starts ...")
2027 :
2028 : // a) reset modified documents (of course the must be saved before this method is called!)
2029 : // b) close it without showing any UI!
2030 :
2031 : // SAFE ->
2032 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2033 :
2034 0 : AutoRecovery::TDocumentList::iterator pIt;
2035 0 : for ( pIt = m_lDocCache.begin();
2036 0 : pIt != m_lDocCache.end() ;
2037 : ++pIt )
2038 : {
2039 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
2040 :
2041 : // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly
2042 : // it is not done on documents saving since shutdown can be cancelled
2043 0 : lc_removeLockFile( rInfo );
2044 :
2045 : // Prevent us from deregistration of these documents.
2046 : // Because we close these documents by ourself (see XClosable below) ...
2047 : // it's fact, that we reach our deregistration method. There we
2048 : // must not(!) update our configuration ... Otherwhise all
2049 : // session data are lost !!!
2050 0 : rInfo.IgnoreClosing = sal_True;
2051 :
2052 : // reset modified flag of these documents (ignoring the notification about it!)
2053 : // Otherwise a message box is shown on closing these models.
2054 0 : implts_stopModifyListeningOnDoc(rInfo);
2055 :
2056 : // if the session save is still running the documents should not be thrown away,
2057 : // actually that would be a bad sign, that means that the SessionManager tryes
2058 : // to kill the session before the saving is ready
2059 0 : if ((m_eJob & AutoRecovery::E_SESSION_SAVE) != AutoRecovery::E_SESSION_SAVE)
2060 : {
2061 0 : css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY);
2062 0 : if (xModify.is())
2063 0 : xModify->setModified(sal_False);
2064 :
2065 : // close the model.
2066 0 : css::uno::Reference< css::util::XCloseable > xClose(rInfo.Document, css::uno::UNO_QUERY);
2067 0 : if (xClose.is())
2068 : {
2069 : try
2070 : {
2071 0 : xClose->close(sal_False);
2072 : }
2073 0 : catch(const css::uno::Exception&)
2074 : {
2075 : // At least it's only a try to close these documents before anybody else it does.
2076 : // So it seams to be possible to ignore any error here .-)
2077 : }
2078 :
2079 0 : rInfo.Document.clear();
2080 0 : }
2081 : }
2082 : }
2083 :
2084 0 : aCacheLock.unlock();
2085 : // <- SAFE
2086 0 : }
2087 :
2088 : //-----------------------------------------------
2089 : /* TODO WORKAROUND:
2090 :
2091 : #i64599#
2092 :
2093 : Normaly the MediaDescriptor argument NoAutoSave indicates,
2094 : that a document must be ignored for AutoSave and Recovery.
2095 : But sometimes XModel->getArgs() does not contained this information
2096 : if implts_registerDocument() was called.
2097 : So we have to check a second time, if this property is set ....
2098 : Best place doing so is to check it immeditaly before saving
2099 : and supressingd saving the document then.
2100 : Of course removing the corresponding cache entry isnt an option.
2101 : Because it would disturb iteration over the cache !
2102 : So we ignore such documents only ...
2103 : Hopefully next time they are not inserted in our cache.
2104 : */
2105 0 : sal_Bool lc_checkIfSaveForbiddenByArguments(AutoRecovery::TDocumentInfo& rInfo)
2106 : {
2107 0 : if (! rInfo.Document.is())
2108 0 : return sal_True;
2109 :
2110 0 : ::comphelper::MediaDescriptor lDescriptor(rInfo.Document->getArgs());
2111 0 : sal_Bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_NOAUTOSAVE(), (sal_Bool)(sal_False));
2112 :
2113 0 : return bNoAutoSave;
2114 : }
2115 :
2116 : //-----------------------------------------------
2117 0 : AutoRecovery::ETimerType AutoRecovery::implts_saveDocs( sal_Bool bAllowUserIdleLoop,
2118 : sal_Bool bRemoveLockFiles,
2119 : const DispatchParams* pParams )
2120 : {
2121 : // SAFE -> ----------------------------------
2122 0 : ReadGuard aReadLock(m_aLock);
2123 0 : css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
2124 0 : aReadLock.unlock();
2125 : // <- SAFE ----------------------------------
2126 :
2127 0 : css::uno::Reference< css::task::XStatusIndicator > xExternalProgress;
2128 0 : if (pParams)
2129 0 : xExternalProgress = pParams->m_xProgress;
2130 :
2131 0 : css::uno::Reference< css::frame::XFramesSupplier > xDesktop (xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY);
2132 0 : ::rtl::OUString sBackupPath (SvtPathOptions().GetBackupPath());
2133 :
2134 0 : css::uno::Reference< css::frame::XController > xActiveController;
2135 0 : css::uno::Reference< css::frame::XModel > xActiveModel ;
2136 0 : css::uno::Reference< css::frame::XFrame > xActiveFrame = xDesktop->getActiveFrame();
2137 0 : if (xActiveFrame.is())
2138 0 : xActiveController = xActiveFrame->getController();
2139 0 : if (xActiveController.is())
2140 0 : xActiveModel = xActiveController->getModel();
2141 :
2142 : // Set the default timer action for our calli.
2143 : // Default = NORMAL_AUTOSAVE
2144 : // We return a suggestion for an active timer only.
2145 : // It will be ignored if the timer was disabled by the user ...
2146 : // Further this state can be set to USER_IDLE only later in this method.
2147 : // Its not allowed to reset such state then. Because we must know, if
2148 : // there exists POSTPONED documents. see below ...
2149 0 : AutoRecovery::ETimerType eTimer = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
2150 :
2151 0 : sal_Int32 eJob = m_eJob;
2152 :
2153 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2154 :
2155 : // SAFE -> ----------------------------------
2156 0 : WriteGuard aWriteLock(m_aLock);
2157 :
2158 : // This list will be filled with every document
2159 : // which should be saved as last one. E.g. if it was used
2160 : // already for an UI save operation => crashed ... and
2161 : // now we try to save it again ... which can fail again ( of course .-) ).
2162 0 : ::std::vector< AutoRecovery::TDocumentList::iterator > lDangerousDocs;
2163 :
2164 0 : AutoRecovery::TDocumentList::iterator pIt;
2165 0 : for ( pIt = m_lDocCache.begin();
2166 0 : pIt != m_lDocCache.end() ;
2167 : ++pIt )
2168 : {
2169 0 : AutoRecovery::TDocumentInfo aInfo = *pIt;
2170 :
2171 : // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly
2172 0 : if ( bRemoveLockFiles )
2173 0 : lc_removeLockFile( aInfo );
2174 :
2175 : // WORKAROUND ... see comment of this method
2176 0 : if (lc_checkIfSaveForbiddenByArguments(aInfo))
2177 0 : continue;
2178 :
2179 : // already auto saved during this session :-)
2180 : // This state must be reset for all documents
2181 : // if timer is started with normnal AutoSaveTimerIntervall!
2182 0 : if ((aInfo.DocumentState & AutoRecovery::E_HANDLED) == AutoRecovery::E_HANDLED)
2183 0 : continue;
2184 :
2185 : // Not modified documents are not saved.
2186 : // We safe an information about the URL only!
2187 0 : Reference< XDocumentRecovery > xDocRecover( aInfo.Document, UNO_QUERY_THROW );
2188 0 : if ( !xDocRecover->wasModifiedSinceLastSave() )
2189 : {
2190 0 : aInfo.DocumentState |= AutoRecovery::E_HANDLED;
2191 0 : continue;
2192 : }
2193 :
2194 : // check if this document is still used by a concurrent save operation
2195 : // e.g. if the user tried to save via UI.
2196 : // Handle it in the following way:
2197 : // i) For an AutoSave ... ignore this document! It will be saved and next time we will (hopefully)
2198 : // get a notification about the state of this operation.
2199 : // And if a document was saved by the user we can remove our temp. file. But that will be done inside
2200 : // our callback for SaveDone notification.
2201 : // ii) For a CrashSave ... add it to the list of dangerous documents and
2202 : // save it after all other documents was saved successfully. That decrease
2203 : // the chance for a crash inside a crash.
2204 : // On the other side it's not neccessary for documents, which are not modified.
2205 : // They can be handled normaly - means we patch the corresponding configuration entry only.
2206 : // iii) For a SessionSave ... ignore it! There is no time to wait for this save operation.
2207 : // Because the WindowManager will kill the process if it doesnt react immediatly.
2208 : // On the other side we cant risk a concurrent save request ... because we know
2209 : // that it will produce a crash.
2210 :
2211 : // Attention: Because eJob is used as a flag field, you have to check for the worst case first.
2212 : // E.g. a CrashSave can overwrite an AutoSave. So you have to check for a CrashSave before an AutoSave!
2213 0 : if (aInfo.UsedForSaving)
2214 : {
2215 0 : if ((eJob & AutoRecovery::E_EMERGENCY_SAVE) == AutoRecovery::E_EMERGENCY_SAVE)
2216 : {
2217 0 : lDangerousDocs.push_back(pIt);
2218 0 : continue;
2219 : }
2220 : else
2221 0 : if ((eJob & AutoRecovery::E_SESSION_SAVE) == AutoRecovery::E_SESSION_SAVE)
2222 : {
2223 0 : continue;
2224 : }
2225 : else
2226 0 : if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE)
2227 : {
2228 0 : eTimer = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED;
2229 0 : aInfo.DocumentState |= AutoRecovery::E_POSTPONED;
2230 0 : continue;
2231 : }
2232 : }
2233 :
2234 : // a) Document was not postponed - and is active now. => postpone it (restart timer, restart loop)
2235 : // b) Document was not postponed - and is not active now. => save it
2236 : // c) Document was postponed - and is not active now. => save it
2237 : // d) Document was postponed - and is active now. => save it (because user idle was checked already)
2238 0 : sal_Bool bActive = (xActiveModel == aInfo.Document);
2239 0 : sal_Bool bWasPostponed = ((aInfo.DocumentState & AutoRecovery::E_POSTPONED) == AutoRecovery::E_POSTPONED);
2240 :
2241 0 : if (
2242 : ! bWasPostponed &&
2243 : bActive
2244 : )
2245 : {
2246 0 : aInfo.DocumentState |= AutoRecovery::E_POSTPONED;
2247 0 : *pIt = aInfo;
2248 : // postponed documents will be saved if this method is called again!
2249 : // That can be done by an outside started timer => E_POLL_FOR_USER_IDLE (if normal AutoSave is active)
2250 : // or it must be done directly without starting any timer => E_CALL_ME_BACK (if Emergency- or SessionSave is active and must be finished ASAP!)
2251 0 : eTimer = AutoRecovery::E_POLL_FOR_USER_IDLE;
2252 0 : if (!bAllowUserIdleLoop)
2253 0 : eTimer = AutoRecovery::E_CALL_ME_BACK;
2254 0 : continue;
2255 : }
2256 :
2257 : // b, c, d)
2258 : // <- SAFE --------------------------
2259 0 : aWriteLock.unlock();
2260 : // changing of aInfo and flushing it is done inside implts_saveOneDoc!
2261 0 : implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress);
2262 0 : implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo));
2263 0 : aWriteLock.lock();
2264 : // SAFE -> --------------------------
2265 :
2266 0 : *pIt = aInfo;
2267 0 : }
2268 :
2269 : // Did we have some "dangerous candidates" ?
2270 : // Try to save it ... but may be it will fail !
2271 0 : ::std::vector< AutoRecovery::TDocumentList::iterator >::iterator pIt2;
2272 0 : for ( pIt2 = lDangerousDocs.begin();
2273 0 : pIt2 != lDangerousDocs.end() ;
2274 : ++pIt2 )
2275 : {
2276 0 : pIt = *pIt2;
2277 0 : AutoRecovery::TDocumentInfo aInfo = *pIt;
2278 :
2279 : // <- SAFE --------------------------
2280 0 : aWriteLock.unlock();
2281 : // changing of aInfo and flushing it is done inside implts_saveOneDoc!
2282 0 : implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress);
2283 0 : implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo));
2284 0 : aWriteLock.lock();
2285 : // SAFE -> --------------------------
2286 :
2287 0 : *pIt = aInfo;
2288 0 : }
2289 :
2290 0 : return eTimer;
2291 : }
2292 :
2293 : //-----------------------------------------------
2294 0 : void AutoRecovery::implts_saveOneDoc(const ::rtl::OUString& sBackupPath ,
2295 : AutoRecovery::TDocumentInfo& rInfo ,
2296 : const css::uno::Reference< css::task::XStatusIndicator >& xExternalProgress)
2297 : {
2298 : // no document? => can occure if we loaded our configuration with files,
2299 : // which couldnt be recovered successfully. In such case we have all needed informations
2300 : // excepting the real document instance!
2301 :
2302 : // TODO: search right place, where such "dead files" can be removed from the configuration!
2303 0 : if (!rInfo.Document.is())
2304 0 : return;
2305 :
2306 0 : ::comphelper::MediaDescriptor lOldArgs(rInfo.Document->getArgs());
2307 0 : implts_generateNewTempURL(sBackupPath, lOldArgs, rInfo);
2308 :
2309 : // if the document was loaded with a password, it should be
2310 : // stored with password
2311 0 : ::comphelper::MediaDescriptor lNewArgs;
2312 0 : ::rtl::OUString sPassword = lOldArgs.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_PASSWORD(), ::rtl::OUString());
2313 0 : if (!sPassword.isEmpty())
2314 0 : lNewArgs[::comphelper::MediaDescriptor::PROP_PASSWORD()] <<= sPassword;
2315 :
2316 : // Further it must be saved using the default file format of that application.
2317 : // Otherwhise we will some data lost.
2318 0 : if (!rInfo.DefaultFilter.isEmpty())
2319 0 : lNewArgs[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= rInfo.DefaultFilter;
2320 :
2321 : // prepare frame/document/mediadescriptor in a way, that it uses OUR progress .-)
2322 0 : if (xExternalProgress.is())
2323 0 : lNewArgs[::comphelper::MediaDescriptor::PROP_STATUSINDICATOR()] <<= xExternalProgress;
2324 0 : impl_establishProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >());
2325 :
2326 : // #i66598# use special handling of property "DocumentBaseURL" (it must be an empty string!)
2327 : // for make hyperlinks working
2328 0 : lNewArgs[::comphelper::MediaDescriptor::PROP_DOCUMENTBASEURL()] <<= ::rtl::OUString();
2329 :
2330 : // try to save this document as a new temp file everytimes.
2331 : // Mark AutoSave state as "INCOMPLETE" if it failed.
2332 : // Because the last temp file is to old and does not include all changes.
2333 0 : Reference< XDocumentRecovery > xDocRecover(rInfo.Document, css::uno::UNO_QUERY_THROW);
2334 :
2335 : // safe the state about "trying to save"
2336 : // ... we need it for recovery if e.g. a crash occures inside next line!
2337 0 : rInfo.DocumentState |= AutoRecovery::E_TRY_SAVE;
2338 0 : implts_flushConfigItem(rInfo);
2339 :
2340 0 : sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER;
2341 0 : sal_Bool bError = sal_False;
2342 0 : do
2343 : {
2344 : try
2345 : {
2346 0 : xDocRecover->storeToRecoveryFile( rInfo.NewTempURL, lNewArgs.getAsConstPropertyValueList() );
2347 :
2348 : #ifdef TRIGGER_FULL_DISC_CHECK
2349 : throw css::uno::Exception();
2350 : #else // TRIGGER_FULL_DISC_CHECK
2351 :
2352 0 : bError = sal_False;
2353 0 : nRetry = 0;
2354 : #endif // TRIGGER_FULL_DISC_CHECK
2355 : }
2356 0 : catch(const css::uno::Exception&)
2357 : {
2358 0 : bError = sal_True;
2359 :
2360 : // a) FULL DISC seams to be the problem behind => show error and retry it forever (e.g. retry=300)
2361 : // b) unknown problem (may be locking problem) => reset RETRY value to more usefull value(!) (e.g. retry=3)
2362 : // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace !
2363 :
2364 : // SAFE ->
2365 0 : ReadGuard aReadLock2(m_aLock);
2366 0 : sal_Int32 nMinSpaceDocSave = m_nMinSpaceDocSave;
2367 0 : aReadLock2.unlock();
2368 : // <- SAFE
2369 :
2370 0 : if (! impl_enoughDiscSpace(nMinSpaceDocSave))
2371 0 : AutoRecovery::impl_showFullDiscError();
2372 : else
2373 0 : if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL)
2374 0 : nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL;
2375 : else
2376 0 : if (nRetry <= GIVE_UP_RETRY)
2377 0 : throw; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!!
2378 :
2379 0 : --nRetry;
2380 : }
2381 : }
2382 : while(nRetry>0);
2383 :
2384 0 : if (! bError)
2385 : {
2386 : // safe the state about success
2387 : // ... you know the reason: to know it on recovery time if next line crash .-)
2388 0 : rInfo.DocumentState &= ~AutoRecovery::E_TRY_SAVE;
2389 0 : rInfo.DocumentState |= AutoRecovery::E_HANDLED;
2390 0 : rInfo.DocumentState |= AutoRecovery::E_SUCCEDED;
2391 : }
2392 : else
2393 : {
2394 : // safe the state about error ...
2395 0 : rInfo.NewTempURL = ::rtl::OUString();
2396 0 : rInfo.DocumentState &= ~AutoRecovery::E_TRY_SAVE;
2397 0 : rInfo.DocumentState |= AutoRecovery::E_HANDLED;
2398 0 : rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE;
2399 : }
2400 :
2401 : // make sure the progress isnt referred any longer
2402 0 : impl_forgetProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >());
2403 :
2404 : // try to remove the old temp file.
2405 : // Ignore any error here. We have a new temp file, which is up to date.
2406 : // The only thing is: we fill the disk with temp files, if we cant remove old ones :-)
2407 0 : ::rtl::OUString sRemoveFile = rInfo.OldTempURL;
2408 0 : rInfo.OldTempURL = rInfo.NewTempURL;
2409 0 : rInfo.NewTempURL = ::rtl::OUString();
2410 :
2411 0 : implts_flushConfigItem(rInfo);
2412 :
2413 : // We must know if the user modifies the document again ...
2414 0 : implts_startModifyListeningOnDoc(rInfo);
2415 :
2416 0 : AutoRecovery::st_impl_removeFile(sRemoveFile);
2417 : }
2418 :
2419 : //-----------------------------------------------
2420 0 : AutoRecovery::ETimerType AutoRecovery::implts_openDocs(const DispatchParams& aParams)
2421 : {
2422 0 : AutoRecovery::ETimerType eTimer = AutoRecovery::E_DONT_START_TIMER;
2423 :
2424 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2425 :
2426 : // SAFE -> ----------------------------------
2427 0 : WriteGuard aWriteLock(m_aLock);
2428 :
2429 0 : sal_Int32 eJob = m_eJob;
2430 0 : AutoRecovery::TDocumentList::iterator pIt;
2431 0 : for ( pIt = m_lDocCache.begin();
2432 0 : pIt != m_lDocCache.end() ;
2433 : ++pIt )
2434 : {
2435 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
2436 :
2437 : // Such documents are already loaded by the last loop.
2438 : // Dont check E_SUCCEDED here! Its may be the final state of an AutoSave
2439 : // operation before!!!
2440 0 : if ((rInfo.DocumentState & AutoRecovery::E_HANDLED) == AutoRecovery::E_HANDLED)
2441 0 : continue;
2442 :
2443 : // a1,b1,c1,d2,e2,f2)
2444 0 : if ((rInfo.DocumentState & AutoRecovery::E_DAMAGED) == AutoRecovery::E_DAMAGED)
2445 : {
2446 : // dont forget to inform listener! May be this document was
2447 : // damaged on last saving time ...
2448 : // Then our listener need this notification.
2449 : // If it was damaged during last "try to open" ...
2450 : // it will be notified more then once. SH.. HAPPENS ...
2451 : // <- SAFE --------------------------
2452 0 : aWriteLock.unlock();
2453 : implts_informListener(eJob,
2454 0 : AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
2455 0 : aWriteLock.lock();
2456 : // SAFE -> --------------------------
2457 0 : continue;
2458 : }
2459 :
2460 0 : ::comphelper::MediaDescriptor lDescriptor;
2461 :
2462 : // its an UI feature - so the "USER" itself must be set as referer
2463 0 : lDescriptor[::comphelper::MediaDescriptor::PROP_REFERRER()] <<= ::rtl::OUString(REFERRER_USER);
2464 0 : lDescriptor[::comphelper::MediaDescriptor::PROP_SALVAGEDFILE()] <<= ::rtl::OUString();
2465 :
2466 : // recovered documents are loaded hidden, and shown all at once, later
2467 0 : lDescriptor[::comphelper::MediaDescriptor::PROP_HIDDEN()] <<= true;
2468 :
2469 0 : if (aParams.m_xProgress.is())
2470 0 : lDescriptor[::comphelper::MediaDescriptor::PROP_STATUSINDICATOR()] <<= aParams.m_xProgress;
2471 :
2472 : sal_Bool bBackupWasTried = (
2473 : ((rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_BACKUP ) == AutoRecovery::E_TRY_LOAD_BACKUP) || // temp. state!
2474 : ((rInfo.DocumentState & AutoRecovery::E_INCOMPLETE ) == AutoRecovery::E_INCOMPLETE ) // transport TRY_LOAD_BACKUP from last loop to this new one!
2475 0 : );
2476 0 : sal_Bool bOriginalWasTried = ((rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_ORIGINAL) == AutoRecovery::E_TRY_LOAD_ORIGINAL);
2477 :
2478 0 : if (bBackupWasTried)
2479 : {
2480 0 : if (!bOriginalWasTried)
2481 : {
2482 0 : rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE;
2483 : // try original URL ... ! dont continue with next item here ...
2484 : }
2485 : else
2486 : {
2487 0 : rInfo.DocumentState |= AutoRecovery::E_DAMAGED;
2488 0 : continue;
2489 : }
2490 : }
2491 :
2492 0 : ::rtl::OUString sLoadOriginalURL;
2493 0 : ::rtl::OUString sLoadBackupURL ;
2494 :
2495 0 : if (!bBackupWasTried)
2496 0 : sLoadBackupURL = rInfo.OldTempURL;
2497 :
2498 0 : if (!rInfo.OrgURL.isEmpty())
2499 : {
2500 0 : sLoadOriginalURL = rInfo.OrgURL;
2501 : }
2502 : else
2503 0 : if (!rInfo.TemplateURL.isEmpty())
2504 : {
2505 0 : sLoadOriginalURL = rInfo.TemplateURL;
2506 0 : lDescriptor[::comphelper::MediaDescriptor::PROP_ASTEMPLATE()] <<= sal_True;
2507 0 : lDescriptor[::comphelper::MediaDescriptor::PROP_TEMPLATENAME()] <<= rInfo.TemplateURL;
2508 : }
2509 : else
2510 0 : if (!rInfo.FactoryURL.isEmpty())
2511 : {
2512 0 : sLoadOriginalURL = rInfo.FactoryURL;
2513 0 : lDescriptor[::comphelper::MediaDescriptor::PROP_ASTEMPLATE()] <<= sal_True;
2514 : }
2515 :
2516 : // A "Salvaged" item must exists every time. The core can make something special then for recovery.
2517 : // Of course it should be the real file name of the original file, in case we load the temp. backup here.
2518 0 : ::rtl::OUString sURL;
2519 0 : if (!sLoadBackupURL.isEmpty())
2520 : {
2521 0 : sURL = sLoadBackupURL;
2522 0 : rInfo.DocumentState |= AutoRecovery::E_TRY_LOAD_BACKUP;
2523 0 : lDescriptor[::comphelper::MediaDescriptor::PROP_SALVAGEDFILE()] <<= sLoadOriginalURL;
2524 : }
2525 0 : else if (!sLoadOriginalURL.isEmpty())
2526 : {
2527 0 : sURL = sLoadOriginalURL;
2528 0 : rInfo.DocumentState |= AutoRecovery::E_TRY_LOAD_ORIGINAL;
2529 : }
2530 : else
2531 0 : continue; // TODO ERROR!
2532 :
2533 0 : LoadEnv::initializeUIDefaults( m_xSMGR, lDescriptor, true, NULL );
2534 :
2535 : // <- SAFE ------------------------------
2536 0 : aWriteLock.unlock();
2537 :
2538 0 : implts_flushConfigItem(rInfo);
2539 : implts_informListener(eJob,
2540 0 : AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
2541 :
2542 : try
2543 : {
2544 0 : implts_openOneDoc(sURL, lDescriptor, rInfo);
2545 : }
2546 0 : catch(const css::uno::Exception&)
2547 : {
2548 0 : rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_BACKUP;
2549 0 : rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_ORIGINAL;
2550 0 : if (!sLoadBackupURL.isEmpty())
2551 : {
2552 0 : rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE;
2553 0 : eTimer = AutoRecovery::E_CALL_ME_BACK;
2554 : }
2555 : else
2556 : {
2557 0 : rInfo.DocumentState |= AutoRecovery::E_HANDLED;
2558 0 : rInfo.DocumentState |= AutoRecovery::E_DAMAGED;
2559 : }
2560 :
2561 0 : implts_flushConfigItem(rInfo, sal_True);
2562 : implts_informListener(eJob,
2563 0 : AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
2564 :
2565 : // SAFE -> ------------------------------
2566 : // Needed for next loop!
2567 0 : aWriteLock.lock();
2568 0 : continue;
2569 : }
2570 :
2571 0 : if (!rInfo.RealFilter.isEmpty())
2572 : {
2573 0 : ::comphelper::MediaDescriptor lPatchDescriptor(rInfo.Document->getArgs());
2574 0 : lPatchDescriptor[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= rInfo.RealFilter;
2575 0 : rInfo.Document->attachResource(rInfo.Document->getURL(), lPatchDescriptor.getAsConstPropertyValueList());
2576 : // do *not* use sURL here. In case this points to the recovery file, it has already been passed
2577 : // to recoverFromFile. Also, passing it here is logically wrong, as attachResource is intended
2578 : // to take the logical file URL.
2579 : }
2580 :
2581 0 : css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY);
2582 0 : if ( xModify.is() )
2583 : {
2584 0 : sal_Bool bModified = ((rInfo.DocumentState & AutoRecovery::E_MODIFIED) == AutoRecovery::E_MODIFIED);
2585 0 : xModify->setModified(bModified);
2586 : }
2587 :
2588 0 : rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_BACKUP;
2589 0 : rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_ORIGINAL;
2590 0 : rInfo.DocumentState |= AutoRecovery::E_HANDLED;
2591 0 : rInfo.DocumentState |= AutoRecovery::E_SUCCEDED;
2592 :
2593 0 : implts_flushConfigItem(rInfo);
2594 : implts_informListener(eJob,
2595 0 : AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
2596 :
2597 : /* Normaly we listen as XModifyListener on a document to know if a document was changed
2598 : since our last AutoSave. And we deregister us in case we know this state.
2599 : But directly after one document as recovered ... we must start listening.
2600 : Otherwhise the first "modify" doesnt reach us. Because we ourself called setModified()
2601 : on the document via API. And currently we dont listen for any events (not at the GlobalEventBroadcaster
2602 : nor at any document!).
2603 : */
2604 0 : implts_startModifyListeningOnDoc(rInfo);
2605 :
2606 : // SAFE -> ------------------------------
2607 : // Needed for next loop. Dont unlock it again!
2608 0 : aWriteLock.lock();
2609 0 : }
2610 :
2611 0 : aWriteLock.unlock();
2612 : // <- SAFE ----------------------------------
2613 :
2614 0 : return eTimer;
2615 : }
2616 :
2617 : //-----------------------------------------------
2618 0 : void AutoRecovery::implts_openOneDoc(const ::rtl::OUString& sURL ,
2619 : ::comphelper::MediaDescriptor& lDescriptor,
2620 : AutoRecovery::TDocumentInfo& rInfo )
2621 : {
2622 : // SAFE -> ----------------------------------
2623 0 : ReadGuard aReadLock(m_aLock);
2624 0 : css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
2625 0 : aReadLock.unlock();
2626 : // <- SAFE ----------------------------------
2627 :
2628 0 : css::uno::Reference< css::frame::XFrame > xDesktop( xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY_THROW );
2629 :
2630 0 : ::std::vector< Reference< XComponent > > aCleanup;
2631 : try
2632 : {
2633 : // create a new document of the desired type
2634 0 : Reference< XModel2 > xModel( xSMGR->createInstance( rInfo.FactoryService ), UNO_QUERY_THROW );
2635 0 : aCleanup.push_back( xModel.get() );
2636 :
2637 : // put the filter name into the descriptor - we're not going to involve any type detection, so
2638 : // the document might be lost without the FilterName property
2639 0 : if ( (rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_ORIGINAL) == AutoRecovery::E_TRY_LOAD_ORIGINAL)
2640 0 : lDescriptor[ ::comphelper::MediaDescriptor::PROP_FILTERNAME() ] <<= rInfo.RealFilter;
2641 : else
2642 0 : lDescriptor[ ::comphelper::MediaDescriptor::PROP_FILTERNAME() ] <<= rInfo.DefaultFilter;
2643 :
2644 0 : if ( sURL == rInfo.FactoryURL )
2645 : {
2646 : // if the document was a new, unmodified document, then there's nothing to recover, just to init
2647 0 : ENSURE_OR_THROW( ( rInfo.DocumentState & AutoRecovery::E_MODIFIED ) == 0,
2648 : "unexpected document state" );
2649 0 : Reference< XLoadable > xModelLoad( xModel, UNO_QUERY_THROW );
2650 0 : xModelLoad->initNew();
2651 :
2652 : // TODO: remove load-process specific arguments from the descriptor, e.g. the status indicator
2653 0 : xModel->attachResource( sURL, lDescriptor.getAsConstPropertyValueList() );
2654 : }
2655 : else
2656 : {
2657 : // let it recover itself
2658 0 : Reference< XDocumentRecovery > xDocRecover( xModel, UNO_QUERY_THROW );
2659 0 : xDocRecover->recoverFromFile(
2660 : sURL,
2661 0 : lDescriptor.getUnpackedValueOrDefault( ::comphelper::MediaDescriptor::PROP_SALVAGEDFILE(), ::rtl::OUString() ),
2662 : lDescriptor.getAsConstPropertyValueList()
2663 0 : );
2664 :
2665 : // No attachResource needed here. By definition (of XDocumentRecovery), the implementation is responsible
2666 : // for completely initializing the model, which includes attachResource (or equivalent), if required.
2667 : }
2668 :
2669 : // re-create all the views
2670 0 : ::std::vector< ::rtl::OUString > aViewsToRestore( rInfo.ViewNames.getLength() );
2671 0 : if ( rInfo.ViewNames.getLength() )
2672 0 : ::std::copy( rInfo.ViewNames.getConstArray(), rInfo.ViewNames.getConstArray() + rInfo.ViewNames.getLength(), aViewsToRestore.begin() );
2673 : // if we don't have views for whatever reason, then create a default-view, at least
2674 0 : if ( aViewsToRestore.empty() )
2675 0 : aViewsToRestore.push_back( ::rtl::OUString() );
2676 :
2677 0 : for ( ::std::vector< ::rtl::OUString >::const_iterator viewName = aViewsToRestore.begin();
2678 0 : viewName != aViewsToRestore.end();
2679 : ++viewName
2680 : )
2681 : {
2682 : // create a frame
2683 0 : Reference< XFrame > xTargetFrame = xDesktop->findFrame( SPECIALTARGET_BLANK, 0 );
2684 0 : aCleanup.push_back( xTargetFrame.get() );
2685 :
2686 : // create a view to the document
2687 0 : Reference< XController2 > xController;
2688 0 : if ( viewName->getLength() )
2689 : {
2690 0 : xController.set( xModel->createViewController( *viewName, Sequence< PropertyValue >(), xTargetFrame ), UNO_SET_THROW );
2691 : }
2692 : else
2693 : {
2694 0 : xController.set( xModel->createDefaultViewController( xTargetFrame ), UNO_SET_THROW );
2695 : }
2696 :
2697 : // introduce model/view/controller to each other
2698 0 : xController->attachModel( xModel.get() );
2699 0 : xModel->connectController( xController.get() );
2700 0 : xTargetFrame->setComponent( xController->getComponentWindow(), xController.get() );
2701 0 : xController->attachFrame( xTargetFrame );
2702 0 : xModel->setCurrentController( xController.get() );
2703 0 : }
2704 :
2705 0 : rInfo.Document = xModel.get();
2706 : }
2707 0 : catch(const css::uno::RuntimeException&)
2708 : {
2709 0 : throw;
2710 : }
2711 0 : catch(const css::uno::Exception&)
2712 : {
2713 0 : Any aCaughtException( ::cppu::getCaughtException() );
2714 :
2715 : // clean up
2716 0 : for ( ::std::vector< Reference< XComponent > >::const_iterator component = aCleanup.begin();
2717 0 : component != aCleanup.end();
2718 : ++component
2719 : )
2720 : {
2721 0 : css::uno::Reference< css::util::XCloseable > xClose( *component, css::uno::UNO_QUERY );
2722 0 : if ( xClose.is() )
2723 0 : xClose->close( sal_True );
2724 : else
2725 0 : (*component)->dispose();
2726 0 : }
2727 :
2728 : // re-throw
2729 0 : ::rtl::OUStringBuffer sMsg(256);
2730 0 : sMsg.appendAscii("Recovery of \"");
2731 0 : sMsg.append (sURL );
2732 0 : sMsg.appendAscii("\" failed." );
2733 :
2734 : throw css::lang::WrappedTargetException(
2735 : sMsg.makeStringAndClear(),
2736 : static_cast< css::frame::XDispatch* >(this),
2737 : aCaughtException
2738 0 : );
2739 0 : }
2740 0 : }
2741 :
2742 : //-----------------------------------------------
2743 0 : void AutoRecovery::implts_generateNewTempURL(const ::rtl::OUString& sBackupPath ,
2744 : ::comphelper::MediaDescriptor& /*rMediaDescriptor*/,
2745 : AutoRecovery::TDocumentInfo& rInfo )
2746 : {
2747 : // SAFE -> ----------------------------------
2748 0 : ReadGuard aReadLock(m_aLock);
2749 0 : css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
2750 0 : aReadLock.unlock();
2751 : // <- SAFE ----------------------------------
2752 :
2753 : // specify URL for saving (which points to a temp file inside backup directory)
2754 : // and define an unique name, so we can locate it later.
2755 : // This unique name must solve an optimization problem too!
2756 : // In case we are asked to save unmodified documents too - and one of them
2757 : // is an empty one (because it was new created using e.g. an URL private:factory/...)
2758 : // we should not save it realy. Then we put the information about such "empty document"
2759 : // into the configuration and dont create any recovery file on disk.
2760 : // We use the title of the document to make it unique.
2761 0 : ::rtl::OUStringBuffer sUniqueName;
2762 0 : if (!rInfo.OrgURL.isEmpty())
2763 : {
2764 0 : css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(::comphelper::getComponentContext(m_xSMGR)));
2765 0 : css::util::URL aURL;
2766 0 : aURL.Complete = rInfo.OrgURL;
2767 0 : xParser->parseStrict(aURL);
2768 0 : sUniqueName.append(aURL.Name);
2769 : }
2770 0 : else if (!rInfo.FactoryURL.isEmpty())
2771 0 : sUniqueName.appendAscii("untitled");
2772 0 : sUniqueName.appendAscii("_");
2773 :
2774 : // TODO: Must we strip some illegal signes - if we use the title?
2775 :
2776 0 : rtl::OUString sName(sUniqueName.makeStringAndClear());
2777 0 : String sExtension(rInfo.Extension);
2778 0 : String sPath(sBackupPath);
2779 0 : ::utl::TempFile aTempFile(sName, &sExtension, &sPath);
2780 :
2781 0 : rInfo.NewTempURL = aTempFile.GetURL();
2782 0 : }
2783 :
2784 : //-----------------------------------------------
2785 0 : void AutoRecovery::implts_informListener( sal_Int32 eJob ,
2786 : const css::frame::FeatureStateEvent& aEvent)
2787 : {
2788 : // Helper shares mutex with us -> threadsafe!
2789 0 : ::cppu::OInterfaceContainerHelper* pListenerForURL = 0;
2790 0 : ::rtl::OUString sJob = AutoRecovery::implst_getJobDescription(eJob);
2791 :
2792 : // inform listener, which are registered for any URLs(!)
2793 0 : pListenerForURL = m_lListener.getContainer(sJob);
2794 0 : if(pListenerForURL != 0)
2795 : {
2796 0 : ::cppu::OInterfaceIteratorHelper pIt(*pListenerForURL);
2797 0 : while(pIt.hasMoreElements())
2798 : {
2799 : try
2800 : {
2801 0 : css::uno::Reference< css::frame::XStatusListener > xListener(((css::frame::XStatusListener*)pIt.next()), css::uno::UNO_QUERY);
2802 0 : xListener->statusChanged(aEvent);
2803 : }
2804 0 : catch(const css::uno::RuntimeException&)
2805 : {
2806 0 : pIt.remove();
2807 : }
2808 0 : }
2809 0 : }
2810 0 : }
2811 :
2812 : //-----------------------------------------------
2813 0 : ::rtl::OUString AutoRecovery::implst_getJobDescription(sal_Int32 eJob)
2814 : {
2815 : // describe the current running operation
2816 0 : ::rtl::OUStringBuffer sFeature(256);
2817 0 : sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_PROTOCOL));
2818 :
2819 : // Attention: Because "eJob" is used as a flag field the order of checking these
2820 : // flags is importent. We must preferr job with higher priorities!
2821 : // E.g. EmergencySave has an higher prio then AutoSave ...
2822 : // On the other side there exist a well defined order between two different jobs.
2823 : // e.g. PrepareEmergencySave must be done before EmergencySave is started of course.
2824 :
2825 0 : if ((eJob & AutoRecovery::E_PREPARE_EMERGENCY_SAVE) == AutoRecovery::E_PREPARE_EMERGENCY_SAVE)
2826 0 : sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_PREPARE_EMERGENCY_SAVE));
2827 : else
2828 0 : if ((eJob & AutoRecovery::E_EMERGENCY_SAVE) == AutoRecovery::E_EMERGENCY_SAVE)
2829 0 : sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_EMERGENCY_SAVE));
2830 : else
2831 0 : if ((eJob & AutoRecovery::E_RECOVERY) == AutoRecovery::E_RECOVERY)
2832 0 : sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_RECOVERY));
2833 : else
2834 0 : if ((eJob & AutoRecovery::E_SESSION_SAVE) == AutoRecovery::E_SESSION_SAVE)
2835 0 : sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_SESSION_SAVE));
2836 : else
2837 0 : if ((eJob & AutoRecovery::E_SESSION_QUIET_QUIT) == AutoRecovery::E_SESSION_QUIET_QUIT)
2838 0 : sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_SESSION_QUIET_QUIT));
2839 : else
2840 0 : if ((eJob & AutoRecovery::E_SESSION_RESTORE) == AutoRecovery::E_SESSION_RESTORE)
2841 0 : sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_SESSION_RESTORE));
2842 : else
2843 0 : if ((eJob & AutoRecovery::E_ENTRY_BACKUP) == AutoRecovery::E_ENTRY_BACKUP)
2844 0 : sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_ENTRY_BACKUP));
2845 : else
2846 0 : if ((eJob & AutoRecovery::E_ENTRY_CLEANUP) == AutoRecovery::E_ENTRY_CLEANUP)
2847 0 : sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_ENTRY_CLEANUP));
2848 : else
2849 0 : if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE)
2850 0 : sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_AUTO_SAVE));
2851 : #ifdef ENABLE_WARNINGS
2852 : else if ( eJob != AutoRecovery::E_NO_JOB )
2853 : LOG_WARNING("AutoRecovery::implst_getJobDescription()", "Invalid job identifier detected.")
2854 : #endif
2855 :
2856 0 : return sFeature.makeStringAndClear();
2857 : }
2858 :
2859 : //-----------------------------------------------
2860 8 : sal_Int32 AutoRecovery::implst_classifyJob(const css::util::URL& aURL)
2861 : {
2862 8 : if ( aURL.Protocol == CMD_PROTOCOL )
2863 : {
2864 8 : if ( aURL.Path == CMD_DO_PREPARE_EMERGENCY_SAVE )
2865 0 : return AutoRecovery::E_PREPARE_EMERGENCY_SAVE;
2866 8 : else if ( aURL.Path == CMD_DO_EMERGENCY_SAVE )
2867 0 : return AutoRecovery::E_EMERGENCY_SAVE;
2868 8 : else if ( aURL.Path == CMD_DO_RECOVERY )
2869 0 : return AutoRecovery::E_RECOVERY;
2870 8 : else if ( aURL.Path == CMD_DO_ENTRY_BACKUP )
2871 0 : return AutoRecovery::E_ENTRY_BACKUP;
2872 8 : else if ( aURL.Path == CMD_DO_ENTRY_CLEANUP )
2873 0 : return AutoRecovery::E_ENTRY_CLEANUP;
2874 8 : else if ( aURL.Path == CMD_DO_SESSION_SAVE )
2875 0 : return AutoRecovery::E_SESSION_SAVE;
2876 8 : else if ( aURL.Path == CMD_DO_SESSION_QUIET_QUIT )
2877 0 : return AutoRecovery::E_SESSION_QUIET_QUIT;
2878 8 : else if ( aURL.Path == CMD_DO_SESSION_RESTORE )
2879 0 : return AutoRecovery::E_SESSION_RESTORE;
2880 8 : else if ( aURL.Path == CMD_DO_DISABLE_RECOVERY )
2881 8 : return AutoRecovery::E_DISABLE_AUTORECOVERY;
2882 0 : else if ( aURL.Path == CMD_DO_SET_AUTOSAVE_STATE )
2883 0 : return AutoRecovery::E_SET_AUTOSAVE_STATE;
2884 : }
2885 :
2886 : LOG_WARNING("AutoRecovery::implts_classifyJob()", "Invalid URL (protocol).")
2887 0 : return AutoRecovery::E_NO_JOB;
2888 : }
2889 :
2890 : //-----------------------------------------------
2891 0 : css::frame::FeatureStateEvent AutoRecovery::implst_createFeatureStateEvent( sal_Int32 eJob ,
2892 : const ::rtl::OUString& sEventType,
2893 : AutoRecovery::TDocumentInfo* pInfo )
2894 : {
2895 0 : css::frame::FeatureStateEvent aEvent;
2896 0 : aEvent.FeatureURL.Complete = AutoRecovery::implst_getJobDescription(eJob);
2897 0 : aEvent.FeatureDescriptor = sEventType;
2898 :
2899 0 : if (pInfo && sEventType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(OPERATION_UPDATE)))
2900 : {
2901 : // pack rInfo for transport via UNO
2902 0 : ::comphelper::NamedValueCollection aInfo;
2903 0 : aInfo.put( rtl::OUString(CFG_ENTRY_PROP_ID), pInfo->ID );
2904 0 : aInfo.put( rtl::OUString(CFG_ENTRY_PROP_ORIGINALURL), pInfo->OrgURL );
2905 0 : aInfo.put( rtl::OUString(CFG_ENTRY_PROP_FACTORYURL), pInfo->FactoryURL );
2906 0 : aInfo.put( rtl::OUString(CFG_ENTRY_PROP_TEMPLATEURL), pInfo->TemplateURL );
2907 0 : aInfo.put( rtl::OUString(CFG_ENTRY_PROP_TEMPURL), pInfo->OldTempURL.isEmpty() ? pInfo->NewTempURL : pInfo->OldTempURL );
2908 0 : aInfo.put( rtl::OUString(CFG_ENTRY_PROP_MODULE), pInfo->AppModule) ;
2909 0 : aInfo.put( rtl::OUString(CFG_ENTRY_PROP_TITLE), pInfo->Title);
2910 0 : aInfo.put( rtl::OUString(CFG_ENTRY_PROP_VIEWNAMES), pInfo->ViewNames);
2911 0 : aInfo.put( rtl::OUString(CFG_ENTRY_PROP_DOCUMENTSTATE), pInfo->DocumentState);
2912 :
2913 0 : aEvent.State <<= aInfo.getPropertyValues();
2914 : }
2915 :
2916 0 : return aEvent;
2917 : }
2918 :
2919 : //-----------------------------------------------
2920 0 : void AutoRecovery::implts_resetHandleStates(sal_Bool /*bLoadCache*/)
2921 : {
2922 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2923 :
2924 : // SAFE -> ------------------------------
2925 0 : WriteGuard aWriteLock(m_aLock);
2926 :
2927 0 : AutoRecovery::TDocumentList::iterator pIt;
2928 0 : for ( pIt = m_lDocCache.begin();
2929 0 : pIt != m_lDocCache.end() ;
2930 : ++pIt )
2931 : {
2932 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
2933 0 : rInfo.DocumentState &= ~AutoRecovery::E_HANDLED ;
2934 0 : rInfo.DocumentState &= ~AutoRecovery::E_POSTPONED;
2935 :
2936 : // SAFE -> ------------------------------
2937 0 : aWriteLock.unlock();
2938 0 : implts_flushConfigItem(rInfo);
2939 0 : aWriteLock.lock();
2940 : // <- SAFE ------------------------------
2941 : }
2942 :
2943 0 : aWriteLock.unlock();
2944 : // <- SAFE ----------------------------------
2945 0 : }
2946 :
2947 : //-----------------------------------------------
2948 0 : void AutoRecovery::implts_prepareEmergencySave()
2949 : {
2950 : // Be sure to know all open documents realy .-)
2951 0 : implts_verifyCacheAgainstDesktopDocumentList();
2952 :
2953 : // hide all docs, so the user cant disturb our emergency save .-)
2954 0 : implts_changeAllDocVisibility(sal_False);
2955 0 : }
2956 :
2957 : //-----------------------------------------------
2958 0 : void AutoRecovery::implts_doEmergencySave(const DispatchParams& aParams)
2959 : {
2960 : // Write a hint "we chrashed" into the configuration, so
2961 : // the error report tool is started too in case no recovery
2962 : // documents exists and was saved.
2963 : ::comphelper::ConfigurationHelper::writeDirectKey(
2964 : comphelper::getComponentContext(m_xSMGR),
2965 : rtl::OUString(CFG_PACKAGE_RECOVERY),
2966 : rtl::OUString(CFG_PATH_RECOVERYINFO),
2967 : rtl::OUString(CFG_ENTRY_CRASHED),
2968 : css::uno::makeAny(sal_True),
2969 0 : ::comphelper::ConfigurationHelper::E_STANDARD);
2970 :
2971 : // for all docs, store their current view/names in the configurtion
2972 0 : implts_persistAllActiveViewNames();
2973 :
2974 : // The called method for saving documents runs
2975 : // during normal AutoSave more then once. Because
2976 : // it postpone active documents and save it later.
2977 : // That is normaly done by recalling it from a timer.
2978 : // Here we must do it immediatly!
2979 : // Of course this method returns the right state -
2980 : // because it knows, that we are running in ERMERGENCY SAVE mode .-)
2981 :
2982 0 : sal_Bool bAllowUserIdleLoop = sal_False; // not allowed to change that .-)
2983 0 : AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
2984 0 : do
2985 : {
2986 0 : eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_True, &aParams);
2987 : }
2988 : while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
2989 :
2990 : // reset the handle state of all
2991 : // cache items. Such handle state indicates, that a document
2992 : // was already saved during the THIS(!) EmergencySave session.
2993 : // Of course following recovery session must be started without
2994 : // any "handle" state ...
2995 0 : implts_resetHandleStates(sal_False);
2996 :
2997 : // flush config cached back to disc.
2998 0 : impl_flushALLConfigChanges();
2999 :
3000 : // try to make sure next time office will be started user wont be
3001 : // notified about any other might be running office instance
3002 : // remove ".lock" file from disc !
3003 0 : AutoRecovery::st_impl_removeLockFile();
3004 0 : }
3005 :
3006 : //-----------------------------------------------
3007 0 : void AutoRecovery::implts_doRecovery(const DispatchParams& aParams)
3008 : {
3009 0 : AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
3010 0 : do
3011 : {
3012 0 : eSuggestedTimer = implts_openDocs(aParams);
3013 : }
3014 : while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
3015 :
3016 : // reset the handle state of all
3017 : // cache items. Such handle state indicates, that a document
3018 : // was already saved during the THIS(!) Recovery session.
3019 : // Of course a may be following EmergencySave session must be started without
3020 : // any "handle" state ...
3021 0 : implts_resetHandleStates(sal_True);
3022 :
3023 : // Reset the configuration hint "we was crashed"!
3024 : ::comphelper::ConfigurationHelper::writeDirectKey(
3025 : comphelper::getComponentContext(m_xSMGR),
3026 : rtl::OUString(CFG_PACKAGE_RECOVERY),
3027 : rtl::OUString(CFG_PATH_RECOVERYINFO),
3028 : rtl::OUString(CFG_ENTRY_CRASHED),
3029 : css::uno::makeAny(sal_False),
3030 0 : ::comphelper::ConfigurationHelper::E_STANDARD);
3031 0 : }
3032 :
3033 : //-----------------------------------------------
3034 0 : void AutoRecovery::implts_doSessionSave(const DispatchParams& aParams)
3035 : {
3036 : LOG_RECOVERY("AutoRecovery::implts_doSessionSave()")
3037 :
3038 : // Be sure to know all open documents realy .-)
3039 0 : implts_verifyCacheAgainstDesktopDocumentList();
3040 :
3041 : // for all docs, store their current view/names in the configurtion
3042 0 : implts_persistAllActiveViewNames();
3043 :
3044 : // The called method for saving documents runs
3045 : // during normal AutoSave more then once. Because
3046 : // it postpone active documents and save it later.
3047 : // That is normaly done by recalling it from a timer.
3048 : // Here we must do it immediatly!
3049 : // Of course this method returns the right state -
3050 : // because it knows, that we are running in SESSION SAVE mode .-)
3051 :
3052 0 : sal_Bool bAllowUserIdleLoop = sal_False; // not allowed to change that .-)
3053 0 : AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
3054 0 : do
3055 : {
3056 : // do not remove lock files of the documents, it will be done on session quit
3057 0 : eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_False, &aParams);
3058 : }
3059 : while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
3060 :
3061 : // reset the handle state of all
3062 : // cache items. Such handle state indicates, that a document
3063 : // was already saved during the THIS(!) save session.
3064 : // Of course following restore session must be started without
3065 : // any "handle" state ...
3066 0 : implts_resetHandleStates(sal_False);
3067 :
3068 : // flush config cached back to disc.
3069 0 : impl_flushALLConfigChanges();
3070 0 : }
3071 :
3072 : //-----------------------------------------------
3073 0 : void AutoRecovery::implts_doSessionQuietQuit(const DispatchParams& /*aParams*/)
3074 : {
3075 : LOG_RECOVERY("AutoRecovery::implts_doSessionQuietQuit()")
3076 :
3077 : // try to make sure next time office will be started user wont be
3078 : // notified about any other might be running office instance
3079 : // remove ".lock" file from disc !
3080 : // it is done as a first action for session save since Gnome sessions
3081 : // do not provide enough time for shutdown, and the dialog looks to be
3082 : // confusing for the user
3083 0 : AutoRecovery::st_impl_removeLockFile();
3084 :
3085 : // reset all modified documents, so the dont show any UI on closing ...
3086 : // and close all documents, so we can shutdown the OS!
3087 0 : implts_prepareSessionShutdown();
3088 :
3089 : // Write a hint for "stored session data" into the configuration, so
3090 : // the on next startup we know what's happen last time
3091 : ::comphelper::ConfigurationHelper::writeDirectKey(
3092 : comphelper::getComponentContext(m_xSMGR),
3093 : rtl::OUString(CFG_PACKAGE_RECOVERY),
3094 : rtl::OUString(CFG_PATH_RECOVERYINFO),
3095 : rtl::OUString(CFG_ENTRY_SESSIONDATA),
3096 : css::uno::makeAny(sal_True),
3097 0 : ::comphelper::ConfigurationHelper::E_STANDARD);
3098 :
3099 : // flush config cached back to disc.
3100 0 : impl_flushALLConfigChanges();
3101 0 : }
3102 :
3103 :
3104 : //-----------------------------------------------
3105 0 : void AutoRecovery::implts_doSessionRestore(const DispatchParams& aParams)
3106 : {
3107 : LOG_RECOVERY("AutoRecovery::implts_doSessionRestore() ...")
3108 :
3109 0 : AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
3110 0 : do
3111 : {
3112 0 : eSuggestedTimer = implts_openDocs(aParams);
3113 : }
3114 : while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
3115 :
3116 : // reset the handle state of all
3117 : // cache items. Such handle state indicates, that a document
3118 : // was already saved during the THIS(!) Restore session.
3119 : // Of course a may be following save session must be started without
3120 : // any "handle" state ...
3121 0 : implts_resetHandleStates(sal_True);
3122 :
3123 : // make all opened documents visible
3124 0 : implts_changeAllDocVisibility(sal_True);
3125 :
3126 : // Reset the configuration hint for "session save"!
3127 : LOG_RECOVERY("... reset config key 'SessionData'")
3128 : ::comphelper::ConfigurationHelper::writeDirectKey(
3129 : comphelper::getComponentContext(m_xSMGR),
3130 : rtl::OUString(CFG_PACKAGE_RECOVERY),
3131 : rtl::OUString(CFG_PATH_RECOVERYINFO),
3132 : rtl::OUString(CFG_ENTRY_SESSIONDATA),
3133 : css::uno::makeAny(sal_False),
3134 0 : ::comphelper::ConfigurationHelper::E_STANDARD);
3135 :
3136 : LOG_RECOVERY("... AutoRecovery::implts_doSessionRestore()")
3137 0 : }
3138 :
3139 : //-----------------------------------------------
3140 0 : void AutoRecovery::implts_backupWorkingEntry(const DispatchParams& aParams)
3141 : {
3142 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
3143 :
3144 0 : AutoRecovery::TDocumentList::iterator pIt;
3145 0 : for ( pIt = m_lDocCache.begin();
3146 0 : pIt != m_lDocCache.end() ;
3147 : ++pIt )
3148 : {
3149 0 : const AutoRecovery::TDocumentInfo& rInfo = *pIt;
3150 0 : if (rInfo.ID != aParams.m_nWorkingEntryID)
3151 0 : continue;
3152 :
3153 0 : ::rtl::OUString sSourceURL;
3154 : // Prefer temp file. It contains the changes against the original document!
3155 0 : if (!rInfo.OldTempURL.isEmpty())
3156 0 : sSourceURL = rInfo.OldTempURL;
3157 0 : else if (!rInfo.NewTempURL.isEmpty())
3158 0 : sSourceURL = rInfo.NewTempURL;
3159 0 : else if (!rInfo.OrgURL.isEmpty())
3160 0 : sSourceURL = rInfo.OrgURL;
3161 : else
3162 0 : continue; // nothing real to save! An unmodified but new created document.
3163 :
3164 0 : INetURLObject aParser(sSourceURL);
3165 : // AutoRecovery::EFailureSafeResult eResult =
3166 0 : implts_copyFile(sSourceURL, aParams.m_sSavePath, aParser.getName());
3167 :
3168 : // TODO: Check eResult and react for errors (InteractionHandler!?)
3169 : // Currently we ignore it ...
3170 : // DONT UPDATE THE CACHE OR REMOVE ANY TEMP. FILES FROM DISK.
3171 : // That has to be forced from outside explicitly.
3172 : // See implts_cleanUpWorkingEntry() for further details.
3173 0 : }
3174 0 : }
3175 :
3176 : //-----------------------------------------------
3177 0 : void AutoRecovery::implts_cleanUpWorkingEntry(const DispatchParams& aParams)
3178 : {
3179 0 : CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
3180 :
3181 0 : AutoRecovery::TDocumentList::iterator pIt;
3182 0 : for ( pIt = m_lDocCache.begin();
3183 0 : pIt != m_lDocCache.end() ;
3184 : ++pIt )
3185 : {
3186 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
3187 0 : if (rInfo.ID != aParams.m_nWorkingEntryID)
3188 0 : continue;
3189 :
3190 0 : AutoRecovery::st_impl_removeFile(rInfo.OldTempURL);
3191 0 : AutoRecovery::st_impl_removeFile(rInfo.NewTempURL);
3192 0 : implts_flushConfigItem(rInfo, sal_True); // sal_True => remove it from xml config!
3193 :
3194 0 : m_lDocCache.erase(pIt);
3195 0 : break; /// !!! pIt is not defined any longer ... further this function has finished it's work
3196 0 : }
3197 0 : }
3198 :
3199 : //-----------------------------------------------
3200 0 : AutoRecovery::EFailureSafeResult AutoRecovery::implts_copyFile(const ::rtl::OUString& sSource ,
3201 : const ::rtl::OUString& sTargetPath,
3202 : const ::rtl::OUString& sTargetName)
3203 : {
3204 : // create content for the parent folder and call transfer on that content with the source content
3205 : // and the destination file name as parameters
3206 :
3207 0 : css::uno::Reference< css::ucb::XCommandEnvironment > xEnvironment;
3208 :
3209 0 : ::ucbhelper::Content aSourceContent;
3210 0 : ::ucbhelper::Content aTargetContent;
3211 :
3212 : try
3213 : {
3214 0 : aTargetContent = ::ucbhelper::Content(sTargetPath, xEnvironment, comphelper::getComponentContext(m_xSMGR));
3215 : }
3216 0 : catch(const css::uno::Exception&)
3217 : {
3218 0 : return AutoRecovery::E_WRONG_TARGET_PATH;
3219 : }
3220 :
3221 : sal_Int32 nNameClash;
3222 0 : nNameClash = css::ucb::NameClash::RENAME;
3223 :
3224 : try
3225 : {
3226 0 : ::ucbhelper::Content::create(sSource, xEnvironment, comphelper::getComponentContext(m_xSMGR), aSourceContent);
3227 0 : aTargetContent.transferContent(aSourceContent, ::ucbhelper::InsertOperation_COPY, sTargetName, nNameClash);
3228 : }
3229 0 : catch(const css::uno::Exception&)
3230 : {
3231 0 : return AutoRecovery::E_ORIGINAL_FILE_MISSING;
3232 : }
3233 :
3234 0 : return AutoRecovery::E_COPIED;
3235 : }
3236 :
3237 : //-----------------------------------------------
3238 0 : sal_Bool SAL_CALL AutoRecovery::convertFastPropertyValue( css::uno::Any& /*aConvertedValue*/,
3239 : css::uno::Any& /*aOldValue*/ ,
3240 : sal_Int32 /*nHandle*/ ,
3241 : const css::uno::Any& /*aValue*/ )
3242 : throw(css::lang::IllegalArgumentException)
3243 : {
3244 : // not needed currently
3245 0 : return sal_False;
3246 : }
3247 :
3248 : //-----------------------------------------------
3249 0 : void SAL_CALL AutoRecovery::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/,
3250 : const css::uno::Any& /*aValue*/ )
3251 : throw(css::uno::Exception)
3252 : {
3253 : // not needed currently
3254 0 : }
3255 :
3256 : //-----------------------------------------------
3257 0 : void SAL_CALL AutoRecovery::getFastPropertyValue(css::uno::Any& aValue ,
3258 : sal_Int32 nHandle) const
3259 : {
3260 0 : switch(nHandle)
3261 : {
3262 : case AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA :
3263 : {
3264 0 : sal_Bool bSessionData = sal_False;
3265 : ::comphelper::ConfigurationHelper::readDirectKey(
3266 : comphelper::getComponentContext(m_xSMGR),
3267 : rtl::OUString(CFG_PACKAGE_RECOVERY),
3268 : rtl::OUString(CFG_PATH_RECOVERYINFO),
3269 : rtl::OUString(CFG_ENTRY_SESSIONDATA),
3270 0 : ::comphelper::ConfigurationHelper::E_READONLY) >>= bSessionData;
3271 :
3272 0 : sal_Bool bRecoveryData = ((sal_Bool)(m_lDocCache.size()>0));
3273 :
3274 : // exists session data ... => then we cant say, that these
3275 : // data are valid for recovery. So we have to return sal_False then!
3276 0 : if (bSessionData)
3277 0 : bRecoveryData = sal_False;
3278 :
3279 0 : aValue <<= bRecoveryData;
3280 : }
3281 0 : break;
3282 :
3283 : case AUTORECOVERY_PROPHANDLE_CRASHED :
3284 : aValue = ::comphelper::ConfigurationHelper::readDirectKey(
3285 : comphelper::getComponentContext(m_xSMGR),
3286 : rtl::OUString(CFG_PACKAGE_RECOVERY),
3287 : rtl::OUString(CFG_PATH_RECOVERYINFO),
3288 : rtl::OUString(CFG_ENTRY_CRASHED),
3289 0 : ::comphelper::ConfigurationHelper::E_READONLY);
3290 0 : break;
3291 :
3292 : case AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA :
3293 : aValue = ::comphelper::ConfigurationHelper::readDirectKey(
3294 : comphelper::getComponentContext(m_xSMGR),
3295 : rtl::OUString(CFG_PACKAGE_RECOVERY),
3296 : rtl::OUString(CFG_PATH_RECOVERYINFO),
3297 : rtl::OUString(CFG_ENTRY_SESSIONDATA),
3298 0 : ::comphelper::ConfigurationHelper::E_READONLY);
3299 0 : break;
3300 : }
3301 0 : }
3302 :
3303 : //-----------------------------------------------
3304 0 : const css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor()
3305 : {
3306 : const css::beans::Property pPropertys[] =
3307 : {
3308 0 : css::beans::Property( AUTORECOVERY_PROPNAME_CRASHED , AUTORECOVERY_PROPHANDLE_CRASHED , ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
3309 0 : css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_RECOVERYDATA, AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA, ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
3310 0 : css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_SESSIONDATA , AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA , ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
3311 0 : };
3312 0 : const css::uno::Sequence< css::beans::Property > lPropertyDescriptor(pPropertys, AUTORECOVERY_PROPCOUNT);
3313 0 : return lPropertyDescriptor;
3314 : }
3315 :
3316 : //-----------------------------------------------
3317 0 : ::cppu::IPropertyArrayHelper& SAL_CALL AutoRecovery::getInfoHelper()
3318 : {
3319 : static ::cppu::OPropertyArrayHelper* pInfoHelper = 0;
3320 0 : if(!pInfoHelper)
3321 : {
3322 0 : ::osl::MutexGuard aGuard( LockHelper::getGlobalLock().getShareableOslMutex() );
3323 0 : if(!pInfoHelper)
3324 : {
3325 0 : static ::cppu::OPropertyArrayHelper aInfoHelper(impl_getStaticPropertyDescriptor(), sal_True);
3326 0 : pInfoHelper = &aInfoHelper;
3327 0 : }
3328 : }
3329 :
3330 0 : return (*pInfoHelper);
3331 : }
3332 :
3333 : //-----------------------------------------------
3334 0 : css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL AutoRecovery::getPropertySetInfo()
3335 : throw(css::uno::RuntimeException)
3336 : {
3337 : static css::uno::Reference< css::beans::XPropertySetInfo >* pInfo = 0;
3338 0 : if(!pInfo)
3339 : {
3340 0 : ::osl::MutexGuard aGuard( LockHelper::getGlobalLock().getShareableOslMutex() );
3341 0 : if(!pInfo)
3342 : {
3343 0 : static css::uno::Reference< css::beans::XPropertySetInfo > xInfo(createPropertySetInfo(getInfoHelper()));
3344 0 : pInfo = &xInfo;
3345 0 : }
3346 : }
3347 :
3348 0 : return (*pInfo);
3349 : }
3350 :
3351 : //-----------------------------------------------
3352 0 : void AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()
3353 : {
3354 : LOG_RECOVERY("AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList() ...")
3355 :
3356 : // SAFE -> ----------------------------------
3357 0 : WriteGuard aWriteLock(m_aLock);
3358 0 : css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
3359 0 : aWriteLock.unlock();
3360 : // <- SAFE ----------------------------------
3361 :
3362 : try
3363 : {
3364 : css::uno::Reference< css::frame::XFramesSupplier > xDesktop(
3365 0 : xSMGR->createInstance(SERVICENAME_DESKTOP),
3366 0 : css::uno::UNO_QUERY_THROW);
3367 :
3368 : css::uno::Reference< css::container::XIndexAccess > xContainer(
3369 0 : xDesktop->getFrames(),
3370 0 : css::uno::UNO_QUERY_THROW);
3371 :
3372 0 : sal_Int32 i = 0;
3373 0 : sal_Int32 c = xContainer->getCount();
3374 :
3375 0 : for (i=0; i<c; ++i)
3376 : {
3377 0 : css::uno::Reference< css::frame::XFrame > xFrame;
3378 : try
3379 : {
3380 0 : xContainer->getByIndex(i) >>= xFrame;
3381 0 : if (!xFrame.is())
3382 0 : continue;
3383 : }
3384 : // can happen in multithreaded environments, that frames was removed from the container during this loop runs!
3385 : // Ignore it.
3386 0 : catch(const css::lang::IndexOutOfBoundsException&)
3387 : {
3388 0 : continue;
3389 : }
3390 :
3391 : // We are interested on visible documents only.
3392 : // Note: It's n optional interface .-(
3393 : css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(
3394 0 : xFrame->getContainerWindow(),
3395 0 : css::uno::UNO_QUERY);
3396 0 : if (
3397 0 : (!xVisibleCheck.is() ) ||
3398 0 : (!xVisibleCheck->isVisible())
3399 : )
3400 : {
3401 0 : continue;
3402 : }
3403 :
3404 : // extract the model from the frame.
3405 : // Ignore "view only" frames, which does not have a model.
3406 0 : css::uno::Reference< css::frame::XController > xController;
3407 0 : css::uno::Reference< css::frame::XModel > xModel;
3408 :
3409 0 : xController = xFrame->getController();
3410 0 : if (xController.is())
3411 0 : xModel = xController->getModel();
3412 0 : if (!xModel.is())
3413 0 : continue;
3414 :
3415 : // insert model into cache ...
3416 : // If the model is already well known inside cache
3417 : // it's information set will be updated by asking the
3418 : // model again for it's new states.
3419 0 : implts_registerDocument(xModel);
3420 0 : }
3421 : }
3422 0 : catch(const css::uno::RuntimeException&)
3423 : {
3424 0 : throw;
3425 : }
3426 0 : catch(const css::uno::Exception&)
3427 : {
3428 0 : }
3429 :
3430 : LOG_RECOVERY("... AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()")
3431 0 : }
3432 :
3433 : //-----------------------------------------------
3434 0 : sal_Bool AutoRecovery::impl_enoughDiscSpace(sal_Int32 nRequiredSpace)
3435 : {
3436 : #ifdef SIMULATE_FULL_DISC
3437 : return sal_False;
3438 : #else // SIMULATE_FULL_DISC
3439 : // In case an error occures and we are not able to retrieve the needed information
3440 : // it's better to "disable" the feature ShowErrorOnFullDisc !
3441 : // Otherwhise we start a confusing process of error handling ...
3442 :
3443 0 : sal_uInt64 nFreeSpace = SAL_MAX_UINT64;
3444 :
3445 0 : ::rtl::OUString sBackupPath(SvtPathOptions().GetBackupPath());
3446 0 : ::osl::VolumeInfo aInfo (osl_VolumeInfo_Mask_FreeSpace);
3447 0 : ::osl::FileBase::RC aRC = ::osl::Directory::getVolumeInfo(sBackupPath, aInfo);
3448 :
3449 0 : if (
3450 0 : (aInfo.isValid(osl_VolumeInfo_Mask_FreeSpace)) &&
3451 : (aRC == ::osl::FileBase::E_None )
3452 : )
3453 : {
3454 0 : nFreeSpace = aInfo.getFreeSpace();
3455 : }
3456 :
3457 0 : sal_uInt64 nFreeMB = (nFreeSpace/1048576);
3458 0 : return (nFreeMB >= (sal_uInt64)nRequiredSpace);
3459 : #endif // SIMULATE_FULL_DISC
3460 : }
3461 :
3462 : //-----------------------------------------------
3463 0 : void AutoRecovery::impl_showFullDiscError()
3464 : {
3465 0 : rtl::OUString sBtn(FWK_RESSTR(STR_FULL_DISC_RETRY_BUTTON));
3466 0 : rtl::OUString sMsg(FWK_RESSTR(STR_FULL_DISC_MSG));
3467 :
3468 0 : rtl::OUString sBackupURL(SvtPathOptions().GetBackupPath());
3469 0 : INetURLObject aConverter(sBackupURL);
3470 : sal_Unicode aDelimiter;
3471 0 : rtl::OUString sBackupPath = aConverter.getFSysPath(INetURLObject::FSYS_DETECT, &aDelimiter);
3472 0 : if (sBackupPath.getLength() < 1)
3473 0 : sBackupPath = sBackupURL;
3474 :
3475 : ErrorBox dlgError(
3476 : 0, WB_OK,
3477 0 : sMsg.replaceAll("%PATH", sBackupPath));
3478 0 : dlgError.SetButtonText(dlgError.GetButtonId(0), sBtn);
3479 0 : dlgError.Execute();
3480 0 : }
3481 :
3482 : //-----------------------------------------------
3483 0 : void AutoRecovery::impl_establishProgress(const AutoRecovery::TDocumentInfo& rInfo ,
3484 : ::comphelper::MediaDescriptor& rArgs ,
3485 : const css::uno::Reference< css::frame::XFrame >& xNewFrame)
3486 : {
3487 : // external well known frame must be preferred (because it was created by ourself
3488 : // for loading documents into this frame)!
3489 : // But if no frame exists ... we can try to locate it using any frame bound to the provided
3490 : // document. Of course we must live without any frame in case the document does not exists at this
3491 : // point. But this state shouldnt occure. In such case xNewFrame should be valid ... hopefully .-)
3492 0 : css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame;
3493 0 : if (
3494 0 : (!xFrame.is() ) &&
3495 0 : (rInfo.Document.is())
3496 : )
3497 : {
3498 0 : css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController();
3499 0 : if (xController.is())
3500 0 : xFrame = xController->getFrame();
3501 : }
3502 :
3503 : // Any outside progress must be used ...
3504 : // Only if there is no progress, we can create our own one.
3505 0 : css::uno::Reference< css::task::XStatusIndicator > xInternalProgress;
3506 : css::uno::Reference< css::task::XStatusIndicator > xExternalProgress = rArgs.getUnpackedValueOrDefault(
3507 0 : ::comphelper::MediaDescriptor::PROP_STATUSINDICATOR(),
3508 0 : css::uno::Reference< css::task::XStatusIndicator >() );
3509 :
3510 : // Normaly a progress is set from outside (e.g. by the CrashSave/Recovery dialog, which uses our dispatch API).
3511 : // But for a normal auto save we dont have such "external progress"... because this function is triggered by our own timer then.
3512 : // In such case we must create our own progress !
3513 0 : if (
3514 0 : (! xExternalProgress.is()) &&
3515 0 : (xFrame.is() )
3516 : )
3517 : {
3518 0 : css::uno::Reference< css::task::XStatusIndicatorFactory > xProgressFactory(xFrame, css::uno::UNO_QUERY);
3519 0 : if (xProgressFactory.is())
3520 0 : xInternalProgress = xProgressFactory->createStatusIndicator();
3521 : }
3522 :
3523 : // HACK
3524 : // An external provided progress (most given by the CrashSave/Recovery dialog)
3525 : // must be preferred. But we know that some application filters query it's own progress instance
3526 : // at the frame method Frame::createStatusIndicator().
3527 : // So we use a two step mechanism:
3528 : // 1) we set the progress inside the MediaDescriptor, which will be provided to the filter
3529 : // 2) and we set a special Frame property, which overwrites the normal behaviour of Frame::createStatusIndicator .-)
3530 : // But we supress 2) in case we uses an internal progress. Because then it doesnt matter
3531 : // if our applications make it wrong. In such case the internal progress resists at the same frame
3532 : // and there is no need to forward progress activities to e.g. an outside dialog .-)
3533 0 : if (
3534 0 : (xExternalProgress.is()) &&
3535 0 : (xFrame.is() )
3536 : )
3537 : {
3538 0 : css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY);
3539 0 : if (xFrameProps.is())
3540 0 : xFrameProps->setPropertyValue(FRAME_PROPNAME_INDICATORINTERCEPTION, css::uno::makeAny(xExternalProgress));
3541 : }
3542 :
3543 : // But inside the MediaDescriptor we must set our own create progress ...
3544 : // in case there is not already anothe rprogress set.
3545 0 : rArgs.createItemIfMissing(::comphelper::MediaDescriptor::PROP_STATUSINDICATOR(), xInternalProgress);
3546 0 : }
3547 :
3548 : //-----------------------------------------------
3549 0 : void AutoRecovery::impl_forgetProgress(const AutoRecovery::TDocumentInfo& rInfo ,
3550 : ::comphelper::MediaDescriptor& rArgs ,
3551 : const css::uno::Reference< css::frame::XFrame >& xNewFrame)
3552 : {
3553 : // external well known frame must be preferred (because it was created by ourself
3554 : // for loading documents into this frame)!
3555 : // But if no frame exists ... we can try to locate it using any frame bound to the provided
3556 : // document. Of course we must live without any frame in case the document does not exists at this
3557 : // point. But this state shouldnt occure. In such case xNewFrame should be valid ... hopefully .-)
3558 0 : css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame;
3559 0 : if (
3560 0 : (!xFrame.is() ) &&
3561 0 : (rInfo.Document.is())
3562 : )
3563 : {
3564 0 : css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController();
3565 0 : if (xController.is())
3566 0 : xFrame = xController->getFrame();
3567 : }
3568 :
3569 : // stop progress interception on corresponding frame.
3570 0 : css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY);
3571 0 : if (xFrameProps.is())
3572 0 : xFrameProps->setPropertyValue(FRAME_PROPNAME_INDICATORINTERCEPTION, css::uno::makeAny(css::uno::Reference< css::task::XStatusIndicator >()));
3573 :
3574 : // forget progress inside list of arguments.
3575 0 : ::comphelper::MediaDescriptor::iterator pArg = rArgs.find(::comphelper::MediaDescriptor::PROP_STATUSINDICATOR());
3576 0 : if (pArg != rArgs.end())
3577 : {
3578 0 : rArgs.erase(pArg);
3579 0 : pArg = rArgs.end();
3580 0 : }
3581 0 : }
3582 :
3583 : //-----------------------------------------------
3584 0 : void AutoRecovery::impl_flushALLConfigChanges()
3585 : {
3586 : try
3587 : {
3588 : // SAFE ->
3589 0 : ReadGuard aReadLock(m_aLock);
3590 0 : css::uno::Reference< css::uno::XInterface > xRecoveryCfg(m_xRecoveryCFG, css::uno::UNO_QUERY);
3591 0 : aReadLock.unlock();
3592 : // <- SAFE
3593 :
3594 0 : if (xRecoveryCfg.is())
3595 0 : ::comphelper::ConfigurationHelper::flush(xRecoveryCfg);
3596 :
3597 : // SOLAR SAFE ->
3598 0 : SolarMutexGuard aGuard;
3599 0 : ::utl::ConfigManager::storeConfigItems();
3600 : }
3601 0 : catch(const css::uno::Exception&)
3602 : {
3603 : }
3604 0 : }
3605 :
3606 : //-----------------------------------------------
3607 0 : void AutoRecovery::st_impl_removeFile(const ::rtl::OUString& sURL)
3608 : {
3609 0 : if ( sURL.isEmpty())
3610 0 : return;
3611 :
3612 : try
3613 : {
3614 0 : ::ucbhelper::Content aContent = ::ucbhelper::Content(sURL, css::uno::Reference< css::ucb::XCommandEnvironment >(), comphelper::getComponentContext(m_xSMGR));
3615 0 : aContent.executeCommand(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("delete")), css::uno::makeAny(sal_True));
3616 : }
3617 0 : catch(const css::uno::Exception&)
3618 : {
3619 : }
3620 : }
3621 :
3622 : //-----------------------------------------------
3623 0 : void AutoRecovery::st_impl_removeLockFile()
3624 : {
3625 : try
3626 : {
3627 0 : ::rtl::OUString sUserURL;
3628 0 : ::utl::Bootstrap::locateUserInstallation( sUserURL );
3629 :
3630 0 : ::rtl::OUStringBuffer sLockURLBuf;
3631 0 : sLockURLBuf.append (sUserURL);
3632 0 : sLockURLBuf.appendAscii("/.lock");
3633 0 : ::rtl::OUString sLockURL = sLockURLBuf.makeStringAndClear();
3634 :
3635 0 : AutoRecovery::st_impl_removeFile(sLockURL);
3636 : }
3637 0 : catch(const css::uno::Exception&)
3638 : {
3639 : }
3640 0 : }
3641 :
3642 : } // namespace framework
3643 :
3644 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|