Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / NetworkAgent.cs @ e5b65606

History | View | Annotate | Download (59.6 kB)

1 f3d080df Panagiotis Kanavos
// -----------------------------------------------------------------------
2 f3d080df Panagiotis Kanavos
// <copyright file="NetworkAgent.cs" company="GRNET">
3 f3d080df Panagiotis Kanavos
// Copyright 2011 GRNET S.A. All rights reserved.
4 f3d080df Panagiotis Kanavos
// 
5 f3d080df Panagiotis Kanavos
// Redistribution and use in source and binary forms, with or
6 f3d080df Panagiotis Kanavos
// without modification, are permitted provided that the following
7 f3d080df Panagiotis Kanavos
// conditions are met:
8 f3d080df Panagiotis Kanavos
// 
9 f3d080df Panagiotis Kanavos
//   1. Redistributions of source code must retain the above
10 f3d080df Panagiotis Kanavos
//      copyright notice, this list of conditions and the following
11 f3d080df Panagiotis Kanavos
//      disclaimer.
12 f3d080df Panagiotis Kanavos
// 
13 f3d080df Panagiotis Kanavos
//   2. Redistributions in binary form must reproduce the above
14 f3d080df Panagiotis Kanavos
//      copyright notice, this list of conditions and the following
15 f3d080df Panagiotis Kanavos
//      disclaimer in the documentation and/or other materials
16 f3d080df Panagiotis Kanavos
//      provided with the distribution.
17 f3d080df Panagiotis Kanavos
// 
18 f3d080df Panagiotis Kanavos
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 f3d080df Panagiotis Kanavos
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 f3d080df Panagiotis Kanavos
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 f3d080df Panagiotis Kanavos
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 f3d080df Panagiotis Kanavos
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 f3d080df Panagiotis Kanavos
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 f3d080df Panagiotis Kanavos
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 f3d080df Panagiotis Kanavos
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 f3d080df Panagiotis Kanavos
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 f3d080df Panagiotis Kanavos
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 f3d080df Panagiotis Kanavos
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 f3d080df Panagiotis Kanavos
// POSSIBILITY OF SUCH DAMAGE.
30 f3d080df Panagiotis Kanavos
// 
31 f3d080df Panagiotis Kanavos
// The views and conclusions contained in the software and
32 f3d080df Panagiotis Kanavos
// documentation are those of the authors and should not be
33 f3d080df Panagiotis Kanavos
// interpreted as representing official policies, either expressed
34 f3d080df Panagiotis Kanavos
// or implied, of GRNET S.A.
35 f3d080df Panagiotis Kanavos
// </copyright>
36 f3d080df Panagiotis Kanavos
// -----------------------------------------------------------------------
37 f3d080df Panagiotis Kanavos
38 f3d080df Panagiotis Kanavos
using System;
39 4f6d51d4 Panagiotis Kanavos
using System.Collections.Concurrent;
40 9c4346c9 Panagiotis Kanavos
using System.Collections.Generic;
41 9c4346c9 Panagiotis Kanavos
using System.ComponentModel.Composition;
42 9c4346c9 Panagiotis Kanavos
using System.Diagnostics;
43 9c4346c9 Panagiotis Kanavos
using System.Diagnostics.Contracts;
44 9c4346c9 Panagiotis Kanavos
using System.IO;
45 9c4346c9 Panagiotis Kanavos
using System.Linq;
46 0af3141d Panagiotis Kanavos
using System.Net;
47 0b346191 Panagiotis Kanavos
using System.Threading;
48 9c4346c9 Panagiotis Kanavos
using System.Threading.Tasks;
49 3c76f045 Panagiotis Kanavos
using System.Threading.Tasks.Dataflow;
50 e81dd1f6 Panagiotis Kanavos
using Castle.ActiveRecord;
51 9c4346c9 Panagiotis Kanavos
using Pithos.Interfaces;
52 5ce54458 Panagiotis Kanavos
using Pithos.Network;
53 cfed7823 Panagiotis Kanavos
using log4net;
54 9c4346c9 Panagiotis Kanavos
55 9c4346c9 Panagiotis Kanavos
namespace Pithos.Core.Agents
56 9c4346c9 Panagiotis Kanavos
{
57 f3d080df Panagiotis Kanavos
    //TODO: Ensure all network operations use exact casing. Pithos is case sensitive
58 9c4346c9 Panagiotis Kanavos
    [Export]
59 9c4346c9 Panagiotis Kanavos
    public class NetworkAgent
60 9c4346c9 Panagiotis Kanavos
    {
61 9c4346c9 Panagiotis Kanavos
        private Agent<CloudAction> _agent;
62 9c4346c9 Panagiotis Kanavos
63 f3d080df Panagiotis Kanavos
        //A separate agent is used to execute delete actions immediatelly;
64 3c76f045 Panagiotis Kanavos
        private ActionBlock<CloudDeleteAction> _deleteAgent;
65 14ecd267 Panagiotis Kanavos
        readonly ConcurrentDictionary<string,DateTime> _deletedFiles=new ConcurrentDictionary<string, DateTime>();
66 5e31048f Panagiotis Kanavos
67 0b346191 Panagiotis Kanavos
68 9d6d2f6e Panagiotis Kanavos
        private readonly ManualResetEventSlim _pauseAgent = new ManualResetEventSlim(true);
69 0b346191 Panagiotis Kanavos
70 e81dd1f6 Panagiotis Kanavos
        [System.ComponentModel.Composition.Import]
71 9c4346c9 Panagiotis Kanavos
        public IStatusKeeper StatusKeeper { get; set; }
72 9c4346c9 Panagiotis Kanavos
        
73 9c4346c9 Panagiotis Kanavos
        public IStatusNotification StatusNotification { get; set; }
74 9c4346c9 Panagiotis Kanavos
75 5120f3cb Panagiotis Kanavos
        private static readonly ILog Log = LogManager.GetLogger("NetworkAgent");
76 cfed7823 Panagiotis Kanavos
77 4f6d51d4 Panagiotis Kanavos
        private readonly ConcurrentBag<AccountInfo> _accounts = new ConcurrentBag<AccountInfo>();
78 5ce54458 Panagiotis Kanavos
79 133f83c2 Panagiotis Kanavos
        [System.ComponentModel.Composition.Import]
80 133f83c2 Panagiotis Kanavos
        public IPithosSettings Settings { get; set; }
81 540b8cf8 Panagiotis Kanavos
82 540b8cf8 Panagiotis Kanavos
        private bool _firstPoll = true;
83 29a6b387 Panagiotis Kanavos
        private TaskCompletionSource<bool> _tcs;
84 29a6b387 Panagiotis Kanavos
85 73cdd135 Panagiotis Kanavos
        public void Start()
86 9c4346c9 Panagiotis Kanavos
        {
87 540b8cf8 Panagiotis Kanavos
            _firstPoll = true;
88 9c4346c9 Panagiotis Kanavos
            _agent = Agent<CloudAction>.Start(inbox =>
89 9c4346c9 Panagiotis Kanavos
            {
90 9c4346c9 Panagiotis Kanavos
                Action loop = null;
91 9c4346c9 Panagiotis Kanavos
                loop = () =>
92 9c4346c9 Panagiotis Kanavos
                {
93 9d6d2f6e Panagiotis Kanavos
                    _pauseAgent.Wait();
94 9c4346c9 Panagiotis Kanavos
                    var message = inbox.Receive();
95 a64c87c8 Panagiotis Kanavos
                    var process=message.Then(Process,inbox.CancellationToken);
96 a27aa447 Panagiotis Kanavos
                    inbox.LoopAsync(process, loop);
97 a64c87c8 Panagiotis Kanavos
                };
98 a64c87c8 Panagiotis Kanavos
                loop();
99 f3d080df Panagiotis Kanavos
            });
100 f3d080df Panagiotis Kanavos
101 3c76f045 Panagiotis Kanavos
            _deleteAgent = new ActionBlock<CloudDeleteAction>(message =>ProcessDelete(message),new ExecutionDataflowBlockOptions{MaxDegreeOfParallelism=4});
102 3c76f045 Panagiotis Kanavos
            /*
103 f3d080df Panagiotis Kanavos
                Action loop = null;
104 f3d080df Panagiotis Kanavos
                loop = () =>
105 f3d080df Panagiotis Kanavos
                            {
106 f3d080df Panagiotis Kanavos
                                var message = inbox.Receive();
107 f3d080df Panagiotis Kanavos
                                var process = message.Then(ProcessDelete,inbox.CancellationToken);
108 f3d080df Panagiotis Kanavos
                                inbox.LoopAsync(process, loop);
109 f3d080df Panagiotis Kanavos
                            };
110 f3d080df Panagiotis Kanavos
                loop();
111 3c76f045 Panagiotis Kanavos
*/
112 f3d080df Panagiotis Kanavos
113 a64c87c8 Panagiotis Kanavos
        }
114 a27aa447 Panagiotis Kanavos
115 27361404 Panagiotis Kanavos
        private async Task Process(CloudAction action)
116 a64c87c8 Panagiotis Kanavos
        {
117 a64c87c8 Panagiotis Kanavos
            if (action == null)
118 a64c87c8 Panagiotis Kanavos
                throw new ArgumentNullException("action");
119 c53aa229 Panagiotis Kanavos
            if (action.AccountInfo==null)
120 c53aa229 Panagiotis Kanavos
                throw new ArgumentException("The action.AccountInfo is empty","action");
121 a64c87c8 Panagiotis Kanavos
            Contract.EndContractBlock();
122 a27aa447 Panagiotis Kanavos
123 e5b65606 Panagiotis Kanavos
            StatusKeeper.SetPithosStatus(PithosStatus.Syncing);
124 c53aa229 Panagiotis Kanavos
            var accountInfo = action.AccountInfo;
125 c53aa229 Panagiotis Kanavos
126 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["NETWORK"].Push("PROCESS"))
127 cfed7823 Panagiotis Kanavos
            {                
128 27361404 Panagiotis Kanavos
                Log.InfoFormat("[ACTION] Start Processing {0}", action);
129 cfed7823 Panagiotis Kanavos
130 1bfc38f1 Panagiotis Kanavos
                var cloudFile = action.CloudFile;
131 1bfc38f1 Panagiotis Kanavos
                var downloadPath = action.GetDownloadPath();
132 cfed7823 Panagiotis Kanavos
133 cfed7823 Panagiotis Kanavos
                try
134 cfed7823 Panagiotis Kanavos
                {
135 cfed7823 Panagiotis Kanavos
136 14ecd267 Panagiotis Kanavos
                    if (action.Action == CloudActionType.DeleteCloud)
137 cfed7823 Panagiotis Kanavos
                    {
138 14ecd267 Panagiotis Kanavos
                        //Redirect deletes to the delete agent 
139 e5b65606 Panagiotis Kanavos
                        _deleteAgent.Post((CloudDeleteAction)action);
140 14ecd267 Panagiotis Kanavos
                    }
141 14ecd267 Panagiotis Kanavos
                    if (IsDeletedFile(action))
142 14ecd267 Panagiotis Kanavos
                    {
143 14ecd267 Panagiotis Kanavos
                        //Clear the status of already deleted files to avoid reprocessing
144 3c76f045 Panagiotis Kanavos
                        if (action.LocalFile != null)
145 3c76f045 Panagiotis Kanavos
                            this.StatusKeeper.ClearFileStatus(action.LocalFile.FullName);
146 14ecd267 Panagiotis Kanavos
                    }
147 14ecd267 Panagiotis Kanavos
                    else
148 14ecd267 Panagiotis Kanavos
                    {
149 14ecd267 Panagiotis Kanavos
                        switch (action.Action)
150 14ecd267 Panagiotis Kanavos
                        {
151 14ecd267 Panagiotis Kanavos
                            case CloudActionType.UploadUnconditional:
152 14ecd267 Panagiotis Kanavos
                                //Abort if the file was deleted before we reached this point
153 039a89e5 Panagiotis Kanavos
                                await UploadCloudFile(action);
154 14ecd267 Panagiotis Kanavos
                                break;
155 14ecd267 Panagiotis Kanavos
                            case CloudActionType.DownloadUnconditional:
156 039a89e5 Panagiotis Kanavos
                                await DownloadCloudFile(accountInfo, cloudFile, downloadPath);
157 14ecd267 Panagiotis Kanavos
                                break;
158 14ecd267 Panagiotis Kanavos
                            case CloudActionType.RenameCloud:
159 e5b65606 Panagiotis Kanavos
                                var moveAction = (CloudMoveAction)action;
160 039a89e5 Panagiotis Kanavos
                                RenameCloudFile(accountInfo, moveAction);
161 14ecd267 Panagiotis Kanavos
                                break;
162 14ecd267 Panagiotis Kanavos
                            case CloudActionType.MustSynch:
163 14ecd267 Panagiotis Kanavos
                                if (!File.Exists(downloadPath) && !Directory.Exists(downloadPath))
164 14ecd267 Panagiotis Kanavos
                                {
165 039a89e5 Panagiotis Kanavos
                                    await DownloadCloudFile(accountInfo, cloudFile, downloadPath);
166 14ecd267 Panagiotis Kanavos
                                }
167 14ecd267 Panagiotis Kanavos
                                else
168 14ecd267 Panagiotis Kanavos
                                {
169 039a89e5 Panagiotis Kanavos
                                    await SyncFiles(accountInfo, action);
170 14ecd267 Panagiotis Kanavos
                                }
171 14ecd267 Panagiotis Kanavos
                                break;
172 14ecd267 Panagiotis Kanavos
                        }
173 cfed7823 Panagiotis Kanavos
                    }
174 cfed7823 Panagiotis Kanavos
                    Log.InfoFormat("[ACTION] End Processing {0}:{1}->{2}", action.Action, action.LocalFile,
175 cfed7823 Panagiotis Kanavos
                                           action.CloudFile.Name);
176 cfed7823 Panagiotis Kanavos
                }
177 a0dcfcc9 Panagiotis Kanavos
                catch (WebException exc)
178 a0dcfcc9 Panagiotis Kanavos
                {
179 73cdd135 Panagiotis Kanavos
                    Log.ErrorFormat("[WEB ERROR] {0} : {1} -> {2} due to exception\r\n{3}", action.Action, action.LocalFile, action.CloudFile, exc);
180 a0dcfcc9 Panagiotis Kanavos
                }
181 cfed7823 Panagiotis Kanavos
                catch (OperationCanceledException)
182 cfed7823 Panagiotis Kanavos
                {
183 cfed7823 Panagiotis Kanavos
                    throw;
184 cfed7823 Panagiotis Kanavos
                }
185 73cdd135 Panagiotis Kanavos
                catch (DirectoryNotFoundException)
186 73cdd135 Panagiotis Kanavos
                {
187 73cdd135 Panagiotis Kanavos
                    Log.ErrorFormat("{0} : {1} -> {2}  failed because the directory was not found.\n Rescheduling a delete",
188 73cdd135 Panagiotis Kanavos
                        action.Action, action.LocalFile, action.CloudFile);
189 73cdd135 Panagiotis Kanavos
                    //Post a delete action for the missing file
190 e5b65606 Panagiotis Kanavos
                    Post(new CloudDeleteAction(action));
191 73cdd135 Panagiotis Kanavos
                }
192 73cdd135 Panagiotis Kanavos
                catch (FileNotFoundException)
193 cfed7823 Panagiotis Kanavos
                {
194 cfed7823 Panagiotis Kanavos
                    Log.ErrorFormat("{0} : {1} -> {2}  failed because the file was not found.\n Rescheduling a delete",
195 73cdd135 Panagiotis Kanavos
                        action.Action, action.LocalFile, action.CloudFile);
196 1bfc38f1 Panagiotis Kanavos
                    //Post a delete action for the missing file
197 1bfc38f1 Panagiotis Kanavos
                    Post(new CloudDeleteAction(action));
198 cfed7823 Panagiotis Kanavos
                }
199 cfed7823 Panagiotis Kanavos
                catch (Exception exc)
200 cfed7823 Panagiotis Kanavos
                {
201 cfed7823 Panagiotis Kanavos
                    Log.ErrorFormat("[REQUEUE] {0} : {1} -> {2} due to exception\r\n{3}",
202 cfed7823 Panagiotis Kanavos
                                     action.Action, action.LocalFile, action.CloudFile, exc);
203 cfed7823 Panagiotis Kanavos
204 cfed7823 Panagiotis Kanavos
                    _agent.Post(action);
205 e5b65606 Panagiotis Kanavos
                }
206 e5b65606 Panagiotis Kanavos
                finally
207 e5b65606 Panagiotis Kanavos
                {
208 e5b65606 Panagiotis Kanavos
                    StatusKeeper.SetPithosStatus(PithosStatus.InSynch);
209 e5b65606 Panagiotis Kanavos
                }
210 cfed7823 Panagiotis Kanavos
            }
211 cfed7823 Panagiotis Kanavos
        }
212 cfed7823 Panagiotis Kanavos
213 f3d080df Panagiotis Kanavos
        /// <summary>
214 f3d080df Panagiotis Kanavos
        /// Processes cloud delete actions
215 f3d080df Panagiotis Kanavos
        /// </summary>
216 f3d080df Panagiotis Kanavos
        /// <param name="action">The delete action to execute</param>
217 f3d080df Panagiotis Kanavos
        /// <returns></returns>
218 f3d080df Panagiotis Kanavos
        /// <remarks>
219 f3d080df Panagiotis Kanavos
        /// When a file/folder is deleted locally, we must delete it ASAP from the server and block any download
220 f3d080df Panagiotis Kanavos
        /// operations that may be in progress.
221 f3d080df Panagiotis Kanavos
        /// <para>
222 f3d080df Panagiotis Kanavos
        /// A separate agent is used to process deletes because the main agent may be busy with a long operation.
223 f3d080df Panagiotis Kanavos
        /// </para>
224 f3d080df Panagiotis Kanavos
        /// </remarks>
225 f3d080df Panagiotis Kanavos
        private async Task ProcessDelete(CloudDeleteAction action)
226 f3d080df Panagiotis Kanavos
        {
227 f3d080df Panagiotis Kanavos
            if (action == null)
228 f3d080df Panagiotis Kanavos
                throw new ArgumentNullException("action");
229 f3d080df Panagiotis Kanavos
            if (action.AccountInfo==null)
230 f3d080df Panagiotis Kanavos
                throw new ArgumentException("The action.AccountInfo is empty","action");
231 f3d080df Panagiotis Kanavos
            Contract.EndContractBlock();
232 f3d080df Panagiotis Kanavos
233 f3d080df Panagiotis Kanavos
            var accountInfo = action.AccountInfo;
234 f3d080df Panagiotis Kanavos
235 f3d080df Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["NETWORK"].Push("PROCESS"))
236 f3d080df Panagiotis Kanavos
            {                
237 f3d080df Panagiotis Kanavos
                Log.InfoFormat("[ACTION] Start Processing {0}", action);
238 f3d080df Panagiotis Kanavos
239 f3d080df Panagiotis Kanavos
                var cloudFile = action.CloudFile;
240 f3d080df Panagiotis Kanavos
241 f3d080df Panagiotis Kanavos
                try
242 f3d080df Panagiotis Kanavos
                {
243 f3d080df Panagiotis Kanavos
                    //Acquire a lock on the deleted file to prevent uploading/downloading operations from the normal
244 f3d080df Panagiotis Kanavos
                    //agent
245 f3d080df Panagiotis Kanavos
                    using (var gate = NetworkGate.Acquire(action.LocalFile.FullName, NetworkOperation.Deleting))
246 f3d080df Panagiotis Kanavos
                    {
247 0b346191 Panagiotis Kanavos
248 039a89e5 Panagiotis Kanavos
                        //Add the file URL to the deleted files list
249 039a89e5 Panagiotis Kanavos
                        var key = GetFileKey(action.CloudFile);
250 0b346191 Panagiotis Kanavos
                        _deletedFiles[key] = DateTime.Now;
251 3c76f045 Panagiotis Kanavos
252 0b346191 Panagiotis Kanavos
                        _pauseAgent.Reset();
253 f3d080df Panagiotis Kanavos
                        // and then delete the file from the server
254 f3d080df Panagiotis Kanavos
                        DeleteCloudFile(accountInfo, cloudFile);
255 3c76f045 Panagiotis Kanavos
256 f3d080df Panagiotis Kanavos
                        Log.InfoFormat("[ACTION] End Delete {0}:{1}->{2}", action.Action, action.LocalFile,
257 f3d080df Panagiotis Kanavos
                                       action.CloudFile.Name);
258 f3d080df Panagiotis Kanavos
                    }
259 f3d080df Panagiotis Kanavos
                }
260 f3d080df Panagiotis Kanavos
                catch (WebException exc)
261 f3d080df Panagiotis Kanavos
                {
262 f3d080df Panagiotis Kanavos
                    Log.ErrorFormat("[WEB ERROR] {0} : {1} -> {2} due to exception\r\n{3}", action.Action, action.LocalFile, action.CloudFile, exc);
263 f3d080df Panagiotis Kanavos
                }
264 f3d080df Panagiotis Kanavos
                catch (OperationCanceledException)
265 f3d080df Panagiotis Kanavos
                {
266 f3d080df Panagiotis Kanavos
                    throw;
267 f3d080df Panagiotis Kanavos
                }
268 f3d080df Panagiotis Kanavos
                catch (DirectoryNotFoundException)
269 f3d080df Panagiotis Kanavos
                {
270 f3d080df Panagiotis Kanavos
                    Log.ErrorFormat("{0} : {1} -> {2}  failed because the directory was not found.\n Rescheduling a delete",
271 f3d080df Panagiotis Kanavos
                        action.Action, action.LocalFile, action.CloudFile);
272 f3d080df Panagiotis Kanavos
                    //Repost a delete action for the missing file
273 0b346191 Panagiotis Kanavos
                    _deleteAgent.Post(action);
274 f3d080df Panagiotis Kanavos
                }
275 f3d080df Panagiotis Kanavos
                catch (FileNotFoundException)
276 f3d080df Panagiotis Kanavos
                {
277 f3d080df Panagiotis Kanavos
                    Log.ErrorFormat("{0} : {1} -> {2}  failed because the file was not found.\n Rescheduling a delete",
278 f3d080df Panagiotis Kanavos
                        action.Action, action.LocalFile, action.CloudFile);
279 f3d080df Panagiotis Kanavos
                    //Post a delete action for the missing file
280 f3d080df Panagiotis Kanavos
                    _deleteAgent.Post(action);
281 f3d080df Panagiotis Kanavos
                }
282 f3d080df Panagiotis Kanavos
                catch (Exception exc)
283 f3d080df Panagiotis Kanavos
                {
284 f3d080df Panagiotis Kanavos
                    Log.ErrorFormat("[REQUEUE] {0} : {1} -> {2} due to exception\r\n{3}",
285 f3d080df Panagiotis Kanavos
                                     action.Action, action.LocalFile, action.CloudFile, exc);
286 f3d080df Panagiotis Kanavos
287 f3d080df Panagiotis Kanavos
                    _deleteAgent.Post(action);
288 0b346191 Panagiotis Kanavos
                }
289 0b346191 Panagiotis Kanavos
                finally
290 0b346191 Panagiotis Kanavos
                {
291 0b346191 Panagiotis Kanavos
                    if (_deleteAgent.InputCount == 0)
292 0b346191 Panagiotis Kanavos
                        _pauseAgent.Set();
293 0b346191 Panagiotis Kanavos
294 0b346191 Panagiotis Kanavos
                }
295 f3d080df Panagiotis Kanavos
            }
296 f3d080df Panagiotis Kanavos
        }
