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