Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / PollAgent.cs @ 4b58c004

History | View | Annotate | Download (54.3 kB)

1 aa7ac00e Panagiotis Kanavos
#region
2 aa7ac00e Panagiotis Kanavos
/* -----------------------------------------------------------------------
3 aa7ac00e Panagiotis Kanavos
 * <copyright file="PollAgent.cs" company="GRNet">
4 aa7ac00e Panagiotis Kanavos
 * 
5 aa7ac00e Panagiotis Kanavos
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
6 aa7ac00e Panagiotis Kanavos
 *
7 aa7ac00e Panagiotis Kanavos
 * Redistribution and use in source and binary forms, with or
8 aa7ac00e Panagiotis Kanavos
 * without modification, are permitted provided that the following
9 aa7ac00e Panagiotis Kanavos
 * conditions are met:
10 aa7ac00e Panagiotis Kanavos
 *
11 aa7ac00e Panagiotis Kanavos
 *   1. Redistributions of source code must retain the above
12 aa7ac00e Panagiotis Kanavos
 *      copyright notice, this list of conditions and the following
13 aa7ac00e Panagiotis Kanavos
 *      disclaimer.
14 aa7ac00e Panagiotis Kanavos
 *
15 aa7ac00e Panagiotis Kanavos
 *   2. Redistributions in binary form must reproduce the above
16 aa7ac00e Panagiotis Kanavos
 *      copyright notice, this list of conditions and the following
17 aa7ac00e Panagiotis Kanavos
 *      disclaimer in the documentation and/or other materials
18 aa7ac00e Panagiotis Kanavos
 *      provided with the distribution.
19 aa7ac00e Panagiotis Kanavos
 *
20 aa7ac00e Panagiotis Kanavos
 *
21 aa7ac00e Panagiotis Kanavos
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22 aa7ac00e Panagiotis Kanavos
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 aa7ac00e Panagiotis Kanavos
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 aa7ac00e Panagiotis Kanavos
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25 aa7ac00e Panagiotis Kanavos
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 aa7ac00e Panagiotis Kanavos
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 aa7ac00e Panagiotis Kanavos
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 aa7ac00e Panagiotis Kanavos
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 aa7ac00e Panagiotis Kanavos
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 aa7ac00e Panagiotis Kanavos
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 aa7ac00e Panagiotis Kanavos
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 aa7ac00e Panagiotis Kanavos
 * POSSIBILITY OF SUCH DAMAGE.
33 aa7ac00e Panagiotis Kanavos
 *
34 aa7ac00e Panagiotis Kanavos
 * The views and conclusions contained in the software and
35 aa7ac00e Panagiotis Kanavos
 * documentation are those of the authors and should not be
36 aa7ac00e Panagiotis Kanavos
 * interpreted as representing official policies, either expressed
37 aa7ac00e Panagiotis Kanavos
 * or implied, of GRNET S.A.
38 aa7ac00e Panagiotis Kanavos
 * </copyright>
39 aa7ac00e Panagiotis Kanavos
 * -----------------------------------------------------------------------
40 aa7ac00e Panagiotis Kanavos
 */
