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