Revision 1caef52e

b/trunk/Pithos.Client.WPF/NativeMethods.cs
1
using System;
2
using System.Runtime.InteropServices;
3

  
4
namespace Pithos.Client.WPF
5
{
6
    #region Enums & Structs
7

  
8
    #region enum HChangeNotifyEventID
9
    /// <summary>
10
    /// Describes the event that has occurred. 
11
    /// Typically, only one event is specified at a time. 
12
    /// If more than one event is specified, the values contained 
13
    /// in the <i>dwItem1</i> and <i>dwItem2</i> 
14
    /// parameters must be the same, respectively, for all specified events. 
15
    /// This parameter can be one or more of the following values. 
16
    /// </summary>
17
    /// <remarks>
18
    /// <para><b>Windows NT/2000/XP:</b> <i>dwItem2</i> contains the index 
19
    /// in the system image list that has changed. 
20
    /// <i>dwItem1</i> is not used and should be <see langword="null"/>.</para>
21
    /// <para><b>Windows 95/98:</b> <i>dwItem1</i> contains the index 
22
    /// in the system image list that has changed. 
23
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>.</para>
24
    /// </remarks>
25
    [Flags]
26
    enum HChangeNotifyEventID
27
    {
28
        /// <summary>
29
        /// All events have occurred. 
30
        /// </summary>
31
        SHCNE_ALLEVENTS = 0x7FFFFFFF,
32

  
33
        /// <summary>
34
        /// A file type association has changed. <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> 
35
        /// must be specified in the <i>uFlags</i> parameter. 
36
        /// <i>dwItem1</i> and <i>dwItem2</i> are not used and must be <see langword="null"/>. 
37
        /// </summary>
38
        SHCNE_ASSOCCHANGED = 0x08000000,
39

  
40
        /// <summary>
41
        /// The attributes of an item or folder have changed. 
42
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
43
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
44
        /// <i>dwItem1</i> contains the item or folder that has changed. 
45
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>.
46
        /// </summary>
47
        SHCNE_ATTRIBUTES = 0x00000800,
48

  
49
        /// <summary>
50
        /// A nonfolder item has been created. 
51
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
52
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
53
        /// <i>dwItem1</i> contains the item that was created. 
54
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>.
55
        /// </summary>
56
        SHCNE_CREATE = 0x00000002,
57

  
58
        /// <summary>
59
        /// A nonfolder item has been deleted. 
60
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
61
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
62
        /// <i>dwItem1</i> contains the item that was deleted. 
63
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
64
        /// </summary>
65
        SHCNE_DELETE = 0x00000004,
66

  
67
        /// <summary>
68
        /// A drive has been added. 
69
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
70
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
71
        /// <i>dwItem1</i> contains the root of the drive that was added. 
72
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
73
        /// </summary>
74
        SHCNE_DRIVEADD = 0x00000100,
75

  
76
        /// <summary>
77
        /// A drive has been added and the Shell should create a new window for the drive. 
78
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
79
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
80
        /// <i>dwItem1</i> contains the root of the drive that was added. 
81
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
82
        /// </summary>
83
        SHCNE_DRIVEADDGUI = 0x00010000,
84

  
85
        /// <summary>
86
        /// A drive has been removed. <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
87
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
88
        /// <i>dwItem1</i> contains the root of the drive that was removed.
89
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
90
        /// </summary>
91
        SHCNE_DRIVEREMOVED = 0x00000080,
92

  
93
        /// <summary>
94
        /// Not currently used. 
95
        /// </summary>
96
        SHCNE_EXTENDED_EVENT = 0x04000000,
97

  
98
        /// <summary>
99
        /// The amount of free space on a drive has changed. 
100
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
101
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
102
        /// <i>dwItem1</i> contains the root of the drive on which the free space changed.
103
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
104
        /// </summary>
105
        SHCNE_FREESPACE = 0x00040000,
106

  
107
        /// <summary>
108
        /// Storage media has been inserted into a drive. 
109
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
110
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
111
        /// <i>dwItem1</i> contains the root of the drive that contains the new media. 
112
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
113
        /// </summary>
114
        SHCNE_MEDIAINSERTED = 0x00000020,
115

  
116
        /// <summary>
117
        /// Storage media has been removed from a drive. 
118
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
119
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
120
        /// <i>dwItem1</i> contains the root of the drive from which the media was removed. 
121
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
122
        /// </summary>
123
        SHCNE_MEDIAREMOVED = 0x00000040,
124

  
125
        /// <summary>
126
        /// A folder has been created. <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> 
127
        /// or <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
128
        /// <i>dwItem1</i> contains the folder that was created. 
129
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
130
        /// </summary>
131
        SHCNE_MKDIR = 0x00000008,
132

  
133
        /// <summary>
134
        /// A folder on the local computer is being shared via the network. 
135
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
136
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
137
        /// <i>dwItem1</i> contains the folder that is being shared. 
138
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
139
        /// </summary>
140
        SHCNE_NETSHARE = 0x00000200,
141

  
142
        /// <summary>
143
        /// A folder on the local computer is no longer being shared via the network. 
144
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
145
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
146
        /// <i>dwItem1</i> contains the folder that is no longer being shared. 
147
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
148
        /// </summary>
149
        SHCNE_NETUNSHARE = 0x00000400,
150

  
151
        /// <summary>
152
        /// The name of a folder has changed. 
153
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
154
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
155
        /// <i>dwItem1</i> contains the previous pointer to an item identifier list (PIDL) or name of the folder. 
156
        /// <i>dwItem2</i> contains the new PIDL or name of the folder. 
157
        /// </summary>
158
        SHCNE_RENAMEFOLDER = 0x00020000,
159

  
160
        /// <summary>
161
        /// The name of a nonfolder item has changed. 
162
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
163
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
164
        /// <i>dwItem1</i> contains the previous PIDL or name of the item. 
165
        /// <i>dwItem2</i> contains the new PIDL or name of the item. 
166
        /// </summary>
167
        SHCNE_RENAMEITEM = 0x00000001,
168

  
169
        /// <summary>
170
        /// A folder has been removed. 
171
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
172
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
173
        /// <i>dwItem1</i> contains the folder that was removed. 
174
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
175
        /// </summary>
176
        SHCNE_RMDIR = 0x00000010,
177

  
178
        /// <summary>
179
        /// The computer has disconnected from a server. 
180
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
181
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
182
        /// <i>dwItem1</i> contains the server from which the computer was disconnected. 
183
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
184
        /// </summary>
185
        SHCNE_SERVERDISCONNECT = 0x00004000,
186

  
187
        /// <summary>
188
        /// The contents of an existing folder have changed, 
189
        /// but the folder still exists and has not been renamed. 
190
        /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
191
        /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
192
        /// <i>dwItem1</i> contains the folder that has changed. 
193
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
194
        /// If a folder has been created, deleted, or renamed, use SHCNE_MKDIR, SHCNE_RMDIR, or 
195
        /// SHCNE_RENAMEFOLDER, respectively, instead. 
196
        /// </summary>
197
        SHCNE_UPDATEDIR = 0x00001000,
198

  
199
        SHCNE_UPDATEITEM = 0x00002000,
200

  
201
        /// <summary>
202
        /// An image in the system image list has changed. 
203
        /// <see cref="HChangeNotifyFlags.SHCNF_DWORD"/> must be specified in <i>uFlags</i>. 
204
        /// </summary>
205
        SHCNE_UPDATEIMAGE = 0x00008000,
206

  
207
    }
208
    #endregion // enum HChangeNotifyEventID
209

  
210
    #region public enum HChangeNotifyFlags
211
    /// <summary>
212
    /// Flags that indicate the meaning of the <i>dwItem1</i> and <i>dwItem2</i> parameters. 
213
    /// The uFlags parameter must be one of the following values.
214
    /// </summary>
215
    [Flags]
216
    public enum HChangeNotifyFlags
217
    {
218
        /// <summary>
219
        /// The <i>dwItem1</i> and <i>dwItem2</i> parameters are DWORD values. 
220
        /// </summary>
221
        SHCNF_DWORD = 0x0003,
222
        /// <summary>
223
        /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of ITEMIDLIST structures that 
224
        /// represent the item(s) affected by the change. 
225
        /// Each ITEMIDLIST must be relative to the desktop folder. 
226
        /// </summary>
227
        SHCNF_IDLIST = 0x0000,
228
        /// <summary>
229
        /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of null-terminated strings of 
230
        /// maximum length MAX_PATH that contain the full path names 
231
        /// of the items affected by the change. 
232
        /// </summary>
233
        SHCNF_PATHA = 0x0001,
234
        /// <summary>
235
        /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of null-terminated strings of 
236
        /// maximum length MAX_PATH that contain the full path names 
237
        /// of the items affected by the change. 
238
        /// </summary>
239
        SHCNF_PATHW = 0x0005,
240
        /// <summary>
241
        /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of null-terminated strings that 
242
        /// represent the friendly names of the printer(s) affected by the change. 
243
        /// </summary>
244
        SHCNF_PRINTERA = 0x0002,
245
        /// <summary>
246
        /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of null-terminated strings that 
247
        /// represent the friendly names of the printer(s) affected by the change. 
248
        /// </summary>
249
        SHCNF_PRINTERW = 0x0006,
250
        /// <summary>
251
        /// The function should not return until the notification 
252
        /// has been delivered to all affected components. 
253
        /// As this flag modifies other data-type flags, it cannot by used by itself.
254
        /// </summary>
255
        SHCNF_FLUSH = 0x1000,
256
        /// <summary>
257
        /// The function should begin delivering notifications to all affected components 
258
        /// but should return as soon as the notification process has begun. 
259
        /// As this flag modifies other data-type flags, it cannot by used by itself.
260
        /// </summary>
261
        SHCNF_FLUSHNOWAIT = 0x2000
262
    }
263
    #endregion // enum HChangeNotifyFlags
264

  
265

  
266
    #endregion
267

  
268

  
269
    internal static class NativeMethods
270
    {
271
        [DllImport("shell32.dll")]
272
        public static extern void SHChangeNotify(HChangeNotifyEventID wEventId,
273
                                           HChangeNotifyFlags uFlags,
274
                                           IntPtr dwItem1,
275
                                           IntPtr dwItem2);
276

  
277
    }
278
}
/dev/null
1
using System;
2
using System.Collections.Generic;
3
using System.IO;
4
using System.Linq;
5
using System.Security.Cryptography;
6
using System.Text;
7
using System.Threading;
8
using System.Threading.Tasks;
9
using NUnit.Framework;
10
using Newtonsoft.Json;
11

  
12
namespace Pithos.Core.Test
13
{
14
    [TestFixture]
15
    public class SignatureTest
16
    {
17

  
18
        [Test]
19
        public void TestCreate()
20
        {
21
            var hasher = HashAlgorithm.Create("sha256");
22
            Assert.IsNotNull(hasher);
23
        }
24

  
25
        [Test]
26
        public void TestHashmapCreation()
27
        {
28
            var file = "e:\\pithos\\vlc-1.1.11-win32.exe";
29

  
30
            decimal blockSize = 4*1024*1024;
31

  
32
            var fileSize = new FileInfo(file).Length;
33
            var numBlocks = decimal.Ceiling(fileSize/blockSize);
34

  
35
            var md5 = Signature.CalculateMD5(file);            
36

  
37
            var hash1 = Signature.CalculateTreeHashAsync(file, (int) blockSize,"sha256").Result;
38
            Assert.IsNotNull(hash1.Hashes);
39
            Assert.AreEqual(numBlocks, hash1.Hashes.Count());
40

  
41
            var topHash = hash1.TopHash;
42
            var hashString = BytesToStr(topHash);
43

  
44
            var stringHashes = (from hash in hash1.Hashes
45
                               select BytesToStr(hash)).ToList();
46
            var hashes = JsonConvert.SerializeObject(stringHashes);
47
            Assert.IsNotNull(topHash);
48
        }
49
        
50
        [Test]
51
        public void TestHashmapStorage()
52
        {
53
            var file = "e:\\pithos\\vlc-1.1.11-win32.exe";
54

  
55
            decimal blockSize = 4*1024*1024;
56

  
57
            var fileSize = new FileInfo(file).Length;
58
            var numBlocks = decimal.Ceiling(fileSize/blockSize);
59

  
60
            var md5 = Signature.CalculateMD5(file);
61

  
62
            var hash1 = Signature.CalculateTreeHashAsync(file, (int) blockSize, "sha256").Result;
63
            hash1.FileId = Guid.NewGuid();
64
            var task = hash1.Save(@"e:\")
65
                .ContinueWith(_ => TreeHash.LoadTreeHash(@"e:\", hash1.FileId)).Unwrap();            
66
            task.ContinueWith(t =>
67
            {                
68
                var hash = t.Result;
69
                Assert.AreEqual(hash1.Hashes, hash.Hashes.ToArray());
70
                int i = 0;
71
            }).Wait();
72
        }
73

  
74
        public static string BytesToStr(byte[] bytes)
75
        {
76
            var str = new StringBuilder();
77

  
78
            foreach (byte t in bytes)
79
                str.AppendFormat("{0:X2}", t);
80
            
81
            return str.ToString();
82
        }
83

  
84

  
85
        [Test]
86
        public void TestTopHashEmpty()
87
        {
88
            using (var hasher = HashAlgorithm.Create("sha256"))
89
            {
90
                var hashEmpty = hasher.ComputeHash(new byte[] {});                
91

  
92
                var empty = new List<byte[]>();
93
                var hash = Signature.CalculateTopHash(empty,"sha256");
94
                Assert.IsNull(hash);
95
            }
96
        }
97
    }
98

  
99
}
b/trunk/Pithos.Core/Agents/CloudAction.cs
1
using System;
2
using System.IO;
3
using System.Threading;
4
using Pithos.Interfaces;
5
using Pithos.Network;
6

  
7
namespace Pithos.Core.Agents
8
{
9
    public enum CloudActionType
10
    {
11
        MustSynch,
12
        UploadUnconditional,
13
        DownloadUnconditional,
14
        DeleteLocal,
15
        DeleteCloud,
16
        RenameCloud
17
    }
18

  
19
    public class CloudAction
20
    {
21
        public CloudActionType Action { get; set; }
22
        public FileInfo LocalFile { get; set; }
23
        public ObjectInfo CloudFile { get; set; }
24

  
25
        public Lazy<string> LocalHash { get; private set; }
26

  
27
        public string OldFileName { get; set; }
28
        public string OldPath { get; set; }
29
        public string NewFileName { get; set; }
30
        public string NewPath { get; set; }
31

  
32
        public CloudAction(CloudActionType action, string oldPath, string oldFileName, string newFileName, string newPath)
33
        {
34
            Action = action;
35
            OldFileName = oldFileName;
36
            OldPath = oldPath;
37
            NewFileName = newFileName;
38
            NewPath = newPath;
39
            LocalHash = new Lazy<string>(() => Signature.CalculateMD5(NewFileName), LazyThreadSafetyMode.ExecutionAndPublication);
40
        }
41

  
42
        public CloudAction(CloudActionType action, FileInfo localFile, ObjectInfo cloudFile)
43
        {
44
            Action = action;
45
            LocalFile = localFile;
46
            CloudFile = cloudFile;
47
            //Skip Hash calculation for folders
48
            if (LocalFile != null)
49
                LocalHash = new Lazy<string>(() => Signature.CalculateMD5(LocalFile.FullName), LazyThreadSafetyMode.ExecutionAndPublication);
50
        }
51

  
52
    }
53
}
b/trunk/Pithos.Core/Agents/FileAgent.cs
1
using System;
2
using System.Collections.Generic;
3
using System.ComponentModel.Composition;
4
using System.Diagnostics;
5
using System.IO;
6
using System.Linq;
7
using System.Text;
8
using Pithos.Interfaces;
9
using Pithos.Network;
10

  
11
namespace Pithos.Core.Agents
12
{
13
    [Export]
14
    public class FileAgent
15
    {
16
        Agent<WorkflowState> _agent;
17
        private FileSystemWatcher _watcher;
18

  
19
        [Import]
20
        public IStatusKeeper StatusKeeper { get; set; }
21
        [Import]
22
        public IPithosWorkflow Workflow { get; set; }
23
        [Import]
24
        public WorkflowAgent WorkflowAgent { get; set; }
25

  
26
        public string RootPath { get; private set; }
27

  
28
        public void Start(string rootPath)
29
        {
30
            RootPath = rootPath;
31
            _watcher = new FileSystemWatcher(rootPath);
32
            _watcher.Changed += OnFileEvent;
33
            _watcher.Created += OnFileEvent;
34
            _watcher.Deleted += OnFileEvent;
35
            _watcher.Renamed += OnRenameEvent;
36
            _watcher.EnableRaisingEvents = true;
37

  
38

  
39
            _agent = Agent<WorkflowState>.Start(inbox =>
40
            {
41
                Action loop = null;
42
                loop = () =>
43
                {
44
                    var message = inbox.Receive();
45
                    var process = message.ContinueWith(t =>
46
                    {
47
                        var state = t.Result;
48
                        Process(state);
49
                        inbox.DoAsync(loop);
50
                    });
51

  
52
                    process.ContinueWith(t =>
53
                    {
54
                        inbox.DoAsync(loop);
55
                        if (t.IsFaulted)
56
                        {
57
                            var ex = t.Exception.InnerException;
58
                            if (ex is OperationCanceledException)
59
                                inbox.Stop();
60
                            Trace.TraceError("[ERROR] File Event Processing:\r{0}", ex);
61
                        }
62
                    });
63

  
64
                };
65
                loop();
66
            });
67
        }
68

  
69
        public bool Pause
70
        {
71
            get { return _watcher == null || !_watcher.EnableRaisingEvents; }
72
            set
73
            {
74
                if (_watcher != null)
75
                    _watcher.EnableRaisingEvents = !value;                
76
            }
77
        }
78

  
79
        public string FragmentsPath { get; set; }
80

  
81
        public void Post(WorkflowState workflowState)
82
        {
83
            _agent.Post(workflowState);
84
        }
85

  
86
        public void Stop()
87
        {
88
            if (_watcher != null)
89
            {
90
                _watcher.Changed -= OnFileEvent;
91
                _watcher.Created -= OnFileEvent;
92
                _watcher.Deleted -= OnFileEvent;
93
                _watcher.Renamed -= OnRenameEvent;
94
                _watcher.Dispose();
95
            }
96
            _watcher = null;
97

  
98
            _agent.Stop();
99
        }
100

  
101
        // Enumerate all files in the Pithos directory except those in the Fragment folder
102
        // and files with a .ignore extension
103
        public IEnumerable<string> EnumerateFiles(string searchPattern="*")
104
        {
105
            var monitoredFiles = from filePath in Directory.EnumerateFileSystemEntries(RootPath, searchPattern, SearchOption.AllDirectories)
106
                                 where !Ignore(filePath)
107
                                 select filePath;
108
            return monitoredFiles;
109
        }
110

  
111
        public IEnumerable<FileInfo> EnumerateFileInfos(string searchPattern="*")
112
        {
113
            var rootDir = new DirectoryInfo(RootPath);
114
            var monitoredFiles = from file in rootDir.EnumerateFiles(searchPattern, SearchOption.AllDirectories)
115
                                 where !Ignore(file.FullName)
116
                                 select file;
117
            return monitoredFiles;
118
        }                
119

  
120
        public IEnumerable<string> EnumerateFilesAsRelativeUrls(string searchPattern="*")
121
        {
122
            var rootDir = new DirectoryInfo(RootPath);
123
            var monitoredFiles = from file in rootDir.EnumerateFiles(searchPattern, SearchOption.AllDirectories)
124
                                 where !Ignore(file.FullName)
125
                                 select file.AsRelativeUrlTo(RootPath);
126
            return monitoredFiles;
127
        }                
128

  
129

  
130
        
131

  
132
        private bool Ignore(string filePath)
133
        {
134
            if (filePath.StartsWith(FragmentsPath))
135
                return true;
136
            return false;
137
        }
138

  
139
        //Post a Change message for all events except rename
140
        void OnFileEvent(object sender, FileSystemEventArgs e)
141
        {
142
            //Ignore events that affect the Fragments folder
143
            var filePath = e.FullPath;
144
            if (Ignore(filePath)) 
145
                return;
146
            _agent.Post(new WorkflowState { Path = filePath, FileName = e.Name, TriggeringChange = e.ChangeType });
147
        }
148

  
149

  
150
        //Post a Change message for renames containing the old and new names
151
        void OnRenameEvent(object sender, RenamedEventArgs e)
152
        {
153
            var oldFullPath = e.OldFullPath;
154
            var fullPath = e.FullPath;
155
            if (Ignore(oldFullPath) || Ignore(fullPath))
156
                return;
157

  
158
            _agent.Post(new WorkflowState
159
            {
160
                OldPath = oldFullPath,
161
                OldFileName = e.OldName,
162
                Path = fullPath,
163
                FileName = e.Name,
164
                TriggeringChange = e.ChangeType
165
            });
166
        }
167

  
168

  
169
        private void Process(WorkflowState state)
170
        {
171
            Debug.Assert(!Ignore(state.Path));            
172

  
173
            var networkState = NetworkGate.GetNetworkState(state.Path);
174
            //Skip if the file is already being downloaded or uploaded and 
175
            //the change is create or modify
176
            if (networkState != NetworkOperation.None &&
177
                (
178
                    state.TriggeringChange == WatcherChangeTypes.Created ||
179
                    state.TriggeringChange == WatcherChangeTypes.Changed
180
                ))
181
                return;
182
            UpdateFileStatus(state);
183
            UpdateOverlayStatus(state);
184
            UpdateFileChecksum(state);
185
            WorkflowAgent.Post(state);
186
        }
187

  
188
        private Dictionary<WatcherChangeTypes, FileStatus> _statusDict = new Dictionary<WatcherChangeTypes, FileStatus>
189
        {
190
            {WatcherChangeTypes.Created,FileStatus.Created},
191
            {WatcherChangeTypes.Changed,FileStatus.Modified},
192
            {WatcherChangeTypes.Deleted,FileStatus.Deleted},
193
            {WatcherChangeTypes.Renamed,FileStatus.Renamed}
194
        };
195

  
196
        private WorkflowState UpdateFileStatus(WorkflowState state)
197
        {
198
            Debug.Assert(!state.Path.Contains("fragments"));
199
            Debug.Assert(!state.Path.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase));
200

  
201
            string path = state.Path;
202
            FileStatus status = _statusDict[state.TriggeringChange];
203
            var oldStatus = Workflow.StatusKeeper.GetFileStatus(path);
204
            if (status == oldStatus)
205
            {
206
                state.Status = status;
207
                state.Skip = true;
208
                return state;
209
            }
210
            if (state.Status == FileStatus.Renamed)
211
                Workflow.ClearFileStatus(path);
212

  
213
            state.Status = Workflow.SetFileStatus(path, status);
214
            return state;
215
        }
216

  
217
        private WorkflowState UpdateOverlayStatus(WorkflowState state)
218
        {
219
            if (state.Skip)
220
                return state;
221

  
222
            switch (state.Status)
223
            {
224
                case FileStatus.Created:
225
                case FileStatus.Modified:
226
                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
227
                    break;
228
                case FileStatus.Deleted:
229
                    this.StatusKeeper.RemoveFileOverlayStatus(state.Path);
230
                    break;
231
                case FileStatus.Renamed:
232
                    this.StatusKeeper.RemoveFileOverlayStatus(state.OldPath);
233
                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
234
                    break;
235
                case FileStatus.Unchanged:
236
                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Normal);
237
                    break;
238
            }
239

  
240
            if (state.Status == FileStatus.Deleted)
241
                NativeMethods.RaiseChangeNotification(Path.GetDirectoryName(state.Path));
242
            else
243
                NativeMethods.RaiseChangeNotification(state.Path);
244
            return state;
245
        }
246

  
247

  
248
        private WorkflowState UpdateFileChecksum(WorkflowState state)
249
        {
250
            if (state.Skip)
251
                return state;
252

  
253
            if (state.Status == FileStatus.Deleted)
254
                return state;
255

  
256
            var path = state.Path;
257
            //Skip calculation for folders
258
            if (Directory.Exists(path))
259
                return state;
260

  
261
            string hash = Signature.CalculateMD5(path);
262

  
263
            StatusKeeper.UpdateFileChecksum(path, hash);
264

  
265
            state.Hash = hash;
266
            return state;
267
        }
268

  
269
       
270

  
271

  
272
    }
273
}
b/trunk/Pithos.Core/Agents/FileInfoExtensions.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using System.Text;
5
using System.IO;
6
using System.Text.RegularExpressions;
7

  
8
namespace Pithos.Core.Agents
9
{
10
    static class FileInfoExtensions
11
    {
12
        public static  string AsRelativeTo(this FileInfo fileInfo,string path )
13
        {
14
            if (!path.EndsWith("\\"))
15
                path=path.ToLower() + "\\";
16
            int pathLength = path.Length;            
17
            
18
            var filePath = fileInfo.FullName;
19

  
20
            if (!filePath.StartsWith(path,StringComparison.InvariantCultureIgnoreCase))
21
                throw new ArgumentException(String.Format("The path {0} doesn't contain the file {1}",path,filePath));
22
            
23
            var relativePath = filePath.Substring(pathLength, filePath.Length - pathLength);
24

  
25
            return relativePath;
26
        }
27

  
28
        public static string AsRelativeUrlTo(this FileInfo fileInfo,string path )
29
        {
30

  
31
            var relativePath = fileInfo.AsRelativeTo(path);
32
            var replacedSlashes = relativePath.Replace("\\","/");
33
            var escaped = Uri.EscapeUriString(replacedSlashes);
34
            return escaped;
35
        }
36

  
37
        public static string RelativeUriToFilePath(this Uri uri)
38
        {
39
            return RelativeUrlToFilePath(uri.ToString());
40
        }
41

  
42
        public static string RelativeUrlToFilePath(this string url)
43
        {
44
            var unescaped=Uri.UnescapeDataString(url);
45
            var path = unescaped.Replace("/", "\\");
46
            return path;
47
        }
48

  
49

  
50
    }
51
}
/dev/null
1
using System;
2
using System.Collections.Generic;
3
using System.ComponentModel.Composition;
4
using System.Diagnostics;
5
using System.IO;
6
using System.Linq;
7
using System.Text;
8
using Pithos.Interfaces;
9

  
10
namespace Pithos.Core.Agents
11
{
12
    [Export]
13
    public class FileWatcherAgent
14
    {
15
        Agent<WorkflowState> _agent;
16
        private FileSystemWatcher _watcher;
17

  
18
        [Import]
19
        public IStatusKeeper StatusKeeper { get; set; }
20
        [Import]
21
        public IPithosWorkflow Workflow { get; set; }
22
        [Import]
23
        public WorkflowAgent WorkflowAgent { get; set; }
24

  
25
        public void Start(string path)
26
        {
27
            _watcher = new FileSystemWatcher(path);
28
            _watcher.Changed += OnFileEvent;
29
            _watcher.Created += OnFileEvent;
30
            _watcher.Deleted += OnFileEvent;
31
            _watcher.Renamed += OnRenameEvent;
32
            _watcher.EnableRaisingEvents = true;
33

  
34

  
35
            _agent = Agent<WorkflowState>.Start(inbox =>
36
            {
37
                Action loop = null;
38
                loop = () =>
39
                {
40
                    var message = inbox.Receive();
41
                    var process = message.ContinueWith(t =>
42
                    {
43
                        var state = t.Result;
44
                        Process(state);
45
                        inbox.DoAsync(loop);
46
                    });
47

  
48
                    process.ContinueWith(t =>
49
                    {
50
                        inbox.DoAsync(loop);
51
                        if (t.IsFaulted)
52
                        {
53
                            var ex = t.Exception.InnerException;
54
                            if (ex is OperationCanceledException)
55
                                inbox.Stop();
56
                            Trace.TraceError("[ERROR] File Event Processing:\r{0}", ex);
57
                        }
58
                    });
59

  
60
                };
61
                loop();
62
            });
63
        }
64

  
65
        public bool Pause
66
        {
67
            get { return _watcher == null || !_watcher.EnableRaisingEvents; }
68
            set
69
            {
70
                if (_watcher != null)
71
                    _watcher.EnableRaisingEvents = !value;                
72
            }
73
        }
74

  
75
        public void Post(WorkflowState workflowState)
76
        {
77
            _agent.Post(workflowState);
78
        }
79

  
80
        public void Stop()
81
        {
82
            if (_watcher != null)
83
            {
84
                _watcher.Changed -= OnFileEvent;
85
                _watcher.Created -= OnFileEvent;
86
                _watcher.Deleted -= OnFileEvent;
87
                _watcher.Renamed -= OnRenameEvent;
88
                _watcher.Dispose();
89
            }
90
            _watcher = null;
91

  
92
            _agent.Stop();
93
        }
94

  
95
        void OnFileEvent(object sender, FileSystemEventArgs e)
96
        {
97
            _agent.Post(new WorkflowState { Path = e.FullPath, FileName = e.Name, TriggeringChange = e.ChangeType });
98
        }
99

  
100
        void OnRenameEvent(object sender, RenamedEventArgs e)
101
        {
102
            _agent.Post(new WorkflowState
103
            {
104
                OldPath = e.OldFullPath,
105
                OldFileName = e.OldName,
106
                Path = e.FullPath,
107
                FileName = e.Name,
108
                TriggeringChange = e.ChangeType
109
            });
110
        }
111

  
112

  
113
        private void Process(WorkflowState state)
114
        {
115
            var networkState = StatusKeeper.GetNetworkState(state.Path);
116
            //Skip if the file is already being downloaded or uploaded and 
117
            //the change is create or modify
118
            if (networkState != NetworkState.None &&
119
                (
120
                    state.TriggeringChange == WatcherChangeTypes.Created ||
121
                    state.TriggeringChange == WatcherChangeTypes.Changed
122
                ))
123
                return;
124
            UpdateFileStatus(state);
125
            UpdateOverlayStatus(state);
126
            UpdateFileChecksum(state);
127
            WorkflowAgent.Post(state);
128
        }
129

  
130
        private Dictionary<WatcherChangeTypes, FileStatus> _statusDict = new Dictionary<WatcherChangeTypes, FileStatus>
131
        {
132
            {WatcherChangeTypes.Created,FileStatus.Created},
133
            {WatcherChangeTypes.Changed,FileStatus.Modified},
134
            {WatcherChangeTypes.Deleted,FileStatus.Deleted},
135
            {WatcherChangeTypes.Renamed,FileStatus.Renamed}
136
        };
137

  
138
        private WorkflowState UpdateFileStatus(WorkflowState state)
139
        {
140
            string path = state.Path;
141
            FileStatus status = _statusDict[state.TriggeringChange];
142
            var oldStatus = Workflow.StatusKeeper.GetFileStatus(path);
143
            if (status == oldStatus)
144
            {
145
                state.Status = status;
146
                state.Skip = true;
147
                return state;
148
            }
149
            if (state.Status == FileStatus.Renamed)
150
                Workflow.ClearFileStatus(path);
151

  
152
            state.Status = Workflow.SetFileStatus(path, status);
153
            return state;
154
        }
155

  
156
        private WorkflowState UpdateOverlayStatus(WorkflowState state)
157
        {
158
            if (state.Skip)
159
                return state;
160

  
161
            switch (state.Status)
162
            {
163
                case FileStatus.Created:
164
                case FileStatus.Modified:
165
                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
166
                    break;
167
                case FileStatus.Deleted:
168
                    this.StatusKeeper.RemoveFileOverlayStatus(state.Path);
169
                    break;
170
                case FileStatus.Renamed:
171
                    this.StatusKeeper.RemoveFileOverlayStatus(state.OldPath);
172
                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
173
                    break;
174
                case FileStatus.Unchanged:
175
                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Normal);
176
                    break;
177
            }
178

  
179
            if (state.Status == FileStatus.Deleted)
180
                Workflow.RaiseChangeNotification(Path.GetDirectoryName(state.Path));
181
            else
182
                Workflow.RaiseChangeNotification(state.Path);
183
            return state;
184
        }
185

  
186

  
187
        private WorkflowState UpdateFileChecksum(WorkflowState state)
188
        {
189
            if (state.Skip)
190
                return state;
191

  
192
            if (state.Status == FileStatus.Deleted)
193
                return state;
194

  
195
            string path = state.Path;
196
            string hash = Signature.CalculateMD5(path);
197

  
198
            StatusKeeper.UpdateFileChecksum(path, hash);
199

  
200
            state.Hash = hash;
201
            return state;
202
        }
203

  
204
       
205

  
206

  
207
    }
208
}
b/trunk/Pithos.Core/NetworkGate.cs
1
using System;
2
using System.Collections.Concurrent;
3
using System.Collections.Generic;
4
using System.Diagnostics.Contracts;
5
using System.IO;
6

  
7
namespace Pithos.Core
8
{
9
    public enum NetworkOperation
10
    {
11
        None,
12
        Uploading,
13
        Downloading
14
    }
15

  
16
    //The NetworkGate prevents starting download/uploads for files that are already in the process of downloading,
17
    //uploading.   
18
    public class NetworkGate:IDisposable
19
    {
20
        //The state of each file is stored in a thread-safe dictionary
21
        static readonly ConcurrentDictionary<string, NetworkOperation> NetworkState = new ConcurrentDictionary<string, NetworkOperation>();
22

  
23
        public static NetworkOperation GetNetworkState(string path)
24
        {
25
            if (String.IsNullOrWhiteSpace(path))
26
                throw new ArgumentNullException("path");
27
            if (!Path.IsPathRooted(path))
28
                throw new ArgumentException("path must be a rooted path", "path");
29
            Contract.EndContractBlock();
30

  
31
            NetworkOperation operation;
32
            if (NetworkState.TryGetValue(path.ToLower(), out operation))
33
                return operation;
34
            return NetworkOperation.None;
35
        }
36

  
37
        //Store a network operation for the specified path
38
        public static void SetNetworkState(string path, NetworkOperation operation)
39
        {
40
            if (String.IsNullOrWhiteSpace(path))
41
                throw new ArgumentNullException("path");
42
            if (!Path.IsPathRooted(path))
43
                throw new ArgumentException("path must be a rooted path", "path");
44
            Contract.EndContractBlock();
45

  
46
            var lower = path.ToLower();
47
            NetworkState[lower] = operation;            
48
            //By default, None values don't need to be stored. They are stored anyway
49
            //because TryRemove may fail.
50
            if (operation == NetworkOperation.None)
51
            {
52
                NetworkOperation oldOperation;
53
                NetworkState.TryRemove(lower, out oldOperation);
54
            }
55
        }
56

  
57
        //Clients should acquire a NetworkGate before starting any network operation.
58
        //If Acquire fails, another network operation is already in progress
59
        public static NetworkGate Acquire(string path, NetworkOperation operation)
60
        {
61
            if (String.IsNullOrWhiteSpace(path))
62
                throw new ArgumentNullException("path");
63
            if (!Path.IsPathRooted(path))
64
                throw new ArgumentException("path must be a rooted path", "path");
65
            Contract.EndContractBlock();
66

  
67
            var state = GetNetworkState(path);
68
            //If no operation is in progress, return a NetworkGate
69
            return (state == NetworkOperation.None)
70
                       ? new NetworkGate(path, operation)
71
                   //otherwise return a gate with Fail flagged
72
                       : new NetworkGate(path, NetworkOperation.None);
73
        }
74

  
75

  
76

  
77
        public string FilePath { get; private set; }
78
        public NetworkOperation Operation { get; private set; }
79

  
80
        private NetworkGate(string path,NetworkOperation operation)
81
        {
82
            if (String.IsNullOrWhiteSpace(path))
83
                throw new ArgumentNullException("path");
84
            Contract.EndContractBlock();
85

  
86
            Operation = operation;
87
            FilePath = path.ToLower();
88

  
89
            //Skip dummy operations (those with Operation == None)
90
            if (Operation != NetworkOperation.None)
91
                //and store the file's operation
92
                SetNetworkState(FilePath, operation);
93
        }
94

  
95
        //A NetworkGate has Failed if its operation is None
96
        public bool Failed { get { return Operation == NetworkOperation.None; } }
97

  
98
        //Release a gate by setting the NetworkOperation to None
99
        public void Release()
100
        {
101
            //Skip Failed flags
102
            if (!Failed)
103
                //And reset the operation state for the file
104
                SetNetworkState(FilePath,NetworkOperation.None);
105
        }
106

  
107

  
108
        public void Dispose()
109
        {
110
            Dispose(true);
111
            GC.SuppressFinalize(this);
112
        }
113

  
114
        protected void Dispose(bool disposing)
115
        {
116
            if (disposing)
117
            {
118
                Release();
119
            }
120
        }
121

  
122
    }
123
}
/dev/null
1
using System;
2
using System.Collections.Concurrent;
3
using System.Collections.Generic;
4
using System.Diagnostics.Contracts;
5
using System.IO;
6
using System.Runtime.Remoting.Metadata.W3cXsd2001;
7
using System.Security.Cryptography;
8
using System.Text;
9
using System.Threading.Tasks;
10
using System.Linq;
11

  
12
namespace Pithos.Core
13
{
14
    public static class Signature
15
    {
16
        public static string CalculateMD5(string path)
17
        {
18
            if (String.IsNullOrWhiteSpace(path))
19
                throw new ArgumentNullException("path");
20
            Contract.EndContractBlock();
21

  
22
            string hash;
23
            using (var hasher = MD5.Create())
24
            using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true))
25
            {
26
                var hashBytes = hasher.ComputeHash(stream);
27
                hash = hashBytes.ToHashString();
28
            }
29
            return hash;
30
        }