41 aa7ac00e Panagiotis Kanavos
#endregion
42 aa7ac00e Panagiotis Kanavos
43 aa7ac00e Panagiotis Kanavos
using System.Collections.Concurrent;
44 aa7ac00e Panagiotis Kanavos
using System.ComponentModel.Composition;
45 aa7ac00e Panagiotis Kanavos
using System.Diagnostics;
46 aa7ac00e Panagiotis Kanavos
using System.Diagnostics.Contracts;
47 aa7ac00e Panagiotis Kanavos
using System.IO;
48 c945b450 pkanavos
using System.Linq.Expressions;
49 db8a9589 Panagiotis Kanavos
using System.Reflection;
50 1cc1e8c5 pkanavos
using System.Security.Cryptography;
51 aa7ac00e Panagiotis Kanavos
using System.Threading;
52 aa7ac00e Panagiotis Kanavos
using System.Threading.Tasks;
53 4b58c004 pkanavos
using System.Threading.Tasks.Dataflow;
54 aa7ac00e Panagiotis Kanavos
using Castle.ActiveRecord;
55 aa7ac00e Panagiotis Kanavos
using Pithos.Interfaces;
56 aa7ac00e Panagiotis Kanavos
using Pithos.Network;
57 aa7ac00e Panagiotis Kanavos
using log4net;
58 aa7ac00e Panagiotis Kanavos
59 aa7ac00e Panagiotis Kanavos
namespace Pithos.Core.Agents
60 aa7ac00e Panagiotis Kanavos
{
61 aa7ac00e Panagiotis Kanavos
    using System;
62 aa7ac00e Panagiotis Kanavos
    using System.Collections.Generic;
63 aa7ac00e Panagiotis Kanavos
    using System.Linq;
64 aa7ac00e Panagiotis Kanavos
65 4b58c004 pkanavos
    public class PollRequest
66 4b58c004 pkanavos
    {
67 4b58c004 pkanavos
        public DateTime? Since { get; set; }
68 4b58c004 pkanavos
        public IEnumerable<string> Batch { get; set; }
69 4b58c004 pkanavos
    }
70 4b58c004 pkanavos
71 c945b450 pkanavos
    [DebuggerDisplay("{FilePath} C:{C} L:{L} S:{S}")]
72 c945b450 pkanavos
    public class StateTuple
73 c945b450 pkanavos
    {
74 c945b450 pkanavos
        public string FilePath { get; private set; }
75 c945b450 pkanavos
76 1cc1e8c5 pkanavos
        public string MD5 { get; set; }
77 1cc1e8c5 pkanavos
78 c945b450 pkanavos
        public string L
79 c945b450 pkanavos
        {
80 c945b450 pkanavos
            get { return FileState==null?null:FileState.Checksum; }
81 c945b450 pkanavos
        }
82 c945b450 pkanavos
83 0670c7e1 pkanavos
        private string _c;
84 0670c7e1 pkanavos
        public string C
85 0670c7e1 pkanavos
        {
86 0670c7e1 pkanavos
            get { return _c; }
87 0670c7e1 pkanavos
            set {
88 0670c7e1 pkanavos
                _c = String.IsNullOrWhiteSpace(value) ? null : value;
89 0670c7e1 pkanavos
            }
90 0670c7e1 pkanavos
        }
91 c945b450 pkanavos
92 c945b450 pkanavos
        public string S
93 c945b450 pkanavos
        {
94 1cc1e8c5 pkanavos
            get { return ObjectInfo == null ? null : ObjectInfo.X_Object_Hash; }
95 c945b450 pkanavos
        }
96 c945b450 pkanavos
97 c945b450 pkanavos
        private FileSystemInfo _fileInfo;
98 732276d3 pkanavos
        private TreeHash _merkle;
99 732276d3 pkanavos
100 c945b450 pkanavos
        public FileSystemInfo FileInfo
101 c945b450 pkanavos
        {
102 c945b450 pkanavos
            get { return _fileInfo; }
103 c945b450 pkanavos
            set
104 c945b450 pkanavos
            {
105 c945b450 pkanavos
                _fileInfo = value;
106 c945b450 pkanavos
                FilePath = value.FullName;
107 c945b450 pkanavos
            }
108 c945b450 pkanavos
        }
109 c945b450 pkanavos
110 c945b450 pkanavos
        public FileState FileState { get; set; }
111 c945b450 pkanavos
        public ObjectInfo ObjectInfo{ get; set; }
112 c945b450 pkanavos
113 732276d3 pkanavos
114 732276d3 pkanavos
        public TreeHash Merkle
115 732276d3 pkanavos
        {
116 732276d3 pkanavos
            get {
117 732276d3 pkanavos
                return _merkle;
118 732276d3 pkanavos
            }
119 732276d3 pkanavos
            set {
120 732276d3 pkanavos
                _merkle = value;
121 732276d3 pkanavos
                C = _merkle.TopHash.ToHashString();
122 732276d3 pkanavos
            }
123 732276d3 pkanavos
        }
124 732276d3 pkanavos
125 c945b450 pkanavos
        public StateTuple() { }
126 c945b450 pkanavos
127 c945b450 pkanavos
        public StateTuple(FileSystemInfo info)
128 c945b450 pkanavos
        {
129 c945b450 pkanavos
            FileInfo = info;
130 c945b450 pkanavos
        }
131 c945b450 pkanavos
132 c945b450 pkanavos
133 c945b450 pkanavos
    }
134 c945b450 pkanavos
135 c945b450 pkanavos
136 aa7ac00e Panagiotis Kanavos
    /// <summary>
137 aa7ac00e Panagiotis Kanavos
    /// PollAgent periodically polls the server to detect object changes. The agent retrieves a listing of all
138 aa7ac00e Panagiotis Kanavos
    /// objects and compares it with a previously cached version to detect differences. 
139 aa7ac00e Panagiotis Kanavos
    /// New files are downloaded, missing files are deleted from the local file system and common files are compared
140 aa7ac00e Panagiotis Kanavos
    /// to determine the appropriate action
141 aa7ac00e Panagiotis Kanavos
    /// </summary>
142 aa7ac00e Panagiotis Kanavos
    [Export]
143 aa7ac00e Panagiotis Kanavos
    public class PollAgent
144 aa7ac00e Panagiotis Kanavos
    {
145 db8a9589 Panagiotis Kanavos
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
146 aa7ac00e Panagiotis Kanavos
147 aa7ac00e Panagiotis Kanavos
        [System.ComponentModel.Composition.Import]
148 aa7ac00e Panagiotis Kanavos
        public IStatusKeeper StatusKeeper { get; set; }
149 aa7ac00e Panagiotis Kanavos
150 aa7ac00e Panagiotis Kanavos
        [System.ComponentModel.Composition.Import]
151 aa7ac00e Panagiotis Kanavos
        public IPithosSettings Settings { get; set; }
152 aa7ac00e Panagiotis Kanavos
153 aa7ac00e Panagiotis Kanavos
        [System.ComponentModel.Composition.Import]
154 aa7ac00e Panagiotis Kanavos
        public NetworkAgent NetworkAgent { get; set; }
155 aa7ac00e Panagiotis Kanavos
156 2341c603 pkanavos
        [System.ComponentModel.Composition.Import]
157 2341c603 pkanavos
        public Selectives Selectives { get; set; }
158 2341c603 pkanavos
159 aa7ac00e Panagiotis Kanavos
        public IStatusNotification StatusNotification { get; set; }
160 aa7ac00e Panagiotis Kanavos
161 c945b450 pkanavos
        private CancellationTokenSource _currentOperationCancellation = new CancellationTokenSource();
162 c945b450 pkanavos
163 c945b450 pkanavos
        public void CancelCurrentOperation()
164 c945b450 pkanavos
        {
165 c945b450 pkanavos
            //What does it mean to cancel the current upload/download?
166 c945b450 pkanavos
            //Obviously, the current operation will be cancelled by throwing
167 c945b450 pkanavos
            //a cancellation exception.
168 c945b450 pkanavos
            //
169 c945b450 pkanavos
            //The default behavior is to retry any operations that throw.
170 c945b450 pkanavos
            //Obviously this is not what we want in this situation.
171 c945b450 pkanavos
            //The cancelled operation should NOT bea retried. 
172 c945b450 pkanavos
            //
173 c945b450 pkanavos
            //This can be done by catching the cancellation exception
174 c945b450 pkanavos
            //and avoiding the retry.
175 c945b450 pkanavos
            //
176 c945b450 pkanavos
177 c945b450 pkanavos
            //Have to reset the cancellation source - it is not possible to reset the source
178 c945b450 pkanavos
            //Have to prevent a case where an operation requests a token from the old source
179 c945b450 pkanavos
            var oldSource = Interlocked.Exchange(ref _currentOperationCancellation, new CancellationTokenSource());
180 c945b450 pkanavos
            oldSource.Cancel();
181 c945b450 pkanavos
182 c945b450 pkanavos
        }
183 c945b450 pkanavos
184 2341c603 pkanavos
        public bool Pause
185 2341c603 pkanavos
        {
186 2341c603 pkanavos
            get {
187 2341c603 pkanavos
                return _pause;
188 2341c603 pkanavos
            }
189 2341c603 pkanavos
            set {
190 2341c603 pkanavos
                _pause = value;                
191 2341c603 pkanavos
                if (!_pause)
192 2341c603 pkanavos
                    _unPauseEvent.Set();
193 2341c603 pkanavos
                else
194 2341c603 pkanavos
                {
195 2341c603 pkanavos
                    _unPauseEvent.Reset();
196 2341c603 pkanavos
                }
197 2341c603 pkanavos
            }
198 2341c603 pkanavos
        }
199 2341c603 pkanavos
200 aa7ac00e Panagiotis Kanavos
        private bool _firstPoll = true;
201 aa7ac00e Panagiotis Kanavos
202 aa7ac00e Panagiotis Kanavos
        //The Sync Event signals a manual synchronisation
203 aa7ac00e Panagiotis Kanavos
        private readonly AsyncManualResetEvent _syncEvent = new AsyncManualResetEvent();
204 aa7ac00e Panagiotis Kanavos
205 2341c603 pkanavos
        private readonly AsyncManualResetEvent _unPauseEvent = new AsyncManualResetEvent(true);
206 2341c603 pkanavos
207 ec1a1baf Panagiotis Kanavos
        private readonly ConcurrentDictionary<string, DateTime> _lastSeen = new ConcurrentDictionary<string, DateTime>();
208 1a41e9ec pkanavos
        private readonly ConcurrentDictionary<Uri, AccountInfo> _accounts = new ConcurrentDictionary<Uri,AccountInfo>();
209 aa7ac00e Panagiotis Kanavos
210 4b58c004 pkanavos
        //private readonly ActionBlock<PollRequest>  _pollAction;
211 4b58c004 pkanavos
212 4b58c004 pkanavos
        public PollAgent()
213 4b58c004 pkanavos
        {
214 4b58c004 pkanavos
            //_pollAction=new ActionBlock<PollRequest>(p=>ProcessPollRequest(p));
215 4b58c004 pkanavos
        }
216 4b58c004 pkanavos
217 aa7ac00e Panagiotis Kanavos
218 4b58c004 pkanavos
        /*private void ProcessPollRequest(PollRequest request)
219 4b58c004 pkanavos
        {
220 4b58c004 pkanavos
221 4b58c004 pkanavos
            if (request.Since == null && request.Batch != null)
222 4b58c004 pkanavos
            {
223 4b58c004 pkanavos
                _batchQueue.Enqueue(request.Batch);
224 4b58c004 pkanavos
                _syncEvent.Set();                
225 4b58c004 pkanavos
            }
226 4b58c004 pkanavos
            else 
227 4b58c004 pkanavos
            {
228 4b58c004 pkanavos
                PollRemoteFiles(request.Since).Wait();
229 4b58c004 pkanavos
            }
230 4b58c004 pkanavos
        }*/
231 aa7ac00e Panagiotis Kanavos
        /// <summary>
232 aa7ac00e Panagiotis Kanavos
        /// Start a manual synchronization
233 aa7ac00e Panagiotis Kanavos
        /// </summary>
234 76686dfe pkanavos
        public void SynchNow(IEnumerable<string> paths=null)
235 4b58c004 pkanavos
        {
236 4b58c004 pkanavos
            _batchQueue.Enqueue(paths);
237 4b58c004 pkanavos
            _syncEvent.Set();                
238 4b58c004 pkanavos
239 4b58c004 pkanavos
            //_pollAction.Post(new PollRequest {Batch = paths});
240 aa7ac00e Panagiotis Kanavos
        }
241 aa7ac00e Panagiotis Kanavos
242 76686dfe pkanavos
        readonly ConcurrentQueue<IEnumerable<string>> _batchQueue=new ConcurrentQueue<IEnumerable<string>>();
243 e0f69809 pkanavos
244 fec5da06 Panagiotis Kanavos
        /// <summary>
245 fec5da06 Panagiotis Kanavos
        /// Remote files are polled periodically. Any changes are processed
246 fec5da06 Panagiotis Kanavos
        /// </summary>
247 fec5da06 Panagiotis Kanavos
        /// <param name="since"></param>
248 fec5da06 Panagiotis Kanavos
        /// <returns></returns>
249 4b58c004 pkanavos
        public  void PollRemoteFiles(DateTime? since = null)
250 aa7ac00e Panagiotis Kanavos
        {
251 174bbb6e Panagiotis Kanavos
            if (Log.IsDebugEnabled)
252 174bbb6e Panagiotis Kanavos
                Log.DebugFormat("Polling changes after [{0}]",since);
253 aa7ac00e Panagiotis Kanavos
254 174bbb6e Panagiotis Kanavos
            Debug.Assert(Thread.CurrentThread.IsBackground, "Polling Ended up in the main thread!");
255 e0f69809 pkanavos
256 ee950288 pkanavos
            //GC.Collect();
257 aa7ac00e Panagiotis Kanavos
258 ec1a1baf Panagiotis Kanavos
            using (ThreadContext.Stacks["Retrieve Remote"].Push("All accounts"))
259 aa7ac00e Panagiotis Kanavos
            {
260 aa7ac00e Panagiotis Kanavos
                //If this poll fails, we will retry with the same since value
261 aa7ac00e Panagiotis Kanavos
                var nextSince = since;
262 aa7ac00e Panagiotis Kanavos
                try
263 aa7ac00e Panagiotis Kanavos
                {
264 4b58c004 pkanavos
                    _unPauseEvent.Wait();
265 174bbb6e Panagiotis Kanavos
                    UpdateStatus(PithosStatus.PollSyncing);
266 174bbb6e Panagiotis Kanavos
267 76686dfe pkanavos
                    var accountBatches=new Dictionary<Uri, IEnumerable<string>>();
268 76686dfe pkanavos
                    IEnumerable<string> batch = null;
269 76686dfe pkanavos
                    if (_batchQueue.TryDequeue(out batch) && batch != null)
270 76686dfe pkanavos
                        foreach (var account in _accounts.Values)
271 76686dfe pkanavos
                        {
272 76686dfe pkanavos
                            var accountBatch = batch.Where(path => path.IsAtOrBelow(account.AccountPath));
273 76686dfe pkanavos
                            accountBatches[account.AccountKey] = accountBatch;
274 76686dfe pkanavos
                        }
275 76686dfe pkanavos
276 76686dfe pkanavos
277 76686dfe pkanavos
                    IEnumerable<Task<DateTime?>> tasks = new List<Task<DateTime?>>();
278 76686dfe pkanavos
                    foreach(var accountInfo in _accounts.Values)
279 76686dfe pkanavos
                    {
280 76686dfe pkanavos
                        IEnumerable<string> accountBatch ;
281 76686dfe pkanavos
                        accountBatches.TryGetValue(accountInfo.AccountKey,out accountBatch);
282 4b58c004 pkanavos
                        ProcessAccountFiles (accountInfo, accountBatch, since).Wait();
283 76686dfe pkanavos
                    }
284 aa7ac00e Panagiotis Kanavos
285 4b58c004 pkanavos
                    var nextTimes=TaskEx.WhenAll(tasks.ToList()).Result;
286 aa7ac00e Panagiotis Kanavos
287 aa7ac00e Panagiotis Kanavos
                    _firstPoll = false;
288 aa7ac00e Panagiotis Kanavos
                    //Reschedule the poll with the current timestamp as a "since" value
289 6f03d6e1 Panagiotis Kanavos
290 6f03d6e1 Panagiotis Kanavos
                    if (nextTimes.Length>0)
291 6f03d6e1 Panagiotis Kanavos
                        nextSince = nextTimes.Min();
292 6f03d6e1 Panagiotis Kanavos
                    if (Log.IsDebugEnabled)
293 6f03d6e1 Panagiotis Kanavos
                        Log.DebugFormat("Next Poll at [{0}]",nextSince);
294 aa7ac00e Panagiotis Kanavos
                }
295 aa7ac00e Panagiotis Kanavos
                catch (Exception ex)
296 aa7ac00e Panagiotis Kanavos
                {
297 aa7ac00e Panagiotis Kanavos
                    Log.ErrorFormat("Error while processing accounts\r\n{0}", ex);
298 aa7ac00e Panagiotis Kanavos
                    //In case of failure retry with the same "since" value
299 aa7ac00e Panagiotis Kanavos
                }
300 aa7ac00e Panagiotis Kanavos
301 174bbb6e Panagiotis Kanavos
                UpdateStatus(PithosStatus.PollComplete);
302 fec5da06 Panagiotis Kanavos
                //The multiple try blocks are required because we can't have an await call
303 fec5da06 Panagiotis Kanavos
                //inside a finally block
304 fec5da06 Panagiotis Kanavos
                //TODO: Find a more elegant solution for reschedulling in the event of an exception
305 fec5da06 Panagiotis Kanavos
                try
306 fec5da06 Panagiotis Kanavos
                {
307 fec5da06 Panagiotis Kanavos
                    //Wait for the polling interval to pass or the Sync event to be signalled
308 4b58c004 pkanavos
                    nextSince = WaitForScheduledOrManualPoll(nextSince).Result;
309 fec5da06 Panagiotis Kanavos
                }
310 fec5da06 Panagiotis Kanavos
                finally
311 fec5da06 Panagiotis Kanavos
                {
312 fec5da06 Panagiotis Kanavos
                    //Ensure polling is scheduled even in case of error
313 4b58c004 pkanavos
                    PollRemoteFiles(nextSince);
314 4b58c004 pkanavos
                    //_pollAction.Post(new PollRequest {Since = nextSince});
315 fec5da06 Panagiotis Kanavos
                }
316 aa7ac00e Panagiotis Kanavos
            }
317 aa7ac00e Panagiotis Kanavos
        }
318 aa7ac00e Panagiotis Kanavos
319 aa7ac00e Panagiotis Kanavos
        /// <summary>
320 aa7ac00e Panagiotis Kanavos
        /// Wait for the polling period to expire or a manual sync request
321 aa7ac00e Panagiotis Kanavos
        /// </summary>
322 aa7ac00e Panagiotis Kanavos
        /// <param name="since"></param>
323 aa7ac00e Panagiotis Kanavos
        /// <returns></returns>
324 aa7ac00e Panagiotis Kanavos
        private async Task<DateTime?> WaitForScheduledOrManualPoll(DateTime? since)
325 aa7ac00e Panagiotis Kanavos
        {
326 aa7ac00e Panagiotis Kanavos
            var sync = _syncEvent.WaitAsync();
327 9036d725 pkanavos
            var wait = TaskEx.Delay(TimeSpan.FromSeconds(Settings.PollingInterval));
328 2341c603 pkanavos
            
329 aa7ac00e Panagiotis Kanavos
            var signaledTask = await TaskEx.WhenAny(sync, wait);
330 2341c603 pkanavos
            
331 2341c603 pkanavos
            //Pausing takes precedence over manual sync or awaiting
332 2341c603 pkanavos
            _unPauseEvent.Wait();
333 2341c603 pkanavos
            
334 aa7ac00e Panagiotis Kanavos
            //Wait for network processing to finish before polling
335 38ac43a6 Panagiotis Kanavos
            var pauseTask=NetworkAgent.ProceedEvent.WaitAsync();
336 aa7ac00e Panagiotis Kanavos
            await TaskEx.WhenAll(signaledTask, pauseTask);
337 aa7ac00e Panagiotis Kanavos
338 aa7ac00e Panagiotis Kanavos
            //If polling is signalled by SynchNow, ignore the since tag
339 aa7ac00e Panagiotis Kanavos
            if (sync.IsCompleted)
340 aa7ac00e Panagiotis Kanavos
            {
341 aa7ac00e Panagiotis Kanavos
                //TODO: Must convert to AutoReset
342 aa7ac00e Panagiotis Kanavos
                _syncEvent.Reset();
343 aa7ac00e Panagiotis Kanavos
                return null;
344 aa7ac00e Panagiotis Kanavos
            }
345 aa7ac00e Panagiotis Kanavos
            return since;
346 aa7ac00e Panagiotis Kanavos
        }
347 aa7ac00e Panagiotis Kanavos
348 76686dfe pkanavos
        public async Task<DateTime?> ProcessAccountFiles(AccountInfo accountInfo, IEnumerable<string> accountBatch, DateTime? since = null)
349 aa7ac00e Panagiotis Kanavos
        {
350 aa7ac00e Panagiotis Kanavos
            if (accountInfo == null)
351 aa7ac00e Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
352 aa7ac00e Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(accountInfo.AccountPath))
353 aa7ac00e Panagiotis Kanavos
                throw new ArgumentException("The AccountInfo.AccountPath is empty", "accountInfo");
354 aa7ac00e Panagiotis Kanavos
            Contract.EndContractBlock();
355 aa7ac00e Panagiotis Kanavos
356 aa7ac00e Panagiotis Kanavos
357 dccd340f Panagiotis Kanavos
            using (ThreadContext.Stacks["Retrieve Remote"].Push(accountInfo.UserName))
358 aa7ac00e Panagiotis Kanavos
            {
359 6f03d6e1 Panagiotis Kanavos
360 aa7ac00e Panagiotis Kanavos
                await NetworkAgent.GetDeleteAwaiter();
361 aa7ac00e Panagiotis Kanavos
362 aa7ac00e Panagiotis Kanavos
                Log.Info("Scheduled");
363 aa7ac00e Panagiotis Kanavos
                var client = new CloudFilesClient(accountInfo);
364 aa7ac00e Panagiotis Kanavos
365 99e6329f Panagiotis Kanavos
                //We don't need to check the trash container
366 ec1a1baf Panagiotis Kanavos
                var containers = client.ListContainers(accountInfo.UserName)
367 ec1a1baf Panagiotis Kanavos
                    .Where(c=>c.Name!="trash")
368 ec1a1baf Panagiotis Kanavos
                    .ToList();
369 aa7ac00e Panagiotis Kanavos
370 aa7ac00e Panagiotis Kanavos
371 aa7ac00e Panagiotis Kanavos
                CreateContainerFolders(accountInfo, containers);
372 aa7ac00e Panagiotis Kanavos
373 6f03d6e1 Panagiotis Kanavos
                //The nextSince time fallback time is the same as the current.
374 6f03d6e1 Panagiotis Kanavos
                //If polling succeeds, the next Since time will be the smallest of the maximum modification times
375 6f03d6e1 Panagiotis Kanavos
                //of the shared and account objects
376 6f03d6e1 Panagiotis Kanavos
                var nextSince = since;
377 6f03d6e1 Panagiotis Kanavos
378 aa7ac00e Panagiotis Kanavos
                try
379 aa7ac00e Panagiotis Kanavos
                {
380 759bd3c4 Panagiotis Kanavos
                    //Wait for any deletions to finish
381 aa7ac00e Panagiotis Kanavos
                    await NetworkAgent.GetDeleteAwaiter();
382 aa7ac00e Panagiotis Kanavos
                    //Get the poll time now. We may miss some deletions but it's better to keep a file that was deleted
383 aa7ac00e Panagiotis Kanavos
                    //than delete a file that was created while we were executing the poll                    
384 aa7ac00e Panagiotis Kanavos
385 aa7ac00e Panagiotis Kanavos
                    //Get the list of server objects changed since the last check
386 aa7ac00e Panagiotis Kanavos
                    //The name of the container is passed as state in order to create a dictionary of tasks in a subsequent step
387 aa7ac00e Panagiotis Kanavos
                    var listObjects = (from container in containers
388 aa7ac00e Panagiotis Kanavos
                                       select Task<IList<ObjectInfo>>.Factory.StartNew(_ =>
389 aa7ac00e Panagiotis Kanavos
                                             client.ListObjects(accountInfo.UserName, container.Name, since), container.Name)).ToList();
390 99e6329f Panagiotis Kanavos
391 99e6329f Panagiotis Kanavos
                    var listShared = Task<IList<ObjectInfo>>.Factory.StartNew(_ => 
392 99e6329f Panagiotis Kanavos
                        client.ListSharedObjects(since), "shared");
393 aa7ac00e Panagiotis Kanavos
                    listObjects.Add(listShared);
394 aa7ac00e Panagiotis Kanavos
                    var listTasks = await Task.Factory.WhenAll(listObjects.ToArray());
395 aa7ac00e Panagiotis Kanavos
396 dccd340f Panagiotis Kanavos
                    using (ThreadContext.Stacks["SCHEDULE"].Push("Process Results"))
397 aa7ac00e Panagiotis Kanavos
                    {
398 aa7ac00e Panagiotis Kanavos
                        var dict = listTasks.ToDictionary(t => t.AsyncState);
399 aa7ac00e Panagiotis Kanavos
400 aa7ac00e Panagiotis Kanavos
                        //Get all non-trash objects. Remember, the container name is stored in AsyncState
401 6f03d6e1 Panagiotis Kanavos
                        var remoteObjects = (from objectList in listTasks
402 aa7ac00e Panagiotis Kanavos
                                            where (string)objectList.AsyncState != "trash"
403 aa7ac00e Panagiotis Kanavos
                                            from obj in objectList.Result
404 c945b450 pkanavos
                                            orderby obj.Bytes ascending 
405 6f03d6e1 Panagiotis Kanavos
                                            select obj).ToList();
406 6f03d6e1 Panagiotis Kanavos
                        
407 6f03d6e1 Panagiotis Kanavos
                        //Get the latest remote object modification date, only if it is after
408 6f03d6e1 Panagiotis Kanavos
                        //the original since date
409 6f03d6e1 Panagiotis Kanavos
                        nextSince = GetLatestDateAfter(nextSince, remoteObjects);
410 aa7ac00e Panagiotis Kanavos
411 aa7ac00e Panagiotis Kanavos
                        var sharedObjects = dict["shared"].Result;
412 6f03d6e1 Panagiotis Kanavos
                        nextSince = GetLatestDateBefore(nextSince, sharedObjects);
413 aa7ac00e Panagiotis Kanavos
414 aa7ac00e Panagiotis Kanavos
                        //DON'T process trashed files
415 aa7ac00e Panagiotis Kanavos
                        //If some files are deleted and added again to a folder, they will be deleted
416 aa7ac00e Panagiotis Kanavos
                        //even though they are new.
417 aa7ac00e Panagiotis Kanavos
                        //We would have to check file dates and hashes to ensure that a trashed file
418 aa7ac00e Panagiotis Kanavos
                        //can be deleted safely from the local hard drive.
419 aa7ac00e Panagiotis Kanavos
                        /*
420 aa7ac00e Panagiotis Kanavos
                        //Items with the same name, hash may be both in the container and the trash
421 aa7ac00e Panagiotis Kanavos
                        //Don't delete items that exist in the container
422 aa7ac00e Panagiotis Kanavos
                        var realTrash = from trash in trashObjects
423 aa7ac00e Panagiotis Kanavos
                                        where
424 aa7ac00e Panagiotis Kanavos
                                            !remoteObjects.Any(
425 aa7ac00e Panagiotis Kanavos
                                                info => info.Name == trash.Name && info.Hash == trash.Hash)
426 0670c7e1 pkanavos
                                   8     select trash;
427 aa7ac00e Panagiotis Kanavos
                        ProcessTrashedFiles(accountInfo, realTrash);
428 aa7ac00e Panagiotis Kanavos
*/
429 aa7ac00e Panagiotis Kanavos
430 aa7ac00e Panagiotis Kanavos
                        var cleanRemotes = (from info in remoteObjects.Union(sharedObjects)
431 92f18b56 Panagiotis Kanavos
                                            let name = info.Name??""
432 aa7ac00e Panagiotis Kanavos
                                            where !name.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase) &&
433 aa7ac00e Panagiotis Kanavos
                                                  !name.StartsWith(FolderConstants.CacheFolder + "/",
434 aa7ac00e Panagiotis Kanavos
                                                                   StringComparison.InvariantCultureIgnoreCase)
435 aa7ac00e Panagiotis Kanavos
                                            select info).ToList();
436 aa7ac00e Panagiotis Kanavos
437 bc27bb7e Panagiotis Kanavos
                        if (_firstPoll)
438 bc27bb7e Panagiotis Kanavos
                            StatusKeeper.CleanupOrphanStates();
439 bc27bb7e Panagiotis Kanavos
                        StatusKeeper.CleanupStaleStates(accountInfo, cleanRemotes);
440 bc27bb7e Panagiotis Kanavos
                        
441 c945b450 pkanavos
                        //var differencer = _differencer.PostSnapshot(accountInfo, cleanRemotes);
442 aa7ac00e Panagiotis Kanavos
443 732276d3 pkanavos
                        //var filterUris = Selectives.SelectiveUris[accountInfo.AccountKey];
444 ebc37b0d pkanavos
445 4b58c004 pkanavos
                        //May have to wait if the FileAgent has asked for a Pause, due to local changes
446 4b58c004 pkanavos
                        await _unPauseEvent.WaitAsync();
447 368e06fd pkanavos
448 c945b450 pkanavos
                        //Get the local files here                        
449 4b58c004 pkanavos
                        var agent = AgentLocator<FileAgent>.Get(accountInfo.AccountPath);                        
450 4b58c004 pkanavos
                        //TODO: Pass the batch here as well
451 4b58c004 pkanavos
                        var files = await LoadLocalFileTuples(accountInfo, accountBatch);
452 759bd3c4 Panagiotis Kanavos
453 732276d3 pkanavos
                        var states = FileState.Queryable.ToList();                        
454 732276d3 pkanavos
                        
455 c945b450 pkanavos
                        var infos = (from remote in cleanRemotes
456 c945b450 pkanavos
                                    let path = remote.RelativeUrlToFilePath(accountInfo.UserName)
457 c945b450 pkanavos
                                    let info=agent.GetFileSystemInfo(path)
458 c945b450 pkanavos
                                    select Tuple.Create(info.FullName,remote))
459 c945b450 pkanavos
                                    .ToList();
460 c945b450 pkanavos
461 c945b450 pkanavos
                        var token = _currentOperationCancellation.Token;
462 c945b450 pkanavos
463 c945b450 pkanavos
                        var tuples = MergeSources(infos, files, states).ToList();
464 aa7ac00e Panagiotis Kanavos
465 4b58c004 pkanavos
                        //Process only the changes in the batch file, if one exists
466 76686dfe pkanavos
                        var stateTuples = accountBatch==null?tuples:tuples.Where(t => accountBatch.Contains(t.FilePath));
467 76686dfe pkanavos
                        foreach (var tuple in stateTuples)
468 aa7ac00e Panagiotis Kanavos
                        {
469 c945b450 pkanavos
                            await _unPauseEvent.WaitAsync();
470 1cc1e8c5 pkanavos
471 1cc1e8c5 pkanavos
                            //Set the Merkle Hash
472 1cc1e8c5 pkanavos
                            SetMerkleHash(accountInfo, tuple);
473 1cc1e8c5 pkanavos
474 c945b450 pkanavos
                            SyncSingleItem(accountInfo, tuple, agent, token);
475 1cc1e8c5 pkanavos
476 aa7ac00e Panagiotis Kanavos
                        }
477 aa7ac00e Panagiotis Kanavos
478 c945b450 pkanavos
479 c945b450 pkanavos
                        //On the first run
480 c945b450 pkanavos
/*
481 c945b450 pkanavos
                        if (_firstPoll)
482 c945b450 pkanavos
                        {
483 c945b450 pkanavos
                            MarkSuspectedDeletes(accountInfo, cleanRemotes);
484 c945b450 pkanavos
                        }
485 c945b450 pkanavos
*/
486 c945b450 pkanavos
487 c945b450 pkanavos
488 aa7ac00e Panagiotis Kanavos
                        Log.Info("[LISTENER] End Processing");
489 aa7ac00e Panagiotis Kanavos
                    }
490 aa7ac00e Panagiotis Kanavos
                }
491 aa7ac00e Panagiotis Kanavos
                catch (Exception ex)
492 aa7ac00e Panagiotis Kanavos
                {
493 aa7ac00e Panagiotis Kanavos
                    Log.ErrorFormat("[FAIL] ListObjects for{0} in ProcessRemoteFiles with {1}", accountInfo.UserName, ex);
494 6f03d6e1 Panagiotis Kanavos
                    return nextSince;
495 aa7ac00e Panagiotis Kanavos
                }
496 aa7ac00e Panagiotis Kanavos
497 aa7ac00e Panagiotis Kanavos
                Log.Info("[LISTENER] Finished");
498 6f03d6e1 Panagiotis Kanavos
                return nextSince;
499 aa7ac00e Panagiotis Kanavos
            }
500 aa7ac00e Panagiotis Kanavos
        }
