cccd13aa02e8d0116f1d117a9cd9bd2e034581ea
[pithos-ms-client] / trunk%2FPithos.Interfaces%2FObjectInfo.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         
62         public string Hash { get; set; }
63
64         public string X_Object_Hash { get { return Hash; } set { Hash = value; } }
65
66         [JsonProperty("x_object_uuid")]
67         public string UUID { get; set; }
68
69         public long Bytes { get; set; }
70         public string Content_Type { get; set; }
71         public DateTime Last_Modified { get; set; }
72
73         private Dictionary<string, string> _tags=new Dictionary<string, string>();
74         public Dictionary<string, string> Tags
75         {
76             get { return _tags; }
77             set { _tags = value; }
78         }
79
80         private Dictionary<string, string> _extensions=new Dictionary<string, string>();
81         public Dictionary<string, string> Extensions
82         {
83             get { return _extensions; }
84             set
85             {
86                 _extensions = value;
87                 ExtractKnownExtensions();
88             }
89         }
90         
91         
92         private Dictionary<string, string> _permissions=new Dictionary<string, string>();
93         [JsonProperty("x_object_sharing")]
94         [JsonConverter(typeof(PermissionConverter))]
95         public Dictionary<string, string> Permissions
96         {
97             get { return _permissions; }
98             set
99             {
100                 _permissions = value;                
101             }
102         }
103
104         /// <summary>
105         /// Version number
106         /// </summary>
107         [JsonProperty("x_object_version")]
108         public long? Version { get; set; }
109
110
111         /// <summary>
112         /// Shared object permissions can be Read or Write
113         /// </summary>
114         [JsonProperty("x_object_allowed_to")]
115         public string AllowedTo { get; set; }
116
117
118         /// <summary>
119         /// Version timestamp
120         /// </summary>
121         [JsonProperty("X_Object_Version_Timestamp"), JsonConverter(typeof(PithosDateTimeConverter))]
122         public DateTime? VersionTimestamp { get; set; }
123
124         [JsonProperty("X_Object_Modified_By")]
125         public string ModifiedBy { get; set; }
126
127
128         public Stream Stream { get; set; }
129
130
131         public Uri StorageUri { get; set; }
132
133         public string Account { get; set; }
134
135         public string Container { get; set; }
136
137         public Uri Uri
138         {
139             get
140             {
141                 var relativeUrl=String.Format("{0}/{1}/{2}",Account, Container,Name);
142                 return StorageUri==null 
143                     ? new Uri(relativeUrl,UriKind.Relative) 
144                     : new Uri(StorageUri, relativeUrl);
145             }
146         }
147
148         public string ContendDisposition { get; set; }
149
150         public string ContentEncoding { get; set; }
151
152         public string Manifest { get; set; }
153         
154         public bool IsPublic
155         {
156             get { return !String.IsNullOrWhiteSpace(PublicUrl); }
157             set
158             {
159                 if (!value)
160                     PublicUrl = null;
161                 else if (String.IsNullOrWhiteSpace(PublicUrl))
162                     PublicUrl="true";                
163             }
164         }
165
166         [JsonProperty("X_Object_Public")]
167         public string PublicUrl { get; set; }
168
169         public string PreviousHash { get; set; }
170
171         public ObjectInfo()
172         {}
173
174         public ObjectInfo(string accountPath,string accountName,FileSystemInfo fileInfo)
175         {
176             if (String.IsNullOrWhiteSpace(accountPath))
177                 throw new ArgumentNullException("accountPath");
178             if (string.IsNullOrWhiteSpace(accountName))
179                 throw new ArgumentNullException("accountName");
180             if (fileInfo == null)
181                 throw new ArgumentNullException("fileInfo");
182             Contract.EndContractBlock();
183
184             var relativeUrl = fileInfo.WithProperCapitalization().AsRelativeUrlTo(accountPath);
185             //The first part of the URL is the container
186             var slashIndex = relativeUrl.IndexOf('/');
187             var container = relativeUrl.Substring(0, slashIndex);
188             //The second is the file's url relative to the container
189             var fileUrl = relativeUrl.Substring(slashIndex + 1);
190
191             Account = accountName;
192             Container = container;
193             Name = fileUrl; 
194         }
195
196
197         private void ExtractKnownExtensions()
198         {
199             Version=GetLong(KnownExtensions.X_Object_Version);
200             VersionTimestamp = GetTimestamp(KnownExtensions.X_Object_Version_Timestamp);
201             ModifiedBy = GetString(KnownExtensions.X_Object_Modified_By);
202         }
203
204         private string GetString(string name)
205         {            
206             string value;
207             _extensions.TryGetValue(name, out value);
208             return value ;                        
209         }
210         
211         private long? GetLong(string name)
212         {
213             string version;
214             long value;
215             return _extensions.TryGetValue(name, out version) && long.TryParse(version, out value)
216                        ? (long?) value
217                        : null;
218         }
219
220         private DateTime? GetTimestamp(string name)
221         {
222             string version;
223             DateTime value;
224             if (_extensions.TryGetValue(name, out version) && 
225                 DateTime.TryParse(version,CultureInfo.InvariantCulture,DateTimeStyles.AdjustToUniversal, out value))
226             {
227                 return value;
228             }
229             return null;
230         }
231
232
233         public static ObjectInfo Empty = new ObjectInfo
234         {
235             Name = String.Empty,
236             Hash = String.Empty,
237             Bytes = 0,
238             Content_Type = String.Empty,
239             Last_Modified = DateTime.MinValue,
240             Exists=false
241         };
242
243         private bool _exists=true;
244
245         public bool Exists
246         {
247             get {
248                 return _exists;
249             }
250             set {
251                 _exists = value;
252             }
253         }
254
255
256         public string RelativeUrlToFilePath(string currentAccount)
257         {
258             if (Name==null)
259                 throw new InvalidOperationException("Name can't be null");
260             if (String.IsNullOrWhiteSpace(currentAccount))
261                 throw new ArgumentNullException("currentAccount");
262             Contract.EndContractBlock();
263
264             if (this == Empty)
265                 return String.Empty;
266
267             var unescaped = Uri.UnescapeDataString(Name);
268             var path = unescaped.Replace("/", "\\");
269             var pathParts=new Stack<string>();
270             pathParts.Push(path);
271             if (!String.IsNullOrWhiteSpace(Container) && !_knownContainers.Contains(Container))
272                 pathParts.Push(Container);
273             if (!currentAccount.Equals(Account, StringComparison.InvariantCultureIgnoreCase))
274             {
275                 if (Account != null)
276                 {
277                     pathParts.Push(Account);
278                     pathParts.Push(FolderConstants.OthersFolder);
279                 }
280             }
281             var finalPath=Path.Combine(pathParts.ToArray());
282             return finalPath;
283         }
284
285 /*
286         public override bool TrySetMember(SetMemberBinder binder, object value)
287         {
288             if (binder.Name.StartsWith("x_object_meta"))
289             {
290                 Tags[binder.Name] = value.ToString();
291             }
292             return false;
293         }
294 */
295
296         public string GetPermissionString()
297         {
298             if (Permissions==null)
299                 throw new InvalidOperationException();
300             Contract.EndContractBlock();
301
302             if (Permissions.Count == 0)
303                 return "~";
304             var permissionBuilder = new StringBuilder();
305             var groupings = Permissions.GroupBy(pair => pair.Value.Trim(), pair => pair.Key.Trim());
306             foreach (var grouping in groupings)
307             {
308                 permissionBuilder.AppendFormat("{0}={1};", grouping.Key, String.Join(",", grouping));
309             }
310             var permissions = Uri.EscapeDataString(permissionBuilder.ToString().Trim(';'));
311             return permissions;
312         }
313
314         public void SetPermissions(string permissions)
315         {
316             if (String.IsNullOrWhiteSpace(permissions))
317                 return;
318             
319             Permissions = PermissionConverter.ParsePermissions(permissions);
320         }
321
322         //The previous values that correspond to a NoModification object
323         //have the same account, container and possibly the same folder
324         public bool CorrespondsTo(ObjectInfo other)
325         {
326             return other.Account == this.Account
327                    && other.Container == this.Container
328                    && (this.Name == null || other.Name.StartsWith(this.Name));
329         }
330
331         public bool IsWritable(string account)
332         {
333             //If the Allowed To header has no value, try to determine the Share permissions
334             if (AllowedTo == null)
335             {
336                 //If this file has no permissions defined, we can probably write it
337                 //This case should occur only when the info comes from a listing of the user's own files
338                 if (Permissions == null || Permissions.Count == 0)
339                     return true;
340                 string perms;
341
342                 //Do we have an explicit write share to this account?
343                 return Permissions.TryGetValue(account, out perms) 
344                     && perms.Equals("write",StringComparison.InvariantCultureIgnoreCase);
345                 
346             }
347             //Otherwise return the permissions specified by AllowedTo
348             return AllowedTo.Equals("write",StringComparison.InvariantCultureIgnoreCase) ;
349         }
350
351         public ObjectInfo Previous { get; private set; }
352
353         public bool IsDirectory
354         {
355             get
356             {
357                 if (Content_Type == null)
358                     return false;
359                 if (Content_Type.StartsWith(@"application/directory",StringComparison.InvariantCultureIgnoreCase))
360                     return true;
361                 if (Content_Type.StartsWith(@"application/folder",StringComparison.InvariantCultureIgnoreCase))
362                     return true;
363                 return false;
364             }
365         }
366
367         public Uri AccountKey
368         {
369             get { return new Uri(StorageUri,"../" + Account); }
370         }
371
372         public ObjectInfo SetPrevious(ObjectInfo previous)
373         {            
374             Previous = previous;
375             PreviousHash = previous.Hash;
376             return this;
377         }
378
379         public bool? IsShared
380         {
381             get
382             {
383                 if (Uri == null || StorageUri == null)
384                     return null;
385                 var isShared = !Uri.ToString().StartsWith(StorageUri.ToString());
386                 return isShared;
387             }
388         }
389     }
390 }