using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Security; using System.Runtime.Serialization; using System.Security.Cryptography.X509Certificates; using AppLimit.CloudComputing.SharpBox.Common.IO; using AppLimit.CloudComputing.SharpBox.Exceptions; using AppLimit.CloudComputing.SharpBox.StorageProvider.BoxNet; using AppLimit.CloudComputing.SharpBox.StorageProvider.CIFS; using AppLimit.CloudComputing.SharpBox.StorageProvider.DropBox; using AppLimit.CloudComputing.SharpBox.StorageProvider.FTP; using AppLimit.CloudComputing.SharpBox.StorageProvider.GoogleDocs; using AppLimit.CloudComputing.SharpBox.StorageProvider.WebDav; namespace AppLimit.CloudComputing.SharpBox { /// /// The class CloudStorage implements all needed methods to get access /// to a supported cloud storage infrastructure. The following vendors /// are currently supported: /// /// - DropBox /// /// public partial class CloudStorage : ICloudStoragePublicAPI { // some information for token storage internal const string TokenProviderConfigurationType = "TokenProvConfigType"; internal const string TokenCredentialType = "TokenCredType"; internal const string TokenMetadataPrefix = "TokenMetadata"; #region Member Declarations private ICloudStorageProviderInternal _provider; private ICloudStorageConfiguration _configuration; private readonly Dictionary _configurationProviderMap; #endregion #region Constructure and logistics /// /// returns the currently setted access token /// public ICloudStorageAccessToken CurrentAccessToken { get { if (_provider == null) return null; return _provider.CurrentAccessToken; } } /// /// Allows access to the current configuration which was used in the open call /// public ICloudStorageConfiguration CurrentConfiguration { get { return _configuration; } } /// /// The default constructure for a cloudstorage /// public CloudStorage() { // build config provider _configurationProviderMap = new Dictionary(); // register provider RegisterStorageProvider(typeof(DropBoxConfiguration), typeof(DropBoxStorageProvider)); RegisterStorageProvider(typeof(WebDavConfiguration), typeof(WebDavStorageProvider)); RegisterStorageProvider(typeof(BoxNetConfiguration), typeof(BoxNetStorageProvider)); RegisterStorageProvider(typeof(FtpConfiguration), typeof(FtpStorageProvider)); RegisterStorageProvider(typeof(CIFSConfiguration), typeof(CIFSStorageProvider)); RegisterStorageProvider(typeof(GoogleDocsConfiguration), typeof(GoogleDocsStorageProvider)); RegisterStorageProvider(typeof(StorageProvider.SkyDrive.SkyDriveConfiguration), typeof(StorageProvider.SkyDrive.Logic.SkyDriveStorageProvider)); } /// /// copy ctor /// /// public CloudStorage(CloudStorage src) : this(src, true) { } /// /// copy ctor /// /// /// public CloudStorage(CloudStorage src, bool openIfSourceWasOpen) : this() { // copy all registered provider from src _configurationProviderMap = src._configurationProviderMap; // open the provider if (src.IsOpened && openIfSourceWasOpen) Open(src._configuration, src.CurrentAccessToken); else _configuration = src._configuration; } /// /// This method allows to register a storage provider for a specific configuration /// type /// /// /// A /// /// /// A /// /// /// A /// public bool RegisterStorageProvider(Type configurationType, Type storageProviderType) { // do double check if (_configurationProviderMap.ContainsKey(configurationType.FullName)) return false; // register _configurationProviderMap.Add(configurationType.FullName, storageProviderType); // go ahead return true; } /// /// Ignores all invalid ssl certs /// /// /// /// /// /// private static bool ValidateAllServerCertificates(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; } #endregion #region Base Functions /// /// True when a the connection to the Cloudstorage will be established /// public bool IsOpened { get; private set; } /// /// Calling this method with vendor specific configuration settings and token based credentials /// to get access to the cloud storage. The following exceptions are possible: /// /// - System.UnauthorizedAccessException when the user provides wrong credentials /// - AppLimit.CloudComputing.SharpBox.Exceptions.SharpBoxException when something /// happens during cloud communication /// /// /// Vendor specific configuration of the cloud storage /// Vendor specific authorization token /// A valid access token or null public ICloudStorageAccessToken Open(ICloudStorageConfiguration configuration, ICloudStorageAccessToken token) { // check state if (IsOpened) return null; // ensures that the right provider will be used SetProviderByConfiguration(configuration); // save the configuration _configuration = configuration; // verify the ssl config if (configuration.TrustUnsecureSSLConnections) System.Net.ServicePointManager.ServerCertificateValidationCallback = ValidateAllServerCertificates; // update the max connection settings ServicePointManager.DefaultConnectionLimit = 250; // disable the not well implementes Expected100 header settings ServicePointManager.Expect100Continue = false; // open the cloud connection token = _provider.Open(configuration, token); // ok without Exception every is good IsOpened = true; // return the token return token; } /// /// This method will close the connection to the cloud storage system /// public void Close() { if (_provider == null) return; _provider.Close(); IsOpened = false; } /// /// This method returns access to the root folder of the storage system /// /// Reference to the root folder of the storage system public ICloudDirectoryEntry GetRoot() { return _provider == null ? null : _provider.GetRoot(); } /// /// This method returns access ro a specific subfolder or file addressed via /// unix like file system path, e.g. /Public/SubFolder/SubSub /// /// Valid Exceptions are: /// SharpBoxException(nSharpBoxErrorCodes.ErrorInvalidFileOrDirectoryName); /// SharpBoxException(nSharpBoxErrorCodes.ErrorFileNotFound); /// /// The path to the target subfolder /// The startfolder for the searchpath /// public ICloudFileSystemEntry GetFileSystemObject(string name, ICloudDirectoryEntry parent) { return _provider == null ? null : _provider.GetFileSystemObject(name, parent); } /// /// This method creates a child folder in the give folder /// of the cloud storage /// /// Name of the new folder /// Parent folder /// Reference to the created folder public ICloudDirectoryEntry CreateFolder(string name, ICloudDirectoryEntry parent) { return _provider == null ? null : _provider.CreateFolder(name, parent); } /// /// This method removes a file or a directory from /// the cloud storage /// /// Reference to the filesystem object which has to be removed /// Returns true or false public bool DeleteFileSystemEntry(ICloudFileSystemEntry fsentry) { return _provider != null && _provider.DeleteFileSystemEntry(fsentry); } /// /// This method moves a file or a directory from its current /// location to a new onw /// /// Filesystem object which has to be moved /// The new location of the targeted filesystem object /// public bool MoveFileSystemEntry(ICloudFileSystemEntry fsentry, ICloudDirectoryEntry newParent) { return _provider == null ? false : _provider.MoveFileSystemEntry(fsentry, newParent); } /// /// This method copy a file or a directory from its current /// location to a new onw /// /// /// /// public bool CopyFileSystemEntry(ICloudFileSystemEntry fsentry, ICloudDirectoryEntry newParent) { return _provider == null ? false : _provider.CopyFileSystemEntry(fsentry, newParent); } /// /// This mehtod allows to perform a server side renam operation which is basicly the same /// then a move operation in the same directory /// /// /// /// public bool RenameFileSystemEntry(ICloudFileSystemEntry fsentry, string newName) { return _provider == null ? false : _provider.RenameFileSystemEntry(fsentry, newName); } /// /// This method creates a new file object in the cloud storage. Use the GetContentStream method to /// get a .net stream which usable in the same way then local stream are usable. /// /// Link to the parent container, null means the root directory /// The name of the targeted file /// public ICloudFileSystemEntry CreateFile(ICloudDirectoryEntry parent, string name) { // pass through the provider return _provider == null ? null : _provider.CreateFile(parent, name); } /// /// This method returns the direct URL to a specific file system object, /// e.g. a file or folder /// /// A relative path to the file /// A reference to the parent of the path /// public Uri GetFileSystemObjectUrl(string path, ICloudDirectoryEntry parent) { // pass through the provider return _provider == null ? null : _provider.GetFileSystemObjectUrl(path, parent); } /// /// Returns the path of the targeted object /// /// /// public string GetFileSystemObjectPath(ICloudFileSystemEntry fsObject) { // pass through the provider return _provider == null ? null : _provider.GetFileSystemObjectPath(fsObject); } #endregion #region AccessTokenHandling /// /// This method allows to store a security token into a serialization stream /// /// /// public Stream SerializeSecurityToken(ICloudStorageAccessToken token) { return SerializeSecurityToken(token, null); } /// /// This method allows to store a security token into a serialization stream and /// makes it possible to store a couple of meta data as well /// /// /// /// public Stream SerializeSecurityToken(ICloudStorageAccessToken token, Dictionary additionalMetaData) { if (!IsOpened) throw new SharpBoxException(SharpBoxErrorCodes.ErrorOpenedConnectionNeeded); return SerializeSecurityTokenEx(token, _configuration.GetType(), additionalMetaData); } /// /// This method can be used for serialize a token without have the connection opened :-) /// /// /// /// /// public Stream SerializeSecurityTokenEx(ICloudStorageAccessToken token, Type configurationType, Dictionary additionalMetaData) { var items = new Dictionary(); var stream = new MemoryStream(); var serializer = new DataContractSerializer(items.GetType()); // add the metadata if (additionalMetaData != null) { foreach (KeyValuePair kvp in additionalMetaData) { items.Add(TokenMetadataPrefix + kvp.Key, kvp.Value); } } // save the token into our list StoreToken(items, token, configurationType); // write the data to stream serializer.WriteObject(stream, items); // go to start stream.Seek(0, SeekOrigin.Begin); // go ahead return stream; } /// /// This method stores a token into a file /// /// /// /// /// public void SerializeSecurityTokenEx(ICloudStorageAccessToken token, Type configurationType, Dictionary additionalMetaData, string fileName) { using (var fs = File.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.None)) { using (var ts = SerializeSecurityTokenEx(token, configurationType, additionalMetaData)) { // copy StreamHelper.CopyStreamData(this, ts, fs, null, null); } // flush fs.Flush(); // close fs.Close(); } } /// /// This method allows to serialize a security token into a base64 string /// /// /// /// /// public string SerializeSecurityTokenToBase64Ex(ICloudStorageAccessToken token, Type configurationType, Dictionary additionalMetaData) { using (var tokenStream = SerializeSecurityTokenEx(token, configurationType, additionalMetaData)) { using (var memStream = new MemoryStream()) { // copy to memory StreamHelper.CopyStreamData(this, tokenStream, memStream, null, null); // reset memStream.Position = 0; // convert return Convert.ToBase64String(memStream.ToArray()); } } } /// /// This method allows to load a token from a previously generated stream /// /// /// public ICloudStorageAccessToken DeserializeSecurityToken(Stream tokenStream) { Dictionary metadata; return DeserializeSecurityToken(tokenStream, out metadata); } /// /// This method restores a tokene from file /// /// /// public ICloudStorageAccessToken DeserializeSecurityTokenEx(string fileName) { using (var fs = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None)) { return DeserializeSecurityToken(fs); } } /// /// This method allows to load a token from a previously generated stream /// and the attached metadata /// /// /// /// public ICloudStorageAccessToken DeserializeSecurityToken(Stream tokenStream, out Dictionary additionalMetaData) { // load the data in our list var serializer = new DataContractSerializer(typeof(Dictionary)); // check the type var obj = serializer.ReadObject(tokenStream); if (!obj.GetType().Equals(typeof(Dictionary))) throw new InvalidDataException("A List was expected"); // evaluate the right provider var tokendata = (Dictionary)obj; // find the right provider by typename var provider = GetProviderByConfigurationTypeName(tokendata[TokenProviderConfigurationType]); // set the output parameter additionalMetaData = new Dictionary(); // fill the metadata foreach (var kvp in tokendata) { if (kvp.Key.StartsWith(TokenMetadataPrefix)) { additionalMetaData.Add(kvp.Key.Remove(0, TokenMetadataPrefix.Length), kvp.Value); } } // build the token return provider.LoadToken(tokendata); } /// /// This methd allows to deserialize a base64 tokenstring /// /// /// public ICloudStorageAccessToken DeserializeSecurityTokenFromBase64(string tokenString) { Dictionary additionalMetaData; return DeserializeSecurityTokenFromBase64(tokenString, out additionalMetaData); } /// /// This methd allows to deserialize a base64 tokenstring /// /// /// /// public ICloudStorageAccessToken DeserializeSecurityTokenFromBase64(string tokenString, out Dictionary additionalMetaData) { // convert base64 to byte array var data = Convert.FromBase64String(tokenString); // create a token stream using (var tokenStream = new MemoryStream(data)) { // read the token stream return DeserializeSecurityToken(tokenStream, out additionalMetaData); } } /// /// This method stores the content of an access token in to /// a list of string. This list can be serialized. /// /// Target list /// the token /// type of configguration which is responsable for the token internal void StoreToken(Dictionary tokendata, ICloudStorageAccessToken token, Type configurationType) { // add the configuration information into the tokendata.Add(TokenProviderConfigurationType, configurationType.FullName); tokendata.Add(TokenCredentialType, token.GetType().FullName); // get the provider by toke var provider = GetProviderByConfigurationTypeName(configurationType.FullName); // store all the other information to tokendata provider.StoreToken(tokendata, token); } /// /// This method generated a access token from the given data /// string list /// /// the string list /// The unserialized token internal ICloudStorageAccessToken LoadToken(Dictionary tokendata) { return _provider.LoadToken(tokendata); } #endregion #region Helper private static ICloudStorageProviderInternal CreateProviderByType(Type providerType) { ICloudStorageProviderInternal provider; try { provider = Activator.CreateInstance(providerType) as ICloudStorageProviderInternal; } catch (Exception e) { throw new SharpBoxException(SharpBoxErrorCodes.ErrorCreateProviderInstanceFailed, e); } if (provider == null) throw new SharpBoxException(SharpBoxErrorCodes.ErrorCreateProviderInstanceFailed); return provider; } private void SetProviderByConfiguration(ICloudStorageConfiguration configuration) { // check if (configuration == null && _provider == null) throw new InvalidOperationException("It's only allowed to set the configuration parameter to null when a provider was set before"); // read out the right provider type SetProviderByConfigurationTypeName(configuration.GetType().FullName); } private void SetProviderByConfigurationTypeName(string typeName) { _provider = GetProviderByConfigurationTypeName(typeName); } private ICloudStorageProviderInternal GetProviderByConfigurationTypeName(string typeName) { // read out the right provider type Type providerType; if (!_configurationProviderMap.TryGetValue(typeName, out providerType)) throw new SharpBoxException(SharpBoxErrorCodes.ErrorNoValidProviderFound); // build up the provider return CreateProviderByType(providerType); } #endregion } }