501 aa7ac00e Panagiotis Kanavos
502 1cc1e8c5 pkanavos
        private static void SetMerkleHash(AccountInfo accountInfo, StateTuple tuple)
503 1cc1e8c5 pkanavos
        {
504 1cc1e8c5 pkanavos
            //The Merkle hash for directories is that of an empty buffer
505 1cc1e8c5 pkanavos
            if (tuple.FileInfo is DirectoryInfo)
506 1cc1e8c5 pkanavos
                tuple.C = MERKLE_EMPTY;
507 1cc1e8c5 pkanavos
            else if (tuple.FileState != null && tuple.MD5 == tuple.FileState.ShortHash)
508 1cc1e8c5 pkanavos
            {
509 732276d3 pkanavos
                //If there is a state whose MD5 matches, load the merkle hash from the file state
510 1cc1e8c5 pkanavos
                //insteaf of calculating it
511 1cc1e8c5 pkanavos
                tuple.C = tuple.FileState.Checksum;                              
512 1cc1e8c5 pkanavos
            }
513 1cc1e8c5 pkanavos
            else
514 1cc1e8c5 pkanavos
            {
515 4b58c004 pkanavos
                tuple.Merkle = TaskEx.Run(()=> Signature.CalculateTreeHash(tuple.FileInfo, accountInfo.BlockSize, accountInfo.BlockHash)).Result;
516 732276d3 pkanavos
                //tuple.C=tuple.Merkle.TopHash.ToHashString();                
517 1cc1e8c5 pkanavos
            }
518 1cc1e8c5 pkanavos
        }