297 f3d080df Panagiotis Kanavos
298 039a89e5 Panagiotis Kanavos
        private static string GetFileKey(ObjectInfo info)
299 039a89e5 Panagiotis Kanavos
        {
300 039a89e5 Panagiotis Kanavos
            var key = String.Format("{0}/{1}/{2}", info.Account, info.Container,info.Name);
301 039a89e5 Panagiotis Kanavos
            return key;
302 039a89e5 Panagiotis Kanavos
        }
303 039a89e5 Panagiotis Kanavos
304 27361404 Panagiotis Kanavos
        private async Task SyncFiles(AccountInfo accountInfo,CloudAction action)
305 cfed7823 Panagiotis Kanavos
        {
306 c53aa229 Panagiotis Kanavos
            if (accountInfo == null)
307 c53aa229 Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
308 cfed7823 Panagiotis Kanavos
            if (action==null)
309 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("action");
310 cfed7823 Panagiotis Kanavos
            if (action.LocalFile==null)
311 cfed7823 Panagiotis Kanavos
                throw new ArgumentException("The action's local file is not specified","action");
312 cfed7823 Panagiotis Kanavos
            if (!Path.IsPathRooted(action.LocalFile.FullName))
313 cfed7823 Panagiotis Kanavos
                throw new ArgumentException("The action's local file path must be absolute","action");
314 cfed7823 Panagiotis Kanavos
            if (action.CloudFile== null)
315 cfed7823 Panagiotis Kanavos
                throw new ArgumentException("The action's cloud file is not specified", "action");
316 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
317 cfed7823 Panagiotis Kanavos
318 a64c87c8 Panagiotis Kanavos
            var localFile = action.LocalFile;
319 a64c87c8 Panagiotis Kanavos
            var cloudFile = action.CloudFile;
320 f3d080df Panagiotis Kanavos
            var downloadPath=action.LocalFile.GetProperCapitalization();
321 a27aa447 Panagiotis Kanavos
322 cfed7823 Panagiotis Kanavos
            var cloudHash = cloudFile.Hash.ToLower();
323 cfed7823 Panagiotis Kanavos
            var localHash = action.LocalHash.Value.ToLower();
324 cfed7823 Panagiotis Kanavos
            var topHash = action.TopHash.Value.ToLower();
325 cfed7823 Panagiotis Kanavos
326 cfed7823 Panagiotis Kanavos
            //Not enough to compare only the local hashes, also have to compare the tophashes
327 cfed7823 Panagiotis Kanavos
            
328 cfed7823 Panagiotis Kanavos
            //If any of the hashes match, we are done
329 cfed7823 Panagiotis Kanavos
            if ((cloudHash == localHash || cloudHash == topHash))
330 a64c87c8 Panagiotis Kanavos
            {
331 cfed7823 Panagiotis Kanavos
                Log.InfoFormat("Skipping {0}, hashes match",downloadPath);
332 cfed7823 Panagiotis Kanavos
                return;
333 cfed7823 Panagiotis Kanavos
            }
334 cfed7823 Panagiotis Kanavos
335 cfed7823 Panagiotis Kanavos
            //The hashes DON'T match. We need to sync
336 cfed7823 Panagiotis Kanavos
            var lastLocalTime = localFile.LastWriteTime;
337 cfed7823 Panagiotis Kanavos
            var lastUpTime = cloudFile.Last_Modified;
338 cfed7823 Panagiotis Kanavos
            
339 cfed7823 Panagiotis Kanavos
            //If the local file is newer upload it
340 cfed7823 Panagiotis Kanavos
            if (lastUpTime <= lastLocalTime)
341 cfed7823 Panagiotis Kanavos
            {
342 cfed7823 Panagiotis Kanavos
                //It probably means it was changed while the app was down                        
343 1bfc38f1 Panagiotis Kanavos
                UploadCloudFile(action);
344 cfed7823 Panagiotis Kanavos
            }
345 cfed7823 Panagiotis Kanavos
            else
346 cfed7823 Panagiotis Kanavos
            {
347 cfed7823 Panagiotis Kanavos
                //It the cloud file has a later date, it was modified by another user or computer.
348 cfed7823 Panagiotis Kanavos
                //We need to check the local file's status                
349 cfed7823 Panagiotis Kanavos
                var status = StatusKeeper.GetFileStatus(downloadPath);
350 cfed7823 Panagiotis Kanavos
                switch (status)
351 a64c87c8 Panagiotis Kanavos
                {
352 cfed7823 Panagiotis Kanavos
                    case FileStatus.Unchanged:                        
353 cfed7823 Panagiotis Kanavos
                        //If the local file's status is Unchanged, we can go on and download the newer cloud file
354 27361404 Panagiotis Kanavos
                        await DownloadCloudFile(accountInfo,cloudFile,downloadPath);
355 a64c87c8 Panagiotis Kanavos
                        break;
356 cfed7823 Panagiotis Kanavos
                    case FileStatus.Modified:
357 cfed7823 Panagiotis Kanavos
                        //If the local file is Modified, we may have a conflict. In this case we should mark the file as Conflict
358 cfed7823 Panagiotis Kanavos
                        //We can't ensure that a file modified online since the last time will appear as Modified, unless we 
359 cfed7823 Panagiotis Kanavos
                        //index all files before we start listening.                       
360 cfed7823 Panagiotis Kanavos
                    case FileStatus.Created:
361 cfed7823 Panagiotis Kanavos
                        //If the local file is Created, it means that the local and cloud files aren't related,
362 cfed7823 Panagiotis Kanavos
                        // yet they have the same name.
363 cfed7823 Panagiotis Kanavos
364 cfed7823 Panagiotis Kanavos
                        //In both cases we must mark the file as in conflict
365 cfed7823 Panagiotis Kanavos
                        ReportConflict(downloadPath);
366 a64c87c8 Panagiotis Kanavos
                        break;
367 cfed7823 Panagiotis Kanavos
                    default:
368 cfed7823 Panagiotis Kanavos
                        //Other cases should never occur. Mark them as Conflict as well but log a warning
369 cfed7823 Panagiotis Kanavos
                        ReportConflict(downloadPath);
370 cfed7823 Panagiotis Kanavos
                        Log.WarnFormat("Unexcepted status {0} for file {1}->{2}", status,
371 cfed7823 Panagiotis Kanavos
                                       downloadPath, action.CloudFile.Name);
372 a64c87c8 Panagiotis Kanavos
                        break;
373 a64c87c8 Panagiotis Kanavos
                }
374 cfed7823 Panagiotis Kanavos
            }
375 cfed7823 Panagiotis Kanavos
        }