31

  
32
/*
33
        public static string BytesToString(byte[] hashBytes)
34
        {
35
            var shb=new SoapHexBinary(hashBytes);
36
            return shb.ToString();
37
            
38
        }
39

  
40

  
41
        public static byte[] StringToBytes(string hash)
42
        {
43
            var shb=SoapHexBinary.Parse(hash);
44
            return shb.Value;
45
        }
46
*/
47

  
48
        public static byte[] ToBytes(this string hash)
49
        {
50
            var shb = SoapHexBinary.Parse(hash);
51
            return shb.Value;
52
        }
53

  
54
        public static string ToHashString(this byte[] hashBytes)
55
        {
56
            var shb = new SoapHexBinary(hashBytes);
57
            return shb.ToString();
58
        }
59

  
60

  
61
        /// <summary>
62
        /// Calculates a file's tree hash synchronously, using the specified block size
63
        /// </summary>
64
        /// <param name="filePath">Path to an existing file</param>
65
        /// <param name="blockSize">Block size used to calculate leaf hashes</param>
66
        /// <param name="algorithm"></param>
67
        /// <returns>A <see cref="TreeHash"/> with the block hashes and top hash</returns>
68
        public static TreeHash CalculateTreeHash(string filePath, int blockSize, string algorithm)
69
        {
70
            if (String.IsNullOrWhiteSpace(filePath))
71
                throw new ArgumentNullException("filePath");
72
            if (blockSize<=0)
73
                throw new ArgumentOutOfRangeException("blockSize","blockSize must be a value greater than zero ");
74
            if (String.IsNullOrWhiteSpace(algorithm))
75
                throw new ArgumentNullException("algorithm");
76
            Contract.EndContractBlock();
77
            
78
            var list = new List<byte[]>();
79
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, blockSize, false))
80
            using (var hasher = HashAlgorithm.Create(algorithm))
