Statistics
| Branch: | Revision:

root / trunk / Pithos.Interfaces / ObjectInfo.cs @ 3a62612d

History | View | Annotate | Download (15.2 kB)

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
        public const string CONTENT_TYPE_DIRECTORY = @"application/directory";
59
        public const string CONTENT_TYPE_FOLDER = @"application/folder";
60
        private readonly List<string> _knownContainers= new List<string>{"trash"};
61

    
62
        [JsonConverter(typeof(RelativeUriConverter))]
63
        public Uri Name
64
        {
65
            get { return _name; }
66
            set
67
            {
68
                if (value != null && value.IsAbsoluteUri)
69
                    throw new ArgumentException("Must be relative Uri", "Name");
70
                _name = value;
71
            }
72
        }
73

    
74
        [JsonProperty("hash")]
75
        public string ETag { get; set; }
76

    
77
        //public string Hash { get; set; }
78

    
79
/*
80
        public string X_Object_Hash { get { return Hash; } set { Hash = value; } }
81
*/
82

    
83
        public string X_Object_Hash { get; set; }
84

    
85
        [JsonProperty("x_object_uuid")]
86
        public string UUID { get; set; }
87

    
88
        public long Bytes { get; set; }
89
        public string Content_Type { get; set; }
90
        public DateTimeOffset? Last_Modified { get; set; }
91

    
92
        private Dictionary<string, string> _tags=new Dictionary<string, string>();
93
        public Dictionary<string, string> Tags
94
        {
95
            get { return _tags; }
96
            set { _tags = value; }
97
        }
98

    
99
        private Dictionary<string, string> _extensions=new Dictionary<string, string>();
100
        public Dictionary<string, string> Extensions
101
        {
102
            get { return _extensions; }
103
            set
104
            {
105
                _extensions = value;
106
                ExtractKnownExtensions();
107
            }
108
        }
109
        
110
        
111
        private Dictionary<string, string> _permissions=new Dictionary<string, string>();
112
        [JsonProperty("x_object_sharing")]
113
        [JsonConverter(typeof(PermissionConverter))]
114
        public Dictionary<string, string> Permissions
115
        {
116
            get { return _permissions; }
117
            set
118
            {
119
                _permissions = value;                
120
            }
121
        }
122

    
123
        /// <summary>
124
        /// Version number
125
        /// </summary>
126
        [JsonProperty("x_object_version")]
127
        public long? Version { get; set; }
128

    
129

    
130
        /// <summary>
131
        /// Shared object permissions can be Read or Write
132
        /// </summary>
133
        [JsonProperty("x_object_allowed_to")]
134
        public string AllowedTo { get; set; }
135

    
136

    
137
        /// <summary>
138
        /// Version timestamp
139
        /// </summary>
140
        [JsonProperty("X_Object_Version_Timestamp"), JsonConverter(typeof(PithosDateTimeConverter))]
141
        public DateTime? VersionTimestamp { get; set; }
142

    
143
        [JsonProperty("X_Object_Modified_By")]
144
        public string ModifiedBy { get; set; }
145

    
146

    
147
        public Stream Stream { get; set; }
148

    
149

    
150
        public Uri StorageUri
151
        {
152
            get { return _storageUri; }
153
            set { _storageUri = value; }
154
        }
155

    
156
        public string Account { get; set; }
157

    
158
        [JsonConverter(typeof(RelativeUriConverter))]
159
        public Uri Container
160
        {
161
            get { return _container; }
162
            set
163
            {
164
                if (value != null && value.IsAbsoluteUri)
165
                    throw new ArgumentException("Must be relative Uri", "Container");
166
                _container = value;
167
            }
168
        }
169

    
170
        public Uri Uri
171
        {
172
            get
173
            {
174
                //If StoreageUri is set, it will contain our own account
175
                if (StorageUri != null)
176
                {
177
                    var isShared = !StorageUri.AbsoluteUri.EndsWith(Account);
178
                    //For shared objects, we need to replace our account with the object's account
179
                    var relativeUrl = isShared
180
                         ? String.Format("../{0}/{1}/{2}",Account, Container, Name)
181
                         : String.Format("{0}/{1}", Container, Name);
182
                    
183
                    var uri = StorageUri.Combine(relativeUrl);
184
                    return uri;
185
                }
186
                else
187
                {
188
                    var relativeUrl = String.Format("{0}/{1}/{2}", Account, Container, Name);
189
                    var uri = new Uri(relativeUrl, UriKind.Relative);
190
                    return uri;
191
                }
192
            }
193
        }
