using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Text.RegularExpressions; using AppLimit.CloudComputing.SharpBox.Common.IO; using AppLimit.CloudComputing.SharpBox.Common.Net; using AppLimit.CloudComputing.SharpBox.Common.Net.Web; using AppLimit.CloudComputing.SharpBox.Common.Net.Web.Dav; using AppLimit.CloudComputing.SharpBox.Exceptions; using AppLimit.CloudComputing.SharpBox.StorageProvider.API; using AppLimit.CloudComputing.SharpBox.StorageProvider.BaseObjects; namespace AppLimit.CloudComputing.SharpBox.StorageProvider.WebDav.Logic { internal class WebDavStorageProviderService : GenericStorageProviderService { #region IStorageProviderService Members /// /// Verifies if the given credentials are valid webdav credentials /// /// /// public override bool VerifyAccessTokenType(ICloudStorageAccessToken token) { return (token is ICredentials); } /// /// This method generates a session to a webdav share via username and password /// /// /// /// public override IStorageProviderSession CreateSession(ICloudStorageAccessToken token, ICloudStorageConfiguration configuration) { // cast the creds to the right type var config = configuration as WebDavConfiguration; // build service var svc = new DavService(); // check if url available int status; WebException e; svc.PerformSimpleWebCall(config.ServiceLocator.ToString(), WebRequestMethodsEx.WebDAV.Options, (token as ICredentials).GetCredential(null, null), null, out status, out e); if (status == (int)HttpStatusCode.Unauthorized) throw new UnauthorizedAccessException(); if (HttpUtilityEx.IsSuccessCode(status)) return new WebDavStorageProviderSession(token, config, this); return null; } /// /// This method request information about a resource /// /// /// /// /// public override ICloudFileSystemEntry RequestResource(IStorageProviderSession session, string name, ICloudDirectoryEntry parent) { // build url var uriString = GetResourceUrl(session, parent, null); uriString = PathHelper.Combine(uriString, name); // get the data List childs; var requestResource = RequestResourceFromWebDavShare(session, uriString, out childs); // check errors if (requestResource == null) return null; // rename the root if (name.Equals("/")) requestResource.Name = "/"; // init parent child relation if (parent != null) { var parentDir = parent as BaseDirectoryEntry; parentDir.AddChild(requestResource); } // check if we have to add childs if (!(requestResource is BaseDirectoryEntry)) return requestResource; var requestedDir = requestResource as BaseDirectoryEntry; // add the childs foreach (var child in childs) { requestedDir.AddChild(child); } // go ahead return requestResource; } public override void RefreshResource(IStorageProviderSession session, ICloudFileSystemEntry resource) { // nothing to do for files if (!(resource is ICloudDirectoryEntry)) return; // build url var uriString = GetResourceUrl(session, resource, null); // get the data List childs; RequestResourceFromWebDavShare(session, uriString, out childs); // set the new childs collection var dirEntry = resource as BaseDirectoryEntry; dirEntry.ClearChilds(); // add the new childs if (childs != null) dirEntry.AddChilds(childs); } /// /// /// /// /// /// /// public override ICloudFileSystemEntry CreateResource(IStorageProviderSession session, string name, ICloudDirectoryEntry parent) { // get the credentials var creds = session.SessionToken as ICredentials; // build url var uriString = GetResourceUrl(session, parent, null); uriString = PathHelper.Combine(uriString, name); var uri = new Uri(uriString); // build the DavService var svc = new DavService(); // create the webrequest int errorCode; WebException e; svc.PerformSimpleWebCall(uri.ToString(), WebRequestMethodsEx.Http.MkCol, creds.GetCredential(null, null), null, out errorCode, out e); if (errorCode != (int)HttpStatusCode.Created) return null; var newDir = new BaseDirectoryEntry(name, 0, DateTime.Now, this, session); // init parent child relation if (parent != null) { var parentDir = parent as BaseDirectoryEntry; parentDir.AddChild(newDir); } return newDir; } /// /// This method removes a specific resource from a webdav share /// /// /// public override bool DeleteResource(IStorageProviderSession session, ICloudFileSystemEntry entry) { // get the credentials var creds = session.SessionToken as ICredentials; // build url var uriString = GetResourceUrl(session, entry, null); var uri = new Uri(uriString); // create the service var svc = new DavService(); // create the webrequest int errorCode; WebException e; svc.PerformSimpleWebCall(uri.ToString(), WebRequestMethodsEx.WebDAV.Delete, creds.GetCredential(null, null), null, out errorCode, out e); if (!HttpUtilityEx.IsSuccessCode(errorCode)) return false; // remove from parent var parentDir = entry.Parent as BaseDirectoryEntry; if (parentDir != null) { parentDir.RemoveChildById(entry.Id); } return true; } /// /// This method moves a resource from one webdav location to an other /// /// /// /// /// public override bool MoveResource(IStorageProviderSession session, ICloudFileSystemEntry fsentry, ICloudDirectoryEntry newParent) { // build the targte url var uriStringTarget = GetResourceUrl(session, newParent, null); uriStringTarget = PathHelper.Combine(uriStringTarget, fsentry.Name); if (!MoveResource(session, fsentry, uriStringTarget)) return false; // readjust parent var oldParent = fsentry.Parent as BaseDirectoryEntry; if (oldParent != null) { oldParent.RemoveChild(fsentry as BaseFileEntry); } var newParentObject = newParent as BaseDirectoryEntry; if (newParentObject != null) { newParentObject.AddChild(fsentry as BaseFileEntry); } return true; } public override bool CopyResource(IStorageProviderSession session, ICloudFileSystemEntry fsentry, ICloudDirectoryEntry newParent) { // build the targte url var uriStringTarget = GetResourceUrl(session, newParent, null); uriStringTarget = PathHelper.Combine(uriStringTarget, fsentry.Name); if (!CopyResource(session, fsentry, uriStringTarget)) return false; var newParentObject = newParent as BaseDirectoryEntry; if (newParentObject != null) { newParentObject.AddChild(fsentry as BaseFileEntry); } return true; } /// /// Renames a webdave file or folder /// /// /// /// /// public override bool RenameResource(IStorageProviderSession session, ICloudFileSystemEntry fsentry, string newName) { // build the targte url var uriStringTarget = GetResourceUrl(session, fsentry.Parent, null); uriStringTarget = PathHelper.Combine(uriStringTarget, newName); if (MoveResource(session, fsentry, uriStringTarget)) { // rename the fsentry var fentry = fsentry as BaseFileEntry; fentry.Name = newName; // go ahead return true; } // go ahead return false; } #endregion public override Stream CreateDownloadStream(IStorageProviderSession session, ICloudFileSystemEntry fileSystemEntry) { // build the url var url = GetResourceUrl(session, fileSystemEntry, null); // get the session creds var creds = session.SessionToken as ICredentials; // Build the service var svc = new DavService(); // create the response var response = CreateDownloadResponse(url, creds.GetCredential(null, null)); // get the data var orgStream = svc.GetResponseStream(response); var dStream = new BaseFileEntryDownloadStream(orgStream, fileSystemEntry); // put the disposable on the stack dStream._DisposableObjects.Push(response); // go ahead return dStream; } private WebResponse CreateDownloadResponse(string url, ICredentials creds) { // Build the service var svc = new DavService(); // create the webrequest var request = svc.CreateWebRequest(url, WebRequestMethodsEx.Http.Get, creds, false, null); try { // create the response var response = svc.GetWebResponse(request); return response; } catch (WebException e) { if (e.Response is HttpWebResponse) { var code = (e.Response as HttpWebResponse).StatusCode; if (code == HttpStatusCode.Moved) { // get the new uri var newUri = e.Response.Headers["Location"]; // redo it return CreateDownloadResponse(newUri, creds); } NetworkCredential networkCredential; if (code == HttpStatusCode.Unauthorized && (networkCredential = creds as NetworkCredential) != null) { var search = new Regex(@"^\w+", RegexOptions.Singleline | RegexOptions.IgnoreCase); // get authentication method var authMethod = search.Match(e.Response.Headers["WWW-Authenticate"]).Value; var newCredentials = new CredentialCache { { new Uri((new Uri(url)).GetLeftPart(UriPartial.Authority)), authMethod, networkCredential } }; // redo it return CreateDownloadResponse(url, newCredentials); } } throw; } } public override Stream CreateUploadStream(IStorageProviderSession session, ICloudFileSystemEntry fileSystemEntry, long uploadSize) { // build the url var url = GetResourceUrl(session, fileSystemEntry, null); // get the session creds var creds = session.SessionToken as ICredentials; // Build the service var svc = new DavService(); // get the service config var conf = (WebDavConfiguration)session.ServiceConfiguration; // build the webrequest var networkRequest = svc.CreateWebRequestPUT(url, creds.GetCredential(null, null), conf.UploadDataStreambuffered); // get the request stream var requestStream = svc.GetRequestStream(networkRequest, uploadSize); // add disposal opp requestStream.PushPostDisposeOperation(CommitUploadStream, svc, networkRequest, fileSystemEntry, requestStream); // go ahead return requestStream; } public override bool SupportsDirectRetrieve { get { return false; } } public void CommitUploadStream(params object[] arg) { // convert the args var svc = arg[0] as DavService; var uploadRequest = arg[1] as HttpWebRequest; var fileSystemEntry = arg[2] as BaseFileEntry; var requestStream = arg[3] as WebRequestStream; // check if all data was written into stream if (requestStream.WrittenBytes != uploadRequest.ContentLength) // nothing todo request was aborted return; // perform the request int code; WebException e; svc.PerformWebRequest(uploadRequest, null, out code, out e); // check the ret value if (!HttpUtilityEx.IsSuccessCode(code)) SharpBoxException.ThrowSharpBoxExceptionBasedOnHttpErrorCode(uploadRequest, (HttpStatusCode)code, e); // adjust the lengt fileSystemEntry.Length = uploadRequest.ContentLength; } public override void CommitStreamOperation(IStorageProviderSession session, ICloudFileSystemEntry fileSystemEntry, nTransferDirection direction, Stream notDisposedStream) { } #region Helper private static bool MoveResource(IStorageProviderSession session, ICloudFileSystemEntry fsentry, string newTargetUrl) { var config = session.ServiceConfiguration as WebDavConfiguration; var creds = session.SessionToken as ICredentials; var uriString = PathHelper.Combine(config.ServiceLocator.ToString(), GenericHelper.GetResourcePath(fsentry)); var uri = new Uri(uriString); var uriTarget = new Uri(newTargetUrl); var errorCode = new DavService().PerformMoveWebRequest(uri.ToString(), uriTarget.ToString(), creds.GetCredential(null, null)); return errorCode == HttpStatusCode.Created || errorCode == HttpStatusCode.NoContent; } private bool CopyResource(IStorageProviderSession session, ICloudFileSystemEntry fsentry, string newTargetUrl) { var config = session.ServiceConfiguration as WebDavConfiguration; var creds = session.SessionToken as ICredentials; var uriString = PathHelper.Combine(config.ServiceLocator.ToString(), GenericHelper.GetResourcePath(fsentry)); var uri = new Uri(uriString); var uriTarget = new Uri(newTargetUrl); var errorCode = new DavService().PerformCopyWebRequest(uri.ToString(), uriTarget.ToString(), creds.GetCredential(null, null)); return errorCode == HttpStatusCode.Created || errorCode == HttpStatusCode.NoContent || errorCode == HttpStatusCode.Accepted && uriString.StartsWith(WebDavConfiguration.YaUrl); } private BaseFileEntry RequestResourceFromWebDavShare(IStorageProviderSession session, string resourceUrl, out List childs) { // get the credebtials var creds = session.SessionToken as ICredentials; return RequestResourceFromWebDavShare(session, creds.GetCredential(null, null), resourceUrl, out childs); } private BaseFileEntry RequestResourceFromWebDavShare(IStorageProviderSession session, ICredentials creds, string resourceUrl, out List childs) { // build the dav service var svc = new DavService(); try { // create the web request var request = svc.CreateWebRequestPROPFIND(resourceUrl, creds); // the result WebDavRequestResult requestResult; // get the response using (var response = svc.GetWebResponse(request) as HttpWebResponse) { if (response.StatusCode == HttpStatusCode.Moved) { // get the new uri var newUri = response.Headers["Location"]; (session.ServiceConfiguration as WebDavConfiguration).ServiceLocator = new Uri(newUri); // close the response response.Close(); // redo it return RequestResourceFromWebDavShare(session, creds, newUri, out childs); } // get the response stream using (var data = svc.GetResponseStream(response)) { if (data == null) { childs = null; return null; } // build the file entries requestResult = WebDavRequestParser.CreateObjectsFromNetworkStream(data, resourceUrl, this, session, WebDavNameBaseFilterCallback); // close the stream data.Close(); } // close the response response.Close(); } // get the request fileentry and fill the childs if (requestResult.Self == null) { childs = null; return null; } // set the childs childs = requestResult.Childs; // go ahead return requestResult.Self; } catch (WebException e) { if (e.Response is HttpWebResponse) { var code = (e.Response as HttpWebResponse).StatusCode; if (code == HttpStatusCode.Moved) { // get the new uri var newUri = e.Response.Headers["Location"]; (session.ServiceConfiguration as WebDavConfiguration).ServiceLocator = new Uri(newUri); // redo it return RequestResourceFromWebDavShare(session, creds, newUri, out childs); } NetworkCredential networkCredential; if (code == HttpStatusCode.Unauthorized && (networkCredential = creds as NetworkCredential) != null) { var search = new Regex(@"^\w+", RegexOptions.Singleline | RegexOptions.IgnoreCase); // get authentication method var authMethod = search.Match(e.Response.Headers["WWW-Authenticate"]).Value; var newCredentials = new CredentialCache { { new Uri((new Uri(resourceUrl)).GetLeftPart(UriPartial.Authority)), authMethod, networkCredential } }; // redo it return RequestResourceFromWebDavShare(session, newCredentials, resourceUrl, out childs); } } childs = null; return null; } } private string WebDavNameBaseFilterCallback(string targetUrl, IStorageProviderService service, IStorageProviderSession session, string NameBase) { // call our virtual method return OnNameBase(targetUrl, service, session, NameBase); } protected virtual string OnNameBase(string targetUrl, IStorageProviderService service, IStorageProviderSession session, string nameBase) { return nameBase; } #endregion } }