81
            {
82
                int read;
83
                var buffer = new byte[blockSize];            
84
                while ((read = stream.Read(buffer, 0, blockSize)) > 0)
85
                {
86
                    var hash = hasher.ComputeHash(buffer, 0, read);
87
                    list.Add(hash);
88
                }
89
                return new TreeHash(algorithm) { Hashes = list,                    
90
                    BlockSize = blockSize, 
91
                    Bytes = stream.Length};
92
            }            
93
        }
94

  
95

  
96
        public static Task<TreeHash> CalculateTreeHashAsync(string path, int blockSize,string algorithm)
97
        {
98
            if (String.IsNullOrWhiteSpace(path))
99
                throw new ArgumentNullException("path");
100
            if (blockSize <= 0)
101
                throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
102
            if (String.IsNullOrWhiteSpace(algorithm))
103
                throw new ArgumentNullException("algorithm");
104
            Contract.EndContractBlock();
105

  
106
            var hashes = new ConcurrentDictionary<int, byte[]>();
107
            var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, blockSize, true);
108

  
109
            return CalculateBlockHashesAsync(stream, blockSize, algorithm,hashes)
110
                .ContinueWith(t => {
111
                                        var length = stream.Length;
112
                                       stream.Close();
113
                                       var list= t.Result.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList();                                       
114
                                       return new TreeHash(algorithm) { Hashes = list,                                           
115
                                           BlockSize = blockSize, 
116
                                           Bytes = length };
117
                });
