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