376 cfed7823 Panagiotis Kanavos
377 cfed7823 Panagiotis Kanavos
        private void ReportConflict(string downloadPath)
378 cfed7823 Panagiotis Kanavos
        {
379 cfed7823 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(downloadPath))
380 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("downloadPath");
381 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
382 cfed7823 Panagiotis Kanavos
383 cfed7823 Panagiotis Kanavos
            StatusKeeper.SetFileOverlayStatus(downloadPath, FileOverlayStatus.Conflict);
384 e5b65606 Panagiotis Kanavos
            StatusKeeper.SetPithosStatus(PithosStatus.HasConflicts);
385 cfed7823 Panagiotis Kanavos
            var message = String.Format("Conflict detected for file {0}", downloadPath);
386 cfed7823 Panagiotis Kanavos
            Log.Warn(message);
387 cfed7823 Panagiotis Kanavos
            StatusNotification.NotifyChange(message, TraceLevel.Warning);
388 cfed7823 Panagiotis Kanavos
        }
389 cfed7823 Panagiotis Kanavos
390 9c4346c9 Panagiotis Kanavos
        public void Post(CloudAction cloudAction)
391 9c4346c9 Panagiotis Kanavos
        {
392 9c4346c9 Panagiotis Kanavos
            if (cloudAction == null)
393 9c4346c9 Panagiotis Kanavos
                throw new ArgumentNullException("cloudAction");
394 c53aa229 Panagiotis Kanavos
            if (cloudAction.AccountInfo==null)
395 c53aa229 Panagiotis Kanavos
                throw new ArgumentException("The CloudAction.AccountInfo is empty","cloudAction");
396 9c4346c9 Panagiotis Kanavos
            Contract.EndContractBlock();
397 f3d080df Panagiotis Kanavos
398 9d6d2f6e Panagiotis Kanavos
            _pauseAgent.Wait();
399 9d6d2f6e Panagiotis Kanavos
400 a27aa447 Panagiotis Kanavos
            //If the action targets a local file, add a treehash calculation
401 e81dd1f6 Panagiotis Kanavos
            if (!(cloudAction is CloudDeleteAction) && cloudAction.LocalFile as FileInfo != null)
402 a27aa447 Panagiotis Kanavos
            {
403 c53aa229 Panagiotis Kanavos
                var accountInfo = cloudAction.AccountInfo;
404 4f6d51d4 Panagiotis Kanavos
                var localFile = (FileInfo) cloudAction.LocalFile;
405 4f6d51d4 Panagiotis Kanavos
                if (localFile.Length > accountInfo.BlockSize)
406 4f6d51d4 Panagiotis Kanavos
                    cloudAction.TopHash =
407 4f6d51d4 Panagiotis Kanavos
                        new Lazy<string>(() => Signature.CalculateTreeHashAsync(localFile,
408 4f6d51d4 Panagiotis Kanavos
                                                                                accountInfo.BlockSize,
409 4f6d51d4 Panagiotis Kanavos
                                                                                accountInfo.BlockHash).Result
410 4f6d51d4 Panagiotis Kanavos
                                                    .TopHash.ToHashString());
411 4f6d51d4 Panagiotis Kanavos
                else
412 cfed7823 Panagiotis Kanavos
                {
413 4f6d51d4 Panagiotis Kanavos
                    cloudAction.TopHash = new Lazy<string>(() => cloudAction.LocalHash.Value);
414 cfed7823 Panagiotis Kanavos
                }
415 4f6d51d4 Panagiotis Kanavos
            }
416 4f6d51d4 Panagiotis Kanavos
            else
417 4f6d51d4 Panagiotis Kanavos
            {
418 4f6d51d4 Panagiotis Kanavos
                //The hash for a directory is the empty string
419 4f6d51d4 Panagiotis Kanavos
                cloudAction.TopHash = new Lazy<string>(() => String.Empty);
420 a27aa447 Panagiotis Kanavos
            }
421 f3d080df Panagiotis Kanavos
            
422 f3d080df Panagiotis Kanavos
            if (cloudAction is CloudDeleteAction)
423 f3d080df Panagiotis Kanavos
                _deleteAgent.Post((CloudDeleteAction)cloudAction);
424 f3d080df Panagiotis Kanavos
            else
425 f3d080df Panagiotis Kanavos
                _agent.Post(cloudAction);
426 9c4346c9 Panagiotis Kanavos
        }
427 9c4346c9 Panagiotis Kanavos
428 5e31048f Panagiotis Kanavos
       /* class ObjectInfoByNameComparer:IEqualityComparer<ObjectInfo>
429 5ce54458 Panagiotis Kanavos
        {
430 5ce54458 Panagiotis Kanavos
            public bool Equals(ObjectInfo x, ObjectInfo y)
431 5ce54458 Panagiotis Kanavos
            {
432 5ce54458 Panagiotis Kanavos
                return x.Name.Equals(y.Name,StringComparison.InvariantCultureIgnoreCase);
433 5ce54458 Panagiotis Kanavos
            }
434 5ce54458 Panagiotis Kanavos
435 5ce54458 Panagiotis Kanavos
            public int GetHashCode(ObjectInfo obj)
436 5ce54458 Panagiotis Kanavos
            {
437 5ce54458 Panagiotis Kanavos
                return obj.Name.ToLower().GetHashCode();
438 5ce54458 Panagiotis Kanavos
            }
439 5e31048f Panagiotis Kanavos
        }*/
440 5ce54458 Panagiotis Kanavos
441 29a6b387 Panagiotis Kanavos
        public void SynchNow()
442 29a6b387 Panagiotis Kanavos
        {             
443 29a6b387 Panagiotis Kanavos
            if (_tcs!=null)
444 29a6b387 Panagiotis Kanavos
                _tcs.SetResult(true);
445 29a6b387 Panagiotis Kanavos
            else
446 29a6b387 Panagiotis Kanavos
            {
447 29a6b387 Panagiotis Kanavos
                //TODO: This may be OK for testing purposes, but we have no guarantee that it will
448 29a6b387 Panagiotis Kanavos
                //work properly in production
449 29a6b387 Panagiotis Kanavos
                PollRemoteFiles(repeat:false);
450 29a6b387 Panagiotis Kanavos
            }
451 29a6b387 Panagiotis Kanavos
        }
452 c53aa229 Panagiotis Kanavos
453 a64c87c8 Panagiotis Kanavos
        //Remote files are polled periodically. Any changes are processed
454 29a6b387 Panagiotis Kanavos
        public async Task PollRemoteFiles(DateTime? since = null,bool repeat=true)
455 29a6b387 Panagiotis Kanavos
        {
456 e5b65606 Panagiotis Kanavos
            StatusNotification.Notify(new Notification{Title="Polling Pithos"});
457 e5b65606 Panagiotis Kanavos
            StatusKeeper.SetPithosStatus(PithosStatus.Syncing);
458 73cdd135 Panagiotis Kanavos
459 73cdd135 Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Retrieve Remote"].Push("All accounts"))
460 c53aa229 Panagiotis Kanavos
            {
461 025046f1 Panagiotis Kanavos
                //If this poll fails, we will retry with the same since value
462 025046f1 Panagiotis Kanavos
                var nextSince = since;
463 73cdd135 Panagiotis Kanavos
                try
464 c53aa229 Panagiotis Kanavos
                {
465 c53aa229 Panagiotis Kanavos
                    //Next time we will check for all changes since the current check minus 1 second
466 c53aa229 Panagiotis Kanavos
                    //This is done to ensure there are no discrepancies due to clock differences
467 025046f1 Panagiotis Kanavos
                    DateTime current = DateTime.Now.AddSeconds(-1);
468 c53aa229 Panagiotis Kanavos
469 73cdd135 Panagiotis Kanavos
                    var tasks = from accountInfo in _accounts
470 73cdd135 Panagiotis Kanavos
                                select ProcessAccountFiles(accountInfo, since);
471 73cdd135 Panagiotis Kanavos
472 4f6d51d4 Panagiotis Kanavos
                    await TaskEx.WhenAll(tasks.ToList());
473 025046f1 Panagiotis Kanavos
                                        
474 540b8cf8 Panagiotis Kanavos
                    _firstPoll = false;
475 025046f1 Panagiotis Kanavos
                    //Reschedule the poll with the current timestamp as a "since" value
476 29a6b387 Panagiotis Kanavos
                    if (repeat)
477 025046f1 Panagiotis Kanavos
                        nextSince = current;
478 025046f1 Panagiotis Kanavos
                    else
479 025046f1 Panagiotis Kanavos
                        return;
480 c53aa229 Panagiotis Kanavos
                }
481 73cdd135 Panagiotis Kanavos
                catch (Exception ex)
482 73cdd135 Panagiotis Kanavos
                {
483 73cdd135 Panagiotis Kanavos
                    Log.ErrorFormat("Error while processing accounts\r\n{0}",ex);
484 025046f1 Panagiotis Kanavos
                    //In case of failure retry with the same "since" value
485 73cdd135 Panagiotis Kanavos
                }
486 025046f1 Panagiotis Kanavos
487 025046f1 Panagiotis Kanavos
                //Wait for the polling interval to pass or the Manual flat to be toggled
488 025046f1 Panagiotis Kanavos
                nextSince = await WaitForScheduledOrManualPoll(nextSince);
489 025046f1 Panagiotis Kanavos
490 025046f1 Panagiotis Kanavos
                PollRemoteFiles(nextSince);
491 73cdd135 Panagiotis Kanavos
492 73cdd135 Panagiotis Kanavos
            }
493 c53aa229 Panagiotis Kanavos
        }
494 c53aa229 Panagiotis Kanavos
495 025046f1 Panagiotis Kanavos
        private async Task<DateTime?> WaitForScheduledOrManualPoll(DateTime? since)
496 025046f1 Panagiotis Kanavos
        {            
497 025046f1 Panagiotis Kanavos
            _tcs = new TaskCompletionSource<bool>();
498 025046f1 Panagiotis Kanavos
            var wait = TaskEx.Delay(TimeSpan.FromSeconds(Settings.PollingInterval), _agent.CancellationToken);
499 025046f1 Panagiotis Kanavos
            var signaledTask = await TaskEx.WhenAny(_tcs.Task, wait);
500 025046f1 Panagiotis Kanavos
            //If polling is signalled by SynchNow, ignore the since tag
501 025046f1 Panagiotis Kanavos
            if (signaledTask is Task<bool>)
502 025046f1 Panagiotis Kanavos
                return null;
503 025046f1 Panagiotis Kanavos
            return since;
504 025046f1 Panagiotis Kanavos
        }
