Added Clear Conflict action to resolver
[pithos-ms-client] / trunk / Pithos.Interfaces / ObjectInfo.cs
1 #region
2 /* -----------------------------------------------------------------------
3  * <copyright file="ObjectInfo.cs" company="GRNet">
4  * 
5  * Copyright 2011-2012 GRNET S.A. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or
8  * without modification, are permitted provided that the following
9  * conditions are met:
10  *
11  *   1. Redistributions of source code must retain the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer.
14  *
15  *   2. Redistributions in binary form must reproduce the above
16  *      copyright notice, this list of conditions and the following
17  *      disclaimer in the documentation and/or other materials
18  *      provided with the distribution.
19  *
20  *
21  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * The views and conclusions contained in the software and
35  * documentation are those of the authors and should not be
36  * interpreted as representing official policies, either expressed
37  * or implied, of GRNET S.A.
38  * </copyright>
39  * -----------------------------------------------------------------------
40  */
41 #endregion
42 using System;
43 using System.Collections.Generic;
44 using System.Diagnostics;
45 using System.Diagnostics.Contracts;
46 using System.Dynamic;
47 using System.Globalization;
48 using System.IO;
49 using System.Linq;
50 using System.Text;
51 using Newtonsoft.Json;
52
53 namespace Pithos.Interfaces
54 {
55     [DebuggerDisplay("Name {Name}")]
56     public class ObjectInfo//:DynamicObject 
57     {
58         private readonly List<string> _knownContainers= new List<string>{"trash"};
59         public string Name { get; set; }
60
61         public string ETag { get; set; }
62
63         public string Hash { get; set; }
64
65         public string X_Object_Hash { get { return Hash; } set { Hash = value; } }
66
67         [JsonProperty("x_object_uuid")]
68         public string UUID { get; set; }
69
70         public long Bytes { get; set; }
71         public string Content_Type { get; set; }
72         public DateTime Last_Modified { get; set; }
73
74         private Dictionary<string, string> _tags=new Dictionary<string, string>();
75         public Dictionary<string, string> Tags
76         {
77             get { return _tags; }
78             set { _tags = value; }
79         }
80
81         private Dictionary<string, string> _extensions=new Dictionary<string, string>();
82         public Dictionary<string, string> Extensions
83         {
84             get { return _extensions; }
85             set
86             {
87                 _extensions = value;
88                 ExtractKnownExtensions();
89             }
90         }
91         
92         
93         private Dictionary<string, string> _permissions=new Dictionary<string, string>();
94         [JsonProperty("x_object_sharing")]
95         [JsonConverter(typeof(PermissionConverter))]
96         public Dictionary<string, string> Permissions
97         {
98             get { return _permissions; }
99             set
100             {
101                 _permissions = value;                
102             }
103         }
104
105         /// <summary>
106         /// Version number
107         /// </summary>
108         [JsonProperty("x_object_version")]
109         public long? Version { get; set; }
110
111
112         /// <summary>
113         /// Shared object permissions can be Read or Write
114         /// </summary>
115         [JsonProperty("x_object_allowed_to")]
116         public string AllowedTo { get; set; }
117
118
119         /// <summary>
120         /// Version timestamp
121         /// </summary>
122         [JsonProperty("X_Object_Version_Timestamp"), JsonConverter(typeof(PithosDateTimeConverter))]
123         public DateTime? VersionTimestamp { get; set; }
124
125         [JsonProperty("X_Object_Modified_By")]
126         public string ModifiedBy { get; set; }
127
128
129         public Stream Stream { get; set; }
130
131
132         public Uri StorageUri { get; set; }
133
134         public string Account { get; set; }
135
136         public string Container { get; set; }
137
138         public Uri Uri
139         {
140             get
141             {
142                 var relativeUrl=String.Format("{0}/{1}/{2}",Account, Container,Name);
143                 return StorageUri==null 
144                     ? new Uri(relativeUrl,UriKind.Relative) 
145                     : new Uri(StorageUri, relativeUrl);
146             }
147         }
148
149         public string ContendDisposition { get; set; }
150
151         public string ContentEncoding { get; set; }
152
153         public string Manifest { get; set; }
154         
155         public bool IsPublic
156         {
157             get { return !String.IsNullOrWhiteSpace(PublicUrl); }
158             set
159             {
160                 if (!value)
161                     PublicUrl = null;
162                 else if (String.IsNullOrWhiteSpace(PublicUrl))
163                     PublicUrl="true";                
164             }
165         }
166
167         [JsonProperty("X_Object_Public")]
168         public string PublicUrl { get; set; }
169
170         public string PreviousHash { get; set; }
171
172         public ObjectInfo()
173         {}
174
175         public ObjectInfo(string accountPath,string accountName,FileSystemInfo fileInfo)
176         {
177             if (String.IsNullOrWhiteSpace(accountPath))
178                 throw new ArgumentNullException("accountPath");
179             if (string.IsNullOrWhiteSpace(accountName))
180                 throw new ArgumentNullException("accountName");
181             if (fileInfo == null)
182                 throw new ArgumentNullException("fileInfo");
183             Contract.EndContractBlock();
184
185             var relativeUrl = fileInfo.WithProperCapitalization().AsRelativeUrlTo(accountPath);
186             //The first part of the URL is the container
187             var slashIndex = relativeUrl.IndexOf('/');
188             var container = relativeUrl.Substring(0, slashIndex);
189             //The second is the file's url relative to the container
190             var fileUrl = relativeUrl.Substring(slashIndex + 1);
191
192             Account = accountName;
193             Container = container;
194             Name = fileUrl; 
195         }
196
197
198         private void ExtractKnownExtensions()
199         {
200             Version=GetLong(KnownExtensions.X_Object_Version);
201             VersionTimestamp = GetTimestamp(KnownExtensions.X_Object_Version_Timestamp);
202             ModifiedBy = GetString(KnownExtensions.X_Object_Modified_By);
203         }
204
205         private string GetString(string name)
206         {            
207             string value;
208             _extensions.TryGetValue(name, out value);
209             return value ;                        
210         }
211         
212         private long? GetLong(string name)
213         {
214             string version;
215             long value;
216             return _extensions.TryGetValue(name, out version) && long.TryParse(version, out value)
217                        ? (long?) value
218                        : null;
219         }
220
221         private DateTime? GetTimestamp(string name)
222         {
223             string version;
224             DateTime value;
225             if (_extensions.TryGetValue(name, out version) && 
226                 DateTime.TryParse(version,CultureInfo.InvariantCulture,DateTimeStyles.AdjustToUniversal, out value))
227             {
228                 return value;
229             }
230             return null;
231         }
232
233
234         public static ObjectInfo Empty = new ObjectInfo
235         {
236             Name = String.Empty,
237             Hash = String.Empty,
238             Bytes = 0,
239             Content_Type = String.Empty,
240             Last_Modified = DateTime.MinValue,
241             Exists=false
242         };
243
244         private bool _exists=true;
245
246         public bool Exists
247         {
248             get {
249                 return _exists;
250             }
251             set {
252                 _exists = value;
253             }
254         }
255
256
257         public string RelativeUrlToFilePath(string currentAccount)
258         {
259             if (Name==null)
260                 throw new InvalidOperationException("Name can't be null");
261             if (String.IsNullOrWhiteSpace(currentAccount))
262                 throw new ArgumentNullException("currentAccount");
263             Contract.EndContractBlock();
264
265             if (this == Empty)
266                 return String.Empty;
267
268             var unescaped = Uri.UnescapeDataString(Name);
269             var path = unescaped.Replace("/", "\\");
270             var pathParts=new Stack<string>();
271             pathParts.Push(path);
272             if (!String.IsNullOrWhiteSpace(Container) && !_knownContainers.Contains(Container))
273                 pathParts.Push(Container);
274             if (!currentAccount.Equals(Account, StringComparison.InvariantCultureIgnoreCase))
275             {
276                 if (Account != null)
277                 {
278                     pathParts.Push(Account);
279                     pathParts.Push(FolderConstants.OthersFolder);
280                 }
281             }
282             var finalPath=Path.Combine(pathParts.ToArray());
283             return finalPath;
284         }
285
286 /*
287         public override bool TrySetMember(SetMemberBinder binder, object value)
288         {
289             if (binder.Name.StartsWith("x_object_meta"))
290             {
291                 Tags[binder.Name] = value.ToString();
292             }
293             return false;
294         }
295 */
296
297         public string GetPermissionString()
298         {
299             if (Permissions==null)
300                 throw new InvalidOperationException();
301             Contract.EndContractBlock();
302
303             if (Permissions.Count == 0)
304                 return "~";
305             var permissionBuilder = new StringBuilder();
306             var groupings = Permissions.GroupBy(pair => pair.Value.Trim(), pair => pair.Key.Trim());
307             foreach (var grouping in groupings)
308             {
309                 permissionBuilder.AppendFormat("{0}={1};", grouping.Key, String.Join(",", grouping));
310             }
311             var permissions = Uri.EscapeDataString(permissionBuilder.ToString().Trim(';'));
312             return permissions;
313         }
314
315         public void SetPermissions(string permissions)
316         {
317             if (String.IsNullOrWhiteSpace(permissions))
318                 return;
319             
320             Permissions = PermissionConverter.ParsePermissions(permissions);
321         }
322
323         //The previous values that correspond to a NoModification object
324         //have the same account, container and possibly the same folder
325         public bool CorrespondsTo(ObjectInfo other)
326         {
327             return other.Account == this.Account
328                    && other.Container == this.Container
329                    && (this.Name == null || other.Name.StartsWith(this.Name));
330         }
331
332         public bool IsWritable(string account)
333         {
334             //If the Allowed To header has no value, try to determine the Share permissions
335             if (AllowedTo == null)
336             {
337                 //If this file has no permissions defined, we can probably write it
338                 //This case should occur only when the info comes from a listing of the user's own files
339                 if (Permissions == null || Permissions.Count == 0)
340                     return true;
341                 string perms;
342
343                 //Do we have an explicit write share to this account?
344                 return Permissions.TryGetValue(account, out perms) 
345                     && perms.Equals("write",StringComparison.InvariantCultureIgnoreCase);
346                 
347             }
348             //Otherwise return the permissions specified by AllowedTo
349             return AllowedTo.Equals("write",StringComparison.InvariantCultureIgnoreCase) ;
350         }
351
352         public ObjectInfo Previous { get; private set; }
353
354         public bool IsDirectory
355         {
356             get
357             {
358                 if (Content_Type == null)
359                     return false;
360                 if (Content_Type.StartsWith(@"application/directory",StringComparison.InvariantCultureIgnoreCase))
361                     return true;
362                 if (Content_Type.StartsWith(@"application/folder",StringComparison.InvariantCultureIgnoreCase))
363                     return true;
364                 return false;
365             }
366         }
367
368         public Uri AccountKey
369         {
370             get { return new Uri(StorageUri,"../" + Account); }
371         }
372
373         public ObjectInfo SetPrevious(ObjectInfo previous)
374         {            
375             Previous = previous;
376             PreviousHash = previous.Hash;
377             return this;
378         }
379
380         public bool? IsShared
381         {
382             get
383             {
384                 if (Uri == null || StorageUri == null)
385                     return null;
386                 var isShared = !Uri.ToString().StartsWith(StorageUri.ToString());
387                 return isShared;
388             }
389         }
390     }
391 }