519 1cc1e8c5 pkanavos
520 4b58c004 pkanavos
        private async Task<List<Tuple<FileSystemInfo, string>>> LoadLocalFileTuples(AccountInfo accountInfo,IEnumerable<string> batch )
521 c945b450 pkanavos
        {
522 c945b450 pkanavos
            using (ThreadContext.Stacks["Account Files Hashing"].Push(accountInfo.UserName))
523 c945b450 pkanavos
            {
524 4b58c004 pkanavos
                var batchPaths = (batch==null)?new List<string>():batch.ToList();
525 4b58c004 pkanavos
                IEnumerable<FileSystemInfo> localInfos=AgentLocator<FileAgent>.Get(accountInfo.AccountPath)
526 4b58c004 pkanavos
                                                        .EnumerateFileSystemInfos();
527 4b58c004 pkanavos
                if (batchPaths.Count>0)
528 4b58c004 pkanavos
                    localInfos= localInfos.Where(fi => batchPaths.Contains(fi.FullName));
529 4b58c004 pkanavos
                
530 c945b450 pkanavos
                //Use the queue to retry locked file hashing
531 c945b450 pkanavos
                var fileQueue = new Queue<FileSystemInfo>(localInfos);
532 1cc1e8c5 pkanavos
                var hasher = MD5.Create();
533 c945b450 pkanavos
534 c945b450 pkanavos
                var results = new List<Tuple<FileSystemInfo, string>>();
535 4b58c004 pkanavos
                var backoff = 0;
536 c945b450 pkanavos
                while (fileQueue.Count > 0)
537 c945b450 pkanavos
                {
538 c945b450 pkanavos
                    var file = fileQueue.Dequeue();
539 c945b450 pkanavos
                    using (ThreadContext.Stacks["File"].Push(file.FullName))
540 c945b450 pkanavos
                    {
541 1cc1e8c5 pkanavos
                        /*
542 1cc1e8c5 pkanavos
                                                Signature.CalculateTreeHash(file, accountInfo.BlockSize,
543 1cc1e8c5 pkanavos
                                                                                                 accountInfo.BlockHash).
544 1cc1e8c5 pkanavos
                                                                         TopHash.ToHashString()
545 1cc1e8c5 pkanavos
                        */
546 c945b450 pkanavos
                        try
547 c945b450 pkanavos
                        {
548 1cc1e8c5 pkanavos
                            //Replace MD5 here, do the calc while syncing individual files
549 1cc1e8c5 pkanavos
                            string hash ;
550 1cc1e8c5 pkanavos
                            if (file is DirectoryInfo)
551 1cc1e8c5 pkanavos
                                hash = MERKLE_EMPTY;
552 1cc1e8c5 pkanavos
                            else
553 1cc1e8c5 pkanavos
                            {
554 4b58c004 pkanavos
                                //Wait in case the FileAgent has requested a Pause
555 4b58c004 pkanavos
                                await _unPauseEvent.WaitAsync();
556 4b58c004 pkanavos
                                
557 4b58c004 pkanavos
                                using (StatusNotification.GetNotifier("Hashing {0}", "Finished hashing {0}", file.Name))
558 1cc1e8c5 pkanavos
                                using (var stream = (file as FileInfo).OpenRead())
559 1f277506 George Pantazis
                                {                                    
560 1cc1e8c5 pkanavos
                                    hash = hasher.ComputeHash(stream).ToHashString();
561 4b58c004 pkanavos
                                    backoff = 0;
562 1cc1e8c5 pkanavos
                                }
563 1cc1e8c5 pkanavos
                            }                            
564 c945b450 pkanavos
                            results.Add(Tuple.Create(file, hash));
565 c945b450 pkanavos
                        }
566 c945b450 pkanavos
                        catch (IOException exc)
567 c945b450 pkanavos
                        {
568 c945b450 pkanavos
                            Log.WarnFormat("[HASH] File in use, will retry [{0}]", exc);
569 c945b450 pkanavos
                            fileQueue.Enqueue(file);
570 4b58c004 pkanavos
                            //If this is the only enqueued file                            
571 4b58c004 pkanavos
                            if (fileQueue.Count != 1) continue;
572 4b58c004 pkanavos
                            
573 4b58c004 pkanavos
                            
574 4b58c004 pkanavos
                            //Increase delay
575 4b58c004 pkanavos
                            if (backoff<60000)
576 4b58c004 pkanavos
                                backoff += 10000;
577 4b58c004 pkanavos
                            //Pause Polling for the specified time
578 c945b450 pkanavos
                        }
579 4b58c004 pkanavos
                        if (backoff>0)
580 4b58c004 pkanavos
                            await PauseFor(backoff);
581 c945b450 pkanavos
                    }
582 c945b450 pkanavos
                }
583 c945b450 pkanavos
584 c945b450 pkanavos
                return results;
585 c945b450 pkanavos
            }
586 c945b450 pkanavos
        }
587 c945b450 pkanavos
588 4b58c004 pkanavos
        /// <summary>
589 4b58c004 pkanavos
        /// Wait and Pause the agent while waiting
590 4b58c004 pkanavos
        /// </summary>
591 4b58c004 pkanavos
        /// <param name="backoff"></param>
592 4b58c004 pkanavos
        /// <returns></returns>
593 4b58c004 pkanavos
        private async Task PauseFor(int backoff)
594 4b58c004 pkanavos
        {
595 4b58c004 pkanavos
596 4b58c004 pkanavos
            Pause = true;
597 4b58c004 pkanavos
            await TaskEx.Delay(backoff);
598 4b58c004 pkanavos
            Pause = false;
599 4b58c004 pkanavos
        }
600 4b58c004 pkanavos
601 c945b450 pkanavos
        private void SyncSingleItem(AccountInfo accountInfo, StateTuple tuple, FileAgent agent, CancellationToken token)