505 025046f1 Panagiotis Kanavos
506 73cdd135 Panagiotis Kanavos
        public async Task ProcessAccountFiles(AccountInfo accountInfo,DateTime? since=null)
507 a64c87c8 Panagiotis Kanavos
        {   
508 c53aa229 Panagiotis Kanavos
            if (accountInfo==null)
509 c53aa229 Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
510 c53aa229 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(accountInfo.AccountPath))
511 c53aa229 Panagiotis Kanavos
                throw new ArgumentException("The AccountInfo.AccountPath is empty","accountInfo");
512 a64c87c8 Panagiotis Kanavos
            Contract.EndContractBlock();
513 9c4346c9 Panagiotis Kanavos
514 e5b65606 Panagiotis Kanavos
            StatusNotification.Notify(new Notification{Title=String.Format("Polling {0}",accountInfo.UserName)});
515 e5b65606 Panagiotis Kanavos
516 c53aa229 Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Retrieve Remote"].Push(accountInfo.UserName))
517 cfed7823 Panagiotis Kanavos
            {
518 c53aa229 Panagiotis Kanavos
                Log.Info("Scheduled");
519 c53aa229 Panagiotis Kanavos
                var client=new CloudFilesClient(accountInfo);
520 0af3141d Panagiotis Kanavos
521 5750d7cc Panagiotis Kanavos
                var containers = client.ListContainers(accountInfo.UserName);
522 5750d7cc Panagiotis Kanavos
                
523 5750d7cc Panagiotis Kanavos
                CreateContainerFolders(accountInfo, containers);
524 5750d7cc Panagiotis Kanavos
525 73cdd135 Panagiotis Kanavos
                try
526 73cdd135 Panagiotis Kanavos
                {
527 9d6d2f6e Panagiotis Kanavos
                    _pauseAgent.Wait();
528 540b8cf8 Panagiotis Kanavos
                    //Get the poll time now. We may miss some deletions but it's better to keep a file that was deleted
529 540b8cf8 Panagiotis Kanavos
                    //than delete a file that was created while we were executing the poll
530 540b8cf8 Panagiotis Kanavos
                    var pollTime = DateTime.Now;
531 540b8cf8 Panagiotis Kanavos
                    
532 73cdd135 Panagiotis Kanavos
                    //Get the list of server objects changed since the last check
533 73cdd135 Panagiotis Kanavos
                    //The name of the container is passed as state in order to create a dictionary of tasks in a subsequent step
534 73cdd135 Panagiotis Kanavos
                    var listObjects = from container in containers
535 73cdd135 Panagiotis Kanavos
                                      select  Task<IList<ObjectInfo>>.Factory.StartNew(_ =>
536 73cdd135 Panagiotis Kanavos
                                            client.ListObjects(accountInfo.UserName,container.Name, since),container.Name);
537 0af3141d Panagiotis Kanavos
538 0af3141d Panagiotis Kanavos
539 73cdd135 Panagiotis Kanavos
                    var listTasks = await Task.Factory.WhenAll(listObjects.ToArray());
540 9c4346c9 Panagiotis Kanavos
541 5120f3cb Panagiotis Kanavos
                    using (log4net.ThreadContext.Stacks["SCHEDULE"].Push("Process Results"))
542 cfed7823 Panagiotis Kanavos
                    {
543 73cdd135 Panagiotis Kanavos
                        var dict = listTasks.ToDictionary(t => t.AsyncState);
544 73cdd135 Panagiotis Kanavos
545 ec6f3895 Panagiotis Kanavos
                        //Get all non-trash objects. Remember, the container name is stored in AsyncState
546 73cdd135 Panagiotis Kanavos
                        var remoteObjects = from objectList in listTasks
547 73cdd135 Panagiotis Kanavos
                                            where (string) objectList.AsyncState != "trash"
548 ec6f3895 Panagiotis Kanavos
                                            from obj in objectList.Result
549 ec6f3895 Panagiotis Kanavos
                                            select obj;
550 73cdd135 Panagiotis Kanavos
551 540b8cf8 Panagiotis Kanavos
                        //TODO: Change the way deleted objects are detected.
552 540b8cf8 Panagiotis Kanavos
                        //The list operation returns all existing objects so we could detect deleted remote objects
553 540b8cf8 Panagiotis Kanavos
                        //by detecting objects that exist only locally. There are several cases where this is NOT the case:
554 540b8cf8 Panagiotis Kanavos
                        //1.    The first time the application runs, as there may be files that were added while 
555 540b8cf8 Panagiotis Kanavos
                        //      the application was down.
556 540b8cf8 Panagiotis Kanavos
                        //2.    An object that is currently being uploaded will not appear in the remote list
557 540b8cf8 Panagiotis Kanavos
                        //      until the upload finishes.
558 540b8cf8 Panagiotis Kanavos
                        //      SOLUTION 1: Check the upload/download queue for the file
559 540b8cf8 Panagiotis Kanavos
                        //      SOLUTION 2: Check the SQLite states for the file's entry. If it is being uploaded, 
560 540b8cf8 Panagiotis Kanavos
                        //          or its last modification was after the current poll, don't delete it. This way we don't
561 540b8cf8 Panagiotis Kanavos
                        //          delete objects whose upload finished too late to be included in the list.
562 540b8cf8 Panagiotis Kanavos
                        //We need to detect and protect against such situations
563 540b8cf8 Panagiotis Kanavos
                        //TODO: Does FileState have a LastModification field?
564 540b8cf8 Panagiotis Kanavos
                        //TODO: How do we update the LastModification field? Do we need to add SQLite triggers?
565 540b8cf8 Panagiotis Kanavos
                        //      Do we need to use a proper SQLite schema?
566 540b8cf8 Panagiotis Kanavos
                        //      We can create a trigger with 
567 540b8cf8 Panagiotis Kanavos
                        // CREATE TRIGGER IF NOT EXISTS update_last_modified UPDATE ON FileState FOR EACH ROW
568 540b8cf8 Panagiotis Kanavos
                        //  BEGIN
569 540b8cf8 Panagiotis Kanavos
                        //      UPDATE FileState SET LastModification=datetime('now')  WHERE Id=old.Id;
570 540b8cf8 Panagiotis Kanavos
                        //  END;
571 540b8cf8 Panagiotis Kanavos
                        //
572 540b8cf8 Panagiotis Kanavos
                        //NOTE: Some files may have been deleted remotely while the application was down. 
573 540b8cf8 Panagiotis Kanavos
                        //  We DO have to delete those files. Checking the trash makes it easy to detect them,
574 540b8cf8 Panagiotis Kanavos
                        //  Otherwise, we can't be really sure whether we need to upload or delete 
575 540b8cf8 Panagiotis Kanavos
                        //  the local-only files.
576 540b8cf8 Panagiotis Kanavos
                        //  SOLUTION 1: Ask the user when such a local-only file is detected during the first poll.
577 540b8cf8 Panagiotis Kanavos
                        //  SOLUTION 2: Mark conflict and ask the user as in #1
578 540b8cf8 Panagiotis Kanavos
579 ec6f3895 Panagiotis Kanavos
                        var trashObjects = dict["trash"].Result;
580 ec6f3895 Panagiotis Kanavos
                        //var sharedObjects = ((Task<IList<ObjectInfo>>) task.Result[2]).Result;
581 cfed7823 Panagiotis Kanavos
582 cfed7823 Panagiotis Kanavos
                        //Items with the same name, hash may be both in the container and the trash
583 cfed7823 Panagiotis Kanavos
                        //Don't delete items that exist in the container
584 cfed7823 Panagiotis Kanavos
                        var realTrash = from trash in trashObjects
585 73cdd135 Panagiotis Kanavos
                                        where
586 73cdd135 Panagiotis Kanavos
                                            !remoteObjects.Any(
587 73cdd135 Panagiotis Kanavos
                                                info => info.Name == trash.Name && info.Hash == trash.Hash)
588 cfed7823 Panagiotis Kanavos
                                        select trash;
589 540b8cf8 Panagiotis Kanavos
                        ProcessTrashedFiles(accountInfo, realTrash);
590 cfed7823 Panagiotis Kanavos
591 cfed7823 Panagiotis Kanavos
592 133f83c2 Panagiotis Kanavos
                        var cleanRemotes = (from info in remoteObjects
593 73cdd135 Panagiotis Kanavos
                                     //.Union(sharedObjects)
594 cfed7823 Panagiotis Kanavos
                                     let name = info.Name
595 cfed7823 Panagiotis Kanavos
                                     where !name.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase) &&
596 73cdd135 Panagiotis Kanavos
                                           !name.StartsWith(FolderConstants.CacheFolder + "/",
597 73cdd135 Panagiotis Kanavos
                                                            StringComparison.InvariantCultureIgnoreCase)
598 133f83c2 Panagiotis Kanavos
                                     select info).ToList();
599 cfed7823 Panagiotis Kanavos
600 540b8cf8 Panagiotis Kanavos
601 540b8cf8 Panagiotis Kanavos
602 133f83c2 Panagiotis Kanavos
                        ProcessDeletedFiles(accountInfo, cleanRemotes, pollTime);
603 540b8cf8 Panagiotis Kanavos
604 cfed7823 Panagiotis Kanavos
                        //Create a list of actions from the remote files
605 540b8cf8 Panagiotis Kanavos
                        var allActions = ObjectsToActions(accountInfo, cleanRemotes);
606 540b8cf8 Panagiotis Kanavos
607 133f83c2 Panagiotis Kanavos
                        
608 540b8cf8 Panagiotis Kanavos
                        //var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);
609 73cdd135 Panagiotis Kanavos
610 cfed7823 Panagiotis Kanavos
                        //And remove those that are already being processed by the agent
611 cfed7823 Panagiotis Kanavos
                        var distinctActions = allActions
612 cfed7823 Panagiotis Kanavos
                            .Except(_agent.GetEnumerable(), new PithosMonitor.LocalFileComparer())
613 cfed7823 Panagiotis Kanavos
                            .ToList();
614 cfed7823 Panagiotis Kanavos
615 e5b65606 Panagiotis Kanavos
                        if (distinctActions.Any())
616 e5b65606 Panagiotis Kanavos
                        {
617 e5b65606 Panagiotis Kanavos
                            StatusNotification.Notify(new Notification {Title = "Changes Detected",Message=String.Format("{0} files were modified",distinctActions.Count)});
618 e5b65606 Panagiotis Kanavos
                        }
619 cfed7823 Panagiotis Kanavos
                        //Queue all the actions
620 cfed7823 Panagiotis Kanavos
                        foreach (var message in distinctActions)
621 cfed7823 Panagiotis Kanavos
                        {
622 cfed7823 Panagiotis Kanavos
                            Post(message);
623 cfed7823 Panagiotis Kanavos
                        }
624 9c4346c9 Panagiotis Kanavos
625 73cdd135 Panagiotis Kanavos
                        Log.Info("[LISTENER] End Processing");
626 cfed7823 Panagiotis Kanavos
                    }
627 73cdd135 Panagiotis Kanavos
                }
628 73cdd135 Panagiotis Kanavos
                catch (Exception ex)
629 73cdd135 Panagiotis Kanavos
                {
630 73cdd135 Panagiotis Kanavos
                    Log.ErrorFormat("[FAIL] ListObjects for{0} in ProcessRemoteFiles with {1}", accountInfo.UserName, ex);
631 73cdd135 Panagiotis Kanavos
                    return;
632 73cdd135 Panagiotis Kanavos
                }
633 73cdd135 Panagiotis Kanavos
634 73cdd135 Panagiotis Kanavos
                Log.Info("[LISTENER] Finished");
635 cfed7823 Panagiotis Kanavos
636 cfed7823 Panagiotis Kanavos
            }
637 cfed7823 Panagiotis Kanavos
        }
638 9c4346c9 Panagiotis Kanavos
639 540b8cf8 Panagiotis Kanavos
        /// <summary>
640 540b8cf8 Panagiotis Kanavos
        /// Deletes local files that are not found in the list of cloud files
641 540b8cf8 Panagiotis Kanavos
        /// </summary>
642 540b8cf8 Panagiotis Kanavos
        /// <param name="accountInfo"></param>
643 540b8cf8 Panagiotis Kanavos
        /// <param name="cloudFiles"></param>
644 540b8cf8 Panagiotis Kanavos
        /// <param name="pollTime"></param>
645 540b8cf8 Panagiotis Kanavos
        private void ProcessDeletedFiles(AccountInfo accountInfo, IEnumerable<ObjectInfo> cloudFiles, DateTime pollTime)
646 540b8cf8 Panagiotis Kanavos
        {
647 540b8cf8 Panagiotis Kanavos
            if (accountInfo == null)
648 540b8cf8 Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
649 540b8cf8 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(accountInfo.AccountPath))
650 540b8cf8 Panagiotis Kanavos
                throw new ArgumentException("The AccountInfo.AccountPath is empty", "accountInfo");
651 540b8cf8 Panagiotis Kanavos
            if (cloudFiles == null)
652 540b8cf8 Panagiotis Kanavos
                throw new ArgumentNullException("cloudFiles");
653 540b8cf8 Panagiotis Kanavos
            Contract.EndContractBlock();
654 540b8cf8 Panagiotis Kanavos
655 9c6d3193 Panagiotis Kanavos
            //Check the Modified date to ensure that were just created and haven't been uploaded yet
656 9c6d3193 Panagiotis Kanavos
            //NOTE: The NHibernate LINQ provider doesn't support custom functions so we need to break the query 
657 9c6d3193 Panagiotis Kanavos
            //in two steps
