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