602 c945b450 pkanavos
        {
603 c945b450 pkanavos
            Log.DebugFormat("Sync [{0}] C:[{1}] L:[{2}] S:[{3}]",tuple.FilePath,tuple.C,tuple.L,tuple.S);
604 c945b450 pkanavos
605 c945b450 pkanavos
            var localFilePath = tuple.FilePath;
606 c945b450 pkanavos
            //Don't use the tuple info, it may have been deleted
607 c945b450 pkanavos
            var localInfo = FileInfoExtensions.FromPath(localFilePath);
608 c945b450 pkanavos
609 e4067290 pkanavos
610 012c8537 pkanavos
            var isUnselectedRootFolder = agent.IsUnselectedRootFolder(tuple.FilePath);
611 012c8537 pkanavos
612 012c8537 pkanavos
            //Unselected root folders that have not yet been uploaded should be uploaded and added to the 
613 012c8537 pkanavos
            //selective folders
614 012c8537 pkanavos
615 012c8537 pkanavos
            if (!Selectives.IsSelected(accountInfo, localFilePath) && !(isUnselectedRootFolder && tuple.ObjectInfo==null) )                
616 012c8537 pkanavos
                return;
617 012c8537 pkanavos
618 c945b450 pkanavos
            // Local file unchanged? If both C and L are null, make sure it's because 
619 c945b450 pkanavos
            //both the file is missing and the state checksum is not missing
620 012c8537 pkanavos
            if (tuple.C == tuple.L /*&& (localInfo.Exists || tuple.FileState == null)*/)
621 c945b450 pkanavos
            {
622 c945b450 pkanavos
                //No local changes
623 c945b450 pkanavos
                //Server unchanged?
624 c945b450 pkanavos
                if (tuple.S == tuple.L)
625 c945b450 pkanavos
                {
626 c945b450 pkanavos
                    // No server changes
627 e4067290 pkanavos
                    //Has the file been renamed on the server?
628 e4067290 pkanavos
                    MoveForServerMove(accountInfo, tuple);
629 c945b450 pkanavos
                }
630 c945b450 pkanavos
                else
631 c945b450 pkanavos
                {
632 c945b450 pkanavos
                    //Different from server
633 012c8537 pkanavos
                    //Does the server file exist?
634 012c8537 pkanavos
                    if (tuple.S == null)
635 c945b450 pkanavos
                    {
636 012c8537 pkanavos
                        //Server file doesn't exist
637 012c8537 pkanavos
                        //deleteObjectFromLocal()
638 012c8537 pkanavos
                        StatusKeeper.SetFileState(localFilePath, FileStatus.Deleted,
639 012c8537 pkanavos
                                                  FileOverlayStatus.Deleted, "");
640 012c8537 pkanavos
                        agent.Delete(localFilePath);
641 012c8537 pkanavos
                        //updateRecord(Remove C, L)
642 012c8537 pkanavos
                        StatusKeeper.ClearFileStatus(localFilePath);
643 012c8537 pkanavos
                    }
644 012c8537 pkanavos
                    else
645 012c8537 pkanavos
                    {
646 012c8537 pkanavos
                        //Server file exists
647 012c8537 pkanavos
                        //downloadServerObject() // Result: L = S
648 012c8537 pkanavos
                        //If the file has moved on the server, move it locally before downloading
649 012c8537 pkanavos
                        var targetPath = MoveForServerMove(accountInfo, tuple);
650 012c8537 pkanavos
651 012c8537 pkanavos
                        StatusKeeper.SetFileState(targetPath, FileStatus.Modified,
652 012c8537 pkanavos
                                                  FileOverlayStatus.Modified, "");
653 012c8537 pkanavos
                        NetworkAgent.Downloader.DownloadCloudFile(accountInfo,
654 012c8537 pkanavos
                                                                  tuple.ObjectInfo,
655 012c8537 pkanavos
                                                                  targetPath, tuple.Merkle, token).Wait(token);
656 012c8537 pkanavos
                        //updateRecord( L = S )
657 012c8537 pkanavos
                        StatusKeeper.UpdateFileChecksum(targetPath, tuple.ObjectInfo.ETag,
658 012c8537 pkanavos
                                                        tuple.ObjectInfo.X_Object_Hash);
659 012c8537 pkanavos
660 012c8537 pkanavos
                        StatusKeeper.StoreInfo(targetPath, tuple.ObjectInfo);
661 e4067290 pkanavos
662 012c8537 pkanavos
                        /*
663 012c8537 pkanavos
                                                        StatusKeeper.SetFileState(targetPath, FileStatus.Unchanged,
664 012c8537 pkanavos
                                                                                  FileOverlayStatus.Normal, "");
665 012c8537 pkanavos
                            */
666 c945b450 pkanavos
                    }
667 c945b450 pkanavos
                }
668 012c8537 pkanavos
669 c945b450 pkanavos
            }
670 c945b450 pkanavos
            else
671 c945b450 pkanavos
            {
672 c945b450 pkanavos
                //Local changes found
673 c945b450 pkanavos
674 c945b450 pkanavos
                //Server unchanged?
675 c945b450 pkanavos
                if (tuple.S == tuple.L)
676 c945b450 pkanavos
                {
677 c945b450 pkanavos
                    //The FileAgent selective sync checks for new root folder files
678 c945b450 pkanavos
                    if (!agent.Ignore(localFilePath))
679 c945b450 pkanavos
                    {
680 c945b450 pkanavos
                        if ((tuple.C == null || !localInfo.Exists) && tuple.ObjectInfo != null)
681 c945b450 pkanavos
                        {
682 c945b450 pkanavos
                            //deleteObjectFromServer()
683 c945b450 pkanavos
                            DeleteCloudFile(accountInfo, tuple);
684 c945b450 pkanavos
                            //updateRecord( Remove L, S)                  
685 c945b450 pkanavos
                        }
686 c945b450 pkanavos
                        else
687 c945b450 pkanavos
                        {
688 c945b450 pkanavos
                            //uploadLocalObject() // Result: S = C, L = S                        
689 c945b450 pkanavos
690 c945b450 pkanavos
                            //Debug.Assert(tuple.FileState !=null);
691 c945b450 pkanavos
                            var action = new CloudUploadAction(accountInfo, localInfo, tuple.FileState,
692 c945b450 pkanavos
                                                               accountInfo.BlockSize, accountInfo.BlockHash,
693 012c8537 pkanavos
                                                               "Poll", isUnselectedRootFolder);
694 012c8537 pkanavos
                            NetworkAgent.Uploader.UploadCloudFile(action, tuple.Merkle, token).Wait(token);
695 c945b450 pkanavos
696 c945b450 pkanavos
                            //updateRecord( S = C )
697 e4067290 pkanavos
                            //State updated by the uploader
698 012c8537 pkanavos
699 012c8537 pkanavos
                            if (isUnselectedRootFolder)
700 c945b450 pkanavos
                            {
701 c945b450 pkanavos
                                ProcessChildren(accountInfo, tuple, agent, token);
702 c945b450 pkanavos
                            }
703 c945b450 pkanavos
                        }
704 c945b450 pkanavos
                    }
705 c945b450 pkanavos
                }
706 c945b450 pkanavos
                else
707 c945b450 pkanavos
                {
708 012c8537 pkanavos
                    if (tuple.C == tuple.S)
709 c945b450 pkanavos
                    {
710 012c8537 pkanavos
                        // (Identical Changes) Result: L = S
711 012c8537 pkanavos
                        //doNothing()
712 012c8537 pkanavos
                        //Detect server moves
713 012c8537 pkanavos
                        var targetPath = MoveForServerMove(accountInfo, tuple);
714 012c8537 pkanavos
                        StatusKeeper.StoreInfo(targetPath, tuple.ObjectInfo);
715 012c8537 pkanavos
                    }
716 012c8537 pkanavos
                    else
717 012c8537 pkanavos
                    {
718 012c8537 pkanavos
                        if ((tuple.C == null || !localInfo.Exists) && tuple.ObjectInfo != null)
719 012c8537 pkanavos
                        {
720 012c8537 pkanavos
                            //deleteObjectFromServer()
721 012c8537 pkanavos
                            DeleteCloudFile(accountInfo, tuple);
722 012c8537 pkanavos
                            //updateRecord(Remove L, S)                  
723 012c8537 pkanavos
                        }
724 012c8537 pkanavos
                            //If both the local and server files are missing, the state is stale
725 012c8537 pkanavos
                        else if (!localInfo.Exists && (tuple.S == null || tuple.ObjectInfo == null))
726 c945b450 pkanavos
                        {
727 012c8537 pkanavos
                            StatusKeeper.ClearFileStatus(localInfo.FullName);
728 c945b450 pkanavos
                        }
729 c945b450 pkanavos
                        else
730 c945b450 pkanavos
                        {
731 012c8537 pkanavos
                            ReportConflictForMismatch(localFilePath);
732 012c8537 pkanavos
                            //identifyAsConflict() // Manual action required
733 c945b450 pkanavos
                        }
734 c945b450 pkanavos
                    }
735 c945b450 pkanavos
                }
736 c945b450 pkanavos
            }
737 c945b450 pkanavos
        }
738 c945b450 pkanavos
739 e4067290 pkanavos
        private string MoveForServerMove(AccountInfo accountInfo, StateTuple tuple)
740 e4067290 pkanavos
        {
741 9036d725 pkanavos
            if (tuple.ObjectInfo == null)
742 9036d725 pkanavos
                return null;
743 e4067290 pkanavos
            var relativePath = tuple.ObjectInfo.RelativeUrlToFilePath(accountInfo.UserName);
744 e4067290 pkanavos
            var serverPath = Path.Combine(accountInfo.AccountPath, relativePath);
745 9036d725 pkanavos
            
746 9036d725 pkanavos
            //Compare Case Insensitive
747 9036d725 pkanavos
            if (String.Equals(tuple.FilePath ,serverPath,StringComparison.InvariantCultureIgnoreCase)) return serverPath;
748 e4067290 pkanavos
749 e4067290 pkanavos
            if (tuple.FileInfo.Exists)
750 e4067290 pkanavos
            {                    
751 e4067290 pkanavos
                var fi = tuple.FileInfo as FileInfo;
752 e4067290 pkanavos
                if (fi != null)
753 e4067290 pkanavos
                    fi.MoveTo(serverPath);
754 e4067290 pkanavos
                var di = tuple.FileInfo as DirectoryInfo;
755 e4067290 pkanavos
                if (di != null)
756 e4067290 pkanavos
                    di.MoveTo(serverPath);
757 e4067290 pkanavos
                StatusKeeper.StoreInfo(serverPath, tuple.ObjectInfo);
758 e4067290 pkanavos
            }
759 e4067290 pkanavos
            else
760 e4067290 pkanavos
            {
761 e4067290 pkanavos
                Debug.Assert(false, "File does not exist");
762 e4067290 pkanavos
            }
763 e4067290 pkanavos
            return serverPath;
764 e4067290 pkanavos
        }
765 e4067290 pkanavos
766 c945b450 pkanavos
        private void DeleteCloudFile(AccountInfo accountInfo, StateTuple tuple)
767 c945b450 pkanavos
        {
768 c945b450 pkanavos
            StatusKeeper.SetFileState(tuple.FilePath, FileStatus.Deleted,
769 c945b450 pkanavos
                                      FileOverlayStatus.Deleted, "");
770 c945b450 pkanavos
            NetworkAgent.DeleteAgent.DeleteCloudFile(accountInfo, tuple.ObjectInfo);
771 c945b450 pkanavos
            StatusKeeper.ClearFileStatus(tuple.FilePath);
772 c945b450 pkanavos
        }
773 c945b450 pkanavos
774 c945b450 pkanavos
        private void ProcessChildren(AccountInfo accountInfo, StateTuple tuple, FileAgent agent, CancellationToken token)
775 c945b450 pkanavos
        {
776 c945b450 pkanavos
777 c945b450 pkanavos
            var dirInfo = tuple.FileInfo as DirectoryInfo;
778 c945b450 pkanavos
            var folderTuples = from folder in dirInfo.EnumerateDirectories("*", SearchOption.AllDirectories)
779 c945b450 pkanavos
                               select new StateTuple(folder);
780 c945b450 pkanavos
            var fileTuples = from file in dirInfo.EnumerateFiles("*", SearchOption.AllDirectories)
781 c945b450 pkanavos
                             select new StateTuple(file);
782 c945b450 pkanavos
            
783 c945b450 pkanavos
            //Process folders first, to ensure folders appear on the sever as soon as possible
784 c945b450 pkanavos
            folderTuples.ApplyAction(t => SyncSingleItem(accountInfo, t, agent, token));
785 c945b450 pkanavos
            
786 c945b450 pkanavos
            fileTuples.ApplyAction(t => SyncSingleItem(accountInfo, t, agent, token));
787 c945b450 pkanavos
        }
788 c945b450 pkanavos
789 c945b450 pkanavos
        private static IEnumerable<StateTuple> MergeSources(
790 c945b450 pkanavos
            IEnumerable<Tuple<string, ObjectInfo>> infos, 
791 c945b450 pkanavos
            IEnumerable<Tuple<FileSystemInfo, string>> files, 
792 c945b450 pkanavos
            IEnumerable<FileState> states)