658 9c6d3193 Panagiotis Kanavos
            //NOTE: DON'T return files that are already in conflict. The first poll would mark them as 
659 9c6d3193 Panagiotis Kanavos
            //"In Conflict" but subsequent polls would delete them
660 025046f1 Panagiotis Kanavos
/*            var t=FileState.Find(new Guid("{cd664c9a-5f17-47c9-b27f-3bcbcb0595ff}"));
661 025046f1 Panagiotis Kanavos
662 025046f1 Panagiotis Kanavos
            var d0 = FileState.Queryable
663 025046f1 Panagiotis Kanavos
                .Where(state => 
664 025046f1 Panagiotis Kanavos
                            state.FilePath.StartsWith(accountInfo.AccountPath)).ToList();
665 025046f1 Panagiotis Kanavos
            
666 025046f1 Panagiotis Kanavos
            var d1 = FileState.Queryable
667 025046f1 Panagiotis Kanavos
                .Where(state => state.Modified <= pollTime).ToList();
668 025046f1 Panagiotis Kanavos
            var d2= FileState.Queryable
669 025046f1 Panagiotis Kanavos
                .Where(state => state.Modified <= pollTime
670 025046f1 Panagiotis Kanavos
                            &&
671 025046f1 Panagiotis Kanavos
                            state.FilePath.StartsWith(accountInfo.AccountPath)).ToList();*/
672 025046f1 Panagiotis Kanavos
673 025046f1 Panagiotis Kanavos
            var deleteCandidates = FileState.Queryable
674 025046f1 Panagiotis Kanavos
                .Where(state => state.Modified <= pollTime
675 025046f1 Panagiotis Kanavos
                            &&
676 025046f1 Panagiotis Kanavos
                            state.FilePath.StartsWith(accountInfo.AccountPath)
677 025046f1 Panagiotis Kanavos
                            && state.FileStatus != FileStatus.Conflict).ToList();
678 025046f1 Panagiotis Kanavos
/*
679 9c6d3193 Panagiotis Kanavos
            var deleteCandidates = (from state in FileState.Queryable
680 9c6d3193 Panagiotis Kanavos
                                   where 
681 9c6d3193 Panagiotis Kanavos
                                        state.Modified <= pollTime 
682 9c6d3193 Panagiotis Kanavos
                                        && state.FilePath.StartsWith(accountInfo.AccountPath)
683 9c6d3193 Panagiotis Kanavos
                                        && state.FileStatus != FileStatus.Conflict
684 9c6d3193 Panagiotis Kanavos
                                   select state).ToList();
685 025046f1 Panagiotis Kanavos
*/
686 9c6d3193 Panagiotis Kanavos
687 9c6d3193 Panagiotis Kanavos
            var filesToDelete = (from deleteCandidate in deleteCandidates 
688 9c6d3193 Panagiotis Kanavos
                         let localFile = FileInfoExtensions.FromPath(deleteCandidate.FilePath) 
689 9c6d3193 Panagiotis Kanavos
                         let relativeFilePath = localFile.AsRelativeTo(accountInfo.AccountPath) 
690 9c6d3193 Panagiotis Kanavos
                         where !cloudFiles.Any(r => Path.Combine(r.Container, r.Name) == relativeFilePath) 
691 9c6d3193 Panagiotis Kanavos
                         select localFile).ToList();
692 9c6d3193 Panagiotis Kanavos
693 9c6d3193 Panagiotis Kanavos
            //On the first run
694 9c6d3193 Panagiotis Kanavos
            if (_firstPoll)
695 540b8cf8 Panagiotis Kanavos
            {
696 9c6d3193 Panagiotis Kanavos
                //Set the status of missing files to Conflict
697 9c6d3193 Panagiotis Kanavos
                foreach (var item in filesToDelete)
698 97f51ab0 Panagiotis Kanavos
                {
699 9c6d3193 Panagiotis Kanavos
                    StatusKeeper.SetFileState(item.FullName, FileStatus.Conflict, FileOverlayStatus.Deleted);
700 97f51ab0 Panagiotis Kanavos
                }
701 e5b65606 Panagiotis Kanavos
                StatusKeeper.SetPithosStatus(PithosStatus.HasConflicts);
702 9c6d3193 Panagiotis Kanavos
                StatusNotification.NotifyConflicts(filesToDelete, String.Format("{0} local files are missing from Pithos, possibly because they were deleted",filesToDelete.Count));
703 540b8cf8 Panagiotis Kanavos
            }
704 9c6d3193 Panagiotis Kanavos
            else
705 9c6d3193 Panagiotis Kanavos
            {
706 9c6d3193 Panagiotis Kanavos
                foreach (var item in filesToDelete)
707 9c6d3193 Panagiotis Kanavos
                {
708 9c6d3193 Panagiotis Kanavos
                    item.Delete();
709 9c6d3193 Panagiotis Kanavos
                    StatusKeeper.ClearFileStatus(item.FullName);
710 9c6d3193 Panagiotis Kanavos
                }
711 9c6d3193 Panagiotis Kanavos
                StatusNotification.NotifyForFiles(filesToDelete, String.Format("{0} files were deleted",filesToDelete.Count),TraceLevel.Info);
712 9c6d3193 Panagiotis Kanavos
            }
713 9c6d3193 Panagiotis Kanavos
714 540b8cf8 Panagiotis Kanavos
        }
715 540b8cf8 Panagiotis Kanavos
716 4f6d51d4 Panagiotis Kanavos
        private static void CreateContainerFolders(AccountInfo accountInfo, IEnumerable<ContainerInfo> containers)
717 5750d7cc Panagiotis Kanavos
        {
718 5750d7cc Panagiotis Kanavos
            var containerPaths = from container in containers
719 5750d7cc Panagiotis Kanavos
                                 let containerPath = Path.Combine(accountInfo.AccountPath, container.Name)
720 5750d7cc Panagiotis Kanavos
                                 where container.Name != FolderConstants.TrashContainer && !Directory.Exists(containerPath)
721 5750d7cc Panagiotis Kanavos
                                 select containerPath;
722 5750d7cc Panagiotis Kanavos
723 5750d7cc Panagiotis Kanavos
            foreach (var path in containerPaths)
724 5750d7cc Panagiotis Kanavos
            {
725 5750d7cc Panagiotis Kanavos
                Directory.CreateDirectory(path);
726 5750d7cc Panagiotis Kanavos
            }
727 5750d7cc Panagiotis Kanavos
        }
728 5750d7cc Panagiotis Kanavos
729 cfed7823 Panagiotis Kanavos
        //Creates an appropriate action for each server file
730 c53aa229 Panagiotis Kanavos
        private IEnumerable<CloudAction> ObjectsToActions(AccountInfo accountInfo,IEnumerable<ObjectInfo> remote)
731 cfed7823 Panagiotis Kanavos
        {
732 cfed7823 Panagiotis Kanavos
            if (remote==null)
733 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException();
734 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
735 c28a075a Panagiotis Kanavos
            var fileAgent = GetFileAgent(accountInfo);
736 9c4346c9 Panagiotis Kanavos
737 cfed7823 Panagiotis Kanavos
            //In order to avoid multiple iterations over the files, we iterate only once
738 cfed7823 Panagiotis Kanavos
            //over the remote files
739 cfed7823 Panagiotis Kanavos
            foreach (var objectInfo in remote)
740 9c4346c9 Panagiotis Kanavos
            {
741 c53aa229 Panagiotis Kanavos
                var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);
742 cfed7823 Panagiotis Kanavos
                //and remove any matching objects from the list, adding them to the commonObjects list
743 c28a075a Panagiotis Kanavos
                
744 c28a075a Panagiotis Kanavos
                if (fileAgent.Exists(relativePath))
745 9c4346c9 Panagiotis Kanavos
                {
746 4f6d51d4 Panagiotis Kanavos
                    //If a directory object already exists, we don't need to perform any other action                    
747 f3d080df Panagiotis Kanavos
                    var localFile = fileAgent.GetFileSystemInfo(relativePath);
748 4f6d51d4 Panagiotis Kanavos
                    if (objectInfo.Content_Type == @"application/directory" && localFile is DirectoryInfo)
749 4f6d51d4 Panagiotis Kanavos
                        continue;
750 e81dd1f6 Panagiotis Kanavos
                    using (new SessionScope(FlushAction.Never))
751 e81dd1f6 Panagiotis Kanavos
                    {
752 039a89e5 Panagiotis Kanavos
                        var state =  StatusKeeper.GetStateByFilePath(localFile.FullName);
753 039a89e5 Panagiotis Kanavos
                        //FileState.FindByFilePath(localFile.FullName);
754 e81dd1f6 Panagiotis Kanavos
                        //Common files should be checked on a per-case basis to detect differences, which is newer
755 cfed7823 Panagiotis Kanavos
756 e81dd1f6 Panagiotis Kanavos
                        yield return new CloudAction(accountInfo, CloudActionType.MustSynch,
757 e81dd1f6 Panagiotis Kanavos
                                                     localFile, objectInfo, state, accountInfo.BlockSize,
758 e81dd1f6 Panagiotis Kanavos
                                                     accountInfo.BlockHash);
759 e81dd1f6 Panagiotis Kanavos
                    }
760 9c4346c9 Panagiotis Kanavos
                }
761 9c4346c9 Panagiotis Kanavos
                else
762 9c4346c9 Panagiotis Kanavos
                {
763 cfed7823 Panagiotis Kanavos
                    //If there is no match we add them to the localFiles list
764 cfed7823 Panagiotis Kanavos
                    //but only if the file is not marked for deletion
765 c53aa229 Panagiotis Kanavos
                    var targetFile = Path.Combine(accountInfo.AccountPath, relativePath);
766 cfed7823 Panagiotis Kanavos
                    var fileStatus = StatusKeeper.GetFileStatus(targetFile);
767 cfed7823 Panagiotis Kanavos
                    if (fileStatus != FileStatus.Deleted)
768 cfed7823 Panagiotis Kanavos
                    {
769 cfed7823 Panagiotis Kanavos
                        //Remote files should be downloaded
770 c53aa229 Panagiotis Kanavos
                        yield return new CloudDownloadAction(accountInfo,objectInfo);
771 cfed7823 Panagiotis Kanavos
                    }
772 9c4346c9 Panagiotis Kanavos
                }
773 cfed7823 Panagiotis Kanavos
            }            
774 9c4346c9 Panagiotis Kanavos
        }
775 9c4346c9 Panagiotis Kanavos
776 c28a075a Panagiotis Kanavos
        private static FileAgent GetFileAgent(AccountInfo accountInfo)
777 c28a075a Panagiotis Kanavos
        {
778 c28a075a Panagiotis Kanavos
            return AgentLocator<FileAgent>.Get(accountInfo.AccountPath);
779 c28a075a Panagiotis Kanavos
        }
780 c28a075a Panagiotis Kanavos
781 540b8cf8 Panagiotis Kanavos
        private void ProcessTrashedFiles(AccountInfo accountInfo,IEnumerable<ObjectInfo> trashObjects)
782 0af3141d Panagiotis Kanavos
        {
783 c28a075a Panagiotis Kanavos
            var fileAgent = GetFileAgent(accountInfo);
784 0af3141d Panagiotis Kanavos
            foreach (var trashObject in trashObjects)
785 0af3141d Panagiotis Kanavos
            {
786 692ec33b Panagiotis Kanavos
                var barePath = trashObject.RelativeUrlToFilePath(accountInfo.UserName);
787 692ec33b Panagiotis Kanavos
                //HACK: Assume only the "pithos" container is used. Must find out what happens when
788 692ec33b Panagiotis Kanavos
                //deleting a file from a different container
789 692ec33b Panagiotis Kanavos
                var relativePath = Path.Combine("pithos", barePath);
790 c28a075a Panagiotis Kanavos
                fileAgent.Delete(relativePath);                                
791 0af3141d Panagiotis Kanavos
            }
792 0af3141d Panagiotis Kanavos
        }
793 0af3141d Panagiotis Kanavos
794 9c4346c9 Panagiotis Kanavos
795 27361404 Panagiotis Kanavos
        private void RenameCloudFile(AccountInfo accountInfo,CloudMoveAction action)