118
        }
119

  
120
        public static byte[] CalculateTopHash(IEnumerable<byte[]> hashMap, string algorithm)
121
        {
122
            if (hashMap == null)
123
                throw new ArgumentNullException("hashMap");
124
            if (String.IsNullOrWhiteSpace(algorithm))
125
                throw new ArgumentNullException("algorithm");
126
            Contract.EndContractBlock();
127

  
128
            using (var hasher = HashAlgorithm.Create(algorithm))
129
            {
130
                var i = 0;
131
                var count = hashMap.Count();
132
                foreach (var block in hashMap)
133
                {
134
                    if (i++ != count - 1)
135
                        hasher.TransformBlock(block, 0, block.Length, null, 0);
136
                    else
137
                        hasher.TransformFinalBlock(block, 0, block.Length);
138
                }
139

  
140
                var finalHash = hasher.Hash;
141

  
142
                return finalHash;
143
            }
144
        }
145

  
146
        private static Task<ConcurrentDictionary<int, byte[]>> CalculateBlockHashesAsync(FileStream stream, int blockSize, string algorithm, ConcurrentDictionary<int, byte[]> hashes, int index = 0)
147
        {
148
            if (stream==null)
149
                throw new ArgumentNullException("stream");
150
            if (hashes==null)
151
                throw new ArgumentNullException("hashes");
152
            if (String.IsNullOrWhiteSpace(algorithm))
153
                throw new ArgumentNullException("algorithm");
154
            if (blockSize <= 0)
155
                throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
156
            if (index< 0)
157
                throw new ArgumentOutOfRangeException("index", "index must be a non-negative value");
158
            Contract.EndContractBlock();
159

  
160

  
161
            var buffer = new byte[blockSize];
162
            return stream.ReadAsync(buffer, 0, blockSize).ContinueWith(t =>
163
            {
164
                var read = t.Result;
165

  
166
                var nextTask = read == blockSize
167
                                    ? CalculateBlockHashesAsync(stream, blockSize, algorithm, hashes, index + 1) 
168
                                    : Task.Factory.StartNew(() => hashes);
169

  
170
                using (var hasher = HashAlgorithm.Create(algorithm))
171
                {
172
                    var hash = hasher.ComputeHash(buffer, 0, read);
173
                    hashes[index]=hash;
174
                }
175
                return nextTask;
176
            }).Unwrap();
177
        }