793 c945b450 pkanavos
        {
794 e4067290 pkanavos
            var tuplesByPath = new Dictionary<string, StateTuple>();
795 c945b450 pkanavos
            foreach (var file in files)
796 c945b450 pkanavos
            {
797 c945b450 pkanavos
                var fsInfo = file.Item1;
798 1cc1e8c5 pkanavos
                var fileHash = fsInfo is DirectoryInfo? MERKLE_EMPTY:file.Item2;
799 1cc1e8c5 pkanavos
800 e4067290 pkanavos
                tuplesByPath[fsInfo.FullName] = new StateTuple {FileInfo = fsInfo, MD5 = fileHash};
801 c945b450 pkanavos
            }
802 c945b450 pkanavos
            foreach (var state in states)
803 c945b450 pkanavos
            {
804 c945b450 pkanavos
                StateTuple hashTuple;
805 e4067290 pkanavos
                if (tuplesByPath.TryGetValue(state.FilePath, out hashTuple))
806 c945b450 pkanavos
                {
807 c945b450 pkanavos
                    hashTuple.FileState = state;
808 c945b450 pkanavos
                }
809 c945b450 pkanavos
                else
810 c945b450 pkanavos
                {
811 c945b450 pkanavos
                    var fsInfo = FileInfoExtensions.FromPath(state.FilePath);
812 e4067290 pkanavos
                    tuplesByPath[state.FilePath] = new StateTuple {FileInfo = fsInfo, FileState = state};
813 c945b450 pkanavos
                }
814 c945b450 pkanavos
            }
815 e4067290 pkanavos
816 e4067290 pkanavos
            var tuplesByID = tuplesByPath.Values
817 e4067290 pkanavos
                .Where(tuple => tuple.FileState != null && tuple.FileState.ObjectID!=null)
818 e4067290 pkanavos
                .ToDictionary(tuple=>tuple.FileState.ObjectID,tuple=>tuple);//new Dictionary<Guid, StateTuple>();
819 e4067290 pkanavos
820 c945b450 pkanavos
            foreach (var info in infos)
821 c945b450 pkanavos
            {
822 c945b450 pkanavos
                StateTuple hashTuple;
823 c945b450 pkanavos
                var filePath = info.Item1;
824 c945b450 pkanavos
                var objectInfo = info.Item2;
825 e4067290 pkanavos
                var objectID = objectInfo.UUID;
826 e4067290 pkanavos
827 e4067290 pkanavos
                if (tuplesByID.TryGetValue(objectID, out hashTuple))
828 e4067290 pkanavos
                {
829 e4067290 pkanavos
                    hashTuple.ObjectInfo = objectInfo;                    
830 e4067290 pkanavos
                }
831 e4067290 pkanavos
                else if (tuplesByPath.TryGetValue(filePath, out hashTuple))
832 c945b450 pkanavos
                {
833 c945b450 pkanavos
                    hashTuple.ObjectInfo = objectInfo;
834 c945b450 pkanavos
                }
835 c945b450 pkanavos
                else
836 c945b450 pkanavos
                {
837 c945b450 pkanavos
                    var fsInfo = FileInfoExtensions.FromPath(filePath);
838 e4067290 pkanavos
                    var tuple = new StateTuple {FileInfo = fsInfo, ObjectInfo = objectInfo};
839 e4067290 pkanavos
                    tuplesByPath[filePath] = tuple;
840 e4067290 pkanavos
                    tuplesByID[objectInfo.UUID] = tuple;
841 c945b450 pkanavos
                }
842 c945b450 pkanavos
            }
843 e4067290 pkanavos
            return tuplesByPath.Values;
844 c945b450 pkanavos
        }
845 c945b450 pkanavos
846 6f03d6e1 Panagiotis Kanavos
        /// <summary>
847 6f03d6e1 Panagiotis Kanavos
        /// Returns the latest LastModified date from the list of objects, but only if it is before
848 6f03d6e1 Panagiotis Kanavos
        /// than the threshold value
849 6f03d6e1 Panagiotis Kanavos
        /// </summary>
850 6f03d6e1 Panagiotis Kanavos
        /// <param name="threshold"></param>
851 6f03d6e1 Panagiotis Kanavos
        /// <param name="cloudObjects"></param>
852 6f03d6e1 Panagiotis Kanavos
        /// <returns></returns>
853 6f03d6e1 Panagiotis Kanavos
        private static DateTime? GetLatestDateBefore(DateTime? threshold, IList<ObjectInfo> cloudObjects)
854 6f03d6e1 Panagiotis Kanavos
        {
855 6f03d6e1 Panagiotis Kanavos
            DateTime? maxDate = null;
856 6f03d6e1 Panagiotis Kanavos
            if (cloudObjects!=null &&  cloudObjects.Count > 0)
857 6f03d6e1 Panagiotis Kanavos
                maxDate = cloudObjects.Max(obj => obj.Last_Modified);
858 6f03d6e1 Panagiotis Kanavos
            if (maxDate == null || maxDate == DateTime.MinValue)
859 6f03d6e1 Panagiotis Kanavos
                return threshold;
860 6f03d6e1 Panagiotis Kanavos
            if (threshold == null || threshold == DateTime.MinValue || threshold > maxDate)
861 6f03d6e1 Panagiotis Kanavos
                return maxDate;
862 6f03d6e1 Panagiotis Kanavos
            return threshold;
863 6f03d6e1 Panagiotis Kanavos
        }
864 6f03d6e1 Panagiotis Kanavos
865 6f03d6e1 Panagiotis Kanavos
        /// <summary>
866 6f03d6e1 Panagiotis Kanavos
        /// Returns the latest LastModified date from the list of objects, but only if it is after
867 6f03d6e1 Panagiotis Kanavos
        /// the threshold value
868 6f03d6e1 Panagiotis Kanavos
        /// </summary>
869 6f03d6e1 Panagiotis Kanavos
        /// <param name="threshold"></param>
870 6f03d6e1 Panagiotis Kanavos
        /// <param name="cloudObjects"></param>
871 6f03d6e1 Panagiotis Kanavos
        /// <returns></returns>
872 6f03d6e1 Panagiotis Kanavos
        private static DateTime? GetLatestDateAfter(DateTime? threshold, IList<ObjectInfo> cloudObjects)
873 6f03d6e1 Panagiotis Kanavos
        {
874 6f03d6e1 Panagiotis Kanavos
            DateTime? maxDate = null;
875 6f03d6e1 Panagiotis Kanavos
            if (cloudObjects!=null &&  cloudObjects.Count > 0)
876 6f03d6e1 Panagiotis Kanavos
                maxDate = cloudObjects.Max(obj => obj.Last_Modified);
877 6f03d6e1 Panagiotis Kanavos
            if (maxDate == null || maxDate == DateTime.MinValue)
878 6f03d6e1 Panagiotis Kanavos
                return threshold;
879 6f03d6e1 Panagiotis Kanavos
            if (threshold == null || threshold == DateTime.MinValue || threshold < maxDate)
880 6f03d6e1 Panagiotis Kanavos
                return maxDate;
881 6f03d6e1 Panagiotis Kanavos
            return threshold;
882 6f03d6e1 Panagiotis Kanavos
        }
883 6f03d6e1 Panagiotis Kanavos
884 732276d3 pkanavos
        readonly AccountsDifferencer _differencer = new AccountsDifferencer();
885 ebc37b0d pkanavos
        private Dictionary<Uri, List<Uri>> _selectiveUris = new Dictionary<Uri, List<Uri>>();
886 2341c603 pkanavos
        private bool _pause;
887 1cc1e8c5 pkanavos
        private static string MERKLE_EMPTY = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
888 aa7ac00e Panagiotis Kanavos
889 aa7ac00e Panagiotis Kanavos
        /// <summary>
890 aa7ac00e Panagiotis Kanavos
        /// Deletes local files that are not found in the list of cloud files
891 aa7ac00e Panagiotis Kanavos
        /// </summary>
892 aa7ac00e Panagiotis Kanavos
        /// <param name="accountInfo"></param>
893 aa7ac00e Panagiotis Kanavos
        /// <param name="cloudFiles"></param>
894 ec1a1baf Panagiotis Kanavos
        private void ProcessDeletedFiles(AccountInfo accountInfo, IEnumerable<ObjectInfo> cloudFiles)
895 aa7ac00e Panagiotis Kanavos
        {
896 aa7ac00e Panagiotis Kanavos
            if (accountInfo == null)
897 aa7ac00e Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
898 aa7ac00e Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(accountInfo.AccountPath))
899 aa7ac00e Panagiotis Kanavos
                throw new ArgumentException("The AccountInfo.AccountPath is empty", "accountInfo");
900 aa7ac00e Panagiotis Kanavos
            if (cloudFiles == null)
901 aa7ac00e Panagiotis Kanavos
                throw new ArgumentNullException("cloudFiles");
902 aa7ac00e Panagiotis Kanavos
            Contract.EndContractBlock();
903 aa7ac00e Panagiotis Kanavos
904 368e06fd pkanavos
            var deletedFiles = new List<FileSystemInfo>();
905 368e06fd pkanavos
            foreach (var objectInfo in cloudFiles)
906 aa7ac00e Panagiotis Kanavos
            {
907 368e06fd pkanavos
                if (Log.IsDebugEnabled)
908 368e06fd pkanavos
                    Log.DebugFormat("Handle deleted [{0}]", objectInfo.Uri);
909 368e06fd pkanavos
                var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);
910 368e06fd pkanavos
                var item = FileAgent.GetFileAgent(accountInfo).GetFileSystemInfo(relativePath);
911 368e06fd pkanavos
                if (Log.IsDebugEnabled)
912 368e06fd pkanavos
                    Log.DebugFormat("Will delete [{0}] for [{1}]", item.FullName, objectInfo.Uri);
913 368e06fd pkanavos
                if (item.Exists)
914 368e06fd pkanavos
                {
915 368e06fd pkanavos
                    if ((item.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
916 368e06fd pkanavos
                    {
917 368e06fd pkanavos
                        item.Attributes = item.Attributes & ~FileAttributes.ReadOnly;
918 aa7ac00e Panagiotis Kanavos
919 368e06fd pkanavos
                    }
920 aa7ac00e Panagiotis Kanavos
921 aa7ac00e Panagiotis Kanavos
922 368e06fd pkanavos
                    Log.DebugFormat("Deleting {0}", item.FullName);
923 aa7ac00e Panagiotis Kanavos
924 368e06fd pkanavos
                    var directory = item as DirectoryInfo;
925 368e06fd pkanavos
                    if (directory != null)
926 368e06fd pkanavos
                        directory.Delete(true);
927 368e06fd pkanavos
                    else
928 368e06fd pkanavos
                        item.Delete();
929 368e06fd pkanavos
                    Log.DebugFormat("Deleted [{0}] for [{1}]", item.FullName, objectInfo.Uri);
930 368e06fd pkanavos
                    DateTime lastDate;
931 368e06fd pkanavos
                    _lastSeen.TryRemove(item.FullName, out lastDate);
932 368e06fd pkanavos
                    deletedFiles.Add(item);
933 aa7ac00e Panagiotis Kanavos
                }
934 368e06fd pkanavos
                StatusKeeper.SetFileState(item.FullName, FileStatus.Deleted, FileOverlayStatus.Deleted, "File Deleted");
935 aa7ac00e Panagiotis Kanavos
            }
936 368e06fd pkanavos
            Log.InfoFormat("[{0}] files were deleted", deletedFiles.Count);
937 368e06fd pkanavos
            StatusNotification.NotifyForFiles(deletedFiles, String.Format("{0} files were deleted", deletedFiles.Count),
938 368e06fd pkanavos
                                              TraceLevel.Info);
939 368e06fd pkanavos
940 368e06fd pkanavos
        }
941 368e06fd pkanavos
942 368e06fd pkanavos
        private void MarkSuspectedDeletes(AccountInfo accountInfo, IEnumerable<ObjectInfo> cloudFiles)