796 9c4346c9 Panagiotis Kanavos
        {
797 c53aa229 Panagiotis Kanavos
            if (accountInfo==null)
798 c53aa229 Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
799 1bfc38f1 Panagiotis Kanavos
            if (action==null)
800 1bfc38f1 Panagiotis Kanavos
                throw new ArgumentNullException("action");
801 27361404 Panagiotis Kanavos
            if (action.CloudFile==null)
802 27361404 Panagiotis Kanavos
                throw new ArgumentException("CloudFile","action");
803 27361404 Panagiotis Kanavos
            if (action.LocalFile==null)
804 27361404 Panagiotis Kanavos
                throw new ArgumentException("LocalFile","action");
805 27361404 Panagiotis Kanavos
            if (action.OldLocalFile==null)
806 27361404 Panagiotis Kanavos
                throw new ArgumentException("OldLocalFile","action");
807 27361404 Panagiotis Kanavos
            if (action.OldCloudFile==null)
808 27361404 Panagiotis Kanavos
                throw new ArgumentException("OldCloudFile","action");
809 9c4346c9 Panagiotis Kanavos
            Contract.EndContractBlock();
810 27361404 Panagiotis Kanavos
            
811 f3d080df Panagiotis Kanavos
            
812 f3d080df Panagiotis Kanavos
            var newFilePath = action.LocalFile.FullName;
813 f3d080df Panagiotis Kanavos
            
814 f3d080df Panagiotis Kanavos
            //How do we handle concurrent renames and deletes/uploads/downloads?
815 f3d080df Panagiotis Kanavos
            //* A conflicting upload means that a file was renamed before it had a chance to finish uploading
816 f3d080df Panagiotis Kanavos
            //  This should never happen as the network agent executes only one action at a time
817 f3d080df Panagiotis Kanavos
            //* A conflicting download means that the file was modified on the cloud. While we can go on and complete
818 f3d080df Panagiotis Kanavos
            //  the rename, there may be a problem if the file is downloaded in blocks, as subsequent block requests for the 
819 f3d080df Panagiotis Kanavos
            //  same name will fail.
820 f3d080df Panagiotis Kanavos
            //  This should never happen as the network agent executes only one action at a time.
821 f3d080df Panagiotis Kanavos
            //* A conflicting delete can happen if the rename was followed by a delete action that didn't have the chance
822 f3d080df Panagiotis Kanavos
            //  to remove the rename from the queue.
823 f3d080df Panagiotis Kanavos
            //  We can probably ignore this case. It will result in an error which should be ignored            
824 f3d080df Panagiotis Kanavos
825 f3d080df Panagiotis Kanavos
            
826 9c4346c9 Panagiotis Kanavos
            //The local file is already renamed
827 73cdd135 Panagiotis Kanavos
            StatusKeeper.SetFileOverlayStatus(newFilePath, FileOverlayStatus.Modified);
828 9c4346c9 Panagiotis Kanavos
829 1bfc38f1 Panagiotis Kanavos
830 1bfc38f1 Panagiotis Kanavos
            var account = action.CloudFile.Account ?? accountInfo.UserName;
831 27361404 Panagiotis Kanavos
            var container = action.CloudFile.Container;
832 1bfc38f1 Panagiotis Kanavos
            
833 c53aa229 Panagiotis Kanavos
            var client = new CloudFilesClient(accountInfo);
834 f3d080df Panagiotis Kanavos
            //TODO: What code is returned when the source file doesn't exist?
835 27361404 Panagiotis Kanavos
            client.MoveObject(account, container, action.OldCloudFile.Name, container, action.CloudFile.Name);
836 9c4346c9 Panagiotis Kanavos
837 73cdd135 Panagiotis Kanavos
            StatusKeeper.SetFileStatus(newFilePath, FileStatus.Unchanged);
838 73cdd135 Panagiotis Kanavos
            StatusKeeper.SetFileOverlayStatus(newFilePath, FileOverlayStatus.Normal);
839 27361404 Panagiotis Kanavos
            NativeMethods.RaiseChangeNotification(newFilePath);
840 9c4346c9 Panagiotis Kanavos
        }
841 9c4346c9 Panagiotis Kanavos
842 5e31048f Panagiotis Kanavos
        private void DeleteCloudFile(AccountInfo accountInfo, ObjectInfo cloudFile)
843 cfed7823 Panagiotis Kanavos
        {
844 c53aa229 Panagiotis Kanavos
            if (accountInfo == null)
845 c53aa229 Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
846 1bfc38f1 Panagiotis Kanavos
            if (cloudFile==null)
847 1bfc38f1 Panagiotis Kanavos
                throw new ArgumentNullException("cloudFile");
848 cfed7823 Panagiotis Kanavos
849 ec6f3895 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(cloudFile.Container))
850 ec6f3895 Panagiotis Kanavos
                throw new ArgumentException("Invalid container", "cloudFile");
851 9c4346c9 Panagiotis Kanavos
            Contract.EndContractBlock();
852 c28a075a Panagiotis Kanavos
            
853 c28a075a Panagiotis Kanavos
            var fileAgent = GetFileAgent(accountInfo);
854 9c4346c9 Panagiotis Kanavos
855 5120f3cb Panagiotis Kanavos
            using ( log4net.ThreadContext.Stacks["DeleteCloudFile"].Push("Delete"))
856 cfed7823 Panagiotis Kanavos
            {
857 5e31048f Panagiotis Kanavos
                var fileName= cloudFile.RelativeUrlToFilePath(accountInfo.UserName);
858 f3d080df Panagiotis Kanavos
                var info = fileAgent.GetFileSystemInfo(fileName);                
859 c53aa229 Panagiotis Kanavos
                var fullPath = info.FullName.ToLower();
860 5e31048f Panagiotis Kanavos
861 73cdd135 Panagiotis Kanavos
                StatusKeeper.SetFileOverlayStatus(fullPath, FileOverlayStatus.Modified);
862 cfed7823 Panagiotis Kanavos
863 1bfc38f1 Panagiotis Kanavos
                var account = cloudFile.Account ?? accountInfo.UserName;
864 ec6f3895 Panagiotis Kanavos
                var container = cloudFile.Container ;//?? FolderConstants.PithosContainer;
865 1bfc38f1 Panagiotis Kanavos
866 c53aa229 Panagiotis Kanavos
                var client = new CloudFilesClient(accountInfo);
867 5e31048f Panagiotis Kanavos
                client.DeleteObject(account, container, cloudFile.Name);
868 a27aa447 Panagiotis Kanavos
869 73cdd135 Panagiotis Kanavos
                StatusKeeper.ClearFileStatus(fullPath);
870 cfed7823 Panagiotis Kanavos
            }
871 9c4346c9 Panagiotis Kanavos
        }
872 9c4346c9 Panagiotis Kanavos
873 5ce54458 Panagiotis Kanavos
        //Download a file.
874 f3d080df Panagiotis Kanavos
        private async Task DownloadCloudFile(AccountInfo accountInfo, ObjectInfo cloudFile , string filePath)
875 9c4346c9 Panagiotis Kanavos
        {
876 c53aa229 Panagiotis Kanavos
            if (accountInfo == null)
877 c53aa229 Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
878 d3a13891 Panagiotis Kanavos
            if (cloudFile == null)
879 d3a13891 Panagiotis Kanavos
                throw new ArgumentNullException("cloudFile");
880 1bfc38f1 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(cloudFile.Account))
881 1bfc38f1 Panagiotis Kanavos
                throw new ArgumentNullException("cloudFile");
882 1bfc38f1 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(cloudFile.Container))
883 1bfc38f1 Panagiotis Kanavos
                throw new ArgumentNullException("cloudFile");
884 f3d080df Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(filePath))
885 f3d080df Panagiotis Kanavos
                throw new ArgumentNullException("filePath");
886 f3d080df Panagiotis Kanavos
            if (!Path.IsPathRooted(filePath))
887 f3d080df Panagiotis Kanavos
                throw new ArgumentException("The filePath must be rooted", "filePath");
888 0af3141d Panagiotis Kanavos
            Contract.EndContractBlock();
889 f3d080df Panagiotis Kanavos
890 f3d080df Panagiotis Kanavos
            var localPath = Interfaces.FileInfoExtensions.GetProperFilePathCapitalization(filePath);
891 73cdd135 Panagiotis Kanavos
            var relativeUrl = new Uri(cloudFile.Name, UriKind.Relative);
892 d3a13891 Panagiotis Kanavos
893 5ce54458 Panagiotis Kanavos
            var url = relativeUrl.ToString();
894 d3a13891 Panagiotis Kanavos
            if (cloudFile.Name.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase))
895 27361404 Panagiotis Kanavos
                return;
896 9c4346c9 Panagiotis Kanavos
897 039a89e5 Panagiotis Kanavos
898 5ce54458 Panagiotis Kanavos
            //Are we already downloading or uploading the file? 
899 5ce54458 Panagiotis Kanavos
            using (var gate=NetworkGate.Acquire(localPath, NetworkOperation.Downloading))
900 5ce54458 Panagiotis Kanavos
            {
901 5ce54458 Panagiotis Kanavos
                if (gate.Failed)
902 27361404 Panagiotis Kanavos
                    return;
903 5ce54458 Panagiotis Kanavos
                //The file's hashmap will be stored in the same location with the extension .hashmap
904 77e10b4f Panagiotis Kanavos
                //var hashPath = Path.Combine(FileAgent.CachePath, relativePath + ".hashmap");
905 5ce54458 Panagiotis Kanavos
                
906 c53aa229 Panagiotis Kanavos
                var client = new CloudFilesClient(accountInfo);
907 1bfc38f1 Panagiotis Kanavos
                var account = cloudFile.Account;
908 1bfc38f1 Panagiotis Kanavos
                var container = cloudFile.Container;
909 1bfc38f1 Panagiotis Kanavos
910 73cdd135 Panagiotis Kanavos
                if (cloudFile.Content_Type == @"application/directory")
911 73cdd135 Panagiotis Kanavos
                {
912 73cdd135 Panagiotis Kanavos
                    if (!Directory.Exists(localPath))
913 73cdd135 Panagiotis Kanavos
                        Directory.CreateDirectory(localPath);
914 73cdd135 Panagiotis Kanavos
                }
915 27361404 Panagiotis Kanavos
                else
916 d3a13891 Panagiotis Kanavos
                {
917 73cdd135 Panagiotis Kanavos
                    //Retrieve the hashmap from the server
918 73cdd135 Panagiotis Kanavos
                    var serverHash = await client.GetHashMap(account, container, url);
919 73cdd135 Panagiotis Kanavos
                    //If it's a small file
920 73cdd135 Panagiotis Kanavos
                    if (serverHash.Hashes.Count == 1)
921 73cdd135 Panagiotis Kanavos
                        //Download it in one go
922 73cdd135 Panagiotis Kanavos
                        await
923 73cdd135 Panagiotis Kanavos
                            DownloadEntireFileAsync(accountInfo, client, cloudFile, relativeUrl, localPath, serverHash);
924 73cdd135 Panagiotis Kanavos
                        //Otherwise download it block by block
925 73cdd135 Panagiotis Kanavos
                    else
926 73cdd135 Panagiotis Kanavos
                        await DownloadWithBlocks(accountInfo, client, cloudFile, relativeUrl, localPath, serverHash);
927 73cdd135 Panagiotis Kanavos
928 73cdd135 Panagiotis Kanavos
                    if (cloudFile.AllowedTo == "read")
929 73cdd135 Panagiotis Kanavos
                    {
930 73cdd135 Panagiotis Kanavos
                        var attributes = File.GetAttributes(localPath);
931 73cdd135 Panagiotis Kanavos
                        File.SetAttributes(localPath, attributes | FileAttributes.ReadOnly);                        
932 73cdd135 Panagiotis Kanavos
                    }
933 d3a13891 Panagiotis Kanavos
                }
934 73cdd135 Panagiotis Kanavos
935 1bfc38f1 Panagiotis Kanavos
                //Now we can store the object's metadata without worrying about ghost status entries
936 1bfc38f1 Panagiotis Kanavos
                StatusKeeper.StoreInfo(localPath, cloudFile);
937 0af3141d Panagiotis Kanavos
                
938 5ce54458 Panagiotis Kanavos
            }
939 9c4346c9 Panagiotis Kanavos
        }
940 9c4346c9 Panagiotis Kanavos
941 a27aa447 Panagiotis Kanavos
        //Download a small file with a single GET operation
942 f3d080df Panagiotis Kanavos
        private async Task DownloadEntireFileAsync(AccountInfo accountInfo, CloudFilesClient client, ObjectInfo cloudFile, Uri relativeUrl, string filePath,TreeHash serverHash)
943 a27aa447 Panagiotis Kanavos
        {
944 c53aa229 Panagiotis Kanavos
            if (client == null)
945 c53aa229 Panagiotis Kanavos
                throw new ArgumentNullException("client");
946 1bfc38f1 Panagiotis Kanavos
            if (cloudFile==null)
947 1bfc38f1 Panagiotis Kanavos
                throw new ArgumentNullException("cloudFile");
948 a27aa447 Panagiotis Kanavos
            if (relativeUrl == null)
949 a27aa447 Panagiotis Kanavos
                throw new ArgumentNullException("relativeUrl");
950 f3d080df Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(filePath))
951 f3d080df Panagiotis Kanavos
                throw new ArgumentNullException("filePath");
952 f3d080df Panagiotis Kanavos
            if (!Path.IsPathRooted(filePath))
953 f3d080df Panagiotis Kanavos
                throw new ArgumentException("The localPath must be rooted", "filePath");
954 a27aa447 Panagiotis Kanavos
            Contract.EndContractBlock();
955 a27aa447 Panagiotis Kanavos
956 f3d080df Panagiotis Kanavos
            var localPath = Pithos.Interfaces.FileInfoExtensions.GetProperFilePathCapitalization(filePath);
957 d3a13891 Panagiotis Kanavos
            //If the file already exists
958 d3a13891 Panagiotis Kanavos
            if (File.Exists(localPath))
959 d3a13891 Panagiotis Kanavos
            {
960 d3a13891 Panagiotis Kanavos
                //First check with MD5 as this is a small file
961 d3a13891 Panagiotis Kanavos
                var localMD5 = Signature.CalculateMD5(localPath);
962 d3a13891 Panagiotis Kanavos
                var cloudHash=serverHash.TopHash.ToHashString();
963 d3a13891 Panagiotis Kanavos
                if (localMD5==cloudHash)
964 27361404 Panagiotis Kanavos
                    return;
965 d3a13891 Panagiotis Kanavos
                //Then check with a treehash
966 d3a13891 Panagiotis Kanavos
                var localTreeHash = Signature.CalculateTreeHash(localPath, serverHash.BlockSize, serverHash.BlockHash);
967 d3a13891 Panagiotis Kanavos
                var localHash = localTreeHash.TopHash.ToHashString();
968 d3a13891 Panagiotis Kanavos
                if (localHash==cloudHash)
969 27361404 Panagiotis Kanavos
                    return;
970 d3a13891 Panagiotis Kanavos
            }
