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