943 368e06fd pkanavos
        {
944 368e06fd pkanavos
//Only consider files that are not being modified, ie they are in the Unchanged state            
945 368e06fd pkanavos
            var deleteCandidates = FileState.Queryable.Where(state =>
946 368e06fd pkanavos
                                                             state.FilePath.StartsWith(accountInfo.AccountPath)
947 368e06fd pkanavos
                                                             && state.FileStatus == FileStatus.Unchanged).ToList();
948 368e06fd pkanavos
949 368e06fd pkanavos
950 368e06fd pkanavos
            //TODO: filesToDelete must take into account the Others container            
951 368e06fd pkanavos
            var filesToDelete = (from deleteCandidate in deleteCandidates
952 368e06fd pkanavos
                                 let localFile = FileInfoExtensions.FromPath(deleteCandidate.FilePath)
953 368e06fd pkanavos
                                 let relativeFilePath = localFile.AsRelativeTo(accountInfo.AccountPath)
954 368e06fd pkanavos
                                 where
955 368e06fd pkanavos
                                     !cloudFiles.Any(r => r.RelativeUrlToFilePath(accountInfo.UserName) == relativeFilePath)
956 368e06fd pkanavos
                                 select localFile).ToList();
957 368e06fd pkanavos
958 368e06fd pkanavos
959 368e06fd pkanavos
            //Set the status of missing files to Conflict
960 368e06fd pkanavos
            foreach (var item in filesToDelete)
961 aa7ac00e Panagiotis Kanavos
            {
962 368e06fd pkanavos
                //Try to acquire a gate on the file, to take into account files that have been dequeued
963 368e06fd pkanavos
                //and are being processed
964 368e06fd pkanavos
                using (var gate = NetworkGate.Acquire(item.FullName, NetworkOperation.Deleting))
965 aa7ac00e Panagiotis Kanavos
                {
966 368e06fd pkanavos
                    if (gate.Failed)
967 368e06fd pkanavos
                        continue;
968 368e06fd pkanavos
                    StatusKeeper.SetFileState(item.FullName, FileStatus.Conflict, FileOverlayStatus.Deleted,
969 368e06fd pkanavos
                                              "Local file missing from server");
970 aa7ac00e Panagiotis Kanavos
                }
971 aa7ac00e Panagiotis Kanavos
            }
972 368e06fd pkanavos
            UpdateStatus(PithosStatus.HasConflicts);
973 368e06fd pkanavos
            StatusNotification.NotifyConflicts(filesToDelete,
974 368e06fd pkanavos
                                               String.Format(
975 368e06fd pkanavos
                                                   "{0} local files are missing from Pithos, possibly because they were deleted",
976 368e06fd pkanavos
                                                   filesToDelete.Count));
977 368e06fd pkanavos
            StatusNotification.NotifyForFiles(filesToDelete, String.Format("{0} files were deleted", filesToDelete.Count),
978 368e06fd pkanavos
                                              TraceLevel.Info);
979 aa7ac00e Panagiotis Kanavos
        }
980 aa7ac00e Panagiotis Kanavos
981 c945b450 pkanavos
        private void ReportConflictForMismatch(string localFilePath)
982 c945b450 pkanavos
        {
983 c945b450 pkanavos
            if (String.IsNullOrWhiteSpace(localFilePath))
984 c945b450 pkanavos
                throw new ArgumentNullException("localFilePath");
985 c945b450 pkanavos
            Contract.EndContractBlock();
986 c945b450 pkanavos
987 c945b450 pkanavos
            StatusKeeper.SetFileState(localFilePath, FileStatus.Conflict, FileOverlayStatus.Conflict, "File changed at the server");
988 c945b450 pkanavos
            UpdateStatus(PithosStatus.HasConflicts);
989 c945b450 pkanavos
            var message = String.Format("Conflict detected for file {0}", localFilePath);
990 c945b450 pkanavos
            Log.Warn(message);
991 c945b450 pkanavos
            StatusNotification.NotifyChange(message, TraceLevel.Warning);
992 c945b450 pkanavos
        }
993 c945b450 pkanavos
994 c945b450 pkanavos
995 c945b450 pkanavos
996 b666b39a Panagiotis Kanavos
        /// <summary>
997 b666b39a Panagiotis Kanavos
        /// Creates a Sync action for each changed server file
998 b666b39a Panagiotis Kanavos
        /// </summary>
999 b666b39a Panagiotis Kanavos
        /// <param name="accountInfo"></param>
1000 b666b39a Panagiotis Kanavos
        /// <param name="changes"></param>
1001 b666b39a Panagiotis Kanavos
        /// <returns></returns>
1002 aa7ac00e Panagiotis Kanavos
        private IEnumerable<CloudAction> ChangesToActions(AccountInfo accountInfo, IEnumerable<ObjectInfo> changes)
1003 aa7ac00e Panagiotis Kanavos
        {
1004 aa7ac00e Panagiotis Kanavos
            if (changes == null)
1005 aa7ac00e Panagiotis Kanavos
                throw new ArgumentNullException();
1006 aa7ac00e Panagiotis Kanavos
            Contract.EndContractBlock();
1007 fbbbe99b Panagiotis Kanavos
            var fileAgent = FileAgent.GetFileAgent(accountInfo);
1008 aa7ac00e Panagiotis Kanavos
1009 aa7ac00e Panagiotis Kanavos
            //In order to avoid multiple iterations over the files, we iterate only once
1010 aa7ac00e Panagiotis Kanavos
            //over the remote files
1011 aa7ac00e Panagiotis Kanavos
            foreach (var objectInfo in changes)
1012 aa7ac00e Panagiotis Kanavos
            {
1013 aa7ac00e Panagiotis Kanavos
                var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);
1014 b666b39a Panagiotis Kanavos
                //If a directory object already exists, we may need to sync it
1015 aa7ac00e Panagiotis Kanavos
                if (fileAgent.Exists(relativePath))
1016 aa7ac00e Panagiotis Kanavos
                {
1017 aa7ac00e Panagiotis Kanavos
                    var localFile = fileAgent.GetFileSystemInfo(relativePath);
1018 b666b39a Panagiotis Kanavos
                    //We don't need to sync directories
1019 268bec7f pkanavos
                    if (objectInfo.IsDirectory && localFile is DirectoryInfo)
1020 aa7ac00e Panagiotis Kanavos
                        continue;
1021 aa7ac00e Panagiotis Kanavos
                    using (new SessionScope(FlushAction.Never))
1022 aa7ac00e Panagiotis Kanavos
                    {
1023 aa7ac00e Panagiotis Kanavos
                        var state = StatusKeeper.GetStateByFilePath(localFile.FullName);
1024 aa7ac00e Panagiotis Kanavos
                        _lastSeen[localFile.FullName] = DateTime.Now;
1025 aa7ac00e Panagiotis Kanavos
                        //Common files should be checked on a per-case basis to detect differences, which is newer
1026 aa7ac00e Panagiotis Kanavos
1027 aa7ac00e Panagiotis Kanavos
                        yield return new CloudAction(accountInfo, CloudActionType.MustSynch,
1028 aa7ac00e Panagiotis Kanavos
                                                     localFile, objectInfo, state, accountInfo.BlockSize,
1029 43dd02a8 pkanavos
                                                     accountInfo.BlockHash,"Poll Changes");
1030 aa7ac00e Panagiotis Kanavos
                    }
1031 aa7ac00e Panagiotis Kanavos
                }
1032 aa7ac00e Panagiotis Kanavos
                else
1033 aa7ac00e Panagiotis Kanavos
                {
1034 aa7ac00e Panagiotis Kanavos
                    //Remote files should be downloaded
1035 43dd02a8 pkanavos
                    yield return new CloudDownloadAction(accountInfo, objectInfo,"Poll Changes");
1036 aa7ac00e Panagiotis Kanavos
                }
1037 aa7ac00e Panagiotis Kanavos
            }
1038 aa7ac00e Panagiotis Kanavos
        }
1039 aa7ac00e Panagiotis Kanavos
1040 b666b39a Panagiotis Kanavos
        /// <summary>
1041 b666b39a Panagiotis Kanavos
        /// Creates a Local Move action for each moved server file
1042 b666b39a Panagiotis Kanavos
        /// </summary>
1043 b666b39a Panagiotis Kanavos
        /// <param name="accountInfo"></param>
1044 b666b39a Panagiotis Kanavos
        /// <param name="moves"></param>
1045 b666b39a Panagiotis Kanavos
        /// <returns></returns>
1046 b666b39a Panagiotis Kanavos
        private IEnumerable<CloudAction> MovesToActions(AccountInfo accountInfo, IEnumerable<ObjectInfo> moves)
1047 b666b39a Panagiotis Kanavos
        {
1048 b666b39a Panagiotis Kanavos
            if (moves == null)
1049 b666b39a Panagiotis Kanavos
                throw new ArgumentNullException();
1050 b666b39a Panagiotis Kanavos
            Contract.EndContractBlock();
1051 b666b39a Panagiotis Kanavos
            var fileAgent = FileAgent.GetFileAgent(accountInfo);
1052 b666b39a Panagiotis Kanavos
1053 b666b39a Panagiotis Kanavos
            //In order to avoid multiple iterations over the files, we iterate only once
1054 b666b39a Panagiotis Kanavos
            //over the remote files
1055 b666b39a Panagiotis Kanavos
            foreach (var objectInfo in moves)
1056 b666b39a Panagiotis Kanavos
            {
1057 b666b39a Panagiotis Kanavos
                var previousRelativepath = objectInfo.Previous.RelativeUrlToFilePath(accountInfo.UserName);
1058 b666b39a Panagiotis Kanavos
                //If the previous file already exists, we can execute a Move operation
1059 b666b39a Panagiotis Kanavos
                if (fileAgent.Exists(previousRelativepath))
1060 b666b39a Panagiotis Kanavos
                {
1061 b666b39a Panagiotis Kanavos
                    var previousFile = fileAgent.GetFileSystemInfo(previousRelativepath);
1062 b666b39a Panagiotis Kanavos
                    using (new SessionScope(FlushAction.Never))
1063 b666b39a Panagiotis Kanavos
                    {
1064 b666b39a Panagiotis Kanavos
                        var state = StatusKeeper.GetStateByFilePath(previousFile.FullName);
1065 b666b39a Panagiotis Kanavos
                        _lastSeen[previousFile.FullName] = DateTime.Now;
1066 b666b39a Panagiotis Kanavos
1067 b666b39a Panagiotis Kanavos
                        //For each moved object we need to move both the local file and update                                                
1068 b666b39a Panagiotis Kanavos
                        yield return new CloudAction(accountInfo, CloudActionType.RenameLocal,
1069 b666b39a Panagiotis Kanavos
                                                     previousFile, objectInfo, state, accountInfo.BlockSize,
1070 43dd02a8 pkanavos
                                                     accountInfo.BlockHash,"Poll Moves");
1071 b666b39a Panagiotis Kanavos
                        //For modified files, we need to download the changes as well
1072 1cc1e8c5 pkanavos
                        if (objectInfo.X_Object_Hash != objectInfo.PreviousHash)
1073 43dd02a8 pkanavos
                            yield return new CloudDownloadAction(accountInfo,objectInfo, "Poll Moves");
1074 b666b39a Panagiotis Kanavos
                    }
1075 b666b39a Panagiotis Kanavos
                }
1076 b666b39a Panagiotis Kanavos
                //If the previous file does not exist, we need to download it in the new location
1077 b666b39a Panagiotis Kanavos
                else
1078 b666b39a Panagiotis Kanavos
                {
1079 b666b39a Panagiotis Kanavos
                    //Remote files should be downloaded
1080 43dd02a8 pkanavos
                    yield return new CloudDownloadAction(accountInfo, objectInfo, "Poll Moves");
1081 b666b39a Panagiotis Kanavos
                }
1082 b666b39a Panagiotis Kanavos
            }
1083 b666b39a Panagiotis Kanavos
        }
1084 b666b39a Panagiotis Kanavos
1085 b666b39a Panagiotis Kanavos
1086 b666b39a Panagiotis Kanavos
        /// <summary>
1087 b666b39a Panagiotis Kanavos
        /// Creates a download action for each new server file
1088 b666b39a Panagiotis Kanavos
        /// </summary>
1089 b666b39a Panagiotis Kanavos
        /// <param name="accountInfo"></param>
1090 b666b39a Panagiotis Kanavos
        /// <param name="creates"></param>