971 d3a13891 Panagiotis Kanavos
972 c28a075a Panagiotis Kanavos
            var fileAgent = GetFileAgent(accountInfo);
973 a27aa447 Panagiotis Kanavos
            //Calculate the relative file path for the new file
974 a27aa447 Panagiotis Kanavos
            var relativePath = relativeUrl.RelativeUriToFilePath();
975 a27aa447 Panagiotis Kanavos
            //The file will be stored in a temporary location while downloading with an extension .download
976 77e10b4f Panagiotis Kanavos
            var tempPath = Path.Combine(fileAgent.CachePath, relativePath + ".download");
977 a27aa447 Panagiotis Kanavos
            //Make sure the target folder exists. DownloadFileTask will not create the folder
978 d3a13891 Panagiotis Kanavos
            var tempFolder = Path.GetDirectoryName(tempPath);
979 d3a13891 Panagiotis Kanavos
            if (!Directory.Exists(tempFolder))
980 d3a13891 Panagiotis Kanavos
                Directory.CreateDirectory(tempFolder);
981 a27aa447 Panagiotis Kanavos
982 a27aa447 Panagiotis Kanavos
            //Download the object to the temporary location
983 f3d080df Panagiotis Kanavos
            await client.GetObject(cloudFile.Account, cloudFile.Container, relativeUrl.ToString(), tempPath);
984 0bd56b7c Panagiotis Kanavos
985 f3d080df Panagiotis Kanavos
            //Create the local folder if it doesn't exist (necessary for shared objects)
986 f3d080df Panagiotis Kanavos
            var localFolder = Path.GetDirectoryName(localPath);
987 f3d080df Panagiotis Kanavos
            if (!Directory.Exists(localFolder))
988 f3d080df Panagiotis Kanavos
                Directory.CreateDirectory(localFolder);            
989 f3d080df Panagiotis Kanavos
            //And move it to its actual location once downloading is finished
990 f3d080df Panagiotis Kanavos
            if (File.Exists(localPath))
991 f3d080df Panagiotis Kanavos
                File.Replace(tempPath,localPath,null,true);
992 f3d080df Panagiotis Kanavos
            else
993 f3d080df Panagiotis Kanavos
                File.Move(tempPath,localPath);
994 f3d080df Panagiotis Kanavos
            //Notify listeners that a local file has changed
995 f3d080df Panagiotis Kanavos
            StatusNotification.NotifyChangedFile(localPath);
996 f3d080df Panagiotis Kanavos
997 f3d080df Panagiotis Kanavos
                       
998 a27aa447 Panagiotis Kanavos
        }
999 a27aa447 Panagiotis Kanavos
1000 0af3141d Panagiotis Kanavos
        //Download a file asynchronously using blocks
1001 f3d080df Panagiotis Kanavos
        public async Task DownloadWithBlocks(AccountInfo accountInfo, CloudFilesClient client, ObjectInfo cloudFile, Uri relativeUrl, string filePath, TreeHash serverHash)
1002 0af3141d Panagiotis Kanavos
        {
1003 c53aa229 Panagiotis Kanavos
            if (client == null)
1004 c53aa229 Panagiotis Kanavos
                throw new ArgumentNullException("client");
1005 1bfc38f1 Panagiotis Kanavos
            if (cloudFile == null)
1006 1bfc38f1 Panagiotis Kanavos
                throw new ArgumentNullException("cloudFile");
1007 0af3141d Panagiotis Kanavos
            if (relativeUrl == null)
1008 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException("relativeUrl");
1009 f3d080df Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(filePath))
1010 f3d080df Panagiotis Kanavos
                throw new ArgumentNullException("filePath");
1011 f3d080df Panagiotis Kanavos
            if (!Path.IsPathRooted(filePath))
1012 f3d080df Panagiotis Kanavos
                throw new ArgumentException("The filePath must be rooted", "filePath");
1013 0af3141d Panagiotis Kanavos
            if (serverHash == null)
1014 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException("serverHash");
1015 0af3141d Panagiotis Kanavos
            Contract.EndContractBlock();
1016 0af3141d Panagiotis Kanavos
            
1017 27361404 Panagiotis Kanavos
           var fileAgent = GetFileAgent(accountInfo);
1018 f3d080df Panagiotis Kanavos
            var localPath = Interfaces.FileInfoExtensions.GetProperFilePathCapitalization(filePath);
1019 a64c87c8 Panagiotis Kanavos
            
1020 a27aa447 Panagiotis Kanavos
            //Calculate the relative file path for the new file
1021 a27aa447 Panagiotis Kanavos
            var relativePath = relativeUrl.RelativeUriToFilePath();
1022 77e10b4f Panagiotis Kanavos
            var blockUpdater = new BlockUpdater(fileAgent.CachePath, localPath, relativePath, serverHash);
1023 a27aa447 Panagiotis Kanavos
1024 a64c87c8 Panagiotis Kanavos
            
1025 0af3141d Panagiotis Kanavos
                        
1026 0af3141d Panagiotis Kanavos
            //Calculate the file's treehash
1027 27361404 Panagiotis Kanavos
            var treeHash = await Signature.CalculateTreeHashAsync(localPath, serverHash.BlockSize, serverHash.BlockHash);
1028 a27aa447 Panagiotis Kanavos
                
1029 0af3141d Panagiotis Kanavos
            //And compare it with the server's hash
1030 0af3141d Panagiotis Kanavos
            var upHashes = serverHash.GetHashesAsStrings();
1031 0af3141d Panagiotis Kanavos
            var localHashes = treeHash.HashDictionary;
1032 0af3141d Panagiotis Kanavos
            for (int i = 0; i < upHashes.Length; i++)
1033 0af3141d Panagiotis Kanavos
            {
1034 0af3141d Panagiotis Kanavos
                //For every non-matching hash
1035 0af3141d Panagiotis Kanavos
                var upHash = upHashes[i];
1036 0af3141d Panagiotis Kanavos
                if (!localHashes.ContainsKey(upHash))
1037 a27aa447 Panagiotis Kanavos
                {
1038 0af3141d Panagiotis Kanavos
                    if (blockUpdater.UseOrphan(i, upHash))
1039 a27aa447 Panagiotis Kanavos
                    {
1040 cfed7823 Panagiotis Kanavos
                        Log.InfoFormat("[BLOCK GET] ORPHAN FOUND for {0} of {1} for {2}", i, upHashes.Length, localPath);
1041 0af3141d Panagiotis Kanavos
                        continue;
1042 0af3141d Panagiotis Kanavos
                    }
1043 cfed7823 Panagiotis Kanavos
                    Log.InfoFormat("[BLOCK GET] START {0} of {1} for {2}", i, upHashes.Length, localPath);
1044 c53aa229 Panagiotis Kanavos
                    var start = i*serverHash.BlockSize;
1045 0af3141d Panagiotis Kanavos
                    //To download the last block just pass a null for the end of the range
1046 0af3141d Panagiotis Kanavos
                    long? end = null;
1047 0af3141d Panagiotis Kanavos
                    if (i < upHashes.Length - 1 )
1048 c53aa229 Panagiotis Kanavos
                        end= ((i + 1)*serverHash.BlockSize) ;
1049 a27aa447 Panagiotis Kanavos
                            
1050 0af3141d Panagiotis Kanavos
                    //Download the missing block
1051 27361404 Panagiotis Kanavos
                    var block = await client.GetBlock(cloudFile.Account, cloudFile.Container, relativeUrl, start, end);
1052 a64c87c8 Panagiotis Kanavos
1053 0af3141d Panagiotis Kanavos
                    //and store it
1054 27361404 Panagiotis Kanavos
                    blockUpdater.StoreBlock(i, block);
1055 a27aa447 Panagiotis Kanavos
1056 cfed7823 Panagiotis Kanavos
1057 cfed7823 Panagiotis Kanavos
                    Log.InfoFormat("[BLOCK GET] FINISH {0} of {1} for {2}", i, upHashes.Length, localPath);
1058 a27aa447 Panagiotis Kanavos
                }
1059 0af3141d Panagiotis Kanavos
            }
1060 a27aa447 Panagiotis Kanavos
1061 0bd56b7c Panagiotis Kanavos
            //Want to avoid notifications if no changes were made
1062 0bd56b7c Panagiotis Kanavos
            var hasChanges = blockUpdater.HasBlocks;
1063 0af3141d Panagiotis Kanavos
            blockUpdater.Commit();
1064 0bd56b7c Panagiotis Kanavos
            
1065 0bd56b7c Panagiotis Kanavos
            if (hasChanges)
1066 0bd56b7c Panagiotis Kanavos
                //Notify listeners that a local file has changed
1067 0bd56b7c Panagiotis Kanavos
                StatusNotification.NotifyChangedFile(localPath);
1068 0bd56b7c Panagiotis Kanavos
1069 cfed7823 Panagiotis Kanavos
            Log.InfoFormat("[BLOCK GET] COMPLETE {0}", localPath);            
1070 0af3141d Panagiotis Kanavos
        }
1071 a27aa447 Panagiotis Kanavos
1072 a27aa447 Panagiotis Kanavos
1073 27361404 Panagiotis Kanavos
        private async Task UploadCloudFile(CloudAction action)
1074 9c4346c9 Panagiotis Kanavos
        {
1075 1bfc38f1 Panagiotis Kanavos
            if (action == null)
1076 1bfc38f1 Panagiotis Kanavos
                throw new ArgumentNullException("action");           
1077 0af3141d Panagiotis Kanavos
            Contract.EndContractBlock();
1078 0af3141d Panagiotis Kanavos
1079 1bfc38f1 Panagiotis Kanavos
            try
1080 1bfc38f1 Panagiotis Kanavos
            {
1081 692ec33b Panagiotis Kanavos
                var accountInfo = action.AccountInfo;
1082 692ec33b Panagiotis Kanavos
1083 692ec33b Panagiotis Kanavos
                var fileInfo = action.LocalFile;
1084 1bfc38f1 Panagiotis Kanavos
1085 437abfca Panagiotis Kanavos
                if (fileInfo.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase))
1086 437abfca Panagiotis Kanavos
                    return;
1087 039a89e5 Panagiotis Kanavos
                
1088 437abfca Panagiotis Kanavos
                var relativePath = fileInfo.AsRelativeTo(accountInfo.AccountPath);
1089 437abfca Panagiotis Kanavos
                if (relativePath.StartsWith(FolderConstants.OthersFolder))
1090 1bfc38f1 Panagiotis Kanavos
                {
1091 692ec33b Panagiotis Kanavos
                    var parts = relativePath.Split('\\');
1092 437abfca Panagiotis Kanavos
                    var accountName = parts[1];
1093 437abfca Panagiotis Kanavos
                    var oldName = accountInfo.UserName;
1094 437abfca Panagiotis Kanavos
                    var absoluteUri = accountInfo.StorageUri.AbsoluteUri;
1095 692ec33b Panagiotis Kanavos
                    var nameIndex = absoluteUri.IndexOf(oldName);
1096 692ec33b Panagiotis Kanavos
                    var root = absoluteUri.Substring(0, nameIndex);
1097 437abfca Panagiotis Kanavos
1098 437abfca Panagiotis Kanavos
                    accountInfo = new AccountInfo
1099 437abfca Panagiotis Kanavos
                    {
1100 437abfca Panagiotis Kanavos
                        UserName = accountName,
1101 437abfca Panagiotis Kanavos
                        AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]),
1102 437abfca Panagiotis Kanavos
                        StorageUri = new Uri(root + accountName),
1103 692ec33b Panagiotis Kanavos
                        BlockHash = accountInfo.BlockHash,
1104 692ec33b Panagiotis Kanavos
                        BlockSize = accountInfo.BlockSize,
1105 692ec33b Panagiotis Kanavos
                        Token = accountInfo.Token
1106 437abfca Panagiotis Kanavos
                    };
1107 437abfca Panagiotis Kanavos
                }
1108 1bfc38f1 Panagiotis Kanavos
1109 1bfc38f1 Panagiotis Kanavos
1110 f3d080df Panagiotis Kanavos
                var fullFileName = fileInfo.GetProperCapitalization();
1111 692ec33b Panagiotis Kanavos
                using (var gate = NetworkGate.Acquire(fullFileName, NetworkOperation.Uploading))
1112 437abfca Panagiotis Kanavos
                {
1113 437abfca Panagiotis Kanavos
                    //Abort if the file is already being uploaded or downloaded
1114 437abfca Panagiotis Kanavos
                    if (gate.Failed)
1115 437abfca Panagiotis Kanavos
                        return;
1116 9c4346c9 Panagiotis Kanavos
1117 437abfca Panagiotis Kanavos
                    var cloudFile = action.CloudFile;
1118 437abfca Panagiotis Kanavos
                    var account = cloudFile.Account ?? accountInfo.UserName;
1119 1bfc38f1 Panagiotis Kanavos
1120 4f6d51d4 Panagiotis Kanavos
                    var client = new CloudFilesClient(accountInfo);                    
1121 437abfca Panagiotis Kanavos
                    //Even if GetObjectInfo times out, we can proceed with the upload            
1122 692ec33b Panagiotis Kanavos
                    var info = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);
1123 5ce54458 Panagiotis Kanavos
1124 4f6d51d4 Panagiotis Kanavos
                    //If this is a read-only file, do not upload changes
1125 4f6d51d4 Panagiotis Kanavos
                    if (info.AllowedTo == "read")
1126 4f6d51d4 Panagiotis Kanavos
                        return;
1127 f3d080df Panagiotis Kanavos
                    
