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