1091 b666b39a Panagiotis Kanavos
        /// <returns></returns>
1092 aa7ac00e Panagiotis Kanavos
        private IEnumerable<CloudAction> CreatesToActions(AccountInfo accountInfo, IEnumerable<ObjectInfo> creates)
1093 aa7ac00e Panagiotis Kanavos
        {
1094 aa7ac00e Panagiotis Kanavos
            if (creates == null)
1095 aa7ac00e Panagiotis Kanavos
                throw new ArgumentNullException();
1096 aa7ac00e Panagiotis Kanavos
            Contract.EndContractBlock();
1097 fbbbe99b Panagiotis Kanavos
            var fileAgent = FileAgent.GetFileAgent(accountInfo);
1098 aa7ac00e Panagiotis Kanavos
1099 aa7ac00e Panagiotis Kanavos
            //In order to avoid multiple iterations over the files, we iterate only once
1100 aa7ac00e Panagiotis Kanavos
            //over the remote files
1101 aa7ac00e Panagiotis Kanavos
            foreach (var objectInfo in creates)
1102 aa7ac00e Panagiotis Kanavos
            {
1103 dccd340f Panagiotis Kanavos
                if (Log.IsDebugEnabled)
1104 dccd340f Panagiotis Kanavos
                    Log.DebugFormat("[NEW INFO] {0}",objectInfo.Uri);
1105 dccd340f Panagiotis Kanavos
1106 aa7ac00e Panagiotis Kanavos
                var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);
1107 0a9d4d18 pkanavos
1108 0a9d4d18 pkanavos
                //If the object already exists, we should check before uploading or downloading
1109 aa7ac00e Panagiotis Kanavos
                if (fileAgent.Exists(relativePath))
1110 aa7ac00e Panagiotis Kanavos
                {
1111 0a9d4d18 pkanavos
                    var localFile= fileAgent.GetFileSystemInfo(relativePath);
1112 0a9d4d18 pkanavos
                    var state = StatusKeeper.GetStateByFilePath(localFile.WithProperCapitalization().FullName);
1113 0a9d4d18 pkanavos
                    yield return new CloudAction(accountInfo, CloudActionType.MustSynch,
1114 0a9d4d18 pkanavos
                                                     localFile, objectInfo, state, accountInfo.BlockSize,
1115 43dd02a8 pkanavos
                                                     accountInfo.BlockHash,"Poll Creates");                    
1116 aa7ac00e Panagiotis Kanavos
                }
1117 aa7ac00e Panagiotis Kanavos
                else
1118 aa7ac00e Panagiotis Kanavos
                {
1119 aa7ac00e Panagiotis Kanavos
                    //Remote files should be downloaded
1120 43dd02a8 pkanavos
                    yield return new CloudDownloadAction(accountInfo, objectInfo,"Poll Creates");
1121 aa7ac00e Panagiotis Kanavos
                }
1122 0a9d4d18 pkanavos
1123 aa7ac00e Panagiotis Kanavos
            }
1124 aa7ac00e Panagiotis Kanavos
        }
1125 aa7ac00e Panagiotis Kanavos
1126 fec5da06 Panagiotis Kanavos
        /// <summary>
1127 fec5da06 Panagiotis Kanavos
        /// Notify the UI to update the visual status
1128 fec5da06 Panagiotis Kanavos
        /// </summary>
1129 fec5da06 Panagiotis Kanavos
        /// <param name="status"></param>
1130 aa7ac00e Panagiotis Kanavos
        private void UpdateStatus(PithosStatus status)
1131 aa7ac00e Panagiotis Kanavos
        {
1132 fec5da06 Panagiotis Kanavos
            try
1133 fec5da06 Panagiotis Kanavos
            {
1134 174bbb6e Panagiotis Kanavos
                StatusNotification.SetPithosStatus(status);
1135 174bbb6e Panagiotis Kanavos
                //StatusNotification.Notify(new Notification());
1136 fec5da06 Panagiotis Kanavos
            }
1137 fec5da06 Panagiotis Kanavos
            catch (Exception exc)
1138 fec5da06 Panagiotis Kanavos
            {
1139 fec5da06 Panagiotis Kanavos
                //Failure is not critical, just log it
1140 fec5da06 Panagiotis Kanavos
                Log.Warn("Error while updating status", exc);
1141 fec5da06 Panagiotis Kanavos
            }
1142 aa7ac00e Panagiotis Kanavos
        }
1143 aa7ac00e Panagiotis Kanavos
1144 aa7ac00e Panagiotis Kanavos
        private static void CreateContainerFolders(AccountInfo accountInfo, IEnumerable<ContainerInfo> containers)
1145 aa7ac00e Panagiotis Kanavos
        {
1146 aa7ac00e Panagiotis Kanavos
            var containerPaths = from container in containers
1147 aa7ac00e Panagiotis Kanavos
                                 let containerPath = Path.Combine(accountInfo.AccountPath, container.Name)
1148 aa7ac00e Panagiotis Kanavos
                                 where container.Name != FolderConstants.TrashContainer && !Directory.Exists(containerPath)
1149 aa7ac00e Panagiotis Kanavos
                                 select containerPath;
1150 aa7ac00e Panagiotis Kanavos
1151 aa7ac00e Panagiotis Kanavos
            foreach (var path in containerPaths)
1152 aa7ac00e Panagiotis Kanavos
            {
1153 aa7ac00e Panagiotis Kanavos
                Directory.CreateDirectory(path);
1154 aa7ac00e Panagiotis Kanavos
            }
1155 aa7ac00e Panagiotis Kanavos
        }
1156 759bd3c4 Panagiotis Kanavos
1157 fec5da06 Panagiotis Kanavos
        public void AddAccount(AccountInfo accountInfo)
1158 fec5da06 Panagiotis Kanavos
        {
1159 ec1a1baf Panagiotis Kanavos
            //Avoid adding a duplicate accountInfo
1160 1a41e9ec pkanavos
            _accounts.TryAdd(accountInfo.AccountKey, accountInfo);
1161 ec1a1baf Panagiotis Kanavos
        }
1162 ec1a1baf Panagiotis Kanavos
1163 ec1a1baf Panagiotis Kanavos
        public void RemoveAccount(AccountInfo accountInfo)
1164 ec1a1baf Panagiotis Kanavos
        {
1165 ec1a1baf Panagiotis Kanavos
            AccountInfo account;
1166 1a41e9ec pkanavos
            _accounts.TryRemove(accountInfo.AccountKey, out account);
1167 732276d3 pkanavos
1168 dccd340f Panagiotis Kanavos
            SnapshotDifferencer differencer;
1169 1a41e9ec pkanavos
            _differencer.Differencers.TryRemove(accountInfo.AccountKey, out differencer);
1170 fec5da06 Panagiotis Kanavos
        }
1171 026a6c5a Panagiotis Kanavos
1172 026a6c5a Panagiotis Kanavos
        public void SetSelectivePaths(AccountInfo accountInfo,Uri[] added, Uri[] removed)
1173 026a6c5a Panagiotis Kanavos
        {
1174 026a6c5a Panagiotis Kanavos
            AbortRemovedPaths(accountInfo,removed);
1175 9036d725 pkanavos
            //DownloadNewPaths(accountInfo,added);
1176 026a6c5a Panagiotis Kanavos
        }
1177 026a6c5a Panagiotis Kanavos
1178 9036d725 pkanavos
/*
1179 026a6c5a Panagiotis Kanavos
        private void DownloadNewPaths(AccountInfo accountInfo, Uri[] added)
1180 026a6c5a Panagiotis Kanavos
        {
1181 026a6c5a Panagiotis Kanavos
            var client = new CloudFilesClient(accountInfo);
1182 026a6c5a Panagiotis Kanavos
            foreach (var folderUri in added)
1183 026a6c5a Panagiotis Kanavos
            {
1184 d21f3c77 pkanavos
                try
1185 026a6c5a Panagiotis Kanavos
                {
1186 d21f3c77 pkanavos
1187 d21f3c77 pkanavos
                    string account;
1188 d21f3c77 pkanavos
                    string container;
1189 d21f3c77 pkanavos
                    var segmentsCount = folderUri.Segments.Length;
1190 dd19241a pkanavos
                    //Is this an account URL?
1191 d21f3c77 pkanavos
                    if (segmentsCount < 3)
1192 d21f3c77 pkanavos
                        continue;
1193 dd19241a pkanavos
                    //Is this a container or  folder URL?
1194 d21f3c77 pkanavos
                    if (segmentsCount == 3)
1195 d21f3c77 pkanavos
                    {
1196 d21f3c77 pkanavos
                        account = folderUri.Segments[1].TrimEnd('/');
1197 d21f3c77 pkanavos
                        container = folderUri.Segments[2].TrimEnd('/');
1198 d21f3c77 pkanavos
                    }
1199 d21f3c77 pkanavos
                    else
1200 d21f3c77 pkanavos
                    {
1201 d21f3c77 pkanavos
                        account = folderUri.Segments[2].TrimEnd('/');
1202 d21f3c77 pkanavos
                        container = folderUri.Segments[3].TrimEnd('/');
1203 d21f3c77 pkanavos
                    }
1204 d21f3c77 pkanavos
                    IList<ObjectInfo> items;
1205 d21f3c77 pkanavos
                    if (segmentsCount > 3)
1206 d21f3c77 pkanavos
                    {
1207 dd19241a pkanavos
                        //List folder
1208 d21f3c77 pkanavos
                        var folder = String.Join("", folderUri.Segments.Splice(4));
1209 d21f3c77 pkanavos
                        items = client.ListObjects(account, container, folder);
1210 d21f3c77 pkanavos
                    }
1211 d21f3c77 pkanavos
                    else
1212 d21f3c77 pkanavos
                    {
1213 dd19241a pkanavos
                        //List container
1214 d21f3c77 pkanavos
                        items = client.ListObjects(account, container);
1215 d21f3c77 pkanavos
                    }
1216 d21f3c77 pkanavos
                    var actions = CreatesToActions(accountInfo, items);
1217 d21f3c77 pkanavos
                    foreach (var action in actions)
1218 d21f3c77 pkanavos
                    {
1219 d21f3c77 pkanavos
                        NetworkAgent.Post(action);
1220 d21f3c77 pkanavos
                    }
1221 026a6c5a Panagiotis Kanavos
                }
1222 d21f3c77 pkanavos
                catch (Exception exc)
1223 026a6c5a Panagiotis Kanavos
                {
1224 d21f3c77 pkanavos
                    Log.WarnFormat("Listing of new selective path [{0}] failed with \r\n{1}", folderUri, exc);
1225 026a6c5a Panagiotis Kanavos
                }
1226 026a6c5a Panagiotis Kanavos
            }
1227 026a6c5a Panagiotis Kanavos
1228 026a6c5a Panagiotis Kanavos
            //Need to get a listing of each of the URLs, then post them to the NetworkAgent
1229 026a6c5a Panagiotis Kanavos
            //CreatesToActions(accountInfo,)
1230 026a6c5a Panagiotis Kanavos
1231 9036d725 pkanavos
/*            NetworkAgent.Post();#1#
1232 026a6c5a Panagiotis Kanavos
        }
1233 9036d725 pkanavos
*/
1234 026a6c5a Panagiotis Kanavos
1235 026a6c5a Panagiotis Kanavos
        private void AbortRemovedPaths(AccountInfo accountInfo, Uri[] removed)
1236 026a6c5a Panagiotis Kanavos
        {
1237 026a6c5a Panagiotis Kanavos
            /*this.NetworkAgent.*/
1238 026a6c5a Panagiotis Kanavos
        }
1239 aa7ac00e Panagiotis Kanavos
    }
1240 aa7ac00e Panagiotis Kanavos
}