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