194

    
195
        public string ContendDisposition { get; set; }
196

    
197
        public string ContentEncoding { get; set; }
198

    
199
        public string Manifest { get; set; }
200
        
201
        public bool IsPublic
202
        {
203
            get { return !String.IsNullOrWhiteSpace(PublicUrl); }
204
            set
205
            {
206
                if (!value)
207
                    PublicUrl = null;
208
                else if (String.IsNullOrWhiteSpace(PublicUrl))
209
                    PublicUrl="true";                
210
            }
211
        }
212

    
213
        [JsonProperty("X_Object_Public")]
214
        public string PublicUrl { get; set; }
215

    
216
        public string PreviousHash { get; set; }
217

    
218
        public ObjectInfo()
219
        {}
220

    
221
        public ObjectInfo(string accountPath,string accountName,FileSystemInfo fileInfo)
222
        {
223
            if (String.IsNullOrWhiteSpace(accountPath))
224
                throw new ArgumentNullException("accountPath");
225
            if (string.IsNullOrWhiteSpace(accountName))
226
                throw new ArgumentNullException("accountName");
227
            if (fileInfo == null)
228
                throw new ArgumentNullException("fileInfo");
229
            Contract.EndContractBlock();
230

    
231
            var relativeUrl = fileInfo.WithProperCapitalization().AsRelativeUrlTo(accountPath);
232
            //The first part of the URL is the container
233
            var parts = relativeUrl.ToString().Split(new[]{'/'}, 2);
234
            var container = parts[0];
235
            //The second is the file's url relative to the container
236
            var fileUrl = parts[1];
237

    
238
            Account = accountName;
239
            Container = new Uri(container,UriKind.Relative);
240
            Name = new Uri(fileUrl,UriKind.Relative); 
241
        }
242

    
243

    
244
        private void ExtractKnownExtensions()
245
        {
246
            Version=GetLong(KnownExtensions.X_Object_Version);
247
            VersionTimestamp = GetTimestamp(KnownExtensions.X_Object_Version_Timestamp);
248
            ModifiedBy = GetString(KnownExtensions.X_Object_Modified_By);
249
        }
250

    
251
        private string GetString(string name)
252
        {            
253
            string value;
254
            _extensions.TryGetValue(name, out value);
255
            return value ;                        
256
        }
257
        
258
        private long? GetLong(string name)
259
        {
260
            string version;
261
            long value;
262
            return _extensions.TryGetValue(name, out version) && long.TryParse(version, out value)
263
                       ? (long?) value
264
                       : null;
265
        }
266

    
267
        private DateTime? GetTimestamp(string name)
268
        {
269
            string version;
270
            DateTime value;
271
            if (_extensions.TryGetValue(name, out version) && 
272
                DateTime.TryParse(version,CultureInfo.InvariantCulture,DateTimeStyles.AdjustToUniversal, out value))
273
            {
274
                return value;
275
            }
276
            return null;
277
        }
278

    
279

    
280
        public static ObjectInfo Empty = new ObjectInfo
281
        {
282
            Name = _emptyUri,
283
            Container = _emptyUri,
284
            ETag= String.Empty,
285
            X_Object_Hash= String.Empty,
286
            Bytes = 0,
287
            Content_Type = String.Empty,
288
            Exists=false
289
        };
290

    
291
        private bool _exists=true;
292
        private Uri _container;
293
        private Uri _name;
294
        private Uri _storageUri;
295
        private static Uri _emptyUri = new Uri(String.Empty, UriKind.Relative);
296

    
297
        public bool Exists
298
        {
299
            get {
300
                return _exists;
301
            }
302
            set {
303
                _exists = value;
304
            }
305
        }
306

    
307

    
308
        public string RelativeUrlToFilePath(string currentAccount)
309
        {
310
            if (Name==null)
311
                throw new InvalidOperationException("Name can't be null");
312
            if (String.IsNullOrWhiteSpace(currentAccount))
313
                throw new ArgumentNullException("currentAccount");
314
            Contract.EndContractBlock();
315

    
316
            if (this == Empty)
317
                return String.Empty;
318

    
319
            var unescaped = Uri.UnescapeDataString(Name.ToString());
320
            var path = unescaped.Replace("/", "\\");
321
            var pathParts=new Stack<string>();
322
            pathParts.Push(path);
323

    
324
            var container = Container.NullSafe(c=>c.ToString());
325
            if (!String.IsNullOrWhiteSpace(container) && !_knownContainers.Contains(container))
326
                pathParts.Push(Uri.UnescapeDataString(container));
327
            if (!currentAccount.Equals(Account, StringComparison.InvariantCultureIgnoreCase))
328
            {
329
                if (Account != null)
330
                {
331
                    pathParts.Push(Account);
332
                    pathParts.Push(FolderConstants.OthersFolder);
333
                }
334
            }
335
            var finalPath=Path.Combine(pathParts.ToArray());
336
            return finalPath;
337
        }
338

    
339
/*
340
        public override bool TrySetMember(SetMemberBinder binder, object value)
341
        {
342
            if (binder.Name.StartsWith("x_object_meta"))
343
            {
344
                Tags[binder.Name] = value.ToString();
345
            }
346
            return false;
347
        }
348
*/
349

    
350
        public string GetPermissionString()
351
        {
352
            if (Permissions==null)
353
                throw new InvalidOperationException();
354
            Contract.EndContractBlock();
355

    
356
            if (Permissions.Count == 0)
357
                return "~";
358
            var permissionBuilder = new StringBuilder();
359
            var groupings = Permissions.GroupBy(pair => pair.Value.Trim(), pair => pair.Key.Trim());
360
            foreach (var grouping in groupings)
361
            {
362
                permissionBuilder.AppendFormat("{0}={1};", grouping.Key, String.Join(",", grouping));
363
            }
364
            var permissions = Uri.EscapeDataString(permissionBuilder.ToString().Trim(';'));
365
            return permissions;
366
        }
367

    
368
        public void SetPermissions(string permissions)
369
        {
370
            if (String.IsNullOrWhiteSpace(permissions))
371
                return;
372
            
373
            Permissions = PermissionConverter.ParsePermissions(permissions);
374
        }
375

    
376
        //The previous values that correspond to a NoModification object
377
        //have the same account, container and possibly the same folder
378
        public bool CorrespondsTo(ObjectInfo other)
379
        {
380
            return other.Account == this.Account
381
                   && other.Container == this.Container
382
                   && (this.Name == null || other.Name.ToString().StartsWith(this.Name.ToString()));
383
        }
384

    
385
        public bool IsWritable(string account)
386
        {
387
            //If the Allowed To header has no value, try to determine the Share permissions
388
            if (AllowedTo == null)
389
            {
390
                //If this file has no permissions defined, we can probably write it
391
                //This case should occur only when the info comes from a listing of the user's own files
392
                if (Permissions == null || Permissions.Count == 0)
393
                    return true;
394
                string perms;
395

    
396
                //Do we have an explicit write share to this account?
397
                return Permissions.TryGetValue(account, out perms) 
398
                    && perms.Equals("write",StringComparison.InvariantCultureIgnoreCase);
399
                
400
            }
401
            //Otherwise return the permissions specified by AllowedTo
402
            return AllowedTo.Equals("write",StringComparison.InvariantCultureIgnoreCase) ;
403
        }
404

    
405
        public ObjectInfo Previous { get; private set; }
406

    
407
        public bool IsDirectory
408
        {
409
            get
410
            {
411
                if (Content_Type == null)
412
                    return false;
413
                if (Content_Type.StartsWith(CONTENT_TYPE_DIRECTORY,StringComparison.InvariantCultureIgnoreCase))
414
                    return true;
415
                if (Content_Type.StartsWith(CONTENT_TYPE_FOLDER,StringComparison.InvariantCultureIgnoreCase))
416
                    return true;
417
                return false;
418
            }
419
        }
420

    
421
        public Uri AccountKey
422
        {
423
            get { return StorageUri.Combine("../" + Account); }
424
        }
425

    
426
        public ObjectInfo SetPrevious(ObjectInfo previous)
427
        {            
428
            Previous = previous;
429
            PreviousHash = previous.X_Object_Hash;
430
            return this;
431
        }
432

    
433
        public bool? IsShared
434
        {
435
            get
436
            {
437
                if (Uri == null || StorageUri == null)
438
                    return null;
439
                var isShared = !Uri.ToString().StartsWith(StorageUri.ToString());
440
                return isShared;
441
            }
442
        }
443
    }
444
}