1128 f3d080df Panagiotis Kanavos
                    //TODO: Check how a directory hash is calculated -> All dirs seem to have the same hash
1129 4f6d51d4 Panagiotis Kanavos
                    if (fileInfo is DirectoryInfo)
1130 437abfca Panagiotis Kanavos
                    {
1131 4f6d51d4 Panagiotis Kanavos
                        //If the directory doesn't exist the Hash property will be empty
1132 4f6d51d4 Panagiotis Kanavos
                        if (String.IsNullOrWhiteSpace(info.Hash))
1133 4f6d51d4 Panagiotis Kanavos
                            //Go on and create the directory
1134 3c76f045 Panagiotis Kanavos
                            await client.PutObject(account, cloudFile.Container, cloudFile.Name, fullFileName, String.Empty, "application/directory");
1135 437abfca Panagiotis Kanavos
                    }
1136 4f6d51d4 Panagiotis Kanavos
                    else
1137 4f6d51d4 Panagiotis Kanavos
                    {
1138 5ce54458 Panagiotis Kanavos
1139 4f6d51d4 Panagiotis Kanavos
                        var cloudHash = info.Hash.ToLower();
1140 d3a13891 Panagiotis Kanavos
1141 4f6d51d4 Panagiotis Kanavos
                        var hash = action.LocalHash.Value;
1142 4f6d51d4 Panagiotis Kanavos
                        var topHash = action.TopHash.Value;
1143 4f6d51d4 Panagiotis Kanavos
1144 4f6d51d4 Panagiotis Kanavos
                        //If the file hashes match, abort the upload
1145 4f6d51d4 Panagiotis Kanavos
                        if (hash == cloudHash || topHash == cloudHash)
1146 4f6d51d4 Panagiotis Kanavos
                        {
1147 4f6d51d4 Panagiotis Kanavos
                            //but store any metadata changes 
1148 4f6d51d4 Panagiotis Kanavos
                            StatusKeeper.StoreInfo(fullFileName, info);
1149 4f6d51d4 Panagiotis Kanavos
                            Log.InfoFormat("Skip upload of {0}, hashes match", fullFileName);
1150 4f6d51d4 Panagiotis Kanavos
                            return;
1151 4f6d51d4 Panagiotis Kanavos
                        }
1152 a27aa447 Panagiotis Kanavos
1153 5750d7cc Panagiotis Kanavos
1154 4f6d51d4 Panagiotis Kanavos
                        //Mark the file as modified while we upload it
1155 4f6d51d4 Panagiotis Kanavos
                        StatusKeeper.SetFileOverlayStatus(fullFileName, FileOverlayStatus.Modified);
1156 4f6d51d4 Panagiotis Kanavos
                        //And then upload it
1157 692ec33b Panagiotis Kanavos
1158 4f6d51d4 Panagiotis Kanavos
                        //Upload even small files using the Hashmap. The server may already contain
1159 4f6d51d4 Panagiotis Kanavos
                        //the relevant block
1160 5750d7cc Panagiotis Kanavos
1161 4f6d51d4 Panagiotis Kanavos
                        //First, calculate the tree hash
1162 f3d080df Panagiotis Kanavos
                        var treeHash = await Signature.CalculateTreeHashAsync(fullFileName, accountInfo.BlockSize,
1163 4f6d51d4 Panagiotis Kanavos
                                                                              accountInfo.BlockHash);
1164 4f6d51d4 Panagiotis Kanavos
1165 4f6d51d4 Panagiotis Kanavos
                        await UploadWithHashMap(accountInfo, cloudFile, fileInfo as FileInfo, cloudFile.Name, treeHash);
1166 4f6d51d4 Panagiotis Kanavos
                    }
1167 437abfca Panagiotis Kanavos
                    //If everything succeeds, change the file and overlay status to normal
1168 73cdd135 Panagiotis Kanavos
                    StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal);
1169 437abfca Panagiotis Kanavos
                }
1170 437abfca Panagiotis Kanavos
                //Notify the Shell to update the overlays
1171 437abfca Panagiotis Kanavos
                NativeMethods.RaiseChangeNotification(fullFileName);
1172 437abfca Panagiotis Kanavos
                StatusNotification.NotifyChangedFile(fullFileName);
1173 5ce54458 Panagiotis Kanavos
            }
1174 437abfca Panagiotis Kanavos
            catch (AggregateException ex)
1175 437abfca Panagiotis Kanavos
            {
1176 437abfca Panagiotis Kanavos
                var exc = ex.InnerException as WebException;
1177 437abfca Panagiotis Kanavos
                if (exc == null)
1178 437abfca Panagiotis Kanavos
                    throw ex.InnerException;
1179 692ec33b Panagiotis Kanavos
                if (HandleUploadWebException(action, exc)) 
1180 437abfca Panagiotis Kanavos
                    return;
1181 692ec33b Panagiotis Kanavos
                throw;
1182 692ec33b Panagiotis Kanavos
            }
1183 692ec33b Panagiotis Kanavos
            catch (WebException ex)
1184 692ec33b Panagiotis Kanavos
            {
1185 692ec33b Panagiotis Kanavos
                if (HandleUploadWebException(action, ex))
1186 692ec33b Panagiotis Kanavos
                    return;
1187 692ec33b Panagiotis Kanavos
                throw;
1188 692ec33b Panagiotis Kanavos
            }
1189 692ec33b Panagiotis Kanavos
            catch (Exception ex)
1190 692ec33b Panagiotis Kanavos
            {
1191 692ec33b Panagiotis Kanavos
                Log.Error("Unexpected error while uploading file", ex);
1192 437abfca Panagiotis Kanavos
                throw;
1193 437abfca Panagiotis Kanavos
            }
1194 437abfca Panagiotis Kanavos
1195 9c4346c9 Panagiotis Kanavos
        }
1196 9c4346c9 Panagiotis Kanavos
1197 039a89e5 Panagiotis Kanavos
        private bool IsDeletedFile(CloudAction action)
1198 039a89e5 Panagiotis Kanavos
        {            
1199 039a89e5 Panagiotis Kanavos
            var key = GetFileKey(action.CloudFile);
1200 039a89e5 Panagiotis Kanavos
            DateTime entryDate;
1201 039a89e5 Panagiotis Kanavos
            if (_deletedFiles.TryGetValue(key, out entryDate))
1202 039a89e5 Panagiotis Kanavos
            {
1203 039a89e5 Panagiotis Kanavos
                //If the delete entry was created after this action, abort the action
1204 039a89e5 Panagiotis Kanavos
                if (entryDate > action.Created)
1205 039a89e5 Panagiotis Kanavos
                    return true;
1206 039a89e5 Panagiotis Kanavos
                //Otherwise, remove the stale entry 
1207 039a89e5 Panagiotis Kanavos
                _deletedFiles.TryRemove(key, out entryDate);
1208 039a89e5 Panagiotis Kanavos
            }
1209 039a89e5 Panagiotis Kanavos
            return false;
1210 039a89e5 Panagiotis Kanavos
        }
1211 039a89e5 Panagiotis Kanavos
1212 692ec33b Panagiotis Kanavos
        private bool HandleUploadWebException(CloudAction action, WebException exc)
1213 692ec33b Panagiotis Kanavos
        {
1214 692ec33b Panagiotis Kanavos
            var response = exc.Response as HttpWebResponse;
1215 692ec33b Panagiotis Kanavos
            if (response == null)
1216 692ec33b Panagiotis Kanavos
                throw exc;
1217 692ec33b Panagiotis Kanavos
            if (response.StatusCode == HttpStatusCode.Unauthorized)
1218 692ec33b Panagiotis Kanavos
            {
1219 692ec33b Panagiotis Kanavos
                Log.Error("Not allowed to upload file", exc);
1220 692ec33b Panagiotis Kanavos
                var message = String.Format("Not allowed to uplad file {0}", action.LocalFile.FullName);
1221 692ec33b Panagiotis Kanavos
                StatusKeeper.SetFileState(action.LocalFile.FullName, FileStatus.Unchanged, FileOverlayStatus.Normal);
1222 692ec33b Panagiotis Kanavos
                StatusNotification.NotifyChange(message, TraceLevel.Warning);
1223 692ec33b Panagiotis Kanavos
                return true;
1224 692ec33b Panagiotis Kanavos
            }
1225 692ec33b Panagiotis Kanavos
            return false;
1226 692ec33b Panagiotis Kanavos
        }
1227 692ec33b Panagiotis Kanavos
1228 27361404 Panagiotis Kanavos
        public async Task UploadWithHashMap(AccountInfo accountInfo,ObjectInfo cloudFile,FileInfo fileInfo,string url,TreeHash treeHash)
1229 a27aa447 Panagiotis Kanavos
        {
1230 c53aa229 Panagiotis Kanavos
            if (accountInfo == null)
1231 c53aa229 Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
1232 1bfc38f1 Panagiotis Kanavos
            if (cloudFile==null)
1233 1bfc38f1 Panagiotis Kanavos
                throw new ArgumentNullException("cloudFile");
1234 cfed7823 Panagiotis Kanavos
            if (fileInfo == null)
1235 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException("fileInfo");
1236 0af3141d Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(url))
1237 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException(url);
1238 0af3141d Panagiotis Kanavos
            if (treeHash==null)
1239 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException("treeHash");
1240 ec6f3895 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(cloudFile.Container) )
1241 ec6f3895 Panagiotis Kanavos
                throw new ArgumentException("Invalid container","cloudFile");
1242 0af3141d Panagiotis Kanavos
            Contract.EndContractBlock();
1243 0af3141d Panagiotis Kanavos
1244 f3d080df Panagiotis Kanavos
            var fullFileName = fileInfo.GetProperCapitalization();
1245 a27aa447 Panagiotis Kanavos
1246 1bfc38f1 Panagiotis Kanavos
            var account = cloudFile.Account ?? accountInfo.UserName;
1247 27361404 Panagiotis Kanavos
            var container = cloudFile.Container ;
1248 1bfc38f1 Panagiotis Kanavos
1249 c53aa229 Panagiotis Kanavos
            var client = new CloudFilesClient(accountInfo);
1250 a27aa447 Panagiotis Kanavos
            //Send the hashmap to the server            
1251 437abfca Panagiotis Kanavos
            var missingHashes =  await client.PutHashMap(account, container, url, treeHash);
1252 0af3141d Panagiotis Kanavos
            //If the server returns no missing hashes, we are done
1253 0af3141d Panagiotis Kanavos
            while (missingHashes.Count > 0)
1254 a27aa447 Panagiotis Kanavos
            {
1255 0af3141d Panagiotis Kanavos
1256 c53aa229 Panagiotis Kanavos
                var buffer = new byte[accountInfo.BlockSize];
1257 0af3141d Panagiotis Kanavos
                foreach (var missingHash in missingHashes)
1258 a27aa447 Panagiotis Kanavos
                {
1259 a27aa447 Panagiotis Kanavos
                    //Find the proper block
1260 437abfca Panagiotis Kanavos
                    var blockIndex = treeHash.HashDictionary[missingHash];
1261 c53aa229 Panagiotis Kanavos
                    var offset = blockIndex*accountInfo.BlockSize;
1262 a27aa447 Panagiotis Kanavos
1263 c53aa229 Panagiotis Kanavos
                    var read = fileInfo.Read(buffer, offset, accountInfo.BlockSize);
1264 0af3141d Panagiotis Kanavos
1265 437abfca Panagiotis Kanavos
                    try
1266 437abfca Panagiotis Kanavos
                    {
1267 437abfca Panagiotis Kanavos
                        //And upload the block                
1268 437abfca Panagiotis Kanavos
                        await client.PostBlock(account, container, buffer, 0, read);
1269 437abfca Panagiotis Kanavos
                        Log.InfoFormat("[BLOCK] Block {0} of {1} uploaded", blockIndex, fullFileName);
1270 437abfca Panagiotis Kanavos
                    }
1271 437abfca Panagiotis Kanavos
                    catch (Exception exc)
1272 437abfca Panagiotis Kanavos
                    {
1273 437abfca Panagiotis Kanavos
                        Log.ErrorFormat("[ERROR] uploading block {0} of {1}\n{2}", blockIndex, fullFileName, exc);
1274 437abfca Panagiotis Kanavos
                    }
1275 0af3141d Panagiotis Kanavos
1276 a27aa447 Panagiotis Kanavos
                }
1277 a27aa447 Panagiotis Kanavos
1278 437abfca Panagiotis Kanavos
                //Repeat until there are no more missing hashes                
1279 437abfca Panagiotis Kanavos
                missingHashes = await client.PutHashMap(account, container, url, treeHash);
1280 0af3141d Panagiotis Kanavos
            }
1281 a27aa447 Panagiotis Kanavos
        }
1282 a27aa447 Panagiotis Kanavos
1283 9c4346c9 Panagiotis Kanavos
1284 c53aa229 Panagiotis Kanavos
        public void AddAccount(AccountInfo accountInfo)
1285 c53aa229 Panagiotis Kanavos
        {            
1286 c53aa229 Panagiotis Kanavos
            if (!_accounts.Contains(accountInfo))
1287 c53aa229 Panagiotis Kanavos
                _accounts.Add(accountInfo);
1288 c53aa229 Panagiotis Kanavos
        }
1289 9c4346c9 Panagiotis Kanavos
    }
1290 9c4346c9 Panagiotis Kanavos
1291 5ce54458 Panagiotis Kanavos
   
1292 5ce54458 Panagiotis Kanavos
1293 9c4346c9 Panagiotis Kanavos
1294 9c4346c9 Panagiotis Kanavos
}