178

  
179
    
180

  
181
        public static byte[] CalculateHash(byte[] buffer,string algorithm)
182
        {
183
            if (buffer == null)
184
                throw new ArgumentNullException("buffer");
185
            if (String.IsNullOrWhiteSpace(algorithm))
186
                throw new ArgumentNullException("algorithm");
187
            Contract.EndContractBlock();
188

  
189
            using (var hasher = HashAlgorithm.Create(algorithm))
190
            {
191
                var hash = hasher.ComputeHash(buffer, 0, buffer.Length);
192
                return hash;
193
            }        
194
        }
195
    }
196
}
197

  
198

  
199
  
/dev/null
1
using System;
2
using System.Collections.Generic;
3
using System.Diagnostics.Contracts;
4
using System.Linq;
5
using System.Text;
6
using System.Threading.Tasks;
7

  
8
namespace Pithos.Interfaces
9
{
10
    [ContractClass(typeof(ICloudClientContract))]
11
    public interface ICloudClient
12
    {
13
        string ApiKey { get; set; }
14
        string UserName { get; set; }
15
        Uri StorageUrl { get; set; }
16
        string Token { get; set; }
17
        bool UsePithos { get; set; }
18
        void Authenticate(string userName,string apiKey);
19
        Uri Proxy { get; set; }
20
        double DownloadPercentLimit { get; set; }
21
        double UploadPercentLimit { get; set; }
22
        string AuthenticationUrl { get; set; }
23

  
24

  
25
        IList<ContainerInfo> ListContainers();
26
        IList<ObjectInfo> ListObjects(string container,DateTime? since=null);
27
        IList<ObjectInfo> ListObjects(string container, string folder, DateTime? since = null); 
28
        bool ContainerExists(string container);
29
        ContainerInfo GetContainerInfo(string container);
30
        void CreateContainer(string container);
31
        void DeleteContainer(string container);
32
        
33
        Task GetObject(string container, string objectName, string fileName);
34
        Task PutObject(string container, string objectName, string fileName, string hash = null);
35
        void DeleteObject(string container, string objectName);
36
        void MoveObject(string sourceContainer, string oldObjectName, string targetContainer,string newObjectName);
37
        bool ObjectExists(string container,string objectName);
38
        ObjectInfo GetObjectInfo(string container, string objectName);
39
        void CreateFolder(string container, string folder);
40

  
41

  
42
    }
43

  
44

  
45
    [ContractClassFor(typeof(ICloudClient))]
46
    public abstract class ICloudClientContract:ICloudClient
47
    {
48
        public string ApiKey { get; set; }
49
        public string UserName { get; set; }
50
        public Uri StorageUrl { get; set; }
51
        public string Token { get; set; }
52
        public Uri Proxy { get; set; }
53
        public double DownloadPercentLimit { get; set; }
54
        public double UploadPercentLimit { get; set; }
55

  
56
        public string AuthenticationUrl { get; set; }
57

  
58
        public bool UsePithos { get; set; }
59

  
60
        public void Authenticate(string userName, string apiKey)
61
        {
62
            Contract.Requires<ArgumentNullException>(!String.IsNullOrWhiteSpace(apiKey), "ApiKey must be filled before calling Authenticate");
63
            Contract.Requires<ArgumentNullException>(!String.IsNullOrWhiteSpace(userName), "UserName must be filled before calling Authenticate");
64

  
65

  
66
            Contract.Ensures(apiKey==ApiKey);
67
            Contract.Ensures(userName==UserName);
68
            Contract.Ensures(StorageUrl!=null);
69
            Contract.Ensures(!String.IsNullOrWhiteSpace(Token));
70
            
71
        }
72

  
73
        public IList<ContainerInfo> ListContainers()
74
        {
75
            Contract.Requires(!String.IsNullOrWhiteSpace(Token));
76
            Contract.Requires(StorageUrl!=null);            
77

  
78
            return default(IList<ContainerInfo>);
79
        }
80

  
81
        public IList<ObjectInfo> ListObjects(string container, DateTime? since = null)
82
        {
83
            Contract.Requires(!String.IsNullOrWhiteSpace(Token));
84
            Contract.Requires(StorageUrl != null);
85
            Contract.Requires(!String.IsNullOrWhiteSpace(container));
86

  
87
            return default(IList<ObjectInfo>);
88
        }
89

  
90
        public IList<ObjectInfo> ListObjects(string container, string folder, DateTime? since = null)
91
        {
92
            Contract.Requires(!String.IsNullOrWhiteSpace(Token));
93
            Contract.Requires(StorageUrl != null);
94
            Contract.Requires(!String.IsNullOrWhiteSpace(container));
95
            Contract.Requires(!String.IsNullOrWhiteSpace(folder));
96

  
97
            return default(IList<ObjectInfo>);
98
        }
99

  
100
        public bool ContainerExists(string container)
101
        {
102
            Contract.Requires(!String.IsNullOrWhiteSpace(Token));
103
            Contract.Requires(StorageUrl!=null);
104
            Contract.Requires(!String.IsNullOrWhiteSpace(container));
105

  
106
            return default(bool);
107
        }
108

  
109
        public ContainerInfo GetContainerInfo(string container)
110
        {
111
            Contract.Requires(!String.IsNullOrWhiteSpace(Token));
112
            Contract.Requires(StorageUrl!=null);
113
            Contract.Requires(!String.IsNullOrWhiteSpace(container));
114

  
115
            return default(ContainerInfo);
116
        }
117

  
118
        public void CreateContainer(string container)
119
        {
120
            Contract.Requires(!String.IsNullOrWhiteSpace(Token));
121
            Contract.Requires(StorageUrl!=null);
122
            Contract.Requires(!String.IsNullOrWhiteSpace(container));
123
        }
124

  
125
        public void DeleteContainer(string container)
126
        {
127
            Contract.Requires(!String.IsNullOrWhiteSpace(Token));
128
            Contract.Requires(StorageUrl!=null);
129
            Contract.Requires(!String.IsNullOrWhiteSpace(container));
130
        }
131

  
132
        public Task GetObject(string container, string objectName, string fileName)
133
        {
134
            Contract.Requires(!String.IsNullOrWhiteSpace(Token));
135
            Contract.Requires(StorageUrl!=null);
136
            Contract.Requires(!String.IsNullOrWhiteSpace(container));
137
            Contract.Requires(!String.IsNullOrWhiteSpace(objectName));
138

  
139
            return default(Task);
140
        }
141

  
142
        public Task PutObject(string container, string objectName, string fileName, string hash = null)
143
        {
144
            Contract.Requires(!String.IsNullOrWhiteSpace(Token));
145
            Contract.Requires(StorageUrl!=null);
146
            Contract.Requires(!String.IsNullOrWhiteSpace(container));
147
            Contract.Requires(!String.IsNullOrWhiteSpace(fileName));                        
148
            Contract.Requires(!String.IsNullOrWhiteSpace(objectName));
149

  
150
            return default(Task);
151
        }
152

  
153
        public void DeleteObject(string container, string objectName)
154
        {
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff