Statistics
| Branch: | Revision:

root / trunk / Pithos.Network / CloudFilesClient.cs @ ff26eb71

History | View | Annotate | Download (51.5 kB)

1 5ce54458 Panagiotis Kanavos
// **CloudFilesClient** provides a simple client interface to CloudFiles and Pithos
2 5ce54458 Panagiotis Kanavos
//
3 5ce54458 Panagiotis Kanavos
// The class provides methods to upload/download files, delete files, manage containers
4 5ce54458 Panagiotis Kanavos
5 5ce54458 Panagiotis Kanavos
6 5ce54458 Panagiotis Kanavos
using System;
7 d78cbf09 Panagiotis Kanavos
using System.Collections.Generic;
8 d78cbf09 Panagiotis Kanavos
using System.ComponentModel.Composition;
9 d78cbf09 Panagiotis Kanavos
using System.Diagnostics.Contracts;
10 d78cbf09 Panagiotis Kanavos
using System.IO;
11 d78cbf09 Panagiotis Kanavos
using System.Linq;
12 d78cbf09 Panagiotis Kanavos
using System.Net;
13 d78cbf09 Panagiotis Kanavos
using System.Security.Cryptography;
14 d78cbf09 Panagiotis Kanavos
using System.Text;
15 b5061ac8 Panagiotis Kanavos
using System.Threading.Tasks;
16 d78cbf09 Panagiotis Kanavos
using Newtonsoft.Json;
17 d78cbf09 Panagiotis Kanavos
using Pithos.Interfaces;
18 5120f3cb Panagiotis Kanavos
using log4net;
19 d78cbf09 Panagiotis Kanavos
20 d78cbf09 Panagiotis Kanavos
namespace Pithos.Network
21 d78cbf09 Panagiotis Kanavos
{
22 d78cbf09 Panagiotis Kanavos
    [Export(typeof(ICloudClient))]
23 d78cbf09 Panagiotis Kanavos
    public class CloudFilesClient:ICloudClient
24 d78cbf09 Panagiotis Kanavos
    {
25 5ce54458 Panagiotis Kanavos
        //CloudFilesClient uses *_baseClient* internally to communicate with the server
26 5ce54458 Panagiotis Kanavos
        //RestClient provides a REST-friendly interface over the standard WebClient.
27 9c4346c9 Panagiotis Kanavos
        private RestClient _baseClient;
28 5ce54458 Panagiotis Kanavos
        
29 c53aa229 Panagiotis Kanavos
30 5ce54458 Panagiotis Kanavos
        //During authentication the client provides a UserName 
31 d78cbf09 Panagiotis Kanavos
        public string UserName { get; set; }
32 5ce54458 Panagiotis Kanavos
        
33 5ce54458 Panagiotis Kanavos
        //and and ApiKey to the server
34 5ce54458 Panagiotis Kanavos
        public string ApiKey { get; set; }
35 5ce54458 Panagiotis Kanavos
        
36 5ce54458 Panagiotis Kanavos
        //And receives an authentication Token. This token must be provided in ALL other operations,
37 5ce54458 Panagiotis Kanavos
        //in the X-Auth-Token header
38 c53aa229 Panagiotis Kanavos
        private string _token;
39 c53aa229 Panagiotis Kanavos
        public string Token
40 c53aa229 Panagiotis Kanavos
        {
41 c53aa229 Panagiotis Kanavos
            get { return _token; }
42 c53aa229 Panagiotis Kanavos
            set
43 c53aa229 Panagiotis Kanavos
            {
44 c53aa229 Panagiotis Kanavos
                _token = value;
45 c53aa229 Panagiotis Kanavos
                _baseClient.Headers["X-Auth-Token"] = value;
46 c53aa229 Panagiotis Kanavos
            }
47 c53aa229 Panagiotis Kanavos
        }
48 c53aa229 Panagiotis Kanavos
49 5ce54458 Panagiotis Kanavos
        //The client also receives a StorageUrl after authentication. All subsequent operations must
50 5ce54458 Panagiotis Kanavos
        //use this url
51 5ce54458 Panagiotis Kanavos
        public Uri StorageUrl { get; set; }
52 cfed7823 Panagiotis Kanavos
53 c53aa229 Panagiotis Kanavos
54 cfed7823 Panagiotis Kanavos
        protected Uri RootAddressUri { get; set; }
55 cfed7823 Panagiotis Kanavos
56 c53aa229 Panagiotis Kanavos
        private Uri _proxy;
57 c53aa229 Panagiotis Kanavos
        public Uri Proxy
58 c53aa229 Panagiotis Kanavos
        {
59 c53aa229 Panagiotis Kanavos
            get { return _proxy; }
60 c53aa229 Panagiotis Kanavos
            set
61 c53aa229 Panagiotis Kanavos
            {
62 c53aa229 Panagiotis Kanavos
                _proxy = value;
63 c53aa229 Panagiotis Kanavos
                if (_baseClient != null)
64 c53aa229 Panagiotis Kanavos
                    _baseClient.Proxy = new WebProxy(value);                
65 c53aa229 Panagiotis Kanavos
            }
66 c53aa229 Panagiotis Kanavos
        }
67 b5061ac8 Panagiotis Kanavos
68 b5061ac8 Panagiotis Kanavos
        public double DownloadPercentLimit { get; set; }
69 b5061ac8 Panagiotis Kanavos
        public double UploadPercentLimit { get; set; }
70 79736291 Panagiotis Kanavos
71 79736291 Panagiotis Kanavos
        public string AuthenticationUrl { get; set; }
72 79736291 Panagiotis Kanavos
73 1482f9ad Panagiotis Kanavos
 
74 1482f9ad Panagiotis Kanavos
        public string VersionPath
75 1482f9ad Panagiotis Kanavos
        {
76 1482f9ad Panagiotis Kanavos
            get { return UsePithos ? "v1" : "v1.0"; }
77 1482f9ad Panagiotis Kanavos
        }
78 2c053915 Panagiotis Kanavos
79 2c053915 Panagiotis Kanavos
        public bool UsePithos { get; set; }
80 d78cbf09 Panagiotis Kanavos
81 3c43ec9b Panagiotis Kanavos
82 5120f3cb Panagiotis Kanavos
        private static readonly ILog Log = LogManager.GetLogger("CloudFilesClient");
83 5120f3cb Panagiotis Kanavos
84 c53aa229 Panagiotis Kanavos
        public CloudFilesClient(string userName, string apiKey)
85 d78cbf09 Panagiotis Kanavos
        {
86 c53aa229 Panagiotis Kanavos
            UserName = userName;
87 c53aa229 Panagiotis Kanavos
            ApiKey = apiKey;
88 c53aa229 Panagiotis Kanavos
        }
89 c53aa229 Panagiotis Kanavos
90 c53aa229 Panagiotis Kanavos
        public CloudFilesClient(AccountInfo accountInfo)
91 c53aa229 Panagiotis Kanavos
        {
92 c53aa229 Panagiotis Kanavos
            if (accountInfo==null)
93 c53aa229 Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
94 c53aa229 Panagiotis Kanavos
            Contract.Ensures(!String.IsNullOrWhiteSpace(Token));
95 c53aa229 Panagiotis Kanavos
            Contract.Ensures(StorageUrl != null);
96 cfed7823 Panagiotis Kanavos
            Contract.Ensures(_baseClient != null);
97 c53aa229 Panagiotis Kanavos
            Contract.Ensures(RootAddressUri != null);
98 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
99 cfed7823 Panagiotis Kanavos
100 c53aa229 Panagiotis Kanavos
            _baseClient = new RestClient
101 c53aa229 Panagiotis Kanavos
            {
102 c53aa229 Panagiotis Kanavos
                BaseAddress = accountInfo.StorageUri.ToString(),
103 c53aa229 Panagiotis Kanavos
                Timeout = 10000,
104 c53aa229 Panagiotis Kanavos
                Retries = 3
105 c53aa229 Panagiotis Kanavos
            };
106 c53aa229 Panagiotis Kanavos
            StorageUrl = accountInfo.StorageUri;
107 c53aa229 Panagiotis Kanavos
            Token = accountInfo.Token;
108 c53aa229 Panagiotis Kanavos
            UserName = accountInfo.UserName;
109 c53aa229 Panagiotis Kanavos
110 c53aa229 Panagiotis Kanavos
            //Get the root address (StorageUrl without the account)
111 c53aa229 Panagiotis Kanavos
            var storageUrl = StorageUrl.AbsoluteUri;
112 c53aa229 Panagiotis Kanavos
            var usernameIndex = storageUrl.LastIndexOf(UserName);
113 c53aa229 Panagiotis Kanavos
            var rootUrl = storageUrl.Substring(0, usernameIndex);
114 c53aa229 Panagiotis Kanavos
            RootAddressUri = new Uri(rootUrl);
115 c53aa229 Panagiotis Kanavos
        }
116 d78cbf09 Panagiotis Kanavos
117 3c43ec9b Panagiotis Kanavos
118 c53aa229 Panagiotis Kanavos
        public AccountInfo Authenticate()
119 c53aa229 Panagiotis Kanavos
        {
120 c53aa229 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(UserName))
121 c53aa229 Panagiotis Kanavos
                throw new InvalidOperationException("UserName is empty");
122 c53aa229 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(ApiKey))
123 c53aa229 Panagiotis Kanavos
                throw new InvalidOperationException("ApiKey is empty");
124 c53aa229 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(AuthenticationUrl))
125 c53aa229 Panagiotis Kanavos
                throw new InvalidOperationException("AuthenticationUrl is empty");
126 c53aa229 Panagiotis Kanavos
            Contract.Ensures(!String.IsNullOrWhiteSpace(Token));
127 c53aa229 Panagiotis Kanavos
            Contract.Ensures(StorageUrl != null);
128 c53aa229 Panagiotis Kanavos
            Contract.Ensures(_baseClient != null);
129 c53aa229 Panagiotis Kanavos
            Contract.Ensures(RootAddressUri != null);
130 c53aa229 Panagiotis Kanavos
            Contract.EndContractBlock();
131 c53aa229 Panagiotis Kanavos
132 c53aa229 Panagiotis Kanavos
133 c53aa229 Panagiotis Kanavos
            Log.InfoFormat("[AUTHENTICATE] Start for {0}", UserName);
134 d78cbf09 Panagiotis Kanavos
135 9c4346c9 Panagiotis Kanavos
            using (var authClient = new RestClient{BaseAddress=AuthenticationUrl})
136 cd7c162f Panagiotis Kanavos
            {
137 4d301e8e Panagiotis Kanavos
                if (Proxy != null)
138 4d301e8e Panagiotis Kanavos
                    authClient.Proxy = new WebProxy(Proxy);
139 d78cbf09 Panagiotis Kanavos
140 cfed7823 Panagiotis Kanavos
                Contract.Assume(authClient.Headers!=null);
141 cfed7823 Panagiotis Kanavos
142 4d301e8e Panagiotis Kanavos
                authClient.Headers.Add("X-Auth-User", UserName);
143 4d301e8e Panagiotis Kanavos
                authClient.Headers.Add("X-Auth-Key", ApiKey);
144 0eea575a Panagiotis Kanavos
145 cd7c162f Panagiotis Kanavos
                authClient.DownloadStringWithRetry(VersionPath, 3);
146 0eea575a Panagiotis Kanavos
147 4d301e8e Panagiotis Kanavos
                authClient.AssertStatusOK("Authentication failed");
148 0eea575a Panagiotis Kanavos
149 cd7c162f Panagiotis Kanavos
                var storageUrl = authClient.GetHeaderValue("X-Storage-Url");
150 0eea575a Panagiotis Kanavos
                if (String.IsNullOrWhiteSpace(storageUrl))
151 0eea575a Panagiotis Kanavos
                    throw new InvalidOperationException("Failed to obtain storage url");
152 c53aa229 Panagiotis Kanavos
                
153 c53aa229 Panagiotis Kanavos
                _baseClient = new RestClient
154 c53aa229 Panagiotis Kanavos
                {
155 c53aa229 Panagiotis Kanavos
                    BaseAddress = storageUrl,
156 c53aa229 Panagiotis Kanavos
                    Timeout = 10000,
157 c53aa229 Panagiotis Kanavos
                    Retries = 3
158 c53aa229 Panagiotis Kanavos
                };
159 c53aa229 Panagiotis Kanavos
160 0eea575a Panagiotis Kanavos
                StorageUrl = new Uri(storageUrl);
161 d9643dd6 Panagiotis Kanavos
                
162 cfed7823 Panagiotis Kanavos
                //Get the root address (StorageUrl without the account)
163 cfed7823 Panagiotis Kanavos
                var usernameIndex=storageUrl.LastIndexOf(UserName);
164 cfed7823 Panagiotis Kanavos
                var rootUrl = storageUrl.Substring(0, usernameIndex);
165 cfed7823 Panagiotis Kanavos
                RootAddressUri = new Uri(rootUrl);
166 cfed7823 Panagiotis Kanavos
                
167 4d301e8e Panagiotis Kanavos
                var token = authClient.GetHeaderValue("X-Auth-Token");
168 b6c72f62 Panagiotis Kanavos
                if (String.IsNullOrWhiteSpace(token))
169 b6c72f62 Panagiotis Kanavos
                    throw new InvalidOperationException("Failed to obtain token url");
170 b6c72f62 Panagiotis Kanavos
                Token = token;
171 c53aa229 Panagiotis Kanavos
172 b6c72f62 Panagiotis Kanavos
            }
173 d78cbf09 Panagiotis Kanavos
174 c53aa229 Panagiotis Kanavos
            Log.InfoFormat("[AUTHENTICATE] End for {0}", UserName);
175 0bd56b7c Panagiotis Kanavos
            
176 9c4346c9 Panagiotis Kanavos
177 c53aa229 Panagiotis Kanavos
            return new AccountInfo {StorageUri = StorageUrl, Token = Token, UserName = UserName};            
178 2c053915 Panagiotis Kanavos
179 b5061ac8 Panagiotis Kanavos
        }
180 d78cbf09 Panagiotis Kanavos
181 d78cbf09 Panagiotis Kanavos
182 cfed7823 Panagiotis Kanavos
183 cfed7823 Panagiotis Kanavos
        public IList<ContainerInfo> ListContainers(string account)
184 9c4346c9 Panagiotis Kanavos
        {
185 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
186 9c4346c9 Panagiotis Kanavos
            {
187 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
188 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
189 cfed7823 Panagiotis Kanavos
                
190 9c4346c9 Panagiotis Kanavos
                client.Parameters.Clear();
191 9c4346c9 Panagiotis Kanavos
                client.Parameters.Add("format", "json");
192 5ce54458 Panagiotis Kanavos
                var content = client.DownloadStringWithRetry("", 3);
193 9c4346c9 Panagiotis Kanavos
                client.AssertStatusOK("List Containers failed");
194 9c4346c9 Panagiotis Kanavos
195 9c4346c9 Panagiotis Kanavos
                if (client.StatusCode == HttpStatusCode.NoContent)
196 9c4346c9 Panagiotis Kanavos
                    return new List<ContainerInfo>();
197 9c4346c9 Panagiotis Kanavos
                var infos = JsonConvert.DeserializeObject<IList<ContainerInfo>>(content);
198 5750d7cc Panagiotis Kanavos
                
199 5750d7cc Panagiotis Kanavos
                foreach (var info in infos)
200 5750d7cc Panagiotis Kanavos
                {
201 5750d7cc Panagiotis Kanavos
                    info.Account = account;
202 5750d7cc Panagiotis Kanavos
                }
203 9c4346c9 Panagiotis Kanavos
                return infos;
204 9c4346c9 Panagiotis Kanavos
            }
205 4d301e8e Panagiotis Kanavos
206 d78cbf09 Panagiotis Kanavos
        }
207 d78cbf09 Panagiotis Kanavos
208 cfed7823 Panagiotis Kanavos
        private string GetAccountUrl(string account)
209 cfed7823 Panagiotis Kanavos
        {
210 cfed7823 Panagiotis Kanavos
            return new Uri(this.RootAddressUri, new Uri(account,UriKind.Relative)).AbsoluteUri;
211 cfed7823 Panagiotis Kanavos
        }
212 cfed7823 Panagiotis Kanavos
213 cfed7823 Panagiotis Kanavos
        public IList<ShareAccountInfo> ListSharingAccounts(DateTime? since=null)
214 cfed7823 Panagiotis Kanavos
        {
215 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Share"].Push("List Accounts"))
216 cfed7823 Panagiotis Kanavos
            {
217 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
218 cfed7823 Panagiotis Kanavos
219 5120f3cb Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
220 5120f3cb Panagiotis Kanavos
                {
221 5120f3cb Panagiotis Kanavos
                    client.Parameters.Clear();
222 5120f3cb Panagiotis Kanavos
                    client.Parameters.Add("format", "json");
223 5120f3cb Panagiotis Kanavos
                    client.IfModifiedSince = since;
224 cfed7823 Panagiotis Kanavos
225 5120f3cb Panagiotis Kanavos
                    //Extract the username from the base address
226 5120f3cb Panagiotis Kanavos
                    client.BaseAddress = RootAddressUri.AbsoluteUri;
227 cfed7823 Panagiotis Kanavos
228 5120f3cb Panagiotis Kanavos
                    var content = client.DownloadStringWithRetry(@"", 3);
229 cfed7823 Panagiotis Kanavos
230 5120f3cb Panagiotis Kanavos
                    client.AssertStatusOK("ListSharingAccounts failed");
231 5120f3cb Panagiotis Kanavos
232 5120f3cb Panagiotis Kanavos
                    //If the result is empty, return an empty list,
233 5120f3cb Panagiotis Kanavos
                    var infos = String.IsNullOrWhiteSpace(content)
234 5120f3cb Panagiotis Kanavos
                                    ? new List<ShareAccountInfo>()
235 5120f3cb Panagiotis Kanavos
                                //Otherwise deserialize the account list into a list of ShareAccountInfos
236 5120f3cb Panagiotis Kanavos
                                    : JsonConvert.DeserializeObject<IList<ShareAccountInfo>>(content);
237 5120f3cb Panagiotis Kanavos
238 5120f3cb Panagiotis Kanavos
                    Log.DebugFormat("END");
239 5120f3cb Panagiotis Kanavos
                    return infos;
240 5120f3cb Panagiotis Kanavos
                }
241 cfed7823 Panagiotis Kanavos
            }
242 cfed7823 Panagiotis Kanavos
        }
243 cfed7823 Panagiotis Kanavos
244 5ce54458 Panagiotis Kanavos
        //Request listing of all objects in a container modified since a specific time.
245 5ce54458 Panagiotis Kanavos
        //If the *since* value is missing, return all objects
246 cfed7823 Panagiotis Kanavos
        public IList<ObjectInfo> ListSharedObjects(DateTime? since = null)
247 cfed7823 Panagiotis Kanavos
        {
248 cfed7823 Panagiotis Kanavos
249 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Share"].Push("List Objects"))
250 cfed7823 Panagiotis Kanavos
            {
251 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
252 5120f3cb Panagiotis Kanavos
253 5120f3cb Panagiotis Kanavos
                var objects = new List<ObjectInfo>();
254 5120f3cb Panagiotis Kanavos
                var accounts = ListSharingAccounts(since);
255 5120f3cb Panagiotis Kanavos
                foreach (var account in accounts)
256 cfed7823 Panagiotis Kanavos
                {
257 5120f3cb Panagiotis Kanavos
                    var containers = ListContainers(account.name);
258 5120f3cb Panagiotis Kanavos
                    foreach (var container in containers)
259 5120f3cb Panagiotis Kanavos
                    {
260 0bd56b7c Panagiotis Kanavos
                        var containerObjects = ListObjects(account.name, container.Name, null);
261 5120f3cb Panagiotis Kanavos
                        objects.AddRange(containerObjects);
262 5120f3cb Panagiotis Kanavos
                    }
263 cfed7823 Panagiotis Kanavos
                }
264 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("END");
265 5120f3cb Panagiotis Kanavos
                return objects;
266 cfed7823 Panagiotis Kanavos
            }
267 cfed7823 Panagiotis Kanavos
        }
268 cfed7823 Panagiotis Kanavos
269 ff26eb71 Panagiotis Kanavos
        public void SetTags(ObjectInfo target,IDictionary<string,string> tags)
270 ff26eb71 Panagiotis Kanavos
        {
271 ff26eb71 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(Token))
272 ff26eb71 Panagiotis Kanavos
                throw new InvalidOperationException("The Token is not set");
273 ff26eb71 Panagiotis Kanavos
            if (StorageUrl == null)
274 ff26eb71 Panagiotis Kanavos
                throw new InvalidOperationException("The StorageUrl is not set");
275 ff26eb71 Panagiotis Kanavos
            if (target == null)
276 ff26eb71 Panagiotis Kanavos
                throw new ArgumentNullException("target");
277 ff26eb71 Panagiotis Kanavos
            Contract.EndContractBlock();
278 ff26eb71 Panagiotis Kanavos
279 ff26eb71 Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Share"].Push("Share Object"))
280 ff26eb71 Panagiotis Kanavos
            {
281 ff26eb71 Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
282 ff26eb71 Panagiotis Kanavos
283 ff26eb71 Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
284 ff26eb71 Panagiotis Kanavos
                {
285 ff26eb71 Panagiotis Kanavos
286 ff26eb71 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(target.Account);
287 ff26eb71 Panagiotis Kanavos
288 ff26eb71 Panagiotis Kanavos
                    client.Parameters.Clear();
289 ff26eb71 Panagiotis Kanavos
                    client.Parameters.Add("update", "");
290 ff26eb71 Panagiotis Kanavos
291 ff26eb71 Panagiotis Kanavos
                    foreach (var tag in tags)
292 ff26eb71 Panagiotis Kanavos
                    {
293 ff26eb71 Panagiotis Kanavos
                        var headerTag = String.Format("X-Object-Meta-{0}", tag.Key);
294 ff26eb71 Panagiotis Kanavos
                        client.Headers.Add(headerTag, tag.Value);
295 ff26eb71 Panagiotis Kanavos
                    }
296 ff26eb71 Panagiotis Kanavos
                    
297 ff26eb71 Panagiotis Kanavos
                    var content = client.DownloadStringWithRetry(target.Container, 3);
298 ff26eb71 Panagiotis Kanavos
299 ff26eb71 Panagiotis Kanavos
                    
300 ff26eb71 Panagiotis Kanavos
                    client.AssertStatusOK("SetTags failed");
301 ff26eb71 Panagiotis Kanavos
                    //If the status is NOT ACCEPTED we have a problem
302 ff26eb71 Panagiotis Kanavos
                    if (client.StatusCode != HttpStatusCode.Accepted)
303 ff26eb71 Panagiotis Kanavos
                    {
304 ff26eb71 Panagiotis Kanavos
                        Log.Error("Failed to set tags");
305 ff26eb71 Panagiotis Kanavos
                        throw new Exception("Failed to set tags");
306 ff26eb71 Panagiotis Kanavos
                    }
307 ff26eb71 Panagiotis Kanavos
308 ff26eb71 Panagiotis Kanavos
                    if (Log.IsDebugEnabled) Log.DebugFormat("END");
309 ff26eb71 Panagiotis Kanavos
                }
310 ff26eb71 Panagiotis Kanavos
            }
311 ff26eb71 Panagiotis Kanavos
312 ff26eb71 Panagiotis Kanavos
313 ff26eb71 Panagiotis Kanavos
        }
314 ff26eb71 Panagiotis Kanavos
315 cfed7823 Panagiotis Kanavos
        public void ShareObject(string account, string container, string objectName, string shareTo, bool read, bool write)
316 cfed7823 Panagiotis Kanavos
        {
317 cfed7823 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(Token))
318 cfed7823 Panagiotis Kanavos
                throw new InvalidOperationException("The Token is not set");
319 cfed7823 Panagiotis Kanavos
            if (StorageUrl==null)
320 cfed7823 Panagiotis Kanavos
                throw new InvalidOperationException("The StorageUrl is not set");
321 cfed7823 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
322 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("container");
323 cfed7823 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
324 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("objectName");
325 cfed7823 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(account))
326 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("account");
327 cfed7823 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(shareTo))
328 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("shareTo");
329 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
330 cfed7823 Panagiotis Kanavos
331 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Share"].Push("Share Object"))
332 cfed7823 Panagiotis Kanavos
            {
333 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
334 5120f3cb Panagiotis Kanavos
                
335 5120f3cb Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
336 5120f3cb Panagiotis Kanavos
                {
337 cfed7823 Panagiotis Kanavos
338 5120f3cb Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
339 cfed7823 Panagiotis Kanavos
340 5120f3cb Panagiotis Kanavos
                    client.Parameters.Clear();
341 5120f3cb Panagiotis Kanavos
                    client.Parameters.Add("format", "json");
342 5120f3cb Panagiotis Kanavos
343 5120f3cb Panagiotis Kanavos
                    string permission = "";
344 5120f3cb Panagiotis Kanavos
                    if (write)
345 5120f3cb Panagiotis Kanavos
                        permission = String.Format("write={0}", shareTo);
346 5120f3cb Panagiotis Kanavos
                    else if (read)
347 5120f3cb Panagiotis Kanavos
                        permission = String.Format("read={0}", shareTo);
348 5120f3cb Panagiotis Kanavos
                    client.Headers.Add("X-Object-Sharing", permission);
349 cfed7823 Panagiotis Kanavos
350 5120f3cb Panagiotis Kanavos
                    var content = client.DownloadStringWithRetry(container, 3);
351 cfed7823 Panagiotis Kanavos
352 5120f3cb Panagiotis Kanavos
                    client.AssertStatusOK("ShareObject failed");
353 cfed7823 Panagiotis Kanavos
354 5120f3cb Panagiotis Kanavos
                    //If the result is empty, return an empty list,
355 5120f3cb Panagiotis Kanavos
                    var infos = String.IsNullOrWhiteSpace(content)
356 5120f3cb Panagiotis Kanavos
                                    ? new List<ObjectInfo>()
357 5120f3cb Panagiotis Kanavos
                                //Otherwise deserialize the object list into a list of ObjectInfos
358 5120f3cb Panagiotis Kanavos
                                    : JsonConvert.DeserializeObject<IList<ObjectInfo>>(content);
359 5120f3cb Panagiotis Kanavos
360 5120f3cb Panagiotis Kanavos
                    if (Log.IsDebugEnabled) Log.DebugFormat("END");
361 5120f3cb Panagiotis Kanavos
                }
362 cfed7823 Panagiotis Kanavos
            }
363 cfed7823 Panagiotis Kanavos
364 5120f3cb Panagiotis Kanavos
365 cfed7823 Panagiotis Kanavos
        }
366 cfed7823 Panagiotis Kanavos
367 0bd56b7c Panagiotis Kanavos
        public AccountInfo GetAccountPolicies(AccountInfo accountInfo)
368 0bd56b7c Panagiotis Kanavos
        {
369 0bd56b7c Panagiotis Kanavos
            if (accountInfo==null)
370 0bd56b7c Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
371 0bd56b7c Panagiotis Kanavos
            Contract.EndContractBlock();
372 0bd56b7c Panagiotis Kanavos
373 0bd56b7c Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Account"].Push("GetPolicies"))
374 0bd56b7c Panagiotis Kanavos
            {
375 0bd56b7c Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
376 0bd56b7c Panagiotis Kanavos
377 0bd56b7c Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
378 0bd56b7c Panagiotis Kanavos
                {
379 0bd56b7c Panagiotis Kanavos
                    if (!String.IsNullOrWhiteSpace(accountInfo.UserName))
380 0bd56b7c Panagiotis Kanavos
                        client.BaseAddress = GetAccountUrl(accountInfo.UserName);
381 0bd56b7c Panagiotis Kanavos
382 0bd56b7c Panagiotis Kanavos
                    client.Parameters.Clear();
383 0bd56b7c Panagiotis Kanavos
                    client.Parameters.Add("format", "json");                    
384 0bd56b7c Panagiotis Kanavos
                    client.Head(String.Empty, 3);
385 0bd56b7c Panagiotis Kanavos
386 0bd56b7c Panagiotis Kanavos
                    var quotaValue=client.ResponseHeaders["X-Account-Policy-Quota"];
387 0bd56b7c Panagiotis Kanavos
                    var bytesValue= client.ResponseHeaders["X-Account-Bytes-Used"];
388 0bd56b7c Panagiotis Kanavos
389 0bd56b7c Panagiotis Kanavos
                    long quota, bytes;
390 0bd56b7c Panagiotis Kanavos
                    if (long.TryParse(quotaValue, out quota))
391 0bd56b7c Panagiotis Kanavos
                        accountInfo.Quota = quota;
392 0bd56b7c Panagiotis Kanavos
                    if (long.TryParse(bytesValue, out bytes))
393 0bd56b7c Panagiotis Kanavos
                        accountInfo.BytesUsed = bytes;
394 0bd56b7c Panagiotis Kanavos
                    
395 0bd56b7c Panagiotis Kanavos
                    return accountInfo;
396 0bd56b7c Panagiotis Kanavos
397 0bd56b7c Panagiotis Kanavos
                }
398 0bd56b7c Panagiotis Kanavos
399 0bd56b7c Panagiotis Kanavos
            }
400 0bd56b7c Panagiotis Kanavos
        }
401 0bd56b7c Panagiotis Kanavos
402 cfed7823 Panagiotis Kanavos
403 cfed7823 Panagiotis Kanavos
        public IList<ObjectInfo> ListObjects(string account, string container, DateTime? since = null)
404 d78cbf09 Panagiotis Kanavos
        {
405 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
406 9c4346c9 Panagiotis Kanavos
                throw new ArgumentNullException("container");
407 9c4346c9 Panagiotis Kanavos
            Contract.EndContractBlock();
408 d78cbf09 Panagiotis Kanavos
409 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Objects"].Push("List"))
410 9c4346c9 Panagiotis Kanavos
            {
411 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
412 cfed7823 Panagiotis Kanavos
413 5120f3cb Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
414 5120f3cb Panagiotis Kanavos
                {
415 5120f3cb Panagiotis Kanavos
                    if (!String.IsNullOrWhiteSpace(account))
416 5120f3cb Panagiotis Kanavos
                        client.BaseAddress = GetAccountUrl(account);
417 4d301e8e Panagiotis Kanavos
418 5120f3cb Panagiotis Kanavos
                    client.Parameters.Clear();
419 5120f3cb Panagiotis Kanavos
                    client.Parameters.Add("format", "json");
420 5120f3cb Panagiotis Kanavos
                    client.IfModifiedSince = since;
421 5120f3cb Panagiotis Kanavos
                    var content = client.DownloadStringWithRetry(container, 3);
422 4d301e8e Panagiotis Kanavos
423 5120f3cb Panagiotis Kanavos
                    client.AssertStatusOK("ListObjects failed");
424 d78cbf09 Panagiotis Kanavos
425 5120f3cb Panagiotis Kanavos
                    //If the result is empty, return an empty list,
426 5120f3cb Panagiotis Kanavos
                    var infos = String.IsNullOrWhiteSpace(content)
427 5120f3cb Panagiotis Kanavos
                                    ? new List<ObjectInfo>()
428 5120f3cb Panagiotis Kanavos
                                //Otherwise deserialize the object list into a list of ObjectInfos
429 5120f3cb Panagiotis Kanavos
                                    : JsonConvert.DeserializeObject<IList<ObjectInfo>>(content);
430 5120f3cb Panagiotis Kanavos
431 5120f3cb Panagiotis Kanavos
                    foreach (var info in infos)
432 5120f3cb Panagiotis Kanavos
                    {
433 5120f3cb Panagiotis Kanavos
                        info.Container = container;
434 5120f3cb Panagiotis Kanavos
                        info.Account = account;
435 5120f3cb Panagiotis Kanavos
                    }
436 5120f3cb Panagiotis Kanavos
                    if (Log.IsDebugEnabled) Log.DebugFormat("START");
437 5120f3cb Panagiotis Kanavos
                    return infos;
438 cfed7823 Panagiotis Kanavos
                }
439 9c4346c9 Panagiotis Kanavos
            }
440 d78cbf09 Panagiotis Kanavos
        }
441 d78cbf09 Panagiotis Kanavos
442 637bc368 Panagiotis Kanavos
443 5bcf6d70 Panagiotis Kanavos
444 cfed7823 Panagiotis Kanavos
        public IList<ObjectInfo> ListObjects(string account, string container, string folder, DateTime? since = null)
445 637bc368 Panagiotis Kanavos
        {
446 637bc368 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
447 9c4346c9 Panagiotis Kanavos
                throw new ArgumentNullException("container");
448 9c4346c9 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(folder))
449 9c4346c9 Panagiotis Kanavos
                throw new ArgumentNullException("folder");
450 9c4346c9 Panagiotis Kanavos
            Contract.EndContractBlock();
451 637bc368 Panagiotis Kanavos
452 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Objects"].Push("List"))
453 9c4346c9 Panagiotis Kanavos
            {
454 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
455 cfed7823 Panagiotis Kanavos
456 5120f3cb Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
457 5120f3cb Panagiotis Kanavos
                {
458 5120f3cb Panagiotis Kanavos
                    if (!String.IsNullOrWhiteSpace(account))
459 5120f3cb Panagiotis Kanavos
                        client.BaseAddress = GetAccountUrl(account);
460 4d301e8e Panagiotis Kanavos
461 5120f3cb Panagiotis Kanavos
                    client.Parameters.Clear();
462 5120f3cb Panagiotis Kanavos
                    client.Parameters.Add("format", "json");
463 5120f3cb Panagiotis Kanavos
                    client.Parameters.Add("path", folder);
464 5120f3cb Panagiotis Kanavos
                    client.IfModifiedSince = since;
465 5120f3cb Panagiotis Kanavos
                    var content = client.DownloadStringWithRetry(container, 3);
466 5120f3cb Panagiotis Kanavos
                    client.AssertStatusOK("ListObjects failed");
467 5bcf6d70 Panagiotis Kanavos
468 5120f3cb Panagiotis Kanavos
                    var infos = JsonConvert.DeserializeObject<IList<ObjectInfo>>(content);
469 5120f3cb Panagiotis Kanavos
470 5120f3cb Panagiotis Kanavos
                    if (Log.IsDebugEnabled) Log.DebugFormat("END");
471 5120f3cb Panagiotis Kanavos
                    return infos;
472 5120f3cb Panagiotis Kanavos
                }
473 9c4346c9 Panagiotis Kanavos
            }
474 5bcf6d70 Panagiotis Kanavos
        }
475 5bcf6d70 Panagiotis Kanavos
476 d15e99b4 Panagiotis Kanavos
 
477 cfed7823 Panagiotis Kanavos
        public bool ContainerExists(string account, string container)
478 d78cbf09 Panagiotis Kanavos
        {
479 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
480 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
481 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
482 cfed7823 Panagiotis Kanavos
483 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Containters"].Push("Exists"))
484 d78cbf09 Panagiotis Kanavos
            {
485 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
486 cfed7823 Panagiotis Kanavos
487 5120f3cb Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
488 9c4346c9 Panagiotis Kanavos
                {
489 5120f3cb Panagiotis Kanavos
                    if (!String.IsNullOrWhiteSpace(account))
490 5120f3cb Panagiotis Kanavos
                        client.BaseAddress = GetAccountUrl(account);
491 5120f3cb Panagiotis Kanavos
492 5120f3cb Panagiotis Kanavos
                    client.Parameters.Clear();
493 5120f3cb Panagiotis Kanavos
                    client.Head(container, 3);
494 5120f3cb Panagiotis Kanavos
                                        
495 5120f3cb Panagiotis Kanavos
                    bool result;
496 5120f3cb Panagiotis Kanavos
                    switch (client.StatusCode)
497 5120f3cb Panagiotis Kanavos
                    {
498 5120f3cb Panagiotis Kanavos
                        case HttpStatusCode.OK:
499 5120f3cb Panagiotis Kanavos
                        case HttpStatusCode.NoContent:
500 5120f3cb Panagiotis Kanavos
                            result=true;
501 5120f3cb Panagiotis Kanavos
                            break;
502 5120f3cb Panagiotis Kanavos
                        case HttpStatusCode.NotFound:
503 5120f3cb Panagiotis Kanavos
                            result=false;
504 5120f3cb Panagiotis Kanavos
                            break;
505 5120f3cb Panagiotis Kanavos
                        default:
506 5120f3cb Panagiotis Kanavos
                            throw CreateWebException("ContainerExists", client.StatusCode);
507 5120f3cb Panagiotis Kanavos
                    }
508 5120f3cb Panagiotis Kanavos
                    if (Log.IsDebugEnabled) Log.DebugFormat("END");
509 5120f3cb Panagiotis Kanavos
510 5120f3cb Panagiotis Kanavos
                    return result;
511 9c4346c9 Panagiotis Kanavos
                }
512 5120f3cb Panagiotis Kanavos
                
513 d78cbf09 Panagiotis Kanavos
            }
514 d78cbf09 Panagiotis Kanavos
        }
515 d78cbf09 Panagiotis Kanavos
516 cfed7823 Panagiotis Kanavos
        public bool ObjectExists(string account, string container, string objectName)
517 d78cbf09 Panagiotis Kanavos
        {
518 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
519 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
520 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
521 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("objectName", "The objectName property can't be empty");
522 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
523 cfed7823 Panagiotis Kanavos
524 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
525 d78cbf09 Panagiotis Kanavos
            {
526 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
527 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
528 cfed7823 Panagiotis Kanavos
529 9c4346c9 Panagiotis Kanavos
                client.Parameters.Clear();
530 9c4346c9 Panagiotis Kanavos
                client.Head(container + "/" + objectName, 3);
531 9c4346c9 Panagiotis Kanavos
532 9c4346c9 Panagiotis Kanavos
                switch (client.StatusCode)
533 9c4346c9 Panagiotis Kanavos
                {
534 9c4346c9 Panagiotis Kanavos
                    case HttpStatusCode.OK:
535 9c4346c9 Panagiotis Kanavos
                    case HttpStatusCode.NoContent:
536 9c4346c9 Panagiotis Kanavos
                        return true;
537 9c4346c9 Panagiotis Kanavos
                    case HttpStatusCode.NotFound:
538 9c4346c9 Panagiotis Kanavos
                        return false;
539 9c4346c9 Panagiotis Kanavos
                    default:
540 9c4346c9 Panagiotis Kanavos
                        throw CreateWebException("ObjectExists", client.StatusCode);
541 9c4346c9 Panagiotis Kanavos
                }
542 d78cbf09 Panagiotis Kanavos
            }
543 9c4346c9 Panagiotis Kanavos
544 d78cbf09 Panagiotis Kanavos
        }
545 d78cbf09 Panagiotis Kanavos
546 cfed7823 Panagiotis Kanavos
        public ObjectInfo GetObjectInfo(string account, string container, string objectName)
547 d78cbf09 Panagiotis Kanavos
        {
548 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
549 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
550 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
551 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("objectName", "The objectName property can't be empty");
552 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
553 d78cbf09 Panagiotis Kanavos
554 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Objects"].Push("GetObjectInfo"))
555 5120f3cb Panagiotis Kanavos
            {                
556 5120f3cb Panagiotis Kanavos
557 5120f3cb Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
558 9c4346c9 Panagiotis Kanavos
                {
559 5120f3cb Panagiotis Kanavos
                    if (!String.IsNullOrWhiteSpace(account))
560 5120f3cb Panagiotis Kanavos
                        client.BaseAddress = GetAccountUrl(account);
561 5120f3cb Panagiotis Kanavos
                    try
562 5120f3cb Panagiotis Kanavos
                    {
563 5120f3cb Panagiotis Kanavos
                        client.Parameters.Clear();
564 d78cbf09 Panagiotis Kanavos
565 5120f3cb Panagiotis Kanavos
                        client.Head(container + "/" + objectName, 3);
566 d78cbf09 Panagiotis Kanavos
567 5120f3cb Panagiotis Kanavos
                        if (client.TimedOut)
568 5120f3cb Panagiotis Kanavos
                            return ObjectInfo.Empty;
569 9c4346c9 Panagiotis Kanavos
570 5120f3cb Panagiotis Kanavos
                        switch (client.StatusCode)
571 5120f3cb Panagiotis Kanavos
                        {
572 5120f3cb Panagiotis Kanavos
                            case HttpStatusCode.OK:
573 5120f3cb Panagiotis Kanavos
                            case HttpStatusCode.NoContent:
574 5120f3cb Panagiotis Kanavos
                                var keys = client.ResponseHeaders.AllKeys.AsQueryable();
575 5120f3cb Panagiotis Kanavos
                                var tags = (from key in keys
576 5120f3cb Panagiotis Kanavos
                                            where key.StartsWith("X-Object-Meta-")
577 5120f3cb Panagiotis Kanavos
                                            let name = key.Substring(14)
578 5120f3cb Panagiotis Kanavos
                                            select new {Name = name, Value = client.ResponseHeaders[name]})
579 5120f3cb Panagiotis Kanavos
                                    .ToDictionary(t => t.Name, t => t.Value);
580 5120f3cb Panagiotis Kanavos
                                var extensions = (from key in keys
581 5120f3cb Panagiotis Kanavos
                                                  where key.StartsWith("X-Object-") && !key.StartsWith("X-Object-Meta-")
582 5120f3cb Panagiotis Kanavos
                                                  select new {Name = key, Value = client.ResponseHeaders[key]})
583 5120f3cb Panagiotis Kanavos
                                    .ToDictionary(t => t.Name, t => t.Value);
584 5120f3cb Panagiotis Kanavos
                                var info = new ObjectInfo
585 5120f3cb Panagiotis Kanavos
                                               {
586 7b0a5fec Panagiotis Kanavos
                                                   Account = account,
587 7b0a5fec Panagiotis Kanavos
                                                   Container = container,
588 5120f3cb Panagiotis Kanavos
                                                   Name = objectName,
589 5120f3cb Panagiotis Kanavos
                                                   Hash = client.GetHeaderValue("ETag"),
590 5120f3cb Panagiotis Kanavos
                                                   Content_Type = client.GetHeaderValue("Content-Type"),
591 7b0a5fec Panagiotis Kanavos
                                                   Bytes = Convert.ToInt64(client.GetHeaderValue("Content-Length")),
592 5120f3cb Panagiotis Kanavos
                                                   Tags = tags,
593 5120f3cb Panagiotis Kanavos
                                                   Last_Modified = client.LastModified,
594 5120f3cb Panagiotis Kanavos
                                                   Extensions = extensions
595 5120f3cb Panagiotis Kanavos
                                               };
596 5120f3cb Panagiotis Kanavos
                                return info;
597 5120f3cb Panagiotis Kanavos
                            case HttpStatusCode.NotFound:
598 5120f3cb Panagiotis Kanavos
                                return ObjectInfo.Empty;
599 5120f3cb Panagiotis Kanavos
                            default:
600 5120f3cb Panagiotis Kanavos
                                throw new WebException(
601 5120f3cb Panagiotis Kanavos
                                    String.Format("[FAIL] GetObjectInfo for {0} failed with unexpected status code {1}",
602 5120f3cb Panagiotis Kanavos
                                                  objectName, client.StatusCode));
603 5120f3cb Panagiotis Kanavos
                        }
604 5120f3cb Panagiotis Kanavos
605 5120f3cb Panagiotis Kanavos
                    }
606 5120f3cb Panagiotis Kanavos
                    catch (RetryException)
607 9c4346c9 Panagiotis Kanavos
                    {
608 5120f3cb Panagiotis Kanavos
                        Log.WarnFormat("[RETRY FAIL] GetObjectInfo for {0} failed.");
609 5120f3cb Panagiotis Kanavos
                        return ObjectInfo.Empty;
610 9c4346c9 Panagiotis Kanavos
                    }
611 5120f3cb Panagiotis Kanavos
                    catch (WebException e)
612 5120f3cb Panagiotis Kanavos
                    {
613 5120f3cb Panagiotis Kanavos
                        Log.Error(
614 5120f3cb Panagiotis Kanavos
                            String.Format("[FAIL] GetObjectInfo for {0} failed with unexpected status code {1}",
615 5120f3cb Panagiotis Kanavos
                                          objectName, client.StatusCode), e);
616 5120f3cb Panagiotis Kanavos
                        throw;
617 5120f3cb Panagiotis Kanavos
                    }
618 5120f3cb Panagiotis Kanavos
                }                
619 d78cbf09 Panagiotis Kanavos
            }
620 9c4346c9 Panagiotis Kanavos
621 d78cbf09 Panagiotis Kanavos
        }
622 d78cbf09 Panagiotis Kanavos
623 cfed7823 Panagiotis Kanavos
        public void CreateFolder(string account, string container, string folder)
624 10523ad9 Panagiotis Kanavos
        {
625 10523ad9 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
626 10523ad9 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
627 10523ad9 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(folder))
628 10523ad9 Panagiotis Kanavos
                throw new ArgumentNullException("folder", "The folder property can't be empty");
629 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
630 10523ad9 Panagiotis Kanavos
631 10523ad9 Panagiotis Kanavos
            var folderUrl=String.Format("{0}/{1}",container,folder);
632 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
633 9c4346c9 Panagiotis Kanavos
            {
634 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
635 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
636 cfed7823 Panagiotis Kanavos
637 9c4346c9 Panagiotis Kanavos
                client.Parameters.Clear();
638 9c4346c9 Panagiotis Kanavos
                client.Headers.Add("Content-Type", @"application/directory");
639 9c4346c9 Panagiotis Kanavos
                client.Headers.Add("Content-Length", "0");
640 9c4346c9 Panagiotis Kanavos
                client.PutWithRetry(folderUrl, 3);
641 10523ad9 Panagiotis Kanavos
642 9c4346c9 Panagiotis Kanavos
                if (client.StatusCode != HttpStatusCode.Created && client.StatusCode != HttpStatusCode.Accepted)
643 9c4346c9 Panagiotis Kanavos
                    throw CreateWebException("CreateFolder", client.StatusCode);
644 9c4346c9 Panagiotis Kanavos
            }
645 10523ad9 Panagiotis Kanavos
        }
646 10523ad9 Panagiotis Kanavos
647 cfed7823 Panagiotis Kanavos
        public ContainerInfo GetContainerInfo(string account, string container)
648 d78cbf09 Panagiotis Kanavos
        {
649 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
650 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
651 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
652 cfed7823 Panagiotis Kanavos
653 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
654 d78cbf09 Panagiotis Kanavos
            {
655 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
656 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);                
657 cfed7823 Panagiotis Kanavos
658 9c4346c9 Panagiotis Kanavos
                client.Head(container);
659 9c4346c9 Panagiotis Kanavos
                switch (client.StatusCode)
660 9c4346c9 Panagiotis Kanavos
                {
661 5ce54458 Panagiotis Kanavos
                    case HttpStatusCode.OK:
662 9c4346c9 Panagiotis Kanavos
                    case HttpStatusCode.NoContent:
663 27361404 Panagiotis Kanavos
                        var keys = client.ResponseHeaders.AllKeys.AsQueryable();
664 27361404 Panagiotis Kanavos
                        var tags = (from key in keys
665 27361404 Panagiotis Kanavos
                                    where key.StartsWith("X-Container-Meta-")
666 27361404 Panagiotis Kanavos
                                    let name = key.Substring(14)
667 27361404 Panagiotis Kanavos
                                    select new { Name = name, Value = client.ResponseHeaders[name] })
668 27361404 Panagiotis Kanavos
                                    .ToDictionary(t => t.Name, t => t.Value);
669 27361404 Panagiotis Kanavos
                        var policies= (from key in keys
670 27361404 Panagiotis Kanavos
                                    where key.StartsWith("X-Container-Policy-")
671 27361404 Panagiotis Kanavos
                                    let name = key.Substring(14)
672 27361404 Panagiotis Kanavos
                                    select new { Name = name, Value = client.ResponseHeaders[name] })
673 27361404 Panagiotis Kanavos
                                    .ToDictionary(t => t.Name, t => t.Value);
674 27361404 Panagiotis Kanavos
675 9c4346c9 Panagiotis Kanavos
                        var containerInfo = new ContainerInfo
676 9c4346c9 Panagiotis Kanavos
                                                {
677 42800be8 Panagiotis Kanavos
                                                    Account=account,
678 9c4346c9 Panagiotis Kanavos
                                                    Name = container,
679 9c4346c9 Panagiotis Kanavos
                                                    Count =
680 9c4346c9 Panagiotis Kanavos
                                                        long.Parse(client.GetHeaderValue("X-Container-Object-Count")),
681 5ce54458 Panagiotis Kanavos
                                                    Bytes = long.Parse(client.GetHeaderValue("X-Container-Bytes-Used")),
682 5ce54458 Panagiotis Kanavos
                                                    BlockHash = client.GetHeaderValue("X-Container-Block-Hash"),
683 42800be8 Panagiotis Kanavos
                                                    BlockSize=int.Parse(client.GetHeaderValue("X-Container-Block-Size")),
684 27361404 Panagiotis Kanavos
                                                    Last_Modified=client.LastModified,
685 27361404 Panagiotis Kanavos
                                                    Tags=tags,
686 27361404 Panagiotis Kanavos
                                                    Policies=policies
687 9c4346c9 Panagiotis Kanavos
                                                };
688 27361404 Panagiotis Kanavos
                        
689 27361404 Panagiotis Kanavos
690 9c4346c9 Panagiotis Kanavos
                        return containerInfo;
691 9c4346c9 Panagiotis Kanavos
                    case HttpStatusCode.NotFound:
692 9c4346c9 Panagiotis Kanavos
                        return ContainerInfo.Empty;
693 9c4346c9 Panagiotis Kanavos
                    default:
694 9c4346c9 Panagiotis Kanavos
                        throw CreateWebException("GetContainerInfo", client.StatusCode);
695 9c4346c9 Panagiotis Kanavos
                }
696 d78cbf09 Panagiotis Kanavos
            }
697 d78cbf09 Panagiotis Kanavos
        }
698 d78cbf09 Panagiotis Kanavos
699 cfed7823 Panagiotis Kanavos
        public void CreateContainer(string account, string container)
700 cfed7823 Panagiotis Kanavos
        {            
701 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
702 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
703 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
704 cfed7823 Panagiotis Kanavos
705 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
706 9c4346c9 Panagiotis Kanavos
            {
707 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
708 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
709 cfed7823 Panagiotis Kanavos
710 9c4346c9 Panagiotis Kanavos
                client.PutWithRetry(container, 3);
711 9c4346c9 Panagiotis Kanavos
                var expectedCodes = new[] {HttpStatusCode.Created, HttpStatusCode.Accepted, HttpStatusCode.OK};
712 9c4346c9 Panagiotis Kanavos
                if (!expectedCodes.Contains(client.StatusCode))
713 9c4346c9 Panagiotis Kanavos
                    throw CreateWebException("CreateContainer", client.StatusCode);
714 9c4346c9 Panagiotis Kanavos
            }
715 d78cbf09 Panagiotis Kanavos
        }
716 d78cbf09 Panagiotis Kanavos
717 cfed7823 Panagiotis Kanavos
        public void DeleteContainer(string account, string container)
718 2c053915 Panagiotis Kanavos
        {
719 2c053915 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
720 2c053915 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
721 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
722 cfed7823 Panagiotis Kanavos
723 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
724 9c4346c9 Panagiotis Kanavos
            {
725 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
726 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
727 cfed7823 Panagiotis Kanavos
728 9c4346c9 Panagiotis Kanavos
                client.DeleteWithRetry(container, 3);
729 9c4346c9 Panagiotis Kanavos
                var expectedCodes = new[] {HttpStatusCode.NotFound, HttpStatusCode.NoContent};
730 9c4346c9 Panagiotis Kanavos
                if (!expectedCodes.Contains(client.StatusCode))
731 9c4346c9 Panagiotis Kanavos
                    throw CreateWebException("DeleteContainer", client.StatusCode);
732 9c4346c9 Panagiotis Kanavos
            }
733 2c053915 Panagiotis Kanavos
734 2c053915 Panagiotis Kanavos
        }
735 2c053915 Panagiotis Kanavos
736 5bcf6d70 Panagiotis Kanavos
        /// <summary>
737 5bcf6d70 Panagiotis Kanavos
        /// 
738 5bcf6d70 Panagiotis Kanavos
        /// </summary>
739 cfed7823 Panagiotis Kanavos
        /// <param name="account"></param>
740 5bcf6d70 Panagiotis Kanavos
        /// <param name="container"></param>
741 5bcf6d70 Panagiotis Kanavos
        /// <param name="objectName"></param>
742 283809f3 Panagiotis Kanavos
        /// <param name="fileName"></param>
743 5bcf6d70 Panagiotis Kanavos
        /// <returns></returns>
744 5ce54458 Panagiotis Kanavos
        /// <remarks>This method should have no timeout or a very long one</remarks>
745 5ce54458 Panagiotis Kanavos
        //Asynchronously download the object specified by *objectName* in a specific *container* to 
746 5ce54458 Panagiotis Kanavos
        // a local file
747 cfed7823 Panagiotis Kanavos
        public Task GetObject(string account, string container, string objectName, string fileName)
748 d78cbf09 Panagiotis Kanavos
        {
749 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
750 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
751 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
752 5ce54458 Panagiotis Kanavos
                throw new ArgumentNullException("objectName", "The objectName property can't be empty");            
753 5ce54458 Panagiotis Kanavos
            Contract.EndContractBlock();
754 5ce54458 Panagiotis Kanavos
755 283809f3 Panagiotis Kanavos
            try
756 283809f3 Panagiotis Kanavos
            {
757 5ce54458 Panagiotis Kanavos
                //WebClient, and by extension RestClient, are not thread-safe. Create a new RestClient
758 5ce54458 Panagiotis Kanavos
                //object to avoid concurrency errors.
759 5ce54458 Panagiotis Kanavos
                //
760 5ce54458 Panagiotis Kanavos
                //Download operations take a long time therefore they have no timeout.
761 9c4346c9 Panagiotis Kanavos
                var client = new RestClient(_baseClient) { Timeout = 0 };
762 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
763 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
764 cfed7823 Panagiotis Kanavos
765 cfed7823 Panagiotis Kanavos
                //The container and objectName are relative names. They are joined with the client's
766 cfed7823 Panagiotis Kanavos
                //BaseAddress to create the object's absolute address
767 cfed7823 Panagiotis Kanavos
                var builder = client.GetAddressBuilder(container, objectName);
768 cfed7823 Panagiotis Kanavos
                var uri = builder.Uri;
769 cfed7823 Panagiotis Kanavos
770 5ce54458 Panagiotis Kanavos
                //Download progress is reported to the Trace log
771 5120f3cb Panagiotis Kanavos
                Log.InfoFormat("[GET] START {0}", objectName);
772 283809f3 Panagiotis Kanavos
                client.DownloadProgressChanged += (sender, args) => 
773 5120f3cb Panagiotis Kanavos
                    Log.InfoFormat("[GET PROGRESS] {0} {1}% {2} of {3}",
774 283809f3 Panagiotis Kanavos
                                    fileName, args.ProgressPercentage,
775 283809f3 Panagiotis Kanavos
                                    args.BytesReceived,
776 5ce54458 Panagiotis Kanavos
                                    args.TotalBytesToReceive);                                
777 5ce54458 Panagiotis Kanavos
778 5ce54458 Panagiotis Kanavos
779 5ce54458 Panagiotis Kanavos
                //Start downloading the object asynchronously
780 5ce54458 Panagiotis Kanavos
                var downloadTask = client.DownloadFileTask(uri, fileName);
781 283809f3 Panagiotis Kanavos
                
782 5ce54458 Panagiotis Kanavos
                //Once the download completes
783 5ce54458 Panagiotis Kanavos
                return downloadTask.ContinueWith(download =>
784 283809f3 Panagiotis Kanavos
                                      {
785 5ce54458 Panagiotis Kanavos
                                          //Delete the local client object
786 283809f3 Panagiotis Kanavos
                                          client.Dispose();
787 5ce54458 Panagiotis Kanavos
                                          //And report failure or completion
788 283809f3 Panagiotis Kanavos
                                          if (download.IsFaulted)
789 283809f3 Panagiotis Kanavos
                                          {
790 5120f3cb Panagiotis Kanavos
                                              Log.ErrorFormat("[GET] FAIL for {0} with \r{1}", objectName,
791 283809f3 Panagiotis Kanavos
                                                               download.Exception);
792 283809f3 Panagiotis Kanavos
                                          }
793 283809f3 Panagiotis Kanavos
                                          else
794 283809f3 Panagiotis Kanavos
                                          {
795 5120f3cb Panagiotis Kanavos
                                              Log.InfoFormat("[GET] END {0}", objectName);                                             
796 283809f3 Panagiotis Kanavos
                                          }
797 283809f3 Panagiotis Kanavos
                                      });
798 283809f3 Panagiotis Kanavos
            }
799 283809f3 Panagiotis Kanavos
            catch (Exception exc)
800 d78cbf09 Panagiotis Kanavos
            {
801 5120f3cb Panagiotis Kanavos
                Log.ErrorFormat("[GET] END {0} with {1}", objectName, exc);
802 283809f3 Panagiotis Kanavos
                throw;
803 d78cbf09 Panagiotis Kanavos
            }
804 283809f3 Panagiotis Kanavos
805 283809f3 Panagiotis Kanavos
806 283809f3 Panagiotis Kanavos
807 d78cbf09 Panagiotis Kanavos
        }
808 d78cbf09 Panagiotis Kanavos
809 cfed7823 Panagiotis Kanavos
        public Task<IList<string>> PutHashMap(string account, string container, string objectName, TreeHash hash)
810 5ce54458 Panagiotis Kanavos
        {
811 5ce54458 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
812 5ce54458 Panagiotis Kanavos
                throw new ArgumentNullException("container");
813 5ce54458 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
814 5ce54458 Panagiotis Kanavos
                throw new ArgumentNullException("objectName");
815 5ce54458 Panagiotis Kanavos
            if (hash==null)
816 5ce54458 Panagiotis Kanavos
                throw new ArgumentNullException("hash");
817 5ce54458 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(Token))
818 5ce54458 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Token");
819 5ce54458 Panagiotis Kanavos
            if (StorageUrl == null)
820 5ce54458 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Storage Url");
821 5ce54458 Panagiotis Kanavos
            Contract.EndContractBlock();
822 cfed7823 Panagiotis Kanavos
823 cfed7823 Panagiotis Kanavos
824 cfed7823 Panagiotis Kanavos
            //Don't use a timeout because putting the hashmap may be a long process
825 cfed7823 Panagiotis Kanavos
            var client = new RestClient(_baseClient) { Timeout = 0 };
826 cfed7823 Panagiotis Kanavos
            if (!String.IsNullOrWhiteSpace(account))
827 cfed7823 Panagiotis Kanavos
                client.BaseAddress = GetAccountUrl(account);
828 cfed7823 Panagiotis Kanavos
829 5ce54458 Panagiotis Kanavos
            //The container and objectName are relative names. They are joined with the client's
830 5ce54458 Panagiotis Kanavos
            //BaseAddress to create the object's absolute address
831 cfed7823 Panagiotis Kanavos
            var builder = client.GetAddressBuilder(container, objectName);
832 5ce54458 Panagiotis Kanavos
            builder.Query = "format=json&hashmap";
833 5ce54458 Panagiotis Kanavos
            var uri = builder.Uri;
834 5ce54458 Panagiotis Kanavos
835 5ce54458 Panagiotis Kanavos
836 5ce54458 Panagiotis Kanavos
            //Send the tree hash as Json to the server            
837 5ce54458 Panagiotis Kanavos
            client.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
838 5ce54458 Panagiotis Kanavos
            var uploadTask=client.UploadStringTask(uri, "PUT", hash.ToJson());
839 5ce54458 Panagiotis Kanavos
840 5ce54458 Panagiotis Kanavos
            
841 5ce54458 Panagiotis Kanavos
            return uploadTask.ContinueWith(t =>
842 5ce54458 Panagiotis Kanavos
            {
843 a27aa447 Panagiotis Kanavos
844 a27aa447 Panagiotis Kanavos
                var empty = (IList<string>)new List<string>();
845 5ce54458 Panagiotis Kanavos
                
846 5ce54458 Panagiotis Kanavos
847 5ce54458 Panagiotis Kanavos
                //The server will respond either with 201-created if all blocks were already on the server
848 a27aa447 Panagiotis Kanavos
                if (client.StatusCode == HttpStatusCode.Created)                    
849 a27aa447 Panagiotis Kanavos
                {
850 5ce54458 Panagiotis Kanavos
                    //in which case we return an empty hash list
851 a27aa447 Panagiotis Kanavos
                    return empty;
852 a27aa447 Panagiotis Kanavos
                }
853 5ce54458 Panagiotis Kanavos
                //or with a 409-conflict and return the list of missing parts
854 5ce54458 Panagiotis Kanavos
                //A 409 will cause an exception so we need to check t.IsFaulted to avoid propagating the exception                
855 5ce54458 Panagiotis Kanavos
                if (t.IsFaulted)
856 5ce54458 Panagiotis Kanavos
                {
857 5ce54458 Panagiotis Kanavos
                    var ex = t.Exception.InnerException;
858 5ce54458 Panagiotis Kanavos
                    var we = ex as WebException;
859 5ce54458 Panagiotis Kanavos
                    var response = we.Response as HttpWebResponse;
860 5ce54458 Panagiotis Kanavos
                    if (response!=null && response.StatusCode==HttpStatusCode.Conflict)
861 5ce54458 Panagiotis Kanavos
                    {
862 5ce54458 Panagiotis Kanavos
                        //In case of 409 the missing parts will be in the response content                        
863 5ce54458 Panagiotis Kanavos
                        using (var stream = response.GetResponseStream())
864 5ce54458 Panagiotis Kanavos
                        using(var reader=new StreamReader(stream))
865 5ce54458 Panagiotis Kanavos
                        {
866 a27aa447 Panagiotis Kanavos
                            //We need to cleanup the content before returning it because it contains
867 a27aa447 Panagiotis Kanavos
                            //error content after the list of hashes
868 a27aa447 Panagiotis Kanavos
                            var hashes = new List<string>();
869 a27aa447 Panagiotis Kanavos
                            string line=null;
870 a27aa447 Panagiotis Kanavos
                            //All lines up to the first empty line are hashes
871 a27aa447 Panagiotis Kanavos
                            while(!String.IsNullOrWhiteSpace(line=reader.ReadLine()))
872 a27aa447 Panagiotis Kanavos
                            {
873 a27aa447 Panagiotis Kanavos
                                hashes.Add(line);
874 a27aa447 Panagiotis Kanavos
                            }
875 a27aa447 Panagiotis Kanavos
876 a27aa447 Panagiotis Kanavos
                            return hashes;
877 5ce54458 Panagiotis Kanavos
                        }                        
878 5ce54458 Panagiotis Kanavos
                    }
879 5ce54458 Panagiotis Kanavos
                    else
880 5ce54458 Panagiotis Kanavos
                        //Any other status code is unexpected and the exception should be rethrown
881 5ce54458 Panagiotis Kanavos
                        throw ex;
882 5ce54458 Panagiotis Kanavos
                    
883 5ce54458 Panagiotis Kanavos
                }
884 5ce54458 Panagiotis Kanavos
                //Any other status code is unexpected but there was no exception. We can probably continue processing
885 5ce54458 Panagiotis Kanavos
                else
886 5ce54458 Panagiotis Kanavos
                {
887 5120f3cb Panagiotis Kanavos
                    Log.WarnFormat("Unexcpected status code when putting map: {0} - {1}",client.StatusCode,client.StatusDescription);                    
888 5ce54458 Panagiotis Kanavos
                }
889 a27aa447 Panagiotis Kanavos
                return empty;
890 5ce54458 Panagiotis Kanavos
            });
891 5ce54458 Panagiotis Kanavos
892 5ce54458 Panagiotis Kanavos
        }
893 5ce54458 Panagiotis Kanavos
894 cfed7823 Panagiotis Kanavos
        public Task<byte[]> GetBlock(string account, string container, Uri relativeUrl, long start, long? end)
895 a27aa447 Panagiotis Kanavos
        {
896 a27aa447 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(Token))
897 a27aa447 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Token");
898 a27aa447 Panagiotis Kanavos
            if (StorageUrl == null)
899 a27aa447 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Storage Url");
900 a27aa447 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
901 a27aa447 Panagiotis Kanavos
                throw new ArgumentNullException("container");
902 a27aa447 Panagiotis Kanavos
            if (relativeUrl== null)
903 a27aa447 Panagiotis Kanavos
                throw new ArgumentNullException("relativeUrl");
904 a27aa447 Panagiotis Kanavos
            if (end.HasValue && end<0)
905 a27aa447 Panagiotis Kanavos
                throw new ArgumentOutOfRangeException("end");
906 a27aa447 Panagiotis Kanavos
            if (start<0)
907 a27aa447 Panagiotis Kanavos
                throw new ArgumentOutOfRangeException("start");
908 a27aa447 Panagiotis Kanavos
            Contract.EndContractBlock();
909 a27aa447 Panagiotis Kanavos
910 a27aa447 Panagiotis Kanavos
911 a27aa447 Panagiotis Kanavos
            //Don't use a timeout because putting the hashmap may be a long process
912 a27aa447 Panagiotis Kanavos
            var client = new RestClient(_baseClient) {Timeout = 0, RangeFrom = start, RangeTo = end};
913 cfed7823 Panagiotis Kanavos
            if (!String.IsNullOrWhiteSpace(account))
914 cfed7823 Panagiotis Kanavos
                client.BaseAddress = GetAccountUrl(account);
915 cfed7823 Panagiotis Kanavos
916 cfed7823 Panagiotis Kanavos
            var builder = client.GetAddressBuilder(container, relativeUrl.ToString());
917 cfed7823 Panagiotis Kanavos
            var uri = builder.Uri;
918 cfed7823 Panagiotis Kanavos
919 a27aa447 Panagiotis Kanavos
            return client.DownloadDataTask(uri)
920 a27aa447 Panagiotis Kanavos
                .ContinueWith(t=>
921 a27aa447 Panagiotis Kanavos
                                  {
922 a27aa447 Panagiotis Kanavos
                                      client.Dispose();
923 a27aa447 Panagiotis Kanavos
                                      return t.Result;
924 a27aa447 Panagiotis Kanavos
                                  });
925 a27aa447 Panagiotis Kanavos
        }
926 a27aa447 Panagiotis Kanavos
927 a27aa447 Panagiotis Kanavos
928 cfed7823 Panagiotis Kanavos
        public Task PostBlock(string account, string container, byte[] block, int offset, int count)
929 a27aa447 Panagiotis Kanavos
        {
930 a27aa447 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
931 a27aa447 Panagiotis Kanavos
                throw new ArgumentNullException("container");
932 a27aa447 Panagiotis Kanavos
            if (block == null)
933 a27aa447 Panagiotis Kanavos
                throw new ArgumentNullException("block");
934 0af3141d Panagiotis Kanavos
            if (offset < 0 || offset >= block.Length)
935 0af3141d Panagiotis Kanavos
                throw new ArgumentOutOfRangeException("offset");
936 0af3141d Panagiotis Kanavos
            if (count < 0 || count > block.Length)
937 0af3141d Panagiotis Kanavos
                throw new ArgumentOutOfRangeException("count");
938 a27aa447 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(Token))
939 a27aa447 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Token");
940 a27aa447 Panagiotis Kanavos
            if (StorageUrl == null)
941 0af3141d Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Storage Url");                        
942 a27aa447 Panagiotis Kanavos
            Contract.EndContractBlock();
943 a27aa447 Panagiotis Kanavos
944 cfed7823 Panagiotis Kanavos
                        
945 cfed7823 Panagiotis Kanavos
            //Don't use a timeout because putting the hashmap may be a long process
946 cfed7823 Panagiotis Kanavos
            var client = new RestClient(_baseClient) { Timeout = 0 };
947 cfed7823 Panagiotis Kanavos
            if (!String.IsNullOrWhiteSpace(account))
948 cfed7823 Panagiotis Kanavos
                client.BaseAddress = GetAccountUrl(account);
949 cfed7823 Panagiotis Kanavos
950 cfed7823 Panagiotis Kanavos
            var builder = client.GetAddressBuilder(container, "");
951 a27aa447 Panagiotis Kanavos
            //We are doing an update
952 a27aa447 Panagiotis Kanavos
            builder.Query = "update";
953 a27aa447 Panagiotis Kanavos
            var uri = builder.Uri;
954 cfed7823 Panagiotis Kanavos
955 a27aa447 Panagiotis Kanavos
            client.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
956 a27aa447 Panagiotis Kanavos
957 5120f3cb Panagiotis Kanavos
            Log.InfoFormat("[BLOCK POST] START");
958 a27aa447 Panagiotis Kanavos
959 a64c87c8 Panagiotis Kanavos
            client.UploadProgressChanged += (sender, args) => 
960 5120f3cb Panagiotis Kanavos
                Log.InfoFormat("[BLOCK POST PROGRESS] {0}% {1} of {2}",
961 a64c87c8 Panagiotis Kanavos
                                    args.ProgressPercentage, args.BytesSent,
962 a64c87c8 Panagiotis Kanavos
                                    args.TotalBytesToSend);
963 a64c87c8 Panagiotis Kanavos
            client.UploadFileCompleted += (sender, args) => 
964 5120f3cb Panagiotis Kanavos
                Log.InfoFormat("[BLOCK POST PROGRESS] Completed ");
965 a27aa447 Panagiotis Kanavos
966 a64c87c8 Panagiotis Kanavos
            
967 a64c87c8 Panagiotis Kanavos
            //Send the block
968 a64c87c8 Panagiotis Kanavos
            var uploadTask = client.UploadDataTask(uri, "POST", block)
969 a64c87c8 Panagiotis Kanavos
            .ContinueWith(upload =>
970 a27aa447 Panagiotis Kanavos
            {
971 a64c87c8 Panagiotis Kanavos
                client.Dispose();
972 a27aa447 Panagiotis Kanavos
973 a64c87c8 Panagiotis Kanavos
                if (upload.IsFaulted)
974 a27aa447 Panagiotis Kanavos
                {
975 a64c87c8 Panagiotis Kanavos
                    var exception = upload.Exception.InnerException;
976 5120f3cb Panagiotis Kanavos
                    Log.ErrorFormat("[BLOCK POST] FAIL with \r{0}", exception);                        
977 a64c87c8 Panagiotis Kanavos
                    throw exception;
978 a64c87c8 Panagiotis Kanavos
                }
979 a64c87c8 Panagiotis Kanavos
                    
980 5120f3cb Panagiotis Kanavos
                Log.InfoFormat("[BLOCK POST] END");
981 a64c87c8 Panagiotis Kanavos
            });
982 a27aa447 Panagiotis Kanavos
            return uploadTask;            
983 a27aa447 Panagiotis Kanavos
        }
984 a27aa447 Panagiotis Kanavos
985 5ce54458 Panagiotis Kanavos
986 cfed7823 Panagiotis Kanavos
        public Task<TreeHash> GetHashMap(string account, string container, string objectName)
987 5ce54458 Panagiotis Kanavos
        {
988 5ce54458 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
989 5ce54458 Panagiotis Kanavos
                throw new ArgumentNullException("container");
990 5ce54458 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
991 5ce54458 Panagiotis Kanavos
                throw new ArgumentNullException("objectName");
992 5ce54458 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(Token))
993 5ce54458 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Token");
994 5ce54458 Panagiotis Kanavos
            if (StorageUrl == null)
995 5ce54458 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Storage Url");
996 5ce54458 Panagiotis Kanavos
            Contract.EndContractBlock();
997 5ce54458 Panagiotis Kanavos
998 5ce54458 Panagiotis Kanavos
            try
999 5ce54458 Panagiotis Kanavos
            {
1000 5ce54458 Panagiotis Kanavos
                //WebClient, and by extension RestClient, are not thread-safe. Create a new RestClient
1001 5ce54458 Panagiotis Kanavos
                //object to avoid concurrency errors.
1002 5ce54458 Panagiotis Kanavos
                //
1003 5ce54458 Panagiotis Kanavos
                //Download operations take a long time therefore they have no timeout.
1004 5ce54458 Panagiotis Kanavos
                //TODO: Do we really? this is a hashmap operation, not a download
1005 5ce54458 Panagiotis Kanavos
                var client = new RestClient(_baseClient) { Timeout = 0 };
1006 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
1007 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
1008 cfed7823 Panagiotis Kanavos
1009 5ce54458 Panagiotis Kanavos
1010 cfed7823 Panagiotis Kanavos
                //The container and objectName are relative names. They are joined with the client's
1011 cfed7823 Panagiotis Kanavos
                //BaseAddress to create the object's absolute address
1012 cfed7823 Panagiotis Kanavos
                var builder = client.GetAddressBuilder(container, objectName);
1013 cfed7823 Panagiotis Kanavos
                builder.Query = "format=json&hashmap";
1014 cfed7823 Panagiotis Kanavos
                var uri = builder.Uri;
1015 cfed7823 Panagiotis Kanavos
                
1016 5ce54458 Panagiotis Kanavos
                //Start downloading the object asynchronously
1017 5ce54458 Panagiotis Kanavos
                var downloadTask = client.DownloadStringTask(uri);
1018 5ce54458 Panagiotis Kanavos
                
1019 5ce54458 Panagiotis Kanavos
                //Once the download completes
1020 5ce54458 Panagiotis Kanavos
                return downloadTask.ContinueWith(download =>
1021 5ce54458 Panagiotis Kanavos
                {
1022 5ce54458 Panagiotis Kanavos
                    //Delete the local client object
1023 5ce54458 Panagiotis Kanavos
                    client.Dispose();
1024 5ce54458 Panagiotis Kanavos
                    //And report failure or completion
1025 5ce54458 Panagiotis Kanavos
                    if (download.IsFaulted)
1026 5ce54458 Panagiotis Kanavos
                    {
1027 5120f3cb Panagiotis Kanavos
                        Log.ErrorFormat("[GET HASH] FAIL for {0} with \r{1}", objectName,
1028 5ce54458 Panagiotis Kanavos
                                        download.Exception);
1029 5ce54458 Panagiotis Kanavos
                        throw download.Exception;
1030 5ce54458 Panagiotis Kanavos
                    }
1031 5ce54458 Panagiotis Kanavos
                                          
1032 5ce54458 Panagiotis Kanavos
                    //The server will return an empty string if the file is empty
1033 5ce54458 Panagiotis Kanavos
                    var json = download.Result;
1034 5ce54458 Panagiotis Kanavos
                    var treeHash = TreeHash.Parse(json);
1035 5120f3cb Panagiotis Kanavos
                    Log.InfoFormat("[GET HASH] END {0}", objectName);                                             
1036 5ce54458 Panagiotis Kanavos
                    return treeHash;
1037 5ce54458 Panagiotis Kanavos
                });
1038 5ce54458 Panagiotis Kanavos
            }
1039 5ce54458 Panagiotis Kanavos
            catch (Exception exc)
1040 5ce54458 Panagiotis Kanavos
            {
1041 5120f3cb Panagiotis Kanavos
                Log.ErrorFormat("[GET HASH] END {0} with {1}", objectName, exc);
1042 5ce54458 Panagiotis Kanavos
                throw;
1043 5ce54458 Panagiotis Kanavos
            }
1044 5ce54458 Panagiotis Kanavos
1045 5ce54458 Panagiotis Kanavos
1046 5ce54458 Panagiotis Kanavos
1047 5ce54458 Panagiotis Kanavos
        }
1048 5ce54458 Panagiotis Kanavos
1049 5ce54458 Panagiotis Kanavos
1050 5bcf6d70 Panagiotis Kanavos
        /// <summary>
1051 5bcf6d70 Panagiotis Kanavos
        /// 
1052 5bcf6d70 Panagiotis Kanavos
        /// </summary>
1053 cfed7823 Panagiotis Kanavos
        /// <param name="account"></param>
1054 5bcf6d70 Panagiotis Kanavos
        /// <param name="container"></param>
1055 5bcf6d70 Panagiotis Kanavos
        /// <param name="objectName"></param>
1056 5bcf6d70 Panagiotis Kanavos
        /// <param name="fileName"></param>
1057 b5061ac8 Panagiotis Kanavos
        /// <param name="hash">Optional hash value for the file. If no hash is provided, the method calculates a new hash</param>
1058 5bcf6d70 Panagiotis Kanavos
        /// <remarks>>This method should have no timeout or a very long one</remarks>
1059 cfed7823 Panagiotis Kanavos
        public Task PutObject(string account, string container, string objectName, string fileName, string hash = null)
1060 d78cbf09 Panagiotis Kanavos
        {
1061 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
1062 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
1063 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
1064 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("objectName", "The objectName property can't be empty");
1065 b6c72f62 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(fileName))
1066 b6c72f62 Panagiotis Kanavos
                throw new ArgumentNullException("fileName", "The fileName property can't be empty");
1067 637bc368 Panagiotis Kanavos
            if (!File.Exists(fileName))
1068 637bc368 Panagiotis Kanavos
                throw new FileNotFoundException("The file does not exist",fileName);
1069 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
1070 b6c72f62 Panagiotis Kanavos
            
1071 b5061ac8 Panagiotis Kanavos
            try
1072 b5061ac8 Panagiotis Kanavos
            {
1073 cfed7823 Panagiotis Kanavos
1074 cfed7823 Panagiotis Kanavos
                var client = new RestClient(_baseClient){Timeout=0};
1075 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
1076 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
1077 cfed7823 Panagiotis Kanavos
1078 cfed7823 Panagiotis Kanavos
                var builder = client.GetAddressBuilder(container, objectName);
1079 5ce54458 Panagiotis Kanavos
                var uri = builder.Uri;
1080 0eea575a Panagiotis Kanavos
1081 0eea575a Panagiotis Kanavos
                string etag = hash ?? CalculateHash(fileName);
1082 0eea575a Panagiotis Kanavos
1083 0eea575a Panagiotis Kanavos
                client.Headers.Add("Content-Type", "application/octet-stream");
1084 0eea575a Panagiotis Kanavos
                client.Headers.Add("ETag", etag);
1085 0eea575a Panagiotis Kanavos
1086 0eea575a Panagiotis Kanavos
1087 5120f3cb Panagiotis Kanavos
                Log.InfoFormat("[PUT] START {0}", objectName);
1088 0eea575a Panagiotis Kanavos
                client.UploadProgressChanged += (sender, args) =>
1089 0eea575a Panagiotis Kanavos
                {
1090 5120f3cb Panagiotis Kanavos
                    using (log4net.ThreadContext.Stacks["PUT"].Push("Progress"))
1091 5120f3cb Panagiotis Kanavos
                    {
1092 5120f3cb Panagiotis Kanavos
                        Log.InfoFormat("{0} {1}% {2} of {3}", fileName, args.ProgressPercentage,
1093 5120f3cb Panagiotis Kanavos
                                       args.BytesSent, args.TotalBytesToSend);
1094 5120f3cb Panagiotis Kanavos
                    }
1095 0eea575a Panagiotis Kanavos
                };
1096 bfc13ed8 Panagiotis Kanavos
1097 bfc13ed8 Panagiotis Kanavos
                client.UploadFileCompleted += (sender, args) =>
1098 bfc13ed8 Panagiotis Kanavos
                {
1099 5120f3cb Panagiotis Kanavos
                    using (log4net.ThreadContext.Stacks["PUT"].Push("Progress"))
1100 5120f3cb Panagiotis Kanavos
                    {
1101 5120f3cb Panagiotis Kanavos
                        Log.InfoFormat("Completed {0}", fileName);
1102 5120f3cb Panagiotis Kanavos
                    }
1103 bfc13ed8 Panagiotis Kanavos
                };
1104 0eea575a Panagiotis Kanavos
                return client.UploadFileTask(uri, "PUT", fileName)
1105 0eea575a Panagiotis Kanavos
                    .ContinueWith(upload=>
1106 0eea575a Panagiotis Kanavos
                                      {
1107 0eea575a Panagiotis Kanavos
                                          client.Dispose();
1108 0eea575a Panagiotis Kanavos
1109 0eea575a Panagiotis Kanavos
                                          if (upload.IsFaulted)
1110 a27aa447 Panagiotis Kanavos
                                          {
1111 a27aa447 Panagiotis Kanavos
                                              var exc = upload.Exception.InnerException;
1112 5120f3cb Panagiotis Kanavos
                                              Log.ErrorFormat("[PUT] FAIL for {0} with \r{1}",objectName,exc);
1113 a27aa447 Panagiotis Kanavos
                                              throw exc;
1114 0eea575a Panagiotis Kanavos
                                          }
1115 0eea575a Panagiotis Kanavos
                                          else
1116 5120f3cb Panagiotis Kanavos
                                            Log.InfoFormat("[PUT] END {0}", objectName);
1117 0eea575a Panagiotis Kanavos
                                      });
1118 b5061ac8 Panagiotis Kanavos
            }
1119 b5061ac8 Panagiotis Kanavos
            catch (Exception exc)
1120 b5061ac8 Panagiotis Kanavos
            {
1121 5120f3cb Panagiotis Kanavos
                Log.ErrorFormat("[PUT] END {0} with {1}", objectName, exc);
1122 b5061ac8 Panagiotis Kanavos
                throw;
1123 b5061ac8 Panagiotis Kanavos
            }                
1124 b5061ac8 Panagiotis Kanavos
1125 0eea575a Panagiotis Kanavos
        }
1126 0050438e Panagiotis Kanavos
       
1127 4d301e8e Panagiotis Kanavos
        
1128 b6c72f62 Panagiotis Kanavos
        private static string CalculateHash(string fileName)
1129 d78cbf09 Panagiotis Kanavos
        {
1130 0bd56b7c Panagiotis Kanavos
            Contract.Requires(!String.IsNullOrWhiteSpace(fileName));
1131 0bd56b7c Panagiotis Kanavos
            Contract.EndContractBlock();
1132 0bd56b7c Panagiotis Kanavos
1133 d78cbf09 Panagiotis Kanavos
            string hash;
1134 d78cbf09 Panagiotis Kanavos
            using (var hasher = MD5.Create())
1135 b6c72f62 Panagiotis Kanavos
            using(var stream=File.OpenRead(fileName))
1136 d78cbf09 Panagiotis Kanavos
            {
1137 d78cbf09 Panagiotis Kanavos
                var hashBuilder=new StringBuilder();
1138 b6c72f62 Panagiotis Kanavos
                foreach (byte b in hasher.ComputeHash(stream))
1139 d78cbf09 Panagiotis Kanavos
                    hashBuilder.Append(b.ToString("x2").ToLower());
1140 d78cbf09 Panagiotis Kanavos
                hash = hashBuilder.ToString();                
1141 d78cbf09 Panagiotis Kanavos
            }
1142 d78cbf09 Panagiotis Kanavos
            return hash;
1143 d78cbf09 Panagiotis Kanavos
        }
1144 0bd56b7c Panagiotis Kanavos
        
1145 cfed7823 Panagiotis Kanavos
        public void MoveObject(string account, string sourceContainer, string oldObjectName, string targetContainer, string newObjectName)
1146 d78cbf09 Panagiotis Kanavos
        {
1147 3c43ec9b Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(sourceContainer))
1148 3c43ec9b Panagiotis Kanavos
                throw new ArgumentNullException("sourceContainer", "The container property can't be empty");
1149 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(oldObjectName))
1150 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("oldObjectName", "The oldObjectName property can't be empty");
1151 3c43ec9b Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(targetContainer))
1152 3c43ec9b Panagiotis Kanavos
                throw new ArgumentNullException("targetContainer", "The container property can't be empty");
1153 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(newObjectName))
1154 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("newObjectName", "The newObjectName property can't be empty");
1155 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
1156 d78cbf09 Panagiotis Kanavos
1157 3c43ec9b Panagiotis Kanavos
            var targetUrl = targetContainer + "/" + newObjectName;
1158 3c43ec9b Panagiotis Kanavos
            var sourceUrl = String.Format("/{0}/{1}", sourceContainer, oldObjectName);
1159 3c43ec9b Panagiotis Kanavos
1160 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
1161 d78cbf09 Panagiotis Kanavos
            {
1162 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
1163 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
1164 cfed7823 Panagiotis Kanavos
1165 0af3141d Panagiotis Kanavos
                client.Headers.Add("X-Move-From", sourceUrl);
1166 9c4346c9 Panagiotis Kanavos
                client.PutWithRetry(targetUrl, 3);
1167 9c4346c9 Panagiotis Kanavos
1168 9c4346c9 Panagiotis Kanavos
                var expectedCodes = new[] {HttpStatusCode.OK, HttpStatusCode.NoContent, HttpStatusCode.Created};
1169 0af3141d Panagiotis Kanavos
                if (!expectedCodes.Contains(client.StatusCode))
1170 9c4346c9 Panagiotis Kanavos
                    throw CreateWebException("MoveObject", client.StatusCode);
1171 9c4346c9 Panagiotis Kanavos
            }
1172 d78cbf09 Panagiotis Kanavos
        }
1173 d78cbf09 Panagiotis Kanavos
1174 c53aa229 Panagiotis Kanavos
        public void DeleteObject(string account, string sourceContainer, string objectName)
1175 0af3141d Panagiotis Kanavos
        {            
1176 0af3141d Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(sourceContainer))
1177 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException("sourceContainer", "The container property can't be empty");
1178 0af3141d Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
1179 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException("objectName", "The oldObjectName property can't be empty");
1180 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
1181 0af3141d Panagiotis Kanavos
1182 c53aa229 Panagiotis Kanavos
            var targetUrl = FolderConstants.TrashContainer + "/" + objectName;
1183 0af3141d Panagiotis Kanavos
            var sourceUrl = String.Format("/{0}/{1}", sourceContainer, objectName);
1184 0af3141d Panagiotis Kanavos
1185 0af3141d Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
1186 0af3141d Panagiotis Kanavos
            {
1187 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
1188 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
1189 cfed7823 Panagiotis Kanavos
1190 0af3141d Panagiotis Kanavos
                client.Headers.Add("X-Move-From", sourceUrl);
1191 c53aa229 Panagiotis Kanavos
                client.AllowedStatusCodes.Add(HttpStatusCode.NotFound);
1192 0af3141d Panagiotis Kanavos
                client.PutWithRetry(targetUrl, 3);
1193 0af3141d Panagiotis Kanavos
1194 0af3141d Panagiotis Kanavos
                var expectedCodes = new[] {HttpStatusCode.OK, HttpStatusCode.NoContent, HttpStatusCode.Created,HttpStatusCode.NotFound};
1195 0af3141d Panagiotis Kanavos
                if (!expectedCodes.Contains(client.StatusCode))
1196 0af3141d Panagiotis Kanavos
                    throw CreateWebException("DeleteObject", client.StatusCode);
1197 0af3141d Panagiotis Kanavos
            }
1198 0af3141d Panagiotis Kanavos
        }
1199 0af3141d Panagiotis Kanavos
1200 4d301e8e Panagiotis Kanavos
      
1201 3c43ec9b Panagiotis Kanavos
        private static WebException CreateWebException(string operation, HttpStatusCode statusCode)
1202 3c43ec9b Panagiotis Kanavos
        {
1203 3c43ec9b Panagiotis Kanavos
            return new WebException(String.Format("{0} failed with unexpected status code {1}", operation, statusCode));
1204 3c43ec9b Panagiotis Kanavos
        }
1205 3c43ec9b Panagiotis Kanavos
1206 d15e99b4 Panagiotis Kanavos
        
1207 d78cbf09 Panagiotis Kanavos
    }
1208 cfed7823 Panagiotis Kanavos
1209 cfed7823 Panagiotis Kanavos
    public class ShareAccountInfo
1210 cfed7823 Panagiotis Kanavos
    {
1211 cfed7823 Panagiotis Kanavos
        public DateTime? last_modified { get; set; }
1212 cfed7823 Panagiotis Kanavos
        public string name { get; set; }
1213 cfed7823 Panagiotis Kanavos
    }
1214 d78cbf09 Panagiotis Kanavos
}