diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/AspProviders.csproj b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/AspProviders.csproj new file mode 100644 index 0000000..359ba3b --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/AspProviders.csproj @@ -0,0 +1,81 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {306D2F9E-D6D0-4D96-94F1-173C60A13875} + Library + Properties + Microsoft.Samples.ServiceHosting.AspProviders + AspProviders + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + 3.5 + + + + + $(UtilityComputingSDKRoot)\ref\Microsoft.ServiceHosting.ServiceRuntime.dll + + + + + + + + + + + + + + {C6F30C10-E1C2-4327-BB6B-3160B479CCA1} + StorageClient + + + + + + + + \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/BlobProvider.cs b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/BlobProvider.cs new file mode 100644 index 0000000..75bc52a --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/BlobProvider.cs @@ -0,0 +1,277 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Configuration; +using Microsoft.ServiceHosting.ServiceRuntime; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Globalization; +using Microsoft.Samples.ServiceHosting.StorageClient; + + +namespace Microsoft.Samples.ServiceHosting.AspProviders +{ + + public enum EventKind + { + Critical, + Error, + Warning, + Information, + Verbose + }; + + static class Log + { + internal static void WriteToLog(string logName, string fmt, params object[] args) + { + RoleManager.WriteToLog(logName, String.Format(CultureInfo.InvariantCulture, fmt, args)); + } + internal static void Write(EventKind eventKind, string message, params object[] args) + { + if (RoleManager.IsRoleManagerRunning) + { + switch(eventKind) + { + case EventKind.Error: + WriteToLog("Error", message, args); + break; + case EventKind.Critical: + WriteToLog("Critical", message, args); + break; + case EventKind.Warning: + WriteToLog("Warning", message, args); + break; + case EventKind.Information: + WriteToLog("Information", message, args); + break; + case EventKind.Verbose: + WriteToLog("Verbose", message, args); + break; + } + } + else + { + switch (eventKind) + { + case EventKind.Error: + case EventKind.Critical: + Trace.TraceError(message, args); + break; + case EventKind.Warning: + Trace.TraceWarning(message, args); + break; + case EventKind.Information: + case EventKind.Verbose: + Trace.TraceInformation(message, args); + break; + } + } + } + } + + internal class BlobProvider + { + private StorageAccountInfo _info; + private BlobContainer _container; + private string _containerName; + private object _lock = new object(); + + private static readonly TimeSpan _Timeout = TimeSpan.FromSeconds(30); + private static readonly RetryPolicy _RetryPolicy = RetryPolicies.RetryN(3, TimeSpan.FromSeconds(1)); + private const string _PathSeparator = "/"; + + + internal BlobProvider(StorageAccountInfo info, string containerName) + { + this._info = info; + this._containerName = containerName; + } + + internal string ContainerUrl + { + get + { + return string.Join(_PathSeparator, new string[] { _info.BaseUri.AbsolutePath, _containerName }); + } + } + + internal bool GetBlobContentsWithoutInitialization(string blobName, Stream outputStream, out BlobProperties properties) + { + Debug.Assert(outputStream != null); + + BlobContainer container = GetContainer(); + + try + { + properties = container.GetBlob(blobName, new BlobContents(outputStream), false); + Log.Write(EventKind.Information, "Getting contents of blob {0}", _info.BaseUri + _PathSeparator + _containerName + _PathSeparator + blobName); + return true; + } + catch (StorageClientException sc) + { + if (sc.ErrorCode == StorageErrorCode.ResourceNotFound || sc.ErrorCode == StorageErrorCode.BlobNotFound) + { + properties = null; + return false; + } + else + throw; + } + } + + internal MemoryStream GetBlobContent(string blobName, out BlobProperties properties) + { + MemoryStream blobContent = new MemoryStream(); + properties = GetBlobContent(blobName, blobContent); + blobContent.Seek(0, SeekOrigin.Begin); + return blobContent; + } + + + internal BlobProperties GetBlobContent(string blobName, Stream outputStream) + { + BlobProperties properties; + BlobContainer container = GetContainer(); + try + { + properties = container.GetBlob(blobName, new BlobContents(outputStream), false); + Log.Write(EventKind.Information, "Getting contents of blob {0}", ContainerUrl + _PathSeparator + blobName); + return properties; + } + catch (StorageClientException sc) + { + Log.Write(EventKind.Error, "Error getting contents of blob {0}: {1}", ContainerUrl + _PathSeparator + blobName, sc.Message); + throw; + } + } + + internal void UploadStream(string blobName, Stream output) + { + UploadStream(blobName, output, true); + } + + internal bool UploadStream(string blobName, Stream output, bool overwrite) + { + BlobContainer container = GetContainer(); + try + { + output.Position = 0; //Rewind to start + Log.Write(EventKind.Information, "Uploading contents of blob {0}", ContainerUrl + _PathSeparator + blobName); + BlobProperties properties = new BlobProperties(blobName); + return container.CreateBlob(properties, new BlobContents(output), overwrite); + } + catch (StorageException se) + { + Log.Write(EventKind.Error, "Error uploading blob {0}: {1}", ContainerUrl + _PathSeparator + blobName, se.Message); + throw; + } + } + + internal bool DeleteBlob(string blobName) + { + BlobContainer container = GetContainer(); + try + { + return container.DeleteBlob(blobName); + } + catch (StorageException se) + { + Log.Write(EventKind.Error, "Error deleting blob {0}: {1}", ContainerUrl + _PathSeparator + blobName, se.Message); + throw; + } + } + + internal bool DeleteBlobsWithPrefix(string prefix) + { + bool ret = true; + + IEnumerable e = ListBlobs(prefix); + if (e == null) + { + return true; + } + IEnumerator props = e.GetEnumerator(); + if (props == null) + { + return true; + } + while (props.MoveNext()) + { + if (props.Current != null) + { + if (!DeleteBlob(props.Current.Name)) + { + // ignore this; it is possible that another thread could try to delete the blob + // at the same time + ret = false; + } + } + } + return ret; + } + + public IEnumerable ListBlobs(string folder) + { + BlobContainer container = GetContainer(); + try + { + return container.ListBlobs(folder, false).OfType(); + } + catch (StorageException se) + { + Log.Write(EventKind.Error, "Error enumerating contents of folder {0} exists: {1}", ContainerUrl + _PathSeparator + folder, se.Message); + throw; + } + } + + private BlobContainer GetContainer() + { + // we have to make sure that only one thread tries to create the container + lock (_lock) + { + if (_container != null) + { + return _container; + } + try + { + BlobContainer container = BlobStorage.Create(_info).GetBlobContainer(_containerName); + container.Timeout = _Timeout; + container.RetryPolicy = _RetryPolicy; + container.CreateContainer(); + _container = container; + return _container; + } + catch (StorageException se) + { + Log.Write(EventKind.Error, "Error creating container {0}: {1}", ContainerUrl, se.Message); + throw; + } + } + } + + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/Configuration.cs b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/Configuration.cs new file mode 100644 index 0000000..70c7805 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/Configuration.cs @@ -0,0 +1,309 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Globalization; +using System.Configuration; +using System.Collections.Specialized; +using System.Runtime.InteropServices; +using Microsoft.Samples.ServiceHosting.StorageClient; +using Microsoft.ServiceHosting.ServiceRuntime; + +[assembly: CLSCompliant(true)] + +namespace Microsoft.Samples.ServiceHosting.AspProviders +{ + internal static class Configuration + { + + internal const string DefaultMembershipTableNameConfigurationString = "DefaultMembershipTableName"; + internal const string DefaultRoleTableNameConfigurationString = "DefaultRoleTableName"; + internal const string DefaultSessionTableNameConfigurationString = "DefaultSessionTableName"; + internal const string DefaultSessionContainerNameConfigurationString = "DefaultSessionContainerName"; + internal const string DefaultProfileContainerNameConfigurationString = "DefaultProfileContainerName"; + internal const string DefaultProviderApplicationNameConfigurationString = "DefaultProviderApplicationName"; + + internal const string DefaultMembershipTableName = "Membership"; + internal const string DefaultRoleTableName = "Roles"; + internal const string DefaultSessionTableName = "Sessions"; + internal const string DefaultSessionContainerName = "sessionprovidercontainer"; + internal const string DefaultProfileContainerName = "profileprovidercontainer"; + internal const string DefaultProviderApplicationName = "appname"; + + + internal static string GetConfigurationSetting(string configurationString, string defaultValue) + { + return GetConfigurationSetting(configurationString, defaultValue, false); + } + + /// + /// Gets a configuration setting from application settings in the Web.config or App.config file. + /// When running in a hosted environment, configuration settings are read from the settings specified in + /// .cscfg files (i.e., the settings are read from the fabrics configuration system). + /// + internal static string GetConfigurationSetting(string configurationString, string defaultValue, bool throwIfNull) + { + if (string.IsNullOrEmpty(configurationString)) { + throw new ArgumentException("The parameter configurationString cannot be null or empty."); + } + + string ret = null; + + // first, try to read from appsettings + ret = TryGetAppSetting(configurationString); + + // settings in the csc file overload settings in Web.config + if (RoleManager.IsRoleManagerRunning) + { + string cscRet = TryGetConfigurationSetting(configurationString); + if (!string.IsNullOrEmpty(cscRet)) + { + ret = cscRet; + } + + // if there is a csc config name in the app settings, this config name even overloads the + // setting we have right now + string refWebRet = TryGetAppSetting(StorageAccountInfo.CSConfigStringPrefix + configurationString); + if (!string.IsNullOrEmpty(refWebRet)) + { + cscRet = TryGetConfigurationSetting(refWebRet); + if (!string.IsNullOrEmpty(cscRet)) + { + ret = cscRet; + } + } + } + + // if we could not retrieve any configuration string set return value to the default value + if (string.IsNullOrEmpty(ret) && defaultValue != null) + { + ret = defaultValue; + } + + if (string.IsNullOrEmpty(ret) && throwIfNull) + { + throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "Cannot find configuration string {0}.", configurationString)); + } + return ret; + } + + internal static string GetConfigurationSettingFromNameValueCollection(NameValueCollection config, string valueName) + { + if (config == null) + { + throw new ArgumentNullException("config"); + } + if (valueName == null) { + throw new ArgumentNullException("valueName"); + } + + string sValue = config[valueName]; + + if (RoleManager.IsRoleManagerRunning) + { + // settings in the hosting configuration are stronger than settings in app config + string cscRet = TryGetConfigurationSetting(valueName); + if (!string.IsNullOrEmpty(cscRet)) + { + sValue = cscRet; + } + + // if there is a csc config name in the app settings, this config name even overloads the + // setting we have right now + string refWebRet = config[StorageAccountInfo.CSConfigStringPrefix + valueName]; + if (!string.IsNullOrEmpty(refWebRet)) + { + cscRet = TryGetConfigurationSetting(refWebRet); + if (!string.IsNullOrEmpty(cscRet)) + { + sValue = cscRet; + } + } + } + return sValue; + } + + internal static bool GetBooleanValue(NameValueCollection config, string valueName, bool defaultValue) + { + string sValue = GetConfigurationSettingFromNameValueCollection(config, valueName); + + if (string.IsNullOrEmpty(sValue)) + { + return defaultValue; + } + + bool result; + if (bool.TryParse(sValue, out result)) + { + return result; + } + else + { + throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The value must be boolean (true or false) for property '{0}'.", valueName)); + } + } + + internal static int GetIntValue(NameValueCollection config, string valueName, int defaultValue, bool zeroAllowed, int maxValueAllowed) + { + string sValue = GetConfigurationSettingFromNameValueCollection(config, valueName); + + if (string.IsNullOrEmpty(sValue)) + { + return defaultValue; + } + + int iValue; + if (!Int32.TryParse(sValue, out iValue)) + { + if (zeroAllowed) + { + throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The value must be a non-negative 32-bit integer for property '{0}'.", valueName)); + } + + throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The value must be a positive 32-bit integer for property '{0}'.", valueName)); + } + + if (zeroAllowed && iValue < 0) + { + throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The value must be a non-negative 32-bit integer for property '{0}'.", valueName)); + } + + if (!zeroAllowed && iValue <= 0) + { + throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The value must be a positive 32-bit integer for property '{0}'.", valueName)); + } + + if (maxValueAllowed > 0 && iValue > maxValueAllowed) + { + throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The value '{0}' can not be greater than '{1}'.", valueName, maxValueAllowed.ToString(CultureInfo.InstalledUICulture))); + } + + return iValue; + } + + internal static string GetStringValue(NameValueCollection config, string valueName, string defaultValue, bool nullAllowed) + { + string sValue = GetConfigurationSettingFromNameValueCollection(config, valueName); + + if (string.IsNullOrEmpty(sValue) && nullAllowed) + { + return null; + } + else if (string.IsNullOrEmpty(sValue) && defaultValue != null) + { + return defaultValue; + } + else if (string.IsNullOrEmpty(sValue)) + { + throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The parameter '{0}' must not be empty.", valueName)); + } + + return sValue; + } + + + internal static string GetStringValueWithGlobalDefault(NameValueCollection config, string valueName, string defaultConfigString, string defaultValue, bool nullAllowed) + { + string sValue = GetConfigurationSettingFromNameValueCollection(config, valueName); + + if (string.IsNullOrEmpty(sValue)) + { + sValue = GetConfigurationSetting(defaultConfigString, null); + } + + if (string.IsNullOrEmpty(sValue) && nullAllowed) + { + return null; + } + else if (string.IsNullOrEmpty(sValue) && defaultValue != null) + { + return defaultValue; + } + else if (string.IsNullOrEmpty(sValue)) + { + throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The parameter '{0}' must not be empty.", valueName)); + } + + return sValue; + } + + internal static string GetInitExceptionDescription(StorageAccountInfo table, StorageAccountInfo blob) { + StringBuilder builder = new StringBuilder(); + builder.Append(GetInitExceptionDescription(table, "table storage configuration")); + builder.Append(GetInitExceptionDescription(blob, "blob storage configuration")); + return builder.ToString(); + } + + internal static string GetInitExceptionDescription(StorageAccountInfo info, string desc) { + StringBuilder builder = new StringBuilder(); + builder.Append("The reason for this exception is typically that the endpoints are not correctly configured. " + Environment.NewLine); + if (info == null) + { + builder.Append("The current " + desc + " is null. Please specify a table endpoint!" + Environment.NewLine); + } + else + { + string baseUri = (info.BaseUri == null) ? "null" : info.BaseUri.ToString(); + builder.Append("The current " + desc + " is: " + baseUri + ", usePathStyleUris is: " + info.UsePathStyleUris + Environment.NewLine); + builder.Append("Please also make sure that the account name and the shared key are specified correctly. This information cannot be shown here because of security reasons."); + } + return builder.ToString(); + } + + private static string TryGetConfigurationSetting(string configName) + { + string ret = null; + try + { + ret = RoleManager.GetConfigurationSetting(configName); + } + catch (RoleException) + { + return null; + } + return ret; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage ("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", + Justification = "Make sure that no error condition prevents environment from reading service configuration.")] + private static string TryGetAppSetting(string configName) + { + string ret = null; + try + { + ret = ConfigurationSettings.AppSettings[configName]; + } + // some exception happened when accessing the app settings section + // most likely this is because there is no app setting file + // this is not an error because configuration settings can also be located in the cscfg file, and explicitly + // all exceptions are captured here + catch (Exception) + { + return null; + } + return ret; + } + + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/SecUtil.cs b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/SecUtil.cs new file mode 100644 index 0000000..cbc00c9 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/SecUtil.cs @@ -0,0 +1,502 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Globalization; +using System.Web.Hosting; +using System.Collections; +using System.Collections.Specialized; +using System.Data; +using System.Data.SqlClient; +using System.Data.SqlTypes; +using System.Configuration.Provider; +using System.Configuration; +using System.Text.RegularExpressions; +using System.Xml; +using System.Net; +using System.Diagnostics; +using System.Security; +using System.Text; +using Microsoft.Samples.ServiceHosting.StorageClient; +using System.Threading; + + +namespace Microsoft.Samples.ServiceHosting.AspProviders +{ + + internal static class SecUtility + { + + internal const int Infinite = Int32.MaxValue; + + internal static bool ValidateParameter(ref string param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize) + { + if (param == null) + { + return !checkForNull; + } + + param = param.Trim(); + if ((checkIfEmpty && param.Length < 1) || + (maxSize > 0 && param.Length > maxSize) || + (checkForCommas && param.Contains(","))) + { + return false; + } + + return true; + } + + internal static void CheckParameter(ref string param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize, string paramName) + { + if (param == null) + { + if (checkForNull) + { + throw new ArgumentNullException(paramName); + } + + return; + } + + param = param.Trim(); + if (checkIfEmpty && param.Length < 1) + { + throw new ArgumentException(string.Format(CultureInfo.InstalledUICulture, "The parameter '{0}' must not be empty.", paramName), paramName); + } + + if (maxSize > 0 && param.Length > maxSize) + { + throw new ArgumentException(string.Format(CultureInfo.InstalledUICulture, "The parameter '{0}' is too long: it must not exceed {1} chars in length.", paramName, maxSize.ToString(CultureInfo.InvariantCulture)), paramName); + } + + if (checkForCommas && param.Contains(",")) + { + throw new ArgumentException(string.Format(CultureInfo.InstalledUICulture, "The parameter '{0}' must not contain commas.", paramName), paramName); + } + } + + internal static void CheckArrayParameter(ref string[] param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize, string paramName) + { + if (param == null) + { + throw new ArgumentNullException(paramName); + } + + if (param.Length < 1) + { + throw new ArgumentException(string.Format(CultureInfo.InstalledUICulture, "The array parameter '{0}' should not be empty.", paramName), paramName); + } + + Hashtable values = new Hashtable(param.Length); + for (int i = param.Length - 1; i >= 0; i--) + { + SecUtility.CheckParameter(ref param[i], checkForNull, checkIfEmpty, checkForCommas, maxSize, + paramName + "[ " + i.ToString(CultureInfo.InvariantCulture) + " ]"); + if (values.Contains(param[i])) + { + throw new ArgumentException(string.Format(CultureInfo.InstalledUICulture, "The array '{0}' should not contain duplicate values.", paramName), paramName); + } + else + { + values.Add(param[i], param[i]); + } + } + } + + internal static void SetUtcTime(DateTime value, out DateTime res) + { + res = TableStorageConstants.MinSupportedDateTime; + if ((value.Kind == DateTimeKind.Local && value.ToUniversalTime() < TableStorageConstants.MinSupportedDateTime) || + value < TableStorageConstants.MinSupportedDateTime) + { + throw new ArgumentException("Invalid time value!"); + } + if (value.Kind == DateTimeKind.Local) + { + res = value.ToUniversalTime(); + } + else + { + res = value; + } + } + + internal static bool IsValidTableName(string name) + { + if (string.IsNullOrEmpty(name)) + { + return false; + } + Regex reg = new Regex(StorageClient.StorageHttpConstants.RegularExpressionStrings.ValidTableNameRegex); + if (reg.IsMatch(name)) + { + return true; + } + else + { + return false; + } + } + + internal static bool IsValidContainerName(string name) + { + if (string.IsNullOrEmpty(name)) + { + return false; + } + Regex reg = new Regex(StorageClient.StorageHttpConstants.RegularExpressionStrings.ValidContainerNameRegex); + if (reg.IsMatch(name)) + { + return true; + } + else + { + return false; + } + } + + // the table storage system currently does not support the StartsWith() operation in + // queries. As a result we transform s.StartsWith(substring) into s.CompareTo(substring) > 0 && + // s.CompareTo(NextComparisonString(substring)) < 0 + // we assume that comparison on the service side is as ordinal comparison + internal static string NextComparisonString(string s) + { + if (string.IsNullOrEmpty(s)) + { + throw new ArgumentException("The string argument must not be null or empty!"); + } + string ret; + char last = s[s.Length - 1]; + if ((int)last + 1 > (int)char.MaxValue) + { + throw new ArgumentException("Cannot convert the string."); + } + // don't use "as" because we want to have an explicit exception here if something goes wrong + last = (char)((int)last + 1); + ret = s.Substring(0, s.Length - 1) + last; + return ret; + } + + // we use a normal character as the separator because of string comparison operations + // these have to be valid characters + internal const char KeySeparator = 'a'; + internal static readonly string KeySeparatorString = new string(KeySeparator, 1); + internal const char EscapeCharacter = 'b'; + internal static readonly string EscapeCharacterString = new string(EscapeCharacter, 1); + + // Some characters can cause problems when they are contained in columns + // that are included in queries. We are very defensive here and escape a wide range + // of characters for key columns (as the key columns are present in most queries) + internal static bool IsInvalidKeyCharacter(char c) + { + return ((c < 32) + || (c >= 127 && c < 160) + || (c == '#') + || (c == '&') + || (c == '+') + || (c == '/') + || (c == '?') + || (c == ':') + || (c == '%') + || (c == '\\') + ); + } + + internal static string CharToEscapeSequence(char c) { + string ret; + ret = EscapeCharacterString + string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int)c); + return ret; + } + + internal static string Escape(string s) + { + if (string.IsNullOrEmpty(s)) { + return s; + } + StringBuilder ret = new StringBuilder(); + foreach (char c in s) + { + if (c == EscapeCharacter || c == KeySeparator || IsInvalidKeyCharacter(c)) + { + ret.Append(CharToEscapeSequence(c)); + } + else + { + ret.Append(c); + } + } + return ret.ToString(); + } + + internal static string UnEscape(string s) + { + if (string.IsNullOrEmpty(s)) + { + return s; + } + StringBuilder ret = new StringBuilder(); + char c; + for (int i = 0; i < s.Length; i++) + { + c = s[i]; + if (c == EscapeCharacter) + { + if (i + 2 >= s.Length) + { + throw new FormatException("The string " + s + " is not correctly escaped!"); + } + int ascii = Convert.ToInt32(s.Substring(i + 1, 2), 16); + ret.Append((char)ascii); + i += 2; + } + else + { + ret.Append(c); + } + } + return ret.ToString(); + } + + internal static string CombineToKey(string s1, string s2) + { + return Escape(s1) + KeySeparator + Escape(s2); + } + + internal static string EscapedFirst(string s) + { + return Escape(s) + KeySeparator; + } + + internal static string GetFirstFromKey(string key) { + Debug.Assert(key.IndexOf(KeySeparator) != -1); + string first = key.Substring(0, key.IndexOf(KeySeparator)); + return UnEscape(first); + } + + internal static string GetSecondFromKey(string key) + { + Debug.Assert(key.IndexOf(KeySeparator) != -1); + string second = key.Substring(key.IndexOf(KeySeparator) + 1); + return UnEscape(second); + } + + internal static void CheckAllowInsecureEndpoints(bool allowInsecureRemoteEndpoints, StorageAccountInfo info) + { + if (info == null) + { + throw new ArgumentNullException("info"); + } + if (allowInsecureRemoteEndpoints) + { + return; + } + if (info.BaseUri == null || string.IsNullOrEmpty(info.BaseUri.Scheme)) + { + throw new SecurityException("allowInsecureRemoteEndpoints is set to false (default setting) but the endpoint URL seems to be empty or there is no URL scheme." + + "Please configure the provider to use an https enpoint for the storage endpoint or " + + "explicitly set the configuration option allowInsecureRemoteEndpoints to true."); + } + if (info.BaseUri.Scheme.ToUpper(CultureInfo.InvariantCulture) == Uri.UriSchemeHttps.ToUpper(CultureInfo.InvariantCulture)) + { + return; + } + if (info.BaseUri.IsLoopback) + { + return; + } + throw new SecurityException("The provider is configured with allowInsecureRemoteEndpoints set to false (default setting) but the endpoint for " + + "the storage system does not seem to be an https or local endpoint. " + + "Please configure the provider to use an https enpoint for the storage endpoint or " + + "explicitly set the configuration option allowInsecureRemoteEndpoints to true."); + } + } + + internal static class Constants + { + internal const int MaxTableUsernameLength = 256; + internal const int MaxTableApplicationNameLength = 256; + } + + + /// + /// This delegate defines the shape of a provider retry policy. + /// Provider retry policies are only used to retry when a row retrieved from a table + /// was changed by another entity before it could be saved to the data store.A retry policy will invoke the given + /// as many times as it wants to in the face of + /// retriable InvalidOperationExceptions. + /// + /// The action to retry + /// + public delegate void ProviderRetryPolicy(Action action); + + + /// + /// We are using this retry policies for only one purpose: the ASP providers often read data from the server, process it + /// locally and then write the result back to the server. The problem is that the row that has been read might have changed + /// between the read and write operation. This retry policy is used to retry the whole process in this case. + /// + /// + /// Provides definitions for some standard retry policies. + /// + public static class ProviderRetryPolicies + { + + public static readonly TimeSpan StandardMinBackoff = TimeSpan.FromMilliseconds(100); + public static readonly TimeSpan StandardMaxBackoff = TimeSpan.FromSeconds(30); + private static readonly Random Random = new Random(); + + /// + /// Policy that does no retries i.e., it just invokes exactly once + /// + /// The action to retry + /// The return value of + internal static void NoRetry(Action action) + { + action(); + } + + /// + /// Policy that retries a specified number of times with an exponential backoff scheme + /// + /// The number of times to retry. Should be a non-negative number. + /// The multiplier in the exponential backoff scheme + /// + /// For this retry policy, the minimum amount of milliseconds between retries is given by the + /// StandardMinBackoff constant, and the maximum backoff is predefined by the StandardMaxBackoff constant. + /// Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff. + public static ProviderRetryPolicy RetryN(int numberOfRetries, TimeSpan deltaBackoff) + { + return new ProviderRetryPolicy((Action action) => + { + RetryNImpl(action, numberOfRetries, StandardMinBackoff, StandardMaxBackoff, deltaBackoff); + } + ); + } + + /// + /// Policy that retries a specified number of times with an exponential backoff scheme + /// + /// The number of times to retry. Should be a non-negative number + /// The multiplier in the exponential backoff scheme + /// The minimum backoff interval + /// The minimum backoff interval + /// + /// For this retry policy, the minimum amount of milliseconds between retries is given by the + /// minBackoff parameter, and the maximum backoff is predefined by the maxBackoff parameter. + /// Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff. + public static ProviderRetryPolicy RetryN(int numberOfRetries, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff) + { + return new ProviderRetryPolicy((Action action) => + { + RetryNImpl(action, numberOfRetries, minBackoff, maxBackoff, deltaBackoff); + } + ); + } + + + private static void RetryNImpl(Action action, int numberOfRetries, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff) + { + int totalNumberOfRetries = numberOfRetries; + int backoff; + + if (minBackoff > maxBackoff) + { + throw new ArgumentException("The minimum backoff must not be larger than the maximum backoff period."); + } + if (minBackoff.TotalMilliseconds < 0) + { + throw new ArgumentException("The minimum backoff period must not be negative."); + } + + do + { + try + { + action(); + break; + } + catch (InvalidOperationException e) + { + HttpStatusCode status; + // precondition failed is the status code returned by the server to indicate that the etag is wrong + if (TableStorageHelpers.EvaluateException(e, out status)) + { + if (status == HttpStatusCode.PreconditionFailed) + { + if (numberOfRetries == 0) + { + throw; + } + backoff = CalculateCurrentBackoff(minBackoff, maxBackoff, deltaBackoff, totalNumberOfRetries - numberOfRetries); + Debug.Assert(backoff >= minBackoff.TotalMilliseconds); + Debug.Assert(backoff <= maxBackoff.TotalMilliseconds); + if (backoff > 0) + { + Thread.Sleep(backoff); + } + } + else + { + throw; + } + } + else + { + throw; + } + } + } + while (numberOfRetries-- > 0); + } + + + private static int CalculateCurrentBackoff(TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff, int curRetry) + { + int backoff; + + if (curRetry > 31) + { + backoff = (int)maxBackoff.TotalMilliseconds; + } + try + { + backoff = Random.Next((1 << curRetry) + 1); + // Console.WriteLine("backoff:" + backoff); + // Console.WriteLine("index:" + ((1 << curRetry) + 1)); + backoff *= (int)deltaBackoff.TotalMilliseconds; + backoff += (int)minBackoff.TotalMilliseconds; + } + catch (OverflowException) + { + backoff = (int)maxBackoff.TotalMilliseconds; + } + if (backoff > (int)maxBackoff.TotalMilliseconds) + { + backoff = (int)maxBackoff.TotalMilliseconds; + } + Debug.Assert(backoff >= (int)minBackoff.TotalMilliseconds); + // Console.WriteLine("real backoff:" + backoff); + return backoff; + } + + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageMembershipProvider.cs b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageMembershipProvider.cs new file mode 100644 index 0000000..c7d02b8 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageMembershipProvider.cs @@ -0,0 +1,2289 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration.Provider; +using System.Data.Services.Client; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Security; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Web; +using System.Web.Security; +using Microsoft.Samples.ServiceHosting.StorageClient; + + +namespace Microsoft.Samples.ServiceHosting.AspProviders +{ + /// + /// This class allows DevtableGen to generate the correct table (named 'Membership') + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", + Justification="Class is used by devtablegen to generate database for the development storage tool")] + internal class MembershipDataServiceContext : TableStorageDataServiceContext + { + public IQueryable Membership + { + get + { + return this.CreateQuery("Membership"); + } + } + } + + internal class MembershipRow : TableStorageEntity, IComparable + { + private string _applicationName; + private string _userName; + private string _password; + private string _passwordSalt; + private string _email; + private string _passwordAnswer; + private string _passwordQuestion; + private string _comment; + private string _profileBlobName; + + private DateTime _createDate; + private DateTime _lastLoginDate; + private DateTime _lastPasswordChangedDate; + private DateTime _lastLockoutDate; + private DateTime _lastActivityDate; + private DateTime _failedPasswordAttemptWindowStart; + private DateTime _failedPasswordAnswerAttemptWindowStart; + private DateTime _profileLastUpdated; + + // partition key is applicationName + userName + // rowKey is empty + public MembershipRow(string applicationName, string userName) + : base() + { + if (string.IsNullOrEmpty(applicationName)) { + throw new ProviderException("Partition key cannot be empty!"); + } + if (string.IsNullOrEmpty(userName)) + { + throw new ProviderException("RowKey cannot be empty!"); + } + + // applicationName + userName is partitionKey + // the reasoning behind this is that we want to strive for the best scalability possible + // chosing applicationName as the partition key and userName as row key would not give us that because + // it would mean that a site with millions of users had all users on a single partition + // having the applicationName and userName inside the partition key is important for queries as queries + // for users in a single application are the most frequent + // these queries are faster because application name and user name are part of the key + PartitionKey = SecUtility.CombineToKey(applicationName, userName); + RowKey = string.Empty; + + ApplicationName = applicationName; + UserName = userName; + + Password = string.Empty; + PasswordSalt = string.Empty; + Email = string.Empty; + PasswordAnswer = string.Empty; + PasswordQuestion = string.Empty; + Comment = string.Empty; + ProfileBlobName = string.Empty; + + CreateDateUtc = TableStorageConstants.MinSupportedDateTime; + LastLoginDateUtc = TableStorageConstants.MinSupportedDateTime; + LastActivityDateUtc = TableStorageConstants.MinSupportedDateTime; + LastLockoutDateUtc = TableStorageConstants.MinSupportedDateTime; + LastPasswordChangedDateUtc = TableStorageConstants.MinSupportedDateTime; + FailedPasswordAttemptWindowStartUtc = TableStorageConstants.MinSupportedDateTime; + FailedPasswordAnswerAttemptWindowStartUtc = TableStorageConstants.MinSupportedDateTime; + ProfileLastUpdatedUtc = TableStorageConstants.MinSupportedDateTime; + + ProfileIsCreatedByProfileProvider = false; + ProfileSize = 0; + + } + + public MembershipRow() + : base() + { + } + + public string ApplicationName + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _applicationName = value; + PartitionKey = SecUtility.CombineToKey(ApplicationName, UserName); + } + get + { + return _applicationName; + } + } + + + public string UserName + { + set { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _userName = value; + PartitionKey = SecUtility.CombineToKey(ApplicationName, UserName); + } + get + { + return _userName; + } + } + + public Guid UserId { + set; + get; + } + + public string Password + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _password = value; + } + get + { + return _password; + } + } + + public int PasswordFormat + { + set; + get; + } + + public string PasswordSalt + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _passwordSalt = value; + } + get + { + return _passwordSalt; + } + } + + public string Email + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _email = value; + } + get + { + return _email; + } + } + + public string PasswordQuestion + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _passwordQuestion = value; + } + get + { + return _passwordQuestion; + } + } + + public string PasswordAnswer + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _passwordAnswer = value; + } + get + { + return _passwordAnswer; + } + } + + + public bool IsApproved + { + set; + get; + } + + public bool IsAnonymous + { + set; + get; + } + + public bool IsLockedOut + { + set; + get; + } + + public DateTime CreateDateUtc { + set { + SecUtility.SetUtcTime(value, out _createDate); + } + get { + return _createDate; + } + } + + public DateTime LastLoginDateUtc + { + set + { + SecUtility.SetUtcTime(value, out _lastLoginDate); + } + get + { + return _lastLoginDate; + } + } + + public DateTime LastPasswordChangedDateUtc + { + set { + SecUtility.SetUtcTime(value, out _lastPasswordChangedDate); + } + get { + return _lastPasswordChangedDate; + } + } + + public DateTime LastLockoutDateUtc + { + set + { + SecUtility.SetUtcTime(value, out _lastLockoutDate); + } + get + { + return _lastLockoutDate; + } + } + + public DateTime LastActivityDateUtc { + set { + SecUtility.SetUtcTime(value, out _lastActivityDate); + } + get { + return _lastActivityDate; + } + } + + public int FailedPasswordAttemptCount + { + set; + get; + } + + public DateTime FailedPasswordAttemptWindowStartUtc + { + set { + SecUtility.SetUtcTime(value, out _failedPasswordAttemptWindowStart); + } + get { + return _failedPasswordAttemptWindowStart; + } + } + + public int FailedPasswordAnswerAttemptCount + { + set; + get; + } + + public DateTime FailedPasswordAnswerAttemptWindowStartUtc + { + set { + SecUtility.SetUtcTime(value, out _failedPasswordAnswerAttemptWindowStart); + } + get { + return _failedPasswordAnswerAttemptWindowStart; + } + } + + public string Comment + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _comment = value; + } + get + { + return _comment; + } + } + + #region Profile provider related properties + + public DateTime ProfileLastUpdatedUtc + { + set { + SecUtility.SetUtcTime(value, out _profileLastUpdated); + } + get { + return _profileLastUpdated; + } + } + + public int ProfileSize + { + set; + get; + } + + public string ProfileBlobName + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.."); + } + _profileBlobName = value; + } + get + { + return _profileBlobName; + } + } + + public bool ProfileIsCreatedByProfileProvider + { + set; + get; + } + + #endregion + + public int CompareTo(object obj) + { + if (obj == null) + { + return 1; + } + MembershipRow row = obj as MembershipRow; + if (row == null) + { + throw new ArgumentException("The parameter obj is not of type MembershipRow."); + } + return string.Compare(this.UserName, row.UserName, StringComparison.Ordinal); + } + + } + + internal class EmailComparer: IComparer { + public int Compare(MembershipRow x, MembershipRow y) + { + if (x == null) + { + if (y == null) + { + return 0; + } + else + { + return -1; + } + } + else + { + return string.Compare(x.Email, y.Email, StringComparison.Ordinal); + } + } + } + + + public class TableStorageMembershipProvider : MembershipProvider + { + + #region Member variables and constants + + private const int MaxTablePasswordSize = 128; + private const int MaxTableEmailLength = 256; + private const int MaxTablePasswordQuestionLength = 256; + private const int MaxTablePasswordAnswerLength = 128; + private const int MaxFindUserSize = 10000; + // this is the absolute minimum password size when generating new password + // the number is chosen so that it corresponds to the SQL membership provider implementation + private const int MinGeneratedPasswordSize = 14; + private const int NumRetries = 3; + + // member variables shared between most providers + private string _applicationName; + private string _accountName; + private string _sharedKey; + private string _tableName; + private string _tableServiceBaseUri; + private TableStorage _tableStorage; + private object _lock = new object(); + // retry policies are used sparingly throughout this class because we often want to be + // very conservative when it comes to security-related functions + private RetryPolicy _tableRetry = RetryPolicies.RetryN(NumRetries, TimeSpan.FromSeconds(1)); + + // membership provider specific member variables + private bool _enablePasswordRetrieval; + private bool _enablePasswordReset; + private bool _requiresQuestionAndAnswer; + private bool _requiresUniqueEmail; + private int _maxInvalidPasswordAttempts; + private int _passwordAttemptWindow; + private int _minRequiredPasswordLength; + private int _minRequiredNonalphanumericCharacters; + private string _passwordStrengthRegularExpression; + private MembershipPasswordFormat _passwordFormat; + + #endregion + + #region Properties + + /// + /// The app name is not used in this implementation. + /// + public override string ApplicationName + { + get { return _applicationName; } + set + { + lock (_lock) + { + SecUtility.CheckParameter(ref value, true, true, true, Constants.MaxTableApplicationNameLength, "ApplicationName"); + _applicationName = value; + } + } + } + + public override bool EnablePasswordRetrieval + { + get + { + return _enablePasswordRetrieval; + } + } + + public override bool EnablePasswordReset + { + get + { + return _enablePasswordReset; + } + } + + public override bool RequiresQuestionAndAnswer + { + get + { + return _requiresQuestionAndAnswer; + } + } + + public override bool RequiresUniqueEmail + { + get + { + return _requiresUniqueEmail; + } + } + + public override MembershipPasswordFormat PasswordFormat + { + get + { + return _passwordFormat; + } + } + + public override int MaxInvalidPasswordAttempts + { + get + { + return _maxInvalidPasswordAttempts; + } + } + + public override int PasswordAttemptWindow + { + get + { + return _passwordAttemptWindow; + } + } + + public override int MinRequiredPasswordLength + { + get + { + return _minRequiredPasswordLength; + } + } + + public override int MinRequiredNonAlphanumericCharacters + { + get + { + return _minRequiredNonalphanumericCharacters; + } + } + + public override string PasswordStrengthRegularExpression + { + get + { + return _passwordStrengthRegularExpression; + } + } + + #endregion + + #region Public methods + + /// + /// Initializes the membership provider. This is the only function that cannot be accessed + /// in parallel by multiple applications. The function reads the properties of the + /// provider specified in the Web.config file and stores them in member variables. + /// + public override void Initialize(string name, NameValueCollection config) + { + if (config == null) + { + throw new ArgumentNullException("config"); + } + + if (String.IsNullOrEmpty(name)) + { + name = "TableStorageMembershipProvider"; + } + + if (string.IsNullOrEmpty(config["description"])) + { + config.Remove("description"); + config.Add("description", "Table storage-based membership provider"); + } + + base.Initialize(name, config); + + bool allowInsecureRemoteEndpoints = Configuration.GetBooleanValue(config, "allowInsecureRemoteEndpoints", false); + _enablePasswordRetrieval = Configuration.GetBooleanValue(config, "enablePasswordRetrieval", false); + _enablePasswordReset = Configuration.GetBooleanValue(config, "enablePasswordReset", true); + _requiresQuestionAndAnswer = Configuration.GetBooleanValue(config, "requiresQuestionAndAnswer", true); + _requiresUniqueEmail = Configuration.GetBooleanValue(config, "requiresUniqueEmail", true); + _maxInvalidPasswordAttempts = Configuration.GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 0); + _passwordAttemptWindow = Configuration.GetIntValue(config, "passwordAttemptWindow", 10, false, 0); + _minRequiredPasswordLength = Configuration.GetIntValue(config, "minRequiredPasswordLength", 7, false, MaxTablePasswordSize); + _minRequiredNonalphanumericCharacters = Configuration.GetIntValue(config, "minRequiredNonalphanumericCharacters", 1, true, MaxTablePasswordSize); + + _passwordStrengthRegularExpression = config["passwordStrengthRegularExpression"]; + if (_passwordStrengthRegularExpression != null) + { + _passwordStrengthRegularExpression = _passwordStrengthRegularExpression.Trim(); + if (_passwordStrengthRegularExpression.Length != 0) + { + try + { + Regex testIfRegexIsValid = new Regex(_passwordStrengthRegularExpression); + } + catch (ArgumentException e) + { + throw new ProviderException(e.Message, e); + } + } + } + else + { + _passwordStrengthRegularExpression = string.Empty; + } + + if (_minRequiredNonalphanumericCharacters > _minRequiredPasswordLength) + { + throw new HttpException("The minRequiredNonalphanumericCharacters can not be greater than minRequiredPasswordLength."); + } + + string strTemp = config["passwordFormat"]; + if (strTemp == null) + { + strTemp = "Hashed"; + } + + switch (strTemp) + { + case "Clear": + _passwordFormat = MembershipPasswordFormat.Clear; + break; + case "Encrypted": + _passwordFormat = MembershipPasswordFormat.Encrypted; + break; + case "Hashed": + _passwordFormat = MembershipPasswordFormat.Hashed; + break; + default: + throw new ProviderException("Password format specified is invalid."); + } + + if (PasswordFormat == MembershipPasswordFormat.Hashed && EnablePasswordRetrieval) + throw new ProviderException("Configured settings are invalid: Hashed passwords cannot be retrieved. Either set the password format to different type, or set supportsPasswordRetrieval to false."); + + + // Table storage-related properties + _applicationName = Configuration.GetStringValueWithGlobalDefault(config, "applicationName", + Configuration.DefaultProviderApplicationNameConfigurationString, + Configuration.DefaultProviderApplicationName, false); + _accountName = Configuration.GetStringValue(config, "accountName", null, true); + _sharedKey = Configuration.GetStringValue(config, "sharedKey", null, true); + _tableName = Configuration.GetStringValueWithGlobalDefault(config, "membershipTableName", + Configuration.DefaultMembershipTableNameConfigurationString, + Configuration.DefaultMembershipTableName, false); + _tableServiceBaseUri = Configuration.GetStringValue(config, "tableServiceBaseUri", null, true); + + config.Remove("allowInsecureRemoteEndpoints"); + config.Remove("enablePasswordRetrieval"); + config.Remove("enablePasswordReset"); + config.Remove("requiresQuestionAndAnswer"); + config.Remove("requiresUniqueEmail"); + config.Remove("maxInvalidPasswordAttempts"); + config.Remove("passwordAttemptWindow"); + config.Remove("passwordFormat"); + config.Remove("minRequiredPasswordLength"); + config.Remove("minRequiredNonalphanumericCharacters"); + config.Remove("passwordStrengthRegularExpression"); + config.Remove("applicationName"); + config.Remove("accountName"); + config.Remove("sharedKey"); + config.Remove("membershipTableName"); + config.Remove("tableServiceBaseUri"); + + + // Throw an exception if unrecognized attributes remain + if (config.Count > 0) + { + string attr = config.GetKey(0); + if (!String.IsNullOrEmpty(attr)) + throw new ProviderException("Unrecognized attribute: " + attr); + } + + StorageAccountInfo info = null; + try + { + info = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration(true); + if (_tableServiceBaseUri != null) + { + info.BaseUri = new Uri(_tableServiceBaseUri); + } + if (_accountName != null) + { + info.AccountName = _accountName; + } + if (_sharedKey != null) + { + info.Base64Key = _sharedKey; + } + info.CheckComplete(); + SecUtility.CheckAllowInsecureEndpoints(allowInsecureRemoteEndpoints, info); + _tableStorage = TableStorage.Create(info); + _tableStorage.RetryPolicy = _tableRetry; + _tableStorage.TryCreateTable(_tableName); + } + catch (SecurityException) + { + throw; + } + catch (Exception e) + { + string exceptionDescription = Configuration.GetInitExceptionDescription(info, "table storage configuration"); + string tableName = (_tableName == null) ? "no membership table name specified" : _tableName; + Log.Write(EventKind.Error, "Could not create or find membership table: " + tableName + "!" + Environment.NewLine + + exceptionDescription + Environment.NewLine + + e.Message + Environment.NewLine + e.StackTrace); + throw new ProviderException("Could not create or find membership table. The most probable reason for this is that " + + "the storage endpoints are not configured correctly. Please look at the configuration settings " + + "in your .cscfg and Web.config files. More information about this error " + + "can be found in the logs when running inside the hosting environment or in the output " + + "window of Visual Studio.", e); + + } + } + + /// + /// Returns true if the username and password match an exsisting user. + /// This implementation does not update a user's login time and does + /// not raise corresponding Web events + /// + public override bool ValidateUser(string username, string password) + { + if (SecUtility.ValidateParameter(ref username, true, true, true, Constants.MaxTableUsernameLength) && + SecUtility.ValidateParameter(ref password, true, true, false, MaxTablePasswordSize) && + CheckPassword(username, password, true, true)) + { + return true; + } + else + { + return false; + } + } + + /// + /// Get a user based on the username parameter. + /// If the userIsOnline parameter is set the lastActivity flag of the user + /// is changed in the data store + /// + public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) + { + if (providerUserKey == null) + { + throw new ArgumentNullException("providerUserKey"); + } + + if (providerUserKey.GetType() != typeof(Guid)) { + throw new ArgumentException("Provided key is not a Guid!"); + } + Guid key = (Guid)providerUserKey; + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + // we need an IQueryable here because we do a Top(2) in the ProcessGetUserQuery() + // and cast it to DataServiceQuery object in this function + // this does not work when we use IEnumerable as a type here + IQueryable query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.UserId == key && + user.ProfileIsCreatedByProfileProvider == false + select user; + return ProcessGetUserQuery(svc, query, userIsOnline); + } + catch (InvalidOperationException e) + { + if (TableStorageHelpers.IsTableStorageException(e)) + { + throw new ProviderException("Error accessing the data source.", e); + } + else + { + throw; + } + } + } + + /// + /// Retrieves a user based on his/her username. + /// The userIsOnline parameter determines whether to update the lastActivityDate of + /// the user in the data store + /// + public override MembershipUser GetUser(string username, bool userIsOnline) + { + SecUtility.CheckParameter( + ref username, + true, + false, + true, + Constants.MaxTableUsernameLength, + "username"); + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + // we need an IQueryable here because we do a Top(2) in the ProcessGetUserQuery() + // and cast it to DataServiceQuery object in this function + // this does not work when we use IEnumerable as a type here + IQueryable query = from user in queryObj + where user.PartitionKey == SecUtility.CombineToKey(_applicationName, username) && + user.ProfileIsCreatedByProfileProvider == false + select user; + return ProcessGetUserQuery(svc, query, userIsOnline); + } + catch (InvalidOperationException e) + { + if (TableStorageHelpers.IsTableStorageException(e)) + { + throw new ProviderException("Error accessing the data source.", e); + } + else + { + throw; + } + } + } + + /// + /// Retrieves a collection of all the users. + /// + public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) + { + if ( pageIndex < 0 ) { + throw new ArgumentException("The page index cannot be negative."); + } + if ( pageSize < 1 ) { + throw new ArgumentException("The page size can only be a positive integer."); + } + + + long upperBound = (long)pageIndex * pageSize + pageSize - 1; + if ( upperBound > Int32.MaxValue ) { + throw new ArgumentException("pageIndex and pageSize are too big."); + } + + totalRecords = 0; + MembershipUserCollection users = new MembershipUserCollection(); + TableStorageDataServiceContext svc = CreateDataServiceContext(); + try { + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.ProfileIsCreatedByProfileProvider == false + select user; + + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable allUsers = q.ExecuteAllWithRetries(); + List allUsersSorted = new List(allUsers); + + // the result should already be sorted because the user name is part of the primary key + // we have to sort anyway because we have encoded the user names in the key + // this is also why we cannot use the table stoage pagination mechanism here and need to retrieve all elements + // for every page + allUsersSorted.Sort(); + + int startIndex = pageIndex * pageSize; + int endIndex = startIndex + pageSize; + MembershipRow row; + for (int i = startIndex; i < endIndex && i < allUsersSorted.Count; i++) { + row = allUsersSorted.ElementAt(i); + users.Add(new MembershipUser(this.Name, + row.UserName, + row.UserId, + row.Email, + row.PasswordQuestion, + row.Comment, + row.IsApproved, + row.IsLockedOut, + row.CreateDateUtc.ToLocalTime(), + row.LastLoginDateUtc.ToLocalTime(), + row.LastActivityDateUtc.ToLocalTime(), + row.LastPasswordChangedDateUtc.ToLocalTime(), + row.LastLockoutDateUtc.ToLocalTime())); + } + } catch (InvalidOperationException e) { + if (TableStorageHelpers.IsTableStorageException(e)) + { + throw new ProviderException("Error accessing the data source.", e); + } + else + { + throw; + } + } + totalRecords = users.Count; + return users; + } + + + /// + /// Changes a users password. We don't use retries in this highly security-related function. + /// All errors are exposed to the user of this function. + /// + public override bool ChangePassword(string username, string oldPassword, string newPassword) + { + SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username"); + SecUtility.CheckParameter(ref oldPassword, true, true, false, MaxTablePasswordSize, "oldPassword"); + SecUtility.CheckParameter(ref newPassword, true, true, false, MaxTablePasswordSize, "newPassword"); + + try + { + string salt = null; + int passwordFormat; + MembershipRow member; + TableStorageDataServiceContext svc = CreateDataServiceContext(); + + if (!CheckPassword(svc, username, oldPassword, false, false, out member)) + { + return false; + } + salt = member.PasswordSalt; + passwordFormat = member.PasswordFormat; + + if (newPassword.Length < MinRequiredPasswordLength) + { + throw new ArgumentException("The new password is to short."); + } + + int count = 0; + + for (int i = 0; i < newPassword.Length; i++) + { + if (!char.IsLetterOrDigit(newPassword, i)) + { + count++; + } + } + + if (count < MinRequiredNonAlphanumericCharacters) + { + throw new ArgumentException("The new password does not have enough non-alphanumeric characters!"); + } + + if (PasswordStrengthRegularExpression.Length > 0) + { + if (!Regex.IsMatch(newPassword, PasswordStrengthRegularExpression)) + { + throw new ArgumentException("The new password does not match the specified password strength regular expression."); + } + } + + string pass = EncodePassword(newPassword, (int)passwordFormat, salt); + if (pass.Length > MaxTablePasswordSize) + { + throw new ArgumentException("Password is too long!"); + } + + ValidatePasswordEventArgs e = new ValidatePasswordEventArgs(username, newPassword, false); + OnValidatingPassword(e); + + if (e.Cancel) + { + if (e.FailureInformation != null) + { + throw e.FailureInformation; + } + else + { + throw new ArgumentException("Password validation failure!"); + } + } + + member.Password = pass; + member.PasswordSalt = salt; + member.PasswordFormat = passwordFormat; + member.LastPasswordChangedDateUtc = DateTime.UtcNow; + svc.UpdateObject(member); + svc.SaveChanges(); + + return true; + } + catch (InvalidOperationException e) + { + if (TableStorageHelpers.IsTableStorageException(e)) + { + throw new ProviderException("Error accessing the data source.", e); + } + else + { + throw; + } + } + } + + /// + /// Creates a new user and stores it in the membership table. We do not use retry policies in this + /// highly security-related function. All error conditions are directly exposed to the user. + /// + public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, + string passwordAnswer, bool isApproved, object providerUserKey, + out MembershipCreateStatus status) + { + if (!SecUtility.ValidateParameter(ref password, true, true, false, MaxTablePasswordSize)) + { + status = MembershipCreateStatus.InvalidPassword; + return null; + } + + string salt = GenerateSalt(); + string pass = EncodePassword(password, (int)_passwordFormat, salt); + if (pass.Length > MaxTablePasswordSize) + { + status = MembershipCreateStatus.InvalidPassword; + return null; + } + + string encodedPasswordAnswer; + if (passwordAnswer != null) + { + passwordAnswer = passwordAnswer.Trim(); + } + + if (!string.IsNullOrEmpty(passwordAnswer)) + { + if (passwordAnswer.Length > MaxTablePasswordSize) + { + status = MembershipCreateStatus.InvalidAnswer; + return null; + } + encodedPasswordAnswer = EncodePassword(passwordAnswer.ToLowerInvariant(), (int)_passwordFormat, salt); + } + else + { + encodedPasswordAnswer = passwordAnswer; + } + + if (!SecUtility.ValidateParameter(ref encodedPasswordAnswer, RequiresQuestionAndAnswer, true, false, MaxTablePasswordSize)) + { + status = MembershipCreateStatus.InvalidAnswer; + return null; + } + + if (!SecUtility.ValidateParameter(ref username, true, true, true, Constants.MaxTableUsernameLength)) + { + status = MembershipCreateStatus.InvalidUserName; + return null; + } + + if (!SecUtility.ValidateParameter(ref email, + RequiresUniqueEmail, + RequiresUniqueEmail, + false, + Constants.MaxTableUsernameLength)) + { + status = MembershipCreateStatus.InvalidEmail; + return null; + } + + if (!SecUtility.ValidateParameter(ref passwordQuestion, RequiresQuestionAndAnswer, true, false, Constants.MaxTableUsernameLength)) + { + status = MembershipCreateStatus.InvalidQuestion; + return null; + } + + if (providerUserKey != null) + { + if (!(providerUserKey is Guid)) + { + status = MembershipCreateStatus.InvalidProviderUserKey; + return null; + } + } + + if (!EvaluatePasswordRequirements(password)) + { + status = MembershipCreateStatus.InvalidPassword; + return null; + } + + ValidatePasswordEventArgs e = new ValidatePasswordEventArgs(username, password, true); + OnValidatingPassword(e); + + if (e.Cancel) + { + status = MembershipCreateStatus.InvalidPassword; + return null; + } + + // Check whether a user with the same email address already exists. + // The danger here is (as we don't have transaction support here) that + // there are overlapping requests for creating two users with the same email + // address at the same point in time. + // A solution for this would be to have a separate table for email addresses. + // At this point here in the code we would try to insert this user's email address into the + // table and thus check whether the email is unique (the email would be the primary key of the + // separate table). There are quite some problems + // associated with that. For example, what happens if the user creation fails etc., stale data in the + // email table etc. + // Another solution is to already insert the user at this point and then check at the end of this + // funcation whether the email is unique. + if (RequiresUniqueEmail && !IsUniqueEmail(email)) + { + status = MembershipCreateStatus.DuplicateEmail; + return null; + } + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + MembershipRow newUser = new MembershipRow(_applicationName, username); + if (providerUserKey == null) + { + providerUserKey = Guid.NewGuid(); + } + newUser.UserId = (Guid)providerUserKey; + newUser.Password = pass; + newUser.PasswordSalt = salt; + newUser.Email = (email == null) ? string.Empty : email; ; + newUser.PasswordQuestion = (passwordQuestion == null) ? string.Empty : passwordQuestion; + newUser.PasswordAnswer = (encodedPasswordAnswer == null) ? string.Empty : encodedPasswordAnswer; + newUser.IsApproved = isApproved; + newUser.PasswordFormat = (int)_passwordFormat; + DateTime now = DateTime.UtcNow; + newUser.CreateDateUtc = now; + newUser.LastActivityDateUtc = now; + newUser.LastPasswordChangedDateUtc = now; + newUser.LastLoginDateUtc = now; + newUser.IsLockedOut = false; + + svc.AddObject(_tableName, newUser); + svc.SaveChanges(); + + status = MembershipCreateStatus.Success; + return new MembershipUser(this.Name, + username, + providerUserKey, + email, + passwordQuestion, + null, + isApproved, + false, + now.ToLocalTime(), + now.ToLocalTime(), + now.ToLocalTime(), + now.ToLocalTime(), + TableStorageConstants.MinSupportedDateTime); + } + catch (InvalidOperationException ex) + { + HttpStatusCode httpStatus; + if (TableStorageHelpers.EvaluateException(ex, out httpStatus) && httpStatus == HttpStatusCode.Conflict) + { + // in this case, some membership providers update the last activity time of the user + // we don't do this in this implementation because it would add another roundtrip + status = MembershipCreateStatus.DuplicateUserName; + return null; + } + else if (TableStorageHelpers.IsTableStorageException(ex)) + { + throw new ProviderException("Cannot add user to membership data store because of problems when accessing the data store.", ex); + } + else + { + throw; + } + } + } + + /// + /// Deletes the user from the membership table. + /// This implementation ignores the deleteAllRelatedData argument + /// + public override bool DeleteUser(string username, bool deleteAllRelatedData) + { + SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username"); + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + MembershipRow user = new MembershipRow(_applicationName, username); + svc.AttachTo(_tableName, user, "*"); + svc.DeleteObject(user); + svc.SaveChangesWithRetries(); + return true; + } + catch (InvalidOperationException e) + { + HttpStatusCode status; + if (TableStorageHelpers.EvaluateException(e, out status)) { + if (status == HttpStatusCode.NotFound) + { + return false; + } + else + { + throw new ProviderException("Error accessing the data source.", e); + } + } + else + { + throw; + } + } + } + + /// + /// Retrieves a username based on a matching email. + /// + public override string GetUserNameByEmail(string email) + { + SecUtility.CheckParameter(ref email, false, false, false, MaxTableEmailLength, "email"); + + string nonNullEmail = (email == null) ? string.Empty : email; + + try + { + DataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.Email == nonNullEmail && + user.ProfileIsCreatedByProfileProvider == false + select user; + + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable allUsers = q.ExecuteWithRetries(); + if (allUsers == null) + { + return null; + } + List allUsersList = new List(allUsers); + if (allUsersList == null || allUsersList.Count < 1) + { + return null; + } + if (allUsersList.Count > 1 && RequiresUniqueEmail) + { + throw new ProviderException("No unique email address!"); + } + MembershipRow firstMatch = allUsersList.ElementAt(0); + return (string.IsNullOrEmpty(firstMatch.Email)) ? null : firstMatch.Email; + } + catch (InvalidOperationException e) + { + if (TableStorageHelpers.IsTableStorageException(e)) + { + throw new ProviderException("Error accessing the data source.", e); + } + else + { + throw; + } + } + } + + /// + /// Updates a user. The username will not be changed. We explicitly don't use a large retry policy statement between + /// reading the user data and updating the user data. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", + MessageId = "0#", Justification = "Code clarity.")] + public override void UpdateUser(MembershipUser updatedUser) + { + if (updatedUser == null) + { + throw new ArgumentNullException("updatedUser"); + } + + try + { + string temp = updatedUser.UserName; + SecUtility.CheckParameter(ref temp, true, true, true, Constants.MaxTableUsernameLength, "username"); + temp = updatedUser.Email; + SecUtility.CheckParameter(ref temp, RequiresUniqueEmail, RequiresUniqueEmail, false, MaxTableEmailLength, "Email"); + updatedUser.Email = temp; + + MembershipRow member = null; + if (RequiresUniqueEmail && !IsUniqueEmail(updatedUser.Email, out member) && + member != null && member.UserName != updatedUser.UserName) + { + throw new ProviderException("Not a unique email address!"); + } + + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query = from user in queryObj + where user.PartitionKey == SecUtility.CombineToKey(_applicationName, updatedUser.UserName) && + user.ProfileIsCreatedByProfileProvider == false + select user; + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable allUsers = q.ExecuteWithRetries(); + + if (allUsers == null) + { + throw new ProviderException("Cannot update user. User not found."); + } + List allUsersList = new List(allUsers); + if (allUsersList == null || allUsersList.Count != 1) + { + throw new ProviderException("No or no unique user to update."); + } + MembershipRow userToUpdate = allUsersList.ElementAt(0); + userToUpdate.Email = updatedUser.Email; + userToUpdate.Comment = (updatedUser.Comment == null) ? string.Empty : updatedUser.Comment; + userToUpdate.IsApproved = updatedUser.IsApproved; + userToUpdate.LastLoginDateUtc = updatedUser.LastLoginDate.ToUniversalTime(); + userToUpdate.LastActivityDateUtc = updatedUser.LastActivityDate.ToUniversalTime(); + + svc.UpdateObject(userToUpdate); + svc.SaveChangesWithRetries(); + } + catch (Exception e) + { + HttpStatusCode status; + if (TableStorageHelpers.EvaluateException(e, out status)) + { + throw new ProviderException("Error accessing the data source.", e); + } + else + { + throw; + } + } + } + + public virtual string GeneratePassword() + { + return Membership.GeneratePassword( + MinRequiredPasswordLength < MinGeneratedPasswordSize ? MinGeneratedPasswordSize : MinRequiredPasswordLength, + MinRequiredNonAlphanumericCharacters); + } + + /// + /// Reset the password of a user. No retry policies are used in this function. + /// + public override string ResetPassword(string username, string answer) + { + if (!EnablePasswordReset) + { + throw new NotSupportedException("Membership provider is configured to not allow password resets!"); + } + + SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username"); + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + MembershipRow member = GetUserFromTable(svc, username); + if (member == null) + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Couldn't find a unique user with the name {0}.", username)); + } + if (member.IsLockedOut) + { + throw new MembershipPasswordException(string.Format(CultureInfo.InstalledUICulture, "The user {0} is currently locked out!", username)); + } + + int passwordFormat = member.PasswordFormat; + string salt = member.PasswordSalt; + string encodedPasswordAnswer; + + if (answer != null) + { + answer = answer.Trim(); + } + if (!string.IsNullOrEmpty(answer)) + { + encodedPasswordAnswer = EncodePassword(answer.ToLowerInvariant(), passwordFormat, salt); + } + else + { + encodedPasswordAnswer = answer; + } + SecUtility.CheckParameter(ref encodedPasswordAnswer, RequiresQuestionAndAnswer, RequiresQuestionAndAnswer, false, MaxTablePasswordSize, "passwordAnswer"); + + string newPassword = GeneratePassword(); + ValidatePasswordEventArgs e = new ValidatePasswordEventArgs(username, newPassword, false); + OnValidatingPassword(e); + if (e.Cancel) + { + if (e.FailureInformation != null) + { + throw e.FailureInformation; + } + else + { + throw new ProviderException("Password validation failed."); + } + } + + DateTime now = DateTime.UtcNow; + Exception ex = null; + if (encodedPasswordAnswer == null || encodedPasswordAnswer == member.PasswordAnswer) + { + member.Password = EncodePassword(newPassword, (int)passwordFormat, salt); + member.LastPasswordChangedDateUtc = now; + if (member.FailedPasswordAnswerAttemptCount > 0 && encodedPasswordAnswer != null) + { + member.FailedPasswordAnswerAttemptCount = 0; + member.FailedPasswordAnswerAttemptWindowStartUtc = TableStorageConstants.MinSupportedDateTime; + } + } + else + { + if (now > member.FailedPasswordAnswerAttemptWindowStartUtc.Add(TimeSpan.FromMinutes(PasswordAttemptWindow))) + { + member.FailedPasswordAnswerAttemptWindowStartUtc = now; + member.FailedPasswordAnswerAttemptCount = 1; + } + else + { + member.FailedPasswordAnswerAttemptWindowStartUtc = now; + member.FailedPasswordAnswerAttemptCount++; + } + if (member.FailedPasswordAnswerAttemptCount >= MaxInvalidPasswordAttempts) + { + member.IsLockedOut = true; + member.LastLockoutDateUtc = now; + } + ex = new MembershipPasswordException("Wrong password answer."); + } + + svc.UpdateObject(member); + svc.SaveChanges(); + + if (ex != null) + { + throw ex; + } + return newPassword; + } + catch (Exception e) + { + HttpStatusCode status; + if (TableStorageHelpers.EvaluateException(e, out status)) + { + throw new ProviderException("Error accessing the data source.", e); + } + else + { + throw; + } + } + } + + /// + /// Unlock a user + /// + public override bool UnlockUser(string userName) + { + SecUtility.CheckParameter(ref userName, true, true, true, Constants.MaxTableUsernameLength, "username"); + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + MembershipRow member = GetUserFromTable(svc, userName); + if (member == null) + { + return false; + } + member.IsLockedOut = false; + member.FailedPasswordAttemptCount = 0; + member.FailedPasswordAttemptWindowStartUtc = TableStorageConstants.MinSupportedDateTime; + member.FailedPasswordAnswerAttemptCount = 0; + member.FailedPasswordAnswerAttemptWindowStartUtc = TableStorageConstants.MinSupportedDateTime; + member.LastLockoutDateUtc = TableStorageConstants.MinSupportedDateTime; + svc.UpdateObject(member); + svc.SaveChangesWithRetries(); + return true; + } + catch (Exception e) + { + if (TableStorageHelpers.IsTableStorageException(e)) { + throw new ProviderException("Error accessing the data source.", e); + } + else + { + throw; + } + } + } + + /// + /// Find users based on their email addresses. + /// The emailToMatch must be a complete email string like abc@def.com or can contain a '%' character at the end. + /// A '%' character at the end implies that arbitrary characters can follow. + /// Supporting additional searches right now would be very expensive because the filtering would have to be done on the + /// client side. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Predefined interface.")] + public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) + { + SecUtility.CheckParameter(ref emailToMatch, false, false, false, Constants.MaxTableUsernameLength, "emailToMatch"); + + if (pageIndex < 0) + { + throw new ArgumentException("Page index must be a non-negative integer."); + } + if (pageSize < 1) + { + throw new ArgumentException("Page size must be a positive integer."); + } + if (emailToMatch == null) + { + emailToMatch = string.Empty; + } + bool startswith = false; + if (emailToMatch.Contains('%')) + { + if (emailToMatch.IndexOf('%') != emailToMatch.Length - 1) + { + throw new ArgumentException("The TableStorageMembershipProvider only supports search strings that contain '%' as the last character!"); + } + emailToMatch = emailToMatch.Substring(0, emailToMatch.Length - 1); + startswith = true; + } + + long upperBound = (long)pageIndex * pageSize + pageSize - 1; + if (upperBound > Int32.MaxValue) + { + throw new ArgumentException("Cannot return so many elements!"); + } + + MembershipUserCollection users = new MembershipUserCollection(); + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query; + if (startswith && string.IsNullOrEmpty(emailToMatch)) { + query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.ProfileIsCreatedByProfileProvider == false + select user; + } else if (startswith) { + // so far, the table storage service does not support StartsWith; thus, we retrieve all users whose email is "larger" than the one + // specified and do the comparison locally + // this can result in significant overhead + query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.ProfileIsCreatedByProfileProvider == false && + user.Email.CompareTo(emailToMatch) >= 0 + select user; + } else { + query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.ProfileIsCreatedByProfileProvider == false && + user.Email == emailToMatch + select user; + } + + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable allUsers = q.ExecuteAllWithRetries(); + + int startIndex = pageIndex * pageSize; + int endIndex = startIndex + pageSize; + int i = 0; + List allUsersList = new List(allUsers); + allUsersList.Sort(new EmailComparer()); + MembershipRow row; + bool userMatches = true; + for (i = startIndex; i < endIndex && i < allUsersList.Count && userMatches; i++) { + row = allUsersList.ElementAt(i); + Debug.Assert(emailToMatch != null); + if (startswith && !string.IsNullOrEmpty(emailToMatch)) + { + if (!row.Email.StartsWith(emailToMatch, StringComparison.Ordinal)) + { + userMatches = false; + continue; + } + } + users.Add(new MembershipUser(this.Name, + row.UserName, + row.UserId, + row.Email, + row.PasswordQuestion, + row.Comment, + row.IsApproved, + row.IsLockedOut, + row.CreateDateUtc.ToLocalTime(), + row.LastLoginDateUtc.ToLocalTime(), + row.LastActivityDateUtc.ToLocalTime(), + row.LastPasswordChangedDateUtc.ToLocalTime(), + row.LastLockoutDateUtc.ToLocalTime())); + } + } + catch (Exception e) + { + if (TableStorageHelpers.IsTableStorageException(e)) + { + throw new ProviderException("Error accessing the data source.", e); + } + else + { + throw; + } + } + + totalRecords = users.Count; + return users; + } + + /// + /// Find users by their names. + /// The usernameToMatch must be the complete username like frank or can contain a '%' character at the end. + /// A '%' character at the end implies that arbitrary characters can follow. + /// Supporting additional searches right now would be very expensive because the filtering would have to be done on the + /// client side; i.e., all users would have to be retrieved in order to do the filtering. + /// IMPORTANT: because of this decision, user names must not contain a % character when using this function. + /// + public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) + { + SecUtility.CheckParameter(ref usernameToMatch, true, true, false, Constants.MaxTableUsernameLength, "usernameToMatch"); + + if (pageIndex < 0) + { + throw new ArgumentException("Page index must be a non-negative integer."); + } + if (pageSize < 1) + { + throw new ArgumentException("Page size must be a positive integer."); + } + + bool startswith = false; + if (usernameToMatch.Contains('%')) + { + if (usernameToMatch.IndexOf('%') != usernameToMatch.Length - 1) + { + throw new ArgumentException("The TableStorageMembershipProvider only supports search strings that contain '%' as the last character!"); + } + usernameToMatch = usernameToMatch.Substring(0, usernameToMatch.Length - 1); + startswith = true; + } + + long upperBound = (long)pageIndex * pageSize + pageSize - 1; + if (upperBound > Int32.MaxValue) + { + throw new ArgumentException("Cannot return so many elements!"); + } + + MembershipUserCollection users = new MembershipUserCollection(); + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query; + if (startswith && string.IsNullOrEmpty(usernameToMatch)) { + query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.ProfileIsCreatedByProfileProvider == false + select user; + } else if (startswith) { + // note that we cannot include the usernameToMatch in the query over the partition key because the partitionkey is escaped, which destroys + // the sorting order + // and yes, we get all users here whose username is larger than the usernameToMatch because StartsWith is not supported in the current + // table storage service + query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.UserName.CompareTo(usernameToMatch) >= 0 && + user.ProfileIsCreatedByProfileProvider == false + select user; + } else { + query = from user in queryObj + where user.PartitionKey == SecUtility.CombineToKey(_applicationName, usernameToMatch) && + user.ProfileIsCreatedByProfileProvider == false + select user; + } + + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable allUsers = q.ExecuteAllWithRetries(); + + int startIndex = pageIndex * pageSize; + int endIndex = startIndex + pageSize; + int i; + List allUsersList = new List(allUsers); + // default sorting is by user name (not the escaped version in the partition key) + allUsersList.Sort(); + MembershipRow row; + bool userMatches = true; + for (i = startIndex; i < endIndex && i < allUsersList.Count && userMatches; i++) { + row = allUsersList.ElementAt(i); + if (startswith && !string.IsNullOrEmpty(usernameToMatch)) + { + if (!row.UserName.StartsWith(usernameToMatch, StringComparison.Ordinal)) + { + userMatches = false; + continue; + } + } + users.Add(new MembershipUser(this.Name, + row.UserName, + row.UserId, + row.Email, + row.PasswordQuestion, + row.Comment, + row.IsApproved, + row.IsLockedOut, + row.CreateDateUtc.ToLocalTime(), + row.LastLoginDateUtc.ToLocalTime(), + row.LastActivityDateUtc.ToLocalTime(), + row.LastPasswordChangedDateUtc.ToLocalTime(), + row.LastLockoutDateUtc.ToLocalTime())); + } + } + catch (Exception e) + { + if (TableStorageHelpers.IsTableStorageException(e)) + { + throw new ProviderException("Error accessing the data source.", e); + } + else + { + throw; + } + } + + totalRecords = users.Count; + return users; + } + + /// + /// Get the number of users that are currently online + /// + /// + public override int GetNumberOfUsersOnline() + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + DateTime thresh = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(Membership.UserIsOnlineTimeWindow)); + IEnumerable query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.LastActivityDateUtc > thresh && + user.ProfileIsCreatedByProfileProvider == false + select user; + + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable allUsers = q.ExecuteAllWithRetries(); + if (allUsers == null) + { + return 0; + } + List allUsersList = new List(allUsers); + if (allUsersList == null) + { + return 0; + } + return allUsersList.Count; + } + + /// + /// Change the password answer for a user. + /// + public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) + { + SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username"); + SecUtility.CheckParameter(ref password, true, true, false, MaxTablePasswordSize, "password"); + + try { + MembershipRow member; + TableStorageDataServiceContext svc = CreateDataServiceContext(); + if (!CheckPassword(svc, username, password, false, false, out member)) + { + return false; + } + + SecUtility.CheckParameter(ref newPasswordQuestion, RequiresQuestionAndAnswer, RequiresQuestionAndAnswer, false, MaxTablePasswordQuestionLength, "newPasswordQuestion"); + string encodedPasswordAnswer; + if (newPasswordAnswer != null) + { + newPasswordAnswer = newPasswordAnswer.Trim(); + } + + SecUtility.CheckParameter(ref newPasswordAnswer, RequiresQuestionAndAnswer, RequiresQuestionAndAnswer, false, MaxTablePasswordAnswerLength, "newPasswordAnswer"); + if (!string.IsNullOrEmpty(newPasswordAnswer)) + { + encodedPasswordAnswer = EncodePassword(newPasswordAnswer.ToLowerInvariant(), member.PasswordFormat, member.PasswordSalt); + } + else + { + encodedPasswordAnswer = newPasswordAnswer; + } + SecUtility.CheckParameter(ref encodedPasswordAnswer, RequiresQuestionAndAnswer, RequiresQuestionAndAnswer, false, MaxTablePasswordAnswerLength, "newPasswordAnswer"); + + member.PasswordQuestion = newPasswordQuestion; + member.PasswordAnswer = encodedPasswordAnswer; + + svc.UpdateObject(member); + svc.SaveChanges(); + return true; + } catch(Exception e) { + if (TableStorageHelpers.IsTableStorageException(e)) + { + throw new ProviderException("Error accessing the data source.", e); + } + else + { + throw; + } + } + } + + /// + /// Gets the password of a user given the provided password answer + /// + public override string GetPassword(string username, string answer) + { + if (!EnablePasswordRetrieval) + { + throw new NotSupportedException("Membership provider is configured to reject password retrieval."); + } + + SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username" ); + + try { + if (answer != null) + { + answer = answer.Trim(); + } + string encodedPasswordAnswer; + DataServiceContext svc = CreateDataServiceContext(); + MembershipRow member = GetUserFromTable(svc, username); + if (member == null) + { + throw new ProviderException("Couldn't find a unique user with that name."); + } + if (member.IsLockedOut) + { + throw new MembershipPasswordException("User is locked out."); + } + if (string.IsNullOrEmpty(answer)) + { + encodedPasswordAnswer = answer; + } + else + { + encodedPasswordAnswer = EncodePassword(answer.ToLowerInvariant(), member.PasswordFormat, member.PasswordSalt); + } + SecUtility.CheckParameter(ref encodedPasswordAnswer, RequiresQuestionAndAnswer, RequiresQuestionAndAnswer, false, MaxTablePasswordAnswerLength, "passwordAnswer"); + + Exception ex = null; + if (RequiresQuestionAndAnswer) { + DateTime now = DateTime.UtcNow; + if (string.IsNullOrEmpty(member.PasswordAnswer) || encodedPasswordAnswer != member.PasswordAnswer) { + ex = new MembershipPasswordException("Password answer is invalid."); + if (now > member.FailedPasswordAnswerAttemptWindowStartUtc.Add(TimeSpan.FromMinutes(PasswordAttemptWindow))) { + member.FailedPasswordAnswerAttemptWindowStartUtc = now; + member.FailedPasswordAnswerAttemptCount = 1; + } + else + { + member.FailedPasswordAnswerAttemptWindowStartUtc = now; + member.FailedPasswordAnswerAttemptCount++; + } + if (member.FailedPasswordAnswerAttemptCount >= MaxInvalidPasswordAttempts) + { + member.IsLockedOut = true; + member.LastLockoutDateUtc = now; + } + } else { + if (member.FailedPasswordAnswerAttemptCount > 0) { + member.FailedPasswordAnswerAttemptCount = 0; + member.FailedPasswordAnswerAttemptWindowStartUtc = TableStorageConstants.MinSupportedDateTime; + } + } + } + svc.UpdateObject(member); + svc.SaveChanges(); + if (ex != null) { + throw ex; + } + return UnEncodePassword(member.Password, member.PasswordFormat); + } catch(Exception e) { + if (TableStorageHelpers.IsTableStorageException(e)) + { + throw new ProviderException("Error accessing the data source.", e); + } + else + { + throw; + } + } + } + + #endregion + + #region Private helper methods + + private TableStorageDataServiceContext CreateDataServiceContext() + { + return _tableStorage.GetDataServiceContext(); + } + + private MembershipUser ProcessGetUserQuery(TableStorageDataServiceContext svc, IQueryable query, bool updateLastActivityDate) + { + if (query == null) + { + throw new ArgumentNullException("query"); + } + + // if no user is found, we return null + MembershipUser res = null; + + // the GetUser query should return at most 1 result, we do a Take(2) to detect error conditions + query = query.Take(2); + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable qResult = q.ExecuteWithRetries(); + if (qResult == null) { + return null; + } + List l = new List(qResult); + if (l.Count == 0) + { + return null; + } + else if (l.Count > 1) + { + throw new ProviderException("Non-unique primary keys!"); + } else { + MembershipRow row = l.First(); + if (updateLastActivityDate) + { + row.LastActivityDateUtc = DateTime.UtcNow; + } + res = new MembershipUser(this.Name, + row.UserName, + row.UserId, + row.Email, + row.PasswordQuestion, + row.Comment, + row.IsApproved, + row.IsLockedOut, + row.CreateDateUtc.ToLocalTime(), + row.LastLoginDateUtc.ToLocalTime(), + row.LastActivityDateUtc.ToLocalTime(), + row.LastPasswordChangedDateUtc.ToLocalTime(), + row.LastLockoutDateUtc.ToLocalTime()); + + if (updateLastActivityDate) + { + svc.UpdateObject(row); + svc.SaveChangesWithRetries(); + } + } + return res; + } + + private bool IsUniqueEmail(string email) + { + MembershipRow member; + return IsUniqueEmail(email, out member); + } + + private bool IsUniqueEmail(string email, out MembershipRow member) + { + member = null; + SecUtility.ValidateParameter(ref email, true, true, true, TableStorageConstants.MaxStringPropertySizeInChars); + + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.Email == email && + user.ProfileIsCreatedByProfileProvider == false + select user; + + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable allUsers = q.ExecuteWithRetries(); + if (allUsers == null) + { + return true; + } + IEnumerator e = allUsers.GetEnumerator(); + if (e == null) + { + return true; + } + // e.Reset() throws a not implemented exception + // according to the spec, the enumerator is at the beginning of the collections after a call to GetEnumerator() + if (!e.MoveNext()) + { + return true; + } + else + { + member = e.Current; + } + return false; + } + + private bool CheckPassword(string username, string password, bool updateLastLoginActivityDate, bool failIfNotApproved) + { + MembershipRow member = null; + return CheckPassword(username, password, updateLastLoginActivityDate, failIfNotApproved, out member); + } + + private bool CheckPassword(string username, string password, bool updateLastLoginActivityDate, bool failIfNotApproved, out MembershipRow member) + { + return CheckPassword(null, username, password, updateLastLoginActivityDate, failIfNotApproved, out member); + } + + private bool CheckPassword(DataServiceContext svc, string username, string password, bool updateLastLoginActivityDate, bool failIfNotApproved, out MembershipRow member) + { + bool createContextAndWriteState = false; + try + { + if (svc == null) + { + svc = CreateDataServiceContext(); + createContextAndWriteState = true; + } + member = GetUserFromTable(svc, username); + if (member == null) + { + return false; + } + if (member.IsLockedOut) + { + return false; + } + if (!member.IsApproved && failIfNotApproved) + { + return false; + } + + DateTime now = DateTime.UtcNow; + string encodedPasswd = EncodePassword(password, member.PasswordFormat, member.PasswordSalt); + + bool isPasswordCorrect = member.Password.Equals(encodedPasswd); + + if (isPasswordCorrect && member.FailedPasswordAttemptCount == 0 && member.FailedPasswordAnswerAttemptCount == 0) + { + if (createContextAndWriteState) + { + svc.UpdateObject(member); + svc.SaveChanges(); + } + return true; + } + + if (!isPasswordCorrect) + { + if (now > member.FailedPasswordAttemptWindowStartUtc.Add(TimeSpan.FromMinutes(PasswordAttemptWindow))) + { + member.FailedPasswordAttemptWindowStartUtc = now; + member.FailedPasswordAttemptCount = 1; + } + else + { + member.FailedPasswordAttemptWindowStartUtc = now; + member.FailedPasswordAttemptCount++; + } + if (member.FailedPasswordAttemptCount >= MaxInvalidPasswordAttempts) + { + member.IsLockedOut = true; + member.LastLockoutDateUtc = now; + } + } + else + { + if (member.FailedPasswordAttemptCount > 0 || member.FailedPasswordAnswerAttemptCount > 0) + { + member.FailedPasswordAnswerAttemptWindowStartUtc = TableStorageConstants.MinSupportedDateTime; + member.FailedPasswordAnswerAttemptCount = 0; + member.FailedPasswordAttemptWindowStartUtc = TableStorageConstants.MinSupportedDateTime; + member.FailedPasswordAttemptCount = 0; + member.LastLockoutDateUtc = TableStorageConstants.MinSupportedDateTime; + } + } + if (isPasswordCorrect && updateLastLoginActivityDate) + { + member.LastActivityDateUtc = now; + member.LastLoginDateUtc = now; + } + + if (createContextAndWriteState) + { + svc.UpdateObject(member); + svc.SaveChanges(); + } + + return isPasswordCorrect; + } + catch (Exception e) + { + HttpStatusCode status; + if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.PreconditionFailed) + { + // this element was changed between read and writes + Log.Write(EventKind.Warning, "A membership element has been changed between read and writes."); + member = null; + return false; + } + else + { + throw new ProviderException("Error accessing the data store!", e); + } + } + } + + private TimeSpan PasswordAttemptWindowAsTimeSpan() + { + return new TimeSpan(0, PasswordAttemptWindow, 0); + } + + private MembershipRow GetUserFromTable(DataServiceContext svc, string username) + { + SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username"); + + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query = from user in queryObj + where user.PartitionKey == SecUtility.CombineToKey(_applicationName, username) && + user.ProfileIsCreatedByProfileProvider == false + select user; + + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable allUsers = q.ExecuteWithRetries(); + if (allUsers == null) + { + return null; + } + IEnumerator e = allUsers.GetEnumerator(); + if (e == null) + { + return null; + } + // e.Reset() throws a not implemented exception + // according to the spec, the enumerator is at the beginning of the collections after a call to GetEnumerator() + if (!e.MoveNext()) + { + return null; + } + MembershipRow ret = e.Current; + if (e.MoveNext()) + { + throw new ProviderException("Duplicate elements for primary keys application and user name."); + } + return ret; + } + + private bool EvaluatePasswordRequirements(string password) + { + if (password.Length < MinRequiredPasswordLength) + { + return false; + } + + int count = 0; + for (int i = 0; i < password.Length; i++) + { + if (!char.IsLetterOrDigit(password, i)) + { + count++; + } + } + + if (count < MinRequiredNonAlphanumericCharacters) + { + return false; + } + + if (PasswordStrengthRegularExpression.Length > 0) + { + if (!Regex.IsMatch(password, PasswordStrengthRegularExpression)) + { + return false; + } + } + return true; + } + + private static string GenerateSalt() + { + byte[] buf = new byte[16]; + (new RNGCryptoServiceProvider()).GetBytes(buf); + return Convert.ToBase64String(buf); + } + + private string EncodePassword(string pass, int passwordFormat, string salt) + { + if (passwordFormat == 0) + { // MembershipPasswordFormat.Clear + return pass; + } + + byte[] bIn = Encoding.Unicode.GetBytes(pass); + byte[] bSalt = Convert.FromBase64String(salt); + byte[] bAll = new byte[bSalt.Length + bIn.Length]; + byte[] bRet = null; + + Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length); + Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length); + if (passwordFormat == 1) + { // MembershipPasswordFormat.Hashed + HashAlgorithm s = HashAlgorithm.Create(Membership.HashAlgorithmType); + bRet = s.ComputeHash(bAll); + } + else + { + bRet = EncryptPassword(bAll); + } + + return Convert.ToBase64String(bRet); + } + + private string UnEncodePassword(string pass, int passwordFormat) + { + switch (passwordFormat) + { + case 0: // MembershipPasswordFormat.Clear: + return pass; + case 1: // MembershipPasswordFormat.Hashed: + throw new ProviderException("Hashed password cannot be decrypted."); + default: + byte[] bIn = Convert.FromBase64String(pass); + byte[] bRet = DecryptPassword(bIn); + if (bRet == null) + { + return null; + } + return Encoding.Unicode.GetString(bRet, 16, bRet.Length - 16); + } + } + + #endregion + } + + +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageProfileProvider.cs b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageProfileProvider.cs new file mode 100644 index 0000000..79f68fc --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageProfileProvider.cs @@ -0,0 +1,1267 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Configuration; +using System.Configuration.Provider; +using System.Data.Services.Client; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Security; +using System.Text; +using System.Text.RegularExpressions; +using System.Web.Profile; +using System.Xml.Serialization; +using Microsoft.Samples.ServiceHosting.StorageClient; + + +namespace Microsoft.Samples.ServiceHosting.AspProviders +{ + + public class TableStorageProfileProvider : ProfileProvider + { + + #region Member variables and constants + + private const int NumRetries = 3; + + // member variables shared between most providers + private string _applicationName; + private string _accountName; + private string _sharedKey; + private string _tableName; + private string _tableServiceBaseUri; + private string _blobServiceBaseUri; + private string _containerName; + private BlobProvider _blobProvider; + private object _lock = new object(); + private TableStorage _tableStorage; + private RetryPolicy _tableRetry = RetryPolicies.RetryN(NumRetries, TimeSpan.FromSeconds(1)); + private ProviderRetryPolicy _providerRetry = ProviderRetryPolicies.RetryN(NumRetries, TimeSpan.FromSeconds(1)); + + #endregion + + public override string ApplicationName + { + get { return _applicationName; } + set + { + lock (_lock) + { + SecUtility.CheckParameter(ref value, true, true, true, Constants.MaxTableApplicationNameLength, "ApplicationName"); + _applicationName = value; + } + } + } + + public override void Initialize(string name, NameValueCollection config) + { + // Verify that config isn't null + if (config == null) + { + throw new ArgumentNullException("config"); + } + + // Assign the provider a default name if it doesn't have one + if (String.IsNullOrEmpty(name)) + { + name = "TableStorageProfileProvider"; + } + + // Add a default "description" attribute to config if the + // attribute doesn't exist or is empty + if (string.IsNullOrEmpty(config["description"])) + { + config.Remove("description"); + config.Add("description", "Table storage-based profile provider"); + } + + // Call the base class's Initialize method + base.Initialize(name, config); + + bool allowInsecureRemoteEndpoints = Configuration.GetBooleanValue(config, "allowInsecureRemoteEndpoints", false); + + // structure storage-related properties + ApplicationName = Configuration.GetStringValueWithGlobalDefault(config, "applicationName", + Configuration.DefaultProviderApplicationNameConfigurationString, + Configuration.DefaultProviderApplicationName, false); + _accountName = Configuration.GetStringValue(config, "accountName", null, true); + _sharedKey = Configuration.GetStringValue(config, "sharedKey", null, true); + // profile information are stored in the membership table + _tableName = Configuration.GetStringValueWithGlobalDefault(config, "membershipTableName", + Configuration.DefaultMembershipTableNameConfigurationString, + Configuration.DefaultMembershipTableName, false); + _tableServiceBaseUri = Configuration.GetStringValue(config, "tableServiceBaseUri", null, true); + _containerName = Configuration.GetStringValueWithGlobalDefault(config, "containerName", + Configuration.DefaultProfileContainerNameConfigurationString, + Configuration.DefaultProfileContainerName, false); + if (!SecUtility.IsValidContainerName(_containerName)) + { + throw new ProviderException("The provider configuration for the TableStorageProfileProvider does not contain a valid container name. " + + "Please refer to the documentation for the concrete rules for valid container names." + + "The current container name is" + _containerName); + } + _blobServiceBaseUri = Configuration.GetStringValue(config, "blobServiceBaseUri", null, true); + + + // remove required attributes + config.Remove("allowInsecureRemoteEndpoints"); + config.Remove("applicationName"); + config.Remove("accountName"); + config.Remove("sharedKey"); + config.Remove("membershipTableName"); + config.Remove("tableServiceBaseUri"); + config.Remove("containerName"); + config.Remove("blobServiceBaseUri"); + + + // Throw an exception if unrecognized attributes remain + if (config.Count > 0) + { + string attr = config.GetKey(0); + if (!String.IsNullOrEmpty(attr)) + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Unrecognized attribute: {0}", attr)); + } + } + + // profiles are stored within the membership table + StorageAccountInfo tableInfo = null; + StorageAccountInfo blobInfo = null; + try + { + tableInfo = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration(true); + blobInfo = StorageAccountInfo.GetDefaultBlobStorageAccountFromConfiguration(true); + if (_tableServiceBaseUri != null) + { + tableInfo.BaseUri = new Uri(_tableServiceBaseUri); + } + if (_blobServiceBaseUri != null) + { + blobInfo.BaseUri = new Uri(_blobServiceBaseUri); + } + if (_accountName != null) + { + tableInfo.AccountName = _accountName; + blobInfo.AccountName = _accountName; + } + if (_sharedKey != null) + { + tableInfo.Base64Key = _sharedKey; + blobInfo.Base64Key = _sharedKey; + } + tableInfo.CheckComplete(); + SecUtility.CheckAllowInsecureEndpoints(allowInsecureRemoteEndpoints, tableInfo); + blobInfo.CheckComplete(); + SecUtility.CheckAllowInsecureEndpoints(allowInsecureRemoteEndpoints, blobInfo); + _tableStorage = TableStorage.Create(tableInfo); + _tableStorage.RetryPolicy = _tableRetry; + _tableStorage.TryCreateTable(_tableName); + _blobProvider = new BlobProvider(blobInfo, _containerName); + } + catch (SecurityException) + { + throw; + } + // catch InvalidOperationException and StorageException + catch (Exception e) + { + string exceptionDescription = Configuration.GetInitExceptionDescription(tableInfo, blobInfo); + string tableName = (_tableName == null) ? "no profile table name specified" : _tableName; + string containerName = (_containerName == null) ? "no container name specified" : _containerName; + Log.Write(EventKind.Error, "Initialization of data service structures (tables and/or blobs) failed!" + Environment.NewLine + + exceptionDescription + Environment.NewLine + + "Configured blob container: " + containerName + Environment.NewLine + + "Configured table name: " + tableName + Environment.NewLine + + e.Message + Environment.NewLine + e.StackTrace); + throw new ProviderException("Initialization of data service structures (tables and/or blobs) failed! " + + "The most probable reason for this is that " + + "the storage endpoints are not configured correctly. Please look at the configuration settings " + + "in your .cscfg and Web.config files. More information about this error " + + "can be found in the logs when running inside the hosting environment or in the output " + + "window of Visual Studio.", e); + } + Debug.Assert(_blobProvider != null); + } + + + public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, + SettingsPropertyCollection collection) + { + if (collection == null) + { + throw new ArgumentNullException("collection"); + } + if (context == null) + { + throw new ArgumentNullException("context"); + } + + SettingsPropertyValueCollection settings = new SettingsPropertyValueCollection(); + + // Do nothing if there are no properties to retrieve + if (collection.Count < 1) + { + return settings; + } + + // For properties lacking an explicit SerializeAs setting, set + // SerializeAs to String for strings and primitives, and XML + // for everything else + foreach (SettingsProperty property in collection) + { + if (property.SerializeAs == SettingsSerializeAs.ProviderSpecific) + { + if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(String)) + { + property.SerializeAs = SettingsSerializeAs.String; + } + else + { + property.SerializeAs = SettingsSerializeAs.Xml; + } + } + settings.Add(new SettingsPropertyValue(property)); + } + + // Get the user name or anonymous user ID + string username = (string)context["UserName"]; + + if (string.IsNullOrEmpty(username)) + { + return settings; + } + + if (!VerifyUsername(ref username)) + { + return settings; + } + + MembershipRow profile = null; + if (!DoesProfileExistAndUpdateUser(username, out profile)) + { + // the profile does not exist + // we update the last activity time of the user only if the profile does exist + // so we can just return here + return settings; + } + + Debug.Assert(profile != null); + + // We are ready to go: load the profile + // Note that we do not have to deal with write locks here because we use a + // different blob name each time we write a new profile + StreamReader reader = null; + MemoryStream stream = null; + BlobProperties blobProperties; + string[] names; + string values; + byte[] buf = null; + string line; + try + { + // Open the blob containing the profile data + stream = new MemoryStream(); + if (!_blobProvider.GetBlobContentsWithoutInitialization(profile.ProfileBlobName, stream, out blobProperties) || blobProperties.ContentLength == 0) + { + // not an error if the blob does not exist + return settings; + } + stream.Seek(0, SeekOrigin.Begin); + reader = new StreamReader(stream); + + // Read names, values, and buf from the blob + line = reader.ReadLine(); + names = line.Split(':'); + values = reader.ReadLine(); + if (!string.IsNullOrEmpty(values)) + { + UnicodeEncoding encoding = new UnicodeEncoding(); + values = encoding.GetString(Convert.FromBase64String(values)); + } + string temp = reader.ReadLine(); + if (!String.IsNullOrEmpty(temp)) + { + buf = Convert.FromBase64String(temp); + } + else + { + buf = new byte[0]; + } + } + catch (StorageException se) + { + throw new ProviderException("Error accessing blob storage when getting property values!", se); + } + catch (Exception e) + { + throw new ProviderException("Error getting property values.", e); + } + finally + { + if (reader != null) + { + reader.Close(); + } + if (stream != null) + { + stream.Close(); + } + } + // Decode names, values, and buf and initialize the + // SettingsPropertyValueCollection returned to the caller + DecodeProfileData(names, values, buf, settings); + + return settings; + } + + public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection) + { + if (collection == null) + { + throw new ArgumentNullException("collection"); + } + if (context == null) + { + throw new ArgumentNullException("context"); + } + + // Get information about the user who owns the profile + string username = (string)context["UserName"]; + bool authenticated = (bool)context["IsAuthenticated"]; + + if (String.IsNullOrEmpty(username) || collection.Count == 0) + { + return; + } + + if (!VerifyUsername(ref username)) + { + return; + } + + // lets create the blob containing the profile without checking whether the user exists + + // Format the profile data for saving + string names = String.Empty; + string values = String.Empty; + byte[] buf = null; + + EncodeProfileData(ref names, ref values, ref buf, collection, authenticated); + + // Do nothing if no properties need saving + if (string.IsNullOrEmpty(names)) + { + return; + } + + // Save the profile data + MemoryStream stream = null; + StreamWriter writer = null; + string blobName = null; + try { + stream = new MemoryStream(); + writer = new StreamWriter(stream); + + writer.WriteLine(names); + if (!String.IsNullOrEmpty(values)) + { + UnicodeEncoding encoding = new UnicodeEncoding(); + writer.WriteLine(Convert.ToBase64String(encoding.GetBytes(values))); + } + else + { + writer.WriteLine(); + } + if (buf != null && buf.Length > 0) + { + writer.WriteLine(Convert.ToBase64String(buf)); + } + else + { + writer.WriteLine(); + } + writer.Flush(); + blobName = GetProfileBlobPrefix(username) + Guid.NewGuid().ToString("N"); + Debug.Assert(blobName.Length <= TableStorageConstants.MaxStringPropertySizeInChars); + stream.Seek(0, SeekOrigin.Begin); + _blobProvider.UploadStream(blobName, stream); + + CreateOrUpdateUserAndProfile(username, authenticated, DateTime.UtcNow, blobName, (int)stream.Length); + } catch(Exception e) { + throw new ProviderException("Error writing property values!", e); + } finally { + if (writer != null) + { + writer.Close(); + } + if (stream != null) + { + stream.Close(); + } + } + } + + public override int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate) + { + try + { + TableStorageDataServiceContext context = CreateDataServiceContext(); + List inactiveUsers = GetUsersInactive(context, "%", true, authenticationOption, userInactiveSinceDate.ToUniversalTime()); + int ret = 0; + if (inactiveUsers == null || inactiveUsers.Count == 0) + { + return ret; + } + foreach (MembershipRow user in inactiveUsers) + { + if (string.IsNullOrEmpty(user.ProfileBlobName)) + { + continue; + } + try + { + if (user.ProfileIsCreatedByProfileProvider) + { + // we can completely remove this user from the table + context.DeleteObject(user); + context.SaveChangesWithRetries(); + } + else + { + user.ProfileBlobName = string.Empty; + user.ProfileLastUpdatedUtc = TableStorageConstants.MinSupportedDateTime; + user.ProfileSize = 0; + context.UpdateObject(user); + context.SaveChangesWithRetries(); + } + ret++; + // if we fail after savechanges, the profile will still be deleted + // but it will be a stale profile + // deletes all blobs that start with the prefix + _blobProvider.DeleteBlobsWithPrefix(GetProfileBlobPrefix(user.UserName)); + } + catch (InvalidOperationException) + { + // we ignore these errors and try to continue deleting + Log.Write(EventKind.Warning, "The deletion of a single profile failed! Problem when writing to table storage."); + if (user != null) + { + context.Detach(user); + } + } + catch (StorageException) + { + // we ignore these errors and try to continue deleting + Log.Write(EventKind.Warning, "The deletion of a single profile failed! Problem when deleting blob."); + } + } + return ret; + } + catch (Exception e) + { + throw new ProviderException("Error while accessing the data store.", e); + } + } + + public override int DeleteProfiles(string[] usernames) + { + SecUtility.CheckArrayParameter(ref usernames, true, true, true, Constants.MaxTableUsernameLength, "usernames"); + + TableStorageDataServiceContext context = CreateDataServiceContext(); + MembershipRow currentProfile = null; + int ret = 0; + + try + { + foreach (string name in usernames) + { + DataServiceQuery queryObj = context.CreateQuery(_tableName); + IEnumerable query = from profile in queryObj + where profile.PartitionKey == SecUtility.CombineToKey(_applicationName, name) + select profile; + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable profiles = q.ExecuteAllWithRetries(); + + if (profiles == null) + { + continue; + } + // enumerate results + List l = new List(profiles); + if (l == null || l.Count == 0) + { + continue; + } + else if (l.Count > 1) + { + // there cannot be more than one items in the list (application name + user name is a primary key) + Log.Write(EventKind.Warning, "There should only be one user with the same name in one application!"); + } + try + { + currentProfile = l.First(); + if (currentProfile.ProfileIsCreatedByProfileProvider) + { + // we can completely delete this user from the table + context.DeleteObject(currentProfile); + context.SaveChangesWithRetries(); + } + else + { + currentProfile.ProfileBlobName = string.Empty; + currentProfile.ProfileLastUpdatedUtc = TableStorageConstants.MinSupportedDateTime; + currentProfile.ProfileSize = 0; + context.UpdateObject(currentProfile); + context.SaveChangesWithRetries(); + } + ret++; + // if we fail after savechanges, the profile will still be deleted + // but it will be a stale profile + // deletes all blobs that start with the prefix + _blobProvider.DeleteBlobsWithPrefix(GetProfileBlobPrefix(l.First().UserName)); + } + catch (InvalidOperationException) + { + // we ignore these errors and try to continue deleting + Log.Write(EventKind.Warning, "The deletion of a single profile failed! Problem when writing to table storage."); + if (currentProfile != null) + { + context.Detach(currentProfile); + } + } + catch (StorageException) + { + // we ignore these errors and try to continue deleting + Log.Write(EventKind.Warning, "The deletion of a single profile failed! Problem when deleting blob."); + } + } + return ret; + } + // catch all exceptions, also InvalidOperationExceptions outside the above try block + catch (Exception e) + { + throw new ProviderException("Error while accessing the data store.", e); + } + } + + public override int DeleteProfiles(ProfileInfoCollection profiles) + { + if (profiles == null) + { + throw new ArgumentNullException("profiles"); + } + + if (profiles.Count < 1) + { + throw new ArgumentException("Cannot delete empty profile collection."); + } + + List usernames = new List(); + + foreach (ProfileInfo profile in profiles) + { + if (!usernames.Contains(profile.UserName)) + { + usernames.Add(profile.UserName); + } + } + return DeleteProfiles(usernames.ToArray()); + } + + public override ProfileInfoCollection FindInactiveProfilesByUserName(ProfileAuthenticationOption authenticationOption, + string usernameToMatch, DateTime userInactiveSinceDate, + int pageIndex, int pageSize, out int totalRecords) + { + return GetProfilesForQuery(usernameToMatch, userInactiveSinceDate.ToUniversalTime(), authenticationOption, pageIndex, pageSize, out totalRecords); + } + + public override ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, + string usernameToMatch, int pageIndex, int pageSize, + out int totalRecords) + { + return GetProfilesForQuery(usernameToMatch, DateTime.Now.ToUniversalTime().AddDays(1), authenticationOption, pageIndex, pageSize, out totalRecords); + } + + public override ProfileInfoCollection GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption, + DateTime userInactiveSinceDate, int pageIndex, int pageSize, + out int totalRecords) + { + return GetProfilesForQuery("%", userInactiveSinceDate.ToUniversalTime(), authenticationOption, pageIndex, pageSize, out totalRecords); + } + + public override ProfileInfoCollection GetAllProfiles(ProfileAuthenticationOption authenticationOption, + int pageIndex, int pageSize, out int totalRecords) + { + return GetProfilesForQuery("%", DateTime.Now.ToUniversalTime().AddDays(1), authenticationOption, pageIndex, pageSize, out totalRecords); + } + + public override int GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption, + DateTime userInactiveSinceDate) + { + // NUM queries are not supported in the PDC offering + // so we have to retrieve all records and count at the client side + int totalRecords; + GetProfilesForQuery("%", userInactiveSinceDate.ToUniversalTime(), authenticationOption, 0, Int32.MaxValue, out totalRecords); + return totalRecords; + } + + #region Helper methods + + private TableStorageDataServiceContext CreateDataServiceContext() + { + return _tableStorage.GetDataServiceContext(); + } + + private string GetProfileBlobPrefix(string username) + { + return _applicationName + username; + } + + private ProfileInfoCollection GetProfilesForQuery(string usernameToMatch, DateTime inactiveSinceUtc, + ProfileAuthenticationOption auth, int pageIndex, int pageSize, + out int totalRecords) + { + SecUtility.CheckParameter(ref usernameToMatch, true, true, false, Constants.MaxTableUsernameLength, "usernameToMatch"); + bool startsWith = false; + if (usernameToMatch.Contains('%')) + { + if (usernameToMatch.IndexOf('%') != usernameToMatch.Length - 1) + { + throw new ArgumentException("The TableStorageProfileProvider only supports search strings that contain '%' as the last character!"); + } + usernameToMatch = usernameToMatch.Substring(0, usernameToMatch.Length - 1); + startsWith = true; + } + if (inactiveSinceUtc < TableStorageConstants.MinSupportedDateTime) + { + throw new ArgumentException("DateTime not supported by data source."); + } + if (pageIndex < 0) + { + throw new ArgumentException("pageIndex must not be a negative integer."); + } + if (pageSize < 1) + { + throw new ArgumentException("pageSize must be a positive integer (strictly larger than zero)."); + } + + long upperBound = (long)pageIndex * pageSize + pageSize - 1; + if (upperBound > Int32.MaxValue) + { + throw new ArgumentException("pageIndex and pageSize too big."); + } + try + { + ProfileInfoCollection infoColl = new ProfileInfoCollection(); + totalRecords = 0; + TableStorageDataServiceContext context = CreateDataServiceContext(); + List users = GetUsersInactive(context, usernameToMatch, startsWith, auth, inactiveSinceUtc); + // default order is by user name (not by escaped user name as it appears in the key) + users.Sort(); + + int startIndex = pageIndex * pageSize; + int endIndex = startIndex + pageSize; + int i = 0; + bool userMatches = true; + MembershipRow user; + for (i = startIndex; i < endIndex && i < users.Count && userMatches; i++) + { + user = users.ElementAt(i); + if (startsWith && !string.IsNullOrEmpty(usernameToMatch)) + { + if (!user.UserName.StartsWith(usernameToMatch, StringComparison.Ordinal)) + { + userMatches = false; + continue; + } + } + infoColl.Add(new ProfileInfo(user.UserName, user.IsAnonymous, user.LastActivityDateUtc.ToLocalTime(), + user.ProfileLastUpdatedUtc.ToLocalTime(), user.ProfileSize)); + } + totalRecords = infoColl.Count; + return infoColl; + } + catch (Exception e) + { + throw new ProviderException("Error accessing the data store.", e); + } + } + + // we don't use _providerRetry here because of the out parameter prof + private bool DoesProfileExistAndUpdateUser(string username, out MembershipRow prof) + { + SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username"); + + int curRetry = 0; + bool retry = false; + do + { + retry = false; + try + { + TableStorageDataServiceContext context = CreateDataServiceContext(); + DataServiceQuery queryObj = context.CreateQuery(_tableName); + IEnumerable query = from profile in queryObj + where profile.PartitionKey == SecUtility.CombineToKey(_applicationName, username) + select profile; + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable profiles = q.ExecuteWithRetries(); + if (profiles == null) + { + prof = null; + return false; + } + + // instantiate results + List profileList = new List(profiles); + + if (profileList.Count > 1) + { + throw new ProviderException("Multiple profile rows for the same user!"); + } + if (profileList.Count == 1) + { + prof = profileList.First(); + if (!string.IsNullOrEmpty(prof.ProfileBlobName)) + { + prof.LastActivityDateUtc = DateTime.UtcNow; + context.UpdateObject(prof); + context.SaveChangesWithRetries(); + return true; + } + else + { + return false; + } + } + else + { + prof = null; + return false; + } + } + catch (InvalidOperationException e) + { + HttpStatusCode status; + if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.PreconditionFailed) + { + retry = true; + } + else + { + throw new ProviderException("Error accessing storage.", e); + } + } + } while (curRetry++ < NumRetries && retry); + prof = null; + return false; + } + + private void UpdateUser(string username, DateTime now) + { + SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username"); + + _providerRetry(() => + { + TableStorageDataServiceContext context = CreateDataServiceContext(); + DataServiceQuery queryObj = context.CreateQuery(_tableName); + IEnumerable query = from user in queryObj + where user.PartitionKey == SecUtility.CombineToKey(_applicationName, username) + select user; + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable users = q.ExecuteWithRetries(); + + if (users == null) + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The user {0} does not exist!", username)); + } + + List memberList = new List(users); + + if (memberList.Count > 1) + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Multiple users under the same name {0}!", username)); + } + + memberList.First().LastActivityDateUtc = now; + context.UpdateObject(memberList.First()); + context.SaveChangesWithRetries(); + }); + } + + private void CreateOrUpdateUserAndProfile(string username, bool authenticated, DateTime now, string blobName, int size) + { + Debug.Assert(now.Kind == DateTimeKind.Utc); + SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username"); + + _providerRetry(() => + { + TableStorageDataServiceContext context = CreateDataServiceContext(); + DataServiceQuery queryObj = context.CreateQuery(_tableName); + IEnumerable query = from user in queryObj + where user.PartitionKey == SecUtility.CombineToKey(_applicationName, username) + select user; + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable users = q.ExecuteWithRetries(); + + // instantiate results + List userList = null; + if (users != null) + { + userList = new List(users); + } + if (userList != null && userList.Count > 0) + { + MembershipRow current = userList.First(); + if (current.IsAnonymous != !authenticated) + { + // this is an error because we would need to create a user with the same name + // this should not happen + throw new ProviderException("A user with the same name but with a different authentication status already exists!"); + } + current.LastActivityDateUtc = now; + current.ProfileBlobName = blobName; + current.ProfileSize = size; + current.ProfileLastUpdatedUtc = now; + context.UpdateObject(current); + } + else + { + if (authenticated) + { + Log.Write(EventKind.Warning, "The authenticated user does not exist in the database."); + } + MembershipRow member = new MembershipRow(_applicationName, username); + member.LastActivityDateUtc = now; + member.IsAnonymous = !authenticated; + member.ProfileBlobName = blobName; + member.ProfileSize = size; + member.ProfileLastUpdatedUtc = now; + member.ProfileIsCreatedByProfileProvider = true; + context.AddObject(_tableName, member); + } + context.SaveChanges(); + }); + } + + private List GetUsersInactive(DataServiceContext context, string usernameToMatch, bool startswith, ProfileAuthenticationOption auth, DateTime userInactiveSinceDateUtc) + { + DataServiceQuery queryObj = context.CreateQuery(_tableName); + + IEnumerable query = null; + + // play a trick to deal with the restrictions of currently supported linq queries + bool first, second; + if (auth == ProfileAuthenticationOption.All) + { + first = true; + second = false; + } + else if (auth == ProfileAuthenticationOption.Anonymous) + { + first = true; + second = true; + } + else + { + first = false; + second = false; + } + + + if (startswith && usernameToMatch == string.Empty) + { + query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.LastActivityDateUtc < userInactiveSinceDateUtc && + user.ProfileBlobName != string.Empty && + (user.IsAnonymous == first || user.IsAnonymous == second) + select user; + } + else if (startswith) + { + query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.LastActivityDateUtc < userInactiveSinceDateUtc && + user.ProfileBlobName != string.Empty && + user.UserName.CompareTo(usernameToMatch) >= 0 && + (user.IsAnonymous == first || user.IsAnonymous == second) + select user; + } + else + { + query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.CombineToKey(_applicationName, usernameToMatch)) == 0 && + user.LastActivityDateUtc < userInactiveSinceDateUtc && + user.ProfileBlobName != string.Empty && + (user.IsAnonymous == first || user.IsAnonymous == second) + select user; + } + + /* + if (auth == ProfileAuthenticationOption.All) { + query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.LastActivityDateUtc < userInactiveSinceDateUtc && + user.ProfileBlobName != string.Empty + select user; + } + else if (auth == ProfileAuthenticationOption.Anonymous) + { + query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.LastActivityDateUtc < userInactiveSinceDateUtc && + user.ProfileBlobName != string.Empty && + user.IsAnonymous == true + select user; + } + else + { + Debug.Assert(auth == ProfileAuthenticationOption.Authenticated); + query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.LastActivityDateUtc < userInactiveSinceDateUtc && + user.ProfileBlobName != string.Empty && + user.IsAnonymous == false + select user; + } + */ + + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable users = q.ExecuteAllWithRetries(); + + if (users == null) + { + return new List(); ; + } + return new List(users); + } + + private List GetAllProfiles() + { + TableStorageDataServiceContext context = CreateDataServiceContext(); + DataServiceQuery queryObj = context.CreateQuery(_tableName); + + IEnumerable query = from profile in queryObj + where profile.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 && + profile.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 + select profile; + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable profiles = q.ExecuteAllWithRetries(); + + if (profiles == null) + { + return new List(); ; + } + return new List(profiles); + } + + private static bool VerifyUsername(ref string username) + { + if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(username.Trim())) + { + return false; + } + if (username.Length > Constants.MaxTableUsernameLength) + { + return false; + } + return true; + } + + private string CompleteContainerName() + { + Debug.Assert(!string.IsNullOrEmpty(_containerName)); + + string completeContainerName = _containerName; + if (!string.IsNullOrEmpty(_applicationName)) + { + completeContainerName = _containerName; + } + return completeContainerName; + } + + private static object Deserialize(SettingsPropertyValue p, string s) { + if (p == null) + { + throw new ArgumentNullException("p"); + } + Type type = p.Property.PropertyType; + SettingsSerializeAs serializeAs = p.Property.SerializeAs; + object ret = null; + + if (type == typeof(string) && (string.IsNullOrEmpty(s) || (serializeAs == SettingsSerializeAs.String))) + { + return s; + } else if (serializeAs == SettingsSerializeAs.String) { + TypeConverter converter = TypeDescriptor.GetConverter(type); + if (converter == null || !converter.CanConvertTo(typeof(string)) || !converter.CanConvertFrom(typeof(string))) + { + throw new ArgumentException("Cannot convert type!"); + } + ret = converter.ConvertFromInvariantString(s); + } else if (serializeAs == SettingsSerializeAs.Xml) { + StringReader reader = new StringReader(s); + XmlSerializer serializer = new XmlSerializer(type); + ret = serializer.Deserialize(reader); + } else { + throw new ProviderException("The provider does not support binary serialization because of security constraints!"); + } + return ret; + } + + private static void DecodeProfileData(string[] names, string values, byte[] buf, SettingsPropertyValueCollection properties) + { + if (names == null || values == null || buf == null || properties == null) + { + return; + } + + for (int i = 0; i < names.Length; i += 4) + { + // Read the next property name from "names" and retrieve + // the corresponding SettingsPropertyValue from + // "properties" + string name = names[i]; + SettingsPropertyValue pp = properties[name]; + if (pp == null) + { + continue; + } + + // Get the length and index of the persisted property value + int pos = Int32.Parse(names[i + 2], CultureInfo.InvariantCulture); + int len = Int32.Parse(names[i + 3], CultureInfo.InvariantCulture); + + // If the length is -1 and the property is a reference + // type, then the property value is null + if (len == -1 && !pp.Property.PropertyType.IsValueType) + { + pp.PropertyValue = null; + pp.IsDirty = false; + pp.Deserialized = true; + } + // If the property value was peristed as a string, + // restore it from "values" + else if (names[i + 1] == "S" && pos >= 0 && len > 0 && values.Length >= pos + len) + { + pp.Deserialized = true; + pp.PropertyValue = Deserialize(pp, values.Substring(pos, len)); + } + // If the property value was peristed as a byte array, + // restore it from "buf" + else if (names[i + 1] == "B" && pos >= 0 && len > 0 && buf.Length >= pos + len) + { + throw new ProviderException("Not supported because of security-related hosting constraints."); + } + } + } + + private static object Serialize(SettingsPropertyValue p) + { + if (p == null) + { + throw new ArgumentNullException("p"); + } + if (p.PropertyValue == null) { + return null; + } + if (p.Property.SerializeAs == SettingsSerializeAs.ProviderSpecific) + { + if (p.PropertyValue is string || p.Property.PropertyType.IsPrimitive) + { + p.Property.SerializeAs = SettingsSerializeAs.String; + } + else + { + p.Property.SerializeAs = SettingsSerializeAs.Xml; + } + } + + if (p.Property.SerializeAs == SettingsSerializeAs.String) + { + return p.PropertyValue.ToString(); + } + else if (p.Property.SerializeAs == SettingsSerializeAs.Xml) + { + string ret; + XmlSerializer serializer = new XmlSerializer(p.Property.PropertyType); + using (StringWriter writer = new StringWriter(CultureInfo.InvariantCulture)) + { + serializer.Serialize((TextWriter)writer, p.PropertyValue); + writer.Flush(); + ret = writer.ToString(); + } + return ret; + } + else if (p.Property.SerializeAs == SettingsSerializeAs.Binary) + { + throw new ProviderException("Binary serialization is not supported because of security constraints!"); + } + else + { + throw new ProviderException("Unknown serialization type."); + } + } + + private static void EncodeProfileData(ref string allNames, ref string allValues, ref byte[] buf, + SettingsPropertyValueCollection properties, bool userIsAuthenticated) + { + StringBuilder names = new StringBuilder(); + StringBuilder values = new StringBuilder(); + // currently not used because of length limitations + MemoryStream stream = new MemoryStream(); + + try + { + bool anyItemsToSave = false; + + foreach (SettingsPropertyValue pp in properties) + { + if (pp.IsDirty) + { + if (!userIsAuthenticated) + { + bool allowAnonymous = (bool)pp.Property.Attributes["AllowAnonymous"]; + if (!allowAnonymous) + { + continue; + } + } + anyItemsToSave = true; + break; + } + } + + if (!anyItemsToSave) + { + return; + } + + foreach (SettingsPropertyValue pp in properties) + { + // Ignore this property if the user is anonymous and + // the property's AllowAnonymous property is false + if (!userIsAuthenticated && !(bool)pp.Property.Attributes["AllowAnonymous"]) + { + continue; + } + + // Ignore this property if it's not dirty and is + // currently assigned its default value + if (!pp.IsDirty && pp.UsingDefaultValue) + { + continue; + } + + int len = 0, pos = 0; + string propValue = null; + // If Deserialized is true and PropertyValue is null, + // then the property's current value is null (which + // we'll represent by setting len to -1) + if (pp.Deserialized && pp.PropertyValue == null) + { + len = -1; + } + // Otherwise get the property value from PropertyValue + else if (pp.Deserialized) + { + // because this implementation cannot make use of the providers internal serialization routine + // due to security constraints, deserialized means that we need to do the serialization on our own + object sVal = Serialize(pp); + // If SerializedValue is null, then the property's + // current value is null + if (sVal == null) + len = -1; + // If sVal is a string, then encode it as a string + else if (sVal is string) + { + propValue = (string)sVal; + len = propValue.Length; + pos = values.Length; + } + // If sVal is binary, then encode it as a byte + // array + else + { + throw new ProviderException("Not supported because of security hosting constraints."); + } + } + // Otherwise get the property value from + // SerializedValue + // This does not work in this implementation because it runs in medium trust and security permissions for binary serialization + // are not available + else + { + throw new ProviderException("Because this provider currently runs under partial trust, accessing the standard serialization interface is not allowed."); + } + + // Add a string conforming to the following format + // to "names:" + // + // "name:B|S:pos:len" + // ^ ^ ^ ^ + // | | | | + // | | | +--- Length of data + // | | +------- Offset of data + // | +----------- Location (B="buf", S="values") + // +--------------- Property name + names.Append(pp.Name + ":" + ((propValue != null) ? "S" : "B") + ":" + + pos.ToString(CultureInfo.InvariantCulture) + ":" + + len.ToString(CultureInfo.InvariantCulture) + ":"); + + // If the propery value is encoded as a string, add the + // string to "values" + if (propValue != null) + { + values.Append(propValue); + } + } + // Copy the binary property values written to the + // stream to "buf" + buf = stream.ToArray(); + } + finally + { + if (stream != null) + { + stream.Close(); + } + } + allNames = names.ToString(); + allValues = values.ToString(); + } + + #endregion + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageRoleProvider.cs b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageRoleProvider.cs new file mode 100644 index 0000000..95230f0 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageRoleProvider.cs @@ -0,0 +1,881 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration.Provider; +using System.Data.Services.Client; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Security; +using System.Web.Security; +using Microsoft.Samples.ServiceHosting.StorageClient; + + +namespace Microsoft.Samples.ServiceHosting.AspProviders +{ + /// + /// This class allows DevtableGen to generate the correct table (named 'Roles') + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", + Justification="Class is used by the devtablegen tool to generate a database for the development storage tool.")] + internal class RoleDataServiceContext : TableStorageDataServiceContext + { + public IQueryable Roles + { + get + { + return this.CreateQuery("Roles"); + } + } + } + + [CLSCompliant(false)] + public class RoleRow : TableStorageEntity + { + private string _applicationName; + private string _roleName; + private string _userName; + + + // applicationName + userName is partitionKey + // roleName is rowKey + public RoleRow(string applicationName, string roleName, string userName) + : base() + { + SecUtility.CheckParameter(ref applicationName, true, true, true, Constants.MaxTableApplicationNameLength, "applicationName"); + SecUtility.CheckParameter(ref roleName, true, true, true, TableStorageRoleProvider.MaxTableRoleNameLength, "roleName"); + SecUtility.CheckParameter(ref userName, true, false, true, Constants.MaxTableUsernameLength, "userName"); + + + PartitionKey = SecUtility.CombineToKey(applicationName, userName); + RowKey = SecUtility.Escape(roleName); + ApplicationName = applicationName; + RoleName = roleName; + UserName = userName; + } + + public RoleRow() + : base() + { + } + + public string ApplicationName + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _applicationName = value; + PartitionKey = SecUtility.CombineToKey(ApplicationName, UserName); + } + get + { + return _applicationName; + } + } + + public string RoleName + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _roleName = value; + RowKey = SecUtility.Escape(RoleName); + } + get + { + return _roleName; + } + } + + public string UserName + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _userName = value; + PartitionKey = SecUtility.CombineToKey(ApplicationName, UserName); + } + get + { + return _userName; + } + } + + } + + public class TableStorageRoleProvider : RoleProvider + { + + #region Member variables and constants + + internal const int MaxTableRoleNameLength = 512; + internal const int NumRetries = 3; + + // member variables shared between most providers + private string _applicationName; + private string _accountName; + private string _sharedKey; + private string _tableName; + private string _membershipTableName; + private string _tableServiceBaseUri; + private TableStorage _tableStorage; + private object _lock = new object(); + private RetryPolicy _tableRetry = RetryPolicies.RetryN(NumRetries, TimeSpan.FromSeconds(1)); + // private ProviderRetryPolicy _providerRetry = ProviderRetryPolicies.RetryN(NumRetries, TimeSpan.FromSeconds(1)); + + #endregion + + #region Properties + + public override string ApplicationName + { + get { return _applicationName; } + set + { + lock (_lock) + { + SecUtility.CheckParameter(ref value, true, true, true, Constants.MaxTableApplicationNameLength, "ApplicationName"); + _applicationName = value; + } + } + } + + #endregion + + #region Public methods + + // RoleProvider methods + public override void Initialize(string name, NameValueCollection config) + { + // Verify that config isn't null + if (config == null) + { + throw new ArgumentNullException("config"); + } + + // Assign the provider a default name if it doesn't have one + if (String.IsNullOrEmpty(name)) + { + name = "TableStorageRoleProvider"; + } + + // Add a default "description" attribute to config if the + // attribute doesn't exist or is empty + if (string.IsNullOrEmpty(config["description"])) + { + config.Remove("description"); + config.Add("description", "Table storage-based role provider"); + } + + // Call the base class's Initialize method + base.Initialize(name, config); + + bool allowInsecureRemoteEndpoints = Configuration.GetBooleanValue(config, "allowInsecureRemoteEndpoints", false); + + // structure storage-related properties + ApplicationName = Configuration.GetStringValueWithGlobalDefault(config, "applicationName", + Configuration.DefaultProviderApplicationNameConfigurationString, + Configuration.DefaultProviderApplicationName, false); + _accountName = Configuration.GetStringValue(config, "accountName", null, true); + _sharedKey = Configuration.GetStringValue(config, "sharedKey", null, true); + _tableName = Configuration.GetStringValueWithGlobalDefault(config, "roleTableName", + Configuration.DefaultRoleTableNameConfigurationString, + Configuration.DefaultRoleTableName, false); + _membershipTableName = Configuration.GetStringValueWithGlobalDefault(config, "membershipTableName", + Configuration.DefaultMembershipTableNameConfigurationString, + Configuration.DefaultMembershipTableName, false); + _tableServiceBaseUri = Configuration.GetStringValue(config, "tableServiceBaseUri", null, true); + + // remove required attributes + config.Remove("allowInsecureRemoteEndpoints"); + config.Remove("applicationName"); + config.Remove("accountName"); + config.Remove("sharedKey"); + config.Remove("roleTableName"); + config.Remove("membershipTableName"); + config.Remove("tableServiceBaseUri"); + + + // Throw an exception if unrecognized attributes remain + if (config.Count > 0) + { + string attr = config.GetKey(0); + if (!String.IsNullOrEmpty(attr)) + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Unrecognized attribute: {0}", attr)); + } + } + + StorageAccountInfo info = null; + try + { + info = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration(true); + if (_tableServiceBaseUri != null) + { + info.BaseUri = new Uri(_tableServiceBaseUri); + } + if (_accountName != null) + { + info.AccountName = _accountName; + } + if (_sharedKey != null) + { + info.Base64Key = _sharedKey; + } + info.CheckComplete(); + SecUtility.CheckAllowInsecureEndpoints(allowInsecureRemoteEndpoints, info); + _tableStorage = TableStorage.Create(info); + _tableStorage.RetryPolicy = _tableRetry; + _tableStorage.TryCreateTable(_tableName); + } + catch (SecurityException) + { + throw; + } + // catch InvalidOperationException as well as StorageException + catch (Exception e) + { + string exceptionDescription = Configuration.GetInitExceptionDescription(info, "table storage configuration"); + string tableName = (_tableName == null) ? "no role table name specified" : _tableName; + Log.Write(EventKind.Error, "Could not create or find role table: " + tableName + "!" + Environment.NewLine + + exceptionDescription + Environment.NewLine + + e.Message + Environment.NewLine + e.StackTrace); + throw new ProviderException("Could not create or find role table. The most probable reason for this is that " + + "the storage endpoints are not configured correctly. Please look at the configuration settings " + + "in your .cscfg and Web.config files. More information about this error " + + "can be found in the logs when running inside the hosting environment or in the output " + + "window of Visual Studio.", e); + } + } + + + public override bool IsUserInRole(string username, string roleName) + { + SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); + SecUtility.CheckParameter(ref username, true, false, true, Constants.MaxTableUsernameLength, "username"); + if (username.Length < 1) + { + return false; + } + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query = from user in queryObj + where (user.PartitionKey == SecUtility.CombineToKey(_applicationName, username) || + user.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty)) && + user.RowKey == SecUtility.Escape(roleName) + select user; + + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable userRows = q.ExecuteWithRetries(); + + if (userRows == null) + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist.", roleName)); + } + List l = new List(userRows); + if (l.Count == 0) + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist.", roleName)); + } + RoleRow row; + if (IsStaleRole(l, out row)) { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist.", roleName)); + } + if (l.Count > 2) + { + throw new ProviderException("User name appears twice in the same role!"); + } + if (l.Count == 1) + { + Debug.Assert(string.IsNullOrEmpty(l.ElementAt(0).UserName)); + return false; + } + return true; + } + catch (InvalidOperationException e) + { + throw new ProviderException("Error while accessing the data store.", e); + } + } + + public override string[] GetRolesForUser(string username) + { + SecUtility.CheckParameter(ref username, true, false, true, Constants.MaxTableUsernameLength, "username"); + if (username.Length < 1) + { + return new string[0]; + } + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query = from user in queryObj + where user.PartitionKey == SecUtility.CombineToKey(_applicationName, username) || + user.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty) + select user; + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable userRows = q.ExecuteAllWithRetries(); + + if (userRows == null) + { + return new string[0]; + } + List l = new List(userRows); + if (l.Count == 0) + { + return new string[0]; + } + List ret = new List(); + foreach (RoleRow user in l) { + if (!string.IsNullOrEmpty(user.UserName) && !IsStaleRole(l, user.RoleName)) + { + ret.Add(user.RoleName); + } + } + return ret.ToArray(); + } + catch (InvalidOperationException e) + { + throw new ProviderException("Error while accessing the data store.", e); + } + } + + public override string[] GetUsersInRole(string roleName) + { + SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query = from user in queryObj + where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 && + user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + user.RowKey == SecUtility.Escape(roleName) + select user; + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable userRows = q.ExecuteAllWithRetries(); + + if (userRows == null) + { + // role does not exist; we are supposed to throw an exception here + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist!", roleName)); + } + List l = new List(userRows); + if (l.Count == 0 || IsStaleRole(l, roleName)) + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist!", roleName)); + } + List ret = new List(); + foreach (RoleRow user in l) + { + if (!string.IsNullOrEmpty(user.UserName)) + { + ret.Add(user.UserName); + } + } + return ret.ToArray(); + } + catch (InvalidOperationException e) + { + throw new ProviderException("Error while accessing the data store.", e); + } + } + + public override string[] GetAllRoles() + { + try + { + DataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query = from role in queryObj + where role.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty) + select role; + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable userRows = q.ExecuteAllWithRetries(); + + if (userRows == null) + { + return new string[0]; + } + List l = new List(userRows); + if (l.Count == 0) + { + return new string[0]; + } + List ret = new List(); + foreach (RoleRow role in l) + { + Debug.Assert(role.UserName != null); + if (string.IsNullOrEmpty(role.UserName)) + { + ret.Add(role.RoleName); + } + } + return ret.ToArray(); + } + catch (InvalidOperationException e) + { + throw new ProviderException("Error while accessing the data store.", e); + } + } + + public override bool RoleExists(string roleName) + { + SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query = from role in queryObj + where role.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty) && + role.RowKey == SecUtility.Escape(roleName) + select role; + + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + try + { + // this query addresses exactly one result + // we thus should get an exception if there is no element + q.ExecuteWithRetries(); + return true; + } + catch (DataServiceQueryException e) + { + HttpStatusCode s; + if (TableStorageHelpers.EvaluateException(e, out s) && s == HttpStatusCode.NotFound) + { + return false; + } + else + { + throw; + } + } + } + catch (InvalidOperationException e) + { + throw new ProviderException("Error while accessing the data store.", e); + } + } + + public override void CreateRole(string roleName) + { + SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + RoleRow newRole = new RoleRow(_applicationName, roleName, string.Empty); + svc.AddObject(_tableName, newRole); + svc.SaveChangesWithRetries(); + } + catch (InvalidOperationException e) + { + HttpStatusCode status; + // when retry policies are used we cannot distinguish between a conflict and success + // so, in the case of a conflict, we just retrun success here + if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.Conflict) + { + return; + // the role already exists + } + throw new ProviderException("Error accessing role table.", e); + } + + } + + public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) + { + SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query = from userRole in queryObj + where userRole.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 && + userRole.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + userRole.RowKey == SecUtility.Escape(roleName) + select userRole; + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable userRows = q.ExecuteAllWithRetries(); + + if (userRows == null) + { + return false; + } + List l = new List(userRows); + if (l.Count == 0) + { + // the role does not exist + return false; + } + RoleRow role; + if (IsStaleRole(l, out role)) { + return false; + } + if (l.Count > 1 && throwOnPopulatedRole) { + throw new ProviderException("Cannot delete populated role."); + } + svc.DeleteObject(role); + svc.SaveChangesWithRetries(); + // lets try to remove all remaining elements in the role + foreach(RoleRow row in l) { + if (row != role) { + try + { + svc.DeleteObject(row); + svc.SaveChangesWithRetries(); + } + catch (InvalidOperationException ex) + { + HttpStatusCode status; + if (TableStorageHelpers.EvaluateException(ex, out status) && (status == HttpStatusCode.NoContent || status == HttpStatusCode.NotFound)) + { + // this element already was already deleted by another process or during a failed retry + // this is not a fatal error; continue deleting elements + Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} does not exist in the role {1}.", row.UserName, row.RoleName)); + } + else + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Error deleting user {0} from role {1}.", row.UserName, row.RoleName)); + } + } + } + } + return true; + } + catch (InvalidOperationException e) + { + throw new ProviderException("Error while accessing the data store.", e); + } + } + + // Because of limited transactional support in the table storage offering, this function gives limited guarantees + // for inserting all users into all roles. + // We do not recommend using this function because of missing transactional support. + public override void AddUsersToRoles(string[] usernames, string[] roleNames) + { + SecUtility.CheckArrayParameter(ref roleNames, true, true, true, MaxTableRoleNameLength, "roleNames"); + SecUtility.CheckArrayParameter(ref usernames, true, true, true, Constants.MaxTableUsernameLength, "usernames"); + + RoleRow row; + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + foreach (string role in roleNames) + { + if (!RoleExists(role)) + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist!", role)); + } + foreach (string user in usernames) + { + row = new RoleRow(_applicationName, role, user); + try + { + svc.AddObject(_tableName, row); + svc.SaveChangesWithRetries(); + } + catch (InvalidOperationException e) + { + HttpStatusCode status; + if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.Conflict) + { + // this element already exists or was created in a failed retry + // this is not a fatal error; continue adding elements + Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} already exists in the role {1}.", user, role)); + svc.Detach(row); + } + else + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Error adding user {0} to role {1}", user, role)); + } + } + } + } + } + catch (InvalidOperationException e) + { + throw new ProviderException("Error while accessing the data store.", e); + } + } + + // the username to match can be in a format that varies between providers + // for this implementation, a syntax similar to the one used in the SQL provider is applied + // "user%" will return all users in a role that start with the string "user" + // the % sign can only appear at the end of the usernameToMatch parameter + // because the current version of the table storage service does not support StartsWith in LINQ queries, + // calling this function can cause significant network trafic when '%' is used in the usernameToMach + // parameter + public override string[] FindUsersInRole(string roleName, string usernameToMatch) + { + SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); + SecUtility.CheckParameter(ref usernameToMatch, true, true, false, Constants.MaxTableUsernameLength, "usernameToMatch"); + + bool startswith = false; + if (usernameToMatch.Contains('%')) + { + if (usernameToMatch.IndexOf('%') != usernameToMatch.Length - 1) + { + throw new ArgumentException("The TableStorageRoleProvider only supports search strings that contain '%' as the last character!"); + } + usernameToMatch = usernameToMatch.Substring(0, usernameToMatch.Length - 1); + startswith = true; + } + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query; + + if (startswith && string.IsNullOrEmpty(usernameToMatch)) { + // get all users in the role + query = from userRole in queryObj + where userRole.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 && + userRole.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + userRole.RowKey == SecUtility.Escape(roleName) + select userRole; + } else if (startswith) { + // get all users in the role that start with the specified string (we cannot restrict the query more because StartsWith is not supported) + // we cannot include the username to search for in the key, because the key might e escaped + query = from userRole in queryObj + where userRole.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 && + userRole.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && + userRole.RowKey == SecUtility.Escape(roleName) && + (userRole.UserName.CompareTo(usernameToMatch) >= 0 || userRole.UserName == string.Empty) + select userRole; + } else { + // get a specific user + query = from userRole in queryObj + where (userRole.PartitionKey == SecUtility.CombineToKey(_applicationName, usernameToMatch) || + userRole.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty)) && + userRole.RowKey == SecUtility.Escape(roleName) + select userRole; + } + + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable userRows = q.ExecuteAllWithRetries(); + + if (userRows == null) + { + throw new ProviderException("The role does not exist!"); + } + List l = new List(userRows); + if (l.Count == 0) + { + // the role does not exist + throw new ProviderException("The role does not exist!"); + } + RoleRow role; + if (IsStaleRole(l, out role)) + { + throw new ProviderException("The role does not exist!"); + } + List ret = new List(); + foreach (RoleRow row in l) + { + if (row != role) + { + if (startswith && !string.IsNullOrEmpty(usernameToMatch) && !row.UserName.StartsWith(usernameToMatch, StringComparison.Ordinal)) + { + continue; + } + ret.Add(row.UserName); + } + } + return ret.ToArray(); + } + catch (InvalidOperationException e) + { + throw new ProviderException("Error while accessing the data store.", e); + } + } + + // remember that there is no is no rollback functionality for the table storage service right now + // be cautious when using this function + // if a role does not exist, we stop deleting roles, if a user in a role does not exist, we continue deleting + // in case of error conditions, the behavior of this function is different than the SQL role provider + public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) + { + SecUtility.CheckArrayParameter(ref roleNames, true, true, true, MaxTableRoleNameLength, "roleNames"); + SecUtility.CheckArrayParameter(ref usernames, true, true, true, Constants.MaxTableUsernameLength, "usernames"); + + RoleRow row; + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + foreach (string role in roleNames) + { + if (!RoleExists(role)) + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist!", role)); + } + foreach (string user in usernames) + { + row = GetUserInRole(svc, role, user); + if (row == null) + { + Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} does not exist in the role {1}.", user, role)); + continue; + } + try + { + svc.DeleteObject(row); + svc.SaveChangesWithRetries(); + } + catch (Exception e) + { + HttpStatusCode status; + if (TableStorageHelpers.EvaluateException(e, out status) && (status == HttpStatusCode.NoContent || status == HttpStatusCode.NotFound)) + { + Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} does not exist in the role {1}.", user, role)); + svc.Detach(row); + } + else + { + throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Error deleting user {0} from role {1}.", user, role)); + } + } + } + } + } + catch (InvalidOperationException e) + { + throw new ProviderException("Error while accessing the data store.", e); + } + } + + #endregion + + + #region Helper methods + + private TableStorageDataServiceContext CreateDataServiceContext() + { + return _tableStorage.GetDataServiceContext(); + } + + private static bool IsStaleRole(List l, string rolename) { + if (l == null || l.Count == 0) + { + return false; + } + foreach (RoleRow row in l) + { + // if (row.RoleName == rolename && row.UserName == string.Empty) + if (string.Compare(row.RoleName, rolename, StringComparison.Ordinal) == 0 && string.IsNullOrEmpty(row.UserName)) + { + return false; + } + } + return true; + } + + + private static bool IsStaleRole(List l, out RoleRow role) + { + role = null; + if (l == null || l.Count == 0) + { + return false; + } + string rolename = l.ElementAt(0).RoleName; + foreach (RoleRow row in l) + { + Debug.Assert(row.RoleName == rolename); + if (string.IsNullOrEmpty(row.UserName)) + { + role = row; + return false; + } + } + return true; + } + + private RoleRow GetUserInRole(DataServiceContext svc, string rolename, string username) + { + SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username"); + SecUtility.CheckParameter(ref rolename, true, true, true, MaxTableRoleNameLength, "rolename"); + + try + { + DataServiceQuery queryObj = svc.CreateQuery(_tableName); + + IEnumerable query = from user in queryObj + where user.PartitionKey == SecUtility.CombineToKey(_applicationName, username) && + user.RowKey == SecUtility.Escape(rolename) + select user; + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + try + { + IEnumerable userRows = q.ExecuteAllWithRetries(); + return userRows.First(); + } + catch (DataServiceQueryException e) + { + HttpStatusCode s; + if (TableStorageHelpers.EvaluateException(e, out s) && s == HttpStatusCode.NotFound) + { + return null; + } + else + { + throw; + } + } + } + catch (InvalidOperationException e) + { + throw new ProviderException("Error while accessing the data store.", e); + } + } + + #endregion + } + +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageSessionStateProvider.cs b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageSessionStateProvider.cs new file mode 100644 index 0000000..9338574 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageSessionStateProvider.cs @@ -0,0 +1,882 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +// This file contains an implementation of a session state provider that uses both blob and table storage. + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration.Provider; +using System.Data.Services.Client; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Security; +using System.Web; +using System.Web.SessionState; +using Microsoft.Samples.ServiceHosting.StorageClient; + + +namespace Microsoft.Samples.ServiceHosting.AspProviders +{ + /// + /// This class allows DevtableGen to generate the correct table (named 'Sessions') + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", + Justification="Class is used by the devtablegen tool to generate a database for the development storage tool")] + internal class SessionDataServiceContext : TableStorageDataServiceContext + { + public IQueryable Sessions + { + get + { + return this.CreateQuery("Sessions"); + } + } + } + + [CLSCompliant(false)] + public class SessionRow : TableStorageEntity + { + private string _id; + private string _applicationName; + private string _blobName; + private DateTime _expires; + private DateTime _created; + private DateTime _lockDate; + + + // application name + session id is partitionKey + public SessionRow(string sessionId, string applicationName) + : base() + { + SecUtility.CheckParameter(ref sessionId, true, true, true, TableStorageConstants.MaxStringPropertySizeInChars, "sessionId"); + SecUtility.CheckParameter(ref applicationName, true, true, true, Constants.MaxTableApplicationNameLength, "applicationName"); + + PartitionKey = SecUtility.CombineToKey(applicationName, sessionId); + RowKey = string.Empty; + + Id = sessionId; + ApplicationName = applicationName; + ExpiresUtc = TableStorageConstants.MinSupportedDateTime; + LockDateUtc = TableStorageConstants.MinSupportedDateTime; + CreatedUtc = TableStorageConstants.MinSupportedDateTime; + Timeout = 0; + BlobName = string.Empty; + } + + public SessionRow() + : base() + { + } + + public string Id + { + set { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _id = value; + PartitionKey = SecUtility.CombineToKey(ApplicationName, Id); + } + get + { + return _id; + } + } + + public string ApplicationName + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _applicationName = value; + PartitionKey = SecUtility.CombineToKey(ApplicationName, Id); + } + get + { + return _applicationName; + } + } + + public int Timeout + { + set; + get; + } + + public DateTime ExpiresUtc + { + set + { + SecUtility.SetUtcTime(value, out _expires); + } + get + { + return _expires; + } + } + + public DateTime CreatedUtc + { + set + { + SecUtility.SetUtcTime(value, out _created); + } + get + { + return _created; + } + } + + public DateTime LockDateUtc + { + set + { + SecUtility.SetUtcTime(value, out _lockDate); + } + get + { + return _lockDate; + } + } + + public bool Locked + { + set; + get; + } + + public int Lock + { + set; + get; + } + + public string BlobName + { + set + { + if (value == null) + { + throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value."); + } + _blobName = value; + } + get + { + return _blobName; + } + } + + public bool Initialized + { + set; + get; + } + + } + + public class TableStorageSessionStateProvider : SessionStateStoreProviderBase + { + + #region Member variables and constants + + private string _applicationName; + private string _accountName; + private string _sharedKey; + private string _tableName; + private string _tableServiceBaseUri; + private string _blobServiceBaseUri; + private string _containerName; + private TableStorage _tableStorage; + private BlobProvider _blobProvider; + private const int NumRetries = 3; + private RetryPolicy _tableRetry = RetryPolicies.RetryN(NumRetries, TimeSpan.FromSeconds(1)); + private ProviderRetryPolicy _providerRetry = ProviderRetryPolicies.RetryN(NumRetries, TimeSpan.FromSeconds(1)); + + #endregion + + + #region public methods + + public override void Initialize(string name, NameValueCollection config) + { + // Verify that config isn't null + if (config == null) + { + throw new ArgumentNullException("config"); + } + + // Assign the provider a default name if it doesn't have one + if (String.IsNullOrEmpty(name)) + { + name = "TableServiceSessionStateProvider"; + } + + // Add a default "description" attribute to config if the + // attribute doesn't exist or is empty + if (string.IsNullOrEmpty(config["description"])) + { + config.Remove("description"); + config.Add("description", "Session state provider using table storage"); + } + + // Call the base class's Initialize method + base.Initialize(name, config); + + bool allowInsecureRemoteEndpoints = Configuration.GetBooleanValue(config, "allowInsecureRemoteEndpoints", false); + + // structure storage-related properties + _applicationName = Configuration.GetStringValueWithGlobalDefault(config, "applicationName", + Configuration.DefaultProviderApplicationNameConfigurationString, + Configuration.DefaultProviderApplicationName, false); + _accountName = Configuration.GetStringValue(config, "accountName", null, true); + _sharedKey = Configuration.GetStringValue(config, "sharedKey", null, true); + _tableName = Configuration.GetStringValueWithGlobalDefault(config, "sessionTableName", + Configuration.DefaultSessionTableNameConfigurationString, + Configuration.DefaultSessionTableName, false); + _tableServiceBaseUri = Configuration.GetStringValue(config, "tableServiceBaseUri", null, true); + _containerName = Configuration.GetStringValueWithGlobalDefault(config, "containerName", + Configuration.DefaultSessionContainerNameConfigurationString, + Configuration.DefaultSessionContainerName, false); + if (!SecUtility.IsValidContainerName(_containerName)) + { + throw new ProviderException("The provider configuration for the TableStorageSessionStateProvider does not contain a valid container name. " + + "Please refer to the documentation for the concrete rules for valid container names." + + "The current container name is: " + _containerName); + } + _blobServiceBaseUri = Configuration.GetStringValue(config, "blobServiceBaseUri", null, true); + + config.Remove("allowInsecureRemoteEndpoints"); + config.Remove("accountName"); + config.Remove("sharedKey"); + config.Remove("containerName"); + config.Remove("applicationName"); + config.Remove("blobServiceBaseUri"); + config.Remove("tableServiceBaseUri"); + config.Remove("sessionTableName"); + + // Throw an exception if unrecognized attributes remain + if (config.Count > 0) + { + string attr = config.GetKey(0); + if (!String.IsNullOrEmpty(attr)) + throw new ProviderException + ("Unrecognized attribute: " + attr); + } + + StorageAccountInfo tableInfo = null; + StorageAccountInfo blobInfo = null; + try + { + tableInfo = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration(true); + blobInfo = StorageAccountInfo.GetDefaultBlobStorageAccountFromConfiguration(true); + if (_tableServiceBaseUri != null) + { + tableInfo.BaseUri = new Uri(_tableServiceBaseUri); + } + if (_blobServiceBaseUri != null) + { + blobInfo.BaseUri = new Uri(_blobServiceBaseUri); + } + if (_accountName != null) + { + tableInfo.AccountName = _accountName; + blobInfo.AccountName = _accountName; + } + if (_sharedKey != null) + { + tableInfo.Base64Key = _sharedKey; + blobInfo.Base64Key = _sharedKey; + } + tableInfo.CheckComplete(); + SecUtility.CheckAllowInsecureEndpoints(allowInsecureRemoteEndpoints, tableInfo); + blobInfo.CheckComplete(); + SecUtility.CheckAllowInsecureEndpoints(allowInsecureRemoteEndpoints, blobInfo); + _tableStorage = TableStorage.Create(tableInfo); + _tableStorage.RetryPolicy = _tableRetry; + _tableStorage.TryCreateTable(_tableName); + _blobProvider = new BlobProvider(blobInfo, _containerName); + } + catch (SecurityException) + { + throw; + } + // catch InvalidOperationException as well as StorageException + catch (Exception e) + { + string exceptionDescription = Configuration.GetInitExceptionDescription(tableInfo, blobInfo); + string tableName = (_tableName == null) ? "no session table name specified" : _tableName; + string containerName = (_containerName == null) ? "no container name specified" : _containerName; + Log.Write(EventKind.Error, "Initialization of data service structures (tables and/or blobs) failed!" + + exceptionDescription + Environment.NewLine + + "Configured blob container: " + containerName + Environment.NewLine + + "Configured table name: " + tableName + Environment.NewLine + + e.Message + Environment.NewLine + e.StackTrace); + throw new ProviderException("Initialization of data service structures (tables and/or blobs) failed!" + + "The most probable reason for this is that " + + "the storage endpoints are not configured correctly. Please look at the configuration settings " + + "in your .cscfg and Web.config files. More information about this error " + + "can be found in the logs when running inside the hosting environment or in the output " + + "window of Visual Studio.", e); + } + Debug.Assert(_blobProvider != null); + } + + public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout) + { + Debug.Assert(context != null); + return new SessionStateStoreData(new SessionStateItemCollection(), + SessionStateUtility.GetSessionStaticObjects(context), + timeout); + } + + public override void CreateUninitializedItem(HttpContext context, string id, int timeout) + { + Debug.Assert(context != null); + SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); + if (timeout < 0) + { + throw new ArgumentException("Parameter timeout must be a non-negative integer!"); + } + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + SessionRow session = new SessionRow(id, _applicationName); + + session.Lock = 0; // no lock + session.Initialized = false; + session.Id = id; + session.Timeout = timeout; + session.ExpiresUtc = DateTime.UtcNow.AddMinutes(timeout); + svc.AddObject(_tableName, session); + svc.SaveChangesWithRetries(); + } catch (InvalidOperationException e) { + throw new ProviderException("Error accessing the data store.", e); + } + } + + public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, + out object lockId, out SessionStateActions actions) + { + Debug.Assert(context != null); + SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); + + return GetSession(context, id, out locked, out lockAge, out lockId, out actions, false); + } + + public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, + out bool locked, out TimeSpan lockAge, out object lockId, + out SessionStateActions actions) + { + Debug.Assert(context != null); + SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); + + return GetSession(context, id, out locked, out lockAge, out lockId, out actions, true); + } + + public override void SetAndReleaseItemExclusive(HttpContext context, string id, + SessionStateStoreData item, object lockId, bool newItem) + { + Debug.Assert(context != null); + SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); + + _providerRetry(() => + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + SessionRow session; + + if (!newItem) + { + session = GetSession(id, svc); + if (session == null || session.Lock != (int)lockId) + { + Debug.Assert(false); + return; + } + } + else + { + session = new SessionRow(id, _applicationName); + session.Lock = 1; + session.LockDateUtc = DateTime.UtcNow; + } + session.Initialized = true; + Debug.Assert(session.Timeout >= 0); + session.Timeout = item.Timeout; + session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); + session.Locked = false; + + // yes, we always create a new blob here + session.BlobName = GetBlobNamePrefix(id) + Guid.NewGuid().ToString("N"); + + + // Serialize the session and write the blob + byte[] items, statics; + SerializeSession(item, out items, out statics); + string serializedItems = Convert.ToBase64String(items); + string serializedStatics = Convert.ToBase64String(statics); + MemoryStream output = new MemoryStream(); + StreamWriter writer = new StreamWriter(output); + + try + { + writer.WriteLine(serializedItems); + writer.WriteLine(serializedStatics); + writer.Flush(); + // for us, it shouldn't matter whether newItem is set to true or false + // because we always create the entire blob and cannot append to an + // existing one + _blobProvider.UploadStream(session.BlobName, output); + writer.Close(); + output.Close(); + } + catch (Exception e) + { + if (!newItem) + { + ReleaseItemExclusive(svc, session, lockId); + } + throw new ProviderException("Error accessing the data store.", e); + } + finally + { + if (writer != null) + { + writer.Close(); + } + if (output != null) + { + output.Close(); + } + } + + if (newItem) + { + svc.AddObject(_tableName, session); + svc.SaveChangesWithRetries(); + } + else + { + // Unlock the session and save changes + ReleaseItemExclusive(svc, session, lockId); + } + }); + } + + public override void ReleaseItemExclusive(HttpContext context, string id, object lockId) + { + Debug.Assert(context != null); + Debug.Assert(lockId != null); + SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + SessionRow session = GetSession(id, svc); + ReleaseItemExclusive(svc, session, lockId); + } + catch (InvalidOperationException e) + { + throw new ProviderException("Error accessing the data store!", e); + } + } + + public override void ResetItemTimeout(HttpContext context, string id) + { + Debug.Assert(context != null); + SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); + + _providerRetry(() => { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + SessionRow session = GetSession(id, svc); + session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); + svc.UpdateObject(session); + svc.SaveChangesWithRetries(); + }); + } + + public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item) + { + Debug.Assert(context != null); + Debug.Assert(lockId != null); + Debug.Assert(_blobProvider != null); + SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); + + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + SessionRow session = GetSession(id, svc); + if (session == null) + { + Debug.Assert(false); + return; + } + if (session.Lock != (int)lockId) + { + Debug.Assert(false); + return; + } + svc.DeleteObject(session); + svc.SaveChangesWithRetries(); + } + catch (InvalidOperationException e) + { + throw new ProviderException("Error accessing the data store!", e); + } + + // delete associated blobs + try + { + IEnumerable e = _blobProvider.ListBlobs(GetBlobNamePrefix(id)); + if (e == null) + { + return; + } + IEnumerator props = e.GetEnumerator(); + if (props == null) + { + return; + } + while (props.MoveNext()) + { + if (props.Current != null) + { + if (!_blobProvider.DeleteBlob(props.Current.Name)) { + // ignore this; it is possible that another thread could try to delete the blob + // at the same time + } + } + } + } catch(StorageException e) { + throw new ProviderException("Error accessing blob storage.", e); + } + } + + public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback) + { + // This provider doesn't support expiration callbacks + // so simply return false here + return false; + } + + public override void InitializeRequest(HttpContext context) + { + // no specific logic for initializing requests in this provider + } + + public override void EndRequest(HttpContext context) + { + // no specific logic for ending requests in this provider + } + + // nothing can be done here because there might be session managers at different machines involved in + // handling sessions + public override void Dispose() + { + } + + #endregion + + #region Helper methods + + private TableStorageDataServiceContext CreateDataServiceContext() + { + return _tableStorage.GetDataServiceContext(); + } + + private static void ReleaseItemExclusive(TableStorageDataServiceContext svc, SessionRow session, object lockId) + { + if ((int)lockId != session.Lock) + { + // obviously that can happen, but let's see when at least in Debug mode + Debug.Assert(false); + return; + } + + session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); + session.Locked = false; + svc.UpdateObject(session); + svc.SaveChangesWithRetries(); + } + + + private SessionRow GetSession(string id) + { + DataServiceContext svc = CreateDataServiceContext(); + return GetSession(id, svc); + } + + + private SessionRow GetSession(string id, DataServiceContext context) + { + Debug.Assert(context != null); + Debug.Assert(id != null && id.Length <= TableStorageConstants.MaxStringPropertySizeInChars); + + try + { + DataServiceQuery queryObj = context.CreateQuery(_tableName); + IEnumerable query = from session in queryObj + where session.PartitionKey == SecUtility.CombineToKey(_applicationName, id) + select session; + TableStorageDataServiceQuery q = new TableStorageDataServiceQuery(query as DataServiceQuery, _tableRetry); + IEnumerable sessions = q.ExecuteWithRetries(); + + // enumerate the result and store it in a list + List sessionList = new List(sessions); + if (sessionList != null && sessionList.Count() == 1) + { + return sessionList.First(); + } else if (sessionList != null && sessionList.Count() > 1) { + throw new ProviderException("Multiple sessions with the same name!"); + } + else + { + return null; + } + } + catch (Exception e) + { + throw new ProviderException("Error accessing storage.", e); + } + } + + + // we don't use the retry policy itself in this function because out parameters are not well handled by + // retry policies + private SessionStateStoreData GetSession(HttpContext context, string id, out bool locked, out TimeSpan lockAge, + out object lockId, out SessionStateActions actions, + bool exclusive) + { + Debug.Assert(context != null); + SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); + + SessionRow session = null; + + int curRetry = 0; + bool retry = false; + + // Assign default values to out parameters + locked = false; + lockId = null; + lockAge = TimeSpan.Zero; + actions = SessionStateActions.None; + + do + { + retry = false; + try + { + TableStorageDataServiceContext svc = CreateDataServiceContext(); + session = GetSession(id, svc); + + // Assign default values to out parameters + locked = false; + lockId = null; + lockAge = TimeSpan.Zero; + actions = SessionStateActions.None; + + // if the blob does not exist, we return null + // ASP.NET will call the corresponding method for creating the session + if (session == null) + { + return null; + } + if (session.Initialized == false) + { + Debug.Assert(session.Locked == false); + actions = SessionStateActions.InitializeItem; + session.Initialized = true; + } + session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); + if (exclusive) + { + if (!session.Locked) + { + if (session.Lock == Int32.MaxValue) + { + session.Lock = 0; + } + else + { + session.Lock++; + } + session.LockDateUtc = DateTime.UtcNow; + } + lockId = session.Lock; + locked = session.Locked; + session.Locked = true; + } + lockAge = DateTime.UtcNow.Subtract(session.LockDateUtc); + lockId = session.Lock; + + if (locked == true) + { + return null; + } + + // let's try to write this back to the data store + // in between, someone else could have written something to the store for the same session + // we retry a number of times; if all fails, we throw an exception + svc.UpdateObject(session); + svc.SaveChangesWithRetries(); + } + catch (InvalidOperationException e) + { + HttpStatusCode status; + // precondition fails indicates problems with the status code + if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.PreconditionFailed) + { + retry = true; + } + else + { + throw new ProviderException("Error accessing the data store.", e); + } + } + } while (retry && curRetry++ < NumRetries); + + // ok, now we have successfully written back our state + // we can now read the blob + // note that we do not need to care about read/write locking when accessing the + // blob because each time we write a new session we create a new blob with a different name + + SessionStateStoreData result = null; + MemoryStream stream = null; + StreamReader reader = null; + BlobProperties properties; + try + { + try + { + stream = _blobProvider.GetBlobContent(session.BlobName, out properties); + } + catch (StorageException e) + { + throw new ProviderException("Couldn't read session blob!", e); + } + + reader = new StreamReader(stream); + if (actions == SessionStateActions.InitializeItem) + { + // Return an empty SessionStateStoreData + result = new SessionStateStoreData(new SessionStateItemCollection(), + SessionStateUtility.GetSessionStaticObjects(context), session.Timeout); + } + else + { + // Read Items, StaticObjects, and Timeout from the file + byte[] items = Convert.FromBase64String(reader.ReadLine()); + byte[] statics = Convert.FromBase64String(reader.ReadLine()); + int timeout = session.Timeout; + // Deserialize the session + result = DeserializeSession(items, statics, timeout); + } + } + finally + { + if (stream != null) + { + stream.Close(); + } + if (reader != null) + { + reader.Close(); + } + } + return result; + } + + private string GetBlobNamePrefix(string id) + { + return string.Format(CultureInfo.InstalledUICulture, "{0}{1}", id, _applicationName); + } + + private static void SerializeSession(SessionStateStoreData store, out byte[] items, out byte[] statics) + { + bool hasItems = (store.Items != null && store.Items.Count > 0); + bool hasStaticObjects = (store.StaticObjects != null && store.StaticObjects.Count > 0 && !store.StaticObjects.NeverAccessed); + items = null; + statics = new byte [0]; + + using (MemoryStream stream1 = new MemoryStream()) + { + using (BinaryWriter writer1 = new BinaryWriter(stream1)) + { + writer1.Write(hasItems); + if (hasItems) + { + ((SessionStateItemCollection)store.Items).Serialize(writer1); + } + items = stream1.ToArray(); + } + } + + if (hasStaticObjects) + { + throw new ProviderException("Static objects are not supported in this provider because of security-related hosting constraints."); + } + } + + private static SessionStateStoreData DeserializeSession(byte[] items, byte[] statics, int timeout) + { + SessionStateItemCollection itemCol = null; + HttpStaticObjectsCollection staticCol = null; + + using (MemoryStream stream1 = new MemoryStream(items)) + { + using (BinaryReader reader1 = new BinaryReader(stream1)) + { + bool hasItems = reader1.ReadBoolean(); + if (hasItems) + { + itemCol = SessionStateItemCollection.Deserialize(reader1); + } + else + { + itemCol = new SessionStateItemCollection(); + } + } + } + + if (HttpContext.Current != null && HttpContext.Current.Application != null && + HttpContext.Current.Application.StaticObjects != null && HttpContext.Current.Application.StaticObjects.Count > 0) { + throw new ProviderException("This provider does not support static session objects because of security-related hosting constraints."); + } + + if (statics != null && statics.Count() > 0) { + throw new ProviderException("This provider does not support static session objects because of security-related hosting constraints."); + } + + return new SessionStateStoreData(itemCol, staticCol, timeout); + } + + #endregion + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/AspProviders.dll b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/AspProviders.dll new file mode 100644 index 0000000..0b14b98 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/AspProviders.dll differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/AspProviders.pdb b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/AspProviders.pdb new file mode 100644 index 0000000..7fa2cbe Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/AspProviders.pdb differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/StorageClient.dll b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/StorageClient.dll new file mode 100644 index 0000000..539fd6d Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/StorageClient.dll differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/StorageClient.pdb b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/StorageClient.pdb new file mode 100644 index 0000000..f10d989 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/StorageClient.pdb differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/StorageClient.xml b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/StorageClient.xml new file mode 100644 index 0000000..8adfba6 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Debug/StorageClient.xml @@ -0,0 +1,2002 @@ + + + + StorageClient + + + + + This type represents the different constituent parts that make up a resource Uri in the context of cloud services. + + + + + Construct a ResourceUriComponents object. + + The account name that should become part of the URI. + The container name (container, queue or table name) that should become part of the URI. + Remaining part of the URI. + + + + Construct a ResourceUriComponents object. + + The account name that should become part of the URI. + The container name (container, queue or table name) that should become part of the URI. + + + + Construct a ResourceUriComponents object. + + The account name that should become part of the URI. + + + + Construct a ResourceUriComponents object. + + + + + The account name in the URI. + + + + + This is really the first component (delimited by '/') after the account name. Since it happens to + be a container name in the context of all our storage services (containers in blob storage, + queues in the queue service and table names in table storage), it's named as ContainerName to make it more + readable at the cost of slightly being incorrectly named. + + + + + The remaining string in the URI. + + + + + Create a canonicalized string out of HTTP request header contents for signing + blob/queue requests with the Shared Authentication scheme. + + The uri address of the HTTP request. + Components of the Uri extracted out of the request. + The method of the HTTP request (GET/PUT, etc.). + The content type of the HTTP request. + The date of the HTTP request. + Should contain other headers of the HTTP request. + A canonicalized string of the HTTP request. + + + + Canonicalize HTTP header contents. + + An HttpWebRequest object. + Components of the Uri extracted out of the request. + The canonicalized string of the given HTTP request's header. + + + + Creates a standard datetime string for the shared key lite authentication scheme. + + DateTime value to convert to a string in the expected format. + + + + + An internal class that stores the canonicalized string version of an HTTP request. + + + + + Constructor for the class. + + The first canonicalized element to start the string with. + + + + Append additional canonicalized element to the string. + + An additional canonicalized element to append to the string. + + + + Property for the canonicalized string. + + + + + Use this class to extract various header values from Http requests. + + + + + A helper function for extracting HTTP header values from a NameValueCollection object. + + A NameValueCollection object that should contain HTTP header name-values pairs. + Name of the header that we want to get values of. + A array list of values for the header. The values are in the same order as they are stored in the NameValueCollection object. + + + + Constructs an URI given all its constituents + + + This is the service endpoint in case of path-style URIs and a host suffix in case of host-style URIs + IMPORTANT: This does NOT include the service name or account name + + Uri constituents + Indicates whether to construct a path-style Uri (true) or host-style URI (false) + Full uri + + + + Constructs a path-style resource URI given all its constituents + + + + + Constructs a host-style resource URI given all its constituents + + + + + Given the host suffix part, service name and account name, this method constructs the account Uri + + + + + Objects of this class contain the credentials (name and key) of a storage account. + + + + + Create a SharedKeyCredentials object given an account name and a shared key. + + + + + Signs the request appropriately to make it an authenticated request. + Note that this method takes the URI components as decoding the URI components requires the knowledge + of whether the URI is in path-style or host-style and a host-suffix if it's host-style. + + + + + Signs requests using the SharedKeyLite authentication scheme with is used for the table storage service. + + + + + This is the default content-type xStore uses when no content type is specified + + + + + When transmitting a blob that is larger than this constant, this library automatically + transmits the blob as individual blocks. I.e., the blob is (1) partitioned + into separate parts (these parts are called blocks) and then (2) each of the blocks is + transmitted separately. + The maximum size of this constant as supported by the real blob storage service is currently + 64 MB; the development storage tool currently restricts this value to 2 MB. + Setting this constant can have a significant impact on the performance for uploading or + downloading blobs. + As a general guideline: If you run in a reliable environment increase this constant to reduce + the amount of roundtrips. In an unreliable environment keep this constant low to reduce the + amount of data that needs to be retransmitted in case of connection failures. + + + + + The size of a single block when transmitting a blob that is larger than the + MaximumBlobSizeBeforeTransmittingAsBlocks constant (see above). + The maximum size of this constant is currently 4 MB; the development storage + tool currently restricts this value to 1 MB. + Setting this constant can have a significant impact on the performance for uploading or + downloading blobs. + As a general guideline: If you run in a reliable environment increase this constant to reduce + the amount of roundtrips. In an unreliable environment keep this constant low to reduce the + amount of data that needs to be retransmitted in case of connection failures. + + + + + Contains regular expressions for checking whether container and table names conform + to the rules of the storage REST protocols. + + + + + Container or queue names that match against this regular expression are valid. + + + + + Table names that match against this regular expression are valid. + + + + + Converts the date time to a valid string form as per HTTP standards + + + + + Parse a string having the date time information in acceptable formats according to HTTP standards + + + + + Copies from one stream to another + + The stream to copy from + The stream to copy to + + + + Error codes that can be returned by the storage service or the client library. + These are divided into server errors and client errors depending on which side + the error can be attributed to. + + + + + The base class for storage service exceptions + + + + + Initializes a new instance of the class with + serialized data. + + The object that contains serialized object + data about the exception being thrown + The object that contains contextual information + about the source or destionation. + + + + Sets the object with additional exception information + + The object that holds the + serialized object data. + The object that contains contextual information + about the source or destionation. + + + + The Http status code returned by the storage service + + + + + The specific error code returned by the storage service + + + + + + + + + + Server exceptions are those due to server side problems. + These may be transient and requests resulting in such exceptions + can be retried with the same parameters. + + + + + Initializes a new instance of the class with + serialized data. + + The object that contains serialized object + data about the exception being thrown + The object that contains contextual information + about the source or destionation. + + + + Client side exceptions are due to incorrect parameters to the request. + These requests should not be retried with the same parameters + + + + + Initializes a new instance of the class with + serialized data. + + The object that contains serialized object + data about the exception being thrown + The object that contains contextual information + about the source or destionation. + + + + Error code strings that are common to all storage services + + + + + Error code strings that are specific to blob service + + + + + Error code strings that are specific to queue service + + + + + Error code strings that are specific to queue service + + public static class TableErrorCodeStrings + + + + The entry point of the blob storage API + + + + + Factory method for BlobStorage + + The base URI of the blob storage service + If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used. + If false host-style URIs (http://accountname.baseuri/containername/objectname) are used, + where baseuri is the URI of the service. + If null, the choice is made automatically: path-style URIs if host name part of base URI is an + IP addres, host-style otherwise. + The name of the storage account + Authentication key used for signing requests + A newly created BlobStorage instance + + + + Factory method for BlobStorage + + Account information + A newly created BlobStorage instance + + + + Get a reference to a newly created BlobContainer object. + This method does not make any calls to the storage service. + + The name of the container + A reference to a newly created BlobContainer object + + + + Lists the containers within the account. + + A list of containers + + + + The default timeout + + + + + The default retry policy + + + + + The time out for each request to the storage service. + + + + + The retry policy used for retrying requests + + + + + The base URI of the blob storage service + + + + + The name of the storage account + + + + + Indicates whether to use/generate path-style or host-style URIs + + + + + Get a reference to a BlobContainer object with the given name. + This method does not make any calls to the storage service. + + The name of the container + A reference to a newly created BlobContainer object + + + + Lists the containers within the account. + + A list of containers + + + + The blob container class. + Used to access and enumerate blobs in the container. + Storage key credentials are needed to access private blobs but not for public blobs. + + + + + Use this constructor to access private blobs. + + The base Uri for the storage endpoint + Name of the storage account + Name of the container + + + + Use this constructor to access private blobs. + + The base Uri for the storage endpoint + + If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used and if false + host-style URIs (http://accountname.baseuri/containername/objectname) are used, where baseuri is the + URI of the service + + Name of the storage account + Name of the container + Date of last modification + + + + Create the container if it does not exist. + The container is created with private access control and no metadata. + + true if the container was created. false if the container already exists + + + + Create the container with the specified metadata and access control if it does not exist + + The metadata for the container. Can be null to indicate no metadata + The access control (public or private) with which to create the container + true if the container was created. false if the container already exists + + + + Check if the blob container exists + + true if the container exists, false otherwise. + + + + Get the properties for the container if it exists. + + The properties for the container if it exists, null otherwise + + + + Get the access control permissions associated with the container. + + + + + + Set the access control permissions associated with the container. + + The permission to set + + + + Deletes the current container. + + + + + Check if the blob container exists + + Name of the BLOB. + true if the blob exists, false otherwise. + + + + Create a new blob or overwrite an existing blob. + + The properties of the blob + The contents of the blob + Should this request overwrite an existing blob ? + true if the blob was created. false if the blob already exists and was set to false + The LastModifiedTime property of is set as a result of this call. + This method also has an effect on the ETag values that are managed by the service. + + + + Updates an existing blob if it has not been modified since the specified time which is typically + the last modified time of the blob when you retrieved it. + Use this method to implement optimistic concurrency by avoiding clobbering changes to the blob + made by another writer. + + The properties of the blob. This object should be one previously + obtained from a call to GetBlob or GetBlobProperties and have its LastModifiedTime property set. + The contents of the blob. The contents of the blob should be readable + true if the blob was updated. false if the blob has changed since the last time + The LastModifiedTime property of is set as a result of this call. + This method also has an effect on the ETag values that are managed by the service if the update was + successful. + + + + Get the blob contents and properties if the blob exists + + The name of the blob + Object in which the contents are returned. + This object should contain a writable stream or should be a default constructed object. + Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller pieces in case of failure. + The properties of the blob if the blob exists. + + + + Gets the blob contents and properties if the blob has not been modified since the time specified. + Use this method if you have cached the contents of a blob and want to avoid retrieving the blob + if it has not changed since the last time you retrieved it. + + The properties of the blob obtained from an earlier call to GetBlob. This + parameter is updated by the call if the blob has been modified + Contains the stream to which the contents of the blob are written if it has been + modified + Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller pieces in case of failure. + true if the blob has been modified, false otherwise + + + + Get the properties of the blob if it exists. + This method is also the simplest way to check if a blob exists. + + The name of the blob + The properties of the blob if it exists. null otherwise. + The properties for the contents of the blob are not set + + + + Set the metadata of an existing blob. + + The blob properties object whose metadata is to be updated + + + + Set the metadata of an existing blob if it has not been modified since it was last retrieved. + + The blob properties object whose metadata is to be updated. + Typically obtained by a previous call to GetBlob or GetBlobProperties + true if the blob metadata was updated. false if it was not updated because the blob + has been modified + + + + Delete a blob with the given name + + The name of the blob + true if the blob exists and was successfully deleted, false if the blob does not exist + + + + Delete a blob with the given name if the blob has not been modified since it was last obtained. + Use this method for optimistic concurrency to avoid deleting a blob that has been modified since + the last time you retrieved it + + A blob object (typically previously obtained from a GetBlob call) + This out parameter is set to true if the blob was not deleted because + it was modified + true if the blob exists and was successfully deleted, false if the blob does not exist or was + not deleted because the blob was modified. + + + + Enumerates all blobs with a given prefix. + + + If true common prefixes with "/" as seperator + The list of blob properties and common prefixes + + + + The time out for each request to the storage service. + + + + + The retry policy used for retrying requests + + + + + The base URI of the blob storage service + + + + + The name of the storage account + + + + + The name of the blob container. + + + + + Indicates whether to use/generate path-style or host-style URIs + + + + + The URI of the container + + + + + The timestamp for last modification of container. + + + + + Create the container with the specified access control if it does not exist + + The metadata for the container. Can be null to indicate no metadata + The access control (public or private) with which to create the container + true if the container was created. false if the container already exists + + + + Get the properties for the container if it exists. + + The metadata for the container if it exists, null otherwise + + + + Get the access control permissions associated with the container. + + + + + + Get the access control permissions associated with the container. + + + + + + Create a new blob or overwrite an existing blob. + + + The properties of the blob + The contents of the blob + Should this request overwrite an existing blob ? + true if the blob was created. false if the blob already exists and was set to false + The LastModifiedTime property of is set as a result of this call + + + + Updates an existing blob if it has not been modified since the specified time which is typically + the last modified time of the blob when you retrieved it. + Use this method to implement optimistic concurrency by avoiding clobbering changes to the blob + made by another writer. + + The properties of the blob. This object should be one previously + obtained from a call to GetBlob or GetBlobProperties and have its LastModifiedTime property set. + The contents of the blob. The contents of the blob should be readable + true if the blob was updated. false if the blob has changed since the last time + The LastModifiedTime property of is set as a result of this call + + + + Get the blob contents and properties if the blob exisits + + The name of the blob + Object in which the contents are returned. + This object should contain a writable stream or should be a default constructed object. + Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller piecs in case of failure. + The properties of the blob if the blob exists. + + + + Gets the blob contents and properties if the blob has not been modified since the time specified. + Use this method if you have cached the contents of a blob and want to avoid retrieving the blob + if it has not changed since the last time you retrieved it. + + The properties of the blob obtained from an earlier call to GetBlob. This + parameter is updated by the call if the blob has been modified + Contains the stream to which the contents of the blob are written if it has been + modified + Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller piecs in case of failure. + true if the blob has been modified, false otherwise + + + + Get the properties of the blob if it exists. + This method is also the simplest way to check if a blob exists. + + The name of the blob + The properties of the blob if it exists. null otherwise. + The properties for the contents of the blob are not set + + + + Set the metadata of an existing blob. + + The blob properties object whose metadata is to be updated + + + + Set the metadata of an existing blob if it has not been modified since it was last retrieved. + + The blob properties object whose metadata is to be updated. + Typically obtained by a previous call to GetBlob or GetBlobProperties + + + + Delete a blob with the given name + + The name of the blob + true if the blob exists and was successfully deleted, false if the blob does not exist + + + + Delete a blob with the given name if the blob has not been modified since it was last obtained. + Use this method for optimistic concurrency to avoid deleting a blob that has been modified since + the last time you retrieved it + + A blob object (typically previously obtained from a GetBlob call) + This out parameter is set to true if the blob was not deleted because + it was modified + true if the blob exists and was successfully deleted, false if the blob does not exist or was + not deleted because the blob was modified. + + + + Enumerates all blobs with a given prefix. + + + If true common prefixes with "/" as seperator + The list of blob properties and common prefixes + + + + Uploads a blob in chunks. + + + + + + + + + + Helper method used for getting blobs, ranges of blobs and blob properties. + + Name of the blob + The output stream to write blob data to. Can be null if only retrieving blob properties + The If-None-Match header. Used to avoid downloading blob data if the blob has not changed + The If-Match header. Used to ensure that all chunks of the blob are of the same blob + The offset of the blob data to begin downloading from. Set to 0 to download all data. + The length of the blob data to download. Set to 0 to download all data + Query paramters to add to the request. + Whether the blob had been modfied with respect to the + + + + + Helper class for loading values from an XML segment + + + + + The entry point of the queue storage API + + + + + Factory method for QueueStorage + + The base URI of the queue service + If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used. + If false host-style URIs (http://accountname.baseuri/containername/objectname) are used, + where baseuri is the URI of the service. + If null, the choice is made automatically: path-style URIs if host name part of base URI is an + IP addres, host-style otherwise. + The name of the storage account + Authentication key used for signing requests + A newly created QueueStorage instance + + + + Get a reference to a Queue object with a specified name. This method does not make a call to + the queue service. + + The name of the queue + A newly created queue object + + + + Lists the queues within the account. + + A list of queues + + + + Lists the queues within the account that start with the given prefix. + + If prefix is null returns all queues. + A list of queues. + + + + The default timeout + + + + + The default retry policy + + + + + The time out for each request to the storage service. + + + + + The retry policy used for retrying requests + + + + + The base URI of the blob storage service + + + + + The name of the storage account + + + + + Indicates whether to use/generate path-style or host-style URIs + + + + + Objects of this class represent a single message in the queue. + + + + + The maximum message size in bytes. + + + + + The maximum amount of time a message is kept in the queue. Max value is 7 days. + Value is given in seconds. + + + + + This constructor is not publicly exposed. + + + + + Creates a message and initializes the content of the message to be the specified string. + + A string representing the contents of the message. + + + + Creates a message and given the specified byte contents. + In this implementation, regardless of whether an XML or binary data is passed into this + function, message contents are converted to base64 before passing the data to the queue service. + When calculating the size of the message, the size of the base64 encoding is thus the important + parameter. + + + + + + Returns the the contents of the message as a string. + + + + + Returns the content of the message as a byte array + + + + + When calling the Get() operation on a queue, the content of messages + returned in the REST protocol are represented as Base64-encoded strings. + This internal function transforms the Base64 representation into a byte array. + + The Base64-encoded string. + + + + Internal method used for creating the XML that becomes part of a REST request + + + + + A unique ID of the message as returned from queue operations. + + + + + When a message is retrieved from a queue, a PopReceipt is returned. The PopReceipt is used when + deleting a message from the queue. + + + + + The point in time when the message was put into the queue. + + + + + A message's expiration time. + + + + + The point in time when a message becomes visible again after a Get() operation was called + that returned the message. + + + + + Queues in the storage client library expose a functionality for listening for incoming messages. + If a message is put into a queue, a corresponding event is issued and this delegate is called. This functionality + is implemented internally in this library by periodically polling for incoming messages. + + The queue that has received a new event. + The event argument containing the message. + + + + The argument class for the MessageReceived event. + + + + + The message itself. + + + + + Constructor for creating a message received argument. + + + + + + The message received by the queue. + + + + + The approximated amount of messages in the queue. + + + + + Metadata for the queue in the form of name-value pairs. + + + + + Objects of this class represent a queue in a user's storage account. + + + + + The name of the queue. + + + + + The user account this queue lives in. + + + + + This constructor is only called by subclasses. + + + + + Creates a queue in the specified storage account. + + true if a queue with the same name already exists. + true if the queue was successfully created. + + + + Creates a queue in the specified storage account. + + true if the queue was successfully created. + + + + Determines whether a queue with the same name already exists in an account. + + true if a queue with the same name already exists. + + + + Deletes the queue. The queue will be deleted regardless of whether there are messages in the + queue or not. + + true if the queue was successfully deleted. + + + + Sets the properties of a queue. + + The queue's properties to set. + true if the properties were successfully written to the queue. + + + + Retrieves the queue's properties. + + The queue's properties. + + + + Retrieves the approximate number of messages in a queue. + + The approximate number of messages in this queue. + + + + Puts a message in the queue. + + The message to store in the queue. + true if the message has been successfully enqueued. + + + + Puts a message in the queue. + + The message to store in the queue. + The time to live for the message in seconds. + true if the message has been successfully enqueued. + + + + Retrieves a message from the queue. + + The message retrieved or null if the queue is empty. + + + + Retrieves a message and sets its visibility timeout to the specified number of seconds. + + Visibility timeout of the message retrieved in seconds. + + + + + Tries to retrieve the given number of messages. + + Maximum number of messages to retrieve. + The list of messages retrieved. + + + + Tries to retrieve the given number of messages. + + Maximum number of messages to retrieve. + The visibility timeout of the retrieved messages in seconds. + The list of messages retrieved. + + + + Get a message from the queue but do not actually dequeue it. The message will remain visible + for other parties requesting messages. + + The message retrieved or null if there are no messages in the queue. + + + + Tries to get a copy of messages in the queue without actually dequeuing the messages. + The messages will remain visible in the queue. + + Maximum number of message to retrieve. + The list of messages retrieved. + + + + Deletes a message from the queue. + + The message to retrieve with a valid popreceipt. + true if the operation was successful. + + + + Delete all messages in a queue. + + true if all messages were deleted successfully. + + + + The default time interval between polling the queue for messages. + Polling is only enabled if the user has called StartReceiving(). + + + + + Starts the automatic reception of messages. + + true if the operation was successful. + + + + Stop the automatic reception of messages. + + + + + The name of the queue exposed as a public property. + + + + + The account info object this queue lives in -- exposed as an internal property. + + + + + Indicates whether to use/generate path-style or host-style URIs + + + + + The URI of the queue + + + + + The retry policy used for retrying requests; this is the retry policy of the + storage account where this queue was created + + + + + The timeout of requests. + + + + + The poll interval in milliseconds. If not explicitly set, this defaults to + the DefaultPollInterval. + + + + + The event users subscribe to in order to automatically receive messages + from a queue. + + + + + This delegate define the shape of a retry policy. A retry policy will invoke the given + as many times as it wants to in the face of + retriable StorageServerExceptions. + + The action to retry + + + + + Provides definitions for some standard retry policies. + + + + + Policy that does no retries i.e., it just invokes exactly once + + The action to retry + The return value of + + + + Policy that retries a specified number of times with a specified fixed time interval between retries + + The number of times to retry. Should be a non-negative number + The time interval between retries. Use TimeSpan.Zero to specify immediate + retries + + When is 0 and is + TimeSpan.Zero this policy is equivalent to the NoRetry policy + + + + Policy that retries a specified number of times with a randomized exponential backoff scheme + + The number of times to retry. Should be a non-negative number. + The multiplier in the exponential backoff scheme + + For this retry policy, the minimum amount of milliseconds between retries is given by the + StandardMinBackoff constant, and the maximum backoff is predefined by the StandardMaxBackoff constant. + Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff. + + + + Policy that retries a specified number of times with a randomized exponential backoff scheme + + The number of times to retry. Should be a non-negative number + The multiplier in the exponential backoff scheme + The minimum backoff interval + The maximum backoff interval + + For this retry policy, the minimum amount of milliseconds between retries is given by the + minBackoff parameter, and the maximum backoff is predefined by the maxBackoff parameter. + Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff. + + + + Access control for containers + + + + + The properties of a blob. + No member of this class makes a storage service request. + + + + + Construct a new BlobProperties object + + The name of the blob + + + + Name of the blob + + + + + URI of the blob + + + + + Content encoding of the blob if it set, null otherwise. + + + + + Content Type of the blob if it is set, null otherwise. + + + + + Content Language of the blob if it is set, null otherwise. + + + + + The length of the blob content, null otherwise. + + + + + Metadata for the blob in the form of name-value pairs. + + + + + The last modified time for the blob. + + + + + The ETag of the blob. This is an identifier assigned to the blob by the storage service + and is used to distinguish contents of two blobs (or versions of the same blob). + + + + + The properties of a container. + No member of this class makes a storage service request. + + + + + The contents of the Blob in various forms. + + + + + Construct a new BlobContents object from a stream. + + The stream to/from which blob contents are written/read. The + stream should be seekable in order for requests to be retried. + + + + Construct a new BlobContents object from a byte array. + + The byte array to/from which contents are written/read. + + + + Get the contents of a blob as a byte array. + + + + + Get the contents of a blob as a stream. + + + + + Class representing some important table storage constants. + + + + + Internal constant for querying tables. + + + + + Internal constant for querying tables. + + + + + The maximum size of strings per property/column is 64 kB (that is 32k characters.) + Note: This constant is smaller for the development storage table service. + + + + + One character in the standard UTF-16 character presentation is 2 bytes. + Note: This constant is smaller for the development storage table service. + + + + + We want to prevent users from the pitfall of mixing up Utc and local time. + Because of this we add some time to the minimum supported datetime. + As a result, there will be no error condition from the server even + if a user converts the minimum supported date time to a local time and + stores this in a DateTime field. + The local development storage support the SQL range of dates which is narrower than the + one for the table storage service and so we use that value here. + + + + + API entry point for using structured storage. The underlying usage pattern is designed to be + similar to the one used in blob and queue services in this library. + Users create a TableStorage object by calling the static Create() method passing account credential + information to this method. The TableStorage object can then be used to create, delete and list tables. + There are two methods to get DataServiceContext objects that conform to the appropriate security scheme. + The first way is to call the GetDataServiceContext() method on TableStorage objects. The naming is again + chosen to conform to the convention in the other APIs for blob and queue services in this library. + This class can also be used as an adapter pattern. I.e., DataServiceContext objects can be created + independnt from a TableStorage object. Calling the Attach() method will make sure that the appropriate + security signing is used on these objects. This design was chosen to support various usage patterns that + might become necessary for autogenerated code. + + + + + The default retry policy + + + + + Creates a TableStorage service object. This object is the entry point into the table storage API. + + The base URI of the table storage service. + Type of URI scheme used. + The account name. + Base64 encoded version of the key. + + + + + Creates a TableStorage object. + + + + + Infers a list of tables from a DataServiceContext-derived type and makes sure + those tables exist in the given service. The table endpoint information is retrieved from the + standard configuration settings. + + + Tables are inferred by finding all the public properties of type IQueryable<T> in + the provided type, where T is a type with an ID (in the case of table storage, this means it either + has a [DataServiceKey("PartitionKey", "RowKey")] attribute in the class, or derives from + the TableStorageEntity class included in this sample library (which in turn has that attribute). + + + + + Infers a list of tables from a DataServiceContext-derived type and makes sure + those tables exist in the given service. + + The DataServiceContext type from which the tables are inferred. + A configuration string that is used to determine the table storage endpoint. + + + + Infers a list of tables from a DataServiceContext-derived type and makes sure + those tables exist in the given service. + + The type of the DataServiceContext. + An object containing information about the table storage endpoint to be used. + + + + Creates a DataServiceContext object that takes care of implementing the table storage signing process. + + + + + If the adaptor pattern with Attach() shall be used, this function can be used to generate the + table service base Uri depending on the path style syntax. + + + + + If the adaptor pattern with Attach() shall be used, this function can be used to generate the + table service base Uri depending on the path style syntax. + + + + + If DataServiceContext objects are created at different places, this method can be called to configure the + DataServiceContext object to implement the required security scheme. + + + + + Lists all the tables under this service's URL + + + + + Creates a new table in the service + + The name of the table to be created + + + + Tries to create a table with the given name. + The main difference to the CreateTable method is that this function first queries the + table storage service whether the table already exists, before it tries to actually create + the table. The reason is that this + is more lightweight for the table storage service than always trying to create a table that + does already exist. Furthermore, as we expect that applications don't really randomly create + tables, the additional roundtrip that is required for creating the table is necessary only very + rarely. + + The name of the table. + True if the operation was completed successfully. False if the table already exists. + + + + Checks whether a table with the same name already exists. + + The name of the table to check. + True iff the table already exists. + + + + Deletes a table from the service. + + The name of the table to be deleted + + + + Tries to delete the table with the given name. + + The name of the table to delete. + True if the table was successfully deleted. False if the table does not exists. + + + + The retry policy used for retrying requests + + + + + The base URI of the table storage service + + + + + The name of the storage account + + + + + Indicates whether to use/generate path-style or host-style URIs + + + + + The base64 encoded version of the key. + + + + + Checks whether the exception is or contains a DataServiceClientException and extracts the + returned http status code and extended error information. + + The exception from which to extract information + The Http status code for the exception + Extended error information including storage service specific + error code and error message + True if the exception is or contains a DataServiceClientException. + + + + Checks whether the exception is or contains a DataServiceClientException and extracts the + returned http status code. + + The exception from which to extract information + The Http status code for the exception + True if the exception is or contains a DataServiceClientException. + + + + Checks whether the exception is either a DataServiceClientException, a DataServiceQueryException or a + DataServiceRequestException. + + + + + Only certain classes of errors should be retried. This method evaluates an exception + and returns whether this class of exception can be retried. + + The exception to analyze. + The HttpStatusCode retrieved from the exception. + + + + Overload that does not retrun the HttpStatusCode. + + + + + Checks whether the string can be inserted in a table storage table. Throws an exception if + this is not the case. + + + + + + Checks whether the string can be inserted into a table storage table. + + + + + Creates a table with the specified name. + + The name of the table. + + + + The table name. + + + + + This class represents an entity (row) in a table in table storage. + + + + + Creates a TableStorageEntity object. + + + + + Creates a TableStorageEntity object. + + + + + Compares to entities. + + + + + Computes a HashCode for this object. + + + + + The partition key of a table entity. The concatenation of the partition key + and row key must be unique per table. + + + + + The row key of a table entity. + + + + + This class can be used for handling continuation tokens in TableStorage. + + + + + + Objects of this class can be created using this constructor directly or by + calling a factory method on the TableStorageDataServiceContext class + + + + + Objects of this class can be created using this constructor directly or by + calling a factory method on the TableStorageDataServiceContext class + + + + + Normal Execute() on the query without retry. Just maps to _query.Execute(). + + + + + + Calling Execute() on the query with the current retry policy. + + An IEnumerable respresenting the results of the query. + + + + Calling Execute() on the query with the current retry policy. + + The retry policy to be used for this request. + An IEnumerable respresenting the results of the query. + + + + Returns all results of the query and hides the complexity of continuation if + this is desired by a user. Users should be aware that this operation can return + many objects. Uses no retries. + Important: this function does not call Execute immediately. Instead, it calls Execute() on + the query only when the result is enumerated. This is a difference to the normal + Execute() and Execute() with retry method. + + An IEnumerable representing the results of the query. + + + + Returns all results of the query and hides the complexity of continuation if + this is desired by a user. Users should be aware that this operation can return + many objects. This operation also uses the current retry policy. + Important: this function does not call Execute immediately. Instead, it calls Execute() on + the query only when the result is enumerated. This is a difference to the normal + Execute() and Execute() with retry method. + + An IEnumerable representing the results of the query. + + + + Returns all results of the query and hides the complexity of continuation if + this is desired by a user. Users should be aware that this operation can return + many objects. + Important: this function does not call Execute immediately. Instead, it calls Execute() on + the query only when the result is enumerated. This is a difference to the normal + Execute() and Execute() with retry method. + + Determines whether to use retries or not. + An IEnumerable representing the results of the query. + + + + Gets the underlying normal query object. + + + + + The retry policy used for retrying requests + + + + + The table storage-specific DataServiceContext class. It adds functionality for handling + the authentication process required by the table storage service. + + + + + Creates a DataServiceContext object and configures it so that it can be used with the table storage service. + + The root URI of the service. + The account name. + The shared key associated with this service. + + + + Creates a DataServiceContext object and configures it so that it can be used with the table storage service. + + A StorageAccountInfo object containing information about how to access the table storage service. + + + + Creates a DataServiceContext object and configures it so that it can be used with the table storage service. + Information on the table storage endpoint is retrieved by accessing configuration settings in the app config section + of a Web.config or app config file, or by accessing settings in cscfg files. + + + + + Calls the SaveChanges() method and applies retry semantics. + + + + + Calls the SaveChanges() method and applies retry semantics. + + + + + Callback method called whenever a request is sent to the table service. This + is where the signing of the request takes place. + + + + + The retry policy used for retrying requests + + + + + Helper class to avoid long-lived references to context objects + + + Need to be careful not to maintain a reference to the context + object from the auth adapter, since the adapter is probably + long-lived and the context is not. This intermediate helper + class is the one subscribing to context events, so when the + context can be collected then this will be collectable as well. + + + + + The retry policies for blobs and queues deal with special StorageClient and StorageServer exceptions. + In case of tables, we don't want to return these exceptions but instead the normal data service + exception. This class serves as a simple wrapper for these exceptions, and indicates that we + need retries. + Data service exceptions are stored as inner exceptions. + + + + + Creates a TableRetryWrapperException object. + + + + + Creates a TableRetryWrapperException object. + + + + + Creates a TableRetryWrapperException object. + + + + + Creates a TableRetryWrapperException object. + + + + + Creates a TableRetryWrapperException object. + + + + + Objects of this class encapsulate information about a storage account and endpoint configuration. + Associated with a storage account is the account name, the base URI of the account and a shared key. + + + + + The default configuration string in configuration files for setting the queue storage endpoint. + + + + + The default configuration string in configuration files for setting the blob storage endpoint. + + + + + The default configuration string in configuration files for setting the table storage endpoint. + + + + + The default configuration string in configuration files for setting the storage account name. + + + + + The default configuration string in configuration files for setting the shared key associated with a storage account. + + + + + The default configuration string in configuration files for setting the UsePathStyleUris option. + + + + + The default prefix string in application config and Web.config files to indicate that this setting should be looked up + in the fabric's configuration system. + + + + + Constructor for creating account info objects. + + The account's base URI. + If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used. + If false host-style URIs (http://accountname.baseuri/containername/objectname) are used, + where baseuri is the URI of the service.. + If null, the choice is made automatically: path-style URIs if host name part of base URI is an + IP addres, host-style otherwise. + The account name. + The account's shared key. + + + + Constructor for creating account info objects. + + The account's base URI. + If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used. + If false host-style URIs (http://accountname.baseuri/containername/objectname) are used, + where baseuri is the URI of the service. + If null, the choice is made automatically: path-style URIs if host name part of base URI is an + IP addres, host-style otherwise. + The account name. + The account's shared key. + true if it shall be allowed to only set parts of the StorageAccountInfo properties. + + + + Retrieves account settings for the queue service from the default settings. + + + + + Retrieves account settings for the queue service from the default settings. + Throws an exception in case of incomplete settings. + + + + + Retrieves account settings for the table service from the default settings. + + + + + Retrieves account settings for the table service from the default settings. + Throws an exception in case of incomplete settings. + + + + + Retrieves account settings for the blob service from the default settings. + + + + + Retrieves account settings for the blob service from the default settings. + Throws an exception in case of incomplete settings. + + + + + Gets settings from default configuration names except for the endpoint configuration string. + + + + + Gets settings from default configuration names except for the endpoint configuration string. Throws an exception + in the case of incomplete settings. + + + + + Gets a configuration setting from application settings in the Web.config or App.config file. + When running in a hosted environment, configuration settings are read from .cscfg + files. + + + + + Retrieves account information settings from configuration settings. First, the implementation checks for + settings in an application config section of an app.config or Web.config file. These values are overwritten + if the same settings appear in a .csdef file. + The implementation also supports indirect settings. In this case, indirect settings overwrite all other settings. + + Configuration string for the account name. + Configuration string for the key. + Configuration string for the endpoint. + Configuration string for the path style. + If false, an exception is thrown if not all settings are available. + StorageAccountInfo object containing the retrieved settings. + + + + Checks whether all essential properties of this object are set. Only then, the account info object + should be used in ohter APIs of this library. + + + + + + Checks whether this StorageAccountInfo object is complete in the sense that all properties are set. + + + + + The base URI of the account. + + + + + The account name. + + + + + The account's key. + + + + + If set, returns the UsePathStyleUris properties. If the property has not been explicitly set, + the implementation tries to derive the correct value from the base URI. + + + + + Get a reference to a Queue object with a specified name. This method does not make a call to + the queue service. + + The name of the queue + A newly created queue object + + + + Lists all queues with a given prefix within an account. + + + The list of queue names. + + + + Lists the queues within the account. + + A list of queues + + + diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Release/AspProviders.dll b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Release/AspProviders.dll new file mode 100644 index 0000000..467dc56 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Release/AspProviders.dll differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Release/AspProviders.pdb b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Release/AspProviders.pdb new file mode 100644 index 0000000..fb57601 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Release/AspProviders.pdb differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Release/StorageClient.dll b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Release/StorageClient.dll new file mode 100644 index 0000000..8376e42 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Release/StorageClient.dll differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Release/StorageClient.pdb b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Release/StorageClient.pdb new file mode 100644 index 0000000..5f8089a Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/bin/Release/StorageClient.pdb differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Debug/AspProviders.csproj.FileListAbsolute.txt b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Debug/AspProviders.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..db40c9d --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Debug/AspProviders.csproj.FileListAbsolute.txt @@ -0,0 +1,16 @@ +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\bin\Debug\AspProviders.dll +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\bin\Debug\AspProviders.pdb +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\bin\Debug\StorageClient.dll +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\bin\Debug\StorageClient.pdb +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\bin\Debug\StorageClient.xml +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\obj\Debug\ResolveAssemblyReference.cache +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\obj\Debug\AspProviders.dll +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\obj\Debug\AspProviders.pdb +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Debug\AspProviders.dll +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Debug\AspProviders.pdb +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Debug\StorageClient.dll +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Debug\StorageClient.pdb +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Debug\StorageClient.xml +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\obj\Debug\ResolveAssemblyReference.cache +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\obj\Debug\AspProviders.dll +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\obj\Debug\AspProviders.pdb diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Debug/AspProviders.dll b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Debug/AspProviders.dll new file mode 100644 index 0000000..0b14b98 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Debug/AspProviders.dll differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Debug/AspProviders.pdb b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Debug/AspProviders.pdb new file mode 100644 index 0000000..7fa2cbe Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Debug/AspProviders.pdb differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Debug/ResolveAssemblyReference.cache b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Debug/ResolveAssemblyReference.cache new file mode 100644 index 0000000..50e9397 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Debug/ResolveAssemblyReference.cache differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Release/AspProviders.csproj.FileListAbsolute.txt b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Release/AspProviders.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..9e5632d --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Release/AspProviders.csproj.FileListAbsolute.txt @@ -0,0 +1,7 @@ +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Release\AspProviders.dll +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Release\AspProviders.pdb +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Release\StorageClient.dll +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Release\StorageClient.pdb +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\obj\Release\ResolveAssemblyReference.cache +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\obj\Release\AspProviders.dll +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\obj\Release\AspProviders.pdb diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Release/AspProviders.dll b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Release/AspProviders.dll new file mode 100644 index 0000000..467dc56 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Release/AspProviders.dll differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Release/AspProviders.pdb b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Release/AspProviders.pdb new file mode 100644 index 0000000..fb57601 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Release/AspProviders.pdb differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Release/ResolveAssemblyReference.cache b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Release/ResolveAssemblyReference.cache new file mode 100644 index 0000000..03b8949 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/obj/Release/ResolveAssemblyReference.cache differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/Authentication.cs b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/Authentication.cs new file mode 100644 index 0000000..b4eeca5 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/Authentication.cs @@ -0,0 +1,525 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Collections; +using System.Collections.Specialized; +using System.Net; +using System.Web; +using System.Security.Cryptography; +using System.Globalization; +using System.Diagnostics; + + +namespace Microsoft.Samples.ServiceHosting.StorageClient +{ + /// + /// This type represents the different constituent parts that make up a resource Uri in the context of cloud services. + /// + public class ResourceUriComponents + { + /// + /// The account name in the URI. + /// + public string AccountName { get; set; } + + /// + /// This is really the first component (delimited by '/') after the account name. Since it happens to + /// be a container name in the context of all our storage services (containers in blob storage, + /// queues in the queue service and table names in table storage), it's named as ContainerName to make it more + /// readable at the cost of slightly being incorrectly named. + /// + public string ContainerName { get; set; } + + /// + /// The remaining string in the URI. + /// + public string RemainingPart { get; set; } + + /// + /// Construct a ResourceUriComponents object. + /// + /// The account name that should become part of the URI. + /// The container name (container, queue or table name) that should become part of the URI. + /// Remaining part of the URI. + public ResourceUriComponents(string accountName, string containerName, string remainingPart) + { + this.AccountName = accountName; + this.ContainerName = containerName; + this.RemainingPart = remainingPart; + } + + /// + /// Construct a ResourceUriComponents object. + /// + /// The account name that should become part of the URI. + /// The container name (container, queue or table name) that should become part of the URI. + public ResourceUriComponents(string accountName, string containerName) + : this(accountName, containerName, null) + { + } + + /// + /// Construct a ResourceUriComponents object. + /// + /// The account name that should become part of the URI. + public ResourceUriComponents(string accountName) + : this(accountName, null, null) + { + } + + /// + /// Construct a ResourceUriComponents object. + /// + public ResourceUriComponents() + { + } + } + + internal static class MessageCanonicalizer + { + /// + /// An internal class that stores the canonicalized string version of an HTTP request. + /// + private class CanonicalizedString + { + private StringBuilder canonicalizedString = new StringBuilder(); + + /// + /// Property for the canonicalized string. + /// + internal string Value + { + get + { + return canonicalizedString.ToString(); + } + } + + /// + /// Constructor for the class. + /// + /// The first canonicalized element to start the string with. + internal CanonicalizedString(string initialElement) + { + canonicalizedString.Append(initialElement); + } + + /// + /// Append additional canonicalized element to the string. + /// + /// An additional canonicalized element to append to the string. + internal void AppendCanonicalizedElement(string element) + { + canonicalizedString.Append(StorageHttpConstants.ConstChars.Linefeed); + canonicalizedString.Append(element); + } + } + + /// + /// Create a canonicalized string out of HTTP request header contents for signing + /// blob/queue requests with the Shared Authentication scheme. + /// + /// The uri address of the HTTP request. + /// Components of the Uri extracted out of the request. + /// The method of the HTTP request (GET/PUT, etc.). + /// The content type of the HTTP request. + /// The date of the HTTP request. + /// Should contain other headers of the HTTP request. + /// A canonicalized string of the HTTP request. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", + Justification = "Authentication algorithm requires canonicalization by converting to lower case")] + internal static string CanonicalizeHttpRequest( + Uri address, + ResourceUriComponents uriComponents, + string method, + string contentType, + string date, + NameValueCollection headers) + { + // The first element should be the Method of the request. + // I.e. GET, POST, PUT, or HEAD. + CanonicalizedString canonicalizedString = new CanonicalizedString(method); + + // The second element should be the MD5 value. + // This is optional and may be empty. + string httpContentMD5Value = string.Empty; + + // First extract all the content MD5 values from the header. + ArrayList httpContentMD5Values = HttpRequestAccessor.GetHeaderValues(headers, StorageHttpConstants.HeaderNames.ContentMD5); + + // If we only have one, then set it to the value we want to append to the canonicalized string. + if (httpContentMD5Values.Count == 1) + { + httpContentMD5Value = (string)httpContentMD5Values[0]; + } + + canonicalizedString.AppendCanonicalizedElement(httpContentMD5Value); + + // The third element should be the content type. + canonicalizedString.AppendCanonicalizedElement(contentType); + + // The fourth element should be the request date. + // See if there's an storage date header. + // If there's one, then don't use the date header. + ArrayList httpStorageDateValues = HttpRequestAccessor.GetHeaderValues(headers, StorageHttpConstants.HeaderNames.StorageDateTime); + if (httpStorageDateValues.Count > 0) + { + date = null; + } + + canonicalizedString.AppendCanonicalizedElement(date); + + // Look for header names that start with StorageHttpConstants.HeaderNames.PrefixForStorageHeader + // Then sort them in case-insensitive manner. + ArrayList httpStorageHeaderNameArray = new ArrayList(); + foreach (string key in headers.Keys) + { + if (key.ToLowerInvariant().StartsWith(StorageHttpConstants.HeaderNames.PrefixForStorageHeader, StringComparison.Ordinal)) + { + httpStorageHeaderNameArray.Add(key.ToLowerInvariant()); + } + } + + httpStorageHeaderNameArray.Sort(); + + // Now go through each header's values in the sorted order and append them to the canonicalized string. + foreach (string key in httpStorageHeaderNameArray) + { + StringBuilder canonicalizedElement = new StringBuilder(key); + string delimiter = ":"; + ArrayList values = HttpRequestAccessor.GetHeaderValues(headers, key); + + // Go through values, unfold them, and then append them to the canonicalized element string. + foreach (string value in values) + { + // Unfolding is simply removal of CRLF. + string unfoldedValue = value.Replace(StorageHttpConstants.ConstChars.CarriageReturnLinefeed, string.Empty); + + // Append it to the canonicalized element string. + canonicalizedElement.Append(delimiter); + canonicalizedElement.Append(unfoldedValue); + delimiter = ","; + } + + // Now, add this canonicalized element to the canonicalized header string. + canonicalizedString.AppendCanonicalizedElement(canonicalizedElement.ToString()); + } + + // Now we append the canonicalized resource element. + string canonicalizedResource = GetCanonicalizedResource(address, uriComponents); + canonicalizedString.AppendCanonicalizedElement(canonicalizedResource); + + return canonicalizedString.Value; + } + + internal static string GetCanonicalizedResource(Uri address, ResourceUriComponents uriComponents) + { + // Algorithem is as follows + // 1. Start with the empty string ("") + // 2. Append the account name owning the resource preceded by a /. This is not + // the name of the account making the request but the account that owns the + // resource being accessed. + // 3. Append the path part of the un-decoded HTTP Request-URI, up-to but not + // including the query string. + // 4. If the request addresses a particular component of a resource, like?comp= + // metadata then append the sub-resource including question mark (like ?comp= + // metadata) + StringBuilder canonicalizedResource = new StringBuilder(StorageHttpConstants.ConstChars.Slash); + canonicalizedResource.Append(uriComponents.AccountName); + + // Note that AbsolutePath starts with a '/'. + canonicalizedResource.Append(address.AbsolutePath); + + NameValueCollection queryVariables = HttpUtility.ParseQueryString(address.Query); + string compQueryParameterValue = queryVariables[StorageHttpConstants.QueryParams.QueryParamComp]; + if (compQueryParameterValue != null) + { + canonicalizedResource.Append(StorageHttpConstants.ConstChars.QuestionMark); + canonicalizedResource.Append(StorageHttpConstants.QueryParams.QueryParamComp); + canonicalizedResource.Append(StorageHttpConstants.QueryParams.SeparatorForParameterAndValue); + canonicalizedResource.Append(compQueryParameterValue); + } + + return canonicalizedResource.ToString(); + } + + + /// + /// Canonicalize HTTP header contents. + /// + /// An HttpWebRequest object. + /// Components of the Uri extracted out of the request. + /// The canonicalized string of the given HTTP request's header. + internal static string CanonicalizeHttpRequest(HttpWebRequest request, ResourceUriComponents uriComponents) + { + return CanonicalizeHttpRequest( + request.Address, uriComponents, request.Method, request.ContentType, string.Empty, request.Headers); + } + + /// + /// Creates a standard datetime string for the shared key lite authentication scheme. + /// + /// DateTime value to convert to a string in the expected format. + /// + internal static string ConvertDateTimeToHttpString(DateTime dateTime) + { + // On the wire everything should be represented in UTC. This assert will catch invalid callers who + // are violating this rule. + Debug.Assert(dateTime == DateTime.MaxValue || dateTime == DateTime.MinValue || dateTime.Kind == DateTimeKind.Utc); + + // 'R' means rfc1123 date which is what the storage services use for all dates... + // It will be in the following format: + // Sun, 28 Jan 2008 12:11:37 GMT + return dateTime.ToString("R", CultureInfo.InvariantCulture); + } + + private static string AppendStringToCanonicalizedString(StringBuilder canonicalizedString, string stringToAppend) + { + canonicalizedString.Append(StorageHttpConstants.ConstChars.Linefeed); + canonicalizedString.Append(stringToAppend); + return canonicalizedString.ToString(); + } + + internal static string CanonicalizeHttpRequestForSharedKeyLite(HttpWebRequest request, ResourceUriComponents uriComponents, string date) + { + StringBuilder canonicalizedString = new StringBuilder(date); + AppendStringToCanonicalizedString(canonicalizedString, MessageCanonicalizer.GetCanonicalizedResource(request.Address, uriComponents)); + + return canonicalizedString.ToString(); + } + } + + /// + /// Use this class to extract various header values from Http requests. + /// + public static class HttpRequestAccessor + { + /// + /// A helper function for extracting HTTP header values from a NameValueCollection object. + /// + /// A NameValueCollection object that should contain HTTP header name-values pairs. + /// Name of the header that we want to get values of. + /// A array list of values for the header. The values are in the same order as they are stored in the NameValueCollection object. + internal static ArrayList GetHeaderValues(NameValueCollection headers, string headerName) + { + ArrayList arrayOfValues = new ArrayList(); + string[] values = headers.GetValues(headerName); + + if (values != null) + { + foreach (string value in values) + { + // canonization formula requires the string to be left trimmed. + arrayOfValues.Add(value.TrimStart()); + } + } + + return arrayOfValues; + } + + + /// + /// Constructs an URI given all its constituents + /// + /// + /// This is the service endpoint in case of path-style URIs and a host suffix in case of host-style URIs + /// IMPORTANT: This does NOT include the service name or account name + /// + /// Uri constituents + /// Indicates whether to construct a path-style Uri (true) or host-style URI (false) + /// Full uri + public static Uri ConstructResourceUri(Uri endpoint, ResourceUriComponents uriComponents, bool pathStyleUri) + { + return pathStyleUri ? + ConstructPathStyleResourceUri(endpoint, uriComponents) : + ConstructHostStyleResourceUri(endpoint, uriComponents); + } + + /// + /// Constructs a path-style resource URI given all its constituents + /// + private static Uri ConstructPathStyleResourceUri(Uri endpoint, ResourceUriComponents uriComponents) + { + StringBuilder path = new StringBuilder(string.Empty); + if (uriComponents.AccountName != null) + { + path.Append(uriComponents.AccountName); + + if (uriComponents.ContainerName != null) + { + path.Append(StorageHttpConstants.ConstChars.Slash); + path.Append(uriComponents.ContainerName); + + if (uriComponents.RemainingPart != null) + { + path.Append(StorageHttpConstants.ConstChars.Slash); + path.Append(uriComponents.RemainingPart); + } + } + } + + return ConstructUriFromUriAndString(endpoint, path.ToString()); + } + + /// + /// Constructs a host-style resource URI given all its constituents + /// + private static Uri ConstructHostStyleResourceUri(Uri hostSuffix, ResourceUriComponents uriComponents) + { + if (uriComponents.AccountName == null) + { + // When there is no account name, full URI is same as hostSuffix + return hostSuffix; + } + else + { + // accountUri will be something like "http://accountname.hostSuffix/" and then we append + // container name and remaining part if they are present. + Uri accountUri = ConstructHostStyleAccountUri(hostSuffix, uriComponents.AccountName); + StringBuilder path = new StringBuilder(string.Empty); + if (uriComponents.ContainerName != null) + { + path.Append(uriComponents.ContainerName); + + if (uriComponents.RemainingPart != null) + { + path.Append(StorageHttpConstants.ConstChars.Slash); + path.Append(uriComponents.RemainingPart); + } + } + + return ConstructUriFromUriAndString(accountUri, path.ToString()); + } + } + + + /// + /// Given the host suffix part, service name and account name, this method constructs the account Uri + /// + private static Uri ConstructHostStyleAccountUri(Uri hostSuffix, string accountName) + { + // Example: + // Input: serviceEndpoint="http://blob.windows.net/", accountName="youraccount" + // Output: accountUri="http://youraccount.blob.windows.net/" + Uri serviceUri = hostSuffix; + + // serviceUri in our example would be "http://blob.windows.net/" + string accountUriString = string.Format(CultureInfo.InvariantCulture, + "{0}{1}{2}.{3}:{4}/", + serviceUri.Scheme, + Uri.SchemeDelimiter, + accountName, + serviceUri.Host, + serviceUri.Port); + + return new Uri(accountUriString); + } + + private static Uri ConstructUriFromUriAndString( + Uri endpoint, + string path) + { + // This is where we encode the url path to be valid + string encodedPath = HttpUtility.UrlPathEncode(path); + return new Uri(endpoint, encodedPath); + } + } + + /// + /// Objects of this class contain the credentials (name and key) of a storage account. + /// + public class SharedKeyCredentials + { + + /// + /// Create a SharedKeyCredentials object given an account name and a shared key. + /// + public SharedKeyCredentials(string accountName, byte[] key) + { + this.accountName = accountName; + this.key = key; + } + + /// + /// Signs the request appropriately to make it an authenticated request. + /// Note that this method takes the URI components as decoding the URI components requires the knowledge + /// of whether the URI is in path-style or host-style and a host-suffix if it's host-style. + /// + public void SignRequest(HttpWebRequest request, ResourceUriComponents uriComponents) + { + if (request == null) + throw new ArgumentNullException("request"); + string message = MessageCanonicalizer.CanonicalizeHttpRequest(request, uriComponents); + string computedBase64Signature = ComputeMacSha(message); + request.Headers.Add(StorageHttpConstants.HeaderNames.Authorization, + string.Format(CultureInfo.InvariantCulture, + "{0} {1}:{2}", + StorageHttpConstants.AuthenticationSchemeNames.SharedKeyAuthSchemeName, + accountName, + computedBase64Signature)); + } + + /// + /// Signs requests using the SharedKeyLite authentication scheme with is used for the table storage service. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Lite", + Justification = "Name of the authentication scheme in the REST protocol")] + public void SignRequestForSharedKeyLite(HttpWebRequest request, ResourceUriComponents uriComponents) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + // add the date header to the request + string dateString = MessageCanonicalizer.ConvertDateTimeToHttpString(DateTime.UtcNow); + request.Headers.Add(StorageHttpConstants.HeaderNames.StorageDateTime, dateString); + + // compute the signature and add the authentication scheme + string message = MessageCanonicalizer.CanonicalizeHttpRequestForSharedKeyLite(request, uriComponents, dateString); + string computedBase64Signature = ComputeMacSha(message); + request.Headers.Add(StorageHttpConstants.HeaderNames.Authorization, + string.Format(CultureInfo.InvariantCulture, + "{0} {1}:{2}", + StorageHttpConstants.AuthenticationSchemeNames.SharedKeyLiteAuthSchemeName, + accountName, + computedBase64Signature)); + } + + + private string ComputeMacSha(string canonicalizedString) + { + byte[] dataToMAC = System.Text.Encoding.UTF8.GetBytes(canonicalizedString); + + using (HMACSHA256 hmacsha1 = new HMACSHA256(key)) + { + return System.Convert.ToBase64String(hmacsha1.ComputeHash(dataToMAC)); + } + } + + private string accountName; + private byte[] key; + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/BlobStorage.cs b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/BlobStorage.cs new file mode 100644 index 0000000..f9a3660 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/BlobStorage.cs @@ -0,0 +1,924 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Collections.Specialized; +using System.Threading; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Diagnostics; + +[assembly:CLSCompliant(true)] + +// disable the generation of warnings for missing documentation elements for +// public classes/members in this file +#pragma warning disable 1591 + +namespace Microsoft.Samples.ServiceHosting.StorageClient +{ + + /// + /// This delegate define the shape of a retry policy. A retry policy will invoke the given + /// as many times as it wants to in the face of + /// retriable StorageServerExceptions. + /// + /// The action to retry + /// + public delegate void RetryPolicy(Action action); + + #region Blob Storage API + /// + /// The entry point of the blob storage API + /// + public abstract class BlobStorage + { + + /// + /// Factory method for BlobStorage + /// + /// The base URI of the blob storage service + /// If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used. + /// If false host-style URIs (http://accountname.baseuri/containername/objectname) are used, + /// where baseuri is the URI of the service. + /// If null, the choice is made automatically: path-style URIs if host name part of base URI is an + /// IP addres, host-style otherwise. + /// The name of the storage account + /// Authentication key used for signing requests + /// A newly created BlobStorage instance + public static BlobStorage Create( + Uri baseUri, + bool? usePathStyleUris, + string accountName, + string base64Key + ) + { + //We create a StorageAccountInfo and then extract the properties of that object. + //This is because the constructor of StorageAccountInfo does normalization of BaseUri. + StorageAccountInfo accountInfo = new StorageAccountInfo( + baseUri, + usePathStyleUris, + accountName, + base64Key + ); + return new BlobStorageRest( + accountInfo.BaseUri, + accountInfo.UsePathStyleUris, + accountInfo.AccountName, + accountInfo.Base64Key + ); + } + + /// + /// Factory method for BlobStorage + /// + /// Account information + /// A newly created BlobStorage instance + public static BlobStorage Create(StorageAccountInfo accountInfo) + { + return new BlobStorageRest( + accountInfo.BaseUri, + accountInfo.UsePathStyleUris, + accountInfo.AccountName, + accountInfo.Base64Key + ); + } + + + /// + /// Get a reference to a newly created BlobContainer object. + /// This method does not make any calls to the storage service. + /// + /// The name of the container + /// A reference to a newly created BlobContainer object + public abstract BlobContainer GetBlobContainer(string containerName); + + + /// + /// Lists the containers within the account. + /// + /// A list of containers + public abstract IEnumerable ListBlobContainers(); + + /// + /// The time out for each request to the storage service. + /// + public TimeSpan Timeout + { + get; + set; + } + + /// + /// The retry policy used for retrying requests + /// + public RetryPolicy RetryPolicy + { + get; + set; + } + + /// + /// The base URI of the blob storage service + /// + public Uri BaseUri + { + get + { + return this.baseUri; + } + } + + /// + /// The name of the storage account + /// + public string AccountName + { + get + { + return this.accountName; + } + } + + /// + /// Indicates whether to use/generate path-style or host-style URIs + /// + public bool UsePathStyleUris + { + get + { + return this.usePathStyleUris; + } + } + + /// + /// The default timeout + /// + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", + Justification = "TimeSpan is a non-mutable type")] + public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30); + + /// + /// The default retry policy + /// + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", + Justification = "RetryPolicy is a non-mutable type")] + public static readonly RetryPolicy DefaultRetryPolicy = RetryPolicies.NoRetry; + + + internal protected BlobStorage(Uri baseUri, + bool? usePathStyleUris, + string accountName, + string base64Key + ) + { + this.baseUri = baseUri; + this.accountName = accountName; + this.Base64Key = base64Key; + if (usePathStyleUris == null) + this.usePathStyleUris = Utilities.StringIsIPAddress(baseUri.Host); + else + this.usePathStyleUris = usePathStyleUris.Value; + + Timeout = DefaultTimeout; + RetryPolicy = DefaultRetryPolicy; + } + + private bool usePathStyleUris; + private Uri baseUri; + private string accountName; + protected internal string Base64Key + { + get; + set; + } + } + + + /// + /// Provides definitions for some standard retry policies. + /// + public static class RetryPolicies + { + + public static readonly TimeSpan StandardMinBackoff = TimeSpan.FromMilliseconds(100); + public static readonly TimeSpan StandardMaxBackoff = TimeSpan.FromSeconds(30); + private static readonly Random random = new Random(); + + /// + /// Policy that does no retries i.e., it just invokes exactly once + /// + /// The action to retry + /// The return value of + public static void NoRetry(Action action) + { + try + { + action(); + } + catch (TableRetryWrapperException e) + { + throw e.InnerException; + } + } + + /// + /// Policy that retries a specified number of times with a specified fixed time interval between retries + /// + /// The number of times to retry. Should be a non-negative number + /// The time interval between retries. Use TimeSpan.Zero to specify immediate + /// retries + /// + /// When is 0 and is + /// TimeSpan.Zero this policy is equivalent to the NoRetry policy + public static RetryPolicy RetryN(int numberOfRetries, TimeSpan intervalBetweenRetries) + { + return new RetryPolicy((Action action) => + { + RetryNImpl(action, numberOfRetries, intervalBetweenRetries); + } + ); + } + + /// + /// Policy that retries a specified number of times with a randomized exponential backoff scheme + /// + /// The number of times to retry. Should be a non-negative number. + /// The multiplier in the exponential backoff scheme + /// + /// For this retry policy, the minimum amount of milliseconds between retries is given by the + /// StandardMinBackoff constant, and the maximum backoff is predefined by the StandardMaxBackoff constant. + /// Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff. + public static RetryPolicy RetryExponentialN(int numberOfRetries, TimeSpan deltaBackoff) + { + return new RetryPolicy((Action action) => + { + RetryExponentialNImpl(action, numberOfRetries, StandardMinBackoff, StandardMaxBackoff, deltaBackoff); + } + ); + } + + /// + /// Policy that retries a specified number of times with a randomized exponential backoff scheme + /// + /// The number of times to retry. Should be a non-negative number + /// The multiplier in the exponential backoff scheme + /// The minimum backoff interval + /// The maximum backoff interval + /// + /// For this retry policy, the minimum amount of milliseconds between retries is given by the + /// minBackoff parameter, and the maximum backoff is predefined by the maxBackoff parameter. + /// Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff. + public static RetryPolicy RetryExponentialN(int numberOfRetries, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff) + { + if (minBackoff > maxBackoff) + { + throw new ArgumentException("The minimum backoff must not be larger than the maximum backoff period."); + } + if (minBackoff < TimeSpan.Zero) + { + throw new ArgumentException("The minimum backoff period must not be negative."); + } + + return new RetryPolicy((Action action) => + { + RetryExponentialNImpl(action, numberOfRetries, minBackoff, maxBackoff, deltaBackoff); + } + ); + } + + #region private helper methods + + private static void RetryNImpl(Action action, int numberOfRetries, TimeSpan intervalBetweenRetries) + { + do + { + try + { + action(); + break; + } + catch (StorageServerException) + { + if (numberOfRetries == 0) + { + throw; + } + if (intervalBetweenRetries > TimeSpan.Zero) + { + Thread.Sleep(intervalBetweenRetries); + } + } + catch (TableRetryWrapperException e) + { + if (numberOfRetries == 0) + { + throw e.InnerException; + } + if (intervalBetweenRetries > TimeSpan.Zero) + { + Thread.Sleep(intervalBetweenRetries); + } + } + } + while (numberOfRetries-- > 0); + } + + private static void RetryExponentialNImpl(Action action, int numberOfRetries, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff) + { + int totalNumberOfRetries = numberOfRetries; + TimeSpan backoff; + + // sanity check + // this is already checked when creating the retry policy in case other than the standard settings are used + // because this library is available in source code, the standard settings can be changed and thus we + // check again at this point + if (minBackoff > maxBackoff) + { + throw new ArgumentException("The minimum backoff must not be larger than the maximum backoff period."); + } + if (minBackoff < TimeSpan.Zero) + { + throw new ArgumentException("The minimum backoff period must not be negative."); + } + + do + { + try + { + action(); + break; + } + catch (StorageServerException) + { + if (numberOfRetries == 0) + { + throw; + } + backoff = CalculateCurrentBackoff(minBackoff, maxBackoff, deltaBackoff, totalNumberOfRetries - numberOfRetries); + Debug.Assert(backoff >= minBackoff); + Debug.Assert(backoff <= maxBackoff); + if (backoff > TimeSpan.Zero) { + Thread.Sleep(backoff); + } + } + catch (TableRetryWrapperException e) + { + if (numberOfRetries == 0) + { + throw e.InnerException; + } + backoff = CalculateCurrentBackoff(minBackoff, maxBackoff, deltaBackoff, totalNumberOfRetries - numberOfRetries); + Debug.Assert(backoff >= minBackoff); + Debug.Assert(backoff <= maxBackoff); + if (backoff > TimeSpan.Zero) + { + Thread.Sleep(backoff); + } + } + } + while (numberOfRetries-- > 0); + } + + private static TimeSpan CalculateCurrentBackoff(TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff, int curRetry) + { + long backoff; + + if (curRetry > 30) + { + backoff = maxBackoff.Ticks; + } + else + { + try + { + checked + { + // only randomize the multiplier here + // it would be as correct to randomize the whole backoff result + lock (random) + { + backoff = random.Next((1 << curRetry) + 1); + } + // Console.WriteLine("backoff:" + backoff); + // Console.WriteLine("random range: [0, " + ((1 << curRetry) + 1) + "]"); + backoff *= deltaBackoff.Ticks; + backoff += minBackoff.Ticks; + } + } + catch (OverflowException) + { + backoff = maxBackoff.Ticks; + } + if (backoff > maxBackoff.Ticks) + { + backoff = maxBackoff.Ticks; + } + } + Debug.Assert(backoff >= minBackoff.Ticks); + Debug.Assert(backoff <= maxBackoff.Ticks); + return TimeSpan.FromTicks(backoff); + } + + #endregion + } + + /// + /// Access control for containers + /// + public enum ContainerAccessControl + { + Private, + Public + } + + /// + /// The blob container class. + /// Used to access and enumerate blobs in the container. + /// Storage key credentials are needed to access private blobs but not for public blobs. + /// + public abstract class BlobContainer + { + /// + /// Use this constructor to access private blobs. + /// + /// The base Uri for the storage endpoint + /// Name of the storage account + /// Name of the container + internal protected BlobContainer(Uri baseUri, string accountName, string containerName) + : this(baseUri, true, accountName, containerName, DateTime.MinValue) + {} + + /// + /// Use this constructor to access private blobs. + /// + /// The base Uri for the storage endpoint + /// + /// If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used and if false + /// host-style URIs (http://accountname.baseuri/containername/objectname) are used, where baseuri is the + /// URI of the service + /// + /// Name of the storage account + /// Name of the container + /// Date of last modification + internal protected BlobContainer(Uri baseUri, bool usePathStyleUris, string accountName, string containerName, DateTime lastModified) + { + if (!Utilities.IsValidContainerOrQueueName(containerName)) + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The specified container name \"{0}\" is not valid!" + + "Please choose a name that conforms to the naming conventions for containers!", containerName)); + } + this.baseUri = baseUri; + this.usePathStyleUris = usePathStyleUris; + this.accountName = accountName; + this.containerName = containerName; + this.Timeout = BlobStorage.DefaultTimeout; + this.RetryPolicy = BlobStorage.DefaultRetryPolicy; + this.LastModifiedTime = lastModified; + } + + + /// + /// The time out for each request to the storage service. + /// + public TimeSpan Timeout + { + get; + set; + } + + /// + /// The retry policy used for retrying requests + /// + public RetryPolicy RetryPolicy + { + get; + set; + } + + /// + /// The base URI of the blob storage service + /// + public Uri BaseUri + { + get + { + return this.baseUri; + } + } + + /// + /// The name of the storage account + /// + public string AccountName + { + get + { + return this.accountName; + } + } + + /// + /// The name of the blob container. + /// + public string ContainerName + { + get + { + return this.containerName; + } + } + + /// + /// Indicates whether to use/generate path-style or host-style URIs + /// + public bool UsePathStyleUris + { + get + { + return this.usePathStyleUris; + } + } + + /// + /// The URI of the container + /// + public abstract Uri ContainerUri + { + get; + } + + /// + /// The timestamp for last modification of container. + /// + public DateTime LastModifiedTime + { + get; + protected set; + } + + /// + /// Create the container if it does not exist. + /// The container is created with private access control and no metadata. + /// + /// true if the container was created. false if the container already exists + public abstract bool CreateContainer(); + + /// + /// Create the container with the specified metadata and access control if it does not exist + /// + /// The metadata for the container. Can be null to indicate no metadata + /// The access control (public or private) with which to create the container + /// true if the container was created. false if the container already exists + public abstract bool CreateContainer(NameValueCollection metadata, ContainerAccessControl accessControl); + + /// + /// Check if the blob container exists + /// + /// true if the container exists, false otherwise. + public abstract bool DoesContainerExist(); + + /// + /// Get the properties for the container if it exists. + /// + /// The properties for the container if it exists, null otherwise + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", + Justification="The method makes a call to the blob service")] + public abstract ContainerProperties GetContainerProperties(); + + /// + /// Get the access control permissions associated with the container. + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", + Justification = "The method makes a call to the blob service")] + public abstract ContainerAccessControl GetContainerAccessControl(); + + /// + /// Set the access control permissions associated with the container. + /// + /// The permission to set + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", + Justification = "The method makes a call to the blob service")] + public abstract void SetContainerAccessControl(ContainerAccessControl acl); + + /// + /// Deletes the current container. + /// + public abstract bool DeleteContainer(); + + /// + /// Check if the blob container exists + /// + /// Name of the BLOB. + /// true if the blob exists, false otherwise. + public abstract bool DoesBlobExist(string blobName); + + /// + /// Create a new blob or overwrite an existing blob. + /// + /// The properties of the blob + /// The contents of the blob + /// Should this request overwrite an existing blob ? + /// true if the blob was created. false if the blob already exists and was set to false + /// The LastModifiedTime property of is set as a result of this call. + /// This method also has an effect on the ETag values that are managed by the service. + public abstract bool CreateBlob(BlobProperties blobProperties, BlobContents blobContents, bool overwrite); + + /// + /// Updates an existing blob if it has not been modified since the specified time which is typically + /// the last modified time of the blob when you retrieved it. + /// Use this method to implement optimistic concurrency by avoiding clobbering changes to the blob + /// made by another writer. + /// + /// The properties of the blob. This object should be one previously + /// obtained from a call to GetBlob or GetBlobProperties and have its LastModifiedTime property set. + /// The contents of the blob. The contents of the blob should be readable + /// true if the blob was updated. false if the blob has changed since the last time + /// The LastModifiedTime property of is set as a result of this call. + /// This method also has an effect on the ETag values that are managed by the service if the update was + /// successful. + public abstract bool UpdateBlobIfNotModified(BlobProperties blob, BlobContents contents); + + /// + /// Get the blob contents and properties if the blob exists + /// + /// The name of the blob + /// Object in which the contents are returned. + /// This object should contain a writable stream or should be a default constructed object. + /// Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller pieces in case of failure. + /// The properties of the blob if the blob exists. + public abstract BlobProperties GetBlob(string name, BlobContents blobContents, bool transferAsChunks); + + /// + /// Gets the blob contents and properties if the blob has not been modified since the time specified. + /// Use this method if you have cached the contents of a blob and want to avoid retrieving the blob + /// if it has not changed since the last time you retrieved it. + /// + /// The properties of the blob obtained from an earlier call to GetBlob. This + /// parameter is updated by the call if the blob has been modified + /// Contains the stream to which the contents of the blob are written if it has been + /// modified + /// Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller pieces in case of failure. + /// true if the blob has been modified, false otherwise + public abstract bool GetBlobIfModified(BlobProperties blobProperties, BlobContents blobContents, bool transferAsChunks); + + /// + /// Get the properties of the blob if it exists. + /// This method is also the simplest way to check if a blob exists. + /// + /// The name of the blob + /// The properties of the blob if it exists. null otherwise. + /// The properties for the contents of the blob are not set + public abstract BlobProperties GetBlobProperties(string name); + + /// + /// Set the metadata of an existing blob. + /// + /// The blob properties object whose metadata is to be updated + public abstract void UpdateBlobMetadata(BlobProperties blobProperties); + + /// + /// Set the metadata of an existing blob if it has not been modified since it was last retrieved. + /// + /// The blob properties object whose metadata is to be updated. + /// Typically obtained by a previous call to GetBlob or GetBlobProperties + /// true if the blob metadata was updated. false if it was not updated because the blob + /// has been modified + public abstract bool UpdateBlobMetadataIfNotModified(BlobProperties blobProperties); + + /// + /// Delete a blob with the given name + /// + /// The name of the blob + /// true if the blob exists and was successfully deleted, false if the blob does not exist + public abstract bool DeleteBlob(string name); + + /// + /// Delete a blob with the given name if the blob has not been modified since it was last obtained. + /// Use this method for optimistic concurrency to avoid deleting a blob that has been modified since + /// the last time you retrieved it + /// + /// A blob object (typically previously obtained from a GetBlob call) + /// This out parameter is set to true if the blob was not deleted because + /// it was modified + /// true if the blob exists and was successfully deleted, false if the blob does not exist or was + /// not deleted because the blob was modified. + public abstract bool DeleteBlobIfNotModified(BlobProperties blob, out bool modified); + + /// + /// Enumerates all blobs with a given prefix. + /// + /// + /// If true common prefixes with "/" as seperator + /// The list of blob properties and common prefixes + public abstract IEnumerable ListBlobs(string prefix, bool combineCommonPrefixes); + + private Uri baseUri; + private string accountName; + private string containerName; + private bool usePathStyleUris; + } + + /// + /// The properties of a blob. + /// No member of this class makes a storage service request. + /// + public class BlobProperties + { + /// + /// Construct a new BlobProperties object + /// + /// The name of the blob + public BlobProperties(string name) + { + Name = name; + } + + + /// + /// Name of the blob + /// + public string Name { get; internal set; } + + /// + /// URI of the blob + /// + public Uri Uri { get; internal set; } + + /// + /// Content encoding of the blob if it set, null otherwise. + /// + public string ContentEncoding { get; set; } + + /// + /// Content Type of the blob if it is set, null otherwise. + /// + public string ContentType { get; set; } + + /// + /// Content Language of the blob if it is set, null otherwise. + /// + public string ContentLanguage { get; set; } + + /// + /// The length of the blob content, null otherwise. + /// + public long ContentLength { get; internal set; } + + /// + /// Metadata for the blob in the form of name-value pairs. + /// + public NameValueCollection Metadata { get; set;} + + /// + /// The last modified time for the blob. + /// + public DateTime LastModifiedTime { get; internal set; } + + /// + /// The ETag of the blob. This is an identifier assigned to the blob by the storage service + /// and is used to distinguish contents of two blobs (or versions of the same blob). + /// + public string ETag { get; internal set; } + + internal void Assign(BlobProperties other) + { + Name = other.Name; + Uri = other.Uri; + ContentEncoding = other.ContentEncoding; + ContentLanguage = other.ContentLanguage; + ContentLength = other.ContentLength; + ContentType = other.ContentType; + ETag = other.ETag; + LastModifiedTime = other.LastModifiedTime; + Metadata = (other.Metadata != null ? new NameValueCollection(other.Metadata) : null) ; + } + } + + /// + /// The properties of a container. + /// No member of this class makes a storage service request. + /// + public class ContainerProperties + { + public ContainerProperties(string name) + { + Name = name; + } + + public string Name + { + get; + internal set; + } + public DateTime LastModifiedTime + { + get; + internal set; + } + + public string ETag + { + get; + internal set; + } + + public Uri Uri + { + get; + internal set; + } + + public NameValueCollection Metadata + { + get; + internal set; + } + } + + + /// + /// The contents of the Blob in various forms. + /// + public class BlobContents + { + /// + /// Construct a new BlobContents object from a stream. + /// + /// The stream to/from which blob contents are written/read. The + /// stream should be seekable in order for requests to be retried. + public BlobContents(Stream stream) + { + this.stream = stream; + } + + /// + /// Construct a new BlobContents object from a byte array. + /// + /// The byte array to/from which contents are written/read. + public BlobContents(byte[] value) + { + this.bytes = value; + this.stream = new MemoryStream(value, false); + } + + /// + /// Get the contents of a blob as a stream. + /// + public Stream AsStream + { + get + { + return stream; + } + + } + + /// + /// Get the contents of a blob as a byte array. + /// + public byte[] AsBytes() + { + if (bytes != null) + return bytes; + if (stream != null) + { + stream.Seek(0, SeekOrigin.Begin); + bytes = new byte[stream.Length]; + int n = 0; + int offset = 0; + do + { + n = stream.Read(bytes, offset, bytes.Length - offset); + offset += n; + + } while (n > 0); + } + return bytes; + } + + private Stream stream; + private byte[] bytes; + } + #endregion +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/Errors.cs b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/Errors.cs new file mode 100644 index 0000000..24c8fec --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/Errors.cs @@ -0,0 +1,767 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.IO; +using System.Collections.Generic; +using System.Net; +using System.Collections.Specialized; +using System.Runtime.Serialization; +using System.Security.Permissions; +using System.Diagnostics.CodeAnalysis; +using System.Web; +using System.Linq; +using System.Xml; +using System.Xml.Linq; + + +// disable the generation of warnings for missing documentation elements for +// public classes/members in this file +// justification is that this file contains many public constants whose names +// sufficiently reflect their intended usage +#pragma warning disable 1591 + +namespace Microsoft.Samples.ServiceHosting.StorageClient +{ + /// + /// Error codes that can be returned by the storage service or the client library. + /// These are divided into server errors and client errors depending on which side + /// the error can be attributed to. + /// + public enum StorageErrorCode + { + None = 0, + + //Server errors + ServiceInternalError = 1, + ServiceTimeout, + ServiceIntegrityCheckFailed, + TransportError, + ServiceBadResponse, + + //Client errors + ResourceNotFound, + AccountNotFound, + ContainerNotFound, + BlobNotFound, + AuthenticationFailure, + AccessDenied, + ResourceAlreadyExists, + ContainerAlreadyExists, + BlobAlreadyExists, + BadRequest, + ConditionFailed, + BadGateway + } + + [Serializable] + public class StorageExtendedErrorInformation + { + public string ErrorCode { get; internal set; } + public string ErrorMessage { get; internal set; } + public NameValueCollection AdditionalDetails { get; internal set; } + } + + /// + /// The base class for storage service exceptions + /// + [Serializable] + public abstract class StorageException : Exception + { + /// + /// The Http status code returned by the storage service + /// + public HttpStatusCode StatusCode { get; private set; } + + /// + /// The specific error code returned by the storage service + /// + public StorageErrorCode ErrorCode { get; private set; } + + /// + /// + /// + public StorageExtendedErrorInformation ExtendedErrorInformation { get; private set; } + + protected StorageException() + { + } + + protected StorageException( + StorageErrorCode errorCode, + string message, + HttpStatusCode statusCode, + StorageExtendedErrorInformation extendedErrorInfo, + Exception innerException + ) + : base(message, innerException) + { + this.ErrorCode = errorCode; + this.StatusCode = statusCode; + this.ExtendedErrorInformation = extendedErrorInfo; + } + + /// + /// Initializes a new instance of the class with + /// serialized data. + /// + /// The object that contains serialized object + /// data about the exception being thrown + /// The object that contains contextual information + /// about the source or destionation. + protected StorageException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (null == info) + { + throw new ArgumentNullException("info"); + } + + this.StatusCode = (HttpStatusCode)info.GetValue("StatusCode", typeof(HttpStatusCode)); + this.ErrorCode = (StorageErrorCode)info.GetValue("ErrorCode", typeof(StorageErrorCode)); + this.ExtendedErrorInformation = (StorageExtendedErrorInformation)info.GetValue( + "ExtendedErrorInformation", typeof(StorageExtendedErrorInformation)); + } + + /// + /// Sets the object with additional exception information + /// + /// The object that holds the + /// serialized object data. + /// The object that contains contextual information + /// about the source or destionation. + [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (null == info) + { + throw new ArgumentNullException("info"); + } + + info.AddValue("StatusCode", this.StatusCode); + info.AddValue("ErrorCode", this.ErrorCode); + info.AddValue("ExtendedErrorInformation", this.ExtendedErrorInformation); + base.GetObjectData(info, context); + } + + } + + /// + /// Server exceptions are those due to server side problems. + /// These may be transient and requests resulting in such exceptions + /// can be retried with the same parameters. + /// + [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", + Justification = "Since this exception comes from the server, there must be an HTTP response code associated with it, hence we exclude the default constructor taking only a string but no status code.")] + [Serializable] + public class StorageServerException : StorageException + { + internal StorageServerException( + StorageErrorCode errorCode, + string message, + HttpStatusCode statusCode, + Exception innerException + ) + : base(errorCode, message, statusCode, null, innerException) + { + } + + internal StorageServerException( + StorageErrorCode errorCode, + string message, + HttpStatusCode statusCode, + StorageExtendedErrorInformation extendedErrorInfo, + Exception innerException + ) + : base(errorCode, message, statusCode, extendedErrorInfo, innerException) + { + } + + + /// + /// Initializes a new instance of the class with + /// serialized data. + /// + /// The object that contains serialized object + /// data about the exception being thrown + /// The object that contains contextual information + /// about the source or destionation. + protected StorageServerException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + public StorageServerException() + { + } + } + + /// + /// Client side exceptions are due to incorrect parameters to the request. + /// These requests should not be retried with the same parameters + /// + [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", + Justification = "Since this exception comes from the server, there must be an HTTP response code associated with it, hence we exclude the default constructor taking only a string but no status code.")] + [Serializable] + public class StorageClientException : StorageException + { + internal StorageClientException( + StorageErrorCode errorCode, + string message, + HttpStatusCode statusCode, + StorageExtendedErrorInformation extendedErrorInfo, + Exception innerException + ) + : base(errorCode, message, statusCode, extendedErrorInfo, innerException) + { + } + + + /// + /// Initializes a new instance of the class with + /// serialized data. + /// + /// The object that contains serialized object + /// data about the exception being thrown + /// The object that contains contextual information + /// about the source or destionation. + protected StorageClientException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + public StorageClientException() + { + } + + } + + #region Error code strings that can be returned in the StorageExtendedErrorInformation.ErrorCode + /// + /// Error code strings that are common to all storage services + /// + public static class StorageErrorCodeStrings + { + public const string UnsupportedHttpVerb = "UnsupportedHttpVerb"; + public const string MissingContentLengthHeader = "MissingContentLengthHeader"; + public const string MissingRequiredHeader = "MissingRequiredHeader"; + public const string MissingRequiredXmlNode = "MissingRequiredXmlNode"; + public const string UnsupportedHeader = "UnsupportedHeader"; + public const string UnsupportedXmlNode = "UnsupportedXmlNode"; + public const string InvalidHeaderValue = "InvalidHeaderValue"; + public const string InvalidXmlNodeValue = "InvalidXmlNodeValue"; + public const string MissingRequiredQueryParameter = "MissingRequiredQueryParameter"; + public const string UnsupportedQueryParameter = "UnsupportedQueryParameter"; + public const string InvalidQueryParameterValue = "InvalidQueryParameterValue"; + public const string OutOfRangeQueryParameterValue = "OutOfRangeQueryParameterValue"; + public const string InvalidUri = "InvalidUri"; + public const string InvalidHttpVerb = "InvalidHttpVerb"; + public const string EmptyMetadataKey = "EmptyMetadataKey"; + public const string RequestBodyTooLarge = "RequestBodyTooLarge"; + public const string InvalidXmlDocument = "InvalidXmlDocument"; + public const string InternalError = "InternalError"; + public const string AuthenticationFailed = "AuthenticationFailed"; + public const string Md5Mismatch = "Md5Mismatch"; + public const string InvalidMd5 = "InvalidMd5"; + public const string OutOfRangeInput = "OutOfRangeInput"; + public const string InvalidInput = "InvalidInput"; + public const string OperationTimedOut = "OperationTimedOut"; + public const string ResourceNotFound = "ResourceNotFound"; + public const string InvalidMetadata = "InvalidMetadata"; + public const string MetadataTooLarge = "MetadataTooLarge"; + public const string ConditionNotMet = "ConditionNotMet"; + public const string InvalidRange = "InvalidRange"; + public const string ContainerNotFound = "ContainerNotFound"; + public const string ContainerAlreadyExists = "ContainerAlreadyExists"; + public const string ContainerDisabled = "ContainerDisabled"; + public const string ContainerBeingDeleted = "ContainerBeingDeleted"; + public const string ServerBusy = "ServerBusy"; + } + + /// + /// Error code strings that are specific to blob service + /// + public static class BlobErrorCodeStrings + { + public const string InvalidBlockId = "InvalidBlockId"; + public const string BlobNotFound = "BlobNotFound"; + public const string BlobAlreadyExists = "BlobAlreadyExists"; + public const string InvalidBlobOrBlock = "InvalidBlobOrBlock"; + public const string InvalidBlockList = "InvalidBlockList"; + } + + /// + /// Error code strings that are specific to queue service + /// + public static class QueueErrorCodeStrings + { + public const string QueueNotFound = "QueueNotFound"; + public const string QueueDisabled = "QueueDisabled"; + public const string QueueAlreadyExists = "QueueAlreadyExists"; + public const string QueueNotEmpty = "QueueNotEmpty"; + public const string QueueBeingDeleted = "QueueBeingDeleted"; + public const string PopReceiptMismatch = "PopReceiptMismatch"; + public const string InvalidParameter = "InvalidParameter"; + public const string MessageNotFound = "MessageNotFound"; + public const string MessageTooLarge = "MessageTooLarge"; + public const string InvalidMarker = "InvalidMarker"; + } + + /// + /// Error code strings that are specific to queue service + /// + /// public static class TableErrorCodeStrings + public static class TableErrorCodeStrings + { + public const string XMethodNotUsingPost = "XMethodNotUsingPost"; + public const string XMethodIncorrectValue = "XMethodIncorrectValue"; + public const string XMethodIncorrectCount = "XMethodIncorrectCount"; + + public const string TableHasNoProperties = "TableHasNoProperties"; + public const string DuplicatePropertiesSpecified = "DuplicatePropertiesSpecified"; + public const string TableHasNoSuchProperty = "TableHasNoSuchProperty"; + public const string DuplicateKeyPropertySpecified = "DuplicateKeyPropertySpecified"; + public const string TableAlreadyExists = "TableAlreadyExists"; + public const string TableNotFound = "TableNotFound"; + public const string EntityNotFound = "EntityNotFound"; + public const string EntityAlreadyExists = "EntityAlreadyExists"; + public const string PartitionKeyNotSpecified = "PartitionKeyNotSpecified"; + public const string OperatorInvalid = "OperatorInvalid"; + public const string UpdateConditionNotSatisfied = "UpdateConditionNotSatisfied"; + public const string PropertiesNeedValue = "PropertiesNeedValue"; + + public const string PartitionKeyPropertyCannotBeUpdated = "PartitionKeyPropertyCannotBeUpdated"; + public const string TooManyProperties = "TooManyProperties"; + public const string EntityTooLarge = "EntityTooLarge"; + public const string PropertyValueTooLarge = "PropertyValueTooLarge"; + public const string InvalidValueType = "InvalidValueType"; + public const string TableBeingDeleted = "TableBeingDeleted"; + public const string TableServerOutOfMemory = "TableServerOutOfMemory"; + public const string PrimaryKeyPropertyIsInvalidType = "PrimaryKeyPropertyIsInvalidType"; + public const string PropertyNameTooLong = "PropertyNameTooLong"; + public const string PropertyNameInvalid = "PropertyNameInvalid"; + + public const string BatchOperationNotSupported = "BatchOperationNotSupported"; + public const string JsonFormatNotSupported = "JsonFormatNotSupported"; + public const string MethodNotAllowed = "MethodNotAllowed"; + public const string NotImplemented = "NotImplemented"; + } + #endregion + + #region Helper functions dealing with errors + internal static partial class Utilities + { + internal static void ProcessUnexpectedStatusCode(HttpWebResponse response) + { + throw new StorageServerException( + StorageErrorCode.ServiceBadResponse, + response.StatusDescription, + response.StatusCode, + null + ); + } + + internal static Exception TranslateWebException(Exception e) + { + WebException we = e as WebException; + if (null == we) + { + return e; + } + + // If the response is not null, let's first see what the status code is. + if (we.Response != null) + { + HttpWebResponse response = ((HttpWebResponse)we.Response); + + StorageExtendedErrorInformation extendedError = + GetExtendedErrorDetailsFromResponse( + response.GetResponseStream(), + response.ContentLength + ); + Exception translatedException = null; + if (extendedError != null) + { + translatedException = TranslateExtendedError( + extendedError, + response.StatusCode, + response.StatusDescription, + e); + if (translatedException != null) + return translatedException; + } + translatedException = TranslateFromHttpStatus( + response.StatusCode, + response.StatusDescription, + extendedError, + we + ); + if (translatedException != null) + return translatedException; + + } + + switch (we.Status) + { + case WebExceptionStatus.RequestCanceled: + return new StorageServerException( + StorageErrorCode.ServiceTimeout, + "The server request did not complete within the specified timeout", + HttpStatusCode.GatewayTimeout, + we); + + case WebExceptionStatus.ConnectFailure: + return we; + + default: + return new StorageServerException( + StorageErrorCode.ServiceInternalError, + "The server encountered an unknown failure: " + e.Message, + HttpStatusCode.InternalServerError, + we + ); + } + } + + internal static Exception TranslateFromHttpStatus( + HttpStatusCode statusCode, + string statusDescription, + StorageExtendedErrorInformation details, + Exception inner + ) + { + switch (statusCode) + { + case HttpStatusCode.Forbidden: + return new StorageClientException( + StorageErrorCode.AccessDenied, + statusDescription, + HttpStatusCode.Forbidden, + details, + inner + ); + + case HttpStatusCode.Gone: + case HttpStatusCode.NotFound: + return new StorageClientException( + StorageErrorCode.ResourceNotFound, + statusDescription, + statusCode, + details, + inner + ); + + case HttpStatusCode.BadRequest: + return new StorageClientException( + StorageErrorCode.BadRequest, + statusDescription, + statusCode, + details, + inner + ); + + case HttpStatusCode.PreconditionFailed: + case HttpStatusCode.NotModified: + return new StorageClientException( + StorageErrorCode.BadRequest, + statusDescription, + statusCode, + details, + inner); + + case HttpStatusCode.Conflict: + return new StorageClientException( + StorageErrorCode.ResourceAlreadyExists, + statusDescription, + statusCode, + details, + inner + ); + + case HttpStatusCode.GatewayTimeout: + return new StorageServerException( + StorageErrorCode.ServiceTimeout, + statusDescription, + statusCode, + details, + inner + ); + + case HttpStatusCode.RequestedRangeNotSatisfiable: + return new StorageClientException( + StorageErrorCode.BadRequest, + statusDescription, + statusCode, + details, + inner + ); + + case HttpStatusCode.InternalServerError: + return new StorageServerException( + StorageErrorCode.ServiceInternalError, + statusDescription, + statusCode, + details, + inner + ); + + case HttpStatusCode.BadGateway: + return new StorageServerException( + StorageErrorCode.BadGateway, + statusDescription, + statusCode, + details, + inner + ); + } + return null; + } + + private static Exception TranslateExtendedError( + StorageExtendedErrorInformation details, + HttpStatusCode statusCode, + string statusDescription, + Exception inner + ) + { + StorageErrorCode errorCode = default(StorageErrorCode); + switch (details.ErrorCode) + { + case StorageErrorCodeStrings.UnsupportedHttpVerb: + case StorageErrorCodeStrings.MissingContentLengthHeader: + case StorageErrorCodeStrings.MissingRequiredHeader: + case StorageErrorCodeStrings.UnsupportedHeader: + case StorageErrorCodeStrings.InvalidHeaderValue: + case StorageErrorCodeStrings.MissingRequiredQueryParameter: + case StorageErrorCodeStrings.UnsupportedQueryParameter: + case StorageErrorCodeStrings.InvalidQueryParameterValue: + case StorageErrorCodeStrings.OutOfRangeQueryParameterValue: + case StorageErrorCodeStrings.InvalidUri: + case StorageErrorCodeStrings.InvalidHttpVerb: + case StorageErrorCodeStrings.EmptyMetadataKey: + case StorageErrorCodeStrings.RequestBodyTooLarge: + case StorageErrorCodeStrings.InvalidXmlDocument: + case StorageErrorCodeStrings.InvalidXmlNodeValue: + case StorageErrorCodeStrings.MissingRequiredXmlNode: + case StorageErrorCodeStrings.InvalidMd5: + case StorageErrorCodeStrings.OutOfRangeInput: + case StorageErrorCodeStrings.InvalidInput: + case StorageErrorCodeStrings.InvalidMetadata: + case StorageErrorCodeStrings.MetadataTooLarge: + case StorageErrorCodeStrings.InvalidRange: + errorCode = StorageErrorCode.BadRequest; + break; + case StorageErrorCodeStrings.AuthenticationFailed: + errorCode = StorageErrorCode.AuthenticationFailure; + break; + case StorageErrorCodeStrings.ResourceNotFound: + errorCode = StorageErrorCode.ResourceNotFound; + break; + case StorageErrorCodeStrings.ConditionNotMet: + errorCode = StorageErrorCode.ConditionFailed; + break; + case StorageErrorCodeStrings.ContainerAlreadyExists: + errorCode = StorageErrorCode.ContainerAlreadyExists; + break; + case StorageErrorCodeStrings.ContainerNotFound: + errorCode = StorageErrorCode.ContainerNotFound; + break; + case BlobErrorCodeStrings.BlobNotFound: + errorCode = StorageErrorCode.BlobNotFound; + break; + case BlobErrorCodeStrings.BlobAlreadyExists: + errorCode = StorageErrorCode.BlobAlreadyExists; + break; + } + + if (errorCode != default(StorageErrorCode)) + return new StorageClientException( + errorCode, + statusDescription, + statusCode, + details, + inner + ); + + switch (details.ErrorCode) + { + case StorageErrorCodeStrings.InternalError: + case StorageErrorCodeStrings.ServerBusy: + errorCode = StorageErrorCode.ServiceInternalError; + break; + case StorageErrorCodeStrings.Md5Mismatch: + errorCode = StorageErrorCode.ServiceIntegrityCheckFailed; + break; + case StorageErrorCodeStrings.OperationTimedOut: + errorCode = StorageErrorCode.ServiceTimeout; + break; + } + if (errorCode != default(StorageErrorCode)) + return new StorageServerException( + errorCode, + statusDescription, + statusCode, + details, + inner + ); + + + + return null; + } + + + // This is the limit where we allow for the error message returned by the server. + // Message longer than that will be truncated. + private const int ErrorTextSizeLimit = 8 * 1024; + + private static StorageExtendedErrorInformation GetExtendedErrorDetailsFromResponse( + Stream httpResponseStream, + long contentLength + ) + { + try + { + int bytesToRead = (int)Math.Max((long)contentLength, (long)ErrorTextSizeLimit); + byte[] responseBuffer = new byte[bytesToRead]; + int bytesRead = CopyStreamToBuffer(httpResponseStream, responseBuffer, (int)bytesToRead); + return GetErrorDetailsFromStream( + new MemoryStream(responseBuffer, 0, bytesRead, false) + ); + } + catch (WebException) + { + //Ignore network errors when reading error details. + return null; + } + catch (IOException) + { + return null; + } + catch (TimeoutException) + { + return null; + } + } + + private static StorageExtendedErrorInformation GetErrorDetailsFromStream( + Stream inputStream + ) + { + StorageExtendedErrorInformation extendedError = new StorageExtendedErrorInformation(); + try + { + using (XmlReader reader = XmlReader.Create(inputStream)) + { + reader.Read(); + reader.ReadStartElement(StorageHttpConstants.XmlElementNames.ErrorRootElement); + extendedError.ErrorCode = reader.ReadElementString(StorageHttpConstants.XmlElementNames.ErrorCode); + extendedError.ErrorMessage = reader.ReadElementString(StorageHttpConstants.XmlElementNames.ErrorMessage); + extendedError.AdditionalDetails = new NameValueCollection(); + + // After error code and message we can have a number of additional details optionally followed + // by ExceptionDetails element - we'll read all of these into the additionalDetails collection + do + { + if (reader.IsStartElement()) + { + if (string.Compare(reader.LocalName, StorageHttpConstants.XmlElementNames.ErrorException, StringComparison.Ordinal) == 0) + { + // Need to read exception details - we have message and stack trace + reader.ReadStartElement(StorageHttpConstants.XmlElementNames.ErrorException); + extendedError.AdditionalDetails.Add(StorageHttpConstants.XmlElementNames.ErrorExceptionMessage, + reader.ReadElementString(StorageHttpConstants.XmlElementNames.ErrorExceptionMessage)); + extendedError.AdditionalDetails.Add(StorageHttpConstants.XmlElementNames.ErrorExceptionStackTrace, + reader.ReadElementString(StorageHttpConstants.XmlElementNames.ErrorExceptionStackTrace)); + reader.ReadEndElement(); + } + else + { + string elementName = reader.LocalName; + extendedError.AdditionalDetails.Add(elementName, reader.ReadString()); + } + } + } + while (reader.Read()); + } + } + catch (XmlException) + { + //If there is a parsing error we cannot return extended error information + return null; + } + return extendedError; + } + + internal static StorageExtendedErrorInformation GetExtendedErrorFromXmlMessage(string xmlErrorMessage) + { + string message = null; + string errorCode = null; + + XName xnErrorCode = XName.Get(StorageHttpConstants.XmlElementNames.TableErrorCodeElement, + StorageHttpConstants.XmlElementNames.DataWebMetadataNamespace); + XName xnMessage = XName.Get(StorageHttpConstants.XmlElementNames.TableErrorMessageElement, + StorageHttpConstants.XmlElementNames.DataWebMetadataNamespace); + + using (StringReader reader = new StringReader(xmlErrorMessage)) + { + XDocument xDocument = null; + try + { + xDocument = XDocument.Load(reader); + } + catch (XmlException) + { + // The XML could not be parsed. This could happen either because the connection + // could not be made to the server, or if the response did not contain the + // error details (for example, if the response status code was neither a failure + // nor a success, but a 3XX code such as NotModified. + return null; + } + + XElement errorCodeElement = + xDocument.Descendants(xnErrorCode).FirstOrDefault(); + + if (errorCodeElement == null) + return null; + + errorCode = errorCodeElement.Value; + + XElement messageElement = + xDocument.Descendants(xnMessage).FirstOrDefault(); + + if (messageElement != null) + { + message = messageElement.Value; + } + + } + + StorageExtendedErrorInformation errorDetails = new StorageExtendedErrorInformation(); + errorDetails.ErrorMessage = message; + errorDetails.ErrorCode = errorCode; + return errorDetails; + } + } + + + #endregion +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/Queue.cs b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/Queue.cs new file mode 100644 index 0000000..04029bb --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/Queue.cs @@ -0,0 +1,705 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Configuration; +using System.Collections.Specialized; +using System.Xml; +using System.IO; +using System.Globalization; +using System.Diagnostics.CodeAnalysis; + + +// disable the generation of warnings for missing documentation elements for +// public classes/members in this file +#pragma warning disable 1591 + + +namespace Microsoft.Samples.ServiceHosting.StorageClient +{ + + /// + /// The entry point of the queue storage API + /// + public abstract class QueueStorage + { + /// + /// Factory method for QueueStorage + /// + /// The base URI of the queue service + /// If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used. + /// If false host-style URIs (http://accountname.baseuri/containername/objectname) are used, + /// where baseuri is the URI of the service. + /// If null, the choice is made automatically: path-style URIs if host name part of base URI is an + /// IP addres, host-style otherwise. + /// The name of the storage account + /// Authentication key used for signing requests + /// A newly created QueueStorage instance + public static QueueStorage Create(Uri baseUri, bool? usePathStyleUris, string accountName, string base64Key) + { + return new QueueStorageRest(new StorageAccountInfo(baseUri, usePathStyleUris, accountName, base64Key),null); + } + + public static QueueStorage Create(StorageAccountInfo accountInfo) + { + return new QueueStorageRest(accountInfo, null); + } + + + public static QueueStorage Create(StorageAccountInfo accountInfo,string version) + { + return new QueueStorageRest(accountInfo,version); + } + + /// + /// Get a reference to a Queue object with a specified name. This method does not make a call to + /// the queue service. + /// + /// The name of the queue + /// A newly created queue object + public abstract MessageQueue GetQueue(string queueName); + + + /// + /// Lists the queues within the account. + /// + /// A list of queues + public virtual IEnumerable ListQueues() + { + return ListQueues(null); + } + + /// + /// Lists the queues within the account that start with the given prefix. + /// + /// If prefix is null returns all queues. + /// A list of queues. + public abstract IEnumerable ListQueues(string prefix); + + /// + /// The time out for each request to the storage service. + /// + public TimeSpan Timeout + { + get; + set; + } + + /// + /// The retry policy used for retrying requests + /// + public RetryPolicy RetryPolicy + { + get; + set; + } + + /// + /// The base URI of the blob storage service + /// + public Uri BaseUri + { + get + { + return AccountInfo.BaseUri; + } + } + + /// + /// The name of the storage account + /// + public string AccountName + { + get + { + return AccountInfo.AccountName; + } + } + + /// + /// Indicates whether to use/generate path-style or host-style URIs + /// + public bool UsePathStyleUris + { + get + { + return AccountInfo.UsePathStyleUris; + } + } + + /// + /// The default timeout + /// + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", + Justification = "TimeSpan is a non-mutable type")] + public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30); + + + /// + /// The default retry policy + /// + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", + Justification = "RetryPolicy is a non-mutable type")] + public static readonly RetryPolicy DefaultRetryPolicy = RetryPolicies.NoRetry; + + internal protected QueueStorage(StorageAccountInfo accountInfo,string version) + { + this.AccountInfo = accountInfo; + Timeout = DefaultTimeout; + RetryPolicy = DefaultRetryPolicy; + Version = version; + } + + protected QueueStorage(QueueStorage other) + { + this.AccountInfo = other.AccountInfo; + this.Timeout = other.Timeout; + this.RetryPolicy = other.RetryPolicy; + this.Version = other.Version; + } + + internal protected StorageAccountInfo AccountInfo { get; set; } + internal protected SharedKeyCredentials Credentials { get; set; } + internal protected string Version { get; set; } + } + + /// + /// Objects of this class represent a single message in the queue. + /// + public class Message + { + + /// + /// The maximum message size in bytes. + /// + public static readonly int MaxMessageSize = 8 * 1024; + + /// + /// The maximum amount of time a message is kept in the queue. Max value is 7 days. + /// Value is given in seconds. + /// + public static readonly int MaxTimeToLive = 7 * 24 * 60 * 60; + + /// + /// This constructor is not publicly exposed. + /// + internal Message() + { + } + + /// + /// Creates a message and initializes the content of the message to be the specified string. + /// + /// A string representing the contents of the message. + public Message(string content) + { + if (content == null) + { + throw new ArgumentNullException("content"); + } + this.content = Encoding.UTF8.GetBytes(content); + } + + /// + /// Creates a message and given the specified byte contents. + /// In this implementation, regardless of whether an XML or binary data is passed into this + /// function, message contents are converted to base64 before passing the data to the queue service. + /// When calculating the size of the message, the size of the base64 encoding is thus the important + /// parameter. + /// + /// + public Message(byte[] content) + { + if (content == null) + { + throw new ArgumentNullException("content"); + } + if (Convert.ToBase64String(content).Length > MaxMessageSize) + { + throw new ArgumentException("Message body is too big!"); + } + this.content = content; + } + + + /// + /// A unique ID of the message as returned from queue operations. + /// + public string Id + { + get; + internal set; + } + + /// + /// When a message is retrieved from a queue, a PopReceipt is returned. The PopReceipt is used when + /// deleting a message from the queue. + /// + public string PopReceipt + { + get; + internal set; + } + + /// + /// The point in time when the message was put into the queue. + /// + public DateTime InsertionTime + { + get; + internal set; + } + + /// + /// A message's expiration time. + /// + public DateTime ExpirationTime + { + get; + internal set; + } + + /// + /// The point in time when a message becomes visible again after a Get() operation was called + /// that returned the message. + /// + public DateTime TimeNextVisible + { + get; + internal set; + } + + /// + /// Returns the the contents of the message as a string. + /// + public string ContentAsString() + { + return Encoding.UTF8.GetString(this.content); + } + + /// + /// Returns the content of the message as a byte array + /// + public byte[] ContentAsBytes() + { + return content; + } + + /// + /// When calling the Get() operation on a queue, the content of messages + /// returned in the REST protocol are represented as Base64-encoded strings. + /// This internal function transforms the Base64 representation into a byte array. + /// + /// The Base64-encoded string. + internal void SetContentFromBase64String(string str) { + if (str == null || str == string.Empty) + { + // we got a message with an empty element + this.content = Encoding.UTF8.GetBytes(string.Empty); + } + else + { + this.content = Convert.FromBase64String(str); + } + } + + /// + /// Internal method used for creating the XML that becomes part of a REST request + /// + internal byte[] GetContentXMLRepresentation(out int length) + { + length = 0; + byte[] ret = null; + StringBuilder builder = new StringBuilder(); + XmlWriterSettings settings = new XmlWriterSettings(); + settings.OmitXmlDeclaration = true; + System.Text.UTF8Encoding enc = new UTF8Encoding(false); + using (XmlWriter writer = XmlWriter.Create(builder, settings)) + { + writer.WriteStartDocument(); + writer.WriteStartElement(StorageHttpConstants.XmlElementNames.QueueMessage); + writer.WriteStartElement(StorageHttpConstants.XmlElementNames.MessageText); + writer.WriteRaw(Convert.ToBase64String(content)); + writer.WriteEndElement(); + writer.WriteEndElement(); + writer.WriteEndDocument(); + writer.Flush(); + } + ret = enc.GetBytes(builder.ToString()); + if (ret != null) + { + length = ret.Length; + } + return ret; + } + + private byte[] content; + } + + /// + /// Queues in the storage client library expose a functionality for listening for incoming messages. + /// If a message is put into a queue, a corresponding event is issued and this delegate is called. This functionality + /// is implemented internally in this library by periodically polling for incoming messages. + /// + /// The queue that has received a new event. + /// The event argument containing the message. + public delegate void MessageReceivedEventHandler(object sender, MessageReceivedEventArgs e); + + /// + /// The argument class for the MessageReceived event. + /// + public class MessageReceivedEventArgs : EventArgs + { + /// + /// The message itself. + /// + private Message _msg; + + /// + /// Constructor for creating a message received argument. + /// + /// + public MessageReceivedEventArgs(Message msg) + { + if (msg == null) + { + throw new ArgumentNullException("msg"); + } + _msg = msg; + } + + /// + /// The message received by the queue. + /// + public Message Message + { + get + { + return _msg; + } + set + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + _msg = value; + } + } + } + + public class QueueProperties + { + /// + /// The approximated amount of messages in the queue. + /// + public int ApproximateMessageCount + { + get; + internal set; + } + + /// + /// Metadata for the queue in the form of name-value pairs. + /// + public NameValueCollection Metadata + { + get; + set; + } + } + + /// + /// Objects of this class represent a queue in a user's storage account. + /// + public abstract class MessageQueue + { + + /// + /// The name of the queue. + /// + private string _name; + + /// + /// The user account this queue lives in. + /// + private StorageAccountInfo _account; + + + /// + /// This constructor is only called by subclasses. + /// + internal protected MessageQueue() + { + // queues are generated using factory methods + } + + internal protected MessageQueue(string name, StorageAccountInfo account) + { + if (string.IsNullOrEmpty(name)) + { + throw new ArgumentException("Queue name cannot be null or empty!"); + } + if (account == null) + { + throw new ArgumentNullException("account"); + } + if (!Utilities.IsValidContainerOrQueueName(name)) + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The specified queue name \"{0}\" is not valid!" + + "Please choose a name that conforms to the naming conventions for queues!", name)); + } + _name = name; + _account = account; + } + + /// + /// The name of the queue exposed as a public property. + /// + public string Name + { + get + { + return _name; + } + } + + /// + /// The account info object this queue lives in -- exposed as an internal property. + /// + internal StorageAccountInfo AccountInfo + { + get { + return _account; + } + } + + /// + /// Indicates whether to use/generate path-style or host-style URIs + /// + public bool UsePathStyleUris + { + get + { + return _account.UsePathStyleUris; + } + } + + /// + /// The URI of the queue + /// + public abstract Uri QueueUri + { + get; + } + + /// + /// The retry policy used for retrying requests; this is the retry policy of the + /// storage account where this queue was created + /// + public RetryPolicy RetryPolicy + { + get; + set; + } + + /// + /// The timeout of requests. + /// + public TimeSpan Timeout + { + get; + set; + } + + /// + /// Creates a queue in the specified storage account. + /// + /// true if a queue with the same name already exists. + /// true if the queue was successfully created. + public abstract bool CreateQueue(out bool queueAlreadyExists); + + /// + /// Creates a queue in the specified storage account. + /// + /// true if the queue was successfully created. + public virtual bool CreateQueue() + { + bool ignore; + return CreateQueue(out ignore); + } + + + /// + /// Determines whether a queue with the same name already exists in an account. + /// + /// true if a queue with the same name already exists. + public virtual bool DoesQueueExist() + { + try + { + this.GetProperties(); + return true; + } + catch (StorageClientException e) + { + if (e.ErrorCode == StorageErrorCode.ResourceNotFound) + return false; + throw; + } + } + + /// + /// Deletes the queue. The queue will be deleted regardless of whether there are messages in the + /// queue or not. + /// + /// true if the queue was successfully deleted. + public abstract bool DeleteQueue(); + + /// + /// Sets the properties of a queue. + /// + /// The queue's properties to set. + /// true if the properties were successfully written to the queue. + public abstract bool SetProperties(QueueProperties properties); + + /// + /// Retrieves the queue's properties. + /// + /// The queue's properties. + public abstract QueueProperties GetProperties(); + + /// + /// Retrieves the approximate number of messages in a queue. + /// + /// The approximate number of messages in this queue. + public abstract int ApproximateCount(); + + /// + /// Puts a message in the queue. + /// + /// The message to store in the queue. + /// true if the message has been successfully enqueued. + public abstract bool PutMessage(Message msg); + + /// + /// Puts a message in the queue. + /// + /// The message to store in the queue. + /// The time to live for the message in seconds. + /// true if the message has been successfully enqueued. + public abstract bool PutMessage(Message msg, int timeToLiveInSeconds); + + /// + /// Retrieves a message from the queue. + /// + /// The message retrieved or null if the queue is empty. + public abstract Message GetMessage(); + + /// + /// Retrieves a message and sets its visibility timeout to the specified number of seconds. + /// + /// Visibility timeout of the message retrieved in seconds. + /// + public abstract Message GetMessage(int visibilityTimeoutInSeconds); + + /// + /// Tries to retrieve the given number of messages. + /// + /// Maximum number of messages to retrieve. + /// The list of messages retrieved. + public abstract IEnumerable GetMessages(int numberOfMessages); + + /// + /// Tries to retrieve the given number of messages. + /// + /// Maximum number of messages to retrieve. + /// The visibility timeout of the retrieved messages in seconds. + /// The list of messages retrieved. + public abstract IEnumerable GetMessages(int numberOfMessages, int visibilityTimeoutInSeconds); + + /// + /// Get a message from the queue but do not actually dequeue it. The message will remain visible + /// for other parties requesting messages. + /// + /// The message retrieved or null if there are no messages in the queue. + public abstract Message PeekMessage(); + + /// + /// Tries to get a copy of messages in the queue without actually dequeuing the messages. + /// The messages will remain visible in the queue. + /// + /// Maximum number of message to retrieve. + /// The list of messages retrieved. + public abstract IEnumerable PeekMessages(int numberOfMessages); + + /// + /// Deletes a message from the queue. + /// + /// The message to retrieve with a valid popreceipt. + /// true if the operation was successful. + public abstract bool DeleteMessage(Message msg); + + /// + /// Delete all messages in a queue. + /// + /// true if all messages were deleted successfully. + public abstract bool Clear(); + + /// + /// The default time interval between polling the queue for messages. + /// Polling is only enabled if the user has called StartReceiving(). + /// + public static readonly int DefaultPollInterval = 5000; + + /// + /// The poll interval in milliseconds. If not explicitly set, this defaults to + /// the DefaultPollInterval. + /// + public abstract int PollInterval + { + get; + set; + } + + /// + /// Starts the automatic reception of messages. + /// + /// true if the operation was successful. + public abstract bool StartReceiving(); + + /// + /// Stop the automatic reception of messages. + /// + public abstract void StopReceiving(); + + /// + /// The event users subscribe to in order to automatically receive messages + /// from a queue. + /// + public abstract event MessageReceivedEventHandler MessageReceived; + + } + +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/RestBlobStorage.cs b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/RestBlobStorage.cs new file mode 100644 index 0000000..9fb5402 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/RestBlobStorage.cs @@ -0,0 +1,1684 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.IO; +using System.Net; +using System.Text; +using System.Xml; +using Microsoft.Samples.ServiceHosting.StorageClient.StorageHttpConstants; + +namespace Microsoft.Samples.ServiceHosting.StorageClient +{ + using System.Reflection; + + internal class BlobStorageRest : BlobStorage + { + private SharedKeyCredentials credentials; + internal BlobStorageRest(Uri baseUri, + bool? usePathStyleUris, + string accountName, + string base64Key + ) + : base(baseUri, usePathStyleUris, accountName, base64Key) + { + byte[] key = null; + this.Base64Key = base64Key; + if (base64Key != null) + key = Convert.FromBase64String(base64Key); + this.credentials = new SharedKeyCredentials(accountName, key); + } + + /// + /// Get a reference to a BlobContainer object with the given name. + /// This method does not make any calls to the storage service. + /// + /// The name of the container + /// A reference to a newly created BlobContainer object + public override BlobContainer GetBlobContainer(string containerName) + { + return new BlobContainerRest( + BaseUri, + UsePathStyleUris, + AccountName, + containerName, + Base64Key, + DateTime.MinValue, + Timeout, + RetryPolicy + ); + } + + /// + /// Lists the containers within the account. + /// + /// A list of containers + public override IEnumerable ListBlobContainers() + { + string marker = "", prefix = null; + const int maxResults = StorageHttpConstants.ListingConstants.MaxContainerListResults; + byte[] key = null; + if (Base64Key != null) + key = Convert.FromBase64String(Base64Key); + SharedKeyCredentials credentials = new SharedKeyCredentials(AccountName, key); + + + do + { + ListContainersResult result = ListContainersImpl(prefix, marker, maxResults); + marker = result.NextMarker; + foreach (ContainerProperties container in result.Containers) + { + yield return new BlobContainerRest( + BaseUri, + UsePathStyleUris, + AccountName, + container.Name, + Base64Key, + container.LastModifiedTime, + Timeout, + RetryPolicy + ); + } + } while (marker != null); + + } + + internal class ListContainersResult + { + internal ListContainersResult(IEnumerable containers, string nextMarker) + { + this.Containers = containers; + this.NextMarker = nextMarker; + } + + public IEnumerable Containers { get; private set; } + + public string NextMarker { get; private set; } + + } + + private ListContainersResult ListContainersImpl( + string prefix, + string marker, + int maxResults + ) + { + ListContainersResult result = null; + ResourceUriComponents uriComponents; + Uri listContainerssUri = CreateRequestUriForListContainers( + prefix, + marker, + null, + maxResults, + out uriComponents + ); + HttpWebRequest request = Utilities.CreateHttpRequest(listContainerssUri, StorageHttpConstants.HttpMethod.Get, Timeout); + credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.OK) + { + using (Stream stream = response.GetResponseStream()) + { + result = ListContainersResultFromResponse(stream); + } + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + } + } + } + catch (IOException ioe) + { + throw new StorageServerException( + StorageErrorCode.TransportError, + "The connection may be lost", + default(HttpStatusCode), + null, + ioe + ); + } + catch (System.TimeoutException te) + { + throw new StorageServerException( + StorageErrorCode.ServiceTimeout, + "Timeout during listing containers", + HttpStatusCode.RequestTimeout, + null, + te + ); + } + catch (WebException we) + { + throw Utilities.TranslateWebException(we); + } + return result; + } + + private Uri CreateRequestUriForListContainers( + string prefix, string fromMarker, string delimiter, int maxResults, out ResourceUriComponents uriComponents) + { + NameValueCollection queryParams = BlobStorageRest.CreateRequestUriForListing(prefix, fromMarker, delimiter, maxResults); + + return Utilities.CreateRequestUri(BaseUri, UsePathStyleUris, AccountName, null, null, Timeout, queryParams, out uriComponents); + } + + internal static NameValueCollection CreateRequestUriForListing(string prefix, string fromMarker, string delimiter, int maxResults) + { + NameValueCollection queryParams = new NameValueCollection(); + queryParams.Add(StorageHttpConstants.QueryParams.QueryParamComp, StorageHttpConstants.CompConstants.List); + + if (!string.IsNullOrEmpty(prefix)) + queryParams.Add(StorageHttpConstants.QueryParams.QueryParamPrefix, prefix); + + if (!string.IsNullOrEmpty(fromMarker)) + queryParams.Add(StorageHttpConstants.QueryParams.QueryParamMarker, fromMarker); + + if (!string.IsNullOrEmpty(delimiter)) + queryParams.Add(StorageHttpConstants.QueryParams.QueryParamDelimiter, delimiter); + + queryParams.Add(StorageHttpConstants.QueryParams.QueryParamMaxResults, + maxResults.ToString(CultureInfo.InvariantCulture)); + + return queryParams; + } + + private static ListContainersResult ListContainersResultFromResponse(Stream responseBody) + { + List containers = new List(); + string nextMarker = null; + + XmlDocument doc = new XmlDocument(); + try + { + doc.Load(responseBody); + } + catch (XmlException xe) + { + throw new StorageServerException(StorageErrorCode.ServiceBadResponse, + "The result of a ListBlobContainers operation could not be parsed", default(HttpStatusCode), xe); + } + + + // Get all the containers returned as the listing results + XmlNodeList containerNodes = doc.SelectNodes(XPathQueryHelper.ContainerQuery); + + foreach (XmlNode containerNode in containerNodes) + { + DateTime? lastModified = XPathQueryHelper.LoadSingleChildDateTimeValue( + containerNode, StorageHttpConstants.XmlElementNames.LastModified, false); + + string eTag = XPathQueryHelper.LoadSingleChildStringValue( + containerNode, StorageHttpConstants.XmlElementNames.Etag, false); + + string containerUri = XPathQueryHelper.LoadSingleChildStringValue( + containerNode, StorageHttpConstants.XmlElementNames.Url, true); + + string containerName = XPathQueryHelper.LoadSingleChildStringValue( + containerNode, StorageHttpConstants.XmlElementNames.Name, true); + + ContainerProperties properties = new ContainerProperties(containerName); + if (lastModified.HasValue) + properties.LastModifiedTime = lastModified.Value; + properties.ETag = eTag; + + Uri uri = null; + Uri.TryCreate(containerUri, UriKind.Absolute, out uri); + properties.Uri = uri; + + containers.Add(properties); + } + + // Get the nextMarker + XmlNode nextMarkerNode = doc.SelectSingleNode(XPathQueryHelper.NextMarkerQuery); + if (nextMarkerNode != null && nextMarkerNode.FirstChild != null) + { + nextMarker = nextMarkerNode.FirstChild.Value; + } + + return new ListContainersResult(containers, nextMarker); + } + } + + internal class BlobContainerRest : BlobContainer + { + + internal BlobContainerRest( + Uri baseUri, + bool usePathStyleUris, + string accountName, + string containerName, + string base64Key, + DateTime lastModified, + TimeSpan timeOut, + RetryPolicy retryPolicy + ) + : base(baseUri, usePathStyleUris, accountName, containerName, lastModified) + { + ResourceUriComponents uriComponents = + new ResourceUriComponents(accountName, containerName, null); + containerUri = HttpRequestAccessor.ConstructResourceUri(baseUri, uriComponents, usePathStyleUris); + if (base64Key != null) + key = Convert.FromBase64String(base64Key); + credentials = new SharedKeyCredentials(accountName, key); + Timeout = timeOut; + RetryPolicy = retryPolicy; + } + + public override Uri ContainerUri + { + get + { + return this.containerUri; + } + } + + public override bool CreateContainer() + { + return CreateContainerImpl(null, ContainerAccessControl.Private); + } + + /// + /// Create the container with the specified access control if it does not exist + /// + /// The metadata for the container. Can be null to indicate no metadata + /// The access control (public or private) with which to create the container + /// true if the container was created. false if the container already exists + public override bool CreateContainer(NameValueCollection metadata, ContainerAccessControl accessControl) + { + return CreateContainerImpl(metadata, accessControl); + } + + + public override bool DoesContainerExist() + { + bool result = false; + RetryPolicy(() => + { + ResourceUriComponents uriComponents; + Uri uri = Utilities.CreateRequestUri(BaseUri, UsePathStyleUris, AccountName, ContainerName, null, Timeout, + new NameValueCollection(), out uriComponents); + HttpWebRequest request = Utilities.CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Get, Timeout); + credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.OK) + result = true; + else if (response.StatusCode == HttpStatusCode.Gone || response.StatusCode == HttpStatusCode.NotFound) + result = false; + else + { + Utilities.ProcessUnexpectedStatusCode(response); + result = false; + } + + response.Close(); + } + } + catch (WebException we) + { + if (we.Response != null && + (((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.Gone || + ((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.NotFound) + ) + result = false; + else + throw Utilities.TranslateWebException(we); + } + }); + return result; + } + + /// + /// Get the properties for the container if it exists. + /// + /// The metadata for the container if it exists, null otherwise + public override ContainerProperties GetContainerProperties() + { + ContainerProperties result = null; + RetryPolicy(() => + { + ResourceUriComponents uriComponents; + Uri uri = Utilities.CreateRequestUri(BaseUri, UsePathStyleUris, AccountName, ContainerName, null, Timeout, + new NameValueCollection(), out uriComponents); + HttpWebRequest request = Utilities.CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Get, Timeout); + credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.OK) + { + result = ContainerPropertiesFromResponse(response); + } + else if (response.StatusCode == HttpStatusCode.Gone || response.StatusCode == HttpStatusCode.NotFound) + { + result = null; + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + result = null; + } + response.Close(); + } + } + catch (WebException we) + { + if (we.Response != null && + ((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.Gone || + (((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.NotFound) + ) + result = null; + else + throw Utilities.TranslateWebException(we); + } + + }); + return result; + } + + /// + /// Get the access control permissions associated with the container. + /// + /// + public override ContainerAccessControl GetContainerAccessControl() + { + ContainerAccessControl accessControl = ContainerAccessControl.Private; + RetryPolicy(() => + { + ResourceUriComponents uriComponents; + NameValueCollection queryParams = new NameValueCollection(); + queryParams.Add(StorageHttpConstants.QueryParams.QueryParamComp, StorageHttpConstants.CompConstants.Acl); + + Uri uri = Utilities.CreateRequestUri(BaseUri, UsePathStyleUris, AccountName, ContainerName, null, Timeout, + queryParams, out uriComponents); + HttpWebRequest request = Utilities.CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Get, Timeout); + credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.OK) + { + string acl = response.Headers[StorageHttpConstants.HeaderNames.PublicAccess]; + bool publicAcl = false; + if (acl != null && bool.TryParse(acl, out publicAcl)) + { + accessControl = (publicAcl ? ContainerAccessControl.Public : ContainerAccessControl.Private); + } + else + { + throw new StorageServerException( + StorageErrorCode.ServiceBadResponse, + "The server did not respond with expected container access control header", + default(HttpStatusCode), + null, + null + ); + } + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + } + response.Close(); + } + } + catch (WebException we) + { + throw Utilities.TranslateWebException(we); + } + + }); + return accessControl; + } + + /// + /// Get the access control permissions associated with the container. + /// + /// + public override void SetContainerAccessControl(ContainerAccessControl acl) + { + RetryPolicy(() => + { + ResourceUriComponents uriComponents; + NameValueCollection queryParams = new NameValueCollection(); + queryParams.Add(StorageHttpConstants.QueryParams.QueryParamComp, StorageHttpConstants.CompConstants.Acl); + + Uri uri = Utilities.CreateRequestUri(BaseUri, UsePathStyleUris, AccountName, ContainerName, null, Timeout, + queryParams, out uriComponents); + HttpWebRequest request = Utilities.CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Put, Timeout); + request.Headers.Add(StorageHttpConstants.HeaderNames.PublicAccess, + (acl == ContainerAccessControl.Public).ToString()); + credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode != HttpStatusCode.OK) + { + Utilities.ProcessUnexpectedStatusCode(response); + } + response.Close(); + } + } + catch (WebException we) + { + throw Utilities.TranslateWebException(we); + } + + }); + } + + public override bool DeleteContainer() + { + bool result = false; + RetryPolicy(() => + { + ResourceUriComponents uriComponents; + Uri uri = Utilities.CreateRequestUri(BaseUri, this.UsePathStyleUris, AccountName, ContainerName, null, Timeout, new NameValueCollection(), + out uriComponents); + HttpWebRequest request = Utilities.CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Delete, Timeout); + credentials.SignRequest(request, uriComponents); + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Accepted) + result = true; + else + Utilities.ProcessUnexpectedStatusCode(response); + response.Close(); + } + } + catch (WebException we) + { + if (we.Response != null && + (((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.NotFound || + ((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.Gone || + ((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.Conflict)) + result = false; + else + throw Utilities.TranslateWebException(we); + } + } + ); + return result; + } + + private bool CreateContainerImpl(NameValueCollection metadata, ContainerAccessControl accessControl) + { + bool result = false; + RetryPolicy(() => + { + ResourceUriComponents uriComponents; + Uri uri = Utilities.CreateRequestUri(BaseUri, UsePathStyleUris, AccountName, ContainerName, null, Timeout, new NameValueCollection(), + out uriComponents); + HttpWebRequest request = Utilities.CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Put, Timeout); + if (metadata != null) + { + Utilities.AddMetadataHeaders(request, metadata); + } + if (accessControl == ContainerAccessControl.Public) + { + request.Headers.Add(StorageHttpConstants.HeaderNames.PublicAccess, "true"); + } + credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.Created) + result = true; + else if (response.StatusCode == HttpStatusCode.Conflict) + result = false; + else + Utilities.ProcessUnexpectedStatusCode(response); + response.Close(); + } + } + catch (WebException we) + { + if (we.Response != null && ((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.Conflict) + result = false; + else + throw Utilities.TranslateWebException(we); + } + }); + return result; + } + + public override bool DoesBlobExist(string blobName) + { + //if the blob exists, the GetBlobProperties function should return for us a BlobProperties object, otherwise it returns null + if (GetBlobProperties(blobName) == null) + { + return false; + } + else + { + return true; + } + } + + /// + /// Create a new blob or overwrite an existing blob. + /// + /// + /// The properties of the blob + /// The contents of the blob + /// Should this request overwrite an existing blob ? + /// true if the blob was created. false if the blob already exists and was set to false + /// The LastModifiedTime property of is set as a result of this call + public override bool CreateBlob(BlobProperties blobProperties, BlobContents blobContents, bool overwrite) + { + return PutBlobImpl(blobProperties, blobContents.AsStream, overwrite, null); + } + + /// + /// Updates an existing blob if it has not been modified since the specified time which is typically + /// the last modified time of the blob when you retrieved it. + /// Use this method to implement optimistic concurrency by avoiding clobbering changes to the blob + /// made by another writer. + /// + /// The properties of the blob. This object should be one previously + /// obtained from a call to GetBlob or GetBlobProperties and have its LastModifiedTime property set. + /// The contents of the blob. The contents of the blob should be readable + /// true if the blob was updated. false if the blob has changed since the last time + /// The LastModifiedTime property of is set as a result of this call + public override bool UpdateBlobIfNotModified(BlobProperties blobProperties, BlobContents contents) + { + return PutBlobImpl(blobProperties, contents.AsStream, true, blobProperties.ETag); + } + + /// + /// Get the blob contents and properties if the blob exisits + /// + /// The name of the blob + /// Object in which the contents are returned. + /// This object should contain a writable stream or should be a default constructed object. + /// Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller piecs in case of failure. + /// The properties of the blob if the blob exists. + public override BlobProperties GetBlob(string name, BlobContents blobContents, bool transferAsChunks) + { + bool notModified = false; + return GetBlobImpl(name, blobContents.AsStream, null, transferAsChunks, out notModified); + } + + /// + /// Gets the blob contents and properties if the blob has not been modified since the time specified. + /// Use this method if you have cached the contents of a blob and want to avoid retrieving the blob + /// if it has not changed since the last time you retrieved it. + /// + /// The properties of the blob obtained from an earlier call to GetBlob. This + /// parameter is updated by the call if the blob has been modified + /// Contains the stream to which the contents of the blob are written if it has been + /// modified + /// Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller piecs in case of failure. + /// true if the blob has been modified, false otherwise + public override bool GetBlobIfModified(BlobProperties blobProperties, BlobContents blobContents, bool transferAsChunks) + { + bool modified = true; + BlobProperties newProperties = + GetBlobImpl(blobProperties.Name, blobContents.AsStream, blobProperties.ETag, transferAsChunks, out modified); + if (modified) + blobProperties.Assign(newProperties); + return modified; + } + + /// + /// Get the properties of the blob if it exists. + /// This method is also the simplest way to check if a blob exists. + /// + /// The name of the blob + /// The properties of the blob if it exists. null otherwise. + /// The properties for the contents of the blob are not set + public override BlobProperties GetBlobProperties(string name) + { + try + { + bool modified = false; + return GetBlobImpl(name, null, null, false, out modified); + } + catch (StorageClientException se) + { + if (se.ErrorCode == StorageErrorCode.ResourceNotFound || se.ErrorCode == StorageErrorCode.BlobNotFound) + return null; + throw; + } + } + + /// + /// Set the metadata of an existing blob. + /// + /// The blob properties object whose metadata is to be updated + public override void UpdateBlobMetadata(BlobProperties blobProperties) + { + SetBlobMetadataImpl(blobProperties, null); + } + + /// + /// Set the metadata of an existing blob if it has not been modified since it was last retrieved. + /// + /// The blob properties object whose metadata is to be updated. + /// Typically obtained by a previous call to GetBlob or GetBlobProperties + public override bool UpdateBlobMetadataIfNotModified(BlobProperties blobProperties) + { + return SetBlobMetadataImpl(blobProperties, blobProperties.ETag); + } + + /// + /// Delete a blob with the given name + /// + /// The name of the blob + /// true if the blob exists and was successfully deleted, false if the blob does not exist + public override bool DeleteBlob(string name) + { + bool unused = false; + return DeleteBlobImpl(name, null, out unused); + } + + /// + /// Delete a blob with the given name if the blob has not been modified since it was last obtained. + /// Use this method for optimistic concurrency to avoid deleting a blob that has been modified since + /// the last time you retrieved it + /// + /// A blob object (typically previously obtained from a GetBlob call) + /// This out parameter is set to true if the blob was not deleted because + /// it was modified + /// true if the blob exists and was successfully deleted, false if the blob does not exist or was + /// not deleted because the blob was modified. + public override bool DeleteBlobIfNotModified(BlobProperties blob, out bool modified) + { + return DeleteBlobImpl(blob.Name, blob.ETag, out modified); + } + + /// + /// Enumerates all blobs with a given prefix. + /// + /// + /// If true common prefixes with "/" as seperator + /// The list of blob properties and common prefixes + public override IEnumerable ListBlobs(string prefix, bool combineCommonPrefixes) + { + string marker = ""; + const int maxResults = StorageHttpConstants.ListingConstants.MaxBlobListResults; + + string delimiter = combineCommonPrefixes ? "/" : ""; + do + { + ListBlobsResult result = ListBlobsImpl(prefix, marker, delimiter, maxResults); + marker = result.NextMarker; + foreach (string commonPrefix in result.CommonPrefixes) + { + yield return commonPrefix; + } + + foreach (BlobProperties blob in result.Blobs) + { + yield return blob; + } + } while (marker != null); + } + + private HttpWebRequest CreateHttpRequestForPutBlob(Uri uri, string httpMethod, BlobProperties blobProperties, + long contentLength, bool overwrite, string eTag) + { + HttpWebRequest request = Utilities.CreateHttpRequest(uri, httpMethod, Timeout); + if (blobProperties.ContentEncoding != null) + request.Headers.Add(StorageHttpConstants.HeaderNames.ContentEncoding, blobProperties.ContentEncoding); + if (blobProperties.ContentLanguage != null) + request.Headers.Add(StorageHttpConstants.HeaderNames.ContentLanguage, blobProperties.ContentLanguage); + if (blobProperties.ContentType != null) + request.ContentType = blobProperties.ContentType; + if (eTag != null) + request.Headers.Add(StorageHttpConstants.HeaderNames.IfMatch, eTag); + + if (blobProperties.Metadata != null && blobProperties.Metadata.Count > 0) + { + Utilities.AddMetadataHeaders(request, blobProperties.Metadata); + } + request.ContentLength = contentLength; + if (!overwrite) + { + request.Headers.Set(StorageHttpConstants.HeaderNames.IfNoneMatch, "*"); + } + return request; + } + + private HttpWebRequest CreateHttpRequestForGetBlob(Uri uri, string httpMethod, string ifNoneMatchETag, string ifMatchETag) + { + HttpWebRequest request = Utilities.CreateHttpRequest(uri, httpMethod, Timeout); + if (ifNoneMatchETag != null) + { + request.Headers.Add(StorageHttpConstants.HeaderNames.IfNoneMatch, ifNoneMatchETag); + } + if (ifMatchETag != null) + { + request.Headers.Add(StorageHttpConstants.HeaderNames.IfMatch, ifMatchETag); + } + return request; + } + + + /// + /// Uploads a blob in chunks. + /// + /// + /// + /// + /// + /// + private bool PutLargeBlobImpl(BlobProperties blobProperties, Stream stream, bool overwrite, string eTag) + { + bool retval = false; + // Since we got a large block, chunk it into smaller pieces called blocks + long blockSize = StorageHttpConstants.BlobBlockConstants.BlockSize; + long startPosition = stream.Position; + long length = stream.Length - startPosition; + int numBlocks = (int)Math.Ceiling((double)length / blockSize); + string[] blockIds = new string[numBlocks]; + + //We can retry only if the stream supports seeking. An alternative is to buffer the data in memory + //but we do not do this currently. + RetryPolicy R = (stream.CanSeek ? this.RetryPolicy : RetryPolicies.NoRetry); + + //Upload each of the blocks, retrying any failed uploads + for (int i = 0; i < numBlocks; ++i) + { + string blockId = Convert.ToBase64String(System.BitConverter.GetBytes(i)); + blockIds[i] = blockId; + R(() => + { + // Rewind the stream to appropriate location in case this is a retry + if (stream.CanSeek) + stream.Position = startPosition + i * blockSize; + NameValueCollection nvc = new NameValueCollection(); + nvc.Add(QueryParams.QueryParamComp, CompConstants.Block); + nvc.Add(QueryParams.QueryParamBlockId, blockId); // The block naming should be more elaborate to give more meanings on GetBlockList + long blockLength = Math.Min(blockSize, length - stream.Position); + retval = UploadData(blobProperties, stream, blockLength, overwrite, eTag, nvc); + }); + } + + // Now commit the list + // First create the output + using (MemoryStream buffer = new MemoryStream()) + { + // construct our own XML segment with correct blockId's + XmlTextWriter writer = new XmlTextWriter(buffer, Encoding.UTF8); + writer.WriteStartDocument(); + writer.WriteStartElement(XmlElementNames.BlockList); + foreach (string id in blockIds) + { + writer.WriteElementString(XmlElementNames.Block, id); + } + writer.WriteEndElement(); + writer.WriteEndDocument(); + writer.Flush(); + buffer.Position = 0; //Rewind + + NameValueCollection nvc = new NameValueCollection(); + nvc.Add(QueryParams.QueryParamComp, CompConstants.BlockList); + + retval = UploadData(blobProperties, buffer, buffer.Length, overwrite, eTag, nvc); + } + + return retval; + } + + private bool PutBlobImpl(BlobProperties blobProperties, Stream stream, bool overwrite, string eTag) + { + // If the blob is large, we should use blocks to upload it in pieces. + // This will ensure that a broken connection will only impact a single piece + long originalPosition = stream.Position; + long length = stream.Length - stream.Position; + if (length > StorageHttpConstants.BlobBlockConstants.MaximumBlobSizeBeforeTransmittingAsBlocks) + return PutLargeBlobImpl(blobProperties, stream, overwrite, eTag); + + bool retval = false; + RetryPolicy R = stream.CanSeek ? this.RetryPolicy : RetryPolicies.NoRetry; + R(() => + { + if (stream.CanSeek) + stream.Position = originalPosition; + retval = UploadData(blobProperties, stream, length, overwrite, eTag, new NameValueCollection()); + }); + + return retval; + } + + private bool UploadData(BlobProperties blobProperties, Stream stream, long length, bool overwrite, string eTag, NameValueCollection nvc) + { + ResourceUriComponents uriComponents; + Uri blobUri = Utilities.CreateRequestUri(BaseUri, this.UsePathStyleUris, AccountName, ContainerName, + blobProperties.Name, Timeout, nvc, out uriComponents); + HttpWebRequest request = CreateHttpRequestForPutBlob( + blobUri, + StorageHttpConstants.HttpMethod.Put, + blobProperties, + length, + overwrite, + eTag + ); + credentials.SignRequest(request, uriComponents); + bool retval = false; + + try + { + using (Stream requestStream = request.GetRequestStream()) + { + Utilities.CopyStream(stream, requestStream, length); + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.Created) + { + retval = true; + } + else if (!overwrite && + (response.StatusCode == HttpStatusCode.PreconditionFailed || + response.StatusCode == HttpStatusCode.NotModified)) + + { + retval = false; + } + else + { + retval = false; + Utilities.ProcessUnexpectedStatusCode(response); + } + + blobProperties.LastModifiedTime = response.LastModified.ToUniversalTime(); + blobProperties.ETag = response.Headers[StorageHttpConstants.HeaderNames.ETag]; + response.Close(); + } + requestStream.Close(); + } + } + catch (IOException ioe) + { + throw new StorageServerException( + StorageErrorCode.TransportError, + "The connection may be lost", + default(HttpStatusCode), + null, + ioe + ); + } + catch (System.TimeoutException te) + { + throw new StorageServerException( + StorageErrorCode.ServiceTimeout, + "Timeout during blob upload", + HttpStatusCode.RequestTimeout, + null, + te + ); + } + catch (WebException we) + { + if (we.Response != null) + { + HttpWebResponse response = (HttpWebResponse)we.Response; + + + if ((response.StatusCode == HttpStatusCode.PreconditionFailed || + response.StatusCode == HttpStatusCode.NotModified) && + (!overwrite || eTag != null)) + + { + retval = false; + return retval; + } + } + throw Utilities.TranslateWebException(we); + } + return retval; + } + + private BlobProperties GetBlobImpl(string blobName, Stream stream, string oldETag, bool chunked, out bool modified) + { + //We introduce this local variable since lambda expressions cannot contain use an out parameter + BlobProperties blobProperties = null; + bool localModified = true; + + //If we are interested only in the blob properties (stream == null) or we are performing + // a chunked download we first obtain just the blob properties + if (stream == null || chunked) + { + RetryPolicy(() => + { + blobProperties = DownloadData( + blobName, + null, + oldETag, + null, + 0, + 0, + new NameValueCollection(), + ref localModified + ); + }); + modified = localModified; + if (stream == null) + { + return blobProperties; + } + } + + + RetryPolicy R = stream.CanSeek ? this.RetryPolicy : RetryPolicies.NoRetry; + long originalPosition = stream.CanSeek ? stream.Position : 0; + if (chunked && blobProperties != null && blobProperties.ContentLength > 0) + { + //Chunked download. Obtain ranges of the blobs in 'BlockSize' chunks + //Ensure that the If-Match header is used on each request so + //that we are assured that all data belongs to the single blob we + //started downloading. + long location = 0; + while (location < blobProperties.ContentLength) + { + long nBytes = Math.Min(blobProperties.ContentLength - location, StorageHttpConstants.BlobBlockConstants.BlockSize); + R(() => + { + // Set the position to rewind in case of a retry + if (stream.CanSeek) + stream.Position = originalPosition + location; + DownloadData( + blobName, + stream, + oldETag, + blobProperties.ETag, + location, + nBytes, + new NameValueCollection(), + ref localModified + ); + }); + location += nBytes; + } + } + else + { + //Non-chunked download. Obtain the entire blob in a single request. + R(() => + { + // Set the position to rewind in case of a retry + if (stream.CanSeek) + stream.Position = originalPosition; + + blobProperties = DownloadData( + blobName, + stream, + oldETag, + null, + 0, + 0, + new NameValueCollection(), + ref localModified + ); + }); + } + modified = localModified; + return blobProperties; + } + + private bool SetBlobMetadataImpl(BlobProperties blobProperties, string eTag) + { + bool retval = false; + RetryPolicy(() => + { + NameValueCollection queryParams = new NameValueCollection(); + queryParams.Add(StorageHttpConstants.QueryParams.QueryParamComp, StorageHttpConstants.CompConstants.Metadata); + + ResourceUriComponents uriComponents; + Uri uri = Utilities.CreateRequestUri( + BaseUri, + this.UsePathStyleUris, + AccountName, + ContainerName, + blobProperties.Name, + Timeout, + queryParams, + out uriComponents + ); + HttpWebRequest request = Utilities.CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Put, Timeout); + if (blobProperties.Metadata != null) + { + Utilities.AddMetadataHeaders(request, blobProperties.Metadata); + } + if (eTag != null) + { + request.Headers.Add(StorageHttpConstants.HeaderNames.IfMatch, eTag); + } + credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.OK) + { + retval = true; + } + else if ((response.StatusCode == HttpStatusCode.PreconditionFailed || + response.StatusCode == HttpStatusCode.NotModified) && + eTag != null) + { + retval = false; + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + retval = false; + } + blobProperties.LastModifiedTime = response.LastModified.ToUniversalTime(); + blobProperties.ETag = response.Headers[StorageHttpConstants.HeaderNames.ETag]; + response.Close(); + } + } + catch (IOException ioe) + { + throw new StorageServerException(StorageErrorCode.TransportError, "The connection may be lost", + default(HttpStatusCode), ioe); + } + catch (System.TimeoutException te) + { + throw new StorageServerException(StorageErrorCode.ServiceTimeout, "Timeout during blob metadata upload", + HttpStatusCode.RequestTimeout, te); + } + catch (WebException we) + { + if (we.Response != null) + { + HttpWebResponse response = (HttpWebResponse)we.Response; + if (eTag != null && + (response.StatusCode == HttpStatusCode.PreconditionFailed || + response.StatusCode == HttpStatusCode.NotModified)) + { + retval = false; + return; + } + } + throw Utilities.TranslateWebException(we); + } + }); + return retval; + } + + private List GetBlockList(string blobName, string eTag) + { + List blocks; + using (MemoryStream blockListStream = new MemoryStream()) + { + bool temp = true; + NameValueCollection nvc = new NameValueCollection(); + nvc.Add(QueryParams.QueryParamComp, CompConstants.BlockList); + + DownloadData(blobName, blockListStream, eTag, null, 0, 0, nvc, ref temp); + XmlDocument doc = new XmlDocument(); + blockListStream.Position = 0; + doc.Load(blockListStream); + + blocks = new List(); + foreach (XmlNode block in doc.SelectNodes(XPathQueryHelper.BlockQuery)) + { + blocks.Add((int)XPathQueryHelper.LoadSingleChildLongValue(block, XmlElementNames.Size, false)); + } + } + return blocks; + } + + /// + /// Helper method used for getting blobs, ranges of blobs and blob properties. + /// + /// Name of the blob + /// The output stream to write blob data to. Can be null if only retrieving blob properties + /// The If-None-Match header. Used to avoid downloading blob data if the blob has not changed + /// The If-Match header. Used to ensure that all chunks of the blob are of the same blob + /// The offset of the blob data to begin downloading from. Set to 0 to download all data. + /// The length of the blob data to download. Set to 0 to download all data + /// Query paramters to add to the request. + /// Whether the blob had been modfied with respect to the + /// + private BlobProperties DownloadData( + string blobName, + Stream stream, + string eTagIfNoneMatch, + string eTagIfMatch, + long offset, + long length, + NameValueCollection nvc, + ref bool localModified + ) + { + ResourceUriComponents uriComponents; + Uri blobUri = Utilities.CreateRequestUri(BaseUri, this.UsePathStyleUris, AccountName, ContainerName, blobName, Timeout, nvc, out uriComponents); + string httpMethod = (stream == null ? StorageHttpConstants.HttpMethod.Head : StorageHttpConstants.HttpMethod.Get); + HttpWebRequest request = CreateHttpRequestForGetBlob(blobUri, httpMethod, eTagIfNoneMatch, eTagIfMatch); + + if (offset != 0 || length != 0) + { + //Use the blob storage custom header for range since the standard HttpWebRequest.AddRange + //accepts only 32 bit integers and so does not work for large blobs + string rangeHeaderValue = string.Format( + CultureInfo.InvariantCulture, + HeaderValues.RangeHeaderFormat, + offset, + offset + length - 1); + request.Headers.Add(HeaderNames.StorageRange, rangeHeaderValue); + } + credentials.SignRequest(request, uriComponents); + + BlobProperties blobProperties; + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.OK + || response.StatusCode == HttpStatusCode.PartialContent) + { + blobProperties = BlobPropertiesFromResponse(blobName, blobUri, response); + if (stream != null) + { + using (Stream responseStream = response.GetResponseStream()) + { + long bytesCopied = Utilities.CopyStream(responseStream, stream); + if (blobProperties.ContentLength > 0 && bytesCopied < blobProperties.ContentLength) + throw new StorageServerException( + StorageErrorCode.ServiceTimeout, + "Unable to read complete data from server", + HttpStatusCode.RequestTimeout, + null + ); + } + } + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + return null; + } + } + return blobProperties; + } + catch (IOException ioe) + { + throw new StorageServerException(StorageErrorCode.TransportError, "The connection may be lost", + default(HttpStatusCode), ioe); + } + catch (System.TimeoutException te) + { + throw new StorageServerException(StorageErrorCode.ServiceTimeout, "Timeout during blob download", + HttpStatusCode.RequestTimeout, te); + } + catch (WebException we) + { + if (we.Response != null) + { + HttpWebResponse response = (HttpWebResponse)we.Response; + if (eTagIfNoneMatch != null && + (response.StatusCode == HttpStatusCode.PreconditionFailed || + response.StatusCode == HttpStatusCode.NotModified)) + { + localModified = false; + blobProperties = null; + return blobProperties; + } + } + throw Utilities.TranslateWebException(we); + } + } + + private static NameValueCollection MetadataFromHeaders(WebHeaderCollection headers) + { + int prefixLength = StorageHttpConstants.HeaderNames.PrefixForMetadata.Length; + string[] headerNames = headers.AllKeys; + NameValueCollection metadataEntries = new NameValueCollection(); + foreach (string headerName in headerNames) + { + if (headerName.StartsWith(StorageHttpConstants.HeaderNames.PrefixForMetadata, + StringComparison.OrdinalIgnoreCase)) + { + // strip out the metadata prefix + metadataEntries.Add(headerName.Substring(prefixLength), headers[headerName]); + } + } + return metadataEntries; + } + + private static BlobProperties BlobPropertiesFromResponse( + string blobName, Uri blobUri, HttpWebResponse response + ) + { + BlobProperties blobProperties = new BlobProperties(blobName); + blobProperties.Uri = blobUri; + blobProperties.ContentEncoding = response.Headers[StorageHttpConstants.HeaderNames.ContentEncoding]; + blobProperties.LastModifiedTime = response.LastModified.ToUniversalTime(); + blobProperties.ETag = response.Headers[StorageHttpConstants.HeaderNames.ETag]; + blobProperties.ContentLanguage = response.Headers[StorageHttpConstants.HeaderNames.ContentLanguage]; + blobProperties.ContentLength = response.ContentLength; + blobProperties.ContentType = response.ContentType; + + NameValueCollection metadataEntries = MetadataFromHeaders(response.Headers); + if (metadataEntries.Count > 0) + blobProperties.Metadata = metadataEntries; + + return blobProperties; + } + + private ContainerProperties ContainerPropertiesFromResponse(HttpWebResponse response) + { + ContainerProperties prop = new ContainerProperties(ContainerName); + prop.LastModifiedTime = response.LastModified.ToUniversalTime(); + prop.ETag = response.Headers[StorageHttpConstants.HeaderNames.ETag]; + prop.Uri = containerUri; + prop.Metadata = MetadataFromHeaders(response.Headers); + return prop; + } + + bool DeleteBlobImpl(string name, string eTag, out bool modified) + { + bool retval = false; + bool localModified = false; + RetryPolicy(() => + { + ResourceUriComponents uriComponents; + Uri blobUri = Utilities.CreateRequestUri(BaseUri, this.UsePathStyleUris, AccountName, ContainerName, name, Timeout, new NameValueCollection(), + out uriComponents); + HttpWebRequest request = Utilities.CreateHttpRequest(blobUri, StorageHttpConstants.HttpMethod.Delete, Timeout); + + if (eTag != null) + request.Headers.Add(StorageHttpConstants.HeaderNames.IfMatch, eTag); + credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.OK || + response.StatusCode == HttpStatusCode.Accepted) + { + retval = true; + } + else + Utilities.ProcessUnexpectedStatusCode(response); + response.Close(); + } + } + catch (IOException ioe) + { + throw new StorageServerException(StorageErrorCode.TransportError, "The connection may be lost", + default(HttpStatusCode), ioe); + } + catch (System.TimeoutException te) + { + throw new StorageServerException(StorageErrorCode.ServiceTimeout, "Timeout during blob delete", + HttpStatusCode.RequestTimeout, te); + } + catch (WebException we) + { + if (we.Response != null) + { + HttpStatusCode status = ((HttpWebResponse)we.Response).StatusCode; + if (status == HttpStatusCode.NotFound || + status == HttpStatusCode.Gone) + { + localModified = true; + retval = false; + } + else if (status == HttpStatusCode.PreconditionFailed || + status == HttpStatusCode.NotModified) + { + retval = false; + localModified = true; + } + else + { + throw Utilities.TranslateWebException(we); + } + } + else + { + throw Utilities.TranslateWebException(we); + } + } + }); + modified = localModified; + return retval; + } + + internal class ListBlobsResult + { + internal ListBlobsResult(IEnumerable blobs, IEnumerable commonPrefixes, string nextMarker) + { + Blobs = blobs; + CommonPrefixes = commonPrefixes; + NextMarker = nextMarker; + } + + internal IEnumerable Blobs + { + get; + private set; + } + + internal IEnumerable CommonPrefixes + { + get; + private set; + } + + internal string NextMarker + { + get; + private set; + } + } + + private ListBlobsResult ListBlobsImpl(string prefix, string fromMarker, string delimiter, int maxCount) + { + ListBlobsResult result = null; + + RetryPolicy(() => + { + ResourceUriComponents uriComponents; + Uri listBlobsUri = CreateRequestUriForListBlobs(prefix, fromMarker, delimiter, maxCount, out uriComponents); + + HttpWebRequest request = Utilities.CreateHttpRequest(listBlobsUri, StorageHttpConstants.HttpMethod.Get, Timeout); + credentials.SignRequest(request, uriComponents); + + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.OK) + { + using (Stream stream = response.GetResponseStream()) + { + result = ListBlobsResultFromResponse(stream); + } + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + } + } + } + catch (IOException ioe) + { + throw new StorageServerException(StorageErrorCode.TransportError, "The connection may be lost", + default(HttpStatusCode), ioe); + } + catch (System.TimeoutException te) + { + throw new StorageServerException(StorageErrorCode.ServiceTimeout, "Timeout during listing blobs", + HttpStatusCode.RequestTimeout, te); + } + catch (WebException we) + { + throw Utilities.TranslateWebException(we); + } + }); + return result; + } + + private Uri CreateRequestUriForListBlobs( + string prefix, string fromMarker, string delimiter, int maxResults, out ResourceUriComponents uriComponents) + { + NameValueCollection queryParams = BlobStorageRest.CreateRequestUriForListing(prefix, fromMarker, delimiter, maxResults); + return Utilities.CreateRequestUri(BaseUri, this.UsePathStyleUris, AccountName, ContainerName, null, Timeout, queryParams, out uriComponents); + } + + private static ListBlobsResult ListBlobsResultFromResponse(Stream responseBody) + { + List blobs = new List(); + List commonPrefixes = new List(); + string nextMarker = null; + + XmlDocument doc = new XmlDocument(); + try + { + doc.Load(responseBody); + } + catch (XmlException xe) + { + throw new StorageServerException(StorageErrorCode.ServiceBadResponse, + "The result of a ListBlobs operation could not be parsed", default(HttpStatusCode), xe); + } + + // Get the commonPrefixes + XmlNodeList prefixNodes = doc.SelectNodes(XPathQueryHelper.CommonPrefixQuery); + + foreach (XmlNode prefixNode in prefixNodes) + { + string blobPrefix = XPathQueryHelper.LoadSingleChildStringValue( + prefixNode, StorageHttpConstants.XmlElementNames.BlobPrefixName, false); + commonPrefixes.Add(blobPrefix); + } + + // Get all the blobs returned as the listing results + XmlNodeList blobNodes = doc.SelectNodes(XPathQueryHelper.BlobQuery); + + foreach (XmlNode blobNode in blobNodes) + { + DateTime? lastModified = XPathQueryHelper.LoadSingleChildDateTimeValue( + blobNode, StorageHttpConstants.XmlElementNames.LastModified, false); + + string eTag = XPathQueryHelper.LoadSingleChildStringValue( + blobNode, StorageHttpConstants.XmlElementNames.Etag, false); + + string contentType = XPathQueryHelper.LoadSingleChildStringValue( + blobNode, StorageHttpConstants.XmlElementNames.ContentType, false); + + string contentEncoding = XPathQueryHelper.LoadSingleChildStringValue( + blobNode, StorageHttpConstants.XmlElementNames.ContentEncoding, false); + + string contentLanguage = XPathQueryHelper.LoadSingleChildStringValue( + blobNode, StorageHttpConstants.XmlElementNames.ContentLanguage, false); + + long? blobSize = XPathQueryHelper.LoadSingleChildLongValue( + blobNode, StorageHttpConstants.XmlElementNames.Size, false); + + string blobUrl = XPathQueryHelper.LoadSingleChildStringValue( + blobNode, StorageHttpConstants.XmlElementNames.Url, true); + + string blobName = XPathQueryHelper.LoadSingleChildStringValue( + blobNode, StorageHttpConstants.XmlElementNames.BlobName, true); + + BlobProperties properties = new BlobProperties(blobName); + properties.Uri = new Uri(blobUrl); + if (lastModified.HasValue) + properties.LastModifiedTime = lastModified.Value; + properties.ContentEncoding = contentEncoding; + properties.ContentLanguage = contentLanguage; + properties.ETag = eTag; + properties.ContentLength = (blobSize == null ? 0 : blobSize.Value); + properties.ContentType = contentType; + + blobs.Add(properties); + } + + // Get the nextMarker + XmlNode nextMarkerNode = doc.SelectSingleNode(XPathQueryHelper.NextMarkerQuery); + if (nextMarkerNode != null && nextMarkerNode.FirstChild != null) + { + nextMarker = nextMarkerNode.FirstChild.Value; + } + + return new ListBlobsResult(blobs, commonPrefixes, nextMarker); + } + + private Uri containerUri; + private byte[] key; + private SharedKeyCredentials credentials; + } + + /// + /// Helper class for loading values from an XML segment + /// + internal static class XPathQueryHelper + { + // In general queries are like "//{0}/{1}/{2}" - using Join as it's more efficient than Format + + internal static readonly string NextMarkerQuery = string.Join(StorageHttpConstants.ConstChars.Slash, + new string[] + { + "", "", + StorageHttpConstants.XmlElementNames.EnumerationResults, + StorageHttpConstants.XmlElementNames.NextMarker + }); + + internal static readonly string ContainerQuery = string.Join(StorageHttpConstants.ConstChars.Slash, + new string[] + { + "", "", + StorageHttpConstants.XmlElementNames.EnumerationResults, + StorageHttpConstants.XmlElementNames.Containers, + StorageHttpConstants.XmlElementNames.Container + }); + + internal static readonly string BlobQuery = string.Join(StorageHttpConstants.ConstChars.Slash, + new string[] + { + "", "", + StorageHttpConstants.XmlElementNames.EnumerationResults, + StorageHttpConstants.XmlElementNames.Blobs, + StorageHttpConstants.XmlElementNames.Blob + }); + + internal static readonly string BlockQuery = string.Join(StorageHttpConstants.ConstChars.Slash, + new string[] + { + "", "", + StorageHttpConstants.XmlElementNames.BlockList, + StorageHttpConstants.XmlElementNames.Block + }); + + internal static readonly string QueueListQuery = string.Join(StorageHttpConstants.ConstChars.Slash, + new string[] + { + "", "", + StorageHttpConstants.XmlElementNames.EnumerationResults, + StorageHttpConstants.XmlElementNames.Queues, + StorageHttpConstants.XmlElementNames.Queue + }); + + internal static readonly string MessagesListQuery = string.Join(StorageHttpConstants.ConstChars.Slash, + new string[] + { + "", "", + StorageHttpConstants.XmlElementNames.QueueMessagesList, + StorageHttpConstants.XmlElementNames.QueueMessage, + }); + + internal static readonly string CommonPrefixQuery = string.Join(StorageHttpConstants.ConstChars.Slash, + new string[] + { + "", "", + StorageHttpConstants.XmlElementNames.EnumerationResults, + StorageHttpConstants.XmlElementNames.Blobs, + StorageHttpConstants.XmlElementNames.BlobPrefix + }); + + internal static DateTime? LoadSingleChildDateTimeValue(XmlNode node, string childName, bool throwIfNotFound) + { + XmlNode childNode = node.SelectSingleNode(childName); + + if (childNode != null && childNode.FirstChild != null) + { + DateTime? dateTime; + if (!Utilities.TryGetDateTimeFromHttpString(childNode.FirstChild.Value, out dateTime)) + { + throw new StorageServerException(StorageErrorCode.ServiceBadResponse, + "Date time value returned from server " + childNode.FirstChild.Value + " can't be parsed.", + default(HttpStatusCode), + null + ); + } + return dateTime; + } + else if (!throwIfNotFound) + { + return null; + } + else + { + return null; + } + } + + + internal static string LoadSingleChildStringValue(XmlNode node, string childName, bool throwIfNotFound) + { + XmlNode childNode = node.SelectSingleNode(childName); + + if (childNode != null && childNode.FirstChild != null) + { + return childNode.FirstChild.Value; + } + else if (!throwIfNotFound) + { + return null; + } + else + { + return null; // unnecessary since Fail will throw, but keeps the compiler happy + } + } + + internal static long? LoadSingleChildLongValue(XmlNode node, string childName, bool throwIfNotFound) + { + XmlNode childNode = node.SelectSingleNode(childName); + + if (childNode != null && childNode.FirstChild != null) + { + return long.Parse(childNode.FirstChild.Value, CultureInfo.InvariantCulture); + } + else if (!throwIfNotFound) + { + return null; + } + else + { + return null; // unnecessary since Fail will throw, but keeps the compiler happy + } + } + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/RestHelpers.cs b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/RestHelpers.cs new file mode 100644 index 0000000..5ff0a4d --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/RestHelpers.cs @@ -0,0 +1,503 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Net; +using System.Diagnostics; +using System.Globalization; +using System.Collections.Specialized; +using System.Web; +using System.Xml; +using System.Text.RegularExpressions; + +namespace Microsoft.Samples.ServiceHosting.StorageClient +{ + namespace StorageHttpConstants + { + + internal static class ConstChars + { + internal const string Linefeed = "\n"; + internal const string CarriageReturnLinefeed = "\r\n"; + internal const string Colon = ":"; + internal const string Comma = ","; + internal const string Slash = "/"; + internal const string BackwardSlash = @"\"; + internal const string Space = " "; + internal const string Ampersand = "&"; + internal const string QuestionMark = "?"; + internal const string Equal = "="; + internal const string Bang = "!"; + internal const string Star = "*"; + internal const string Dot = "."; + } + + internal static class RequestParams + { + internal const string NumOfMessages = "numofmessages"; + internal const string VisibilityTimeout = "visibilitytimeout"; + internal const string PeekOnly = "peekonly"; + internal const string MessageTtl = "messagettl"; + internal const string Messages = "messages"; + internal const string PopReceipt = "popreceipt"; + } + + internal static class QueryParams + { + internal const string SeparatorForParameterAndValue = "="; + internal const string QueryParamTimeout = "timeout"; + internal const string QueryParamComp = "comp"; + + // Other query string parameter names + internal const string QueryParamBlockId = "blockid"; + internal const string QueryParamPrefix = "prefix"; + internal const string QueryParamMarker = "marker"; + internal const string QueryParamMaxResults = "maxresults"; + internal const string QueryParamDelimiter = "delimiter"; + internal const string QueryParamModifiedSince = "modifiedsince"; + } + + internal static class CompConstants + { + internal const string Metadata = "metadata"; + internal const string List = "list"; + internal const string BlobList = "bloblist"; + internal const string BlockList = "blocklist"; + internal const string Block = "block"; + internal const string Acl = "acl"; + } + + internal static class XmlElementNames + { + internal const string BlockList = "BlockList"; + internal const string Block = "Block"; + internal const string EnumerationResults = "EnumerationResults"; + internal const string Prefix = "Prefix"; + internal const string Marker = "Marker"; + internal const string MaxResults = "MaxResults"; + internal const string Delimiter = "Delimiter"; + internal const string NextMarker = "NextMarker"; + internal const string Containers = "Containers"; + internal const string Container = "Container"; + internal const string ContainerName = "Name"; + internal const string ContainerNameAttribute = "ContainerName"; + internal const string AccountNameAttribute = "AccountName"; + internal const string LastModified = "LastModified"; + internal const string Etag = "Etag"; + internal const string Url = "Url"; + internal const string CommonPrefixes = "CommonPrefixes"; + internal const string ContentType = "ContentType"; + internal const string ContentEncoding = "ContentEncoding"; + internal const string ContentLanguage = "ContentLanguage"; + internal const string Size = "Size"; + internal const string Blobs = "Blobs"; + internal const string Blob = "Blob"; + internal const string BlobName = "Name"; + internal const string BlobPrefix = "BlobPrefix"; + internal const string BlobPrefixName = "Name"; + internal const string Name = "Name"; + internal const string Queues = "Queues"; + internal const string Queue = "Queue"; + internal const string QueueName = "QueueName"; + internal const string QueueMessagesList = "QueueMessagesList"; + internal const string QueueMessage = "QueueMessage"; + internal const string MessageId = "MessageId"; + internal const string PopReceipt = "PopReceipt"; + internal const string InsertionTime = "InsertionTime"; + internal const string ExpirationTime = "ExpirationTime"; + internal const string TimeNextVisible = "TimeNextVisible"; + internal const string MessageText = "MessageText"; + + // Error specific constants + internal const string ErrorRootElement = "Error"; + internal const string ErrorCode = "Code"; + internal const string ErrorMessage = "Message"; + internal const string ErrorException = "ExceptionDetails"; + internal const string ErrorExceptionMessage = "ExceptionMessage"; + internal const string ErrorExceptionStackTrace = "StackTrace"; + internal const string AuthenticationErrorDetail = "AuthenticationErrorDetail"; + + //The following are for table error messages + internal const string DataWebMetadataNamespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"; + internal const string TableErrorCodeElement = "code"; + internal const string TableErrorMessageElement = "message"; + } + + internal static class HeaderNames + { + internal const string PrefixForStorageProperties = "x-ms-prop-"; + internal const string PrefixForMetadata = "x-ms-meta-"; + internal const string PrefixForStorageHeader = "x-ms-"; + internal const string PrefixForTableContinuation = "x-ms-continuation-"; + + // + // Standard headers... + // + internal const string ContentLanguage = "Content-Language"; + internal const string ContentLength = "Content-Length"; + internal const string ContentType = "Content-Type"; + internal const string ContentEncoding = "Content-Encoding"; + internal const string ContentMD5 = "Content-MD5"; + internal const string ContentRange = "Content-Range"; + internal const string LastModifiedTime = "Last-Modified"; + internal const string Server = "Server"; + internal const string Allow = "Allow"; + internal const string ETag = "ETag"; + internal const string Range = "Range"; + internal const string Date = "Date"; + internal const string Authorization = "Authorization"; + internal const string IfModifiedSince = "If-Modified-Since"; + internal const string IfUnmodifiedSince = "If-Unmodified-Since"; + internal const string IfMatch = "If-Match"; + internal const string IfNoneMatch = "If-None-Match"; + internal const string IfRange = "If-Range"; + internal const string NextPartitionKey = "NextPartitionKey"; + internal const string NextRowKey = "NextRowKey"; + internal const string NextTableName = "NextTableName"; + + // + // Storage specific custom headers... + // + internal const string StorageDateTime = PrefixForStorageHeader + "date"; + internal const string PublicAccess = PrefixForStorageProperties + "publicaccess"; + internal const string StorageRange = PrefixForStorageHeader + "range"; + + internal const string CreationTime = PrefixForStorageProperties + "creation-time"; + internal const string ForceUpdate = PrefixForStorageHeader + "force-update"; + internal const string ApproximateMessagesCount = PrefixForStorageHeader + "approximate-messages-count"; + internal const string Version = PrefixForStorageHeader + "version"; + } + + internal static class HeaderValues + { + internal const string ContentTypeXml = "application/xml"; + + /// + /// This is the default content-type xStore uses when no content type is specified + /// + internal const string DefaultContentType = "application/octet-stream"; + + // The Range header value is "bytes=start-end", both start and end can be empty + internal const string RangeHeaderFormat = "bytes={0}-{1}"; + + } + + internal static class AuthenticationSchemeNames + { + internal const string SharedKeyAuthSchemeName = "SharedKey"; + internal const string SharedKeyLiteAuthSchemeName = "SharedKeyLite"; + } + + internal static class HttpMethod + { + internal const string Get = "GET"; + internal const string Put = "PUT"; + internal const string Post = "POST"; + internal const string Head = "HEAD"; + internal const string Delete = "DELETE"; + internal const string Trace = "TRACE"; + internal const string Options = "OPTIONS"; + internal const string Connect = "CONNECT"; + } + + internal static class BlobBlockConstants + { + internal const int KB = 1024; + internal const int MB = 1024 * KB; + /// + /// When transmitting a blob that is larger than this constant, this library automatically + /// transmits the blob as individual blocks. I.e., the blob is (1) partitioned + /// into separate parts (these parts are called blocks) and then (2) each of the blocks is + /// transmitted separately. + /// The maximum size of this constant as supported by the real blob storage service is currently + /// 64 MB; the development storage tool currently restricts this value to 2 MB. + /// Setting this constant can have a significant impact on the performance for uploading or + /// downloading blobs. + /// As a general guideline: If you run in a reliable environment increase this constant to reduce + /// the amount of roundtrips. In an unreliable environment keep this constant low to reduce the + /// amount of data that needs to be retransmitted in case of connection failures. + /// + internal const long MaximumBlobSizeBeforeTransmittingAsBlocks = 2 * MB; + /// + /// The size of a single block when transmitting a blob that is larger than the + /// MaximumBlobSizeBeforeTransmittingAsBlocks constant (see above). + /// The maximum size of this constant is currently 4 MB; the development storage + /// tool currently restricts this value to 1 MB. + /// Setting this constant can have a significant impact on the performance for uploading or + /// downloading blobs. + /// As a general guideline: If you run in a reliable environment increase this constant to reduce + /// the amount of roundtrips. In an unreliable environment keep this constant low to reduce the + /// amount of data that needs to be retransmitted in case of connection failures. + /// + internal const long BlockSize = 1 * MB; + } + + internal static class ListingConstants + { + internal const int MaxContainerListResults = 100; + internal const int MaxBlobListResults = 100; + internal const int MaxQueueListResults = 50; + internal const int MaxTableListResults = 50; + } + + /// + /// Contains regular expressions for checking whether container and table names conform + /// to the rules of the storage REST protocols. + /// + public static class RegularExpressionStrings + { + /// + /// Container or queue names that match against this regular expression are valid. + /// + public const string ValidContainerNameRegex = @"^([a-z]|\d){1}([a-z]|-|\d){1,61}([a-z]|\d){1}$"; + + /// + /// Table names that match against this regular expression are valid. + /// + public const string ValidTableNameRegex = @"^([a-z]|[A-Z]){1}([a-z]|[A-Z]|\d){2,62}$"; + } + + internal static class StandardPortalEndpoints + { + internal const string BlobStorage = "blob"; + internal const string QueueStorage = "queue"; + internal const string TableStorage = "table"; + internal const string StorageHostSuffix = ".core.windows.net"; + internal const string BlobStorageEndpoint = BlobStorage + StorageHostSuffix; + internal const string QueueStorageEndpoint = QueueStorage + StorageHostSuffix; + internal const string TableStorageEndpoint = TableStorage + StorageHostSuffix; + } + } + + internal static partial class Utilities + { + internal static HttpWebRequest CreateHttpRequest(Uri uri, string httpMethod, TimeSpan timeout) + { + HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri); + request.Timeout = (int)timeout.TotalMilliseconds; + request.ReadWriteTimeout = (int)timeout.TotalMilliseconds; + request.Method = httpMethod; + request.ContentLength = 0; + request.Headers.Add(StorageHttpConstants.HeaderNames.StorageDateTime, + Utilities.ConvertDateTimeToHttpString(DateTime.UtcNow)); + return request; + } + + /// + /// Converts the date time to a valid string form as per HTTP standards + /// + internal static string ConvertDateTimeToHttpString(DateTime dateTime) + { + // On the wire everything should be represented in UTC. This assert will catch invalid callers who + // are violating this rule. + Debug.Assert(dateTime == DateTime.MaxValue || dateTime == DateTime.MinValue || dateTime.Kind == DateTimeKind.Utc); + + // 'R' means rfc1123 date which is what our server uses for all dates... + // It will be in the following format: + // Sun, 28 Jan 2008 12:11:37 GMT + return dateTime.ToString("R", CultureInfo.InvariantCulture); + } + + /// + /// Parse a string having the date time information in acceptable formats according to HTTP standards + /// + internal static bool TryGetDateTimeFromHttpString(string dateString, out DateTime? result) + { + DateTime dateTime; + result = null; + + // 'R' means rfc1123 date which is the preferred format used in HTTP + bool parsed = DateTime.TryParseExact(dateString, "R", null, DateTimeStyles.None, out dateTime); + if (parsed) + { + // For some reason, format string "R" makes the DateTime.Kind as Unspecified while it's actually + // Utc. Specifying DateTimeStyles.AssumeUniversal also doesn't make the difference. If we also + // specify AdjustToUniversal it works as expected but we don't really want Parse to adjust + // things automatically. + result = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc); + return true; + } + + return false; + } + + + /// + /// Copies from one stream to another + /// + /// The stream to copy from + /// The stream to copy to + internal static long CopyStream(Stream sourceStream, Stream destinationStream) + { + const int BufferSize = 0x10000; + byte[] buffer = new byte[BufferSize]; + int n = 0; + long totalRead = 0; + do + { + n = sourceStream.Read(buffer, 0, BufferSize); + if (n > 0) + { + totalRead += n; + destinationStream.Write(buffer, 0, n); + } + + } while (n > 0); + return totalRead; + } + + internal static void CopyStream(Stream sourceStream, Stream destinationStream, long length) + { + const int BufferSize = 0x10000; + byte[] buffer = new byte[BufferSize]; + int n = 0; + long amountLeft = length; + + do + { + amountLeft -= n; + n = sourceStream.Read(buffer, 0, (int)Math.Min(BufferSize, amountLeft)); + if (n > 0) + { + destinationStream.Write(buffer, 0, n); + } + + } while (n > 0); + } + + internal static int CopyStreamToBuffer(Stream sourceStream, byte[] buffer, int bytesToRead) + { + int n = 0; + int amountLeft = bytesToRead; + do + { + n = sourceStream.Read(buffer, bytesToRead - amountLeft, amountLeft); + amountLeft -= n; + } while (n > 0); + return bytesToRead - amountLeft; + } + + internal static Uri CreateRequestUri( + Uri baseUri, + bool usePathStyleUris, + string accountName, + string containerName, + string blobName, + TimeSpan Timeout, + NameValueCollection queryParameters, + out ResourceUriComponents uriComponents + ) + { + uriComponents = + new ResourceUriComponents(accountName, containerName, blobName); + Uri uri = HttpRequestAccessor.ConstructResourceUri(baseUri, uriComponents, usePathStyleUris); + + if (queryParameters != null) + { + UriBuilder builder = new UriBuilder(uri); + + if (queryParameters.Get(StorageHttpConstants.QueryParams.QueryParamTimeout) == null) + { + queryParameters.Add(StorageHttpConstants.QueryParams.QueryParamTimeout, + Timeout.TotalSeconds.ToString(CultureInfo.InvariantCulture)); + } + + StringBuilder sb = new StringBuilder(); + bool firstParam = true; + foreach (string queryKey in queryParameters.AllKeys) + { + if (!firstParam) + sb.Append("&"); + sb.Append(HttpUtility.UrlEncode(queryKey)); + sb.Append('='); + sb.Append(HttpUtility.UrlEncode(queryParameters[queryKey])); + firstParam = false; + } + + if (sb.Length > 0) + { + builder.Query = sb.ToString(); + } + return builder.Uri; + } + else + { + return uri; + } + } + + internal static bool StringIsIPAddress(string address) + { + IPAddress outIPAddress; + + return IPAddress.TryParse(address, out outIPAddress); + } + + internal static void AddMetadataHeaders(HttpWebRequest request, NameValueCollection metadata) + { + foreach (string key in metadata.Keys) + { + request.Headers.Add( + StorageHttpConstants.HeaderNames.PrefixForMetadata + key, + metadata[key] + ); + } + } + + internal static bool IsValidTableName(string name) + { + if (string.IsNullOrEmpty(name)) + { + return false; + } + Regex reg = new Regex(StorageHttpConstants.RegularExpressionStrings.ValidTableNameRegex); + if (reg.IsMatch(name)) + { + return true; + } + else + { + return false; + } + } + + internal static bool IsValidContainerOrQueueName(string name) + { + if (string.IsNullOrEmpty(name)) + { + return false; + } + Regex reg = new Regex(StorageHttpConstants.RegularExpressionStrings.ValidContainerNameRegex); + if (reg.IsMatch(name)) + { + return true; + } + else + { + return false; + } + } + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/RestQueue.cs b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/RestQueue.cs new file mode 100644 index 0000000..6c14804 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/RestQueue.cs @@ -0,0 +1,977 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using System.Net; +using System.Collections.Specialized; +using System.IO; +using System.Xml; +using System.Diagnostics; +using System.Globalization; +using System.Threading; + + +namespace Microsoft.Samples.ServiceHosting.StorageClient +{ + + internal class QueueStorageRest : QueueStorage + { + private SharedKeyCredentials _credentials; + + internal QueueStorageRest(StorageAccountInfo accountInfo,string version) + : base(accountInfo,version) + { + byte[] key = null; + if (accountInfo.Base64Key != null) + { + key = Convert.FromBase64String(accountInfo.Base64Key); + } + _credentials = new SharedKeyCredentials(accountInfo.AccountName, key); + } + + /// + /// Get a reference to a Queue object with a specified name. This method does not make a call to + /// the queue service. + /// + /// The name of the queue + /// A newly created queue object + public override MessageQueue GetQueue(string queueName) + { + return new QueueRest(queueName, + AccountInfo, + Timeout, + RetryPolicy, + Version + ); + } + + internal class ListQueueResult + { + internal ListQueueResult(IEnumerable names, IEnumerable urls, string nextMarker) + { + Names = names; + Urls = urls; + NextMarker = nextMarker; + } + + internal IEnumerable Names + { + get; + private set; + } + + internal IEnumerable Urls + { + get; + private set; + } + + internal string NextMarker + { + get; + private set; + } + } + + /// + /// Lists all queues with a given prefix within an account. + /// + /// + /// The list of queue names. + public override IEnumerable ListQueues(string prefix) + { + string marker = ""; + const int maxResults = StorageHttpConstants.ListingConstants.MaxQueueListResults; + + do + { + ListQueueResult result = ListQueuesImpl(prefix, marker, maxResults); + if (result == null) + { + marker = null; + } + else + { + marker = result.NextMarker; + + foreach (string name in result.Names) + { + yield return new QueueRest(name, AccountInfo, this.Timeout, this.RetryPolicy,this.Version); + } + } + } while (marker != null); + } + + /// + /// Lists the queues within the account. + /// + /// A list of queues + private ListQueueResult ListQueuesImpl(string prefix, string marker, int maxResult) + { + ListQueueResult result = null; + + RetryPolicy(() => + { + NameValueCollection col = new NameValueCollection(); + col.Add(StorageHttpConstants.QueryParams.QueryParamComp, StorageHttpConstants.CompConstants.List); + if (!string.IsNullOrEmpty(prefix)) + { + col.Add(StorageHttpConstants.QueryParams.QueryParamPrefix, prefix); + } + if (!string.IsNullOrEmpty(marker)) + { + col.Add(StorageHttpConstants.QueryParams.QueryParamMarker, marker); + } + col.Add(StorageHttpConstants.QueryParams.QueryParamMaxResults, maxResult.ToString(CultureInfo.InvariantCulture)); + + ResourceUriComponents uriComponents; + Uri uri = Utilities.CreateRequestUri( + AccountInfo.BaseUri, + AccountInfo.UsePathStyleUris, + AccountInfo.AccountName, + null, + null, + Timeout, + col, + out uriComponents + ); + HttpWebRequest request = Utilities.CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Get, Timeout); + _credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.OK) + { + using (Stream stream = response.GetResponseStream()) + { + result = GetQueuesFromResponse(stream); + stream.Close(); + } + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + } + response.Close(); + } + } + catch (WebException we) + { + throw Utilities.TranslateWebException(we); + } + }); + + return result; + } + + private static ListQueueResult GetQueuesFromResponse(Stream stream) + { + ListQueueResult result = null; + List names = new List(); + List urls = new List(); + string nextMarker = null; + + XmlDocument doc = new XmlDocument(); + try + { + doc.Load(stream); + } + catch (XmlException xe) + { + throw new StorageServerException(StorageErrorCode.ServiceBadResponse, + "The result of a ListQueue operation could not be parsed", default(HttpStatusCode), xe); + } + + // get queue names and urls + XmlNodeList queueNameNodes = doc.SelectNodes(XPathQueryHelper.QueueListQuery); + foreach (XmlNode queueNameNode in queueNameNodes) + { + string queueName = XPathQueryHelper.LoadSingleChildStringValue(queueNameNode, StorageHttpConstants.XmlElementNames.QueueName, true); + names.Add(queueName); + string url = XPathQueryHelper.LoadSingleChildStringValue(queueNameNode, StorageHttpConstants.XmlElementNames.Url, true); + urls.Add(url); + } + + // Get the nextMarker + XmlNode nextMarkerNode = doc.SelectSingleNode(XPathQueryHelper.NextMarkerQuery); + if (nextMarkerNode != null && nextMarkerNode.FirstChild != null) + { + nextMarker = nextMarkerNode.FirstChild.Value; + } + if (names.Count > 0) + { + Debug.Assert(names.Count == urls.Count); + result = new ListQueueResult(names, urls, nextMarker); + } + return result; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", + Justification = "Disposable types are only used for automatic receiving of messages, which handle the disposal themselves.")] + internal class QueueRest : MessageQueue + { + #region Member variables and constructors + + private Uri _queueUri; + private SharedKeyCredentials _credentials; + private int _pollInterval = DefaultPollInterval; + private string version; + + + internal QueueRest( + string name, + StorageAccountInfo account, + TimeSpan timeout, + RetryPolicy retryPolicy, + string version + ) + : base(name, account) + { + byte[] key = null; + if (AccountInfo.Base64Key != null) + { + key = Convert.FromBase64String(AccountInfo.Base64Key); + } + ResourceUriComponents uriComponents = new ResourceUriComponents(account.AccountName, name, null); + _credentials = new SharedKeyCredentials(AccountInfo.AccountName, key); + _queueUri = HttpRequestAccessor.ConstructResourceUri(account.BaseUri, uriComponents, account.UsePathStyleUris); + Timeout = timeout; + RetryPolicy = retryPolicy; + this.version = version; + } + #endregion + + #region Public interface + + public override Uri QueueUri + { + get + { + return _queueUri; + } + } + + public override bool CreateQueue(out bool queueAlreadyExists) + { + bool result = false; + queueAlreadyExists = false; + // cannot use ref or out parameters in the retry expression below + bool exists = false; + + RetryPolicy(() => + { + ResourceUriComponents uriComponents; + Uri uri = CreateRequestUri(null, new NameValueCollection(), false, out uriComponents); + HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Put); + _credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.Created) + { + // as an addition we could parse the result and retrieve + // queue properties at this point + exists = false; + result = true; + } + else if (response.StatusCode == HttpStatusCode.NoContent) + { + exists = true; + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + } + response.Close(); + } + } + catch (WebException we) + { + if (we.Response != null && + ((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.Conflict) + { + exists = true; + } + else + { + throw Utilities.TranslateWebException(we); + } + } + }); + + queueAlreadyExists = exists; + return result; + } + + public override bool DeleteQueue() + { + bool result = false; + + RetryPolicy(() => + { + NameValueCollection col = new NameValueCollection(); + + ResourceUriComponents uriComponents; + Uri uri = CreateRequestUri(null, col, false, out uriComponents); + HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Delete); + _credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.NoContent) + { + result = true; + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + } + response.Close(); + } + } + catch (WebException we) + { + if (we.Response != null && + (((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.NotFound || + ((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.PreconditionFailed || + ((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.Conflict)) + { + result = false; + } + else + { + throw Utilities.TranslateWebException(we); + } + } + }); + + return result; + } + + public override bool SetProperties(QueueProperties properties) + { + if (properties == null) + { + throw new ArgumentNullException("properties"); + } + + bool result = false; + + RetryPolicy(() => + { + NameValueCollection col = new NameValueCollection(); + col.Add(StorageHttpConstants.QueryParams.QueryParamComp, StorageHttpConstants.CompConstants.Metadata); + + ResourceUriComponents uriComponents; + Uri uri = CreateRequestUri(null, col, out uriComponents); + HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Put, properties.Metadata); + _credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.NoContent) + { + result = true; + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + } + response.Close(); + } + } + catch (WebException we) + { + throw Utilities.TranslateWebException(we); + } + }); + + return result; + } + + public override QueueProperties GetProperties() + { + QueueProperties result = null; + + RetryPolicy(() => + { + NameValueCollection col = new NameValueCollection(); + col.Add(StorageHttpConstants.QueryParams.QueryParamComp, StorageHttpConstants.CompConstants.Metadata); + + ResourceUriComponents uriComponents; + Uri uri = CreateRequestUri(null, col, out uriComponents); + HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Get, null); + _credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.OK) + { + result = GetPropertiesFromHeaders(response); + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + } + response.Close(); + } + } + catch (WebException we) + { + throw Utilities.TranslateWebException(we); + } + }); + + return result; + } + + public override int ApproximateCount() + { + QueueProperties props = GetProperties(); + return props.ApproximateMessageCount; + } + + // getting and putting messages + + public override bool PutMessage(Message msg) + { + return PutMessage(msg, -1); + } + + public override bool PutMessage(Message msg, int timeToLiveInSeconds) + { + if (timeToLiveInSeconds < -1) + { + throw new ArgumentException("ttl parameter must be equal or larger than 0."); + } + else if (timeToLiveInSeconds > Message.MaxTimeToLive) + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, + "timeToLiveHours parameter must be smaller or equal than {0}, which is 7 days in hours.", Message.MaxTimeToLive)); + } + if (msg == null || msg.ContentAsBytes() == null) + { + throw new ArgumentNullException("msg"); + } + if (Convert.ToBase64String(msg.ContentAsBytes()).Length > Message.MaxMessageSize) + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Messages cannot be larger than {0} bytes.", Message.MaxMessageSize)); + } + + bool result = false; + + RetryPolicy(() => + { + NameValueCollection col = new NameValueCollection(); + if (timeToLiveInSeconds != -1) + { + col.Add(StorageHttpConstants.RequestParams.MessageTtl, timeToLiveInSeconds.ToString(CultureInfo.InvariantCulture)); + } + + ResourceUriComponents uriComponents; + Uri uri = CreateRequestUri(StorageHttpConstants.RequestParams.Messages, col, out uriComponents); + HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Post, null); + int len; + byte[] body = msg.GetContentXMLRepresentation(out len); + request.ContentLength = len; + _credentials.SignRequest(request, uriComponents); + + try + { + using (Stream requestStream = request.GetRequestStream()) + { + requestStream.Write(body, 0, body.Length); + requestStream.Close(); + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.Created) + { + result = true; + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + } + response.Close(); + } + } + } + catch (WebException we) + { + throw Utilities.TranslateWebException(we); + } + }); + + return result; + } + + public override Message GetMessage() + { + IEnumerable result = GetMessages(1); + if (result == null || result.Count() == 0) + { + return null; + } + return result.First(); + } + + public override Message GetMessage(int visibilityTimeoutInSeconds) + { + IEnumerable result = GetMessages(1, visibilityTimeoutInSeconds); + if (result == null || result.Count() == 0) + { + return null; + } + return result.First(); + } + + public override IEnumerable GetMessages(int numberOfMessages) + { + return GetMessages(numberOfMessages, -1); + } + + public override IEnumerable GetMessages(int numberOfMessages, int visibilityTimeout) + { + return InternalGet(numberOfMessages, visibilityTimeout, false); + } + + public override Message PeekMessage() + { + IEnumerable result = PeekMessages(1); + if (result == null || result.Count() == 0) + { + return null; + } + return result.First(); + } + + public override IEnumerable PeekMessages(int numberOfMessages) + { + return InternalGet(numberOfMessages, -1, true); + } + + // deleting messages + public override bool DeleteMessage(Message msg) + { + if (msg.PopReceipt == null) + { + throw new ArgumentException("No PopReceipt for the given message!"); + } + + bool result = false; + + RetryPolicy(() => + { + NameValueCollection col = new NameValueCollection(); + col.Add(StorageHttpConstants.RequestParams.PopReceipt, msg.PopReceipt.ToString()); + + ResourceUriComponents uriComponents; + Uri uri = CreateRequestUri(StorageHttpConstants.RequestParams.Messages + "/" + msg.Id, col, out uriComponents); + HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Delete, null); + _credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.NoContent) + { + result = true; + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + } + response.Close(); + } + + } + catch (WebException we) + { + throw Utilities.TranslateWebException(we); + } + }); + + return result; + } + + public override bool Clear() + { + bool result = false; + + RetryPolicy(() => + { + NameValueCollection col = new NameValueCollection(); + + ResourceUriComponents uriComponents; + Uri uri = CreateRequestUri(StorageHttpConstants.RequestParams.Messages, col, out uriComponents); + HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Delete, null); + _credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.NoContent) + { + result = true; + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + } + response.Close(); + } + + } + catch (WebException we) + { + throw Utilities.TranslateWebException(we); + } + }); + + return result; + } + + // automatic receiving of messages + public override int PollInterval + { + get + { + return _pollInterval; + } + set + { + if (value < 0) + { + throw new ArgumentException("The send threshold must be a positive value."); + } + if (_run) + { + throw new ArgumentException("You cannot set the poll interval while the receive thread is running"); + } + _pollInterval = value; + } + } + + private AutoResetEvent _evStarted; + private AutoResetEvent _evStopped; + private AutoResetEvent _evQuit; + private bool _run; + private Thread _receiveThread; + private int _internalPollInterval; + + + private void PeriodicReceive() + { + Message msg; + + _evStarted.Set(); + _internalPollInterval = PollInterval; + while (!_evQuit.WaitOne(_internalPollInterval, false)) + { + // time is up, so we get the message and continue + msg = GetMessage(); + if (msg != null) + { + MessageReceived(this, new MessageReceivedEventArgs(msg)); + // continue receiving fast until we get no message + _internalPollInterval = 10; + } + else + { + // we got no message, so we can fall back to the normal speed + _internalPollInterval = PollInterval; + } + } + _evStopped.Set(); + } + + public override bool StartReceiving() + { + lock (this) + { + if (_run) + { + return true; + } + _run = true; + } + if (_evStarted == null) { + _evStarted = new AutoResetEvent(false); + } + if (_evStopped == null) { + _evStopped = new AutoResetEvent(false); + } + if (_evQuit == null) { + _evQuit = new AutoResetEvent(false); + } + _receiveThread = new Thread(new ThreadStart(this.PeriodicReceive)); + _receiveThread.Start(); + if (!_evStarted.WaitOne(10000, false)) + { + _receiveThread.Abort(); + CloseEvents(); + _run = false; + return false; + } + return true; + } + + public override void StopReceiving() + { + _evQuit.Set(); + if (!_evStopped.WaitOne(10000, false)) + { + _receiveThread.Abort(); + } + CloseEvents(); + _run = false; + } + + private void CloseEvents() + { + if (_evStarted != null) + { + _evStarted.Close(); + } + if (_evStopped != null) + { + _evStopped.Close(); + } + if (_evQuit != null) + { + _evQuit.Close(); + } + } + + public override event MessageReceivedEventHandler MessageReceived; + + #endregion + + #region Helper methods + + private static QueueProperties GetPropertiesFromHeaders(HttpWebResponse response) + { + QueueProperties properties = new QueueProperties(); + int prefixLength = StorageHttpConstants.HeaderNames.PrefixForMetadata.Length; + foreach (string key in response.Headers.AllKeys) + { + if (key.Equals(StorageHttpConstants.HeaderNames.ApproximateMessagesCount, StringComparison.OrdinalIgnoreCase)) + { + properties.ApproximateMessageCount = Convert.ToInt32(response.Headers[key], CultureInfo.InvariantCulture); + } + else if (key.StartsWith(StorageHttpConstants.HeaderNames.PrefixForMetadata, StringComparison.OrdinalIgnoreCase)) + { + if (properties.Metadata == null) + { + properties.Metadata = new NameValueCollection(); + } + properties.Metadata.Add(key.Substring(prefixLength), response.Headers[key]); + } + } + return properties; + } + + + private IEnumerable InternalGet(int numberOfMessages, int visibilityTimeout, bool peekOnly) + { + if (peekOnly && visibilityTimeout != -1) + { + throw new ArgumentException("A peek operation does not change the visibility of messages", "visibilityTimeout"); + } + if (numberOfMessages < 1) + { + throw new ArgumentException("numberOfMessages must be a positive integer", "numberOfMessages"); + } + if (visibilityTimeout < -1) + { + throw new ArgumentException("Visibility Timeout must be 0 or a positive integer", "visibilityTimeout"); + } + + IEnumerable result = null; + + RetryPolicy(() => + { + NameValueCollection col = new NameValueCollection(); + col.Add(StorageHttpConstants.RequestParams.NumOfMessages, numberOfMessages.ToString(CultureInfo.InvariantCulture)); + if (visibilityTimeout != -1) + { + col.Add(StorageHttpConstants.RequestParams.VisibilityTimeout, + visibilityTimeout.ToString(CultureInfo.InvariantCulture)); + } + if (peekOnly) + { + col.Add(StorageHttpConstants.RequestParams.PeekOnly, + peekOnly.ToString(CultureInfo.InvariantCulture)); + } + + ResourceUriComponents uriComponents; + Uri uri = CreateRequestUri(StorageHttpConstants.RequestParams.Messages, col, out uriComponents); + HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Get, null); + _credentials.SignRequest(request, uriComponents); + + try + { + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + if (response.StatusCode == HttpStatusCode.OK) + { + using (Stream stream = response.GetResponseStream()) + { + result = GetMessageFromResponse(stream); + stream.Close(); + } + } + else + { + Utilities.ProcessUnexpectedStatusCode(response); + } + response.Close(); + } + + } + catch (WebException we) + { + throw Utilities.TranslateWebException(we); + } + }); + + + return result; + } + + private static IEnumerable GetMessageFromResponse(Stream stream) + { + List result = null; + Message msg; + + XmlDocument doc = new XmlDocument(); + try + { + doc.Load(stream); + } + catch (XmlException xe) + { + throw new StorageServerException(StorageErrorCode.ServiceBadResponse, + "The result of a get message opertation could not be parsed", default(HttpStatusCode), xe); + } + + XmlNodeList messagesNodes = doc.SelectNodes(XPathQueryHelper.MessagesListQuery); + if (messagesNodes.Count > 0) + { + result = new List(); + } + foreach (XmlNode messageNode in messagesNodes) + { + msg = new Message(); + msg.Id = messageNode.SelectSingleNode(StorageHttpConstants.XmlElementNames.MessageId).FirstChild.Value.Trim(); + Debug.Assert(msg.Id != null); + if (messageNode.SelectSingleNode(StorageHttpConstants.XmlElementNames.PopReceipt) != null) + { + msg.PopReceipt = messageNode.SelectSingleNode(StorageHttpConstants.XmlElementNames.PopReceipt).FirstChild.Value.Trim(); + Debug.Assert(msg.PopReceipt != null); + } + msg.InsertionTime = XPathQueryHelper.LoadSingleChildDateTimeValue(messageNode, StorageHttpConstants.XmlElementNames.InsertionTime, false).Value; + msg.ExpirationTime = XPathQueryHelper.LoadSingleChildDateTimeValue(messageNode, StorageHttpConstants.XmlElementNames.ExpirationTime, false).Value; + if (XPathQueryHelper.LoadSingleChildDateTimeValue(messageNode, StorageHttpConstants.XmlElementNames.TimeNextVisible, false) != null) + { + msg.TimeNextVisible = XPathQueryHelper.LoadSingleChildDateTimeValue(messageNode, StorageHttpConstants.XmlElementNames.TimeNextVisible, false).Value; + } + msg.SetContentFromBase64String(XPathQueryHelper.LoadSingleChildStringValue(messageNode, StorageHttpConstants.XmlElementNames.MessageText, false)); + result.Add(msg); + } + return result.AsEnumerable(); + } + + private HttpWebRequest CreateHttpRequest(Uri uri, string httpMethod) + { + return CreateHttpRequest(uri, httpMethod, null); + } + + private HttpWebRequest CreateHttpRequest(Uri uri, string httpMethod, NameValueCollection metadata) + { + HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri); + request.Timeout = (int)Timeout.TotalMilliseconds; + request.ReadWriteTimeout = (int)Timeout.TotalMilliseconds; + request.Method = httpMethod; + request.ContentLength = 0; + request.Headers.Add(StorageHttpConstants.HeaderNames.StorageDateTime, + Utilities.ConvertDateTimeToHttpString(DateTime.UtcNow)); + if (!String.IsNullOrEmpty(this.version)) + { + request.Headers.Add(StorageHttpConstants.HeaderNames.Version, this.version); + } + + if (metadata != null) + { + Utilities.AddMetadataHeaders(request, metadata); + } + return request; + } + + private Uri CreateRequestUri( + string uriSuffix, + NameValueCollection queryParameters, + out ResourceUriComponents uriComponents + ) + { + return CreateRequestUri(uriSuffix, queryParameters, false, out uriComponents); + } + + private Uri CreateRequestUri( + string uriSuffix, + NameValueCollection queryParameters, + bool accountOperation, + out ResourceUriComponents uriComponents + ) + { + return Utilities.CreateRequestUri( + this.AccountInfo.BaseUri, + this.AccountInfo.UsePathStyleUris, + this.AccountInfo.AccountName, + accountOperation ? null : this.Name, + uriSuffix, + this.Timeout, + queryParameters, + out uriComponents + ); + } + + #endregion + + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/StorageAccountInfo.cs b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/StorageAccountInfo.cs new file mode 100644 index 0000000..84fc62d --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/StorageAccountInfo.cs @@ -0,0 +1,628 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Globalization; +using System.Configuration; +using System.Collections.Specialized; +using System.Diagnostics; +using Microsoft.ServiceHosting.ServiceRuntime; + +namespace Microsoft.Samples.ServiceHosting.StorageClient +{ + /// + /// Objects of this class encapsulate information about a storage account and endpoint configuration. + /// Associated with a storage account is the account name, the base URI of the account and a shared key. + /// + public class StorageAccountInfo + { + + /// + /// The default configuration string in configuration files for setting the queue storage endpoint. + /// + public static readonly string DefaultQueueStorageEndpointConfigurationString = "QueueStorageEndpoint"; + + /// + /// The default configuration string in configuration files for setting the blob storage endpoint. + /// + public static readonly string DefaultBlobStorageEndpointConfigurationString = "BlobStorageEndpoint"; + + /// + /// The default configuration string in configuration files for setting the table storage endpoint. + /// + public static readonly string DefaultTableStorageEndpointConfigurationString = "TableStorageEndpoint"; + + /// + /// The default configuration string in configuration files for setting the storage account name. + /// + public static readonly string DefaultAccountNameConfigurationString = "AccountName"; + + /// + /// The default configuration string in configuration files for setting the shared key associated with a storage account. + /// + public static readonly string DefaultAccountSharedKeyConfigurationString = "AccountSharedKey"; + + /// + /// The default configuration string in configuration files for setting the UsePathStyleUris option. + /// + public static readonly string DefaultUsePathStyleUrisConfigurationString = "UsePathStyleUris"; + + /// + /// The default prefix string in application config and Web.config files to indicate that this setting should be looked up + /// in the fabric's configuration system. + /// + public static readonly string CSConfigStringPrefix = "CSConfigName"; + + private bool? _usePathStyleUris; + + /// + /// Constructor for creating account info objects. + /// + /// The account's base URI. + /// If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used. + /// If false host-style URIs (http://accountname.baseuri/containername/objectname) are used, + /// where baseuri is the URI of the service.. + /// If null, the choice is made automatically: path-style URIs if host name part of base URI is an + /// IP addres, host-style otherwise. + /// The account name. + /// The account's shared key. + public StorageAccountInfo(Uri baseUri, bool? usePathStyleUris, string accountName, string base64Key) + : this(baseUri, usePathStyleUris, accountName, base64Key, false) + { + } + + /// + /// Constructor for creating account info objects. + /// + /// The account's base URI. + /// If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used. + /// If false host-style URIs (http://accountname.baseuri/containername/objectname) are used, + /// where baseuri is the URI of the service. + /// If null, the choice is made automatically: path-style URIs if host name part of base URI is an + /// IP addres, host-style otherwise. + /// The account name. + /// The account's shared key. + /// true if it shall be allowed to only set parts of the StorageAccountInfo properties. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] + public StorageAccountInfo(Uri baseUri, bool? usePathStyleUris, string accountName, string base64Key, bool allowIncompleteSettings) + { + if (baseUri == null && !allowIncompleteSettings) + { + throw new ArgumentNullException("baseUri"); + } + if (string.IsNullOrEmpty(base64Key) && !allowIncompleteSettings) + { + throw new ArgumentNullException("base64Key"); + } + if (baseUri != null) + { + string newAccountName = null; + Uri newBaseUri = null; + if (IsStandardStorageEndpoint(baseUri, out newAccountName, out newBaseUri)) { + if (!string.IsNullOrEmpty(newAccountName) && + !string.IsNullOrEmpty(accountName) && + string.Compare(accountName, newAccountName, StringComparison.Ordinal) != 0) + { + throw new ArgumentException("The configured base URI " + baseUri.AbsoluteUri + " for the storage service is incorrect. " + + "The configured account name " + accountName + " must match the account name " + newAccountName + + " as specified in the storage service base URI." + + GeneralAccountConfigurationExceptionString); + } + Debug.Assert((newBaseUri == null && newAccountName == null) || (newBaseUri != null && newAccountName != null)); + if (newAccountName != null && newBaseUri != null) { + accountName = newAccountName; + baseUri = newBaseUri; + } + } + } + if (string.IsNullOrEmpty(accountName) && !allowIncompleteSettings) + { + throw new ArgumentNullException("accountName"); + } + if (!string.IsNullOrEmpty(accountName) && accountName.ToLowerInvariant() != accountName) { + throw new ArgumentException("The account name must not contain upper-case letters. " + + "The account name is the first part of the URL for accessing the storage services as presented to you by the portal or " + + "the predefined storage account name when using the development storage tool. " + + GeneralAccountConfigurationExceptionString); + } + + BaseUri = baseUri; + AccountName = accountName; + Base64Key = base64Key; + if (usePathStyleUris == null && baseUri == null && !allowIncompleteSettings) { + throw new ArgumentException("Cannot determine setting from empty URI."); + } + else if (usePathStyleUris == null) + { + if (baseUri != null) + { + _usePathStyleUris = Utilities.StringIsIPAddress(baseUri.Host); + } + else + { + _usePathStyleUris = null; + } + } + else + { + UsePathStyleUris = usePathStyleUris.Value; + } + } + + /// + /// The base URI of the account. + /// + public Uri BaseUri + { + get; + set; + } + + /// + /// The account name. + /// + public string AccountName + { + get; + set; + } + + /// + /// The account's key. + /// + public string Base64Key + { + get; + set; + } + + /// + /// If set, returns the UsePathStyleUris properties. If the property has not been explicitly set, + /// the implementation tries to derive the correct value from the base URI. + /// + public bool UsePathStyleUris + { + get + { + if (_usePathStyleUris == null) + { + if (BaseUri == null) + { + return false; + } + else + { + return Utilities.StringIsIPAddress(BaseUri.Host); + } + } + else + { + return _usePathStyleUris.Value; + } + } + set { + _usePathStyleUris = value; + } + } + + /// + /// Retrieves account settings for the queue service from the default settings. + /// + public static StorageAccountInfo GetDefaultQueueStorageAccountFromConfiguration(bool allowIncompleteSettings) + { + return GetAccountInfoFromConfiguration(DefaultQueueStorageEndpointConfigurationString, allowIncompleteSettings); + } + + /// + /// Retrieves account settings for the queue service from the default settings. + /// Throws an exception in case of incomplete settings. + /// + public static StorageAccountInfo GetDefaultQueueStorageAccountFromConfiguration() + { + return GetAccountInfoFromConfiguration(DefaultQueueStorageEndpointConfigurationString, false); + } + + /// + /// Retrieves account settings for the table service from the default settings. + /// + public static StorageAccountInfo GetDefaultTableStorageAccountFromConfiguration(bool allowIncompleteSettings) + { + return GetAccountInfoFromConfiguration(DefaultTableStorageEndpointConfigurationString, allowIncompleteSettings); + } + + /// + /// Retrieves account settings for the table service from the default settings. + /// Throws an exception in case of incomplete settings. + /// + public static StorageAccountInfo GetDefaultTableStorageAccountFromConfiguration() + { + return GetAccountInfoFromConfiguration(DefaultTableStorageEndpointConfigurationString, false); + } + + /// + /// Retrieves account settings for the blob service from the default settings. + /// + public static StorageAccountInfo GetDefaultBlobStorageAccountFromConfiguration(bool allowIncompleteSettings) + { + return GetAccountInfoFromConfiguration(DefaultBlobStorageEndpointConfigurationString, allowIncompleteSettings); + } + + /// + /// Retrieves account settings for the blob service from the default settings. + /// Throws an exception in case of incomplete settings. + /// + public static StorageAccountInfo GetDefaultBlobStorageAccountFromConfiguration() + { + return GetAccountInfoFromConfiguration(DefaultBlobStorageEndpointConfigurationString, false); + } + + /// + /// Gets settings from default configuration names except for the endpoint configuration string. + /// + public static StorageAccountInfo GetAccountInfoFromConfiguration(string endpointConfiguration, bool allowIncompleteSettings) + { + return GetAccountInfoFromConfiguration(DefaultAccountNameConfigurationString, + DefaultAccountSharedKeyConfigurationString, + endpointConfiguration, + DefaultUsePathStyleUrisConfigurationString, + allowIncompleteSettings); + } + + /// + /// Gets settings from default configuration names except for the endpoint configuration string. Throws an exception + /// in the case of incomplete settings. + /// + public static StorageAccountInfo GetAccountInfoFromConfiguration(string endpointConfiguration) + { + return GetAccountInfoFromConfiguration(DefaultAccountNameConfigurationString, + DefaultAccountSharedKeyConfigurationString, + endpointConfiguration, + DefaultUsePathStyleUrisConfigurationString, + false); + } + + /// + /// Gets a configuration setting from application settings in the Web.config or App.config file. + /// When running in a hosted environment, configuration settings are read from .cscfg + /// files. + /// + public static string GetConfigurationSetting(string configurationSetting, string defaultValue, bool throwIfNull) + { + if (string.IsNullOrEmpty(configurationSetting)) + { + throw new ArgumentException("configurationSetting cannot be empty or null", "configurationSetting"); + } + + string ret = null; + + // first, try to read from appsettings + ret = TryGetAppSetting(configurationSetting); + + // settings in the csc file overload settings in Web.config + if (RoleManager.IsRoleManagerRunning) + { + string cscRet = TryGetConfigurationSetting(configurationSetting); + if (!string.IsNullOrEmpty(cscRet)) + { + ret = cscRet; + } + + // if there is a csc config name in the app settings, this config name even overloads the + // setting we have right now + string refWebRet = TryGetAppSetting(StorageAccountInfo.CSConfigStringPrefix + configurationSetting); + if (!string.IsNullOrEmpty(refWebRet)) + { + cscRet = TryGetConfigurationSetting(refWebRet); + if (!string.IsNullOrEmpty(cscRet)) + { + ret = cscRet; + } + } + } + + // if we could not retrieve any configuration string set return value to the default value + if (string.IsNullOrEmpty(ret) && defaultValue != null) + { + ret = defaultValue; + } + + if (string.IsNullOrEmpty(ret) && throwIfNull) + { + throw new ConfigurationErrorsException( + string.Format(CultureInfo.InvariantCulture, "Cannot find configuration string {0}.", configurationSetting)); + } + return ret; + } + + /// + /// Retrieves account information settings from configuration settings. First, the implementation checks for + /// settings in an application config section of an app.config or Web.config file. These values are overwritten + /// if the same settings appear in a .csdef file. + /// The implementation also supports indirect settings. In this case, indirect settings overwrite all other settings. + /// + /// Configuration string for the account name. + /// Configuration string for the key. + /// Configuration string for the endpoint. + /// Configuration string for the path style. + /// If false, an exception is thrown if not all settings are available. + /// StorageAccountInfo object containing the retrieved settings. + public static StorageAccountInfo GetAccountInfoFromConfiguration(string accountNameConfiguration, + string accountSharedKeyConfiguration, + string endpointConfiguration, + string usePathStyleUrisConfiguration, + bool allowIncompleteSettings) + { + if (string.IsNullOrEmpty(endpointConfiguration)) + { + throw new ArgumentException("Endpoint configuration is missing", "endpointConfiguration"); + } + string endpoint = null; + string name = null; + string key = null; + string pathStyle = null; + + name = TryGetAppSetting(accountNameConfiguration); + key = TryGetAppSetting(accountSharedKeyConfiguration); + endpoint = TryGetAppSetting(endpointConfiguration); + pathStyle = TryGetAppSetting(usePathStyleUrisConfiguration); + + + // settings in the csc file overload settings in Web.config + if (RoleManager.IsRoleManagerRunning) + { + // get config settings from the csc file + string cscName = TryGetConfigurationSetting(accountNameConfiguration); + if (!string.IsNullOrEmpty(cscName)) + { + name = cscName; + } + string cscKey = TryGetConfigurationSetting(accountSharedKeyConfiguration); + if (!string.IsNullOrEmpty(cscKey)) + { + key = cscKey; + } + string cscEndpoint = TryGetConfigurationSetting(endpointConfiguration); + if (!string.IsNullOrEmpty(cscEndpoint)) + { + endpoint = cscEndpoint; + } + string cscPathStyle = TryGetConfigurationSetting(usePathStyleUrisConfiguration); + if (!string.IsNullOrEmpty(cscPathStyle)) + { + pathStyle = cscPathStyle; + } + + // the Web.config can have references to csc setting strings + // these count event stronger than the direct settings in the csc file + string refWebName = TryGetAppSetting(CSConfigStringPrefix + accountNameConfiguration); + if (!string.IsNullOrEmpty(refWebName)) + { + cscName = TryGetConfigurationSetting(refWebName); + if (!string.IsNullOrEmpty(cscName)) + { + name = cscName; + } + } + string refWebKey = TryGetAppSetting(CSConfigStringPrefix + accountSharedKeyConfiguration); + if (!string.IsNullOrEmpty(refWebKey)) + { + cscKey = TryGetConfigurationSetting(refWebKey); + if (!string.IsNullOrEmpty(cscKey)) + { + key = cscKey; + } + } + string refWebEndpoint = TryGetAppSetting(CSConfigStringPrefix + endpointConfiguration); + if (!string.IsNullOrEmpty(refWebEndpoint)) + { + cscEndpoint = TryGetConfigurationSetting(refWebEndpoint); + if (!string.IsNullOrEmpty(cscEndpoint)) + { + endpoint = cscEndpoint; + } + } + string refWebPathStyle = TryGetAppSetting(CSConfigStringPrefix + usePathStyleUrisConfiguration); + if (!string.IsNullOrEmpty(refWebPathStyle)) + { + cscPathStyle = TryGetConfigurationSetting(refWebPathStyle); + if (!string.IsNullOrEmpty(cscPathStyle)) + { + pathStyle = cscPathStyle; + } + } + } + + if (string.IsNullOrEmpty(key) && !allowIncompleteSettings) + { + throw new ArgumentException("No account key specified!"); + } + if (string.IsNullOrEmpty(endpoint) && !allowIncompleteSettings) + { + throw new ArgumentException("No endpoint specified!"); + } + if (string.IsNullOrEmpty(name)) + { + // in this case let's try to derive the account name from the Uri + string newAccountName = null; + Uri newBaseUri = null; + if (IsStandardStorageEndpoint(new Uri(endpoint), out newAccountName, out newBaseUri)) + { + Debug.Assert((newAccountName != null && newBaseUri != null) || (newAccountName == null && newBaseUri == null)); + if (newAccountName != null && newBaseUri != null) + { + endpoint = newBaseUri.AbsoluteUri; + name = newAccountName; + } + } + if (string.IsNullOrEmpty(name) && !allowIncompleteSettings) + { + throw new ArgumentException("No account name specified."); + } + } + + bool? usePathStyleUris = null; + if (!string.IsNullOrEmpty(pathStyle)) + { + bool b; + if (!bool.TryParse(pathStyle, out b)) + { + throw new ConfigurationErrorsException("Cannot parse value of setting UsePathStyleUris as a boolean"); + } + usePathStyleUris = b; + } + Uri tmpBaseUri = null; + if (!string.IsNullOrEmpty(endpoint)) + { + tmpBaseUri = new Uri(endpoint); + } + return new StorageAccountInfo(tmpBaseUri, usePathStyleUris, name, key, allowIncompleteSettings); + } + + + /// + /// Checks whether all essential properties of this object are set. Only then, the account info object + /// should be used in ohter APIs of this library. + /// + /// + public bool IsCompleteSetting() + { + return BaseUri != null && Base64Key != null && AccountName != null; + } + + /// + /// Checks whether this StorageAccountInfo object is complete in the sense that all properties are set. + /// + public void CheckComplete() + { + if (!IsCompleteSetting()) + { + throw new ConfigurationErrorsException("Account information incomplete!"); + } + } + + #region Private methods + + private static string TryGetConfigurationSetting(string configName) + { + string ret = null; + try + { + ret = RoleManager.GetConfigurationSetting(configName); + } + catch (RoleException) + { + return null; + } + return ret; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", + Justification = "Make sure that nothing prevents us to read from the fabric's configuration envrionment.")] + private static string TryGetAppSetting(string configName) + { + string ret = null; + try + { + ret = ConfigurationSettings.AppSettings[configName]; + } + // some exception happened when accessing the app settings section + // most likely this is because there is no app setting file + // we assume that this is because there is no app settings file; this is not an error + // and explicitly all exceptions are captured here + catch (Exception) + { + return null; + } + return ret; + } + + private static string GeneralAccountConfigurationExceptionString { + get { + return "If the portal defines http://test.blob.core.windows.net as your blob storage endpoint, the string \"test\" " + + "is your account name, and you can specify http://blob.core.windows.net as the BlobStorageEndpoint in your " + + "service's configuration file(s)."; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] + private static bool IsStandardStorageEndpoint(Uri baseUri, out string newAccountName, out Uri newBaseUri) { + if (baseUri == null) { + throw new ArgumentNullException("baseUri"); + } + + newAccountName = null; + newBaseUri = null; + + string host = baseUri.Host; + if (string.IsNullOrEmpty(host)) { + throw new ArgumentException("The host part of the Uri " + baseUri.AbsoluteUri + " must not be null or empty."); + } + if (host != host.ToLowerInvariant()) { + throw new ArgumentException("The specified host string " + host + " must not contain upper-case letters."); + } + + string suffix = null; + if (host.EndsWith(StorageHttpConstants.StandardPortalEndpoints.TableStorageEndpoint, StringComparison.Ordinal)) { + suffix = StorageHttpConstants.StandardPortalEndpoints.TableStorageEndpoint; + } + if (host.EndsWith(StorageHttpConstants.StandardPortalEndpoints.BlobStorageEndpoint, StringComparison.Ordinal)) + { + suffix = StorageHttpConstants.StandardPortalEndpoints.BlobStorageEndpoint; + } + if (host.EndsWith(StorageHttpConstants.StandardPortalEndpoints.QueueStorageEndpoint, StringComparison.Ordinal)) + { + suffix = StorageHttpConstants.StandardPortalEndpoints.QueueStorageEndpoint; + } + // a URL as presented on the portal was specified, lets find out whether it is in the correct format + if (suffix != null) { + int index = host.IndexOf(suffix, StringComparison.Ordinal); + Debug.Assert(index != -1); + if (index > 0) { + string first = host.Substring(0, index); + Debug.Assert(!string.IsNullOrEmpty(first)); + if (first[first.Length-1] != StorageHttpConstants.ConstChars.Dot[0]) { + return false; + } + first = first.Substring(0, first.Length - 1); + if (string.IsNullOrEmpty(first)) { + throw new ArgumentException("The configured base URI " + baseUri.AbsoluteUri + " for the storage service is incorrect. " + + GeneralAccountConfigurationExceptionString); + } + if (first.Contains(StorageHttpConstants.ConstChars.Dot)) { + throw new ArgumentException("The configured base URI " + baseUri.AbsoluteUri + " for the storage service is incorrect. " + + GeneralAccountConfigurationExceptionString); + } + newAccountName = first; + newBaseUri = new Uri(baseUri.Scheme + Uri.SchemeDelimiter + suffix + baseUri.PathAndQuery); + } + return true; + } + return false; + } + + + #endregion + + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/StorageClient.csproj b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/StorageClient.csproj new file mode 100644 index 0000000..5a4e0d1 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/StorageClient.csproj @@ -0,0 +1,67 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {C6F30C10-E1C2-4327-BB6B-3160B479CCA1} + Library + Microsoft.Samples.ServiceHosting.StorageClient + StorageClient + v3.5 + + + true + full + false + bin\Debug\ + TRACE;DEBUG;CODE_ANALYSIS + prompt + 4 + bin\Debug\StorageClient.XML + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + 3.5 + + + + 3.5 + + + + 3.5 + + + + False + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/TableStorage.cs b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/TableStorage.cs new file mode 100644 index 0000000..38573b3 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/TableStorage.cs @@ -0,0 +1,1503 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// +// This file contains helper classes for accessing the table storage service: +// - base classes for tables and table entities (table rows) containing the necessary +// partition key and row key values +// - methods for applying the table storage authentication scheme and for handling +// authentication in DataServiceContext objects +// - helper methods for creating, listing, and managing tables +// - a set of table storage constants +// - convenience methods for dealing with continuation tokens and paging +// - simple error handling helpers +// - retry semantics for table storage requests +// +// Examples of how to make use of the classes and methods in this file are available +// in the simple sample application contained in this solution. + + +using System; +using System.Collections.Generic; +using System.Data.Services.Client; +using System.Data.Services.Common; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text; + +// disable the generation of warnings for missing documentation elements for +// public classes/members in this file +#pragma warning disable 1591 + +namespace Microsoft.Samples.ServiceHosting.StorageClient +{ + + /// + /// Class representing some important table storage constants. + /// + public static class TableStorageConstants + { + /// + /// The maximum size of strings per property/column is 64 kB (that is 32k characters.) + /// Note: This constant is smaller for the development storage table service. + /// + public static readonly int MaxStringPropertySizeInBytes = 64 * 1024; + + + /// + /// One character in the standard UTF-16 character presentation is 2 bytes. + /// Note: This constant is smaller for the development storage table service. + /// + public static readonly int MaxStringPropertySizeInChars = MaxStringPropertySizeInBytes / 2; + + /// + /// We want to prevent users from the pitfall of mixing up Utc and local time. + /// Because of this we add some time to the minimum supported datetime. + /// As a result, there will be no error condition from the server even + /// if a user converts the minimum supported date time to a local time and + /// stores this in a DateTime field. + /// The local development storage support the SQL range of dates which is narrower than the + /// one for the table storage service and so we use that value here. + /// + public static readonly DateTime MinSupportedDateTime = DateTime.FromFileTime(0).ToUniversalTime().AddYears(200); + + //You can use this if you are programming against the real table storage service only but then your + //code will not work against the local development table storage. + //public static readonly DateTime MinSupportedDateTime = DateTime.FromFileTime(0).ToUniversalTime().AddDays(7); + + /// + /// Internal constant for querying tables. + /// + internal const string TablesName = "Tables"; + + /// + /// Internal constant for querying tables. + /// + internal const string TablesQuery = "/" + TablesName; + } + + + /// + /// API entry point for using structured storage. The underlying usage pattern is designed to be + /// similar to the one used in blob and queue services in this library. + /// Users create a TableStorage object by calling the static Create() method passing account credential + /// information to this method. The TableStorage object can then be used to create, delete and list tables. + /// There are two methods to get DataServiceContext objects that conform to the appropriate security scheme. + /// The first way is to call the GetDataServiceContext() method on TableStorage objects. The naming is again + /// chosen to conform to the convention in the other APIs for blob and queue services in this library. + /// This class can also be used as an adapter pattern. I.e., DataServiceContext objects can be created + /// independnt from a TableStorage object. Calling the Attach() method will make sure that the appropriate + /// security signing is used on these objects. This design was chosen to support various usage patterns that + /// might become necessary for autogenerated code. + /// + public class TableStorage + { + + /// + /// The default retry policy + /// + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", + Justification = "RetryPolicy is a non-mutable type")] + public static readonly RetryPolicy DefaultRetryPolicy = RetryPolicies.NoRetry; + + + /// + /// Creates a TableStorage service object. This object is the entry point into the table storage API. + /// + /// The base URI of the table storage service. + /// Type of URI scheme used. + /// The account name. + /// Base64 encoded version of the key. + /// + public static TableStorage Create(Uri baseUri, + bool? usePathStyleUris, + string accountName, + string base64Key + ) + { + //We create a StorageAccountInfo and then extract the properties of that object. + //This is because the constructor of StorageAccountInfo does normalization of BaseUri. + StorageAccountInfo info = new StorageAccountInfo( + baseUri, + usePathStyleUris, + accountName, + base64Key + ); + + return new TableStorage(info.BaseUri, info.UsePathStyleUris, info.AccountName, info.Base64Key); + } + + /// + /// Creates a TableStorage object. + /// + public static TableStorage Create(StorageAccountInfo info) + { + return new TableStorage(info.BaseUri, info.UsePathStyleUris, info.AccountName, info.Base64Key); + } + + /// + /// Infers a list of tables from a DataServiceContext-derived type and makes sure + /// those tables exist in the given service. The table endpoint information is retrieved from the + /// standard configuration settings. + /// + /// + /// Tables are inferred by finding all the public properties of type IQueryable<T> in + /// the provided type, where T is a type with an ID (in the case of table storage, this means it either + /// has a [DataServiceKey("PartitionKey", "RowKey")] attribute in the class, or derives from + /// the TableStorageEntity class included in this sample library (which in turn has that attribute). + /// + public static void CreateTablesFromModel(Type serviceContextType) + { + CreateTablesFromModel(serviceContextType, StorageAccountInfo.DefaultTableStorageEndpointConfigurationString); + } + + /// + /// Infers a list of tables from a DataServiceContext-derived type and makes sure + /// those tables exist in the given service. + /// + /// The DataServiceContext type from which the tables are inferred. + /// A configuration string that is used to determine the table storage endpoint. + public static void CreateTablesFromModel(Type serviceContextType, string endpointConfiguration) + { + StorageAccountInfo account = StorageAccountInfo.GetAccountInfoFromConfiguration(endpointConfiguration); + CreateTablesFromModel(serviceContextType, account); + } + + /// + /// Infers a list of tables from a DataServiceContext-derived type and makes sure + /// those tables exist in the given service. + /// + /// The type of the DataServiceContext. + /// An object containing information about the table storage endpoint to be used. + public static void CreateTablesFromModel(Type serviceContextType, StorageAccountInfo account) + { + TableStorage tableStorage = TableStorage.Create(account); + foreach (string tableName in DataServiceUtilities.EnumerateEntitySetNames(serviceContextType)) + { + tableStorage.TryCreateTable(tableName); + } + } + + /// + /// Creates a DataServiceContext object that takes care of implementing the table storage signing process. + /// + public TableStorageDataServiceContext GetDataServiceContext() + { + ResourceUriComponents uriComponents = new ResourceUriComponents(_accountName); + Uri uri = HttpRequestAccessor.ConstructResourceUri(_baseUri, uriComponents, _usePathStyleUris); + TableStorageDataServiceContext svc = new TableStorageDataServiceContext(uri, _accountName, _base64Key); + if (svc != null) + { + svc.RetryPolicy = this.RetryPolicy; + } + return svc; + } + + + /// + /// If the adaptor pattern with Attach() shall be used, this function can be used to generate the + /// table service base Uri depending on the path style syntax. + /// + static public Uri GetServiceBaseUri(Uri baseUri, bool usePathStyleUris, string accountName) + { + ResourceUriComponents uriComponents = new ResourceUriComponents(accountName); + Uri uri = HttpRequestAccessor.ConstructResourceUri(baseUri, uriComponents, usePathStyleUris); + return uri; + } + + /// + /// If the adaptor pattern with Attach() shall be used, this function can be used to generate the + /// table service base Uri depending on the path style syntax. + /// + static public Uri GetServiceBaseUri(StorageAccountInfo account) + { + return GetServiceBaseUri(account.BaseUri, account.UsePathStyleUris, account.AccountName); + } + + /// + /// If DataServiceContext objects are created at different places, this method can be called to configure the + /// DataServiceContext object to implement the required security scheme. + /// + public void Attach(DataServiceContext svc) + { + // this is an explicit way of dealing with situations where Attach() is called on objects that already + // have the necessary events hooked up to deal with table storage + // in the event Attach() is called multiple times on a normal DataServiceContext object, we make sure to + // not doing authentication twice in the sending event itself + if (svc is TableStorageDataServiceContext) + { + throw new ArgumentException("Cannot attach to a TableStorageDataServiceContext object. " + + "These objects already contain the functionality for accessing the table storage service."); + } + new ContextRef(this, svc); + } + + internal IEnumerable ListTableImpl(DataServiceQuery query) + { + IEnumerable ret = null; + + RetryPolicy(() => + { + try + { + ret = query.Execute(); + } + catch (InvalidOperationException e) + { + if (TableStorageHelpers.CanBeRetried(e)) + { + throw new TableRetryWrapperException(e); + } + throw; + } + }); + return ret; + } + + /// + /// Lists all the tables under this service's URL + /// + public IEnumerable ListTables() + { + DataServiceContext svc = GetDataServiceContext(); + string nextKey = null; + DataServiceQuery localQuery; + IEnumerable tmp; + svc.MergeOption = MergeOption.NoTracking; + IQueryable query = from t in svc.CreateQuery(TableStorageConstants.TablesName) + select t; + // result chunking + // if we would not do this, the default value of 1000 is used before query result pagination + // occurs + query = query.Take(StorageHttpConstants.ListingConstants.MaxTableListResults); + + + DataServiceQuery orig = query as DataServiceQuery; + try + { + tmp = ListTableImpl(orig); + } + catch (InvalidOperationException e) + { + HttpStatusCode status; + if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.NotFound) + { + yield break; + } + throw; + } + if (tmp == null) + { + yield break; + } + foreach (TableStorageTable table in tmp) + { + yield return table.TableName; + } + + QueryOperationResponse qor = tmp as QueryOperationResponse; + qor.Headers.TryGetValue(StorageHttpConstants.HeaderNames.PrefixForTableContinuation + + StorageHttpConstants.HeaderNames.NextTableName, + out nextKey); + + while (nextKey != null) + { + localQuery = orig; + localQuery = localQuery.AddQueryOption(StorageHttpConstants.HeaderNames.NextTableName, nextKey); + try + { + tmp = ListTableImpl(localQuery); + } + catch (InvalidOperationException e) + { + HttpStatusCode status; + if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.NotFound) + { + yield break; + } + throw; + } + if (tmp == null) + { + yield break; + } + foreach (TableStorageTable table in tmp) + { + yield return table.TableName; + } + qor = tmp as QueryOperationResponse; + qor.Headers.TryGetValue(StorageHttpConstants.HeaderNames.PrefixForTableContinuation + + StorageHttpConstants.HeaderNames.NextTableName, + out nextKey); + } + + } + + /// + /// Creates a new table in the service + /// + /// The name of the table to be created + public void CreateTable(string tableName) + { + ParameterValidator.CheckStringParameter(tableName, false, "tableName"); + if (!Utilities.IsValidTableName(tableName)) + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The specified table name \"{0}\" is not valid!" + + "Please choose a name that conforms to the naming conventions for tables!", tableName)); + } + RetryPolicy(() => + { + try + { + DataServiceContext svc = GetDataServiceContext(); + svc.AddObject(TableStorageConstants.TablesName, new TableStorageTable() { TableName = tableName }); + svc.SaveChanges(); + } + // exceptions are DataServiceClientException, DataServiceQueryException and DataServiceRequestException + // all of the above exceptions are InvalidOperationExceptions + catch (InvalidOperationException e) + { + HttpStatusCode status; + if (TableStorageHelpers.CanBeRetried(e, out status)) + { + if (status == HttpStatusCode.Conflict) + { + // don't retry in this case as this is an expected exception if the table exists + // just return the exception + throw; + } + else + { + throw new TableRetryWrapperException(e); + } + } + throw; + } + }); + } + + /// + /// Tries to create a table with the given name. + /// The main difference to the CreateTable method is that this function first queries the + /// table storage service whether the table already exists, before it tries to actually create + /// the table. The reason is that this + /// is more lightweight for the table storage service than always trying to create a table that + /// does already exist. Furthermore, as we expect that applications don't really randomly create + /// tables, the additional roundtrip that is required for creating the table is necessary only very + /// rarely. + /// + /// The name of the table. + /// True if the operation was completed successfully. False if the table already exists. + public bool TryCreateTable(string tableName) + { + ParameterValidator.CheckStringParameter(tableName, false, "tableName"); + if (DoesTableExist(tableName)) + { + return false; + } + try + { + CreateTable(tableName); + return true; + } + catch (InvalidOperationException e) + { + HttpStatusCode status; + if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.Conflict) + { + return false; + } + throw; + } + } + + /// + /// Checks whether a table with the same name already exists. + /// + /// The name of the table to check. + /// True iff the table already exists. + public bool DoesTableExist(string tableName) + { + ParameterValidator.CheckStringParameter(tableName, false, "tableName"); + bool tableExists = false; + + RetryPolicy(() => + { + try + { + DataServiceContext svc = GetDataServiceContext(); + svc.MergeOption = MergeOption.NoTracking; + IEnumerable query = from t in svc.CreateQuery(TableStorageConstants.TablesName) + where t.TableName == tableName + select t; + tableExists = false; + try + { + // the query contains the whole primary key + // thus, if the query succeeds we can be sure that the table exists + (query as DataServiceQuery).Execute(); + tableExists = true; + } + catch (DataServiceQueryException e) + { + HttpStatusCode s; + if (TableStorageHelpers.EvaluateException(e, out s) && s == HttpStatusCode.NotFound) + { + tableExists = false; + } + else + { + throw; + } + } + catch (NullReferenceException ne) + { + //This is a workaround for bug in DataServiceQuery.Execute. It throws a + //NullReferenceException instead of a DataServiceRequestException when it + //cannot connect to the the server. This workaround will be removed when + //the fix for this bug is released. + throw new DataServiceRequestException("Unable to connect to server.", ne); + } + } + catch (InvalidOperationException e) + { + HttpStatusCode status; + if (TableStorageHelpers.CanBeRetried(e, out status)) + { + throw new TableRetryWrapperException(e); + } + throw; + } + }); + return tableExists; + } + + /// + /// Deletes a table from the service. + /// + /// The name of the table to be deleted + public void DeleteTable(string tableName) + { + ParameterValidator.CheckStringParameter(tableName, false, "tableName"); + + RetryPolicy(() => + { + try + { + DataServiceContext svc = GetDataServiceContext(); + TableStorageTable table = new TableStorageTable() { TableName = tableName }; + svc.AttachTo(TableStorageConstants.TablesName, table); + svc.DeleteObject(table); + svc.SaveChanges(); + } + catch (InvalidOperationException e) + { + HttpStatusCode status; + if (TableStorageHelpers.CanBeRetried(e, out status)) + { + // we do this even thouh NoContent is currently not in the exceptions that + // are retried + if (status == HttpStatusCode.NoContent || status == HttpStatusCode.NotFound) + { + // don't retry in this case, just return the exception + throw; + } + else + { + throw new TableRetryWrapperException(e); + } + } + throw; + } + }); + } + + /// + /// Tries to delete the table with the given name. + /// + /// The name of the table to delete. + /// True if the table was successfully deleted. False if the table does not exists. + public bool TryDeleteTable(string tableName) + { + ParameterValidator.CheckStringParameter(tableName, false, "tableName"); + try + { + DeleteTable(tableName); + return true; + } + catch (InvalidOperationException e) + { + HttpStatusCode status; + if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.NotFound) + { + return false; + } + throw; + } + } + + /// + /// The retry policy used for retrying requests + /// + public RetryPolicy RetryPolicy + { + get; + set; + } + + /// + /// The base URI of the table storage service + /// + public Uri BaseUri + { + get + { + return this._baseUri; + } + } + + /// + /// The name of the storage account + /// + public string AccountName + { + get + { + return this._accountName; + } + } + + /// + /// Indicates whether to use/generate path-style or host-style URIs + /// + public bool UsePathStyleUris + { + get + { + return this._usePathStyleUris; + } + } + + /// + /// The base64 encoded version of the key. + /// + internal string Base64Key + { + get + { + return _base64Key; + } + } + + internal protected TableStorage(Uri baseUri, + bool? usePathStyleUris, + string accountName, + string base64Key + ) + { + this._baseUri = baseUri; + this._accountName = accountName; + this._base64Key = base64Key; + if (usePathStyleUris == null) + { + this._usePathStyleUris = Utilities.StringIsIPAddress(baseUri.Host); + } + else + { + this._usePathStyleUris = usePathStyleUris.Value; + } + RetryPolicy = DefaultRetryPolicy; + } + + private Uri _baseUri; + private bool _usePathStyleUris; + private string _accountName; + private string _base64Key; + } + + + + public static class TableStorageHelpers + { + + #region Error handling helpers + + /// + /// Checks whether the exception is or contains a DataServiceClientException and extracts the + /// returned http status code and extended error information. + /// + /// The exception from which to extract information + /// The Http status code for the exception + /// Extended error information including storage service specific + /// error code and error message + /// True if the exception is or contains a DataServiceClientException. + public static bool EvaluateException( + Exception exception, + out HttpStatusCode status, + out StorageExtendedErrorInformation extendedErrorInfo + ) + { + return EvaluateExceptionImpl(exception, out status, out extendedErrorInfo, true); + } + + /// + /// Checks whether the exception is or contains a DataServiceClientException and extracts the + /// returned http status code. + /// + /// The exception from which to extract information + /// The Http status code for the exception + /// True if the exception is or contains a DataServiceClientException. + public static bool EvaluateException( + Exception exception, + out HttpStatusCode status + ) + { + StorageExtendedErrorInformation extendedErrorInfo; + return EvaluateExceptionImpl(exception, out status, out extendedErrorInfo, false); + } + + private static bool EvaluateExceptionImpl( + Exception e, + out HttpStatusCode status, + out StorageExtendedErrorInformation extendedErrorInfo, + bool getExtendedErrors + ) + { + status = HttpStatusCode.Unused; + extendedErrorInfo = null; + while (e.InnerException != null) + { + e = e.InnerException; + + DataServiceClientException dsce = e as DataServiceClientException; + if (dsce != null) + { + status = (HttpStatusCode)dsce.StatusCode; + if (getExtendedErrors) + extendedErrorInfo = Utilities.GetExtendedErrorFromXmlMessage(dsce.Message); + return true; + } + } + return false; + } + + /// + /// Checks whether the exception is either a DataServiceClientException, a DataServiceQueryException or a + /// DataServiceRequestException. + /// + public static bool IsTableStorageException(Exception exception) + { + return ((exception is DataServiceClientException) || (exception is DataServiceQueryException) || (exception is DataServiceRequestException)); + } + + /// + /// Only certain classes of errors should be retried. This method evaluates an exception + /// and returns whether this class of exception can be retried. + /// + /// The exception to analyze. + /// The HttpStatusCode retrieved from the exception. + internal static bool CanBeRetried(InvalidOperationException e, out HttpStatusCode statusCode) + { + HttpStatusCode status; + + statusCode = HttpStatusCode.Unused; + if (EvaluateException(e, out status)) + { + statusCode = status; + if (status == HttpStatusCode.RequestTimeout || + // server error codes above 500 + status == HttpStatusCode.ServiceUnavailable || + status == HttpStatusCode.InternalServerError || + status == HttpStatusCode.BadGateway || + status == HttpStatusCode.GatewayTimeout) + { + return true; + } + } + return false; + } + + /// + /// Overload that does not retrun the HttpStatusCode. + /// + internal static bool CanBeRetried(InvalidOperationException e) + { + HttpStatusCode ignored; + return CanBeRetried(e, out ignored); + } + + #endregion + + #region Methods for checking properties to be inserted into a table + + /// + /// Checks whether the string can be inserted in a table storage table. Throws an exception if + /// this is not the case. + /// + /// + public static void CheckStringProperty(string propertyValue) + { + if (string.IsNullOrEmpty(propertyValue)) + { + throw new ArgumentException("The string cannot be null or empty!"); + } + if (propertyValue.Length > TableStorageConstants.MaxStringPropertySizeInChars) + { + throw new ArgumentException("The string cannot be longer than the maximum string property size."); + } + } + + /// + /// Checks whether the string can be inserted into a table storage table. + /// + public static bool ValidateStringProperty(string propertyValue) + { + if (string.IsNullOrEmpty(propertyValue)) + { + return false; + } + if (propertyValue.Length > TableStorageConstants.MaxStringPropertySizeInChars) + { + return false; + } + return true; + } + + #endregion + + } + + [DataServiceKey("TableName")] + public class TableStorageTable + { + private string _tableName; + + + /// + /// The table name. + /// + public string TableName + { + get + { + return this._tableName; + } + + set + { + ParameterValidator.CheckStringParameter(value, false, "TableName"); + this._tableName = value; + } + } + + public TableStorageTable() + { + } + + /// + /// Creates a table with the specified name. + /// + /// The name of the table. + public TableStorageTable(string name) + { + ParameterValidator.CheckStringParameter(name, false, "name"); + this.TableName = name; + } + + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + TableStorageTable rhs = obj as TableStorageTable; + + if (rhs == null) + { + return false; + } + + return (this.TableName == rhs.TableName); + } + + public override int GetHashCode() + { + return this.TableName.GetHashCode(); + } + } + + + /// + /// This class represents an entity (row) in a table in table storage. + /// + [CLSCompliant(false)] + [DataServiceKey("PartitionKey", "RowKey")] + public abstract class TableStorageEntity + { + + public DateTime Timestamp + { + get; + set; + } + + /// + /// The partition key of a table entity. The concatenation of the partition key + /// and row key must be unique per table. + /// + public virtual string PartitionKey + { + get; + set; + } + + /// + /// The row key of a table entity. + /// + public virtual string RowKey + { + get; + set; + } + + /// + /// Creates a TableStorageEntity object. + /// + protected TableStorageEntity(string partitionKey, string rowKey) + { + PartitionKey = partitionKey; + RowKey = rowKey; + } + + /// + /// Creates a TableStorageEntity object. + /// + protected TableStorageEntity() + { + } + + + /// + /// Compares to entities. + /// + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + TableStorageEntity rhs = obj as TableStorageEntity; + + if (rhs == null) + { + return false; + } + + return (this.PartitionKey == rhs.PartitionKey + && this.RowKey == rhs.RowKey); + } + + + /// + /// Computes a HashCode for this object. + /// + public override int GetHashCode() + { + if (PartitionKey == null) + { + return base.GetHashCode(); + } + if (!String.IsNullOrEmpty(this.RowKey)) + { + return this.PartitionKey.GetHashCode() ^ this.RowKey.GetHashCode(); + } + else + { + return this.PartitionKey.GetHashCode(); + } + } + } + + /// + /// This class can be used for handling continuation tokens in TableStorage. + /// + /// + public class TableStorageDataServiceQuery + { + + private DataServiceQuery _query; + + /// + /// Objects of this class can be created using this constructor directly or by + /// calling a factory method on the TableStorageDataServiceContext class + /// + public TableStorageDataServiceQuery(DataServiceQuery query) + : this(query, RetryPolicies.NoRetry) + { + } + + /// + /// Objects of this class can be created using this constructor directly or by + /// calling a factory method on the TableStorageDataServiceContext class + /// + public TableStorageDataServiceQuery(DataServiceQuery query, RetryPolicy policy) + { + if (query == null) + { + throw new ArgumentNullException("query"); + } + _query = query; + RetryPolicy = policy; + } + + /// + /// Gets the underlying normal query object. + /// + public DataServiceQuery Query + { + get + { + return _query; + } + set + { + _query = value; + } + } + + + /// + /// The retry policy used for retrying requests + /// + public RetryPolicy RetryPolicy + { + get; + set; + } + + /// + /// Normal Execute() on the query without retry. Just maps to _query.Execute(). + /// + /// + public IEnumerable Execute() + { + return ExecuteWithRetries(RetryPolicies.NoRetry); + } + + /// + /// Calling Execute() on the query with the current retry policy. + /// + /// An IEnumerable respresenting the results of the query. + public IEnumerable ExecuteWithRetries() + { + return ExecuteWithRetries(RetryPolicy); + } + + /// + /// Calling Execute() on the query with the current retry policy. + /// + /// The retry policy to be used for this request. + /// An IEnumerable respresenting the results of the query. + public IEnumerable ExecuteWithRetries(RetryPolicy retry) + { + IEnumerable ret = null; + if (retry == null) + { + throw new ArgumentNullException("retry"); + } + retry(() => + { + try + { + ret = _query.Execute(); + } + catch (InvalidOperationException e) + { + if (TableStorageHelpers.CanBeRetried(e)) + { + throw new TableRetryWrapperException(e); + } + throw; + } + }); + return ret; + } + + /// + /// Returns all results of the query and hides the complexity of continuation if + /// this is desired by a user. Users should be aware that this operation can return + /// many objects. Uses no retries. + /// Important: this function does not call Execute immediately. Instead, it calls Execute() on + /// the query only when the result is enumerated. This is a difference to the normal + /// Execute() and Execute() with retry method. + /// + /// An IEnumerable representing the results of the query. + public IEnumerable ExecuteAll() + { + return ExecuteAll(false); + } + + /// + /// Returns all results of the query and hides the complexity of continuation if + /// this is desired by a user. Users should be aware that this operation can return + /// many objects. This operation also uses the current retry policy. + /// Important: this function does not call Execute immediately. Instead, it calls Execute() on + /// the query only when the result is enumerated. This is a difference to the normal + /// Execute() and Execute() with retry method. + /// + /// An IEnumerable representing the results of the query. + public IEnumerable ExecuteAllWithRetries() + { + return ExecuteAll(true); + } + + /// + /// Returns all results of the query and hides the complexity of continuation if + /// this is desired by a user. Users should be aware that this operation can return + /// many objects. + /// Important: this function does not call Execute immediately. Instead, it calls Execute() on + /// the query only when the result is enumerated. This is a difference to the normal + /// Execute() and Execute() with retry method. + /// + /// Determines whether to use retries or not. + /// An IEnumerable representing the results of the query. + public IEnumerable ExecuteAll(bool withRetry) + { + IEnumerable res; + IEnumerable tmp; + string nextPartitionKey = null; + string nextRowKey = null; + DataServiceQuery localQuery; + + if (_query == null) + { + throw new ArgumentException("The local DataServiceQuery element cannot be null!"); + } + + if (withRetry) + { + res = ExecuteWithRetries(); + } + else + { + res = _query.Execute(); + } + if (res == null) + { + yield break; + } + foreach (TElement item in res) + { + yield return item; + } + QueryOperationResponse qor = res as QueryOperationResponse; + qor.Headers.TryGetValue(StorageHttpConstants.HeaderNames.PrefixForTableContinuation + + StorageHttpConstants.HeaderNames.NextPartitionKey, + out nextPartitionKey); + qor.Headers.TryGetValue(StorageHttpConstants.HeaderNames.PrefixForTableContinuation + + StorageHttpConstants.HeaderNames.NextRowKey, + out nextRowKey); + + while (nextPartitionKey != null || nextRowKey != null) + { + localQuery = _query; + if (nextPartitionKey != null) + { + localQuery = localQuery.AddQueryOption(StorageHttpConstants.HeaderNames.NextPartitionKey, nextPartitionKey); + } + if (nextRowKey != null) + { + localQuery = localQuery.AddQueryOption(StorageHttpConstants.HeaderNames.NextRowKey, nextRowKey); + } + if (withRetry) + { + TableStorageDataServiceQuery retryQuery = new TableStorageDataServiceQuery(localQuery, RetryPolicy); + tmp = retryQuery.ExecuteWithRetries(); + } + else + { + tmp = localQuery.Execute(); + } + if (tmp == null) + { + yield break; + } + foreach (TElement item in tmp) + { + yield return item; + } + qor = tmp as QueryOperationResponse; + qor.Headers.TryGetValue(StorageHttpConstants.HeaderNames.PrefixForTableContinuation + + StorageHttpConstants.HeaderNames.NextPartitionKey, + out nextPartitionKey); + qor.Headers.TryGetValue(StorageHttpConstants.HeaderNames.PrefixForTableContinuation + + StorageHttpConstants.HeaderNames.NextRowKey, + out nextRowKey); + } + } + } + + /// + /// The table storage-specific DataServiceContext class. It adds functionality for handling + /// the authentication process required by the table storage service. + /// + public class TableStorageDataServiceContext : DataServiceContext + { + private string _sharedKey; + private string _accountName; + + /// + /// Creates a DataServiceContext object and configures it so that it can be used with the table storage service. + /// + /// The root URI of the service. + /// The account name. + /// The shared key associated with this service. + internal TableStorageDataServiceContext(Uri serviceRoot, string accountName, string sharedKey) + : base(serviceRoot) + { + if (string.IsNullOrEmpty(accountName)) + { + throw new ArgumentNullException("accountName"); + } + if (string.IsNullOrEmpty(sharedKey)) + { + throw new ArgumentNullException("sharedKey"); + } + _sharedKey = sharedKey; + _accountName = accountName; + SendingRequest += new EventHandler(DataContextSendingRequest); + IgnoreMissingProperties = true; + + // we assume that this is the expected client behavior + MergeOption = MergeOption.PreserveChanges; + + RetryPolicy = TableStorage.DefaultRetryPolicy; + } + + /// + /// Creates a DataServiceContext object and configures it so that it can be used with the table storage service. + /// + /// A StorageAccountInfo object containing information about how to access the table storage service. + public TableStorageDataServiceContext(StorageAccountInfo account) + : this(TableStorage.GetServiceBaseUri(account), account.AccountName, account.Base64Key) { } + + + /// + /// Creates a DataServiceContext object and configures it so that it can be used with the table storage service. + /// Information on the table storage endpoint is retrieved by accessing configuration settings in the app config section + /// of a Web.config or app config file, or by accessing settings in cscfg files. + /// + public TableStorageDataServiceContext() + : this(StorageAccountInfo.GetAccountInfoFromConfiguration(StorageAccountInfo.DefaultTableStorageEndpointConfigurationString)) { } + + + /// + /// The retry policy used for retrying requests + /// + public RetryPolicy RetryPolicy + { + get; + set; + } + + /// + /// Calls the SaveChanges() method and applies retry semantics. + /// + public DataServiceResponse SaveChangesWithRetries() + { + DataServiceResponse ret = null; + RetryPolicy(() => + { + try + { + ret = SaveChanges(); + } + catch (InvalidOperationException e) + { + if (TableStorageHelpers.CanBeRetried(e)) + { + throw new TableRetryWrapperException(e); + } + throw; + } + }); + return ret; + } + + /// + /// Calls the SaveChanges() method and applies retry semantics. + /// + public DataServiceResponse SaveChangesWithRetries(SaveChangesOptions options) + { + DataServiceResponse ret = null; + RetryPolicy(() => + { + try + { + ret = SaveChanges(options); + } + catch (InvalidOperationException e) + { + if (TableStorageHelpers.CanBeRetried(e)) + { + throw new TableRetryWrapperException(e); + } + throw; + } + }); + return ret; + } + + /// + /// Callback method called whenever a request is sent to the table service. This + /// is where the signing of the request takes place. + /// + private void DataContextSendingRequest(object sender, SendingRequestEventArgs e) + { + HttpWebRequest request = e.Request as HttpWebRequest; + + // this setting can potentially result in very rare error conditions from HttpWebRequest + // using retry policies, these error conditions are dealt with + request.KeepAlive = true; + + // do the authentication + byte[] key; + SharedKeyCredentials credentials; + + Debug.Assert(_sharedKey != null); + key = Convert.FromBase64String(_sharedKey); + credentials = new SharedKeyCredentials(_accountName, key); + credentials.SignRequestForSharedKeyLite(request, new ResourceUriComponents(_accountName, _accountName)); + } + } + + #region Internal and private helpers + + /// + /// Helper class to avoid long-lived references to context objects + /// + /// + /// Need to be careful not to maintain a reference to the context + /// object from the auth adapter, since the adapter is probably + /// long-lived and the context is not. This intermediate helper + /// class is the one subscribing to context events, so when the + /// context can be collected then this will be collectable as well. + /// + internal class ContextRef + { + private TableStorage _adapter; + + public ContextRef(TableStorage adapter, DataServiceContext context) + { + if (adapter == null) + { + throw new ArgumentNullException("adapter"); + } + if (context == null) + { + throw new ArgumentNullException("context"); + } + this._adapter = adapter; + context.SendingRequest += this.HandleSendingRequest; + } + + private void HandleSendingRequest(object sender, SendingRequestEventArgs e) + { + HttpWebRequest request = e.Request as HttpWebRequest; + + // first, we have to make sure that the request is not signed twice + // this could happen if Attach() is called multiple times on the same DataServiceContext object + WebHeaderCollection col = request.Headers; + if (col != null) + { + foreach (string header in col) + { + if (string.Compare(header, StorageHttpConstants.HeaderNames.StorageDateTime, StringComparison.Ordinal) == 0) + { + return; + } + } + } + + // this setting can potentially result in very rare error conditions from HttpWebRequest + // using retry policies, these error conditions are dealt with + request.KeepAlive = true; + + // do the authentication + byte[] key; + SharedKeyCredentials credentials; + + Debug.Assert(_adapter.Base64Key != null); + key = Convert.FromBase64String(_adapter.Base64Key); + credentials = new SharedKeyCredentials(_adapter.AccountName, key); + credentials.SignRequestForSharedKeyLite(request, new ResourceUriComponents(_adapter.AccountName, _adapter.AccountName)); + } + } + + /// + /// The retry policies for blobs and queues deal with special StorageClient and StorageServer exceptions. + /// In case of tables, we don't want to return these exceptions but instead the normal data service + /// exception. This class serves as a simple wrapper for these exceptions, and indicates that we + /// need retries. + /// Data service exceptions are stored as inner exceptions. + /// + [Serializable] + public class TableRetryWrapperException : Exception + { + /// + /// Creates a TableRetryWrapperException object. + /// + public TableRetryWrapperException() + : base() + { + } + + /// + /// Creates a TableRetryWrapperException object. + /// + public TableRetryWrapperException(Exception innerException) + : base(string.Empty, innerException) + { + } + + /// + /// Creates a TableRetryWrapperException object. + /// + public TableRetryWrapperException(string message) + : base(message) + { + } + + /// + /// Creates a TableRetryWrapperException object. + /// + public TableRetryWrapperException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Creates a TableRetryWrapperException object. + /// + protected TableRetryWrapperException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + } + + internal static class ParameterValidator + { + internal static void CheckStringParameter(string s, bool canBeNullOrEmpty, string name) + { + if (string.IsNullOrEmpty(s) && !canBeNullOrEmpty) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, "The parameter {0} cannot be null or empty.", name)); + } + if (s.Length > TableStorageConstants.MaxStringPropertySizeInChars) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, + "The parameter {0} cannot be longer than {1} characters.", + name, TableStorageConstants.MaxStringPropertySizeInChars)); + } + } + } + + internal static class DataServiceUtilities + { + public static bool IsEntityType(Type t, Type contextType) + { + // ADO.NET data services convention: a type 't' is an entity if + // 1) 't' has at least one key column + // 2) there is a top level IQueryable property in the context where T is 't' or a supertype of 't' + // Non-primitive types that are not entity types become nested structures ("complex types" in EDM) + + if (!t.GetProperties().Any(p => IsKeyColumn(p))) return false; + + foreach (PropertyInfo pi in contextType.GetProperties()) + { + if (typeof(IQueryable).IsAssignableFrom(pi.PropertyType)) + { + if (pi.PropertyType.GetGenericArguments()[0].IsAssignableFrom(t)) + { + return true; + } + } + } + + return false; + } + + public static bool IsKeyColumn(PropertyInfo pi) + { + // Astoria convention: + // 1) try the DataServiceKey attribute + // 2) if not attribute, try ID + // 3) finally, try just ID + + object[] attribs = pi.DeclaringType.GetCustomAttributes(typeof(DataServiceKeyAttribute), true); + if (attribs != null && attribs.Length > 0) + { + Debug.Assert(attribs.Length == 1); + return ((DataServiceKeyAttribute)attribs[0]).KeyNames.Contains(pi.Name); + } + + if (pi.Name.Equals(pi.DeclaringType.Name + "ID", System.StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + if (pi.Name == "ID") + { + return true; + } + + return false; + } + + public static IEnumerable EnumerateEntitySetProperties(Type contextType) + { + foreach (PropertyInfo prop in contextType.GetProperties()) + { + if (typeof(IQueryable).IsAssignableFrom(prop.PropertyType) && + prop.PropertyType.GetGenericArguments().Length > 0 && + DataServiceUtilities.IsEntityType(prop.PropertyType.GetGenericArguments()[0], contextType)) + { + yield return prop; + } + } + } + + public static IEnumerable EnumerateEntitySetNames(Type contextType) + { + return EnumerateEntitySetProperties(contextType).Select(p => p.Name); + } + + } + + #endregion +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Debug/StorageClient.XML b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Debug/StorageClient.XML new file mode 100644 index 0000000..8adfba6 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Debug/StorageClient.XML @@ -0,0 +1,2002 @@ + + + + StorageClient + + + + + This type represents the different constituent parts that make up a resource Uri in the context of cloud services. + + + + + Construct a ResourceUriComponents object. + + The account name that should become part of the URI. + The container name (container, queue or table name) that should become part of the URI. + Remaining part of the URI. + + + + Construct a ResourceUriComponents object. + + The account name that should become part of the URI. + The container name (container, queue or table name) that should become part of the URI. + + + + Construct a ResourceUriComponents object. + + The account name that should become part of the URI. + + + + Construct a ResourceUriComponents object. + + + + + The account name in the URI. + + + + + This is really the first component (delimited by '/') after the account name. Since it happens to + be a container name in the context of all our storage services (containers in blob storage, + queues in the queue service and table names in table storage), it's named as ContainerName to make it more + readable at the cost of slightly being incorrectly named. + + + + + The remaining string in the URI. + + + + + Create a canonicalized string out of HTTP request header contents for signing + blob/queue requests with the Shared Authentication scheme. + + The uri address of the HTTP request. + Components of the Uri extracted out of the request. + The method of the HTTP request (GET/PUT, etc.). + The content type of the HTTP request. + The date of the HTTP request. + Should contain other headers of the HTTP request. + A canonicalized string of the HTTP request. + + + + Canonicalize HTTP header contents. + + An HttpWebRequest object. + Components of the Uri extracted out of the request. + The canonicalized string of the given HTTP request's header. + + + + Creates a standard datetime string for the shared key lite authentication scheme. + + DateTime value to convert to a string in the expected format. + + + + + An internal class that stores the canonicalized string version of an HTTP request. + + + + + Constructor for the class. + + The first canonicalized element to start the string with. + + + + Append additional canonicalized element to the string. + + An additional canonicalized element to append to the string. + + + + Property for the canonicalized string. + + + + + Use this class to extract various header values from Http requests. + + + + + A helper function for extracting HTTP header values from a NameValueCollection object. + + A NameValueCollection object that should contain HTTP header name-values pairs. + Name of the header that we want to get values of. + A array list of values for the header. The values are in the same order as they are stored in the NameValueCollection object. + + + + Constructs an URI given all its constituents + + + This is the service endpoint in case of path-style URIs and a host suffix in case of host-style URIs + IMPORTANT: This does NOT include the service name or account name + + Uri constituents + Indicates whether to construct a path-style Uri (true) or host-style URI (false) + Full uri + + + + Constructs a path-style resource URI given all its constituents + + + + + Constructs a host-style resource URI given all its constituents + + + + + Given the host suffix part, service name and account name, this method constructs the account Uri + + + + + Objects of this class contain the credentials (name and key) of a storage account. + + + + + Create a SharedKeyCredentials object given an account name and a shared key. + + + + + Signs the request appropriately to make it an authenticated request. + Note that this method takes the URI components as decoding the URI components requires the knowledge + of whether the URI is in path-style or host-style and a host-suffix if it's host-style. + + + + + Signs requests using the SharedKeyLite authentication scheme with is used for the table storage service. + + + + + This is the default content-type xStore uses when no content type is specified + + + + + When transmitting a blob that is larger than this constant, this library automatically + transmits the blob as individual blocks. I.e., the blob is (1) partitioned + into separate parts (these parts are called blocks) and then (2) each of the blocks is + transmitted separately. + The maximum size of this constant as supported by the real blob storage service is currently + 64 MB; the development storage tool currently restricts this value to 2 MB. + Setting this constant can have a significant impact on the performance for uploading or + downloading blobs. + As a general guideline: If you run in a reliable environment increase this constant to reduce + the amount of roundtrips. In an unreliable environment keep this constant low to reduce the + amount of data that needs to be retransmitted in case of connection failures. + + + + + The size of a single block when transmitting a blob that is larger than the + MaximumBlobSizeBeforeTransmittingAsBlocks constant (see above). + The maximum size of this constant is currently 4 MB; the development storage + tool currently restricts this value to 1 MB. + Setting this constant can have a significant impact on the performance for uploading or + downloading blobs. + As a general guideline: If you run in a reliable environment increase this constant to reduce + the amount of roundtrips. In an unreliable environment keep this constant low to reduce the + amount of data that needs to be retransmitted in case of connection failures. + + + + + Contains regular expressions for checking whether container and table names conform + to the rules of the storage REST protocols. + + + + + Container or queue names that match against this regular expression are valid. + + + + + Table names that match against this regular expression are valid. + + + + + Converts the date time to a valid string form as per HTTP standards + + + + + Parse a string having the date time information in acceptable formats according to HTTP standards + + + + + Copies from one stream to another + + The stream to copy from + The stream to copy to + + + + Error codes that can be returned by the storage service or the client library. + These are divided into server errors and client errors depending on which side + the error can be attributed to. + + + + + The base class for storage service exceptions + + + + + Initializes a new instance of the class with + serialized data. + + The object that contains serialized object + data about the exception being thrown + The object that contains contextual information + about the source or destionation. + + + + Sets the object with additional exception information + + The object that holds the + serialized object data. + The object that contains contextual information + about the source or destionation. + + + + The Http status code returned by the storage service + + + + + The specific error code returned by the storage service + + + + + + + + + + Server exceptions are those due to server side problems. + These may be transient and requests resulting in such exceptions + can be retried with the same parameters. + + + + + Initializes a new instance of the class with + serialized data. + + The object that contains serialized object + data about the exception being thrown + The object that contains contextual information + about the source or destionation. + + + + Client side exceptions are due to incorrect parameters to the request. + These requests should not be retried with the same parameters + + + + + Initializes a new instance of the class with + serialized data. + + The object that contains serialized object + data about the exception being thrown + The object that contains contextual information + about the source or destionation. + + + + Error code strings that are common to all storage services + + + + + Error code strings that are specific to blob service + + + + + Error code strings that are specific to queue service + + + + + Error code strings that are specific to queue service + + public static class TableErrorCodeStrings + + + + The entry point of the blob storage API + + + + + Factory method for BlobStorage + + The base URI of the blob storage service + If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used. + If false host-style URIs (http://accountname.baseuri/containername/objectname) are used, + where baseuri is the URI of the service. + If null, the choice is made automatically: path-style URIs if host name part of base URI is an + IP addres, host-style otherwise. + The name of the storage account + Authentication key used for signing requests + A newly created BlobStorage instance + + + + Factory method for BlobStorage + + Account information + A newly created BlobStorage instance + + + + Get a reference to a newly created BlobContainer object. + This method does not make any calls to the storage service. + + The name of the container + A reference to a newly created BlobContainer object + + + + Lists the containers within the account. + + A list of containers + + + + The default timeout + + + + + The default retry policy + + + + + The time out for each request to the storage service. + + + + + The retry policy used for retrying requests + + + + + The base URI of the blob storage service + + + + + The name of the storage account + + + + + Indicates whether to use/generate path-style or host-style URIs + + + + + Get a reference to a BlobContainer object with the given name. + This method does not make any calls to the storage service. + + The name of the container + A reference to a newly created BlobContainer object + + + + Lists the containers within the account. + + A list of containers + + + + The blob container class. + Used to access and enumerate blobs in the container. + Storage key credentials are needed to access private blobs but not for public blobs. + + + + + Use this constructor to access private blobs. + + The base Uri for the storage endpoint + Name of the storage account + Name of the container + + + + Use this constructor to access private blobs. + + The base Uri for the storage endpoint + + If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used and if false + host-style URIs (http://accountname.baseuri/containername/objectname) are used, where baseuri is the + URI of the service + + Name of the storage account + Name of the container + Date of last modification + + + + Create the container if it does not exist. + The container is created with private access control and no metadata. + + true if the container was created. false if the container already exists + + + + Create the container with the specified metadata and access control if it does not exist + + The metadata for the container. Can be null to indicate no metadata + The access control (public or private) with which to create the container + true if the container was created. false if the container already exists + + + + Check if the blob container exists + + true if the container exists, false otherwise. + + + + Get the properties for the container if it exists. + + The properties for the container if it exists, null otherwise + + + + Get the access control permissions associated with the container. + + + + + + Set the access control permissions associated with the container. + + The permission to set + + + + Deletes the current container. + + + + + Check if the blob container exists + + Name of the BLOB. + true if the blob exists, false otherwise. + + + + Create a new blob or overwrite an existing blob. + + The properties of the blob + The contents of the blob + Should this request overwrite an existing blob ? + true if the blob was created. false if the blob already exists and was set to false + The LastModifiedTime property of is set as a result of this call. + This method also has an effect on the ETag values that are managed by the service. + + + + Updates an existing blob if it has not been modified since the specified time which is typically + the last modified time of the blob when you retrieved it. + Use this method to implement optimistic concurrency by avoiding clobbering changes to the blob + made by another writer. + + The properties of the blob. This object should be one previously + obtained from a call to GetBlob or GetBlobProperties and have its LastModifiedTime property set. + The contents of the blob. The contents of the blob should be readable + true if the blob was updated. false if the blob has changed since the last time + The LastModifiedTime property of is set as a result of this call. + This method also has an effect on the ETag values that are managed by the service if the update was + successful. + + + + Get the blob contents and properties if the blob exists + + The name of the blob + Object in which the contents are returned. + This object should contain a writable stream or should be a default constructed object. + Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller pieces in case of failure. + The properties of the blob if the blob exists. + + + + Gets the blob contents and properties if the blob has not been modified since the time specified. + Use this method if you have cached the contents of a blob and want to avoid retrieving the blob + if it has not changed since the last time you retrieved it. + + The properties of the blob obtained from an earlier call to GetBlob. This + parameter is updated by the call if the blob has been modified + Contains the stream to which the contents of the blob are written if it has been + modified + Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller pieces in case of failure. + true if the blob has been modified, false otherwise + + + + Get the properties of the blob if it exists. + This method is also the simplest way to check if a blob exists. + + The name of the blob + The properties of the blob if it exists. null otherwise. + The properties for the contents of the blob are not set + + + + Set the metadata of an existing blob. + + The blob properties object whose metadata is to be updated + + + + Set the metadata of an existing blob if it has not been modified since it was last retrieved. + + The blob properties object whose metadata is to be updated. + Typically obtained by a previous call to GetBlob or GetBlobProperties + true if the blob metadata was updated. false if it was not updated because the blob + has been modified + + + + Delete a blob with the given name + + The name of the blob + true if the blob exists and was successfully deleted, false if the blob does not exist + + + + Delete a blob with the given name if the blob has not been modified since it was last obtained. + Use this method for optimistic concurrency to avoid deleting a blob that has been modified since + the last time you retrieved it + + A blob object (typically previously obtained from a GetBlob call) + This out parameter is set to true if the blob was not deleted because + it was modified + true if the blob exists and was successfully deleted, false if the blob does not exist or was + not deleted because the blob was modified. + + + + Enumerates all blobs with a given prefix. + + + If true common prefixes with "/" as seperator + The list of blob properties and common prefixes + + + + The time out for each request to the storage service. + + + + + The retry policy used for retrying requests + + + + + The base URI of the blob storage service + + + + + The name of the storage account + + + + + The name of the blob container. + + + + + Indicates whether to use/generate path-style or host-style URIs + + + + + The URI of the container + + + + + The timestamp for last modification of container. + + + + + Create the container with the specified access control if it does not exist + + The metadata for the container. Can be null to indicate no metadata + The access control (public or private) with which to create the container + true if the container was created. false if the container already exists + + + + Get the properties for the container if it exists. + + The metadata for the container if it exists, null otherwise + + + + Get the access control permissions associated with the container. + + + + + + Get the access control permissions associated with the container. + + + + + + Create a new blob or overwrite an existing blob. + + + The properties of the blob + The contents of the blob + Should this request overwrite an existing blob ? + true if the blob was created. false if the blob already exists and was set to false + The LastModifiedTime property of is set as a result of this call + + + + Updates an existing blob if it has not been modified since the specified time which is typically + the last modified time of the blob when you retrieved it. + Use this method to implement optimistic concurrency by avoiding clobbering changes to the blob + made by another writer. + + The properties of the blob. This object should be one previously + obtained from a call to GetBlob or GetBlobProperties and have its LastModifiedTime property set. + The contents of the blob. The contents of the blob should be readable + true if the blob was updated. false if the blob has changed since the last time + The LastModifiedTime property of is set as a result of this call + + + + Get the blob contents and properties if the blob exisits + + The name of the blob + Object in which the contents are returned. + This object should contain a writable stream or should be a default constructed object. + Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller piecs in case of failure. + The properties of the blob if the blob exists. + + + + Gets the blob contents and properties if the blob has not been modified since the time specified. + Use this method if you have cached the contents of a blob and want to avoid retrieving the blob + if it has not changed since the last time you retrieved it. + + The properties of the blob obtained from an earlier call to GetBlob. This + parameter is updated by the call if the blob has been modified + Contains the stream to which the contents of the blob are written if it has been + modified + Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller piecs in case of failure. + true if the blob has been modified, false otherwise + + + + Get the properties of the blob if it exists. + This method is also the simplest way to check if a blob exists. + + The name of the blob + The properties of the blob if it exists. null otherwise. + The properties for the contents of the blob are not set + + + + Set the metadata of an existing blob. + + The blob properties object whose metadata is to be updated + + + + Set the metadata of an existing blob if it has not been modified since it was last retrieved. + + The blob properties object whose metadata is to be updated. + Typically obtained by a previous call to GetBlob or GetBlobProperties + + + + Delete a blob with the given name + + The name of the blob + true if the blob exists and was successfully deleted, false if the blob does not exist + + + + Delete a blob with the given name if the blob has not been modified since it was last obtained. + Use this method for optimistic concurrency to avoid deleting a blob that has been modified since + the last time you retrieved it + + A blob object (typically previously obtained from a GetBlob call) + This out parameter is set to true if the blob was not deleted because + it was modified + true if the blob exists and was successfully deleted, false if the blob does not exist or was + not deleted because the blob was modified. + + + + Enumerates all blobs with a given prefix. + + + If true common prefixes with "/" as seperator + The list of blob properties and common prefixes + + + + Uploads a blob in chunks. + + + + + + + + + + Helper method used for getting blobs, ranges of blobs and blob properties. + + Name of the blob + The output stream to write blob data to. Can be null if only retrieving blob properties + The If-None-Match header. Used to avoid downloading blob data if the blob has not changed + The If-Match header. Used to ensure that all chunks of the blob are of the same blob + The offset of the blob data to begin downloading from. Set to 0 to download all data. + The length of the blob data to download. Set to 0 to download all data + Query paramters to add to the request. + Whether the blob had been modfied with respect to the + + + + + Helper class for loading values from an XML segment + + + + + The entry point of the queue storage API + + + + + Factory method for QueueStorage + + The base URI of the queue service + If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used. + If false host-style URIs (http://accountname.baseuri/containername/objectname) are used, + where baseuri is the URI of the service. + If null, the choice is made automatically: path-style URIs if host name part of base URI is an + IP addres, host-style otherwise. + The name of the storage account + Authentication key used for signing requests + A newly created QueueStorage instance + + + + Get a reference to a Queue object with a specified name. This method does not make a call to + the queue service. + + The name of the queue + A newly created queue object + + + + Lists the queues within the account. + + A list of queues + + + + Lists the queues within the account that start with the given prefix. + + If prefix is null returns all queues. + A list of queues. + + + + The default timeout + + + + + The default retry policy + + + + + The time out for each request to the storage service. + + + + + The retry policy used for retrying requests + + + + + The base URI of the blob storage service + + + + + The name of the storage account + + + + + Indicates whether to use/generate path-style or host-style URIs + + + + + Objects of this class represent a single message in the queue. + + + + + The maximum message size in bytes. + + + + + The maximum amount of time a message is kept in the queue. Max value is 7 days. + Value is given in seconds. + + + + + This constructor is not publicly exposed. + + + + + Creates a message and initializes the content of the message to be the specified string. + + A string representing the contents of the message. + + + + Creates a message and given the specified byte contents. + In this implementation, regardless of whether an XML or binary data is passed into this + function, message contents are converted to base64 before passing the data to the queue service. + When calculating the size of the message, the size of the base64 encoding is thus the important + parameter. + + + + + + Returns the the contents of the message as a string. + + + + + Returns the content of the message as a byte array + + + + + When calling the Get() operation on a queue, the content of messages + returned in the REST protocol are represented as Base64-encoded strings. + This internal function transforms the Base64 representation into a byte array. + + The Base64-encoded string. + + + + Internal method used for creating the XML that becomes part of a REST request + + + + + A unique ID of the message as returned from queue operations. + + + + + When a message is retrieved from a queue, a PopReceipt is returned. The PopReceipt is used when + deleting a message from the queue. + + + + + The point in time when the message was put into the queue. + + + + + A message's expiration time. + + + + + The point in time when a message becomes visible again after a Get() operation was called + that returned the message. + + + + + Queues in the storage client library expose a functionality for listening for incoming messages. + If a message is put into a queue, a corresponding event is issued and this delegate is called. This functionality + is implemented internally in this library by periodically polling for incoming messages. + + The queue that has received a new event. + The event argument containing the message. + + + + The argument class for the MessageReceived event. + + + + + The message itself. + + + + + Constructor for creating a message received argument. + + + + + + The message received by the queue. + + + + + The approximated amount of messages in the queue. + + + + + Metadata for the queue in the form of name-value pairs. + + + + + Objects of this class represent a queue in a user's storage account. + + + + + The name of the queue. + + + + + The user account this queue lives in. + + + + + This constructor is only called by subclasses. + + + + + Creates a queue in the specified storage account. + + true if a queue with the same name already exists. + true if the queue was successfully created. + + + + Creates a queue in the specified storage account. + + true if the queue was successfully created. + + + + Determines whether a queue with the same name already exists in an account. + + true if a queue with the same name already exists. + + + + Deletes the queue. The queue will be deleted regardless of whether there are messages in the + queue or not. + + true if the queue was successfully deleted. + + + + Sets the properties of a queue. + + The queue's properties to set. + true if the properties were successfully written to the queue. + + + + Retrieves the queue's properties. + + The queue's properties. + + + + Retrieves the approximate number of messages in a queue. + + The approximate number of messages in this queue. + + + + Puts a message in the queue. + + The message to store in the queue. + true if the message has been successfully enqueued. + + + + Puts a message in the queue. + + The message to store in the queue. + The time to live for the message in seconds. + true if the message has been successfully enqueued. + + + + Retrieves a message from the queue. + + The message retrieved or null if the queue is empty. + + + + Retrieves a message and sets its visibility timeout to the specified number of seconds. + + Visibility timeout of the message retrieved in seconds. + + + + + Tries to retrieve the given number of messages. + + Maximum number of messages to retrieve. + The list of messages retrieved. + + + + Tries to retrieve the given number of messages. + + Maximum number of messages to retrieve. + The visibility timeout of the retrieved messages in seconds. + The list of messages retrieved. + + + + Get a message from the queue but do not actually dequeue it. The message will remain visible + for other parties requesting messages. + + The message retrieved or null if there are no messages in the queue. + + + + Tries to get a copy of messages in the queue without actually dequeuing the messages. + The messages will remain visible in the queue. + + Maximum number of message to retrieve. + The list of messages retrieved. + + + + Deletes a message from the queue. + + The message to retrieve with a valid popreceipt. + true if the operation was successful. + + + + Delete all messages in a queue. + + true if all messages were deleted successfully. + + + + The default time interval between polling the queue for messages. + Polling is only enabled if the user has called StartReceiving(). + + + + + Starts the automatic reception of messages. + + true if the operation was successful. + + + + Stop the automatic reception of messages. + + + + + The name of the queue exposed as a public property. + + + + + The account info object this queue lives in -- exposed as an internal property. + + + + + Indicates whether to use/generate path-style or host-style URIs + + + + + The URI of the queue + + + + + The retry policy used for retrying requests; this is the retry policy of the + storage account where this queue was created + + + + + The timeout of requests. + + + + + The poll interval in milliseconds. If not explicitly set, this defaults to + the DefaultPollInterval. + + + + + The event users subscribe to in order to automatically receive messages + from a queue. + + + + + This delegate define the shape of a retry policy. A retry policy will invoke the given + as many times as it wants to in the face of + retriable StorageServerExceptions. + + The action to retry + + + + + Provides definitions for some standard retry policies. + + + + + Policy that does no retries i.e., it just invokes exactly once + + The action to retry + The return value of + + + + Policy that retries a specified number of times with a specified fixed time interval between retries + + The number of times to retry. Should be a non-negative number + The time interval between retries. Use TimeSpan.Zero to specify immediate + retries + + When is 0 and is + TimeSpan.Zero this policy is equivalent to the NoRetry policy + + + + Policy that retries a specified number of times with a randomized exponential backoff scheme + + The number of times to retry. Should be a non-negative number. + The multiplier in the exponential backoff scheme + + For this retry policy, the minimum amount of milliseconds between retries is given by the + StandardMinBackoff constant, and the maximum backoff is predefined by the StandardMaxBackoff constant. + Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff. + + + + Policy that retries a specified number of times with a randomized exponential backoff scheme + + The number of times to retry. Should be a non-negative number + The multiplier in the exponential backoff scheme + The minimum backoff interval + The maximum backoff interval + + For this retry policy, the minimum amount of milliseconds between retries is given by the + minBackoff parameter, and the maximum backoff is predefined by the maxBackoff parameter. + Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff. + + + + Access control for containers + + + + + The properties of a blob. + No member of this class makes a storage service request. + + + + + Construct a new BlobProperties object + + The name of the blob + + + + Name of the blob + + + + + URI of the blob + + + + + Content encoding of the blob if it set, null otherwise. + + + + + Content Type of the blob if it is set, null otherwise. + + + + + Content Language of the blob if it is set, null otherwise. + + + + + The length of the blob content, null otherwise. + + + + + Metadata for the blob in the form of name-value pairs. + + + + + The last modified time for the blob. + + + + + The ETag of the blob. This is an identifier assigned to the blob by the storage service + and is used to distinguish contents of two blobs (or versions of the same blob). + + + + + The properties of a container. + No member of this class makes a storage service request. + + + + + The contents of the Blob in various forms. + + + + + Construct a new BlobContents object from a stream. + + The stream to/from which blob contents are written/read. The + stream should be seekable in order for requests to be retried. + + + + Construct a new BlobContents object from a byte array. + + The byte array to/from which contents are written/read. + + + + Get the contents of a blob as a byte array. + + + + + Get the contents of a blob as a stream. + + + + + Class representing some important table storage constants. + + + + + Internal constant for querying tables. + + + + + Internal constant for querying tables. + + + + + The maximum size of strings per property/column is 64 kB (that is 32k characters.) + Note: This constant is smaller for the development storage table service. + + + + + One character in the standard UTF-16 character presentation is 2 bytes. + Note: This constant is smaller for the development storage table service. + + + + + We want to prevent users from the pitfall of mixing up Utc and local time. + Because of this we add some time to the minimum supported datetime. + As a result, there will be no error condition from the server even + if a user converts the minimum supported date time to a local time and + stores this in a DateTime field. + The local development storage support the SQL range of dates which is narrower than the + one for the table storage service and so we use that value here. + + + + + API entry point for using structured storage. The underlying usage pattern is designed to be + similar to the one used in blob and queue services in this library. + Users create a TableStorage object by calling the static Create() method passing account credential + information to this method. The TableStorage object can then be used to create, delete and list tables. + There are two methods to get DataServiceContext objects that conform to the appropriate security scheme. + The first way is to call the GetDataServiceContext() method on TableStorage objects. The naming is again + chosen to conform to the convention in the other APIs for blob and queue services in this library. + This class can also be used as an adapter pattern. I.e., DataServiceContext objects can be created + independnt from a TableStorage object. Calling the Attach() method will make sure that the appropriate + security signing is used on these objects. This design was chosen to support various usage patterns that + might become necessary for autogenerated code. + + + + + The default retry policy + + + + + Creates a TableStorage service object. This object is the entry point into the table storage API. + + The base URI of the table storage service. + Type of URI scheme used. + The account name. + Base64 encoded version of the key. + + + + + Creates a TableStorage object. + + + + + Infers a list of tables from a DataServiceContext-derived type and makes sure + those tables exist in the given service. The table endpoint information is retrieved from the + standard configuration settings. + + + Tables are inferred by finding all the public properties of type IQueryable<T> in + the provided type, where T is a type with an ID (in the case of table storage, this means it either + has a [DataServiceKey("PartitionKey", "RowKey")] attribute in the class, or derives from + the TableStorageEntity class included in this sample library (which in turn has that attribute). + + + + + Infers a list of tables from a DataServiceContext-derived type and makes sure + those tables exist in the given service. + + The DataServiceContext type from which the tables are inferred. + A configuration string that is used to determine the table storage endpoint. + + + + Infers a list of tables from a DataServiceContext-derived type and makes sure + those tables exist in the given service. + + The type of the DataServiceContext. + An object containing information about the table storage endpoint to be used. + + + + Creates a DataServiceContext object that takes care of implementing the table storage signing process. + + + + + If the adaptor pattern with Attach() shall be used, this function can be used to generate the + table service base Uri depending on the path style syntax. + + + + + If the adaptor pattern with Attach() shall be used, this function can be used to generate the + table service base Uri depending on the path style syntax. + + + + + If DataServiceContext objects are created at different places, this method can be called to configure the + DataServiceContext object to implement the required security scheme. + + + + + Lists all the tables under this service's URL + + + + + Creates a new table in the service + + The name of the table to be created + + + + Tries to create a table with the given name. + The main difference to the CreateTable method is that this function first queries the + table storage service whether the table already exists, before it tries to actually create + the table. The reason is that this + is more lightweight for the table storage service than always trying to create a table that + does already exist. Furthermore, as we expect that applications don't really randomly create + tables, the additional roundtrip that is required for creating the table is necessary only very + rarely. + + The name of the table. + True if the operation was completed successfully. False if the table already exists. + + + + Checks whether a table with the same name already exists. + + The name of the table to check. + True iff the table already exists. + + + + Deletes a table from the service. + + The name of the table to be deleted + + + + Tries to delete the table with the given name. + + The name of the table to delete. + True if the table was successfully deleted. False if the table does not exists. + + + + The retry policy used for retrying requests + + + + + The base URI of the table storage service + + + + + The name of the storage account + + + + + Indicates whether to use/generate path-style or host-style URIs + + + + + The base64 encoded version of the key. + + + + + Checks whether the exception is or contains a DataServiceClientException and extracts the + returned http status code and extended error information. + + The exception from which to extract information + The Http status code for the exception + Extended error information including storage service specific + error code and error message + True if the exception is or contains a DataServiceClientException. + + + + Checks whether the exception is or contains a DataServiceClientException and extracts the + returned http status code. + + The exception from which to extract information + The Http status code for the exception + True if the exception is or contains a DataServiceClientException. + + + + Checks whether the exception is either a DataServiceClientException, a DataServiceQueryException or a + DataServiceRequestException. + + + + + Only certain classes of errors should be retried. This method evaluates an exception + and returns whether this class of exception can be retried. + + The exception to analyze. + The HttpStatusCode retrieved from the exception. + + + + Overload that does not retrun the HttpStatusCode. + + + + + Checks whether the string can be inserted in a table storage table. Throws an exception if + this is not the case. + + + + + + Checks whether the string can be inserted into a table storage table. + + + + + Creates a table with the specified name. + + The name of the table. + + + + The table name. + + + + + This class represents an entity (row) in a table in table storage. + + + + + Creates a TableStorageEntity object. + + + + + Creates a TableStorageEntity object. + + + + + Compares to entities. + + + + + Computes a HashCode for this object. + + + + + The partition key of a table entity. The concatenation of the partition key + and row key must be unique per table. + + + + + The row key of a table entity. + + + + + This class can be used for handling continuation tokens in TableStorage. + + + + + + Objects of this class can be created using this constructor directly or by + calling a factory method on the TableStorageDataServiceContext class + + + + + Objects of this class can be created using this constructor directly or by + calling a factory method on the TableStorageDataServiceContext class + + + + + Normal Execute() on the query without retry. Just maps to _query.Execute(). + + + + + + Calling Execute() on the query with the current retry policy. + + An IEnumerable respresenting the results of the query. + + + + Calling Execute() on the query with the current retry policy. + + The retry policy to be used for this request. + An IEnumerable respresenting the results of the query. + + + + Returns all results of the query and hides the complexity of continuation if + this is desired by a user. Users should be aware that this operation can return + many objects. Uses no retries. + Important: this function does not call Execute immediately. Instead, it calls Execute() on + the query only when the result is enumerated. This is a difference to the normal + Execute() and Execute() with retry method. + + An IEnumerable representing the results of the query. + + + + Returns all results of the query and hides the complexity of continuation if + this is desired by a user. Users should be aware that this operation can return + many objects. This operation also uses the current retry policy. + Important: this function does not call Execute immediately. Instead, it calls Execute() on + the query only when the result is enumerated. This is a difference to the normal + Execute() and Execute() with retry method. + + An IEnumerable representing the results of the query. + + + + Returns all results of the query and hides the complexity of continuation if + this is desired by a user. Users should be aware that this operation can return + many objects. + Important: this function does not call Execute immediately. Instead, it calls Execute() on + the query only when the result is enumerated. This is a difference to the normal + Execute() and Execute() with retry method. + + Determines whether to use retries or not. + An IEnumerable representing the results of the query. + + + + Gets the underlying normal query object. + + + + + The retry policy used for retrying requests + + + + + The table storage-specific DataServiceContext class. It adds functionality for handling + the authentication process required by the table storage service. + + + + + Creates a DataServiceContext object and configures it so that it can be used with the table storage service. + + The root URI of the service. + The account name. + The shared key associated with this service. + + + + Creates a DataServiceContext object and configures it so that it can be used with the table storage service. + + A StorageAccountInfo object containing information about how to access the table storage service. + + + + Creates a DataServiceContext object and configures it so that it can be used with the table storage service. + Information on the table storage endpoint is retrieved by accessing configuration settings in the app config section + of a Web.config or app config file, or by accessing settings in cscfg files. + + + + + Calls the SaveChanges() method and applies retry semantics. + + + + + Calls the SaveChanges() method and applies retry semantics. + + + + + Callback method called whenever a request is sent to the table service. This + is where the signing of the request takes place. + + + + + The retry policy used for retrying requests + + + + + Helper class to avoid long-lived references to context objects + + + Need to be careful not to maintain a reference to the context + object from the auth adapter, since the adapter is probably + long-lived and the context is not. This intermediate helper + class is the one subscribing to context events, so when the + context can be collected then this will be collectable as well. + + + + + The retry policies for blobs and queues deal with special StorageClient and StorageServer exceptions. + In case of tables, we don't want to return these exceptions but instead the normal data service + exception. This class serves as a simple wrapper for these exceptions, and indicates that we + need retries. + Data service exceptions are stored as inner exceptions. + + + + + Creates a TableRetryWrapperException object. + + + + + Creates a TableRetryWrapperException object. + + + + + Creates a TableRetryWrapperException object. + + + + + Creates a TableRetryWrapperException object. + + + + + Creates a TableRetryWrapperException object. + + + + + Objects of this class encapsulate information about a storage account and endpoint configuration. + Associated with a storage account is the account name, the base URI of the account and a shared key. + + + + + The default configuration string in configuration files for setting the queue storage endpoint. + + + + + The default configuration string in configuration files for setting the blob storage endpoint. + + + + + The default configuration string in configuration files for setting the table storage endpoint. + + + + + The default configuration string in configuration files for setting the storage account name. + + + + + The default configuration string in configuration files for setting the shared key associated with a storage account. + + + + + The default configuration string in configuration files for setting the UsePathStyleUris option. + + + + + The default prefix string in application config and Web.config files to indicate that this setting should be looked up + in the fabric's configuration system. + + + + + Constructor for creating account info objects. + + The account's base URI. + If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used. + If false host-style URIs (http://accountname.baseuri/containername/objectname) are used, + where baseuri is the URI of the service.. + If null, the choice is made automatically: path-style URIs if host name part of base URI is an + IP addres, host-style otherwise. + The account name. + The account's shared key. + + + + Constructor for creating account info objects. + + The account's base URI. + If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used. + If false host-style URIs (http://accountname.baseuri/containername/objectname) are used, + where baseuri is the URI of the service. + If null, the choice is made automatically: path-style URIs if host name part of base URI is an + IP addres, host-style otherwise. + The account name. + The account's shared key. + true if it shall be allowed to only set parts of the StorageAccountInfo properties. + + + + Retrieves account settings for the queue service from the default settings. + + + + + Retrieves account settings for the queue service from the default settings. + Throws an exception in case of incomplete settings. + + + + + Retrieves account settings for the table service from the default settings. + + + + + Retrieves account settings for the table service from the default settings. + Throws an exception in case of incomplete settings. + + + + + Retrieves account settings for the blob service from the default settings. + + + + + Retrieves account settings for the blob service from the default settings. + Throws an exception in case of incomplete settings. + + + + + Gets settings from default configuration names except for the endpoint configuration string. + + + + + Gets settings from default configuration names except for the endpoint configuration string. Throws an exception + in the case of incomplete settings. + + + + + Gets a configuration setting from application settings in the Web.config or App.config file. + When running in a hosted environment, configuration settings are read from .cscfg + files. + + + + + Retrieves account information settings from configuration settings. First, the implementation checks for + settings in an application config section of an app.config or Web.config file. These values are overwritten + if the same settings appear in a .csdef file. + The implementation also supports indirect settings. In this case, indirect settings overwrite all other settings. + + Configuration string for the account name. + Configuration string for the key. + Configuration string for the endpoint. + Configuration string for the path style. + If false, an exception is thrown if not all settings are available. + StorageAccountInfo object containing the retrieved settings. + + + + Checks whether all essential properties of this object are set. Only then, the account info object + should be used in ohter APIs of this library. + + + + + + Checks whether this StorageAccountInfo object is complete in the sense that all properties are set. + + + + + The base URI of the account. + + + + + The account name. + + + + + The account's key. + + + + + If set, returns the UsePathStyleUris properties. If the property has not been explicitly set, + the implementation tries to derive the correct value from the base URI. + + + + + Get a reference to a Queue object with a specified name. This method does not make a call to + the queue service. + + The name of the queue + A newly created queue object + + + + Lists all queues with a given prefix within an account. + + + The list of queue names. + + + + Lists the queues within the account. + + A list of queues + + + diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Debug/StorageClient.dll b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Debug/StorageClient.dll new file mode 100644 index 0000000..539fd6d Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Debug/StorageClient.dll differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Debug/StorageClient.pdb b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Debug/StorageClient.pdb new file mode 100644 index 0000000..f10d989 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Debug/StorageClient.pdb differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Release/StorageClient.dll b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Release/StorageClient.dll new file mode 100644 index 0000000..8376e42 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Release/StorageClient.dll differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Release/StorageClient.pdb b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Release/StorageClient.pdb new file mode 100644 index 0000000..5f8089a Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/bin/Release/StorageClient.pdb differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Debug/ResolveAssemblyReference.cache b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Debug/ResolveAssemblyReference.cache new file mode 100644 index 0000000..ded43fd Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Debug/ResolveAssemblyReference.cache differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Debug/StorageClient.csproj.FileListAbsolute.txt b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Debug/StorageClient.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..1170691 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Debug/StorageClient.csproj.FileListAbsolute.txt @@ -0,0 +1,10 @@ +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\StorageClient\bin\Debug\StorageClient.dll +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\StorageClient\bin\Debug\StorageClient.pdb +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\StorageClient\obj\Debug\ResolveAssemblyReference.cache +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\StorageClient\obj\Debug\StorageClient.dll +G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\StorageClient\obj\Debug\StorageClient.pdb +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\bin\Debug\StorageClient.dll +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\bin\Debug\StorageClient.pdb +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\obj\Debug\ResolveAssemblyReference.cache +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\obj\Debug\StorageClient.dll +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\obj\Debug\StorageClient.pdb diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Debug/StorageClient.dll b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Debug/StorageClient.dll new file mode 100644 index 0000000..539fd6d Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Debug/StorageClient.dll differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Debug/StorageClient.pdb b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Debug/StorageClient.pdb new file mode 100644 index 0000000..f10d989 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Debug/StorageClient.pdb differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Release/ResolveAssemblyReference.cache b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Release/ResolveAssemblyReference.cache new file mode 100644 index 0000000..ded43fd Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Release/ResolveAssemblyReference.cache differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Release/StorageClient.csproj.FileListAbsolute.txt b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Release/StorageClient.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..d01402e --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Release/StorageClient.csproj.FileListAbsolute.txt @@ -0,0 +1,5 @@ +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\bin\Release\StorageClient.dll +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\bin\Release\StorageClient.pdb +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\obj\Release\ResolveAssemblyReference.cache +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\obj\Release\StorageClient.dll +F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\obj\Release\StorageClient.pdb diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Release/StorageClient.dll b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Release/StorageClient.dll new file mode 100644 index 0000000..8376e42 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Release/StorageClient.dll differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Release/StorageClient.pdb b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Release/StorageClient.pdb new file mode 100644 index 0000000..5f8089a Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/obj/Release/StorageClient.pdb differ diff --git a/aspclassiccompiler/AzureStoreAsp/Assets/updateHostsFile.cmd b/aspclassiccompiler/AzureStoreAsp/Assets/updateHostsFile.cmd new file mode 100644 index 0000000..26fe392 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/Assets/updateHostsFile.cmd @@ -0,0 +1,40 @@ +setlocal enabledelayedexpansion +@echo off + +%~d0 +cd "%~dp0" + +IF "%1%"=="" GOTO ERROR + +echo. +echo ======================================== +echo Updating HOSTS file... +echo ======================================== +echo. + +for /f "useback tokens=*" %%a in ('%1') do set STR=%%~a +for /l %%a in (1,1,5) do if "!STR:~-1!"=="/" set STR=!STR:~0,-1! + +copy %systemroot%\System32\drivers\etc\hosts %systemroot%\System32\drivers\etc\hosts.bak + +echo. >> %systemroot%\System32\drivers\etc\hosts +echo 127.0.0.1 %STR% >> %systemroot%\System32\drivers\etc\hosts + +echo. +echo ======================================== +echo Updating HOSTS file done. +echo ======================================== +echo. + +GOTO FINISH + +:ERROR +echo. +echo ======================================== +echo Domain parameter missing. +echo (required for updating the hosts file). +echo ======================================== +echo. + +:FINISH + diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore.sln b/aspclassiccompiler/AzureStoreAsp/AzureStore.sln new file mode 100644 index 0000000..971e63a --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{CC5FD16D-436D-48AD-A40C-5A424C6E3E79}") = "AzureStoreService", "AzureStoreService\AzureStoreService.ccproj", "{B8FEB21E-A4D5-441C-B5BB-5CEB8A1BE537}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MVCAzureStore", "AzureStore\MVCAzureStore.csproj", "{D4109103-5396-4DE5-8042-1ACAAB367C32}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Assets", "Assets", "{103A3D88-1691-4659-AE90-A252552BC85B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspProviders", "Assets\AspProviders\AspProviders.csproj", "{306D2F9E-D6D0-4D96-94F1-173C60A13875}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StorageClient", "Assets\StorageClient\StorageClient.csproj", "{C6F30C10-E1C2-4327-BB6B-3160B479CCA1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9C75B39B-52AF-4887-8FD6-C8F23AE0ACCD}" + ProjectSection(SolutionItems) = preProject + Readme.txt = Readme.txt + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D4109103-5396-4DE5-8042-1ACAAB367C32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4109103-5396-4DE5-8042-1ACAAB367C32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4109103-5396-4DE5-8042-1ACAAB367C32}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4109103-5396-4DE5-8042-1ACAAB367C32}.Release|Any CPU.Build.0 = Release|Any CPU + {B8FEB21E-A4D5-441C-B5BB-5CEB8A1BE537}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8FEB21E-A4D5-441C-B5BB-5CEB8A1BE537}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8FEB21E-A4D5-441C-B5BB-5CEB8A1BE537}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8FEB21E-A4D5-441C-B5BB-5CEB8A1BE537}.Release|Any CPU.Build.0 = Release|Any CPU + {306D2F9E-D6D0-4D96-94F1-173C60A13875}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {306D2F9E-D6D0-4D96-94F1-173C60A13875}.Debug|Any CPU.Build.0 = Debug|Any CPU + {306D2F9E-D6D0-4D96-94F1-173C60A13875}.Release|Any CPU.ActiveCfg = Release|Any CPU + {306D2F9E-D6D0-4D96-94F1-173C60A13875}.Release|Any CPU.Build.0 = Release|Any CPU + {C6F30C10-E1C2-4327-BB6B-3160B479CCA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C6F30C10-E1C2-4327-BB6B-3160B479CCA1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C6F30C10-E1C2-4327-BB6B-3160B479CCA1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C6F30C10-E1C2-4327-BB6B-3160B479CCA1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {306D2F9E-D6D0-4D96-94F1-173C60A13875} = {103A3D88-1691-4659-AE90-A252552BC85B} + {C6F30C10-E1C2-4327-BB6B-3160B479CCA1} = {103A3D88-1691-4659-AE90-A252552BC85B} + EndGlobalSection +EndGlobal diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore.suo b/aspclassiccompiler/AzureStoreAsp/AzureStore.suo new file mode 100644 index 0000000..1aa4fd4 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/AzureStore.suo differ diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/Site.css b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/Site.css new file mode 100644 index 0000000..c2fd074 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/Site.css @@ -0,0 +1,755 @@ + +/* 9 of 12 Microsoft Internet Explorer Design Templates + + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + HTML TAGS + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +body { + font-family:Arial, Tahoma, Helvetica, sans-serif; + margin:0; + padding:0; + } + +p, li { + font-size:.75em; + line-height:160%; + } + +a img { + border:none; + } + +/* HEADINGS */ + +h1,h2,h3,h4,h5,h6 { + margin:15px 0; + font-family:"trebuchet MS", verdana, sans-serif; + color:#e99400; + } + +.content-left h1, .content-right h1, +.content-left h2, .content-right h2, +.content-left h3, .content-right h3, +.content-left h4, .content-right h4, +.content-left h5, .content-right h5, +.content-left h6, .content-right h6 { + color:#000; + margin:5px 0; + } + +h1.first, +h2.first, +h3.first, +h4.first, +h5.first, +h6.first { + margin-top:0; + } + +h1 { + font-size:1.2em; + } + +h2 { + font-size:1em; + } + +h3 { + font-size:.9em; + background:url(images/bg-line.png) repeat-x bottom; + } + +h4 { + font-size:.8em; + } + +h5 { + font-size:.8em; + } + +h6 { + font-size:.8em; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + FORMS + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +fieldset { + padding:10px 20px; + margin:15px 0; + position:relative; + } + +fieldset fieldset { + margin:10px 0; + width:90%; + } + +legend { + font-weight:bold; + font-size:1em; + padding:10px; + color:#000; + margin:0 0 10px 30px; + font-family:"trebuchet MS", verdana, sans-serif; + } + +fieldset fieldset legend { + font-size:.8em; + } + +.input-box { + display:block; + margin-bottom:5px; + vertical-align:top; + padding:2px; + color:#666666; + font-size:.8em; + border:1px solid #82b26e; + background:url(images/bg-input.png) no-repeat; + } + +label { + font-weight:bold; + font-size:.7em; + font-weight:bold; + display:block; + font-family:Arial, Helvetica, sans-serif; + } + +.checkbox { + margin:12px 0; + padding:4px 0 2px 3px; + border:1px dotted #585858; + width:95%; + } + +.checkbox input { + margin-right:6px; + margin-bottom:2px; + } + +.forgot-password { + margin:2px 0; + font-size:.65em; + } + +input.button { + color:#FFF; + background:#317f0c url(images/bg-button.png) repeat-x; + border:2px groove #FFF; + font-size:.7em; + font-weight:bold; + padding:0 5px 2px 5px; + cursor:pointer; + } + +input.button-big { + padding:6px 20px; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + LINKS + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +a:link { + color:#33840c; + text-decoration:underline; + } + +a:visited { + color:#72840c; + text-decoration:underline; + } + +a:hover { + text-decoration:none; + } + +a:active { + text-decoration:underline; + color:#86c400; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + HEADER + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.header-container { + position:relative; + padding:6px 0 0 0; + background:#373737; + color:#FFF; + border-bottom:1px dotted #585858; + min-height:1px; + _height:1px; + } + +/* LOGO */ + +.logo { + font-size:1.5em; + width:60%; + float:left; + font-family:"trebuchet MS", verdana, sans-serif; + padding:5px 0 10px 15px; + letter-spacing:.2em; + } + +/* LOGO LINK */ + +.logo a:link, +.logo a:visited { + text-decoration:none; + color:#FFF; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + LOGIN + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.nav-login { + font-size:.9em; + text-align:right; + margin:10px 15px 3px 0; + float:right; + width:20%; + } + +/* LOGIN LISTS */ + +.nav-login ul { + margin:0; + padding:0; + } + +.nav-login li { + list-style:none; + padding:3px 0 3px 17px; + display:inline; + margin-left:15px; + background:url(images/bullet.png) no-repeat 0 4px; + } + +.nav-login li.first { + list-style:none; + margin-left:0; + } + +/* LOGIN LINKS */ + +.nav-login a:link, +.nav-login a:visited{ + color:#FFF; + text-decoration:none; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + MAIN NAV + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.nav-main { + margin:-41px 0 0 199px; + padding:12px 15px 0 15px; + background:#f6f6f6; + min-height:28px; + _height:28px; + border-top:1px dotted #7a7a7a; + border-bottom:1px dotted #7a7a7a; + border-left:1px dotted #7a7a7a; + } + +/* MAIN NAV LISTS */ + +.nav-main ul { + margin:0; + padding:0; + } + +.nav-main li { + list-style:none; + float:left; + background:none; + padding:0; + } + +.nav-main li a { + display:block; + padding:0 10px; + border-right:1px dotted #d2d2d2; + } + +.nav-main li.first a { + padding-left:0; + } + +/* NAV MAIN LINKS */ + +.nav-main a:link, +.nav-main a:visited { + color:#33840c; + text-decoration:none; + } + +.nav-main .active a:link, +.nav-main .active a:visited { + color:#000; + } + +.nav-main a:hover { + text-decoration:underline; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + FOOTER + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.footer { + padding:10px 0; + _height:1px; + background:#373737; + clear:both; + } + +.nav-footer { + position:relative; + _height:1px; + margin:0 200px; + text-align:center; + clear:both; + } + +.nav-footer ul{ + padding:0; + margin:0; + text-align:center; + } + +.nav-footer li{ + background:none; + display:inline; + border-right:1px dotted #686868; + padding:0 10px; + } + +.nav-footer li.first { + border-left:1px dotted #686868; + } + +.copyright { + color:#999; + font-size:.7em; + clear:both; + margin:5px 0 0 0; + } + +.nav-footer a:link, +.nav-footer a:visited { + color:#CCC; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + POSTER + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.poster-container { + background:url(images/bg-poster-tile.jpg) repeat-x; + min-height:200px; + _height:200px; + margin-top:-1px; + } + +.poster-container-no-image { + background:url(images/bg-poster-tile.jpg) repeat-x; + min-height:70px; + _height:70px; + margin-top:-1px; + } + +.poster-inner { + background:url(images/bg-poster.jpg) no-repeat left top; + font-family:"trebuchet MS", verdana, sans-serif; + min-height:70px; + _height:70px; + } + +.poster-photo { + background:url(images/bg-poster-photo.jpg) no-repeat left; + min-height:170px; + _height:170px; + padding:30px 30% 0 200px; + font-style:italic; + } + +.poster-container h1 { + margin:0; + font-size:1.2em; + color:#000; + } + +.poster-container p { + margin:10px 0 0 0; + line-height:normal; + font-size:1em; + } + +/* POSTER LINKS */ + +.poster-links { + font-weight:bold; + } + +.poster-container a:link, +.poster-container a:visited { + color:#33840c; + } + +.poster-container a:hover { + text-decoration:none; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + BREADCRUMB + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.breadcrumb { + min-height:1px; + _height:1px; + padding:0 0 15px 0; + margin:0 0 15px 0; + font-size:.7em; + border-bottom:1px dotted #686868; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + PRIMARY LAYOUT STYLES + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.content-container { + position:relative; + _height:1px; + min-height:1px; + background:url(images/bg-column-left.png) repeat-y; + } + +.content-container-inner { + background:url(images/bg-column-right.png) repeat-y right; + _height:1px; + min-height:1px; + padding:0 200px; + position:relative; + } + +.content-main { + padding:15px 2% 20px 2%; + position:relative; + min-height:1px; + _height:1px; + float:left; + width:96%; + } + +.content-left { + padding:20px 10px; + float:left; + width:180px; + margin-top:-1px; + position:relative; + margin-left:-100%; + right:200px; + _left:200px; + border-top:1px dotted #797979; + } + +.content-right { + padding:15px 10px 20px 10px; + float:left; + width:180px; + position:relative; + margin-right:-200px; + } + +.ads { + text-align:center; + margin:20px 0; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + SIDE BUCKET + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.side-bucket { + margin-bottom:20px; + line-height:normal; + } + +/* SIDE BUCKET LISTS */ + +.side-bucket ul { + margin:0; + padding:0; + _height:1px; + } + +.side-bucket li { + list-style:none; + border-bottom:1px dotted #7a7a7a; + background-position:0 4px; + } + +.side-bucket li.first { + border-top:1px dotted #7a7a7a; + } + +/* SIDE BUCKET LINKS */ + +.side-bucket a:link, +.side-bucket a:visited { + text-decoration:none; + display:block; + _display:inline; + } + +.side-bucket a:hover { + text-decoration:underline; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + PHOTOS & CAPTIONS + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.photo-container { + padding:10px; + border:1px dotted #7a7a7a; + } + +.photo-container img { + display:block; + border:1px solid #666; + } + +.photo-caption { + font-size:.7em; + padding:10px 10px 0 10px; + text-align:center; + color:#999; + font-style:italic + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + THREE-COLUMN SECTION + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.three-column-container { + margin:10px 0px; + } + +.three-column-left, +.three-column-right, +.three-column-middle { + float:left; + width:30%; + margin-right:5%; + } + +.three-column-right { + margin-right:0; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + DATA TABLES + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +table.data-table { + background:#f6f6f6; + font-size:.7em; + border-top:1px dotted #7a7a7a; + border-left:1px dotted #7a7a7a; + } + +table.data-table caption{ + background:#b6b6b6; + padding:10px; + text-align:center; + font-weight:bold; + font-family:Arial, Helvetica, sans-serif; + border:1px dotted #7a7a7a; + border-bottom:none; + } + +table.data-table td, +table.data-table th{ + padding:8px; + border-right:1px dotted #7a7a7a; + border-bottom:1px dotted #7a7a7a; + } + +table.data-table th{ + text-align:left; + font-family:Arial, Helvetica, sans-serif; + background:#ececec; + } + +table.data-table td{ + } + +table.data-table tr.row-alternating td{ + background:#e0e0e0; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + LISTS + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +li { + list-style:none; + background:url(images/bullet.png) no-repeat 0 5px; + padding:3px 0 3px 17px; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + MISC STYLES + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.none { + display:none; + } + +.clear { + clear:both; + font-size:1px; + margin:0; + padding:0; + } + +.align-left { + float:left; + margin:0 20px 15px 0; + } + +.align-right { + float:right; + margin:0 0 15px 20px; + } + +.align-middle { + vertical-align:middle; + } + +.inline { + display:inline; + } + +.no-margin { + margin:0; + } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + MODIFIED + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.login +{ + margin:auto auto; +} + +.login input[type=text], .login input[type=password] +{ + width: 10em; + margin: 0.5em; +} + +.content-main +{ + _height:400px; + min-height:400px; +} + +.product-category +{ + color: #666666; +} + +.product-list +{ + padding:0.25em; + border: solid 1px #999; + font-size:10pt; + width:100%; + margin-top:0.5em; + margin-bottom: 0.5em; + height:150px; +} + +.identity +{ + margin-left:0.25em; + max-width:6.5em; + display:inline-block; + overflow:hidden; + vertical-align:middle; +} + +.login label +{ + display: inline; +} + +label +{ + font-size:1em; +} + +.role label +{ + margin:1em; +} + +.role input +{ + margin:1em 0 0.5em 0; + vertical-align:middle; +} + +.login-status, .login-status iframe +{ + height: 2em !important; + width: 5em !important; +} diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-button.png b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-button.png new file mode 100644 index 0000000..86f4460 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-button.png differ diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-column-left.png b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-column-left.png new file mode 100644 index 0000000..9136894 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-column-left.png differ diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-column-right.png b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-column-right.png new file mode 100644 index 0000000..da5f17a Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-column-right.png differ diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-input.png b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-input.png new file mode 100644 index 0000000..f423454 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-input.png differ diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-poster-tile.jpg b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-poster-tile.jpg new file mode 100644 index 0000000..dc39d96 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-poster-tile.jpg differ diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-poster.jpg b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-poster.jpg new file mode 100644 index 0000000..0d16a22 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bg-poster.jpg differ diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bullet.png b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bullet.png new file mode 100644 index 0000000..380a602 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/bullet.png differ diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/favicon.ico b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/favicon.ico new file mode 100644 index 0000000..db26712 Binary files /dev/null and b/aspclassiccompiler/AzureStoreAsp/AzureStore/Content/images/favicon.ico differ diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Controllers/AccountController.cs b/aspclassiccompiler/AzureStoreAsp/AzureStore/Controllers/AccountController.cs new file mode 100644 index 0000000..c2c5241 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Controllers/AccountController.cs @@ -0,0 +1,422 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +namespace MVCAzureStore.Controllers +{ + using System; + using System.Globalization; + using System.Security.Principal; + using System.Web.Mvc; + using System.Web.Security; + + [HandleError] + public class AccountController : Controller + { + + // This constructor is used by the MVC framework to instantiate the controller using + // the default forms authentication and membership providers. + + public AccountController() + : this(null, null, null) + { + } + + // This constructor is not used by the MVC framework but is instead provided for ease + // of unit testing this type. See the comments at the end of this file for more + // information. + public AccountController(IFormsAuthentication formsAuth, IMembershipService service, IRoleService roleService) + { + FormsAuth = formsAuth ?? new FormsAuthenticationService(); + MembershipService = service ?? new AccountMembershipService(); + RoleService = roleService ?? new AccountRoleService(); + } + + public IFormsAuthentication FormsAuth + { + get; + private set; + } + + public IMembershipService MembershipService + { + get; + private set; + } + + public IRoleService RoleService + { + get; + private set; + } + + public ActionResult LogOn() + { + return View(); + } + + [AcceptVerbs(HttpVerbs.Post)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", + Justification = "Needs to take same parameter type as Controller.Redirect()")] + public ActionResult LogOn(string userName, string password, bool rememberMe, string returnUrl) + { + if (!ValidateLogOn(userName, password)) + { + return View(); + } + + FormsAuth.SignIn(userName, rememberMe); + if (!String.IsNullOrEmpty(returnUrl)) + { + return Redirect(returnUrl); + } + else + { + return RedirectToAction("Index", "Home"); + } + } + + public ActionResult LogOff() + { + FormsAuth.SignOut(); + + // ensure that selected items are cleared from session when users logs out + Session.Abandon(); + + return RedirectToAction("Index", "Home"); + } + + public ActionResult Register() + { + ViewData["PasswordLength"] = MembershipService.MinPasswordLength; + ViewData["Roles"] = RoleService.GetAllRoles(); + + return View(); + } + + [AcceptVerbs(HttpVerbs.Post)] + public ActionResult Register(string userName, string email, string password, string confirmPassword, string roleName) + { + ViewData["PasswordLength"] = MembershipService.MinPasswordLength; + + if (ValidateRegistration(userName, email, password, confirmPassword, roleName)) + { + // Attempt to register the user + MembershipCreateStatus createStatus = MembershipService.CreateUser(userName, password, email); + + if (createStatus == MembershipCreateStatus.Success) + { + // assign user to role + this.RoleService.AddUserToRole(userName, roleName); + + FormsAuth.SignIn(userName, false /* createPersistentCookie */); + return RedirectToAction("Index", "Home"); + } + else + { + ModelState.AddModelError("_FORM", ErrorCodeToString(createStatus)); + } + } + + // If we got this far, something failed, redisplay form + return View(); + } + + [Authorize] + public ActionResult ChangePassword() + { + ViewData["PasswordLength"] = MembershipService.MinPasswordLength; + + return View(); + } + + [Authorize] + [AcceptVerbs(HttpVerbs.Post)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", + Justification = "Exceptions result in password not being changed.")] + public ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword) + { + ViewData["PasswordLength"] = MembershipService.MinPasswordLength; + + if (!ValidateChangePassword(currentPassword, newPassword, confirmPassword)) + { + return View(); + } + + try + { + if (MembershipService.ChangePassword(User.Identity.Name, currentPassword, newPassword)) + { + return RedirectToAction("ChangePasswordSuccess"); + } + else + { + ModelState.AddModelError("_FORM", "The current password is incorrect or the new password is invalid."); + return View(); + } + } + catch + { + ModelState.AddModelError("_FORM", "The current password is incorrect or the new password is invalid."); + return View(); + } + } + + public ActionResult ChangePasswordSuccess() + { + return View(); + } + + protected override void OnActionExecuting(ActionExecutingContext filterContext) + { + if (filterContext.HttpContext.User.Identity is WindowsIdentity) + { + throw new InvalidOperationException("Windows authentication is not supported."); + } + } + + #region Validation Methods + + private bool ValidateChangePassword(string currentPassword, string newPassword, string confirmPassword) + { + if (String.IsNullOrEmpty(currentPassword)) + { + ModelState.AddModelError("currentPassword", "You must specify a current password."); + } + if (newPassword == null || newPassword.Length < MembershipService.MinPasswordLength) + { + ModelState.AddModelError("newPassword", + String.Format(CultureInfo.CurrentCulture, + "You must specify a new password of {0} or more characters.", + MembershipService.MinPasswordLength)); + } + + if (!String.Equals(newPassword, confirmPassword, StringComparison.Ordinal)) + { + ModelState.AddModelError("_FORM", "The new password and confirmation password do not match."); + } + + return ModelState.IsValid; + } + + private bool ValidateLogOn(string userName, string password) + { + if (String.IsNullOrEmpty(userName)) + { + ModelState.AddModelError("username", "You must specify a username."); + } + if (String.IsNullOrEmpty(password)) + { + ModelState.AddModelError("password", "You must specify a password."); + } + if (!MembershipService.ValidateUser(userName, password)) + { + ModelState.AddModelError("_FORM", "The username or password provided is incorrect."); + } + + return ModelState.IsValid; + } + + private bool ValidateRegistration(string userName, string email, string password, string confirmPassword, string roleName) + { + if (String.IsNullOrEmpty(userName)) + { + ModelState.AddModelError("username", "You must specify a username."); + } + if (String.IsNullOrEmpty(email)) + { + ModelState.AddModelError("email", "You must specify an email address."); + } + if (password == null || password.Length < MembershipService.MinPasswordLength) + { + ModelState.AddModelError("password", + String.Format(CultureInfo.CurrentCulture, + "You must specify a password of {0} or more characters.", + MembershipService.MinPasswordLength)); + } + if (!String.Equals(password, confirmPassword, StringComparison.Ordinal)) + { + ModelState.AddModelError("_FORM", "The new password and confirmation password do not match."); + } + if (this.RoleService.GetAllRoles().Length > 0 && String.IsNullOrEmpty(roleName)) + { + ModelState.AddModelError("roleName", "You must specify a role."); + } + return ModelState.IsValid; + } + + private static string ErrorCodeToString(MembershipCreateStatus createStatus) + { + // See http://msdn.microsoft.com/en-us/library/system.web.security.membershipcreatestatus.aspx for + // a full list of status codes. + switch (createStatus) + { + case MembershipCreateStatus.DuplicateUserName: + return "Username already exists. Please enter a different user name."; + + case MembershipCreateStatus.DuplicateEmail: + return "A username for that e-mail address already exists. Please enter a different e-mail address."; + + case MembershipCreateStatus.InvalidPassword: + return "The password provided is invalid. Please enter a valid password value."; + + case MembershipCreateStatus.InvalidEmail: + return "The e-mail address provided is invalid. Please check the value and try again."; + + case MembershipCreateStatus.InvalidAnswer: + return "The password retrieval answer provided is invalid. Please check the value and try again."; + + case MembershipCreateStatus.InvalidQuestion: + return "The password retrieval question provided is invalid. Please check the value and try again."; + + case MembershipCreateStatus.InvalidUserName: + return "The user name provided is invalid. Please check the value and try again."; + + case MembershipCreateStatus.ProviderError: + return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator."; + + case MembershipCreateStatus.UserRejected: + return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator."; + + default: + return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator."; + } + } + #endregion + } + + // The FormsAuthentication type is sealed and contains static members, so it is difficult to + // unit test code that calls its members. The interface and helper class below demonstrate + // how to create an abstract wrapper around such a type in order to make the AccountController + // code unit testable. + + public interface IFormsAuthentication + { + void SignIn(string userName, bool createPersistentCookie); + void SignOut(); + } + + public class FormsAuthenticationService : IFormsAuthentication + { + public void SignIn(string userName, bool createPersistentCookie) + { + FormsAuthentication.SetAuthCookie(userName, createPersistentCookie); + } + public void SignOut() + { + FormsAuthentication.SignOut(); + } + } + + public interface IMembershipService + { + int MinPasswordLength { get; } + + bool ValidateUser(string userName, string password); + MembershipCreateStatus CreateUser(string userName, string password, string email); + bool ChangePassword(string userName, string oldPassword, string newPassword); + } + + public class AccountMembershipService : IMembershipService + { + private MembershipProvider _provider; + + public AccountMembershipService() + : this(null) + { + } + + public AccountMembershipService(MembershipProvider provider) + { + _provider = provider ?? Membership.Provider; + } + + public int MinPasswordLength + { + get + { + return _provider.MinRequiredPasswordLength; + } + } + + public bool ValidateUser(string userName, string password) + { + return _provider.ValidateUser(userName, password); + } + + public MembershipCreateStatus CreateUser(string userName, string password, string email) + { + MembershipCreateStatus status; + _provider.CreateUser(userName, password, email, null, null, true, null, out status); + return status; + } + + public bool ChangePassword(string userName, string oldPassword, string newPassword) + { + MembershipUser currentUser = _provider.GetUser(userName, true /* userIsOnline */); + return currentUser.ChangePassword(oldPassword, newPassword); + } + } + + public interface IRoleService + { + bool IsUserInRole(string userName, string roleName); + void AddUserToRole(string userName, string roleName); + string[] GetAllRoles(); + } + + public class AccountRoleService : IRoleService + { + private readonly RoleProvider roleProvider; + + public AccountRoleService() + : this(null) + { + } + + public AccountRoleService(RoleProvider provider) + { + this.roleProvider = provider ?? (Roles.Enabled ? Roles.Provider : null); + } + + public void AddUserToRole(string userName, string roleName) + { + if (this.roleProvider != null) + { + this.roleProvider.AddUsersToRoles(new string[] { userName }, new string[] { roleName }); + } + } + + public bool IsUserInRole(string userName, string roleName) + { + if (this.roleProvider != null) + { + return this.roleProvider.IsUserInRole(userName, roleName); + } + + return false; + } + + public string[] GetAllRoles() + { + if (this.roleProvider != null) + { + return this.roleProvider.GetAllRoles(); + } + + return new string[0]; + } + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Controllers/HomeController.cs b/aspclassiccompiler/AzureStoreAsp/AzureStore/Controllers/HomeController.cs new file mode 100644 index 0000000..8242f87 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Controllers/HomeController.cs @@ -0,0 +1,84 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +namespace MVCAzureStore.Controllers +{ + using System.Collections.Generic; + using System.Linq; + using System.Web.Mvc; + + [HandleError] + [Authorize] + public class HomeController : Controller + { + public ActionResult About() + { + return View(); + } + + public ActionResult Index() + { + var products = this.HttpContext.Application["Products"] as List; + var itemsInSession = this.Session["Cart"] as List ?? new List(); + + // add all products currently not in session + var filteredProducts = products.Where(item => !itemsInSession.Contains(item)); + + // Add additional filters here + // filter product list for home users + if (User.IsInRole("Home")) + { + filteredProducts = filteredProducts.Where(item => item.Contains("Home")); + } + + return View(filteredProducts); + } + + [AcceptVerbs(HttpVerbs.Post)] + public ActionResult Add(string selectedItem) + { + if (selectedItem != null) + { + List cart = this.Session["Cart"] as List ?? new List(); + cart.Add(selectedItem); + Session["Cart"] = cart; + } + + return RedirectToAction("Index"); + } + + public ActionResult Checkout() + { + var itemsInSession = this.Session["Cart"] as List ?? new List(); + return View(itemsInSession); + } + + [AcceptVerbs(HttpVerbs.Post)] + public ActionResult Remove(string selectedItem) + { + if (selectedItem != null) + { + var itemsInSession = this.Session["Cart"] as List; + if (itemsInSession != null) + { + itemsInSession.Remove(selectedItem); + } + } + + return RedirectToAction("Checkout"); + } + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Default.aspx b/aspclassiccompiler/AzureStoreAsp/AzureStore/Default.aspx new file mode 100644 index 0000000..0d059a7 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Default.aspx @@ -0,0 +1,3 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="MVCAzureStore._Default" %> + +<%-- Please do not delete this file. It is used to ensure that ASP.NET MVC is activated by IIS when a user makes a "/" request to the server. --%> diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Default.aspx.cs b/aspclassiccompiler/AzureStoreAsp/AzureStore/Default.aspx.cs new file mode 100644 index 0000000..0145b71 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Default.aspx.cs @@ -0,0 +1,38 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +using System.Web; +using System.Web.Mvc; +using System.Web.UI; + +namespace MVCAzureStore +{ + public partial class _Default : Page + { + public void Page_Load(object sender, System.EventArgs e) + { + // Change the current path so that the Routing handler can correctly interpret + // the request, then restore the original path so that the OutputCache module + // can correctly process the response (if caching is enabled). + + string originalPath = Request.Path; + HttpContext.Current.RewritePath(Request.ApplicationPath, false); + IHttpHandler httpHandler = new MvcHttpHandler(); + httpHandler.ProcessRequest(HttpContext.Current); + HttpContext.Current.RewritePath(originalPath, false); + } + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Global.asax b/aspclassiccompiler/AzureStoreAsp/AzureStore/Global.asax new file mode 100644 index 0000000..d77a1f5 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="MVCAzureStore.MvcApplication" Language="C#" %> diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Global.asax.cs b/aspclassiccompiler/AzureStoreAsp/AzureStore/Global.asax.cs new file mode 100644 index 0000000..8bcdae7 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Global.asax.cs @@ -0,0 +1,105 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +namespace MVCAzureStore +{ + using System; + using System.Collections.Generic; + using System.Web.Mvc; + using System.Web.Routing; + using System.Web.Security; + using Dlrsoft.Asp.Mvc; + + // Note: For instructions on enabling IIS6 or IIS7 classic mode, + // visit http://go.microsoft.com/?LinkId=9394801 + + public class MvcApplication : System.Web.HttpApplication + { + public static void RegisterRoutes(RouteCollection routes) + { + routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); + + routes.MapRoute( + "Default", // Route name + "{controller}/{action}/{id}", // URL with parameters + new { controller = "Home", action = "Index", id = "" } // Parameter defaults + ); + + } + + protected void Application_Start() + { + RegisterRoutes(RouteTable.Routes); + ViewEngines.Engines.Add(new AspViewEngine()); + } + + private static bool initialized = false; + private static object gate = new object(); + + // The Windows Azure fabric runs IIS 7.0 in integrated mode. In integrated + // mode, the Application_Start event does not support access to the request + // context or to the members of the RoleManager class provided by the Windows + // Azure SDK runtime API. If you are writing an ASP.NET application that + // accesses the request context or calls methods of the RoleManager class from + // the Application_Start event, you should modify it to initialize in the + // Application_BeginRequest event instead. + protected void Application_BeginRequest(object sender, EventArgs e) + { + if (initialized) + { + return; + } + + lock (gate) + { + if (initialized) + { + return; + } + + LoadProducts(); + + // Initialize application roles here + // (requires access to RoleManager to read configuration) + if (!Roles.RoleExists("Home")) + { + Roles.CreateRole("Home"); + } + + if (!Roles.RoleExists("Enterprise")) + { + Roles.CreateRole("Enterprise"); + } + + initialized = true; + } + } + + private void LoadProducts() + { + this.Application["Products"] = new List { + "Microsoft Office 2007 Ultimate", + "Microsoft Office Communications Server Enterprise CAL", + "Microsoft Core CAL - License & software assurance - 1 user CAL", + "Windows Server 2008 Enterprise", + "Windows Vista Home Premium (Upgrade)", + "Windows XP Home Edition w/SP2 (OEM)", + "Windows Home Server - 10 Client (OEM License)", + "Console XBOX 360 Arcade" + }; + } + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Helpers/IsCurrentActionHelper.cs b/aspclassiccompiler/AzureStoreAsp/AzureStore/Helpers/IsCurrentActionHelper.cs new file mode 100644 index 0000000..f9c5c2e --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Helpers/IsCurrentActionHelper.cs @@ -0,0 +1,35 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +namespace Helpers +{ + using System; + using System.Web.Mvc; + + public static class IsCurrentActionHelper + { + public static bool IsCurrentAction(this HtmlHelper helper, string actionName, string controllerName) + { + string currentControllerName = (string)helper.ViewContext.RouteData.Values["controller"]; + string currentActionName = (string)helper.ViewContext.RouteData.Values["action"]; + + if (currentControllerName.Equals(controllerName, StringComparison.CurrentCultureIgnoreCase) && currentActionName.Equals(actionName, StringComparison.CurrentCultureIgnoreCase)) + return true; + + return false; + } + } +} \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/MVCAzureStore.csproj b/aspclassiccompiler/AzureStoreAsp/AzureStore/MVCAzureStore.csproj new file mode 100644 index 0000000..694bbf8 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/MVCAzureStore.csproj @@ -0,0 +1,153 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {D4109103-5396-4DE5-8042-1ACAAB367C32} + {603c0e0b-db56-11dc-be95-000d561079b0};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + MVCAzureStore + MVCAzureStore + v3.5 + false + Web + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + False + ..\..\asp\bin\Release\Dlrsoft.Asp.dll + + + + + 3.5 + + + 3.5 + + + + 3.5 + + + + + + + + + + + + + + + + + Default.aspx + ASPXCodeBehind + + + Global.asax + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ASPXCodeBehind + + + + + + + + + {306D2F9E-D6D0-4D96-94F1-173C60A13875} + AspProviders + + + + + + + + + + + + + + + + + + + + + False + True + 6004 + / + + + False + False + + + False + + + + + \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/MVCAzureStore.csproj.user b/aspclassiccompiler/AzureStoreAsp/AzureStore/MVCAzureStore.csproj.user new file mode 100644 index 0000000..e0449fa --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/MVCAzureStore.csproj.user @@ -0,0 +1,36 @@ + + + ShowAllFiles + + + + + + + + CurrentPage + True + False + False + False + RunFiles + + + False + True + + + + + + + + + False + False + False + + + + + \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Properties/AssemblyInfo.cs b/aspclassiccompiler/AzureStoreAsp/AzureStore/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..736a5e6 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Properties/AssemblyInfo.cs @@ -0,0 +1,51 @@ +// ---------------------------------------------------------------------------------- +// Microsoft Developer & Platform Evangelism +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// ---------------------------------------------------------------------------------- +// The example companies, organizations, products, domain names, +// e-mail addresses, logos, people, places, and events depicted +// herein are fictitious. No association with any real company, +// organization, product, domain name, email address, logo, person, +// places, or events is intended or should be inferred. +// ---------------------------------------------------------------------------------- + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MVC Azure Store")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Azure Services Training Kit")] +[assembly: AssemblyCopyright("Copyright © 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("b125afdd-6de0-4884-b46f-9942153ba86c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftAjax.debug.js b/aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftAjax.debug.js new file mode 100644 index 0000000..054b46d --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftAjax.debug.js @@ -0,0 +1,6850 @@ +// Name: MicrosoftAjax.debug.js +// Assembly: System.Web.Extensions +// Version: 3.5.0.0 +// FileVersion: 3.5.30729.1 +//----------------------------------------------------------------------- +// Copyright (C) Microsoft Corporation. All rights reserved. +//----------------------------------------------------------------------- +// MicrosoftAjax.js +// Microsoft AJAX Framework. + +Function.__typeName = 'Function'; +Function.__class = true; +Function.createCallback = function Function$createCallback(method, context) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "method", type: Function}, + {name: "context", mayBeNull: true} + ]); + if (e) throw e; + return function() { + var l = arguments.length; + if (l > 0) { + var args = []; + for (var i = 0; i < l; i++) { + args[i] = arguments[i]; + } + args[l] = context; + return method.apply(this, args); + } + return method.call(this, context); + } +} +Function.createDelegate = function Function$createDelegate(instance, method) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "instance", mayBeNull: true}, + {name: "method", type: Function} + ]); + if (e) throw e; + return function() { + return method.apply(instance, arguments); + } +} +Function.emptyFunction = Function.emptyMethod = function Function$emptyMethod() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); +} +Function._validateParams = function Function$_validateParams(params, expectedParams) { + var e; + e = Function._validateParameterCount(params, expectedParams); + if (e) { + e.popStackFrame(); + return e; + } + for (var i=0; i < params.length; i++) { + var expectedParam = expectedParams[Math.min(i, expectedParams.length - 1)]; + var paramName = expectedParam.name; + if (expectedParam.parameterArray) { + paramName += "[" + (i - expectedParams.length + 1) + "]"; + } + e = Function._validateParameter(params[i], expectedParam, paramName); + if (e) { + e.popStackFrame(); + return e; + } + } + return null; +} +Function._validateParameterCount = function Function$_validateParameterCount(params, expectedParams) { + var maxParams = expectedParams.length; + var minParams = 0; + for (var i=0; i < expectedParams.length; i++) { + if (expectedParams[i].parameterArray) { + maxParams = Number.MAX_VALUE; + } + else if (!expectedParams[i].optional) { + minParams++; + } + } + if (params.length < minParams || params.length > maxParams) { + var e = Error.parameterCount(); + e.popStackFrame(); + return e; + } + return null; +} +Function._validateParameter = function Function$_validateParameter(param, expectedParam, paramName) { + var e; + var expectedType = expectedParam.type; + var expectedInteger = !!expectedParam.integer; + var expectedDomElement = !!expectedParam.domElement; + var mayBeNull = !!expectedParam.mayBeNull; + e = Function._validateParameterType(param, expectedType, expectedInteger, expectedDomElement, mayBeNull, paramName); + if (e) { + e.popStackFrame(); + return e; + } + var expectedElementType = expectedParam.elementType; + var elementMayBeNull = !!expectedParam.elementMayBeNull; + if (expectedType === Array && typeof(param) !== "undefined" && param !== null && + (expectedElementType || !elementMayBeNull)) { + var expectedElementInteger = !!expectedParam.elementInteger; + var expectedElementDomElement = !!expectedParam.elementDomElement; + for (var i=0; i < param.length; i++) { + var elem = param[i]; + e = Function._validateParameterType(elem, expectedElementType, + expectedElementInteger, expectedElementDomElement, elementMayBeNull, + paramName + "[" + i + "]"); + if (e) { + e.popStackFrame(); + return e; + } + } + } + return null; +} +Function._validateParameterType = function Function$_validateParameterType(param, expectedType, expectedInteger, expectedDomElement, mayBeNull, paramName) { + var e; + if (typeof(param) === "undefined") { + if (mayBeNull) { + return null; + } + else { + e = Error.argumentUndefined(paramName); + e.popStackFrame(); + return e; + } + } + if (param === null) { + if (mayBeNull) { + return null; + } + else { + e = Error.argumentNull(paramName); + e.popStackFrame(); + return e; + } + } + if (expectedType && expectedType.__enum) { + if (typeof(param) !== 'number') { + e = Error.argumentType(paramName, Object.getType(param), expectedType); + e.popStackFrame(); + return e; + } + if ((param % 1) === 0) { + var values = expectedType.prototype; + if (!expectedType.__flags || (param === 0)) { + for (var i in values) { + if (values[i] === param) return null; + } + } + else { + var v = param; + for (var i in values) { + var vali = values[i]; + if (vali === 0) continue; + if ((vali & param) === vali) { + v -= vali; + } + if (v === 0) return null; + } + } + } + e = Error.argumentOutOfRange(paramName, param, String.format(Sys.Res.enumInvalidValue, param, expectedType.getName())); + e.popStackFrame(); + return e; + } + if (expectedDomElement) { + var val; + if (typeof(param.nodeType) !== 'number') { + var doc = param.ownerDocument || param.document || param; + if (doc != param) { + var w = doc.defaultView || doc.parentWindow; + val = (w != param) && !(w.document && param.document && (w.document === param.document)); + } + else { + val = (typeof(doc.body) === 'undefined'); + } + } + else { + val = (param.nodeType === 3); + } + if (val) { + e = Error.argument(paramName, Sys.Res.argumentDomElement); + e.popStackFrame(); + return e; + } + } + if (expectedType && !expectedType.isInstanceOfType(param)) { + e = Error.argumentType(paramName, Object.getType(param), expectedType); + e.popStackFrame(); + return e; + } + if (expectedType === Number && expectedInteger) { + if ((param % 1) !== 0) { + e = Error.argumentOutOfRange(paramName, param, Sys.Res.argumentInteger); + e.popStackFrame(); + return e; + } + } + return null; +} + +Error.__typeName = 'Error'; +Error.__class = true; +Error.create = function Error$create(message, errorInfo) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "message", type: String, mayBeNull: true, optional: true}, + {name: "errorInfo", mayBeNull: true, optional: true} + ]); + if (e) throw e; + var e = new Error(message); + e.message = message; + if (errorInfo) { + for (var v in errorInfo) { + e[v] = errorInfo[v]; + } + } + e.popStackFrame(); + return e; +} +Error.argument = function Error$argument(paramName, message) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "paramName", type: String, mayBeNull: true, optional: true}, + {name: "message", type: String, mayBeNull: true, optional: true} + ]); + if (e) throw e; + var displayMessage = "Sys.ArgumentException: " + (message ? message : Sys.Res.argument); + if (paramName) { + displayMessage += "\n" + String.format(Sys.Res.paramName, paramName); + } + var e = Error.create(displayMessage, { name: "Sys.ArgumentException", paramName: paramName }); + e.popStackFrame(); + return e; +} +Error.argumentNull = function Error$argumentNull(paramName, message) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "paramName", type: String, mayBeNull: true, optional: true}, + {name: "message", type: String, mayBeNull: true, optional: true} + ]); + if (e) throw e; + var displayMessage = "Sys.ArgumentNullException: " + (message ? message : Sys.Res.argumentNull); + if (paramName) { + displayMessage += "\n" + String.format(Sys.Res.paramName, paramName); + } + var e = Error.create(displayMessage, { name: "Sys.ArgumentNullException", paramName: paramName }); + e.popStackFrame(); + return e; +} +Error.argumentOutOfRange = function Error$argumentOutOfRange(paramName, actualValue, message) { + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "paramName", type: String, mayBeNull: true, optional: true}, + {name: "actualValue", mayBeNull: true, optional: true}, + {name: "message", type: String, mayBeNull: true, optional: true} + ]); + if (e) throw e; + var displayMessage = "Sys.ArgumentOutOfRangeException: " + (message ? message : Sys.Res.argumentOutOfRange); + if (paramName) { + displayMessage += "\n" + String.format(Sys.Res.paramName, paramName); + } + if (typeof(actualValue) !== "undefined" && actualValue !== null) { + displayMessage += "\n" + String.format(Sys.Res.actualValue, actualValue); + } + var e = Error.create(displayMessage, { + name: "Sys.ArgumentOutOfRangeException", + paramName: paramName, + actualValue: actualValue + }); + e.popStackFrame(); + return e; +} +Error.argumentType = function Error$argumentType(paramName, actualType, expectedType, message) { + /// + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "paramName", type: String, mayBeNull: true, optional: true}, + {name: "actualType", type: Type, mayBeNull: true, optional: true}, + {name: "expectedType", type: Type, mayBeNull: true, optional: true}, + {name: "message", type: String, mayBeNull: true, optional: true} + ]); + if (e) throw e; + var displayMessage = "Sys.ArgumentTypeException: "; + if (message) { + displayMessage += message; + } + else if (actualType && expectedType) { + displayMessage += + String.format(Sys.Res.argumentTypeWithTypes, actualType.getName(), expectedType.getName()); + } + else { + displayMessage += Sys.Res.argumentType; + } + if (paramName) { + displayMessage += "\n" + String.format(Sys.Res.paramName, paramName); + } + var e = Error.create(displayMessage, { + name: "Sys.ArgumentTypeException", + paramName: paramName, + actualType: actualType, + expectedType: expectedType + }); + e.popStackFrame(); + return e; +} +Error.argumentUndefined = function Error$argumentUndefined(paramName, message) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "paramName", type: String, mayBeNull: true, optional: true}, + {name: "message", type: String, mayBeNull: true, optional: true} + ]); + if (e) throw e; + var displayMessage = "Sys.ArgumentUndefinedException: " + (message ? message : Sys.Res.argumentUndefined); + if (paramName) { + displayMessage += "\n" + String.format(Sys.Res.paramName, paramName); + } + var e = Error.create(displayMessage, { name: "Sys.ArgumentUndefinedException", paramName: paramName }); + e.popStackFrame(); + return e; +} +Error.format = function Error$format(message) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "message", type: String, mayBeNull: true, optional: true} + ]); + if (e) throw e; + var displayMessage = "Sys.FormatException: " + (message ? message : Sys.Res.format); + var e = Error.create(displayMessage, {name: 'Sys.FormatException'}); + e.popStackFrame(); + return e; +} +Error.invalidOperation = function Error$invalidOperation(message) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "message", type: String, mayBeNull: true, optional: true} + ]); + if (e) throw e; + var displayMessage = "Sys.InvalidOperationException: " + (message ? message : Sys.Res.invalidOperation); + var e = Error.create(displayMessage, {name: 'Sys.InvalidOperationException'}); + e.popStackFrame(); + return e; +} +Error.notImplemented = function Error$notImplemented(message) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "message", type: String, mayBeNull: true, optional: true} + ]); + if (e) throw e; + var displayMessage = "Sys.NotImplementedException: " + (message ? message : Sys.Res.notImplemented); + var e = Error.create(displayMessage, {name: 'Sys.NotImplementedException'}); + e.popStackFrame(); + return e; +} +Error.parameterCount = function Error$parameterCount(message) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "message", type: String, mayBeNull: true, optional: true} + ]); + if (e) throw e; + var displayMessage = "Sys.ParameterCountException: " + (message ? message : Sys.Res.parameterCount); + var e = Error.create(displayMessage, {name: 'Sys.ParameterCountException'}); + e.popStackFrame(); + return e; +} +Error.prototype.popStackFrame = function Error$popStackFrame() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (typeof(this.stack) === "undefined" || this.stack === null || + typeof(this.fileName) === "undefined" || this.fileName === null || + typeof(this.lineNumber) === "undefined" || this.lineNumber === null) { + return; + } + var stackFrames = this.stack.split("\n"); + var currentFrame = stackFrames[0]; + var pattern = this.fileName + ":" + this.lineNumber; + while(typeof(currentFrame) !== "undefined" && + currentFrame !== null && + currentFrame.indexOf(pattern) === -1) { + stackFrames.shift(); + currentFrame = stackFrames[0]; + } + var nextFrame = stackFrames[1]; + if (typeof(nextFrame) === "undefined" || nextFrame === null) { + return; + } + var nextFrameParts = nextFrame.match(/@(.*):(\d+)$/); + if (typeof(nextFrameParts) === "undefined" || nextFrameParts === null) { + return; + } + this.fileName = nextFrameParts[1]; + this.lineNumber = parseInt(nextFrameParts[2]); + stackFrames.shift(); + this.stack = stackFrames.join("\n"); +} + +Object.__typeName = 'Object'; +Object.__class = true; +Object.getType = function Object$getType(instance) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "instance"} + ]); + if (e) throw e; + var ctor = instance.constructor; + if (!ctor || (typeof(ctor) !== "function") || !ctor.__typeName || (ctor.__typeName === 'Object')) { + return Object; + } + return ctor; +} +Object.getTypeName = function Object$getTypeName(instance) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "instance"} + ]); + if (e) throw e; + return Object.getType(instance).getName(); +} + +String.__typeName = 'String'; +String.__class = true; +String.prototype.endsWith = function String$endsWith(suffix) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "suffix", type: String} + ]); + if (e) throw e; + return (this.substr(this.length - suffix.length) === suffix); +} +String.prototype.startsWith = function String$startsWith(prefix) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "prefix", type: String} + ]); + if (e) throw e; + return (this.substr(0, prefix.length) === prefix); +} +String.prototype.trim = function String$trim() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this.replace(/^\s+|\s+$/g, ''); +} +String.prototype.trimEnd = function String$trimEnd() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this.replace(/\s+$/, ''); +} +String.prototype.trimStart = function String$trimStart() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this.replace(/^\s+/, ''); +} +String.format = function String$format(format, args) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "format", type: String}, + {name: "args", mayBeNull: true, parameterArray: true} + ]); + if (e) throw e; + return String._toFormattedString(false, arguments); +} +String.localeFormat = function String$localeFormat(format, args) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "format", type: String}, + {name: "args", mayBeNull: true, parameterArray: true} + ]); + if (e) throw e; + return String._toFormattedString(true, arguments); +} +String._toFormattedString = function String$_toFormattedString(useLocale, args) { + var result = ''; + var format = args[0]; + for (var i=0;;) { + var open = format.indexOf('{', i); + var close = format.indexOf('}', i); + if ((open < 0) && (close < 0)) { + result += format.slice(i); + break; + } + if ((close > 0) && ((close < open) || (open < 0))) { + if (format.charAt(close + 1) !== '}') { + throw Error.argument('format', Sys.Res.stringFormatBraceMismatch); + } + result += format.slice(i, close + 1); + i = close + 2; + continue; + } + result += format.slice(i, open); + i = open + 1; + if (format.charAt(i) === '{') { + result += '{'; + i++; + continue; + } + if (close < 0) throw Error.argument('format', Sys.Res.stringFormatBraceMismatch); + var brace = format.substring(i, close); + var colonIndex = brace.indexOf(':'); + var argNumber = parseInt((colonIndex < 0)? brace : brace.substring(0, colonIndex), 10) + 1; + if (isNaN(argNumber)) throw Error.argument('format', Sys.Res.stringFormatInvalid); + var argFormat = (colonIndex < 0)? '' : brace.substring(colonIndex + 1); + var arg = args[argNumber]; + if (typeof(arg) === "undefined" || arg === null) { + arg = ''; + } + if (arg.toFormattedString) { + result += arg.toFormattedString(argFormat); + } + else if (useLocale && arg.localeFormat) { + result += arg.localeFormat(argFormat); + } + else if (arg.format) { + result += arg.format(argFormat); + } + else + result += arg.toString(); + i = close + 1; + } + return result; +} + +Boolean.__typeName = 'Boolean'; +Boolean.__class = true; +Boolean.parse = function Boolean$parse(value) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "value", type: String} + ]); + if (e) throw e; + var v = value.trim().toLowerCase(); + if (v === 'false') return false; + if (v === 'true') return true; + throw Error.argumentOutOfRange('value', value, Sys.Res.boolTrueOrFalse); +} + +Date.__typeName = 'Date'; +Date.__class = true; +Date._appendPreOrPostMatch = function Date$_appendPreOrPostMatch(preMatch, strBuilder) { + var quoteCount = 0; + var escaped = false; + for (var i = 0, il = preMatch.length; i < il; i++) { + var c = preMatch.charAt(i); + switch (c) { + case '\'': + if (escaped) strBuilder.append("'"); + else quoteCount++; + escaped = false; + break; + case '\\': + if (escaped) strBuilder.append("\\"); + escaped = !escaped; + break; + default: + strBuilder.append(c); + escaped = false; + break; + } + } + return quoteCount; +} +Date._expandFormat = function Date$_expandFormat(dtf, format) { + if (!format) { + format = "F"; + } + if (format.length === 1) { + switch (format) { + case "d": + return dtf.ShortDatePattern; + case "D": + return dtf.LongDatePattern; + case "t": + return dtf.ShortTimePattern; + case "T": + return dtf.LongTimePattern; + case "F": + return dtf.FullDateTimePattern; + case "M": case "m": + return dtf.MonthDayPattern; + case "s": + return dtf.SortableDateTimePattern; + case "Y": case "y": + return dtf.YearMonthPattern; + default: + throw Error.format(Sys.Res.formatInvalidString); + } + } + return format; +} +Date._expandYear = function Date$_expandYear(dtf, year) { + if (year < 100) { + var curr = new Date().getFullYear(); + year += curr - (curr % 100); + if (year > dtf.Calendar.TwoDigitYearMax) { + return year - 100; + } + } + return year; +} +Date._getParseRegExp = function Date$_getParseRegExp(dtf, format) { + if (!dtf._parseRegExp) { + dtf._parseRegExp = {}; + } + else if (dtf._parseRegExp[format]) { + return dtf._parseRegExp[format]; + } + var expFormat = Date._expandFormat(dtf, format); + expFormat = expFormat.replace(/([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1"); + var regexp = new Sys.StringBuilder("^"); + var groups = []; + var index = 0; + var quoteCount = 0; + var tokenRegExp = Date._getTokenRegExp(); + var match; + while ((match = tokenRegExp.exec(expFormat)) !== null) { + var preMatch = expFormat.slice(index, match.index); + index = tokenRegExp.lastIndex; + quoteCount += Date._appendPreOrPostMatch(preMatch, regexp); + if ((quoteCount%2) === 1) { + regexp.append(match[0]); + continue; + } + switch (match[0]) { + case 'dddd': case 'ddd': + case 'MMMM': case 'MMM': + regexp.append("(\\D+)"); + break; + case 'tt': case 't': + regexp.append("(\\D*)"); + break; + case 'yyyy': + regexp.append("(\\d{4})"); + break; + case 'fff': + regexp.append("(\\d{3})"); + break; + case 'ff': + regexp.append("(\\d{2})"); + break; + case 'f': + regexp.append("(\\d)"); + break; + case 'dd': case 'd': + case 'MM': case 'M': + case 'yy': case 'y': + case 'HH': case 'H': + case 'hh': case 'h': + case 'mm': case 'm': + case 'ss': case 's': + regexp.append("(\\d\\d?)"); + break; + case 'zzz': + regexp.append("([+-]?\\d\\d?:\\d{2})"); + break; + case 'zz': case 'z': + regexp.append("([+-]?\\d\\d?)"); + break; + } + Array.add(groups, match[0]); + } + Date._appendPreOrPostMatch(expFormat.slice(index), regexp); + regexp.append("$"); + var regexpStr = regexp.toString().replace(/\s+/g, "\\s+"); + var parseRegExp = {'regExp': regexpStr, 'groups': groups}; + dtf._parseRegExp[format] = parseRegExp; + return parseRegExp; +} +Date._getTokenRegExp = function Date$_getTokenRegExp() { + return /dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z/g; +} +Date.parseLocale = function Date$parseLocale(value, formats) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "value", type: String}, + {name: "formats", mayBeNull: true, optional: true, parameterArray: true} + ]); + if (e) throw e; + return Date._parse(value, Sys.CultureInfo.CurrentCulture, arguments); +} +Date.parseInvariant = function Date$parseInvariant(value, formats) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "value", type: String}, + {name: "formats", mayBeNull: true, optional: true, parameterArray: true} + ]); + if (e) throw e; + return Date._parse(value, Sys.CultureInfo.InvariantCulture, arguments); +} +Date._parse = function Date$_parse(value, cultureInfo, args) { + var custom = false; + for (var i = 1, il = args.length; i < il; i++) { + var format = args[i]; + if (format) { + custom = true; + var date = Date._parseExact(value, format, cultureInfo); + if (date) return date; + } + } + if (! custom) { + var formats = cultureInfo._getDateTimeFormats(); + for (var i = 0, il = formats.length; i < il; i++) { + var date = Date._parseExact(value, formats[i], cultureInfo); + if (date) return date; + } + } + return null; +} +Date._parseExact = function Date$_parseExact(value, format, cultureInfo) { + value = value.trim(); + var dtf = cultureInfo.dateTimeFormat; + var parseInfo = Date._getParseRegExp(dtf, format); + var match = new RegExp(parseInfo.regExp).exec(value); + if (match === null) return null; + + var groups = parseInfo.groups; + var year = null, month = null, date = null, weekDay = null; + var hour = 0, min = 0, sec = 0, msec = 0, tzMinOffset = null; + var pmHour = false; + for (var j = 0, jl = groups.length; j < jl; j++) { + var matchGroup = match[j+1]; + if (matchGroup) { + switch (groups[j]) { + case 'dd': case 'd': + date = parseInt(matchGroup, 10); + if ((date < 1) || (date > 31)) return null; + break; + case 'MMMM': + month = cultureInfo._getMonthIndex(matchGroup); + if ((month < 0) || (month > 11)) return null; + break; + case 'MMM': + month = cultureInfo._getAbbrMonthIndex(matchGroup); + if ((month < 0) || (month > 11)) return null; + break; + case 'M': case 'MM': + var month = parseInt(matchGroup, 10) - 1; + if ((month < 0) || (month > 11)) return null; + break; + case 'y': case 'yy': + year = Date._expandYear(dtf,parseInt(matchGroup, 10)); + if ((year < 0) || (year > 9999)) return null; + break; + case 'yyyy': + year = parseInt(matchGroup, 10); + if ((year < 0) || (year > 9999)) return null; + break; + case 'h': case 'hh': + hour = parseInt(matchGroup, 10); + if (hour === 12) hour = 0; + if ((hour < 0) || (hour > 11)) return null; + break; + case 'H': case 'HH': + hour = parseInt(matchGroup, 10); + if ((hour < 0) || (hour > 23)) return null; + break; + case 'm': case 'mm': + min = parseInt(matchGroup, 10); + if ((min < 0) || (min > 59)) return null; + break; + case 's': case 'ss': + sec = parseInt(matchGroup, 10); + if ((sec < 0) || (sec > 59)) return null; + break; + case 'tt': case 't': + var upperToken = matchGroup.toUpperCase(); + pmHour = (upperToken === dtf.PMDesignator.toUpperCase()); + if (!pmHour && (upperToken !== dtf.AMDesignator.toUpperCase())) return null; + break; + case 'f': + msec = parseInt(matchGroup, 10) * 100; + if ((msec < 0) || (msec > 999)) return null; + break; + case 'ff': + msec = parseInt(matchGroup, 10) * 10; + if ((msec < 0) || (msec > 999)) return null; + break; + case 'fff': + msec = parseInt(matchGroup, 10); + if ((msec < 0) || (msec > 999)) return null; + break; + case 'dddd': + weekDay = cultureInfo._getDayIndex(matchGroup); + if ((weekDay < 0) || (weekDay > 6)) return null; + break; + case 'ddd': + weekDay = cultureInfo._getAbbrDayIndex(matchGroup); + if ((weekDay < 0) || (weekDay > 6)) return null; + break; + case 'zzz': + var offsets = matchGroup.split(/:/); + if (offsets.length !== 2) return null; + var hourOffset = parseInt(offsets[0], 10); + if ((hourOffset < -12) || (hourOffset > 13)) return null; + var minOffset = parseInt(offsets[1], 10); + if ((minOffset < 0) || (minOffset > 59)) return null; + tzMinOffset = (hourOffset * 60) + (matchGroup.startsWith('-')? -minOffset : minOffset); + break; + case 'z': case 'zz': + var hourOffset = parseInt(matchGroup, 10); + if ((hourOffset < -12) || (hourOffset > 13)) return null; + tzMinOffset = hourOffset * 60; + break; + } + } + } + var result = new Date(); + if (year === null) { + year = result.getFullYear(); + } + if (month === null) { + month = result.getMonth(); + } + if (date === null) { + date = result.getDate(); + } + result.setFullYear(year, month, date); + if (result.getDate() !== date) return null; + if ((weekDay !== null) && (result.getDay() !== weekDay)) { + return null; + } + if (pmHour && (hour < 12)) { + hour += 12; + } + result.setHours(hour, min, sec, msec); + if (tzMinOffset !== null) { + var adjustedMin = result.getMinutes() - (tzMinOffset + result.getTimezoneOffset()); + result.setHours(result.getHours() + parseInt(adjustedMin/60, 10), adjustedMin%60); + } + return result; +} +Date.prototype.format = function Date$format(format) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "format", type: String} + ]); + if (e) throw e; + return this._toFormattedString(format, Sys.CultureInfo.InvariantCulture); +} +Date.prototype.localeFormat = function Date$localeFormat(format) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "format", type: String} + ]); + if (e) throw e; + return this._toFormattedString(format, Sys.CultureInfo.CurrentCulture); +} +Date.prototype._toFormattedString = function Date$_toFormattedString(format, cultureInfo) { + if (!format || (format.length === 0) || (format === 'i')) { + if (cultureInfo && (cultureInfo.name.length > 0)) { + return this.toLocaleString(); + } + else { + return this.toString(); + } + } + var dtf = cultureInfo.dateTimeFormat; + format = Date._expandFormat(dtf, format); + var ret = new Sys.StringBuilder(); + var hour; + function addLeadingZero(num) { + if (num < 10) { + return '0' + num; + } + return num.toString(); + } + function addLeadingZeros(num) { + if (num < 10) { + return '00' + num; + } + if (num < 100) { + return '0' + num; + } + return num.toString(); + } + var quoteCount = 0; + var tokenRegExp = Date._getTokenRegExp(); + for (;;) { + var index = tokenRegExp.lastIndex; + var ar = tokenRegExp.exec(format); + var preMatch = format.slice(index, ar ? ar.index : format.length); + quoteCount += Date._appendPreOrPostMatch(preMatch, ret); + if (!ar) break; + if ((quoteCount%2) === 1) { + ret.append(ar[0]); + continue; + } + switch (ar[0]) { + case "dddd": + ret.append(dtf.DayNames[this.getDay()]); + break; + case "ddd": + ret.append(dtf.AbbreviatedDayNames[this.getDay()]); + break; + case "dd": + ret.append(addLeadingZero(this.getDate())); + break; + case "d": + ret.append(this.getDate()); + break; + case "MMMM": + ret.append(dtf.MonthNames[this.getMonth()]); + break; + case "MMM": + ret.append(dtf.AbbreviatedMonthNames[this.getMonth()]); + break; + case "MM": + ret.append(addLeadingZero(this.getMonth() + 1)); + break; + case "M": + ret.append(this.getMonth() + 1); + break; + case "yyyy": + ret.append(this.getFullYear()); + break; + case "yy": + ret.append(addLeadingZero(this.getFullYear() % 100)); + break; + case "y": + ret.append(this.getFullYear() % 100); + break; + case "hh": + hour = this.getHours() % 12; + if (hour === 0) hour = 12; + ret.append(addLeadingZero(hour)); + break; + case "h": + hour = this.getHours() % 12; + if (hour === 0) hour = 12; + ret.append(hour); + break; + case "HH": + ret.append(addLeadingZero(this.getHours())); + break; + case "H": + ret.append(this.getHours()); + break; + case "mm": + ret.append(addLeadingZero(this.getMinutes())); + break; + case "m": + ret.append(this.getMinutes()); + break; + case "ss": + ret.append(addLeadingZero(this.getSeconds())); + break; + case "s": + ret.append(this.getSeconds()); + break; + case "tt": + ret.append((this.getHours() < 12) ? dtf.AMDesignator : dtf.PMDesignator); + break; + case "t": + ret.append(((this.getHours() < 12) ? dtf.AMDesignator : dtf.PMDesignator).charAt(0)); + break; + case "f": + ret.append(addLeadingZeros(this.getMilliseconds()).charAt(0)); + break; + case "ff": + ret.append(addLeadingZeros(this.getMilliseconds()).substr(0, 2)); + break; + case "fff": + ret.append(addLeadingZeros(this.getMilliseconds())); + break; + case "z": + hour = this.getTimezoneOffset() / 60; + ret.append(((hour <= 0) ? '+' : '-') + Math.floor(Math.abs(hour))); + break; + case "zz": + hour = this.getTimezoneOffset() / 60; + ret.append(((hour <= 0) ? '+' : '-') + addLeadingZero(Math.floor(Math.abs(hour)))); + break; + case "zzz": + hour = this.getTimezoneOffset() / 60; + ret.append(((hour <= 0) ? '+' : '-') + addLeadingZero(Math.floor(Math.abs(hour))) + + dtf.TimeSeparator + addLeadingZero(Math.abs(this.getTimezoneOffset() % 60))); + break; + } + } + return ret.toString(); +} + +Number.__typeName = 'Number'; +Number.__class = true; +Number.parseLocale = function Number$parseLocale(value) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "value", type: String} + ]); + if (e) throw e; + return Number._parse(value, Sys.CultureInfo.CurrentCulture); +} +Number.parseInvariant = function Number$parseInvariant(value) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "value", type: String} + ]); + if (e) throw e; + return Number._parse(value, Sys.CultureInfo.InvariantCulture); +} +Number._parse = function Number$_parse(value, cultureInfo) { + value = value.trim(); + + if (value.match(/^[+-]?infinity$/i)) { + return parseFloat(value); + } + if (value.match(/^0x[a-f0-9]+$/i)) { + return parseInt(value); + } + var numFormat = cultureInfo.numberFormat; + var signInfo = Number._parseNumberNegativePattern(value, numFormat, numFormat.NumberNegativePattern); + var sign = signInfo[0]; + var num = signInfo[1]; + + if ((sign === '') && (numFormat.NumberNegativePattern !== 1)) { + signInfo = Number._parseNumberNegativePattern(value, numFormat, 1); + sign = signInfo[0]; + num = signInfo[1]; + } + if (sign === '') sign = '+'; + + var exponent; + var intAndFraction; + var exponentPos = num.indexOf('e'); + if (exponentPos < 0) exponentPos = num.indexOf('E'); + if (exponentPos < 0) { + intAndFraction = num; + exponent = null; + } + else { + intAndFraction = num.substr(0, exponentPos); + exponent = num.substr(exponentPos + 1); + } + + var integer; + var fraction; + var decimalPos = intAndFraction.indexOf(numFormat.NumberDecimalSeparator); + if (decimalPos < 0) { + integer = intAndFraction; + fraction = null; + } + else { + integer = intAndFraction.substr(0, decimalPos); + fraction = intAndFraction.substr(decimalPos + numFormat.NumberDecimalSeparator.length); + } + + integer = integer.split(numFormat.NumberGroupSeparator).join(''); + var altNumGroupSeparator = numFormat.NumberGroupSeparator.replace(/\u00A0/g, " "); + if (numFormat.NumberGroupSeparator !== altNumGroupSeparator) { + integer = integer.split(altNumGroupSeparator).join(''); + } + + var p = sign + integer; + if (fraction !== null) { + p += '.' + fraction; + } + if (exponent !== null) { + var expSignInfo = Number._parseNumberNegativePattern(exponent, numFormat, 1); + if (expSignInfo[0] === '') { + expSignInfo[0] = '+'; + } + p += 'e' + expSignInfo[0] + expSignInfo[1]; + } + if (p.match(/^[+-]?\d*\.?\d*(e[+-]?\d+)?$/)) { + return parseFloat(p); + } + return Number.NaN; +} +Number._parseNumberNegativePattern = function Number$_parseNumberNegativePattern(value, numFormat, numberNegativePattern) { + var neg = numFormat.NegativeSign; + var pos = numFormat.PositiveSign; + switch (numberNegativePattern) { + case 4: + neg = ' ' + neg; + pos = ' ' + pos; + case 3: + if (value.endsWith(neg)) { + return ['-', value.substr(0, value.length - neg.length)]; + } + else if (value.endsWith(pos)) { + return ['+', value.substr(0, value.length - pos.length)]; + } + break; + case 2: + neg += ' '; + pos += ' '; + case 1: + if (value.startsWith(neg)) { + return ['-', value.substr(neg.length)]; + } + else if (value.startsWith(pos)) { + return ['+', value.substr(pos.length)]; + } + break; + case 0: + if (value.startsWith('(') && value.endsWith(')')) { + return ['-', value.substr(1, value.length - 2)]; + } + break; + } + return ['', value]; +} +Number.prototype.format = function Number$format(format) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "format", type: String} + ]); + if (e) throw e; + return this._toFormattedString(format, Sys.CultureInfo.InvariantCulture); +} +Number.prototype.localeFormat = function Number$localeFormat(format) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "format", type: String} + ]); + if (e) throw e; + return this._toFormattedString(format, Sys.CultureInfo.CurrentCulture); +} +Number.prototype._toFormattedString = function Number$_toFormattedString(format, cultureInfo) { + if (!format || (format.length === 0) || (format === 'i')) { + if (cultureInfo && (cultureInfo.name.length > 0)) { + return this.toLocaleString(); + } + else { + return this.toString(); + } + } + + var _percentPositivePattern = ["n %", "n%", "%n" ]; + var _percentNegativePattern = ["-n %", "-n%", "-%n"]; + var _numberNegativePattern = ["(n)","-n","- n","n-","n -"]; + var _currencyPositivePattern = ["$n","n$","$ n","n $"]; + var _currencyNegativePattern = ["($n)","-$n","$-n","$n-","(n$)","-n$","n-$","n$-","-n $","-$ n","n $-","$ n-","$ -n","n- $","($ n)","(n $)"]; + function zeroPad(str, count, left) { + for (var l=str.length; l < count; l++) { + str = (left ? ('0' + str) : (str + '0')); + } + return str; + } + + function expandNumber(number, precision, groupSizes, sep, decimalChar) { + + var curSize = groupSizes[0]; + var curGroupIndex = 1; + var factor = Math.pow(10, precision); + var rounded = (Math.round(number * factor) / factor); + if (!isFinite(rounded)) { + rounded = number; + } + number = rounded; + + var numberString = number.toString(); + var right = ""; + var exponent; + + + var split = numberString.split(/e/i); + numberString = split[0]; + exponent = (split.length > 1 ? parseInt(split[1]) : 0); + split = numberString.split('.'); + numberString = split[0]; + right = split.length > 1 ? split[1] : ""; + + var l; + if (exponent > 0) { + right = zeroPad(right, exponent, false); + numberString += right.slice(0, exponent); + right = right.substr(exponent); + } + else if (exponent < 0) { + exponent = -exponent; + numberString = zeroPad(numberString, exponent+1, true); + right = numberString.slice(-exponent, numberString.length) + right; + numberString = numberString.slice(0, -exponent); + } + if (precision > 0) { + if (right.length > precision) { + right = right.slice(0, precision); + } + else { + right = zeroPad(right, precision, false); + } + right = decimalChar + right; + } + else { + right = ""; + } + var stringIndex = numberString.length-1; + var ret = ""; + while (stringIndex >= 0) { + if (curSize === 0 || curSize > stringIndex) { + if (ret.length > 0) + return numberString.slice(0, stringIndex + 1) + sep + ret + right; + else + return numberString.slice(0, stringIndex + 1) + right; + } + if (ret.length > 0) + ret = numberString.slice(stringIndex - curSize + 1, stringIndex+1) + sep + ret; + else + ret = numberString.slice(stringIndex - curSize + 1, stringIndex+1); + stringIndex -= curSize; + if (curGroupIndex < groupSizes.length) { + curSize = groupSizes[curGroupIndex]; + curGroupIndex++; + } + } + return numberString.slice(0, stringIndex + 1) + sep + ret + right; + } + var nf = cultureInfo.numberFormat; + var number = Math.abs(this); + if (!format) + format = "D"; + var precision = -1; + if (format.length > 1) precision = parseInt(format.slice(1), 10); + var pattern; + switch (format.charAt(0)) { + case "d": + case "D": + pattern = 'n'; + if (precision !== -1) { + number = zeroPad(""+number, precision, true); + } + if (this < 0) number = -number; + break; + case "c": + case "C": + if (this < 0) pattern = _currencyNegativePattern[nf.CurrencyNegativePattern]; + else pattern = _currencyPositivePattern[nf.CurrencyPositivePattern]; + if (precision === -1) precision = nf.CurrencyDecimalDigits; + number = expandNumber(Math.abs(this), precision, nf.CurrencyGroupSizes, nf.CurrencyGroupSeparator, nf.CurrencyDecimalSeparator); + break; + case "n": + case "N": + if (this < 0) pattern = _numberNegativePattern[nf.NumberNegativePattern]; + else pattern = 'n'; + if (precision === -1) precision = nf.NumberDecimalDigits; + number = expandNumber(Math.abs(this), precision, nf.NumberGroupSizes, nf.NumberGroupSeparator, nf.NumberDecimalSeparator); + break; + case "p": + case "P": + if (this < 0) pattern = _percentNegativePattern[nf.PercentNegativePattern]; + else pattern = _percentPositivePattern[nf.PercentPositivePattern]; + if (precision === -1) precision = nf.PercentDecimalDigits; + number = expandNumber(Math.abs(this) * 100, precision, nf.PercentGroupSizes, nf.PercentGroupSeparator, nf.PercentDecimalSeparator); + break; + default: + throw Error.format(Sys.Res.formatBadFormatSpecifier); + } + var regex = /n|\$|-|%/g; + var ret = ""; + for (;;) { + var index = regex.lastIndex; + var ar = regex.exec(pattern); + ret += pattern.slice(index, ar ? ar.index : pattern.length); + if (!ar) + break; + switch (ar[0]) { + case "n": + ret += number; + break; + case "$": + ret += nf.CurrencySymbol; + break; + case "-": + ret += nf.NegativeSign; + break; + case "%": + ret += nf.PercentSymbol; + break; + } + } + return ret; +} + +RegExp.__typeName = 'RegExp'; +RegExp.__class = true; + +Array.__typeName = 'Array'; +Array.__class = true; +Array.add = Array.enqueue = function Array$enqueue(array, item) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "array", type: Array, elementMayBeNull: true}, + {name: "item", mayBeNull: true} + ]); + if (e) throw e; + array[array.length] = item; +} +Array.addRange = function Array$addRange(array, items) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "array", type: Array, elementMayBeNull: true}, + {name: "items", type: Array, elementMayBeNull: true} + ]); + if (e) throw e; + array.push.apply(array, items); +} +Array.clear = function Array$clear(array) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "array", type: Array, elementMayBeNull: true} + ]); + if (e) throw e; + array.length = 0; +} +Array.clone = function Array$clone(array) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "array", type: Array, elementMayBeNull: true} + ]); + if (e) throw e; + if (array.length === 1) { + return [array[0]]; + } + else { + return Array.apply(null, array); + } +} +Array.contains = function Array$contains(array, item) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "array", type: Array, elementMayBeNull: true}, + {name: "item", mayBeNull: true} + ]); + if (e) throw e; + return (Array.indexOf(array, item) >= 0); +} +Array.dequeue = function Array$dequeue(array) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "array", type: Array, elementMayBeNull: true} + ]); + if (e) throw e; + return array.shift(); +} +Array.forEach = function Array$forEach(array, method, instance) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "array", type: Array, elementMayBeNull: true}, + {name: "method", type: Function}, + {name: "instance", mayBeNull: true, optional: true} + ]); + if (e) throw e; + for (var i = 0, l = array.length; i < l; i++) { + var elt = array[i]; + if (typeof(elt) !== 'undefined') method.call(instance, elt, i, array); + } +} +Array.indexOf = function Array$indexOf(array, item, start) { + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "array", type: Array, elementMayBeNull: true}, + {name: "item", mayBeNull: true, optional: true}, + {name: "start", mayBeNull: true, optional: true} + ]); + if (e) throw e; + if (typeof(item) === "undefined") return -1; + var length = array.length; + if (length !== 0) { + start = start - 0; + if (isNaN(start)) { + start = 0; + } + else { + if (isFinite(start)) { + start = start - (start % 1); + } + if (start < 0) { + start = Math.max(0, length + start); + } + } + for (var i = start; i < length; i++) { + if ((typeof(array[i]) !== "undefined") && (array[i] === item)) { + return i; + } + } + } + return -1; +} +Array.insert = function Array$insert(array, index, item) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "array", type: Array, elementMayBeNull: true}, + {name: "index", mayBeNull: true}, + {name: "item", mayBeNull: true} + ]); + if (e) throw e; + array.splice(index, 0, item); +} +Array.parse = function Array$parse(value) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "value", type: String, mayBeNull: true} + ]); + if (e) throw e; + if (!value) return []; + var v = eval(value); + if (!Array.isInstanceOfType(v)) throw Error.argument('value', Sys.Res.arrayParseBadFormat); + return v; +} +Array.remove = function Array$remove(array, item) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "array", type: Array, elementMayBeNull: true}, + {name: "item", mayBeNull: true} + ]); + if (e) throw e; + var index = Array.indexOf(array, item); + if (index >= 0) { + array.splice(index, 1); + } + return (index >= 0); +} +Array.removeAt = function Array$removeAt(array, index) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "array", type: Array, elementMayBeNull: true}, + {name: "index", mayBeNull: true} + ]); + if (e) throw e; + array.splice(index, 1); +} + +if (!window) this.window = this; +window.Type = Function; +Type.__fullyQualifiedIdentifierRegExp = new RegExp("^[^.0-9 \\s|,;:&*=+\\-()\\[\\]{}^%#@!~\\n\\r\\t\\f\\\\]([^ \\s|,;:&*=+\\-()\\[\\]{}^%#@!~\\n\\r\\t\\f\\\\]*[^. \\s|,;:&*=+\\-()\\[\\]{}^%#@!~\\n\\r\\t\\f\\\\])?$", "i"); +Type.__identifierRegExp = new RegExp("^[^.0-9 \\s|,;:&*=+\\-()\\[\\]{}^%#@!~\\n\\r\\t\\f\\\\][^. \\s|,;:&*=+\\-()\\[\\]{}^%#@!~\\n\\r\\t\\f\\\\]*$", "i"); +Type.prototype.callBaseMethod = function Type$callBaseMethod(instance, name, baseArguments) { + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "instance"}, + {name: "name", type: String}, + {name: "baseArguments", type: Array, mayBeNull: true, optional: true, elementMayBeNull: true} + ]); + if (e) throw e; + var baseMethod = this.getBaseMethod(instance, name); + if (!baseMethod) throw Error.invalidOperation(String.format(Sys.Res.methodNotFound, name)); + if (!baseArguments) { + return baseMethod.apply(instance); + } + else { + return baseMethod.apply(instance, baseArguments); + } +} +Type.prototype.getBaseMethod = function Type$getBaseMethod(instance, name) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "instance"}, + {name: "name", type: String} + ]); + if (e) throw e; + if (!this.isInstanceOfType(instance)) throw Error.argumentType('instance', Object.getType(instance), this); + var baseType = this.getBaseType(); + if (baseType) { + var baseMethod = baseType.prototype[name]; + return (baseMethod instanceof Function) ? baseMethod : null; + } + return null; +} +Type.prototype.getBaseType = function Type$getBaseType() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return (typeof(this.__baseType) === "undefined") ? null : this.__baseType; +} +Type.prototype.getInterfaces = function Type$getInterfaces() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + var result = []; + var type = this; + while(type) { + var interfaces = type.__interfaces; + if (interfaces) { + for (var i = 0, l = interfaces.length; i < l; i++) { + var interfaceType = interfaces[i]; + if (!Array.contains(result, interfaceType)) { + result[result.length] = interfaceType; + } + } + } + type = type.__baseType; + } + return result; +} +Type.prototype.getName = function Type$getName() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return (typeof(this.__typeName) === "undefined") ? "" : this.__typeName; +} +Type.prototype.implementsInterface = function Type$implementsInterface(interfaceType) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "interfaceType", type: Type} + ]); + if (e) throw e; + this.resolveInheritance(); + var interfaceName = interfaceType.getName(); + var cache = this.__interfaceCache; + if (cache) { + var cacheEntry = cache[interfaceName]; + if (typeof(cacheEntry) !== 'undefined') return cacheEntry; + } + else { + cache = this.__interfaceCache = {}; + } + var baseType = this; + while (baseType) { + var interfaces = baseType.__interfaces; + if (interfaces) { + if (Array.indexOf(interfaces, interfaceType) !== -1) { + return cache[interfaceName] = true; + } + } + baseType = baseType.__baseType; + } + return cache[interfaceName] = false; +} +Type.prototype.inheritsFrom = function Type$inheritsFrom(parentType) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "parentType", type: Type} + ]); + if (e) throw e; + this.resolveInheritance(); + var baseType = this.__baseType; + while (baseType) { + if (baseType === parentType) { + return true; + } + baseType = baseType.__baseType; + } + return false; +} +Type.prototype.initializeBase = function Type$initializeBase(instance, baseArguments) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "instance"}, + {name: "baseArguments", type: Array, mayBeNull: true, optional: true, elementMayBeNull: true} + ]); + if (e) throw e; + if (!this.isInstanceOfType(instance)) throw Error.argumentType('instance', Object.getType(instance), this); + this.resolveInheritance(); + if (this.__baseType) { + if (!baseArguments) { + this.__baseType.apply(instance); + } + else { + this.__baseType.apply(instance, baseArguments); + } + } + return instance; +} +Type.prototype.isImplementedBy = function Type$isImplementedBy(instance) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "instance", mayBeNull: true} + ]); + if (e) throw e; + if (typeof(instance) === "undefined" || instance === null) return false; + var instanceType = Object.getType(instance); + return !!(instanceType.implementsInterface && instanceType.implementsInterface(this)); +} +Type.prototype.isInstanceOfType = function Type$isInstanceOfType(instance) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "instance", mayBeNull: true} + ]); + if (e) throw e; + if (typeof(instance) === "undefined" || instance === null) return false; + if (instance instanceof this) return true; + var instanceType = Object.getType(instance); + return !!(instanceType === this) || + (instanceType.inheritsFrom && instanceType.inheritsFrom(this)) || + (instanceType.implementsInterface && instanceType.implementsInterface(this)); +} +Type.prototype.registerClass = function Type$registerClass(typeName, baseType, interfaceTypes) { + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "typeName", type: String}, + {name: "baseType", type: Type, mayBeNull: true, optional: true}, + {name: "interfaceTypes", type: Type, parameterArray: true} + ]); + if (e) throw e; + if (!Type.__fullyQualifiedIdentifierRegExp.test(typeName)) throw Error.argument('typeName', Sys.Res.notATypeName); + var parsedName; + try { + parsedName = eval(typeName); + } + catch(e) { + throw Error.argument('typeName', Sys.Res.argumentTypeName); + } + if (parsedName !== this) throw Error.argument('typeName', Sys.Res.badTypeName); + if (Sys.__registeredTypes[typeName]) throw Error.invalidOperation(String.format(Sys.Res.typeRegisteredTwice, typeName)); + if ((arguments.length > 1) && (typeof(baseType) === 'undefined')) throw Error.argumentUndefined('baseType'); + if (baseType && !baseType.__class) throw Error.argument('baseType', Sys.Res.baseNotAClass); + this.prototype.constructor = this; + this.__typeName = typeName; + this.__class = true; + if (baseType) { + this.__baseType = baseType; + this.__basePrototypePending = true; + } + Sys.__upperCaseTypes[typeName.toUpperCase()] = this; + if (interfaceTypes) { + this.__interfaces = []; + this.resolveInheritance(); + for (var i = 2, l = arguments.length; i < l; i++) { + var interfaceType = arguments[i]; + if (!interfaceType.__interface) throw Error.argument('interfaceTypes[' + (i - 2) + ']', Sys.Res.notAnInterface); + for (var methodName in interfaceType.prototype) { + var method = interfaceType.prototype[methodName]; + if (!this.prototype[methodName]) { + this.prototype[methodName] = method; + } + } + this.__interfaces.push(interfaceType); + } + } + Sys.__registeredTypes[typeName] = true; + return this; +} +Type.prototype.registerInterface = function Type$registerInterface(typeName) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "typeName", type: String} + ]); + if (e) throw e; + if (!Type.__fullyQualifiedIdentifierRegExp.test(typeName)) throw Error.argument('typeName', Sys.Res.notATypeName); + var parsedName; + try { + parsedName = eval(typeName); + } + catch(e) { + throw Error.argument('typeName', Sys.Res.argumentTypeName); + } + if (parsedName !== this) throw Error.argument('typeName', Sys.Res.badTypeName); + if (Sys.__registeredTypes[typeName]) throw Error.invalidOperation(String.format(Sys.Res.typeRegisteredTwice, typeName)); + Sys.__upperCaseTypes[typeName.toUpperCase()] = this; + this.prototype.constructor = this; + this.__typeName = typeName; + this.__interface = true; + Sys.__registeredTypes[typeName] = true; + return this; +} +Type.prototype.resolveInheritance = function Type$resolveInheritance() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (this.__basePrototypePending) { + var baseType = this.__baseType; + baseType.resolveInheritance(); + for (var memberName in baseType.prototype) { + var memberValue = baseType.prototype[memberName]; + if (!this.prototype[memberName]) { + this.prototype[memberName] = memberValue; + } + } + delete this.__basePrototypePending; + } +} +Type.getRootNamespaces = function Type$getRootNamespaces() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return Array.clone(Sys.__rootNamespaces); +} +Type.isClass = function Type$isClass(type) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "type", mayBeNull: true} + ]); + if (e) throw e; + if ((typeof(type) === 'undefined') || (type === null)) return false; + return !!type.__class; +} +Type.isInterface = function Type$isInterface(type) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "type", mayBeNull: true} + ]); + if (e) throw e; + if ((typeof(type) === 'undefined') || (type === null)) return false; + return !!type.__interface; +} +Type.isNamespace = function Type$isNamespace(object) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "object", mayBeNull: true} + ]); + if (e) throw e; + if ((typeof(object) === 'undefined') || (object === null)) return false; + return !!object.__namespace; +} +Type.parse = function Type$parse(typeName, ns) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "typeName", type: String, mayBeNull: true}, + {name: "ns", mayBeNull: true, optional: true} + ]); + if (e) throw e; + var fn; + if (ns) { + fn = Sys.__upperCaseTypes[ns.getName().toUpperCase() + '.' + typeName.toUpperCase()]; + return fn || null; + } + if (!typeName) return null; + if (!Type.__htClasses) { + Type.__htClasses = {}; + } + fn = Type.__htClasses[typeName]; + if (!fn) { + fn = eval(typeName); + if (typeof(fn) !== 'function') throw Error.argument('typeName', Sys.Res.notATypeName); + Type.__htClasses[typeName] = fn; + } + return fn; +} +Type.registerNamespace = function Type$registerNamespace(namespacePath) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "namespacePath", type: String} + ]); + if (e) throw e; + if (!Type.__fullyQualifiedIdentifierRegExp.test(namespacePath)) throw Error.argument('namespacePath', Sys.Res.invalidNameSpace); + var rootObject = window; + var namespaceParts = namespacePath.split('.'); + for (var i = 0; i < namespaceParts.length; i++) { + var currentPart = namespaceParts[i]; + var ns = rootObject[currentPart]; + if (ns && !ns.__namespace) { + throw Error.invalidOperation(String.format(Sys.Res.namespaceContainsObject, namespaceParts.splice(0, i + 1).join('.'))); + } + if (!ns) { + ns = rootObject[currentPart] = { + __namespace: true, + __typeName: namespaceParts.slice(0, i + 1).join('.') + }; + if (i === 0) { + Sys.__rootNamespaces[Sys.__rootNamespaces.length] = ns; + } + var parsedName; + try { + parsedName = eval(ns.__typeName); + } + catch(e) { + parsedName = null; + } + if (parsedName !== ns) { + delete rootObject[currentPart]; + throw Error.argument('namespacePath', Sys.Res.invalidNameSpace); + } + ns.getName = function ns$getName() {return this.__typeName;} + } + rootObject = ns; + } +} +window.Sys = { + __namespace: true, + __typeName: "Sys", + getName: function() {return "Sys";}, + __upperCaseTypes: {} +}; +Sys.__rootNamespaces = [Sys]; +Sys.__registeredTypes = {}; + +Sys.IDisposable = function Sys$IDisposable() { + throw Error.notImplemented(); +} + function Sys$IDisposable$dispose() { + throw Error.notImplemented(); + } +Sys.IDisposable.prototype = { + dispose: Sys$IDisposable$dispose +} +Sys.IDisposable.registerInterface('Sys.IDisposable'); + +Sys.StringBuilder = function Sys$StringBuilder(initialText) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "initialText", mayBeNull: true, optional: true} + ]); + if (e) throw e; + this._parts = (typeof(initialText) !== 'undefined' && initialText !== null && initialText !== '') ? + [initialText.toString()] : []; + this._value = {}; + this._len = 0; +} + function Sys$StringBuilder$append(text) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "text", mayBeNull: true} + ]); + if (e) throw e; + this._parts[this._parts.length] = text; + } + function Sys$StringBuilder$appendLine(text) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "text", mayBeNull: true, optional: true} + ]); + if (e) throw e; + this._parts[this._parts.length] = + ((typeof(text) === 'undefined') || (text === null) || (text === '')) ? + '\r\n' : text + '\r\n'; + } + function Sys$StringBuilder$clear() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + this._parts = []; + this._value = {}; + this._len = 0; + } + function Sys$StringBuilder$isEmpty() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (this._parts.length === 0) return true; + return this.toString() === ''; + } + function Sys$StringBuilder$toString(separator) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "separator", type: String, mayBeNull: true, optional: true} + ]); + if (e) throw e; + separator = separator || ''; + var parts = this._parts; + if (this._len !== parts.length) { + this._value = {}; + this._len = parts.length; + } + var val = this._value; + if (typeof(val[separator]) === 'undefined') { + if (separator !== '') { + for (var i = 0; i < parts.length;) { + if ((typeof(parts[i]) === 'undefined') || (parts[i] === '') || (parts[i] === null)) { + parts.splice(i, 1); + } + else { + i++; + } + } + } + val[separator] = this._parts.join(separator); + } + return val[separator]; + } +Sys.StringBuilder.prototype = { + append: Sys$StringBuilder$append, + appendLine: Sys$StringBuilder$appendLine, + clear: Sys$StringBuilder$clear, + isEmpty: Sys$StringBuilder$isEmpty, + toString: Sys$StringBuilder$toString +} +Sys.StringBuilder.registerClass('Sys.StringBuilder'); + +if (!window.XMLHttpRequest) { + window.XMLHttpRequest = function window$XMLHttpRequest() { + var progIDs = [ 'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP' ]; + for (var i = 0, l = progIDs.length; i < l; i++) { + try { + return new ActiveXObject(progIDs[i]); + } + catch (ex) { + } + } + return null; + } +} + +Sys.Browser = {}; +Sys.Browser.InternetExplorer = {}; +Sys.Browser.Firefox = {}; +Sys.Browser.Safari = {}; +Sys.Browser.Opera = {}; +Sys.Browser.agent = null; +Sys.Browser.hasDebuggerStatement = false; +Sys.Browser.name = navigator.appName; +Sys.Browser.version = parseFloat(navigator.appVersion); +Sys.Browser.documentMode = 0; +if (navigator.userAgent.indexOf(' MSIE ') > -1) { + Sys.Browser.agent = Sys.Browser.InternetExplorer; + Sys.Browser.version = parseFloat(navigator.userAgent.match(/MSIE (\d+\.\d+)/)[1]); + if (Sys.Browser.version >= 8) { + if (document.documentMode >= 7) { + Sys.Browser.documentMode = document.documentMode; + } + } + Sys.Browser.hasDebuggerStatement = true; +} +else if (navigator.userAgent.indexOf(' Firefox/') > -1) { + Sys.Browser.agent = Sys.Browser.Firefox; + Sys.Browser.version = parseFloat(navigator.userAgent.match(/ Firefox\/(\d+\.\d+)/)[1]); + Sys.Browser.name = 'Firefox'; + Sys.Browser.hasDebuggerStatement = true; +} +else if (navigator.userAgent.indexOf(' AppleWebKit/') > -1) { + Sys.Browser.agent = Sys.Browser.Safari; + Sys.Browser.version = parseFloat(navigator.userAgent.match(/ AppleWebKit\/(\d+(\.\d+)?)/)[1]); + Sys.Browser.name = 'Safari'; +} +else if (navigator.userAgent.indexOf('Opera/') > -1) { + Sys.Browser.agent = Sys.Browser.Opera; +} +Type.registerNamespace('Sys.UI'); + +Sys._Debug = function Sys$_Debug() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); +} + function Sys$_Debug$_appendConsole(text) { + if ((typeof(Debug) !== 'undefined') && Debug.writeln) { + Debug.writeln(text); + } + if (window.console && window.console.log) { + window.console.log(text); + } + if (window.opera) { + window.opera.postError(text); + } + if (window.debugService) { + window.debugService.trace(text); + } + } + function Sys$_Debug$_appendTrace(text) { + var traceElement = document.getElementById('TraceConsole'); + if (traceElement && (traceElement.tagName.toUpperCase() === 'TEXTAREA')) { + traceElement.value += text + '\n'; + } + } + function Sys$_Debug$assert(condition, message, displayCaller) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "condition", type: Boolean}, + {name: "message", type: String, mayBeNull: true, optional: true}, + {name: "displayCaller", type: Boolean, optional: true} + ]); + if (e) throw e; + if (!condition) { + message = (displayCaller && this.assert.caller) ? + String.format(Sys.Res.assertFailedCaller, message, this.assert.caller) : + String.format(Sys.Res.assertFailed, message); + if (confirm(String.format(Sys.Res.breakIntoDebugger, message))) { + this.fail(message); + } + } + } + function Sys$_Debug$clearTrace() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + var traceElement = document.getElementById('TraceConsole'); + if (traceElement && (traceElement.tagName.toUpperCase() === 'TEXTAREA')) { + traceElement.value = ''; + } + } + function Sys$_Debug$fail(message) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "message", type: String, mayBeNull: true} + ]); + if (e) throw e; + this._appendConsole(message); + if (Sys.Browser.hasDebuggerStatement) { + eval('debugger'); + } + } + function Sys$_Debug$trace(text) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "text"} + ]); + if (e) throw e; + this._appendConsole(text); + this._appendTrace(text); + } + function Sys$_Debug$traceDump(object, name) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "object", mayBeNull: true}, + {name: "name", type: String, mayBeNull: true, optional: true} + ]); + if (e) throw e; + var text = this._traceDump(object, name, true); + } + function Sys$_Debug$_traceDump(object, name, recursive, indentationPadding, loopArray) { + name = name? name : 'traceDump'; + indentationPadding = indentationPadding? indentationPadding : ''; + if (object === null) { + this.trace(indentationPadding + name + ': null'); + return; + } + switch(typeof(object)) { + case 'undefined': + this.trace(indentationPadding + name + ': Undefined'); + break; + case 'number': case 'string': case 'boolean': + this.trace(indentationPadding + name + ': ' + object); + break; + default: + if (Date.isInstanceOfType(object) || RegExp.isInstanceOfType(object)) { + this.trace(indentationPadding + name + ': ' + object.toString()); + break; + } + if (!loopArray) { + loopArray = []; + } + else if (Array.contains(loopArray, object)) { + this.trace(indentationPadding + name + ': ...'); + return; + } + Array.add(loopArray, object); + if ((object == window) || (object === document) || + (window.HTMLElement && (object instanceof HTMLElement)) || + (typeof(object.nodeName) === 'string')) { + var tag = object.tagName? object.tagName : 'DomElement'; + if (object.id) { + tag += ' - ' + object.id; + } + this.trace(indentationPadding + name + ' {' + tag + '}'); + } + else { + var typeName = Object.getTypeName(object); + this.trace(indentationPadding + name + (typeof(typeName) === 'string' ? ' {' + typeName + '}' : '')); + if ((indentationPadding === '') || recursive) { + indentationPadding += " "; + var i, length, properties, p, v; + if (Array.isInstanceOfType(object)) { + length = object.length; + for (i = 0; i < length; i++) { + this._traceDump(object[i], '[' + i + ']', recursive, indentationPadding, loopArray); + } + } + else { + for (p in object) { + v = object[p]; + if (!Function.isInstanceOfType(v)) { + this._traceDump(v, p, recursive, indentationPadding, loopArray); + } + } + } + } + } + Array.remove(loopArray, object); + } + } +Sys._Debug.prototype = { + _appendConsole: Sys$_Debug$_appendConsole, + _appendTrace: Sys$_Debug$_appendTrace, + assert: Sys$_Debug$assert, + clearTrace: Sys$_Debug$clearTrace, + fail: Sys$_Debug$fail, + trace: Sys$_Debug$trace, + traceDump: Sys$_Debug$traceDump, + _traceDump: Sys$_Debug$_traceDump +} +Sys._Debug.registerClass('Sys._Debug'); +Sys.Debug = new Sys._Debug(); + Sys.Debug.isDebug = true; + +function Sys$Enum$parse(value, ignoreCase) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "value", type: String}, + {name: "ignoreCase", type: Boolean, optional: true} + ]); + if (e) throw e; + var values, parsed, val; + if (ignoreCase) { + values = this.__lowerCaseValues; + if (!values) { + this.__lowerCaseValues = values = {}; + var prototype = this.prototype; + for (var name in prototype) { + values[name.toLowerCase()] = prototype[name]; + } + } + } + else { + values = this.prototype; + } + if (!this.__flags) { + val = (ignoreCase ? value.toLowerCase() : value); + parsed = values[val.trim()]; + if (typeof(parsed) !== 'number') throw Error.argument('value', String.format(Sys.Res.enumInvalidValue, value, this.__typeName)); + return parsed; + } + else { + var parts = (ignoreCase ? value.toLowerCase() : value).split(','); + var v = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var part = parts[i].trim(); + parsed = values[part]; + if (typeof(parsed) !== 'number') throw Error.argument('value', String.format(Sys.Res.enumInvalidValue, value.split(',')[i].trim(), this.__typeName)); + v |= parsed; + } + return v; + } +} +function Sys$Enum$toString(value) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "value", mayBeNull: true, optional: true} + ]); + if (e) throw e; + if ((typeof(value) === 'undefined') || (value === null)) return this.__string; + if ((typeof(value) != 'number') || ((value % 1) !== 0)) throw Error.argumentType('value', Object.getType(value), this); + var values = this.prototype; + var i; + if (!this.__flags || (value === 0)) { + for (i in values) { + if (values[i] === value) { + return i; + } + } + } + else { + var sorted = this.__sortedValues; + if (!sorted) { + sorted = []; + for (i in values) { + sorted[sorted.length] = {key: i, value: values[i]}; + } + sorted.sort(function(a, b) { + return a.value - b.value; + }); + this.__sortedValues = sorted; + } + var parts = []; + var v = value; + for (i = sorted.length - 1; i >= 0; i--) { + var kvp = sorted[i]; + var vali = kvp.value; + if (vali === 0) continue; + if ((vali & value) === vali) { + parts[parts.length] = kvp.key; + v -= vali; + if (v === 0) break; + } + } + if (parts.length && v === 0) return parts.reverse().join(', '); + } + throw Error.argumentOutOfRange('value', value, String.format(Sys.Res.enumInvalidValue, value, this.__typeName)); +} +Type.prototype.registerEnum = function Type$registerEnum(name, flags) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "name", type: String}, + {name: "flags", type: Boolean, optional: true} + ]); + if (e) throw e; + if (!Type.__fullyQualifiedIdentifierRegExp.test(name)) throw Error.argument('name', Sys.Res.notATypeName); + var parsedName; + try { + parsedName = eval(name); + } + catch(e) { + throw Error.argument('name', Sys.Res.argumentTypeName); + } + if (parsedName !== this) throw Error.argument('name', Sys.Res.badTypeName); + if (Sys.__registeredTypes[name]) throw Error.invalidOperation(String.format(Sys.Res.typeRegisteredTwice, name)); + for (var i in this.prototype) { + var val = this.prototype[i]; + if (!Type.__identifierRegExp.test(i)) throw Error.invalidOperation(String.format(Sys.Res.enumInvalidValueName, i)); + if (typeof(val) !== 'number' || (val % 1) !== 0) throw Error.invalidOperation(Sys.Res.enumValueNotInteger); + if (typeof(this[i]) !== 'undefined') throw Error.invalidOperation(String.format(Sys.Res.enumReservedName, i)); + } + Sys.__upperCaseTypes[name.toUpperCase()] = this; + for (var i in this.prototype) { + this[i] = this.prototype[i]; + } + this.__typeName = name; + this.parse = Sys$Enum$parse; + this.__string = this.toString(); + this.toString = Sys$Enum$toString; + this.__flags = flags; + this.__enum = true; + Sys.__registeredTypes[name] = true; +} +Type.isEnum = function Type$isEnum(type) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "type", mayBeNull: true} + ]); + if (e) throw e; + if ((typeof(type) === 'undefined') || (type === null)) return false; + return !!type.__enum; +} +Type.isFlags = function Type$isFlags(type) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "type", mayBeNull: true} + ]); + if (e) throw e; + if ((typeof(type) === 'undefined') || (type === null)) return false; + return !!type.__flags; +} + +Sys.EventHandlerList = function Sys$EventHandlerList() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + this._list = {}; +} + function Sys$EventHandlerList$addHandler(id, handler) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "id", type: String}, + {name: "handler", type: Function} + ]); + if (e) throw e; + Array.add(this._getEvent(id, true), handler); + } + function Sys$EventHandlerList$removeHandler(id, handler) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "id", type: String}, + {name: "handler", type: Function} + ]); + if (e) throw e; + var evt = this._getEvent(id); + if (!evt) return; + Array.remove(evt, handler); + } + function Sys$EventHandlerList$getHandler(id) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "id", type: String} + ]); + if (e) throw e; + var evt = this._getEvent(id); + if (!evt || (evt.length === 0)) return null; + evt = Array.clone(evt); + return function(source, args) { + for (var i = 0, l = evt.length; i < l; i++) { + evt[i](source, args); + } + }; + } + function Sys$EventHandlerList$_getEvent(id, create) { + if (!this._list[id]) { + if (!create) return null; + this._list[id] = []; + } + return this._list[id]; + } +Sys.EventHandlerList.prototype = { + addHandler: Sys$EventHandlerList$addHandler, + removeHandler: Sys$EventHandlerList$removeHandler, + getHandler: Sys$EventHandlerList$getHandler, + _getEvent: Sys$EventHandlerList$_getEvent +} +Sys.EventHandlerList.registerClass('Sys.EventHandlerList'); + +Sys.EventArgs = function Sys$EventArgs() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); +} +Sys.EventArgs.registerClass('Sys.EventArgs'); +Sys.EventArgs.Empty = new Sys.EventArgs(); + +Sys.CancelEventArgs = function Sys$CancelEventArgs() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + Sys.CancelEventArgs.initializeBase(this); + this._cancel = false; +} + function Sys$CancelEventArgs$get_cancel() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._cancel; + } + function Sys$CancelEventArgs$set_cancel(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Boolean}]); + if (e) throw e; + this._cancel = value; + } +Sys.CancelEventArgs.prototype = { + get_cancel: Sys$CancelEventArgs$get_cancel, + set_cancel: Sys$CancelEventArgs$set_cancel +} +Sys.CancelEventArgs.registerClass('Sys.CancelEventArgs', Sys.EventArgs); + +Sys.INotifyPropertyChange = function Sys$INotifyPropertyChange() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); +} + function Sys$INotifyPropertyChange$add_propertyChanged(handler) { + /// + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + throw Error.notImplemented(); + } + function Sys$INotifyPropertyChange$remove_propertyChanged(handler) { + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + throw Error.notImplemented(); + } +Sys.INotifyPropertyChange.prototype = { + add_propertyChanged: Sys$INotifyPropertyChange$add_propertyChanged, + remove_propertyChanged: Sys$INotifyPropertyChange$remove_propertyChanged +} +Sys.INotifyPropertyChange.registerInterface('Sys.INotifyPropertyChange'); + +Sys.PropertyChangedEventArgs = function Sys$PropertyChangedEventArgs(propertyName) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "propertyName", type: String} + ]); + if (e) throw e; + Sys.PropertyChangedEventArgs.initializeBase(this); + this._propertyName = propertyName; +} + + function Sys$PropertyChangedEventArgs$get_propertyName() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._propertyName; + } +Sys.PropertyChangedEventArgs.prototype = { + get_propertyName: Sys$PropertyChangedEventArgs$get_propertyName +} +Sys.PropertyChangedEventArgs.registerClass('Sys.PropertyChangedEventArgs', Sys.EventArgs); + +Sys.INotifyDisposing = function Sys$INotifyDisposing() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); +} + function Sys$INotifyDisposing$add_disposing(handler) { + /// + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + throw Error.notImplemented(); + } + function Sys$INotifyDisposing$remove_disposing(handler) { + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + throw Error.notImplemented(); + } +Sys.INotifyDisposing.prototype = { + add_disposing: Sys$INotifyDisposing$add_disposing, + remove_disposing: Sys$INotifyDisposing$remove_disposing +} +Sys.INotifyDisposing.registerInterface("Sys.INotifyDisposing"); + +Sys.Component = function Sys$Component() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (Sys.Application) Sys.Application.registerDisposableObject(this); +} + function Sys$Component$get_events() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (!this._events) { + this._events = new Sys.EventHandlerList(); + } + return this._events; + } + function Sys$Component$get_id() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._id; + } + function Sys$Component$set_id(value) { + var e = Function._validateParams(arguments, [{name: "value", type: String}]); + if (e) throw e; + if (this._idSet) throw Error.invalidOperation(Sys.Res.componentCantSetIdTwice); + this._idSet = true; + var oldId = this.get_id(); + if (oldId && Sys.Application.findComponent(oldId)) throw Error.invalidOperation(Sys.Res.componentCantSetIdAfterAddedToApp); + this._id = value; + } + function Sys$Component$get_isInitialized() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._initialized; + } + function Sys$Component$get_isUpdating() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._updating; + } + function Sys$Component$add_disposing(handler) { + /// + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this.get_events().addHandler("disposing", handler); + } + function Sys$Component$remove_disposing(handler) { + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this.get_events().removeHandler("disposing", handler); + } + function Sys$Component$add_propertyChanged(handler) { + /// + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this.get_events().addHandler("propertyChanged", handler); + } + function Sys$Component$remove_propertyChanged(handler) { + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this.get_events().removeHandler("propertyChanged", handler); + } + function Sys$Component$beginUpdate() { + this._updating = true; + } + function Sys$Component$dispose() { + if (this._events) { + var handler = this._events.getHandler("disposing"); + if (handler) { + handler(this, Sys.EventArgs.Empty); + } + } + delete this._events; + Sys.Application.unregisterDisposableObject(this); + Sys.Application.removeComponent(this); + } + function Sys$Component$endUpdate() { + this._updating = false; + if (!this._initialized) this.initialize(); + this.updated(); + } + function Sys$Component$initialize() { + this._initialized = true; + } + function Sys$Component$raisePropertyChanged(propertyName) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "propertyName", type: String} + ]); + if (e) throw e; + if (!this._events) return; + var handler = this._events.getHandler("propertyChanged"); + if (handler) { + handler(this, new Sys.PropertyChangedEventArgs(propertyName)); + } + } + function Sys$Component$updated() { + } +Sys.Component.prototype = { + _id: null, + _idSet: false, + _initialized: false, + _updating: false, + get_events: Sys$Component$get_events, + get_id: Sys$Component$get_id, + set_id: Sys$Component$set_id, + get_isInitialized: Sys$Component$get_isInitialized, + get_isUpdating: Sys$Component$get_isUpdating, + add_disposing: Sys$Component$add_disposing, + remove_disposing: Sys$Component$remove_disposing, + add_propertyChanged: Sys$Component$add_propertyChanged, + remove_propertyChanged: Sys$Component$remove_propertyChanged, + beginUpdate: Sys$Component$beginUpdate, + dispose: Sys$Component$dispose, + endUpdate: Sys$Component$endUpdate, + initialize: Sys$Component$initialize, + raisePropertyChanged: Sys$Component$raisePropertyChanged, + updated: Sys$Component$updated +} +Sys.Component.registerClass('Sys.Component', null, Sys.IDisposable, Sys.INotifyPropertyChange, Sys.INotifyDisposing); +function Sys$Component$_setProperties(target, properties) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "target"}, + {name: "properties"} + ]); + if (e) throw e; + var current; + var targetType = Object.getType(target); + var isObject = (targetType === Object) || (targetType === Sys.UI.DomElement); + var isComponent = Sys.Component.isInstanceOfType(target) && !target.get_isUpdating(); + if (isComponent) target.beginUpdate(); + for (var name in properties) { + var val = properties[name]; + var getter = isObject ? null : target["get_" + name]; + if (isObject || typeof(getter) !== 'function') { + var targetVal = target[name]; + if (!isObject && typeof(targetVal) === 'undefined') throw Error.invalidOperation(String.format(Sys.Res.propertyUndefined, name)); + if (!val || (typeof(val) !== 'object') || (isObject && !targetVal)) { + target[name] = val; + } + else { + Sys$Component$_setProperties(targetVal, val); + } + } + else { + var setter = target["set_" + name]; + if (typeof(setter) === 'function') { + setter.apply(target, [val]); + } + else if (val instanceof Array) { + current = getter.apply(target); + if (!(current instanceof Array)) throw new Error.invalidOperation(String.format(Sys.Res.propertyNotAnArray, name)); + for (var i = 0, j = current.length, l= val.length; i < l; i++, j++) { + current[j] = val[i]; + } + } + else if ((typeof(val) === 'object') && (Object.getType(val) === Object)) { + current = getter.apply(target); + if ((typeof(current) === 'undefined') || (current === null)) throw new Error.invalidOperation(String.format(Sys.Res.propertyNullOrUndefined, name)); + Sys$Component$_setProperties(current, val); + } + else { + throw new Error.invalidOperation(String.format(Sys.Res.propertyNotWritable, name)); + } + } + } + if (isComponent) target.endUpdate(); +} +function Sys$Component$_setReferences(component, references) { + for (var name in references) { + var setter = component["set_" + name]; + var reference = $find(references[name]); + if (typeof(setter) !== 'function') throw new Error.invalidOperation(String.format(Sys.Res.propertyNotWritable, name)); + if (!reference) throw Error.invalidOperation(String.format(Sys.Res.referenceNotFound, references[name])); + setter.apply(component, [reference]); + } +} +var $create = Sys.Component.create = function Sys$Component$create(type, properties, events, references, element) { + /// + /// + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "type", type: Type}, + {name: "properties", mayBeNull: true, optional: true}, + {name: "events", mayBeNull: true, optional: true}, + {name: "references", mayBeNull: true, optional: true}, + {name: "element", mayBeNull: true, domElement: true, optional: true} + ]); + if (e) throw e; + if (!type.inheritsFrom(Sys.Component)) { + throw Error.argument('type', String.format(Sys.Res.createNotComponent, type.getName())); + } + if (type.inheritsFrom(Sys.UI.Behavior) || type.inheritsFrom(Sys.UI.Control)) { + if (!element) throw Error.argument('element', Sys.Res.createNoDom); + } + else if (element) throw Error.argument('element', Sys.Res.createComponentOnDom); + var component = (element ? new type(element): new type()); + var app = Sys.Application; + var creatingComponents = app.get_isCreatingComponents(); + component.beginUpdate(); + if (properties) { + Sys$Component$_setProperties(component, properties); + } + if (events) { + for (var name in events) { + if (!(component["add_" + name] instanceof Function)) throw new Error.invalidOperation(String.format(Sys.Res.undefinedEvent, name)); + if (!(events[name] instanceof Function)) throw new Error.invalidOperation(Sys.Res.eventHandlerNotFunction); + component["add_" + name](events[name]); + } + } + if (component.get_id()) { + app.addComponent(component); + } + if (creatingComponents) { + app._createdComponents[app._createdComponents.length] = component; + if (references) { + app._addComponentToSecondPass(component, references); + } + else { + component.endUpdate(); + } + } + else { + if (references) { + Sys$Component$_setReferences(component, references); + } + component.endUpdate(); + } + return component; +} + +Sys.UI.MouseButton = function Sys$UI$MouseButton() { + /// + /// + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); +} +Sys.UI.MouseButton.prototype = { + leftButton: 0, + middleButton: 1, + rightButton: 2 +} +Sys.UI.MouseButton.registerEnum("Sys.UI.MouseButton"); + +Sys.UI.Key = function Sys$UI$Key() { + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); +} +Sys.UI.Key.prototype = { + backspace: 8, + tab: 9, + enter: 13, + esc: 27, + space: 32, + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + del: 127 +} +Sys.UI.Key.registerEnum("Sys.UI.Key"); + +Sys.UI.Point = function Sys$UI$Point(x, y) { + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "x", type: Number, integer: true}, + {name: "y", type: Number, integer: true} + ]); + if (e) throw e; + this.x = x; + this.y = y; +} +Sys.UI.Point.registerClass('Sys.UI.Point'); + +Sys.UI.Bounds = function Sys$UI$Bounds(x, y, width, height) { + /// + /// + /// + /// + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "x", type: Number, integer: true}, + {name: "y", type: Number, integer: true}, + {name: "height", type: Number, integer: true}, + {name: "width", type: Number, integer: true} + ]); + if (e) throw e; + this.x = x; + this.y = y; + this.height = height; + this.width = width; +} +Sys.UI.Bounds.registerClass('Sys.UI.Bounds'); + +Sys.UI.DomEvent = function Sys$UI$DomEvent(eventObject) { + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "eventObject"} + ]); + if (e) throw e; + var e = eventObject; + var etype = this.type = e.type.toLowerCase(); + this.rawEvent = e; + this.altKey = e.altKey; + if (typeof(e.button) !== 'undefined') { + this.button = (typeof(e.which) !== 'undefined') ? e.button : + (e.button === 4) ? Sys.UI.MouseButton.middleButton : + (e.button === 2) ? Sys.UI.MouseButton.rightButton : + Sys.UI.MouseButton.leftButton; + } + if (etype === 'keypress') { + this.charCode = e.charCode || e.keyCode; + } + else if (e.keyCode && (e.keyCode === 46)) { + this.keyCode = 127; + } + else { + this.keyCode = e.keyCode; + } + this.clientX = e.clientX; + this.clientY = e.clientY; + this.ctrlKey = e.ctrlKey; + this.target = e.target ? e.target : e.srcElement; + if (!etype.startsWith('key')) { + if ((typeof(e.offsetX) !== 'undefined') && (typeof(e.offsetY) !== 'undefined')) { + this.offsetX = e.offsetX; + this.offsetY = e.offsetY; + } + else if (this.target && (this.target.nodeType !== 3) && (typeof(e.clientX) === 'number')) { + var loc = Sys.UI.DomElement.getLocation(this.target); + var w = Sys.UI.DomElement._getWindow(this.target); + this.offsetX = (w.pageXOffset || 0) + e.clientX - loc.x; + this.offsetY = (w.pageYOffset || 0) + e.clientY - loc.y; + } + } + this.screenX = e.screenX; + this.screenY = e.screenY; + this.shiftKey = e.shiftKey; +} + function Sys$UI$DomEvent$preventDefault() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (this.rawEvent.preventDefault) { + this.rawEvent.preventDefault(); + } + else if (window.event) { + this.rawEvent.returnValue = false; + } + } + function Sys$UI$DomEvent$stopPropagation() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (this.rawEvent.stopPropagation) { + this.rawEvent.stopPropagation(); + } + else if (window.event) { + this.rawEvent.cancelBubble = true; + } + } +Sys.UI.DomEvent.prototype = { + preventDefault: Sys$UI$DomEvent$preventDefault, + stopPropagation: Sys$UI$DomEvent$stopPropagation +} +Sys.UI.DomEvent.registerClass('Sys.UI.DomEvent'); +var $addHandler = Sys.UI.DomEvent.addHandler = function Sys$UI$DomEvent$addHandler(element, eventName, handler) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element"}, + {name: "eventName", type: String}, + {name: "handler", type: Function} + ]); + if (e) throw e; + Sys.UI.DomEvent._ensureDomNode(element); + if (eventName === "error") throw Error.invalidOperation(Sys.Res.addHandlerCantBeUsedForError); + if (!element._events) { + element._events = {}; + } + var eventCache = element._events[eventName]; + if (!eventCache) { + element._events[eventName] = eventCache = []; + } + var browserHandler; + if (element.addEventListener) { + browserHandler = function(e) { + return handler.call(element, new Sys.UI.DomEvent(e)); + } + element.addEventListener(eventName, browserHandler, false); + } + else if (element.attachEvent) { + browserHandler = function() { + var e = {}; + try {e = Sys.UI.DomElement._getWindow(element).event} catch(ex) {} + return handler.call(element, new Sys.UI.DomEvent(e)); + } + element.attachEvent('on' + eventName, browserHandler); + } + eventCache[eventCache.length] = {handler: handler, browserHandler: browserHandler}; +} +var $addHandlers = Sys.UI.DomEvent.addHandlers = function Sys$UI$DomEvent$addHandlers(element, events, handlerOwner) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element"}, + {name: "events", type: Object}, + {name: "handlerOwner", optional: true} + ]); + if (e) throw e; + Sys.UI.DomEvent._ensureDomNode(element); + for (var name in events) { + var handler = events[name]; + if (typeof(handler) !== 'function') throw Error.invalidOperation(Sys.Res.cantAddNonFunctionhandler); + if (handlerOwner) { + handler = Function.createDelegate(handlerOwner, handler); + } + $addHandler(element, name, handler); + } +} +var $clearHandlers = Sys.UI.DomEvent.clearHandlers = function Sys$UI$DomEvent$clearHandlers(element) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element"} + ]); + if (e) throw e; + Sys.UI.DomEvent._ensureDomNode(element); + if (element._events) { + var cache = element._events; + for (var name in cache) { + var handlers = cache[name]; + for (var i = handlers.length - 1; i >= 0; i--) { + $removeHandler(element, name, handlers[i].handler); + } + } + element._events = null; + } +} +var $removeHandler = Sys.UI.DomEvent.removeHandler = function Sys$UI$DomEvent$removeHandler(element, eventName, handler) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element"}, + {name: "eventName", type: String}, + {name: "handler", type: Function} + ]); + if (e) throw e; + Sys.UI.DomEvent._ensureDomNode(element); + var browserHandler = null; + if ((typeof(element._events) !== 'object') || (element._events == null)) throw Error.invalidOperation(Sys.Res.eventHandlerInvalid); + var cache = element._events[eventName]; + if (!(cache instanceof Array)) throw Error.invalidOperation(Sys.Res.eventHandlerInvalid); + for (var i = 0, l = cache.length; i < l; i++) { + if (cache[i].handler === handler) { + browserHandler = cache[i].browserHandler; + break; + } + } + if (typeof(browserHandler) !== 'function') throw Error.invalidOperation(Sys.Res.eventHandlerInvalid); + if (element.removeEventListener) { + element.removeEventListener(eventName, browserHandler, false); + } + else if (element.detachEvent) { + element.detachEvent('on' + eventName, browserHandler); + } + cache.splice(i, 1); +} +Sys.UI.DomEvent._ensureDomNode = function Sys$UI$DomEvent$_ensureDomNode(element) { + if (element.tagName && (element.tagName.toUpperCase() === "SCRIPT")) return; + + var doc = element.ownerDocument || element.document || element; + if ((typeof(element.document) !== 'object') && (element != doc) && (typeof(element.nodeType) !== 'number')) { + throw Error.argument("element", Sys.Res.argumentDomNode); + } +} + +Sys.UI.DomElement = function Sys$UI$DomElement() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); +} +Sys.UI.DomElement.registerClass('Sys.UI.DomElement'); +Sys.UI.DomElement.addCssClass = function Sys$UI$DomElement$addCssClass(element, className) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true}, + {name: "className", type: String} + ]); + if (e) throw e; + if (!Sys.UI.DomElement.containsCssClass(element, className)) { + if (element.className === '') { + element.className = className; + } + else { + element.className += ' ' + className; + } + } +} +Sys.UI.DomElement.containsCssClass = function Sys$UI$DomElement$containsCssClass(element, className) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true}, + {name: "className", type: String} + ]); + if (e) throw e; + return Array.contains(element.className.split(' '), className); +} +Sys.UI.DomElement.getBounds = function Sys$UI$DomElement$getBounds(element) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true} + ]); + if (e) throw e; + var offset = Sys.UI.DomElement.getLocation(element); + return new Sys.UI.Bounds(offset.x, offset.y, element.offsetWidth || 0, element.offsetHeight || 0); +} +var $get = Sys.UI.DomElement.getElementById = function Sys$UI$DomElement$getElementById(id, element) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "id", type: String}, + {name: "element", mayBeNull: true, domElement: true, optional: true} + ]); + if (e) throw e; + if (!element) return document.getElementById(id); + if (element.getElementById) return element.getElementById(id); + var nodeQueue = []; + var childNodes = element.childNodes; + for (var i = 0; i < childNodes.length; i++) { + var node = childNodes[i]; + if (node.nodeType == 1) { + nodeQueue[nodeQueue.length] = node; + } + } + while (nodeQueue.length) { + node = nodeQueue.shift(); + if (node.id == id) { + return node; + } + childNodes = node.childNodes; + for (i = 0; i < childNodes.length; i++) { + node = childNodes[i]; + if (node.nodeType == 1) { + nodeQueue[nodeQueue.length] = node; + } + } + } + return null; +} +switch(Sys.Browser.agent) { + case Sys.Browser.InternetExplorer: + Sys.UI.DomElement.getLocation = function Sys$UI$DomElement$getLocation(element) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true} + ]); + if (e) throw e; + if (element.self || element.nodeType === 9) return new Sys.UI.Point(0,0); + var clientRect = element.getBoundingClientRect(); + if (!clientRect) { + return new Sys.UI.Point(0,0); + } + var documentElement = element.ownerDocument.documentElement; + var offsetX = clientRect.left - 2 + documentElement.scrollLeft, + offsetY = clientRect.top - 2 + documentElement.scrollTop; + + try { + var f = element.ownerDocument.parentWindow.frameElement || null; + if (f) { + var offset = (f.frameBorder === "0" || f.frameBorder === "no") ? 2 : 0; + offsetX += offset; + offsetY += offset; + } + } + catch(ex) { + } + + return new Sys.UI.Point(offsetX, offsetY); + } + break; + case Sys.Browser.Safari: + Sys.UI.DomElement.getLocation = function Sys$UI$DomElement$getLocation(element) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true} + ]); + if (e) throw e; + if ((element.window && (element.window === element)) || element.nodeType === 9) return new Sys.UI.Point(0,0); + var offsetX = 0; + var offsetY = 0; + var previous = null; + var previousStyle = null; + var currentStyle; + for (var parent = element; parent; previous = parent, previousStyle = currentStyle, parent = parent.offsetParent) { + currentStyle = Sys.UI.DomElement._getCurrentStyle(parent); + var tagName = parent.tagName ? parent.tagName.toUpperCase() : null; + if ((parent.offsetLeft || parent.offsetTop) && + ((tagName !== "BODY") || (!previousStyle || previousStyle.position !== "absolute"))) { + offsetX += parent.offsetLeft; + offsetY += parent.offsetTop; + } + } + currentStyle = Sys.UI.DomElement._getCurrentStyle(element); + var elementPosition = currentStyle ? currentStyle.position : null; + if (!elementPosition || (elementPosition !== "absolute")) { + for (var parent = element.parentNode; parent; parent = parent.parentNode) { + tagName = parent.tagName ? parent.tagName.toUpperCase() : null; + if ((tagName !== "BODY") && (tagName !== "HTML") && (parent.scrollLeft || parent.scrollTop)) { + offsetX -= (parent.scrollLeft || 0); + offsetY -= (parent.scrollTop || 0); + } + currentStyle = Sys.UI.DomElement._getCurrentStyle(parent); + var parentPosition = currentStyle ? currentStyle.position : null; + if (parentPosition && (parentPosition === "absolute")) break; + } + } + return new Sys.UI.Point(offsetX, offsetY); + } + break; + case Sys.Browser.Opera: + Sys.UI.DomElement.getLocation = function Sys$UI$DomElement$getLocation(element) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true} + ]); + if (e) throw e; + if ((element.window && (element.window === element)) || element.nodeType === 9) return new Sys.UI.Point(0,0); + var offsetX = 0; + var offsetY = 0; + var previous = null; + for (var parent = element; parent; previous = parent, parent = parent.offsetParent) { + var tagName = parent.tagName; + offsetX += parent.offsetLeft || 0; + offsetY += parent.offsetTop || 0; + } + var elementPosition = element.style.position; + var elementPositioned = elementPosition && (elementPosition !== "static"); + for (var parent = element.parentNode; parent; parent = parent.parentNode) { + tagName = parent.tagName ? parent.tagName.toUpperCase() : null; + if ((tagName !== "BODY") && (tagName !== "HTML") && (parent.scrollLeft || parent.scrollTop) && + ((elementPositioned && + ((parent.style.overflow === "scroll") || (parent.style.overflow === "auto"))))) { + offsetX -= (parent.scrollLeft || 0); + offsetY -= (parent.scrollTop || 0); + } + var parentPosition = (parent && parent.style) ? parent.style.position : null; + elementPositioned = elementPositioned || (parentPosition && (parentPosition !== "static")); + } + return new Sys.UI.Point(offsetX, offsetY); + } + break; + default: + Sys.UI.DomElement.getLocation = function Sys$UI$DomElement$getLocation(element) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true} + ]); + if (e) throw e; + if ((element.window && (element.window === element)) || element.nodeType === 9) return new Sys.UI.Point(0,0); + var offsetX = 0; + var offsetY = 0; + var previous = null; + var previousStyle = null; + var currentStyle = null; + for (var parent = element; parent; previous = parent, previousStyle = currentStyle, parent = parent.offsetParent) { + var tagName = parent.tagName ? parent.tagName.toUpperCase() : null; + currentStyle = Sys.UI.DomElement._getCurrentStyle(parent); + if ((parent.offsetLeft || parent.offsetTop) && + !((tagName === "BODY") && + (!previousStyle || previousStyle.position !== "absolute"))) { + offsetX += parent.offsetLeft; + offsetY += parent.offsetTop; + } + if (previous !== null && currentStyle) { + if ((tagName !== "TABLE") && (tagName !== "TD") && (tagName !== "HTML")) { + offsetX += parseInt(currentStyle.borderLeftWidth) || 0; + offsetY += parseInt(currentStyle.borderTopWidth) || 0; + } + if (tagName === "TABLE" && + (currentStyle.position === "relative" || currentStyle.position === "absolute")) { + offsetX += parseInt(currentStyle.marginLeft) || 0; + offsetY += parseInt(currentStyle.marginTop) || 0; + } + } + } + currentStyle = Sys.UI.DomElement._getCurrentStyle(element); + var elementPosition = currentStyle ? currentStyle.position : null; + if (!elementPosition || (elementPosition !== "absolute")) { + for (var parent = element.parentNode; parent; parent = parent.parentNode) { + tagName = parent.tagName ? parent.tagName.toUpperCase() : null; + if ((tagName !== "BODY") && (tagName !== "HTML") && (parent.scrollLeft || parent.scrollTop)) { + offsetX -= (parent.scrollLeft || 0); + offsetY -= (parent.scrollTop || 0); + currentStyle = Sys.UI.DomElement._getCurrentStyle(parent); + if (currentStyle) { + offsetX += parseInt(currentStyle.borderLeftWidth) || 0; + offsetY += parseInt(currentStyle.borderTopWidth) || 0; + } + } + } + } + return new Sys.UI.Point(offsetX, offsetY); + } + break; +} +Sys.UI.DomElement.removeCssClass = function Sys$UI$DomElement$removeCssClass(element, className) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true}, + {name: "className", type: String} + ]); + if (e) throw e; + var currentClassName = ' ' + element.className + ' '; + var index = currentClassName.indexOf(' ' + className + ' '); + if (index >= 0) { + element.className = (currentClassName.substr(0, index) + ' ' + + currentClassName.substring(index + className.length + 1, currentClassName.length)).trim(); + } +} +Sys.UI.DomElement.setLocation = function Sys$UI$DomElement$setLocation(element, x, y) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true}, + {name: "x", type: Number, integer: true}, + {name: "y", type: Number, integer: true} + ]); + if (e) throw e; + var style = element.style; + style.position = 'absolute'; + style.left = x + "px"; + style.top = y + "px"; +} +Sys.UI.DomElement.toggleCssClass = function Sys$UI$DomElement$toggleCssClass(element, className) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true}, + {name: "className", type: String} + ]); + if (e) throw e; + if (Sys.UI.DomElement.containsCssClass(element, className)) { + Sys.UI.DomElement.removeCssClass(element, className); + } + else { + Sys.UI.DomElement.addCssClass(element, className); + } +} +Sys.UI.DomElement.getVisibilityMode = function Sys$UI$DomElement$getVisibilityMode(element) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true} + ]); + if (e) throw e; + return (element._visibilityMode === Sys.UI.VisibilityMode.hide) ? + Sys.UI.VisibilityMode.hide : + Sys.UI.VisibilityMode.collapse; +} +Sys.UI.DomElement.setVisibilityMode = function Sys$UI$DomElement$setVisibilityMode(element, value) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true}, + {name: "value", type: Sys.UI.VisibilityMode} + ]); + if (e) throw e; + Sys.UI.DomElement._ensureOldDisplayMode(element); + if (element._visibilityMode !== value) { + element._visibilityMode = value; + if (Sys.UI.DomElement.getVisible(element) === false) { + if (element._visibilityMode === Sys.UI.VisibilityMode.hide) { + element.style.display = element._oldDisplayMode; + } + else { + element.style.display = 'none'; + } + } + element._visibilityMode = value; + } +} +Sys.UI.DomElement.getVisible = function Sys$UI$DomElement$getVisible(element) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true} + ]); + if (e) throw e; + var style = element.currentStyle || Sys.UI.DomElement._getCurrentStyle(element); + if (!style) return true; + return (style.visibility !== 'hidden') && (style.display !== 'none'); +} +Sys.UI.DomElement.setVisible = function Sys$UI$DomElement$setVisible(element, value) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true}, + {name: "value", type: Boolean} + ]); + if (e) throw e; + if (value !== Sys.UI.DomElement.getVisible(element)) { + Sys.UI.DomElement._ensureOldDisplayMode(element); + element.style.visibility = value ? 'visible' : 'hidden'; + if (value || (element._visibilityMode === Sys.UI.VisibilityMode.hide)) { + element.style.display = element._oldDisplayMode; + } + else { + element.style.display = 'none'; + } + } +} +Sys.UI.DomElement._ensureOldDisplayMode = function Sys$UI$DomElement$_ensureOldDisplayMode(element) { + if (!element._oldDisplayMode) { + var style = element.currentStyle || Sys.UI.DomElement._getCurrentStyle(element); + element._oldDisplayMode = style ? style.display : null; + if (!element._oldDisplayMode || element._oldDisplayMode === 'none') { + switch(element.tagName.toUpperCase()) { + case 'DIV': case 'P': case 'ADDRESS': case 'BLOCKQUOTE': case 'BODY': case 'COL': + case 'COLGROUP': case 'DD': case 'DL': case 'DT': case 'FIELDSET': case 'FORM': + case 'H1': case 'H2': case 'H3': case 'H4': case 'H5': case 'H6': case 'HR': + case 'IFRAME': case 'LEGEND': case 'OL': case 'PRE': case 'TABLE': case 'TD': + case 'TH': case 'TR': case 'UL': + element._oldDisplayMode = 'block'; + break; + case 'LI': + element._oldDisplayMode = 'list-item'; + break; + default: + element._oldDisplayMode = 'inline'; + } + } + } +} +Sys.UI.DomElement._getWindow = function Sys$UI$DomElement$_getWindow(element) { + var doc = element.ownerDocument || element.document || element; + return doc.defaultView || doc.parentWindow; +} +Sys.UI.DomElement._getCurrentStyle = function Sys$UI$DomElement$_getCurrentStyle(element) { + if (element.nodeType === 3) return null; + var w = Sys.UI.DomElement._getWindow(element); + if (element.documentElement) element = element.documentElement; + var computedStyle = (w && (element !== w) && w.getComputedStyle) ? + w.getComputedStyle(element, null) : + element.currentStyle || element.style; + if (!computedStyle && (Sys.Browser.agent === Sys.Browser.Safari) && element.style) { + var oldDisplay = element.style.display; + var oldPosition = element.style.position; + element.style.position = 'absolute'; + element.style.display = 'block'; + var style = w.getComputedStyle(element, null); + element.style.display = oldDisplay; + element.style.position = oldPosition; + computedStyle = {}; + for (var n in style) { + computedStyle[n] = style[n]; + } + computedStyle.display = 'none'; + } + return computedStyle; +} + +Sys.IContainer = function Sys$IContainer() { + throw Error.notImplemented(); +} + function Sys$IContainer$addComponent(component) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "component", type: Sys.Component} + ]); + if (e) throw e; + throw Error.notImplemented(); + } + function Sys$IContainer$removeComponent(component) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "component", type: Sys.Component} + ]); + if (e) throw e; + throw Error.notImplemented(); + } + function Sys$IContainer$findComponent(id) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "id", type: String} + ]); + if (e) throw e; + throw Error.notImplemented(); + } + function Sys$IContainer$getComponents() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); + } +Sys.IContainer.prototype = { + addComponent: Sys$IContainer$addComponent, + removeComponent: Sys$IContainer$removeComponent, + findComponent: Sys$IContainer$findComponent, + getComponents: Sys$IContainer$getComponents +} +Sys.IContainer.registerInterface("Sys.IContainer"); + +Sys._ScriptLoader = function Sys$_ScriptLoader() { + this._scriptsToLoad = null; + this._sessions = []; + this._scriptLoadedDelegate = Function.createDelegate(this, this._scriptLoadedHandler); +} + function Sys$_ScriptLoader$dispose() { + this._stopSession(); + this._loading = false; + if(this._events) { + delete this._events; + } + this._sessions = null; + this._currentSession = null; + this._scriptLoadedDelegate = null; + } + function Sys$_ScriptLoader$loadScripts(scriptTimeout, allScriptsLoadedCallback, scriptLoadFailedCallback, scriptLoadTimeoutCallback) { + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "scriptTimeout", type: Number, integer: true}, + {name: "allScriptsLoadedCallback", type: Function, mayBeNull: true}, + {name: "scriptLoadFailedCallback", type: Function, mayBeNull: true}, + {name: "scriptLoadTimeoutCallback", type: Function, mayBeNull: true} + ]); + if (e) throw e; + var session = { + allScriptsLoadedCallback: allScriptsLoadedCallback, + scriptLoadFailedCallback: scriptLoadFailedCallback, + scriptLoadTimeoutCallback: scriptLoadTimeoutCallback, + scriptsToLoad: this._scriptsToLoad, + scriptTimeout: scriptTimeout }; + this._scriptsToLoad = null; + this._sessions[this._sessions.length] = session; + + if (!this._loading) { + this._nextSession(); + } + } + function Sys$_ScriptLoader$notifyScriptLoaded() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + + if(!this._loading) { + return; + } + this._currentTask._notified++; + + if(Sys.Browser.agent === Sys.Browser.Safari) { + if(this._currentTask._notified === 1) { + window.setTimeout(Function.createDelegate(this, function() { + this._scriptLoadedHandler(this._currentTask.get_scriptElement(), true); + }), 0); + } + } + } + function Sys$_ScriptLoader$queueCustomScriptTag(scriptAttributes) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "scriptAttributes"} + ]); + if (e) throw e; + if(!this._scriptsToLoad) { + this._scriptsToLoad = []; + } + Array.add(this._scriptsToLoad, scriptAttributes); + } + function Sys$_ScriptLoader$queueScriptBlock(scriptContent) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "scriptContent", type: String} + ]); + if (e) throw e; + if(!this._scriptsToLoad) { + this._scriptsToLoad = []; + } + Array.add(this._scriptsToLoad, {text: scriptContent}); + } + function Sys$_ScriptLoader$queueScriptReference(scriptUrl) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "scriptUrl", type: String} + ]); + if (e) throw e; + if(!this._scriptsToLoad) { + this._scriptsToLoad = []; + } + Array.add(this._scriptsToLoad, {src: scriptUrl}); + } + function Sys$_ScriptLoader$_createScriptElement(queuedScript) { + var scriptElement = document.createElement('script'); + scriptElement.type = 'text/javascript'; + for (var attr in queuedScript) { + scriptElement[attr] = queuedScript[attr]; + } + + return scriptElement; + } + function Sys$_ScriptLoader$_loadScriptsInternal() { + var session = this._currentSession; + if (session.scriptsToLoad && session.scriptsToLoad.length > 0) { + var nextScript = Array.dequeue(session.scriptsToLoad); + var scriptElement = this._createScriptElement(nextScript); + + if (scriptElement.text && Sys.Browser.agent === Sys.Browser.Safari) { + scriptElement.innerHTML = scriptElement.text; + delete scriptElement.text; + } + if (typeof(nextScript.src) === "string") { + this._currentTask = new Sys._ScriptLoaderTask(scriptElement, this._scriptLoadedDelegate); + this._currentTask.execute(); + } + else { + var headElements = document.getElementsByTagName('head'); + if (headElements.length === 0) { + throw new Error.invalidOperation(Sys.Res.scriptLoadFailedNoHead); + } + else { + headElements[0].appendChild(scriptElement); + } + + + Sys._ScriptLoader._clearScript(scriptElement); + this._loadScriptsInternal(); + } + } + else { + this._stopSession(); + var callback = session.allScriptsLoadedCallback; + if(callback) { + callback(this); + } + this._nextSession(); + } + } + function Sys$_ScriptLoader$_nextSession() { + if (this._sessions.length === 0) { + this._loading = false; + this._currentSession = null; + return; + } + this._loading = true; + + var session = Array.dequeue(this._sessions); + this._currentSession = session; + this._loadScriptsInternal(); + } + function Sys$_ScriptLoader$_raiseError(multipleCallbacks) { + var callback = this._currentSession.scriptLoadFailedCallback; + var scriptElement = this._currentTask.get_scriptElement(); + this._stopSession(); + + if(callback) { + callback(this, scriptElement, multipleCallbacks); + this._nextSession(); + } + else { + this._loading = false; + throw Sys._ScriptLoader._errorScriptLoadFailed(scriptElement.src, multipleCallbacks); + } + } + function Sys$_ScriptLoader$_scriptLoadedHandler(scriptElement, loaded) { + if(loaded && this._currentTask._notified) { + if(this._currentTask._notified > 1) { + this._raiseError(true); + } + else { + Array.add(Sys._ScriptLoader._getLoadedScripts(), scriptElement.src); + this._currentTask.dispose(); + this._currentTask = null; + this._loadScriptsInternal(); + } + } + else { + this._raiseError(false); + } + } + function Sys$_ScriptLoader$_scriptLoadTimeoutHandler() { + var callback = this._currentSession.scriptLoadTimeoutCallback; + this._stopSession(); + if(callback) { + callback(this); + } + this._nextSession(); + } + function Sys$_ScriptLoader$_stopSession() { + if(this._currentTask) { + this._currentTask.dispose(); + this._currentTask = null; + } + } +Sys._ScriptLoader.prototype = { + dispose: Sys$_ScriptLoader$dispose, + loadScripts: Sys$_ScriptLoader$loadScripts, + notifyScriptLoaded: Sys$_ScriptLoader$notifyScriptLoaded, + queueCustomScriptTag: Sys$_ScriptLoader$queueCustomScriptTag, + queueScriptBlock: Sys$_ScriptLoader$queueScriptBlock, + queueScriptReference: Sys$_ScriptLoader$queueScriptReference, + _createScriptElement: Sys$_ScriptLoader$_createScriptElement, + _loadScriptsInternal: Sys$_ScriptLoader$_loadScriptsInternal, + _nextSession: Sys$_ScriptLoader$_nextSession, + _raiseError: Sys$_ScriptLoader$_raiseError, + _scriptLoadedHandler: Sys$_ScriptLoader$_scriptLoadedHandler, + _scriptLoadTimeoutHandler: Sys$_ScriptLoader$_scriptLoadTimeoutHandler, + _stopSession: Sys$_ScriptLoader$_stopSession +} +Sys._ScriptLoader.registerClass('Sys._ScriptLoader', null, Sys.IDisposable); +Sys._ScriptLoader.getInstance = function Sys$_ScriptLoader$getInstance() { + var sl = Sys._ScriptLoader._activeInstance; + if(!sl) { + sl = Sys._ScriptLoader._activeInstance = new Sys._ScriptLoader(); + } + return sl; +} +Sys._ScriptLoader.isScriptLoaded = function Sys$_ScriptLoader$isScriptLoaded(scriptSrc) { + var dummyScript = document.createElement('script'); + dummyScript.src = scriptSrc; + return Array.contains(Sys._ScriptLoader._getLoadedScripts(), dummyScript.src); +} +Sys._ScriptLoader.readLoadedScripts = function Sys$_ScriptLoader$readLoadedScripts() { + if(!Sys._ScriptLoader._referencedScripts) { + var referencedScripts = Sys._ScriptLoader._referencedScripts = []; + var existingScripts = document.getElementsByTagName('script'); + for (i = existingScripts.length - 1; i >= 0; i--) { + var scriptNode = existingScripts[i]; + var scriptSrc = scriptNode.src; + if (scriptSrc.length) { + if (!Array.contains(referencedScripts, scriptSrc)) { + Array.add(referencedScripts, scriptSrc); + } + } + } + } +} +Sys._ScriptLoader._clearScript = function Sys$_ScriptLoader$_clearScript(scriptElement) { + if (!Sys.Debug.isDebug) { + scriptElement.parentNode.removeChild(scriptElement); + } +} +Sys._ScriptLoader._errorScriptLoadFailed = function Sys$_ScriptLoader$_errorScriptLoadFailed(scriptUrl, multipleCallbacks) { + var errorMessage; + if(multipleCallbacks) { + errorMessage = Sys.Res.scriptLoadMultipleCallbacks; + } + else { + errorMessage = Sys.Res.scriptLoadFailedDebug; + } + var displayMessage = "Sys.ScriptLoadFailedException: " + String.format(errorMessage, scriptUrl); + var e = Error.create(displayMessage, {name: 'Sys.ScriptLoadFailedException', 'scriptUrl': scriptUrl }); + e.popStackFrame(); + return e; +} +Sys._ScriptLoader._getLoadedScripts = function Sys$_ScriptLoader$_getLoadedScripts() { + if(!Sys._ScriptLoader._referencedScripts) { + Sys._ScriptLoader._referencedScripts = []; + Sys._ScriptLoader.readLoadedScripts(); + } + return Sys._ScriptLoader._referencedScripts; +} + +Sys._ScriptLoaderTask = function Sys$_ScriptLoaderTask(scriptElement, completedCallback) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "scriptElement", domElement: true}, + {name: "completedCallback", type: Function} + ]); + if (e) throw e; + this._scriptElement = scriptElement; + this._completedCallback = completedCallback; + this._notified = 0; +} + function Sys$_ScriptLoaderTask$get_scriptElement() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._scriptElement; + } + function Sys$_ScriptLoaderTask$dispose() { + if(this._disposed) { + return; + } + this._disposed = true; + this._removeScriptElementHandlers(); + Sys._ScriptLoader._clearScript(this._scriptElement); + this._scriptElement = null; + } + function Sys$_ScriptLoaderTask$execute() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + this._addScriptElementHandlers(); + var headElements = document.getElementsByTagName('head'); + if (headElements.length === 0) { + throw new Error.invalidOperation(Sys.Res.scriptLoadFailedNoHead); + } + else { + headElements[0].appendChild(this._scriptElement); + } + } + function Sys$_ScriptLoaderTask$_addScriptElementHandlers() { + this._scriptLoadDelegate = Function.createDelegate(this, this._scriptLoadHandler); + + if (Sys.Browser.agent !== Sys.Browser.InternetExplorer) { + this._scriptElement.readyState = 'loaded'; + $addHandler(this._scriptElement, 'load', this._scriptLoadDelegate); + } + else { + $addHandler(this._scriptElement, 'readystatechange', this._scriptLoadDelegate); + } + if (this._scriptElement.addEventListener) { + this._scriptErrorDelegate = Function.createDelegate(this, this._scriptErrorHandler); + this._scriptElement.addEventListener('error', this._scriptErrorDelegate, false); + } + } + function Sys$_ScriptLoaderTask$_removeScriptElementHandlers() { + if(this._scriptLoadDelegate) { + var scriptElement = this.get_scriptElement(); + if (Sys.Browser.agent !== Sys.Browser.InternetExplorer) { + $removeHandler(scriptElement, 'load', this._scriptLoadDelegate); + } + else { + $removeHandler(scriptElement, 'readystatechange', this._scriptLoadDelegate); + } + if (this._scriptErrorDelegate) { + this._scriptElement.removeEventListener('error', this._scriptErrorDelegate, false); + this._scriptErrorDelegate = null; + } + this._scriptLoadDelegate = null; + } + } + function Sys$_ScriptLoaderTask$_scriptErrorHandler() { + if(this._disposed) { + return; + } + + this._completedCallback(this.get_scriptElement(), false); + } + function Sys$_ScriptLoaderTask$_scriptLoadHandler() { + if(this._disposed) { + return; + } + var scriptElement = this.get_scriptElement(); + if ((scriptElement.readyState !== 'loaded') && + (scriptElement.readyState !== 'complete')) { + return; + } + + var _this = this; + window.setTimeout(function() { + _this._completedCallback(scriptElement, true); + }, 0); + } +Sys._ScriptLoaderTask.prototype = { + get_scriptElement: Sys$_ScriptLoaderTask$get_scriptElement, + dispose: Sys$_ScriptLoaderTask$dispose, + execute: Sys$_ScriptLoaderTask$execute, + _addScriptElementHandlers: Sys$_ScriptLoaderTask$_addScriptElementHandlers, + _removeScriptElementHandlers: Sys$_ScriptLoaderTask$_removeScriptElementHandlers, + _scriptErrorHandler: Sys$_ScriptLoaderTask$_scriptErrorHandler, + _scriptLoadHandler: Sys$_ScriptLoaderTask$_scriptLoadHandler +} +Sys._ScriptLoaderTask.registerClass("Sys._ScriptLoaderTask", null, Sys.IDisposable); + +Sys.ApplicationLoadEventArgs = function Sys$ApplicationLoadEventArgs(components, isPartialLoad) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "components", type: Array, elementType: Sys.Component}, + {name: "isPartialLoad", type: Boolean} + ]); + if (e) throw e; + Sys.ApplicationLoadEventArgs.initializeBase(this); + this._components = components; + this._isPartialLoad = isPartialLoad; +} + + function Sys$ApplicationLoadEventArgs$get_components() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._components; + } + function Sys$ApplicationLoadEventArgs$get_isPartialLoad() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._isPartialLoad; + } +Sys.ApplicationLoadEventArgs.prototype = { + get_components: Sys$ApplicationLoadEventArgs$get_components, + get_isPartialLoad: Sys$ApplicationLoadEventArgs$get_isPartialLoad +} +Sys.ApplicationLoadEventArgs.registerClass('Sys.ApplicationLoadEventArgs', Sys.EventArgs); +Sys.HistoryEventArgs = function Sys$HistoryEventArgs(state) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "state", type: Object} + ]); + if (e) throw e; + Sys.HistoryEventArgs.initializeBase(this); + this._state = state; +} + function Sys$HistoryEventArgs$get_state() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._state; + } +Sys.HistoryEventArgs.prototype = { + get_state: Sys$HistoryEventArgs$get_state +} +Sys.HistoryEventArgs.registerClass('Sys.HistoryEventArgs', Sys.EventArgs); + +Sys._Application = function Sys$_Application() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + Sys._Application.initializeBase(this); + this._disposableObjects = []; + this._components = {}; + this._createdComponents = []; + this._secondPassComponents = []; + this._appLoadHandler = null; + this._beginRequestHandler = null; + this._clientId = null; + this._currentEntry = ''; + this._endRequestHandler = null; + this._history = null; + this._enableHistory = false; + this._historyEnabledInScriptManager = false; + this._historyFrame = null; + this._historyInitialized = false; + this._historyInitialLength = 0; + this._historyLength = 0; + this._historyPointIsNew = false; + this._ignoreTimer = false; + this._initialState = null; + this._state = {}; + this._timerCookie = 0; + this._timerHandler = null; + this._uniqueId = null; + this._unloadHandlerDelegate = Function.createDelegate(this, this._unloadHandler); + this._loadHandlerDelegate = Function.createDelegate(this, this._loadHandler); + Sys.UI.DomEvent.addHandler(window, "unload", this._unloadHandlerDelegate); + Sys.UI.DomEvent.addHandler(window, "load", this._loadHandlerDelegate); +} + function Sys$_Application$get_isCreatingComponents() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._creatingComponents; + } + function Sys$_Application$get_stateString() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + var hash = window.location.hash; + if (this._isSafari2()) { + var history = this._getHistory(); + if (history) { + hash = history[window.history.length - this._historyInitialLength]; + } + } + if ((hash.length > 0) && (hash.charAt(0) === '#')) { + hash = hash.substring(1); + } + if (Sys.Browser.agent === Sys.Browser.Firefox) { + hash = this._serializeState(this._deserializeState(hash, true)); + } + return hash; + } + function Sys$_Application$get_enableHistory() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._enableHistory; + } + function Sys$_Application$set_enableHistory(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Boolean}]); + if (e) throw e; + if (this._initialized && !this._initializing) { + throw Error.invalidOperation(Sys.Res.historyCannotEnableHistory); + } + else if (this._historyEnabledInScriptManager && !value) { + throw Error.invalidOperation(Sys.Res.invalidHistorySettingCombination); + } + this._enableHistory = value; + } + function Sys$_Application$add_init(handler) { + /// + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + if (this._initialized) { + handler(this, Sys.EventArgs.Empty); + } + else { + this.get_events().addHandler("init", handler); + } + } + function Sys$_Application$remove_init(handler) { + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this.get_events().removeHandler("init", handler); + } + function Sys$_Application$add_load(handler) { + /// + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this.get_events().addHandler("load", handler); + } + function Sys$_Application$remove_load(handler) { + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this.get_events().removeHandler("load", handler); + } + function Sys$_Application$add_navigate(handler) { + /// + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this.get_events().addHandler("navigate", handler); + } + function Sys$_Application$remove_navigate(handler) { + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this.get_events().removeHandler("navigate", handler); + } + function Sys$_Application$add_unload(handler) { + /// + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this.get_events().addHandler("unload", handler); + } + function Sys$_Application$remove_unload(handler) { + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this.get_events().removeHandler("unload", handler); + } + function Sys$_Application$addComponent(component) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "component", type: Sys.Component} + ]); + if (e) throw e; + var id = component.get_id(); + if (!id) throw Error.invalidOperation(Sys.Res.cantAddWithoutId); + if (typeof(this._components[id]) !== 'undefined') throw Error.invalidOperation(String.format(Sys.Res.appDuplicateComponent, id)); + this._components[id] = component; + } + function Sys$_Application$addHistoryPoint(state, title) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "state", type: Object}, + {name: "title", type: String, mayBeNull: true, optional: true} + ]); + if (e) throw e; + if (!this._enableHistory) throw Error.invalidOperation(Sys.Res.historyCannotAddHistoryPointWithHistoryDisabled); + for (var n in state) { + var v = state[n]; + var t = typeof(v); + if ((v !== null) && ((t === 'object') || (t === 'function') || (t === 'undefined'))) { + throw Error.argument('state', Sys.Res.stateMustBeStringDictionary); + } + } + this._ensureHistory(); + var initialState = this._state; + for (var key in state) { + var value = state[key]; + if (value === null) { + if (typeof(initialState[key]) !== 'undefined') { + delete initialState[key]; + } + } + else { + initialState[key] = value; + } + } + var entry = this._serializeState(initialState); + this._historyPointIsNew = true; + this._setState(entry, title); + this._raiseNavigate(); + } + function Sys$_Application$beginCreateComponents() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + this._creatingComponents = true; + } + function Sys$_Application$dispose() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (!this._disposing) { + this._disposing = true; + if (this._timerCookie) { + window.clearTimeout(this._timerCookie); + delete this._timerCookie; + } + if (this._endRequestHandler) { + Sys.WebForms.PageRequestManager.getInstance().remove_endRequest(this._endRequestHandler); + delete this._endRequestHandler; + } + if (this._beginRequestHandler) { + Sys.WebForms.PageRequestManager.getInstance().remove_beginRequest(this._beginRequestHandler); + delete this._beginRequestHandler; + } + if (window.pageUnload) { + window.pageUnload(this, Sys.EventArgs.Empty); + } + var unloadHandler = this.get_events().getHandler("unload"); + if (unloadHandler) { + unloadHandler(this, Sys.EventArgs.Empty); + } + var disposableObjects = Array.clone(this._disposableObjects); + for (var i = 0, l = disposableObjects.length; i < l; i++) { + disposableObjects[i].dispose(); + } + Array.clear(this._disposableObjects); + Sys.UI.DomEvent.removeHandler(window, "unload", this._unloadHandlerDelegate); + if(this._loadHandlerDelegate) { + Sys.UI.DomEvent.removeHandler(window, "load", this._loadHandlerDelegate); + this._loadHandlerDelegate = null; + } + var sl = Sys._ScriptLoader.getInstance(); + if(sl) { + sl.dispose(); + } + Sys._Application.callBaseMethod(this, 'dispose'); + } + } + function Sys$_Application$endCreateComponents() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + var components = this._secondPassComponents; + for (var i = 0, l = components.length; i < l; i++) { + var component = components[i].component; + Sys$Component$_setReferences(component, components[i].references); + component.endUpdate(); + } + this._secondPassComponents = []; + this._creatingComponents = false; + } + function Sys$_Application$findComponent(id, parent) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "id", type: String}, + {name: "parent", mayBeNull: true, optional: true} + ]); + if (e) throw e; + return (parent ? + ((Sys.IContainer.isInstanceOfType(parent)) ? + parent.findComponent(id) : + parent[id] || null) : + Sys.Application._components[id] || null); + } + function Sys$_Application$getComponents() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + var res = []; + var components = this._components; + for (var name in components) { + res[res.length] = components[name]; + } + return res; + } + function Sys$_Application$initialize() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if(!this._initialized && !this._initializing) { + this._initializing = true; + window.setTimeout(Function.createDelegate(this, this._doInitialize), 0); + } + } + function Sys$_Application$notifyScriptLoaded() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + var sl = Sys._ScriptLoader.getInstance(); + if(sl) { + sl.notifyScriptLoaded(); + } + } + function Sys$_Application$registerDisposableObject(object) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "object", type: Sys.IDisposable} + ]); + if (e) throw e; + if (!this._disposing) { + this._disposableObjects[this._disposableObjects.length] = object; + } + } + function Sys$_Application$raiseLoad() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + var h = this.get_events().getHandler("load"); + var args = new Sys.ApplicationLoadEventArgs(Array.clone(this._createdComponents), !this._initializing); + if (h) { + h(this, args); + } + if (window.pageLoad) { + window.pageLoad(this, args); + } + this._createdComponents = []; + } + function Sys$_Application$removeComponent(component) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "component", type: Sys.Component} + ]); + if (e) throw e; + var id = component.get_id(); + if (id) delete this._components[id]; + } + function Sys$_Application$setServerId(clientId, uniqueId) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "clientId", type: String}, + {name: "uniqueId", type: String} + ]); + if (e) throw e; + this._clientId = clientId; + this._uniqueId = uniqueId; + } + function Sys$_Application$setServerState(value) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "value", type: String} + ]); + if (e) throw e; + this._ensureHistory(); + this._state.__s = value; + this._updateHiddenField(value); + } + function Sys$_Application$unregisterDisposableObject(object) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "object", type: Sys.IDisposable} + ]); + if (e) throw e; + if (!this._disposing) { + Array.remove(this._disposableObjects, object); + } + } + function Sys$_Application$_addComponentToSecondPass(component, references) { + this._secondPassComponents[this._secondPassComponents.length] = {component: component, references: references}; + } + function Sys$_Application$_deserializeState(entry, skipDecodeUri) { + var result = {}; + entry = entry || ''; + var serverSeparator = entry.indexOf('&&'); + if ((serverSeparator !== -1) && (serverSeparator + 2 < entry.length)) { + result.__s = entry.substr(serverSeparator + 2); + entry = entry.substr(0, serverSeparator); + } + var tokens = entry.split('&'); + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + var equal = token.indexOf('='); + if ((equal !== -1) && (equal + 1 < token.length)) { + var name = token.substr(0, equal); + var value = token.substr(equal + 1); + result[name] = skipDecodeUri ? value : decodeURIComponent(value); + } + } + return result; + } + function Sys$_Application$_doInitialize() { + Sys._Application.callBaseMethod(this, 'initialize'); + + var handler = this.get_events().getHandler("init"); + if (handler) { + this.beginCreateComponents(); + handler(this, Sys.EventArgs.Empty); + this.endCreateComponents(); + } + if (Sys.WebForms) { + this._beginRequestHandler = Function.createDelegate(this, this._onPageRequestManagerBeginRequest); + Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(this._beginRequestHandler); + this._endRequestHandler = Function.createDelegate(this, this._onPageRequestManagerEndRequest); + Sys.WebForms.PageRequestManager.getInstance().add_endRequest(this._endRequestHandler); + } + + var loadedEntry = this.get_stateString(); + if (loadedEntry !== this._currentEntry) { + this._navigate(loadedEntry); + } + + this.raiseLoad(); + this._initializing = false; + } + function Sys$_Application$_enableHistoryInScriptManager() { + this._enableHistory = true; + this._historyEnabledInScriptManager = true; + } + function Sys$_Application$_ensureHistory() { + if (!this._historyInitialized && this._enableHistory) { + if ((Sys.Browser.agent === Sys.Browser.InternetExplorer) && (Sys.Browser.documentMode < 8)) { + this._historyFrame = document.getElementById('__historyFrame'); + if (!this._historyFrame) throw Error.invalidOperation(Sys.Res.historyMissingFrame); + this._ignoreIFrame = true; + } + if (this._isSafari2()) { + var historyElement = document.getElementById('__history'); + if (!historyElement) throw Error.invalidOperation(Sys.Res.historyMissingHiddenInput); + this._setHistory([window.location.hash]); + this._historyInitialLength = window.history.length; + } + + this._timerHandler = Function.createDelegate(this, this._onIdle); + this._timerCookie = window.setTimeout(this._timerHandler, 100); + + try { + this._initialState = this._deserializeState(this.get_stateString()); + } catch(e) {} + + this._historyInitialized = true; + } + } + function Sys$_Application$_getHistory() { + var historyElement = document.getElementById('__history'); + if (!historyElement) return ''; + var v = historyElement.value; + return v ? Sys.Serialization.JavaScriptSerializer.deserialize(v, true) : ''; + } + function Sys$_Application$_isSafari2() { + return (Sys.Browser.agent === Sys.Browser.Safari) && + (Sys.Browser.version <= 419.3); + } + function Sys$_Application$_loadHandler() { + if(this._loadHandlerDelegate) { + Sys.UI.DomEvent.removeHandler(window, "load", this._loadHandlerDelegate); + this._loadHandlerDelegate = null; + } + this.initialize(); + } + function Sys$_Application$_navigate(entry) { + this._ensureHistory(); + var state = this._deserializeState(entry); + + if (this._uniqueId) { + var oldServerEntry = this._state.__s || ''; + var newServerEntry = state.__s || ''; + if (newServerEntry !== oldServerEntry) { + this._updateHiddenField(newServerEntry); + __doPostBack(this._uniqueId, newServerEntry); + this._state = state; + return; + } + } + this._setState(entry); + this._state = state; + this._raiseNavigate(); + } + function Sys$_Application$_onIdle() { + delete this._timerCookie; + + var entry = this.get_stateString(); + if (entry !== this._currentEntry) { + if (!this._ignoreTimer) { + this._historyPointIsNew = false; + this._navigate(entry); + this._historyLength = window.history.length; + } + } + else { + this._ignoreTimer = false; + } + this._timerCookie = window.setTimeout(this._timerHandler, 100); + } + function Sys$_Application$_onIFrameLoad(entry) { + this._ensureHistory(); + if (!this._ignoreIFrame) { + this._historyPointIsNew = false; + this._navigate(entry); + } + this._ignoreIFrame = false; + } + function Sys$_Application$_onPageRequestManagerBeginRequest(sender, args) { + this._ignoreTimer = true; + } + function Sys$_Application$_onPageRequestManagerEndRequest(sender, args) { + var dataItem = args.get_dataItems()[this._clientId]; + var eventTarget = document.getElementById("__EVENTTARGET"); + if (eventTarget && eventTarget.value === this._uniqueId) { + eventTarget.value = ''; + } + if (typeof(dataItem) !== 'undefined') { + this.setServerState(dataItem); + this._historyPointIsNew = true; + } + else { + this._ignoreTimer = false; + } + var entry = this._serializeState(this._state); + if (entry !== this._currentEntry) { + this._ignoreTimer = true; + this._setState(entry); + this._raiseNavigate(); + } + } + function Sys$_Application$_raiseNavigate() { + var h = this.get_events().getHandler("navigate"); + var stateClone = {}; + for (var key in this._state) { + if (key !== '__s') { + stateClone[key] = this._state[key]; + } + } + var args = new Sys.HistoryEventArgs(stateClone); + if (h) { + h(this, args); + } + } + function Sys$_Application$_serializeState(state) { + var serialized = []; + for (var key in state) { + var value = state[key]; + if (key === '__s') { + var serverState = value; + } + else { + if (key.indexOf('=') !== -1) throw Error.argument('state', Sys.Res.stateFieldNameInvalid); + serialized[serialized.length] = key + '=' + encodeURIComponent(value); + } + } + return serialized.join('&') + (serverState ? '&&' + serverState : ''); + } + function Sys$_Application$_setHistory(historyArray) { + var historyElement = document.getElementById('__history'); + if (historyElement) { + historyElement.value = Sys.Serialization.JavaScriptSerializer.serialize(historyArray); + } + } + function Sys$_Application$_setState(entry, title) { + entry = entry || ''; + if (entry !== this._currentEntry) { + if (window.theForm) { + var action = window.theForm.action; + var hashIndex = action.indexOf('#'); + window.theForm.action = ((hashIndex !== -1) ? action.substring(0, hashIndex) : action) + '#' + entry; + } + + if (this._historyFrame && this._historyPointIsNew) { + this._ignoreIFrame = true; + this._historyPointIsNew = false; + var frameDoc = this._historyFrame.contentWindow.document; + frameDoc.open("javascript:''"); + frameDoc.write("" + (title || document.title) + + "parent.Sys.Application._onIFrameLoad('" + + entry + "');"); + frameDoc.close(); + } + this._ignoreTimer = false; + var currentHash = this.get_stateString(); + this._currentEntry = entry; + if (entry !== currentHash) { + var loc = document.location; + if (loc.href.length - loc.hash.length + entry.length > 1024) { + throw Error.invalidOperation(Sys.Res.urlMustBeLessThan1024chars); + } + if (this._isSafari2()) { + var history = this._getHistory(); + history[window.history.length - this._historyInitialLength + 1] = entry; + this._setHistory(history); + this._historyLength = window.history.length + 1; + var form = document.createElement('form'); + form.method = 'get'; + form.action = '#' + entry; + document.appendChild(form); + form.submit(); + document.removeChild(form); + } + else { + window.location.hash = entry; + } + if ((typeof(title) !== 'undefined') && (title !== null)) { + document.title = title; + } + } + } + } + function Sys$_Application$_unloadHandler(event) { + this.dispose(); + } + function Sys$_Application$_updateHiddenField(value) { + if (this._clientId) { + var serverStateField = document.getElementById(this._clientId); + if (serverStateField) { + serverStateField.value = value; + } + } + } +Sys._Application.prototype = { + _creatingComponents: false, + _disposing: false, + get_isCreatingComponents: Sys$_Application$get_isCreatingComponents, + get_stateString: Sys$_Application$get_stateString, + get_enableHistory: Sys$_Application$get_enableHistory, + set_enableHistory: Sys$_Application$set_enableHistory, + add_init: Sys$_Application$add_init, + remove_init: Sys$_Application$remove_init, + add_load: Sys$_Application$add_load, + remove_load: Sys$_Application$remove_load, + add_navigate: Sys$_Application$add_navigate, + remove_navigate: Sys$_Application$remove_navigate, + add_unload: Sys$_Application$add_unload, + remove_unload: Sys$_Application$remove_unload, + addComponent: Sys$_Application$addComponent, + addHistoryPoint: Sys$_Application$addHistoryPoint, + beginCreateComponents: Sys$_Application$beginCreateComponents, + dispose: Sys$_Application$dispose, + endCreateComponents: Sys$_Application$endCreateComponents, + findComponent: Sys$_Application$findComponent, + getComponents: Sys$_Application$getComponents, + initialize: Sys$_Application$initialize, + notifyScriptLoaded: Sys$_Application$notifyScriptLoaded, + registerDisposableObject: Sys$_Application$registerDisposableObject, + raiseLoad: Sys$_Application$raiseLoad, + removeComponent: Sys$_Application$removeComponent, + setServerId: Sys$_Application$setServerId, + setServerState: Sys$_Application$setServerState, + unregisterDisposableObject: Sys$_Application$unregisterDisposableObject, + _addComponentToSecondPass: Sys$_Application$_addComponentToSecondPass, + _deserializeState: Sys$_Application$_deserializeState, + _doInitialize: Sys$_Application$_doInitialize, + _enableHistoryInScriptManager: Sys$_Application$_enableHistoryInScriptManager, + _ensureHistory: Sys$_Application$_ensureHistory, + _getHistory: Sys$_Application$_getHistory, + _isSafari2: Sys$_Application$_isSafari2, + _loadHandler: Sys$_Application$_loadHandler, + _navigate: Sys$_Application$_navigate, + _onIdle: Sys$_Application$_onIdle, + _onIFrameLoad: Sys$_Application$_onIFrameLoad, + _onPageRequestManagerBeginRequest: Sys$_Application$_onPageRequestManagerBeginRequest, + _onPageRequestManagerEndRequest: Sys$_Application$_onPageRequestManagerEndRequest, + _raiseNavigate: Sys$_Application$_raiseNavigate, + _serializeState: Sys$_Application$_serializeState, + _setHistory: Sys$_Application$_setHistory, + _setState: Sys$_Application$_setState, + _unloadHandler: Sys$_Application$_unloadHandler, + _updateHiddenField: Sys$_Application$_updateHiddenField +} +Sys._Application.registerClass('Sys._Application', Sys.Component, Sys.IContainer); +Sys.Application = new Sys._Application(); +var $find = Sys.Application.findComponent; +Type.registerNamespace('Sys.Net'); + +Sys.Net.WebRequestExecutor = function Sys$Net$WebRequestExecutor() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + this._webRequest = null; + this._resultObject = null; +} + function Sys$Net$WebRequestExecutor$get_webRequest() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._webRequest; + } + function Sys$Net$WebRequestExecutor$_set_webRequest(value) { + if (this.get_started()) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallOnceStarted, 'set_webRequest')); + } + this._webRequest = value; + } + function Sys$Net$WebRequestExecutor$get_started() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); + } + function Sys$Net$WebRequestExecutor$get_responseAvailable() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); + } + function Sys$Net$WebRequestExecutor$get_timedOut() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); + } + function Sys$Net$WebRequestExecutor$get_aborted() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); + } + function Sys$Net$WebRequestExecutor$get_responseData() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); + } + function Sys$Net$WebRequestExecutor$get_statusCode() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); + } + function Sys$Net$WebRequestExecutor$get_statusText() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); + } + function Sys$Net$WebRequestExecutor$get_xml() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); + } + function Sys$Net$WebRequestExecutor$get_object() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (!this._resultObject) { + this._resultObject = Sys.Serialization.JavaScriptSerializer.deserialize(this.get_responseData()); + } + return this._resultObject; + } + function Sys$Net$WebRequestExecutor$executeRequest() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); + } + function Sys$Net$WebRequestExecutor$abort() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); + } + function Sys$Net$WebRequestExecutor$getResponseHeader(header) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "header", type: String} + ]); + if (e) throw e; + throw Error.notImplemented(); + } + function Sys$Net$WebRequestExecutor$getAllResponseHeaders() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); + } +Sys.Net.WebRequestExecutor.prototype = { + get_webRequest: Sys$Net$WebRequestExecutor$get_webRequest, + _set_webRequest: Sys$Net$WebRequestExecutor$_set_webRequest, + get_started: Sys$Net$WebRequestExecutor$get_started, + get_responseAvailable: Sys$Net$WebRequestExecutor$get_responseAvailable, + get_timedOut: Sys$Net$WebRequestExecutor$get_timedOut, + get_aborted: Sys$Net$WebRequestExecutor$get_aborted, + get_responseData: Sys$Net$WebRequestExecutor$get_responseData, + get_statusCode: Sys$Net$WebRequestExecutor$get_statusCode, + get_statusText: Sys$Net$WebRequestExecutor$get_statusText, + get_xml: Sys$Net$WebRequestExecutor$get_xml, + get_object: Sys$Net$WebRequestExecutor$get_object, + executeRequest: Sys$Net$WebRequestExecutor$executeRequest, + abort: Sys$Net$WebRequestExecutor$abort, + getResponseHeader: Sys$Net$WebRequestExecutor$getResponseHeader, + getAllResponseHeaders: Sys$Net$WebRequestExecutor$getAllResponseHeaders +} +Sys.Net.WebRequestExecutor.registerClass('Sys.Net.WebRequestExecutor'); + +Sys.Net.XMLDOM = function Sys$Net$XMLDOM(markup) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "markup", type: String} + ]); + if (e) throw e; + if (!window.DOMParser) { + var progIDs = [ 'Msxml2.DOMDocument.3.0', 'Msxml2.DOMDocument' ]; + for (var i = 0, l = progIDs.length; i < l; i++) { + try { + var xmlDOM = new ActiveXObject(progIDs[i]); + xmlDOM.async = false; + xmlDOM.loadXML(markup); + xmlDOM.setProperty('SelectionLanguage', 'XPath'); + return xmlDOM; + } + catch (ex) { + } + } + } + else { + try { + var domParser = new window.DOMParser(); + return domParser.parseFromString(markup, 'text/xml'); + } + catch (ex) { + } + } + return null; +} +Sys.Net.XMLHttpExecutor = function Sys$Net$XMLHttpExecutor() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + Sys.Net.XMLHttpExecutor.initializeBase(this); + var _this = this; + this._xmlHttpRequest = null; + this._webRequest = null; + this._responseAvailable = false; + this._timedOut = false; + this._timer = null; + this._aborted = false; + this._started = false; + this._onReadyStateChange = (function () { + + if (_this._xmlHttpRequest.readyState === 4 ) { + try { + if (typeof(_this._xmlHttpRequest.status) === "undefined") { + return; + } + } + catch(ex) { + return; + } + + _this._clearTimer(); + _this._responseAvailable = true; + try { + _this._webRequest.completed(Sys.EventArgs.Empty); + } + finally { + if (_this._xmlHttpRequest != null) { + _this._xmlHttpRequest.onreadystatechange = Function.emptyMethod; + _this._xmlHttpRequest = null; + } + } + } + }); + this._clearTimer = (function() { + if (_this._timer != null) { + window.clearTimeout(_this._timer); + _this._timer = null; + } + }); + this._onTimeout = (function() { + if (!_this._responseAvailable) { + _this._clearTimer(); + _this._timedOut = true; + _this._xmlHttpRequest.onreadystatechange = Function.emptyMethod; + _this._xmlHttpRequest.abort(); + _this._webRequest.completed(Sys.EventArgs.Empty); + _this._xmlHttpRequest = null; + } + }); +} + function Sys$Net$XMLHttpExecutor$get_timedOut() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._timedOut; + } + function Sys$Net$XMLHttpExecutor$get_started() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._started; + } + function Sys$Net$XMLHttpExecutor$get_responseAvailable() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._responseAvailable; + } + function Sys$Net$XMLHttpExecutor$get_aborted() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._aborted; + } + function Sys$Net$XMLHttpExecutor$executeRequest() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + this._webRequest = this.get_webRequest(); + if (this._started) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallOnceStarted, 'executeRequest')); + } + if (this._webRequest === null) { + throw Error.invalidOperation(Sys.Res.nullWebRequest); + } + var body = this._webRequest.get_body(); + var headers = this._webRequest.get_headers(); + this._xmlHttpRequest = new XMLHttpRequest(); + this._xmlHttpRequest.onreadystatechange = this._onReadyStateChange; + var verb = this._webRequest.get_httpVerb(); + this._xmlHttpRequest.open(verb, this._webRequest.getResolvedUrl(), true ); + if (headers) { + for (var header in headers) { + var val = headers[header]; + if (typeof(val) !== "function") + this._xmlHttpRequest.setRequestHeader(header, val); + } + } + if (verb.toLowerCase() === "post") { + if ((headers === null) || !headers['Content-Type']) { + this._xmlHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8'); + } + if (!body) { + body = ""; + } + } + var timeout = this._webRequest.get_timeout(); + if (timeout > 0) { + this._timer = window.setTimeout(Function.createDelegate(this, this._onTimeout), timeout); + } + this._xmlHttpRequest.send(body); + this._started = true; + } + function Sys$Net$XMLHttpExecutor$getResponseHeader(header) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "header", type: String} + ]); + if (e) throw e; + if (!this._responseAvailable) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallBeforeResponse, 'getResponseHeader')); + } + if (!this._xmlHttpRequest) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallOutsideHandler, 'getResponseHeader')); + } + var result; + try { + result = this._xmlHttpRequest.getResponseHeader(header); + } catch (e) { + } + if (!result) result = ""; + return result; + } + function Sys$Net$XMLHttpExecutor$getAllResponseHeaders() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (!this._responseAvailable) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallBeforeResponse, 'getAllResponseHeaders')); + } + if (!this._xmlHttpRequest) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallOutsideHandler, 'getAllResponseHeaders')); + } + return this._xmlHttpRequest.getAllResponseHeaders(); + } + function Sys$Net$XMLHttpExecutor$get_responseData() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (!this._responseAvailable) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallBeforeResponse, 'get_responseData')); + } + if (!this._xmlHttpRequest) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallOutsideHandler, 'get_responseData')); + } + return this._xmlHttpRequest.responseText; + } + function Sys$Net$XMLHttpExecutor$get_statusCode() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (!this._responseAvailable) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallBeforeResponse, 'get_statusCode')); + } + if (!this._xmlHttpRequest) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallOutsideHandler, 'get_statusCode')); + } + var result = 0; + try { + result = this._xmlHttpRequest.status; + } + catch(ex) { + } + return result; + } + function Sys$Net$XMLHttpExecutor$get_statusText() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (!this._responseAvailable) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallBeforeResponse, 'get_statusText')); + } + if (!this._xmlHttpRequest) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallOutsideHandler, 'get_statusText')); + } + return this._xmlHttpRequest.statusText; + } + function Sys$Net$XMLHttpExecutor$get_xml() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (!this._responseAvailable) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallBeforeResponse, 'get_xml')); + } + if (!this._xmlHttpRequest) { + throw Error.invalidOperation(String.format(Sys.Res.cannotCallOutsideHandler, 'get_xml')); + } + var xml = this._xmlHttpRequest.responseXML; + if (!xml || !xml.documentElement) { + xml = Sys.Net.XMLDOM(this._xmlHttpRequest.responseText); + if (!xml || !xml.documentElement) + return null; + } + else if (navigator.userAgent.indexOf('MSIE') !== -1) { + xml.setProperty('SelectionLanguage', 'XPath'); + } + if (xml.documentElement.namespaceURI === "http://www.mozilla.org/newlayout/xml/parsererror.xml" && + xml.documentElement.tagName === "parsererror") { + return null; + } + + if (xml.documentElement.firstChild && xml.documentElement.firstChild.tagName === "parsererror") { + return null; + } + + return xml; + } + function Sys$Net$XMLHttpExecutor$abort() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (!this._started) { + throw Error.invalidOperation(Sys.Res.cannotAbortBeforeStart); + } + if (this._aborted || this._responseAvailable || this._timedOut) + return; + this._aborted = true; + this._clearTimer(); + if (this._xmlHttpRequest && !this._responseAvailable) { + this._xmlHttpRequest.onreadystatechange = Function.emptyMethod; + this._xmlHttpRequest.abort(); + + this._xmlHttpRequest = null; + this._webRequest.completed(Sys.EventArgs.Empty); + } + } +Sys.Net.XMLHttpExecutor.prototype = { + get_timedOut: Sys$Net$XMLHttpExecutor$get_timedOut, + get_started: Sys$Net$XMLHttpExecutor$get_started, + get_responseAvailable: Sys$Net$XMLHttpExecutor$get_responseAvailable, + get_aborted: Sys$Net$XMLHttpExecutor$get_aborted, + executeRequest: Sys$Net$XMLHttpExecutor$executeRequest, + getResponseHeader: Sys$Net$XMLHttpExecutor$getResponseHeader, + getAllResponseHeaders: Sys$Net$XMLHttpExecutor$getAllResponseHeaders, + get_responseData: Sys$Net$XMLHttpExecutor$get_responseData, + get_statusCode: Sys$Net$XMLHttpExecutor$get_statusCode, + get_statusText: Sys$Net$XMLHttpExecutor$get_statusText, + get_xml: Sys$Net$XMLHttpExecutor$get_xml, + abort: Sys$Net$XMLHttpExecutor$abort +} +Sys.Net.XMLHttpExecutor.registerClass('Sys.Net.XMLHttpExecutor', Sys.Net.WebRequestExecutor); + +Sys.Net._WebRequestManager = function Sys$Net$_WebRequestManager() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + this._defaultTimeout = 0; + this._defaultExecutorType = "Sys.Net.XMLHttpExecutor"; +} + function Sys$Net$_WebRequestManager$add_invokingRequest(handler) { + /// + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this._get_eventHandlerList().addHandler("invokingRequest", handler); + } + function Sys$Net$_WebRequestManager$remove_invokingRequest(handler) { + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this._get_eventHandlerList().removeHandler("invokingRequest", handler); + } + function Sys$Net$_WebRequestManager$add_completedRequest(handler) { + /// + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this._get_eventHandlerList().addHandler("completedRequest", handler); + } + function Sys$Net$_WebRequestManager$remove_completedRequest(handler) { + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this._get_eventHandlerList().removeHandler("completedRequest", handler); + } + function Sys$Net$_WebRequestManager$_get_eventHandlerList() { + if (!this._events) { + this._events = new Sys.EventHandlerList(); + } + return this._events; + } + function Sys$Net$_WebRequestManager$get_defaultTimeout() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._defaultTimeout; + } + function Sys$Net$_WebRequestManager$set_defaultTimeout(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Number}]); + if (e) throw e; + if (value < 0) { + throw Error.argumentOutOfRange("value", value, Sys.Res.invalidTimeout); + } + this._defaultTimeout = value; + } + function Sys$Net$_WebRequestManager$get_defaultExecutorType() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._defaultExecutorType; + } + function Sys$Net$_WebRequestManager$set_defaultExecutorType(value) { + var e = Function._validateParams(arguments, [{name: "value", type: String}]); + if (e) throw e; + this._defaultExecutorType = value; + } + function Sys$Net$_WebRequestManager$executeRequest(webRequest) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "webRequest", type: Sys.Net.WebRequest} + ]); + if (e) throw e; + var executor = webRequest.get_executor(); + if (!executor) { + var failed = false; + try { + var executorType = eval(this._defaultExecutorType); + executor = new executorType(); + } catch (e) { + failed = true; + } + if (failed || !Sys.Net.WebRequestExecutor.isInstanceOfType(executor) || !executor) { + throw Error.argument("defaultExecutorType", String.format(Sys.Res.invalidExecutorType, this._defaultExecutorType)); + } + webRequest.set_executor(executor); + } + if (executor.get_aborted()) { + return; + } + var evArgs = new Sys.Net.NetworkRequestEventArgs(webRequest); + var handler = this._get_eventHandlerList().getHandler("invokingRequest"); + if (handler) { + handler(this, evArgs); + } + if (!evArgs.get_cancel()) { + executor.executeRequest(); + } + } +Sys.Net._WebRequestManager.prototype = { + add_invokingRequest: Sys$Net$_WebRequestManager$add_invokingRequest, + remove_invokingRequest: Sys$Net$_WebRequestManager$remove_invokingRequest, + add_completedRequest: Sys$Net$_WebRequestManager$add_completedRequest, + remove_completedRequest: Sys$Net$_WebRequestManager$remove_completedRequest, + _get_eventHandlerList: Sys$Net$_WebRequestManager$_get_eventHandlerList, + get_defaultTimeout: Sys$Net$_WebRequestManager$get_defaultTimeout, + set_defaultTimeout: Sys$Net$_WebRequestManager$set_defaultTimeout, + get_defaultExecutorType: Sys$Net$_WebRequestManager$get_defaultExecutorType, + set_defaultExecutorType: Sys$Net$_WebRequestManager$set_defaultExecutorType, + executeRequest: Sys$Net$_WebRequestManager$executeRequest +} +Sys.Net._WebRequestManager.registerClass('Sys.Net._WebRequestManager'); +Sys.Net.WebRequestManager = new Sys.Net._WebRequestManager(); + +Sys.Net.NetworkRequestEventArgs = function Sys$Net$NetworkRequestEventArgs(webRequest) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "webRequest", type: Sys.Net.WebRequest} + ]); + if (e) throw e; + Sys.Net.NetworkRequestEventArgs.initializeBase(this); + this._webRequest = webRequest; +} + function Sys$Net$NetworkRequestEventArgs$get_webRequest() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._webRequest; + } +Sys.Net.NetworkRequestEventArgs.prototype = { + get_webRequest: Sys$Net$NetworkRequestEventArgs$get_webRequest +} +Sys.Net.NetworkRequestEventArgs.registerClass('Sys.Net.NetworkRequestEventArgs', Sys.CancelEventArgs); + +Sys.Net.WebRequest = function Sys$Net$WebRequest() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + this._url = ""; + this._headers = { }; + this._body = null; + this._userContext = null; + this._httpVerb = null; + this._executor = null; + this._invokeCalled = false; + this._timeout = 0; +} + function Sys$Net$WebRequest$add_completed(handler) { + /// + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this._get_eventHandlerList().addHandler("completed", handler); + } + function Sys$Net$WebRequest$remove_completed(handler) { + var e = Function._validateParams(arguments, [{name: "handler", type: Function}]); + if (e) throw e; + this._get_eventHandlerList().removeHandler("completed", handler); + } + function Sys$Net$WebRequest$completed(eventArgs) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "eventArgs", type: Sys.EventArgs} + ]); + if (e) throw e; + var handler = Sys.Net.WebRequestManager._get_eventHandlerList().getHandler("completedRequest"); + if (handler) { + handler(this._executor, eventArgs); + } + handler = this._get_eventHandlerList().getHandler("completed"); + if (handler) { + handler(this._executor, eventArgs); + } + } + function Sys$Net$WebRequest$_get_eventHandlerList() { + if (!this._events) { + this._events = new Sys.EventHandlerList(); + } + return this._events; + } + function Sys$Net$WebRequest$get_url() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._url; + } + function Sys$Net$WebRequest$set_url(value) { + var e = Function._validateParams(arguments, [{name: "value", type: String}]); + if (e) throw e; + this._url = value; + } + function Sys$Net$WebRequest$get_headers() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._headers; + } + function Sys$Net$WebRequest$get_httpVerb() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (this._httpVerb === null) { + if (this._body === null) { + return "GET"; + } + return "POST"; + } + return this._httpVerb; + } + function Sys$Net$WebRequest$set_httpVerb(value) { + var e = Function._validateParams(arguments, [{name: "value", type: String}]); + if (e) throw e; + if (value.length === 0) { + throw Error.argument('value', Sys.Res.invalidHttpVerb); + } + this._httpVerb = value; + } + function Sys$Net$WebRequest$get_body() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._body; + } + function Sys$Net$WebRequest$set_body(value) { + var e = Function._validateParams(arguments, [{name: "value", mayBeNull: true}]); + if (e) throw e; + this._body = value; + } + function Sys$Net$WebRequest$get_userContext() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._userContext; + } + function Sys$Net$WebRequest$set_userContext(value) { + var e = Function._validateParams(arguments, [{name: "value", mayBeNull: true}]); + if (e) throw e; + this._userContext = value; + } + function Sys$Net$WebRequest$get_executor() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._executor; + } + function Sys$Net$WebRequest$set_executor(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Sys.Net.WebRequestExecutor}]); + if (e) throw e; + if (this._executor !== null && this._executor.get_started()) { + throw Error.invalidOperation(Sys.Res.setExecutorAfterActive); + } + this._executor = value; + this._executor._set_webRequest(this); + } + function Sys$Net$WebRequest$get_timeout() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (this._timeout === 0) { + return Sys.Net.WebRequestManager.get_defaultTimeout(); + } + return this._timeout; + } + function Sys$Net$WebRequest$set_timeout(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Number}]); + if (e) throw e; + if (value < 0) { + throw Error.argumentOutOfRange("value", value, Sys.Res.invalidTimeout); + } + this._timeout = value; + } + function Sys$Net$WebRequest$getResolvedUrl() { + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return Sys.Net.WebRequest._resolveUrl(this._url); + } + function Sys$Net$WebRequest$invoke() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (this._invokeCalled) { + throw Error.invalidOperation(Sys.Res.invokeCalledTwice); + } + Sys.Net.WebRequestManager.executeRequest(this); + this._invokeCalled = true; + } +Sys.Net.WebRequest.prototype = { + add_completed: Sys$Net$WebRequest$add_completed, + remove_completed: Sys$Net$WebRequest$remove_completed, + completed: Sys$Net$WebRequest$completed, + _get_eventHandlerList: Sys$Net$WebRequest$_get_eventHandlerList, + get_url: Sys$Net$WebRequest$get_url, + set_url: Sys$Net$WebRequest$set_url, + get_headers: Sys$Net$WebRequest$get_headers, + get_httpVerb: Sys$Net$WebRequest$get_httpVerb, + set_httpVerb: Sys$Net$WebRequest$set_httpVerb, + get_body: Sys$Net$WebRequest$get_body, + set_body: Sys$Net$WebRequest$set_body, + get_userContext: Sys$Net$WebRequest$get_userContext, + set_userContext: Sys$Net$WebRequest$set_userContext, + get_executor: Sys$Net$WebRequest$get_executor, + set_executor: Sys$Net$WebRequest$set_executor, + get_timeout: Sys$Net$WebRequest$get_timeout, + set_timeout: Sys$Net$WebRequest$set_timeout, + getResolvedUrl: Sys$Net$WebRequest$getResolvedUrl, + invoke: Sys$Net$WebRequest$invoke +} +Sys.Net.WebRequest._resolveUrl = function Sys$Net$WebRequest$_resolveUrl(url, baseUrl) { + if (url && url.indexOf('://') !== -1) { + return url; + } + if (!baseUrl || baseUrl.length === 0) { + var baseElement = document.getElementsByTagName('base')[0]; + if (baseElement && baseElement.href && baseElement.href.length > 0) { + baseUrl = baseElement.href; + } + else { + baseUrl = document.URL; + } + } + var qsStart = baseUrl.indexOf('?'); + if (qsStart !== -1) { + baseUrl = baseUrl.substr(0, qsStart); + } + qsStart = baseUrl.indexOf('#'); + if (qsStart !== -1) { + baseUrl = baseUrl.substr(0, qsStart); + } + baseUrl = baseUrl.substr(0, baseUrl.lastIndexOf('/') + 1); + if (!url || url.length === 0) { + return baseUrl; + } + if (url.charAt(0) === '/') { + var slashslash = baseUrl.indexOf('://'); + if (slashslash === -1) { + throw Error.argument("baseUrl", Sys.Res.badBaseUrl1); + } + var nextSlash = baseUrl.indexOf('/', slashslash + 3); + if (nextSlash === -1) { + throw Error.argument("baseUrl", Sys.Res.badBaseUrl2); + } + return baseUrl.substr(0, nextSlash) + url; + } + else { + var lastSlash = baseUrl.lastIndexOf('/'); + if (lastSlash === -1) { + throw Error.argument("baseUrl", Sys.Res.badBaseUrl3); + } + return baseUrl.substr(0, lastSlash+1) + url; + } +} +Sys.Net.WebRequest._createQueryString = function Sys$Net$WebRequest$_createQueryString(queryString, encodeMethod) { + if (!encodeMethod) + encodeMethod = encodeURIComponent; + var sb = new Sys.StringBuilder(); + var i = 0; + for (var arg in queryString) { + var obj = queryString[arg]; + if (typeof(obj) === "function") continue; + var val = Sys.Serialization.JavaScriptSerializer.serialize(obj); + if (i !== 0) { + sb.append('&'); + } + sb.append(arg); + sb.append('='); + sb.append(encodeMethod(val)); + i++; + } + return sb.toString(); +} +Sys.Net.WebRequest._createUrl = function Sys$Net$WebRequest$_createUrl(url, queryString) { + if (!queryString) { + return url; + } + var qs = Sys.Net.WebRequest._createQueryString(queryString); + if (qs.length > 0) { + var sep = '?'; + if (url && url.indexOf('?') !== -1) + sep = '&'; + return url + sep + qs; + } else { + return url; + } +} +Sys.Net.WebRequest.registerClass('Sys.Net.WebRequest'); + +Sys.Net.WebServiceProxy = function Sys$Net$WebServiceProxy() { +} + function Sys$Net$WebServiceProxy$get_timeout() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._timeout; + } + function Sys$Net$WebServiceProxy$set_timeout(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Number}]); + if (e) throw e; + if (value < 0) { throw Error.argumentOutOfRange('value', value, Sys.Res.invalidTimeout); } + this._timeout = value; + } + function Sys$Net$WebServiceProxy$get_defaultUserContext() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._userContext; + } + function Sys$Net$WebServiceProxy$set_defaultUserContext(value) { + var e = Function._validateParams(arguments, [{name: "value", mayBeNull: true}]); + if (e) throw e; + this._userContext = value; + } + function Sys$Net$WebServiceProxy$get_defaultSucceededCallback() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._succeeded; + } + function Sys$Net$WebServiceProxy$set_defaultSucceededCallback(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Function, mayBeNull: true}]); + if (e) throw e; + this._succeeded = value; + } + function Sys$Net$WebServiceProxy$get_defaultFailedCallback() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._failed; + } + function Sys$Net$WebServiceProxy$set_defaultFailedCallback(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Function, mayBeNull: true}]); + if (e) throw e; + this._failed = value; + } + function Sys$Net$WebServiceProxy$get_path() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._path; + } + function Sys$Net$WebServiceProxy$set_path(value) { + var e = Function._validateParams(arguments, [{name: "value", type: String}]); + if (e) throw e; + this._path = value; + } + function Sys$Net$WebServiceProxy$_invoke(servicePath, methodName, useGet, params, onSuccess, onFailure, userContext) { + /// + /// + /// + /// + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "servicePath", type: String}, + {name: "methodName", type: String}, + {name: "useGet", type: Boolean}, + {name: "params"}, + {name: "onSuccess", type: Function, mayBeNull: true, optional: true}, + {name: "onFailure", type: Function, mayBeNull: true, optional: true}, + {name: "userContext", mayBeNull: true, optional: true} + ]); + if (e) throw e; + if (onSuccess === null || typeof onSuccess === 'undefined') onSuccess = this.get_defaultSucceededCallback(); + if (onFailure === null || typeof onFailure === 'undefined') onFailure = this.get_defaultFailedCallback(); + if (userContext === null || typeof userContext === 'undefined') userContext = this.get_defaultUserContext(); + + return Sys.Net.WebServiceProxy.invoke(servicePath, methodName, useGet, params, onSuccess, onFailure, userContext, this.get_timeout()); + } +Sys.Net.WebServiceProxy.prototype = { + get_timeout: Sys$Net$WebServiceProxy$get_timeout, + set_timeout: Sys$Net$WebServiceProxy$set_timeout, + get_defaultUserContext: Sys$Net$WebServiceProxy$get_defaultUserContext, + set_defaultUserContext: Sys$Net$WebServiceProxy$set_defaultUserContext, + get_defaultSucceededCallback: Sys$Net$WebServiceProxy$get_defaultSucceededCallback, + set_defaultSucceededCallback: Sys$Net$WebServiceProxy$set_defaultSucceededCallback, + get_defaultFailedCallback: Sys$Net$WebServiceProxy$get_defaultFailedCallback, + set_defaultFailedCallback: Sys$Net$WebServiceProxy$set_defaultFailedCallback, + get_path: Sys$Net$WebServiceProxy$get_path, + set_path: Sys$Net$WebServiceProxy$set_path, + _invoke: Sys$Net$WebServiceProxy$_invoke +} +Sys.Net.WebServiceProxy.registerClass('Sys.Net.WebServiceProxy'); +Sys.Net.WebServiceProxy.invoke = function Sys$Net$WebServiceProxy$invoke(servicePath, methodName, useGet, params, onSuccess, onFailure, userContext, timeout) { + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "servicePath", type: String}, + {name: "methodName", type: String}, + {name: "useGet", type: Boolean, optional: true}, + {name: "params", mayBeNull: true, optional: true}, + {name: "onSuccess", type: Function, mayBeNull: true, optional: true}, + {name: "onFailure", type: Function, mayBeNull: true, optional: true}, + {name: "userContext", mayBeNull: true, optional: true}, + {name: "timeout", type: Number, optional: true} + ]); + if (e) throw e; + var request = new Sys.Net.WebRequest(); + request.get_headers()['Content-Type'] = 'application/json; charset=utf-8'; + if (!params) params = {}; + var urlParams = params; + if (!useGet || !urlParams) urlParams = {}; + request.set_url(Sys.Net.WebRequest._createUrl(servicePath+"/"+encodeURIComponent(methodName), urlParams)); + var body = null; + if (!useGet) { + body = Sys.Serialization.JavaScriptSerializer.serialize(params); + if (body === "{}") body = ""; + } + request.set_body(body); + request.add_completed(onComplete); + if (timeout && timeout > 0) request.set_timeout(timeout); + request.invoke(); + function onComplete(response, eventArgs) { + if (response.get_responseAvailable()) { + var statusCode = response.get_statusCode(); + var result = null; + + try { + var contentType = response.getResponseHeader("Content-Type"); + if (contentType.startsWith("application/json")) { + result = response.get_object(); + } + else if (contentType.startsWith("text/xml")) { + result = response.get_xml(); + } + else { + result = response.get_responseData(); + } + } catch (ex) { + } + var error = response.getResponseHeader("jsonerror"); + var errorObj = (error === "true"); + if (errorObj) { + if (result) { + result = new Sys.Net.WebServiceError(false, result.Message, result.StackTrace, result.ExceptionType); + } + } + else if (contentType.startsWith("application/json")) { + if (!result || typeof(result.d) === "undefined") { + throw Sys.Net.WebServiceProxy._createFailedError(methodName, String.format(Sys.Res.webServiceInvalidJsonWrapper, methodName)); + } + result = result.d; + } + if (((statusCode < 200) || (statusCode >= 300)) || errorObj) { + if (onFailure) { + if (!result || !errorObj) { + result = new Sys.Net.WebServiceError(false , String.format(Sys.Res.webServiceFailedNoMsg, methodName), "", ""); + } + result._statusCode = statusCode; + onFailure(result, userContext, methodName); + } + else { + var error; + if (result && errorObj) { + error = result.get_exceptionType() + "-- " + result.get_message(); + } + else { + error = response.get_responseData(); + } + throw Sys.Net.WebServiceProxy._createFailedError(methodName, String.format(Sys.Res.webServiceFailed, methodName, error)); + } + } + else if (onSuccess) { + onSuccess(result, userContext, methodName); + } + } + else { + var msg; + if (response.get_timedOut()) { + msg = String.format(Sys.Res.webServiceTimedOut, methodName); + } + else { + msg = String.format(Sys.Res.webServiceFailedNoMsg, methodName) + } + if (onFailure) { + onFailure(new Sys.Net.WebServiceError(response.get_timedOut(), msg, "", ""), userContext, methodName); + } + else { + throw Sys.Net.WebServiceProxy._createFailedError(methodName, msg); + } + } + } + return request; +} +Sys.Net.WebServiceProxy._createFailedError = function Sys$Net$WebServiceProxy$_createFailedError(methodName, errorMessage) { + var displayMessage = "Sys.Net.WebServiceFailedException: " + errorMessage; + var e = Error.create(displayMessage, { 'name': 'Sys.Net.WebServiceFailedException', 'methodName': methodName }); + e.popStackFrame(); + return e; +} +Sys.Net.WebServiceProxy._defaultFailedCallback = function Sys$Net$WebServiceProxy$_defaultFailedCallback(err, methodName) { + var error = err.get_exceptionType() + "-- " + err.get_message(); + throw Sys.Net.WebServiceProxy._createFailedError(methodName, String.format(Sys.Res.webServiceFailed, methodName, error)); +} +Sys.Net.WebServiceProxy._generateTypedConstructor = function Sys$Net$WebServiceProxy$_generateTypedConstructor(type) { + return function(properties) { + if (properties) { + for (var name in properties) { + this[name] = properties[name]; + } + } + this.__type = type; + } +} + +Sys.Net.WebServiceError = function Sys$Net$WebServiceError(timedOut, message, stackTrace, exceptionType) { + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "timedOut", type: Boolean}, + {name: "message", type: String, mayBeNull: true}, + {name: "stackTrace", type: String, mayBeNull: true}, + {name: "exceptionType", type: String, mayBeNull: true} + ]); + if (e) throw e; + this._timedOut = timedOut; + this._message = message; + this._stackTrace = stackTrace; + this._exceptionType = exceptionType; + this._statusCode = -1; +} + function Sys$Net$WebServiceError$get_timedOut() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._timedOut; + } + function Sys$Net$WebServiceError$get_statusCode() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._statusCode; + } + function Sys$Net$WebServiceError$get_message() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._message; + } + function Sys$Net$WebServiceError$get_stackTrace() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._stackTrace; + } + function Sys$Net$WebServiceError$get_exceptionType() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._exceptionType; + } +Sys.Net.WebServiceError.prototype = { + get_timedOut: Sys$Net$WebServiceError$get_timedOut, + get_statusCode: Sys$Net$WebServiceError$get_statusCode, + get_message: Sys$Net$WebServiceError$get_message, + get_stackTrace: Sys$Net$WebServiceError$get_stackTrace, + get_exceptionType: Sys$Net$WebServiceError$get_exceptionType +} +Sys.Net.WebServiceError.registerClass('Sys.Net.WebServiceError'); +Type.registerNamespace('Sys.Services'); +Sys.Services._ProfileService = function Sys$Services$_ProfileService() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + Sys.Services._ProfileService.initializeBase(this); + this.properties = {}; +} +Sys.Services._ProfileService.DefaultWebServicePath = ''; + function Sys$Services$_ProfileService$get_defaultLoadCompletedCallback() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._defaultLoadCompletedCallback; + } + function Sys$Services$_ProfileService$set_defaultLoadCompletedCallback(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Function, mayBeNull: true}]); + if (e) throw e; + this._defaultLoadCompletedCallback = value; + } + function Sys$Services$_ProfileService$get_defaultSaveCompletedCallback() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._defaultSaveCompletedCallback; + } + function Sys$Services$_ProfileService$set_defaultSaveCompletedCallback(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Function, mayBeNull: true}]); + if (e) throw e; + this._defaultSaveCompletedCallback = value; + } + function Sys$Services$_ProfileService$get_path() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._path || ''; + } + function Sys$Services$_ProfileService$load(propertyNames, loadCompletedCallback, failedCallback, userContext) { + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "propertyNames", type: Array, mayBeNull: true, optional: true, elementType: String}, + {name: "loadCompletedCallback", type: Function, mayBeNull: true, optional: true}, + {name: "failedCallback", type: Function, mayBeNull: true, optional: true}, + {name: "userContext", mayBeNull: true, optional: true} + ]); + if (e) throw e; + var parameters; + var methodName; + if (!propertyNames) { + methodName = "GetAllPropertiesForCurrentUser"; + parameters = { authenticatedUserOnly: false }; + } + else { + methodName = "GetPropertiesForCurrentUser"; + parameters = { properties: this._clonePropertyNames(propertyNames), authenticatedUserOnly: false }; + } + this._invoke(this._get_path(), + methodName, + false, + parameters, + Function.createDelegate(this, this._onLoadComplete), + Function.createDelegate(this, this._onLoadFailed), + [loadCompletedCallback, failedCallback, userContext]); + } + function Sys$Services$_ProfileService$save(propertyNames, saveCompletedCallback, failedCallback, userContext) { + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "propertyNames", type: Array, mayBeNull: true, optional: true, elementType: String}, + {name: "saveCompletedCallback", type: Function, mayBeNull: true, optional: true}, + {name: "failedCallback", type: Function, mayBeNull: true, optional: true}, + {name: "userContext", mayBeNull: true, optional: true} + ]); + if (e) throw e; + var flattenedProperties = this._flattenProperties(propertyNames, this.properties); + this._invoke(this._get_path(), + "SetPropertiesForCurrentUser", + false, + { values: flattenedProperties.value, authenticatedUserOnly: false }, + Function.createDelegate(this, this._onSaveComplete), + Function.createDelegate(this, this._onSaveFailed), + [saveCompletedCallback, failedCallback, userContext, flattenedProperties.count]); + } + function Sys$Services$_ProfileService$_clonePropertyNames(arr) { + var nodups = []; + var seen = {}; + for (var i=0; i < arr.length; i++) { + var prop = arr[i]; + if(!seen[prop]) { Array.add(nodups, prop); seen[prop]=true; }; + } + return nodups; + } + function Sys$Services$_ProfileService$_flattenProperties(propertyNames, properties, groupName) { + var flattenedProperties = {}; + var val; + var key; + var count = 0; + if (propertyNames && propertyNames.length === 0) { + return { value: flattenedProperties, count: 0 }; + } + for (var property in properties) { + val = properties[property]; + key = groupName ? groupName + "." + property : property; + if(Sys.Services.ProfileGroup.isInstanceOfType(val)) { + var obj = this._flattenProperties(propertyNames, val, key); + var groupProperties = obj.value; + count += obj.count; + for(var subKey in groupProperties) { + var subVal = groupProperties[subKey]; + flattenedProperties[subKey] = subVal; + } + } + else { + if(!propertyNames || Array.indexOf(propertyNames, key) !== -1) { + flattenedProperties[key] = val; + count++; + } + } + } + return { value: flattenedProperties, count: count }; + } + function Sys$Services$_ProfileService$_get_path() { + var path = this.get_path(); + if (!path.length) { + path = Sys.Services._ProfileService.DefaultWebServicePath; + } + if (!path || !path.length) { + throw Error.invalidOperation(Sys.Res.servicePathNotSet); + } + return path; + } + function Sys$Services$_ProfileService$_onLoadComplete(result, context, methodName) { + if (typeof(result) !== "object") { + throw Error.invalidOperation(String.format(Sys.Res.webServiceInvalidReturnType, methodName, "Object")); + } + var unflattened = this._unflattenProperties(result); + for (var name in unflattened) { + this.properties[name] = unflattened[name]; + } + + var callback = context[0] || this.get_defaultLoadCompletedCallback() || this.get_defaultSucceededCallback(); + if (callback) { + var userContext = context[2] || this.get_defaultUserContext(); + callback(result.length, userContext, "Sys.Services.ProfileService.load"); + } + } + function Sys$Services$_ProfileService$_onLoadFailed(err, context, methodName) { + var callback = context[1] || this.get_defaultFailedCallback(); + if (callback) { + var userContext = context[2] || this.get_defaultUserContext(); + callback(err, userContext, "Sys.Services.ProfileService.load"); + } + else { + Sys.Net.WebServiceProxy._defaultFailedCallback(err, methodName); + } + } + function Sys$Services$_ProfileService$_onSaveComplete(result, context, methodName) { + var count = context[3]; + if (result !== null) { + if (result instanceof Array) { + count -= result.length; + } + else if (typeof(result) === 'number') { + count = result; + } + else { + throw Error.invalidOperation(String.format(Sys.Res.webServiceInvalidReturnType, methodName, "Array")); + } + } + + var callback = context[0] || this.get_defaultSaveCompletedCallback() || this.get_defaultSucceededCallback(); + if (callback) { + var userContext = context[2] || this.get_defaultUserContext(); + callback(count, userContext, "Sys.Services.ProfileService.save"); + } + } + function Sys$Services$_ProfileService$_onSaveFailed(err, context, methodName) { + var callback = context[1] || this.get_defaultFailedCallback(); + if (callback) { + var userContext = context[2] || this.get_defaultUserContext(); + callback(err, userContext, "Sys.Services.ProfileService.save"); + } + else { + Sys.Net.WebServiceProxy._defaultFailedCallback(err, methodName); + } + } + function Sys$Services$_ProfileService$_unflattenProperties(properties) { + var unflattenedProperties = {}; + var dotIndex; + var val; + var count = 0; + for (var key in properties) { + count++; + val = properties[key]; + dotIndex = key.indexOf('.'); + if (dotIndex !== -1) { + var groupName = key.substr(0, dotIndex); + key = key.substr(dotIndex+1); + var group = unflattenedProperties[groupName]; + if (!group || !Sys.Services.ProfileGroup.isInstanceOfType(group)) { + group = new Sys.Services.ProfileGroup(); + unflattenedProperties[groupName] = group; + } + group[key] = val; + } + else { + unflattenedProperties[key] = val; + } + } + properties.length = count; + return unflattenedProperties; + } +Sys.Services._ProfileService.prototype = { + _defaultLoadCompletedCallback: null, + _defaultSaveCompletedCallback: null, + _path: '', + _timeout: 0, + get_defaultLoadCompletedCallback: Sys$Services$_ProfileService$get_defaultLoadCompletedCallback, + set_defaultLoadCompletedCallback: Sys$Services$_ProfileService$set_defaultLoadCompletedCallback, + get_defaultSaveCompletedCallback: Sys$Services$_ProfileService$get_defaultSaveCompletedCallback, + set_defaultSaveCompletedCallback: Sys$Services$_ProfileService$set_defaultSaveCompletedCallback, + get_path: Sys$Services$_ProfileService$get_path, + load: Sys$Services$_ProfileService$load, + save: Sys$Services$_ProfileService$save, + _clonePropertyNames: Sys$Services$_ProfileService$_clonePropertyNames, + _flattenProperties: Sys$Services$_ProfileService$_flattenProperties, + _get_path: Sys$Services$_ProfileService$_get_path, + _onLoadComplete: Sys$Services$_ProfileService$_onLoadComplete, + _onLoadFailed: Sys$Services$_ProfileService$_onLoadFailed, + _onSaveComplete: Sys$Services$_ProfileService$_onSaveComplete, + _onSaveFailed: Sys$Services$_ProfileService$_onSaveFailed, + _unflattenProperties: Sys$Services$_ProfileService$_unflattenProperties +} +Sys.Services._ProfileService.registerClass('Sys.Services._ProfileService', Sys.Net.WebServiceProxy); +Sys.Services.ProfileService = new Sys.Services._ProfileService(); +Sys.Services.ProfileGroup = function Sys$Services$ProfileGroup(properties) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "properties", mayBeNull: true, optional: true} + ]); + if (e) throw e; + if (properties) { + for (var property in properties) { + this[property] = properties[property]; + } + } +} +Sys.Services.ProfileGroup.registerClass('Sys.Services.ProfileGroup'); +Sys.Services._AuthenticationService = function Sys$Services$_AuthenticationService() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + Sys.Services._AuthenticationService.initializeBase(this); +} +Sys.Services._AuthenticationService.DefaultWebServicePath = ''; + function Sys$Services$_AuthenticationService$get_defaultLoginCompletedCallback() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._defaultLoginCompletedCallback; + } + function Sys$Services$_AuthenticationService$set_defaultLoginCompletedCallback(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Function, mayBeNull: true}]); + if (e) throw e; + this._defaultLoginCompletedCallback = value; + } + function Sys$Services$_AuthenticationService$get_defaultLogoutCompletedCallback() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._defaultLogoutCompletedCallback; + } + function Sys$Services$_AuthenticationService$set_defaultLogoutCompletedCallback(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Function, mayBeNull: true}]); + if (e) throw e; + this._defaultLogoutCompletedCallback = value; + } + function Sys$Services$_AuthenticationService$get_isLoggedIn() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._authenticated; + } + function Sys$Services$_AuthenticationService$get_path() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._path || ''; + } + function Sys$Services$_AuthenticationService$login(username, password, isPersistent, customInfo, redirectUrl, loginCompletedCallback, failedCallback, userContext) { + /// + /// + /// + /// + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "username", type: String}, + {name: "password", type: String, mayBeNull: true}, + {name: "isPersistent", type: Boolean, mayBeNull: true, optional: true}, + {name: "customInfo", type: String, mayBeNull: true, optional: true}, + {name: "redirectUrl", type: String, mayBeNull: true, optional: true}, + {name: "loginCompletedCallback", type: Function, mayBeNull: true, optional: true}, + {name: "failedCallback", type: Function, mayBeNull: true, optional: true}, + {name: "userContext", mayBeNull: true, optional: true} + ]); + if (e) throw e; + this._invoke(this._get_path(), "Login", false, + { userName: username, password: password, createPersistentCookie: isPersistent }, + Function.createDelegate(this, this._onLoginComplete), + Function.createDelegate(this, this._onLoginFailed), + [username, password, isPersistent, customInfo, redirectUrl, loginCompletedCallback, failedCallback, userContext]); + } + function Sys$Services$_AuthenticationService$logout(redirectUrl, logoutCompletedCallback, failedCallback, userContext) { + /// + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "redirectUrl", type: String, mayBeNull: true, optional: true}, + {name: "logoutCompletedCallback", type: Function, mayBeNull: true, optional: true}, + {name: "failedCallback", type: Function, mayBeNull: true, optional: true}, + {name: "userContext", mayBeNull: true, optional: true} + ]); + if (e) throw e; + this._invoke(this._get_path(), "Logout", false, {}, + Function.createDelegate(this, this._onLogoutComplete), + Function.createDelegate(this, this._onLogoutFailed), + [redirectUrl, logoutCompletedCallback, failedCallback, userContext]); + } + function Sys$Services$_AuthenticationService$_get_path() { + var path = this.get_path(); + if(!path.length) { + path = Sys.Services._AuthenticationService.DefaultWebServicePath; + } + if(!path || !path.length) { + throw Error.invalidOperation(Sys.Res.servicePathNotSet); + } + return path; + } + function Sys$Services$_AuthenticationService$_onLoginComplete(result, context, methodName) { + if(typeof(result) !== "boolean") { + throw Error.invalidOperation(String.format(Sys.Res.webServiceInvalidReturnType, methodName, "Boolean")); + } + + var redirectUrl = context[4]; + var userContext = context[7] || this.get_defaultUserContext(); + var callback = context[5] || this.get_defaultLoginCompletedCallback() || this.get_defaultSucceededCallback(); + + if(result) { + this._authenticated = true; + if (callback) { + callback(true, userContext, "Sys.Services.AuthenticationService.login"); + } + + if (typeof(redirectUrl) !== "undefined" && redirectUrl !== null) { + window.location.href = redirectUrl; + } + } + else if (callback) { + callback(false, userContext, "Sys.Services.AuthenticationService.login"); + } + } + function Sys$Services$_AuthenticationService$_onLoginFailed(err, context, methodName) { + var callback = context[6] || this.get_defaultFailedCallback(); + if (callback) { + var userContext = context[7] || this.get_defaultUserContext(); + callback(err, userContext, "Sys.Services.AuthenticationService.login"); + } + else { + Sys.Net.WebServiceProxy._defaultFailedCallback(err, methodName); + } + } + function Sys$Services$_AuthenticationService$_onLogoutComplete(result, context, methodName) { + if(result !== null) { + throw Error.invalidOperation(String.format(Sys.Res.webServiceInvalidReturnType, methodName, "null")); + } + + var redirectUrl = context[0]; + var userContext = context[3] || this.get_defaultUserContext(); + var callback = context[1] || this.get_defaultLogoutCompletedCallback() || this.get_defaultSucceededCallback(); + this._authenticated = false; + + if (callback) { + callback(null, userContext, "Sys.Services.AuthenticationService.logout"); + } + + if(!redirectUrl) { + window.location.reload(); + } + else { + window.location.href = redirectUrl; + } + } + function Sys$Services$_AuthenticationService$_onLogoutFailed(err, context, methodName) { + var callback = context[2] || this.get_defaultFailedCallback(); + if (callback) { + callback(err, context[3], "Sys.Services.AuthenticationService.logout"); + } + else { + Sys.Net.WebServiceProxy._defaultFailedCallback(err, methodName); + } + } + function Sys$Services$_AuthenticationService$_setAuthenticated(authenticated) { + this._authenticated = authenticated; + } +Sys.Services._AuthenticationService.prototype = { + _defaultLoginCompletedCallback: null, + _defaultLogoutCompletedCallback: null, + _path: '', + _timeout: 0, + _authenticated: false, + get_defaultLoginCompletedCallback: Sys$Services$_AuthenticationService$get_defaultLoginCompletedCallback, + set_defaultLoginCompletedCallback: Sys$Services$_AuthenticationService$set_defaultLoginCompletedCallback, + get_defaultLogoutCompletedCallback: Sys$Services$_AuthenticationService$get_defaultLogoutCompletedCallback, + set_defaultLogoutCompletedCallback: Sys$Services$_AuthenticationService$set_defaultLogoutCompletedCallback, + get_isLoggedIn: Sys$Services$_AuthenticationService$get_isLoggedIn, + get_path: Sys$Services$_AuthenticationService$get_path, + login: Sys$Services$_AuthenticationService$login, + logout: Sys$Services$_AuthenticationService$logout, + _get_path: Sys$Services$_AuthenticationService$_get_path, + _onLoginComplete: Sys$Services$_AuthenticationService$_onLoginComplete, + _onLoginFailed: Sys$Services$_AuthenticationService$_onLoginFailed, + _onLogoutComplete: Sys$Services$_AuthenticationService$_onLogoutComplete, + _onLogoutFailed: Sys$Services$_AuthenticationService$_onLogoutFailed, + _setAuthenticated: Sys$Services$_AuthenticationService$_setAuthenticated +} +Sys.Services._AuthenticationService.registerClass('Sys.Services._AuthenticationService', Sys.Net.WebServiceProxy); +Sys.Services.AuthenticationService = new Sys.Services._AuthenticationService(); +Sys.Services._RoleService = function Sys$Services$_RoleService() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + Sys.Services._RoleService.initializeBase(this); + this._roles = []; +} +Sys.Services._RoleService.DefaultWebServicePath = ''; + function Sys$Services$_RoleService$get_defaultLoadCompletedCallback() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._defaultLoadCompletedCallback; + } + function Sys$Services$_RoleService$set_defaultLoadCompletedCallback(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Function, mayBeNull: true}]); + if (e) throw e; + this._defaultLoadCompletedCallback = value; + } + function Sys$Services$_RoleService$get_path() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._path || ''; + } + function Sys$Services$_RoleService$get_roles() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return Array.clone(this._roles); + } + function Sys$Services$_RoleService$isUserInRole(role) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "role", type: String} + ]); + if (e) throw e; + var v = this._get_rolesIndex()[role.trim().toLowerCase()]; + return !!v; + } + function Sys$Services$_RoleService$load(loadCompletedCallback, failedCallback, userContext) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "loadCompletedCallback", type: Function, mayBeNull: true, optional: true}, + {name: "failedCallback", type: Function, mayBeNull: true, optional: true}, + {name: "userContext", mayBeNull: true, optional: true} + ]); + if (e) throw e; + Sys.Net.WebServiceProxy.invoke( + this._get_path(), + "GetRolesForCurrentUser", + false, + {} , + Function.createDelegate(this, this._onLoadComplete), + Function.createDelegate(this, this._onLoadFailed), + [loadCompletedCallback, failedCallback, userContext], + this.get_timeout()); + } + function Sys$Services$_RoleService$_get_path() { + var path = this.get_path(); + if(!path || !path.length) { + path = Sys.Services._RoleService.DefaultWebServicePath; + } + if(!path || !path.length) { + throw Error.invalidOperation(Sys.Res.servicePathNotSet); + } + return path; + } + function Sys$Services$_RoleService$_get_rolesIndex() { + if (!this._rolesIndex) { + var index = {}; + for(var i=0; i < this._roles.length; i++) { + index[this._roles[i].toLowerCase()] = true; + } + this._rolesIndex = index; + } + return this._rolesIndex; + } + function Sys$Services$_RoleService$_onLoadComplete(result, context, methodName) { + if(result && !(result instanceof Array)) { + throw Error.invalidOperation(String.format(Sys.Res.webServiceInvalidReturnType, methodName, "Array")); + } + this._roles = result; + this._rolesIndex = null; + var callback = context[0] || this.get_defaultLoadCompletedCallback() || this.get_defaultSucceededCallback(); + if (callback) { + var userContext = context[2] || this.get_defaultUserContext(); + var clonedResult = Array.clone(result); + callback(clonedResult, userContext, "Sys.Services.RoleService.load"); + } + } + function Sys$Services$_RoleService$_onLoadFailed(err, context, methodName) { + var callback = context[1] || this.get_defaultFailedCallback(); + if (callback) { + var userContext = context[2] || this.get_defaultUserContext(); + callback(err, userContext, "Sys.Services.RoleService.load"); + } + else { + Sys.Net.WebServiceProxy._defaultFailedCallback(err, methodName); + } + } +Sys.Services._RoleService.prototype = { + _defaultLoadCompletedCallback: null, + _rolesIndex: null, + _timeout: 0, + _path: '', + get_defaultLoadCompletedCallback: Sys$Services$_RoleService$get_defaultLoadCompletedCallback, + set_defaultLoadCompletedCallback: Sys$Services$_RoleService$set_defaultLoadCompletedCallback, + get_path: Sys$Services$_RoleService$get_path, + get_roles: Sys$Services$_RoleService$get_roles, + isUserInRole: Sys$Services$_RoleService$isUserInRole, + load: Sys$Services$_RoleService$load, + _get_path: Sys$Services$_RoleService$_get_path, + _get_rolesIndex: Sys$Services$_RoleService$_get_rolesIndex, + _onLoadComplete: Sys$Services$_RoleService$_onLoadComplete, + _onLoadFailed: Sys$Services$_RoleService$_onLoadFailed +} +Sys.Services._RoleService.registerClass('Sys.Services._RoleService', Sys.Net.WebServiceProxy); +Sys.Services.RoleService = new Sys.Services._RoleService(); +Type.registerNamespace('Sys.Serialization'); +Sys.Serialization.JavaScriptSerializer = function Sys$Serialization$JavaScriptSerializer() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); +} +Sys.Serialization.JavaScriptSerializer.registerClass('Sys.Serialization.JavaScriptSerializer'); +Sys.Serialization.JavaScriptSerializer._charsToEscapeRegExs = []; +Sys.Serialization.JavaScriptSerializer._charsToEscape = []; +Sys.Serialization.JavaScriptSerializer._dateRegEx = new RegExp('(^|[^\\\\])\\"\\\\/Date\\((-?[0-9]+)(?:[a-zA-Z]|(?:\\+|-)[0-9]{4})?\\)\\\\/\\"', 'g'); +Sys.Serialization.JavaScriptSerializer._escapeChars = {}; +Sys.Serialization.JavaScriptSerializer._escapeRegEx = new RegExp('["\\\\\\x00-\\x1F]', 'i'); +Sys.Serialization.JavaScriptSerializer._escapeRegExGlobal = new RegExp('["\\\\\\x00-\\x1F]', 'g'); +Sys.Serialization.JavaScriptSerializer._jsonRegEx = new RegExp('[^,:{}\\[\\]0-9.\\-+Eaeflnr-u \\n\\r\\t]', 'g'); +Sys.Serialization.JavaScriptSerializer._jsonStringRegEx = new RegExp('"(\\\\.|[^"\\\\])*"', 'g'); +Sys.Serialization.JavaScriptSerializer._serverTypeFieldName = '__type'; +Sys.Serialization.JavaScriptSerializer._init = function Sys$Serialization$JavaScriptSerializer$_init() { + var replaceChars = ['\\u0000','\\u0001','\\u0002','\\u0003','\\u0004','\\u0005','\\u0006','\\u0007', + '\\b','\\t','\\n','\\u000b','\\f','\\r','\\u000e','\\u000f','\\u0010','\\u0011', + '\\u0012','\\u0013','\\u0014','\\u0015','\\u0016','\\u0017','\\u0018','\\u0019', + '\\u001a','\\u001b','\\u001c','\\u001d','\\u001e','\\u001f']; + Sys.Serialization.JavaScriptSerializer._charsToEscape[0] = '\\'; + Sys.Serialization.JavaScriptSerializer._charsToEscapeRegExs['\\'] = new RegExp('\\\\', 'g'); + Sys.Serialization.JavaScriptSerializer._escapeChars['\\'] = '\\\\'; + Sys.Serialization.JavaScriptSerializer._charsToEscape[1] = '"'; + Sys.Serialization.JavaScriptSerializer._charsToEscapeRegExs['"'] = new RegExp('"', 'g'); + Sys.Serialization.JavaScriptSerializer._escapeChars['"'] = '\\"'; + for (var i = 0; i < 32; i++) { + var c = String.fromCharCode(i); + Sys.Serialization.JavaScriptSerializer._charsToEscape[i+2] = c; + Sys.Serialization.JavaScriptSerializer._charsToEscapeRegExs[c] = new RegExp(c, 'g'); + Sys.Serialization.JavaScriptSerializer._escapeChars[c] = replaceChars[i]; + } +} +Sys.Serialization.JavaScriptSerializer._serializeBooleanWithBuilder = function Sys$Serialization$JavaScriptSerializer$_serializeBooleanWithBuilder(object, stringBuilder) { + stringBuilder.append(object.toString()); +} +Sys.Serialization.JavaScriptSerializer._serializeNumberWithBuilder = function Sys$Serialization$JavaScriptSerializer$_serializeNumberWithBuilder(object, stringBuilder) { + if (isFinite(object)) { + stringBuilder.append(String(object)); + } + else { + throw Error.invalidOperation(Sys.Res.cannotSerializeNonFiniteNumbers); + } +} +Sys.Serialization.JavaScriptSerializer._serializeStringWithBuilder = function Sys$Serialization$JavaScriptSerializer$_serializeStringWithBuilder(string, stringBuilder) { + stringBuilder.append('"'); + if (Sys.Serialization.JavaScriptSerializer._escapeRegEx.test(string)) { + if (Sys.Serialization.JavaScriptSerializer._charsToEscape.length === 0) { + Sys.Serialization.JavaScriptSerializer._init(); + } + if (string.length < 128) { + string = string.replace(Sys.Serialization.JavaScriptSerializer._escapeRegExGlobal, + function(x) { return Sys.Serialization.JavaScriptSerializer._escapeChars[x]; }); + } + else { + for (var i = 0; i < 34; i++) { + var c = Sys.Serialization.JavaScriptSerializer._charsToEscape[i]; + if (string.indexOf(c) !== -1) { + if (Sys.Browser.agent === Sys.Browser.Opera || Sys.Browser.agent === Sys.Browser.FireFox) { + string = string.split(c).join(Sys.Serialization.JavaScriptSerializer._escapeChars[c]); + } + else { + string = string.replace(Sys.Serialization.JavaScriptSerializer._charsToEscapeRegExs[c], + Sys.Serialization.JavaScriptSerializer._escapeChars[c]); + } + } + } + } + } + stringBuilder.append(string); + stringBuilder.append('"'); +} +Sys.Serialization.JavaScriptSerializer._serializeWithBuilder = function Sys$Serialization$JavaScriptSerializer$_serializeWithBuilder(object, stringBuilder, sort, prevObjects) { + var i; + switch (typeof object) { + case 'object': + if (object) { + if (prevObjects){ + for( var j = 0; j < prevObjects.length; j++) { + if (prevObjects[j] === object) { + throw Error.invalidOperation(Sys.Res.cannotSerializeObjectWithCycle); + } + } + } + else { + prevObjects = new Array(); + } + try { + Array.add(prevObjects, object); + + if (Number.isInstanceOfType(object)){ + Sys.Serialization.JavaScriptSerializer._serializeNumberWithBuilder(object, stringBuilder); + } + else if (Boolean.isInstanceOfType(object)){ + Sys.Serialization.JavaScriptSerializer._serializeBooleanWithBuilder(object, stringBuilder); + } + else if (String.isInstanceOfType(object)){ + Sys.Serialization.JavaScriptSerializer._serializeStringWithBuilder(object, stringBuilder); + } + + else if (Array.isInstanceOfType(object)) { + stringBuilder.append('['); + + for (i = 0; i < object.length; ++i) { + if (i > 0) { + stringBuilder.append(','); + } + Sys.Serialization.JavaScriptSerializer._serializeWithBuilder(object[i], stringBuilder,false,prevObjects); + } + stringBuilder.append(']'); + } + else { + if (Date.isInstanceOfType(object)) { + stringBuilder.append('"\\/Date('); + stringBuilder.append(object.getTime()); + stringBuilder.append(')\\/"'); + break; + } + var properties = []; + var propertyCount = 0; + for (var name in object) { + if (name.startsWith('$')) { + continue; + } + if (name === Sys.Serialization.JavaScriptSerializer._serverTypeFieldName && propertyCount !== 0){ + properties[propertyCount++] = properties[0]; + properties[0] = name; + } + else{ + properties[propertyCount++] = name; + } + } + if (sort) properties.sort(); + stringBuilder.append('{'); + var needComma = false; + + for (i=0; i + /// + /// + var e = Function._validateParams(arguments, [ + {name: "object", mayBeNull: true} + ]); + if (e) throw e; + var stringBuilder = new Sys.StringBuilder(); + Sys.Serialization.JavaScriptSerializer._serializeWithBuilder(object, stringBuilder, false); + return stringBuilder.toString(); +} +Sys.Serialization.JavaScriptSerializer.deserialize = function Sys$Serialization$JavaScriptSerializer$deserialize(data, secure) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "data", type: String}, + {name: "secure", type: Boolean, optional: true} + ]); + if (e) throw e; + + if (data.length === 0) throw Error.argument('data', Sys.Res.cannotDeserializeEmptyString); + try { + var exp = data.replace(Sys.Serialization.JavaScriptSerializer._dateRegEx, "$1new Date($2)"); + + if (secure && Sys.Serialization.JavaScriptSerializer._jsonRegEx.test( + exp.replace(Sys.Serialization.JavaScriptSerializer._jsonStringRegEx, ''))) throw null; + return eval('(' + exp + ')'); + } + catch (e) { + throw Error.argument('data', Sys.Res.cannotDeserializeInvalidJson); + } +} + +Sys.CultureInfo = function Sys$CultureInfo(name, numberFormat, dateTimeFormat) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "name", type: String}, + {name: "numberFormat", type: Object}, + {name: "dateTimeFormat", type: Object} + ]); + if (e) throw e; + this.name = name; + this.numberFormat = numberFormat; + this.dateTimeFormat = dateTimeFormat; +} + function Sys$CultureInfo$_getDateTimeFormats() { + if (! this._dateTimeFormats) { + var dtf = this.dateTimeFormat; + this._dateTimeFormats = + [ dtf.MonthDayPattern, + dtf.YearMonthPattern, + dtf.ShortDatePattern, + dtf.ShortTimePattern, + dtf.LongDatePattern, + dtf.LongTimePattern, + dtf.FullDateTimePattern, + dtf.RFC1123Pattern, + dtf.SortableDateTimePattern, + dtf.UniversalSortableDateTimePattern ]; + } + return this._dateTimeFormats; + } + function Sys$CultureInfo$_getMonthIndex(value) { + if (!this._upperMonths) { + this._upperMonths = this._toUpperArray(this.dateTimeFormat.MonthNames); + } + return Array.indexOf(this._upperMonths, this._toUpper(value)); + } + function Sys$CultureInfo$_getAbbrMonthIndex(value) { + if (!this._upperAbbrMonths) { + this._upperAbbrMonths = this._toUpperArray(this.dateTimeFormat.AbbreviatedMonthNames); + } + return Array.indexOf(this._upperAbbrMonths, this._toUpper(value)); + } + function Sys$CultureInfo$_getDayIndex(value) { + if (!this._upperDays) { + this._upperDays = this._toUpperArray(this.dateTimeFormat.DayNames); + } + return Array.indexOf(this._upperDays, this._toUpper(value)); + } + function Sys$CultureInfo$_getAbbrDayIndex(value) { + if (!this._upperAbbrDays) { + this._upperAbbrDays = this._toUpperArray(this.dateTimeFormat.AbbreviatedDayNames); + } + return Array.indexOf(this._upperAbbrDays, this._toUpper(value)); + } + function Sys$CultureInfo$_toUpperArray(arr) { + var result = []; + for (var i = 0, il = arr.length; i < il; i++) { + result[i] = this._toUpper(arr[i]); + } + return result; + } + function Sys$CultureInfo$_toUpper(value) { + return value.split("\u00A0").join(' ').toUpperCase(); + } +Sys.CultureInfo.prototype = { + _getDateTimeFormats: Sys$CultureInfo$_getDateTimeFormats, + _getMonthIndex: Sys$CultureInfo$_getMonthIndex, + _getAbbrMonthIndex: Sys$CultureInfo$_getAbbrMonthIndex, + _getDayIndex: Sys$CultureInfo$_getDayIndex, + _getAbbrDayIndex: Sys$CultureInfo$_getAbbrDayIndex, + _toUpperArray: Sys$CultureInfo$_toUpperArray, + _toUpper: Sys$CultureInfo$_toUpper +} +Sys.CultureInfo._parse = function Sys$CultureInfo$_parse(value) { + var cultureInfo = Sys.Serialization.JavaScriptSerializer.deserialize(value); + return new Sys.CultureInfo(cultureInfo.name, cultureInfo.numberFormat, cultureInfo.dateTimeFormat); +} +Sys.CultureInfo.registerClass('Sys.CultureInfo'); +Sys.CultureInfo.InvariantCulture = Sys.CultureInfo._parse('{"name":"","numberFormat":{"CurrencyDecimalDigits":2,"CurrencyDecimalSeparator":".","IsReadOnly":true,"CurrencyGroupSizes":[3],"NumberGroupSizes":[3],"PercentGroupSizes":[3],"CurrencyGroupSeparator":",","CurrencySymbol":"\u00A4","NaNSymbol":"NaN","CurrencyNegativePattern":0,"NumberNegativePattern":1,"PercentPositivePattern":0,"PercentNegativePattern":0,"NegativeInfinitySymbol":"-Infinity","NegativeSign":"-","NumberDecimalDigits":2,"NumberDecimalSeparator":".","NumberGroupSeparator":",","CurrencyPositivePattern":0,"PositiveInfinitySymbol":"Infinity","PositiveSign":"+","PercentDecimalDigits":2,"PercentDecimalSeparator":".","PercentGroupSeparator":",","PercentSymbol":"%","PerMilleSymbol":"\u2030","NativeDigits":["0","1","2","3","4","5","6","7","8","9"],"DigitSubstitution":1},"dateTimeFormat":{"AMDesignator":"AM","Calendar":{"MinSupportedDateTime":"@-62135568000000@","MaxSupportedDateTime":"@253402300799999@","AlgorithmType":1,"CalendarType":1,"Eras":[1],"TwoDigitYearMax":2029,"IsReadOnly":true},"DateSeparator":"/","FirstDayOfWeek":0,"CalendarWeekRule":0,"FullDateTimePattern":"dddd, dd MMMM yyyy HH:mm:ss","LongDatePattern":"dddd, dd MMMM yyyy","LongTimePattern":"HH:mm:ss","MonthDayPattern":"MMMM dd","PMDesignator":"PM","RFC1123Pattern":"ddd, dd MMM yyyy HH\':\'mm\':\'ss \'GMT\'","ShortDatePattern":"MM/dd/yyyy","ShortTimePattern":"HH:mm","SortableDateTimePattern":"yyyy\'-\'MM\'-\'dd\'T\'HH\':\'mm\':\'ss","TimeSeparator":":","UniversalSortableDateTimePattern":"yyyy\'-\'MM\'-\'dd HH\':\'mm\':\'ss\'Z\'","YearMonthPattern":"yyyy MMMM","AbbreviatedDayNames":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"ShortestDayNames":["Su","Mo","Tu","We","Th","Fr","Sa"],"DayNames":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"AbbreviatedMonthNames":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""],"MonthNames":["January","February","March","April","May","June","July","August","September","October","November","December",""],"IsReadOnly":true,"NativeCalendarName":"Gregorian Calendar","AbbreviatedMonthGenitiveNames":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""],"MonthGenitiveNames":["January","February","March","April","May","June","July","August","September","October","November","December",""]}}'); +if (typeof(__cultureInfo) === 'undefined') { + var __cultureInfo = '{"name":"en-US","numberFormat":{"CurrencyDecimalDigits":2,"CurrencyDecimalSeparator":".","IsReadOnly":false,"CurrencyGroupSizes":[3],"NumberGroupSizes":[3],"PercentGroupSizes":[3],"CurrencyGroupSeparator":",","CurrencySymbol":"$","NaNSymbol":"NaN","CurrencyNegativePattern":0,"NumberNegativePattern":1,"PercentPositivePattern":0,"PercentNegativePattern":0,"NegativeInfinitySymbol":"-Infinity","NegativeSign":"-","NumberDecimalDigits":2,"NumberDecimalSeparator":".","NumberGroupSeparator":",","CurrencyPositivePattern":0,"PositiveInfinitySymbol":"Infinity","PositiveSign":"+","PercentDecimalDigits":2,"PercentDecimalSeparator":".","PercentGroupSeparator":",","PercentSymbol":"%","PerMilleSymbol":"\u2030","NativeDigits":["0","1","2","3","4","5","6","7","8","9"],"DigitSubstitution":1},"dateTimeFormat":{"AMDesignator":"AM","Calendar":{"MinSupportedDateTime":"@-62135568000000@","MaxSupportedDateTime":"@253402300799999@","AlgorithmType":1,"CalendarType":1,"Eras":[1],"TwoDigitYearMax":2029,"IsReadOnly":false},"DateSeparator":"/","FirstDayOfWeek":0,"CalendarWeekRule":0,"FullDateTimePattern":"dddd, MMMM dd, yyyy h:mm:ss tt","LongDatePattern":"dddd, MMMM dd, yyyy","LongTimePattern":"h:mm:ss tt","MonthDayPattern":"MMMM dd","PMDesignator":"PM","RFC1123Pattern":"ddd, dd MMM yyyy HH\':\'mm\':\'ss \'GMT\'","ShortDatePattern":"M/d/yyyy","ShortTimePattern":"h:mm tt","SortableDateTimePattern":"yyyy\'-\'MM\'-\'dd\'T\'HH\':\'mm\':\'ss","TimeSeparator":":","UniversalSortableDateTimePattern":"yyyy\'-\'MM\'-\'dd HH\':\'mm\':\'ss\'Z\'","YearMonthPattern":"MMMM, yyyy","AbbreviatedDayNames":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"ShortestDayNames":["Su","Mo","Tu","We","Th","Fr","Sa"],"DayNames":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"AbbreviatedMonthNames":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""],"MonthNames":["January","February","March","April","May","June","July","August","September","October","November","December",""],"IsReadOnly":false,"NativeCalendarName":"Gregorian Calendar","AbbreviatedMonthGenitiveNames":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""],"MonthGenitiveNames":["January","February","March","April","May","June","July","August","September","October","November","December",""]}}'; +} +Sys.CultureInfo.CurrentCulture = Sys.CultureInfo._parse(__cultureInfo); +delete __cultureInfo; + +Sys.UI.Behavior = function Sys$UI$Behavior(element) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true} + ]); + if (e) throw e; + Sys.UI.Behavior.initializeBase(this); + this._element = element; + var behaviors = element._behaviors; + if (!behaviors) { + element._behaviors = [this]; + } + else { + behaviors[behaviors.length] = this; + } +} + function Sys$UI$Behavior$get_element() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._element; + } + function Sys$UI$Behavior$get_id() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + var baseId = Sys.UI.Behavior.callBaseMethod(this, 'get_id'); + if (baseId) return baseId; + if (!this._element || !this._element.id) return ''; + return this._element.id + '$' + this.get_name(); + } + function Sys$UI$Behavior$get_name() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (this._name) return this._name; + var name = Object.getTypeName(this); + var i = name.lastIndexOf('.'); + if (i != -1) name = name.substr(i + 1); + if (!this.get_isInitialized()) this._name = name; + return name; + } + function Sys$UI$Behavior$set_name(value) { + var e = Function._validateParams(arguments, [{name: "value", type: String}]); + if (e) throw e; + if ((value === '') || (value.charAt(0) === ' ') || (value.charAt(value.length - 1) === ' ')) + throw Error.argument('value', Sys.Res.invalidId); + if (typeof(this._element[value]) !== 'undefined') + throw Error.invalidOperation(String.format(Sys.Res.behaviorDuplicateName, value)); + if (this.get_isInitialized()) throw Error.invalidOperation(Sys.Res.cantSetNameAfterInit); + this._name = value; + } + function Sys$UI$Behavior$initialize() { + Sys.UI.Behavior.callBaseMethod(this, 'initialize'); + var name = this.get_name(); + if (name) this._element[name] = this; + } + function Sys$UI$Behavior$dispose() { + Sys.UI.Behavior.callBaseMethod(this, 'dispose'); + if (this._element) { + var name = this.get_name(); + if (name) { + this._element[name] = null; + } + Array.remove(this._element._behaviors, this); + delete this._element; + } + } +Sys.UI.Behavior.prototype = { + _name: null, + get_element: Sys$UI$Behavior$get_element, + get_id: Sys$UI$Behavior$get_id, + get_name: Sys$UI$Behavior$get_name, + set_name: Sys$UI$Behavior$set_name, + initialize: Sys$UI$Behavior$initialize, + dispose: Sys$UI$Behavior$dispose +} +Sys.UI.Behavior.registerClass('Sys.UI.Behavior', Sys.Component); +Sys.UI.Behavior.getBehaviorByName = function Sys$UI$Behavior$getBehaviorByName(element, name) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true}, + {name: "name", type: String} + ]); + if (e) throw e; + var b = element[name]; + return (b && Sys.UI.Behavior.isInstanceOfType(b)) ? b : null; +} +Sys.UI.Behavior.getBehaviors = function Sys$UI$Behavior$getBehaviors(element) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true} + ]); + if (e) throw e; + if (!element._behaviors) return []; + return Array.clone(element._behaviors); +} +Sys.UI.Behavior.getBehaviorsByType = function Sys$UI$Behavior$getBehaviorsByType(element, type) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true}, + {name: "type", type: Type} + ]); + if (e) throw e; + var behaviors = element._behaviors; + var results = []; + if (behaviors) { + for (var i = 0, l = behaviors.length; i < l; i++) { + if (type.isInstanceOfType(behaviors[i])) { + results[results.length] = behaviors[i]; + } + } + } + return results; +} + +Sys.UI.VisibilityMode = function Sys$UI$VisibilityMode() { + /// + /// + /// + if (arguments.length !== 0) throw Error.parameterCount(); + throw Error.notImplemented(); +} +Sys.UI.VisibilityMode.prototype = { + hide: 0, + collapse: 1 +} +Sys.UI.VisibilityMode.registerEnum("Sys.UI.VisibilityMode"); + +Sys.UI.Control = function Sys$UI$Control(element) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "element", domElement: true} + ]); + if (e) throw e; + if (typeof(element.control) != 'undefined') throw Error.invalidOperation(Sys.Res.controlAlreadyDefined); + Sys.UI.Control.initializeBase(this); + this._element = element; + element.control = this; +} + function Sys$UI$Control$get_element() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + return this._element; + } + function Sys$UI$Control$get_id() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (!this._element) return ''; + return this._element.id; + } + function Sys$UI$Control$set_id(value) { + var e = Function._validateParams(arguments, [{name: "value", type: String}]); + if (e) throw e; + throw Error.invalidOperation(Sys.Res.cantSetId); + } + function Sys$UI$Control$get_parent() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (this._parent) return this._parent; + if (!this._element) return null; + + var parentElement = this._element.parentNode; + while (parentElement) { + if (parentElement.control) { + return parentElement.control; + } + parentElement = parentElement.parentNode; + } + return null; + } + function Sys$UI$Control$set_parent(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Sys.UI.Control}]); + if (e) throw e; + if (!this._element) throw Error.invalidOperation(Sys.Res.cantBeCalledAfterDispose); + var parents = [this]; + var current = value; + while (current) { + if (Array.contains(parents, current)) throw Error.invalidOperation(Sys.Res.circularParentChain); + parents[parents.length] = current; + current = current.get_parent(); + } + this._parent = value; + } + function Sys$UI$Control$get_visibilityMode() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (!this._element) throw Error.invalidOperation(Sys.Res.cantBeCalledAfterDispose); + return Sys.UI.DomElement.getVisibilityMode(this._element); + } + function Sys$UI$Control$set_visibilityMode(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Sys.UI.VisibilityMode}]); + if (e) throw e; + if (!this._element) throw Error.invalidOperation(Sys.Res.cantBeCalledAfterDispose); + Sys.UI.DomElement.setVisibilityMode(this._element, value); + } + function Sys$UI$Control$get_visible() { + /// + if (arguments.length !== 0) throw Error.parameterCount(); + if (!this._element) throw Error.invalidOperation(Sys.Res.cantBeCalledAfterDispose); + return Sys.UI.DomElement.getVisible(this._element); + } + function Sys$UI$Control$set_visible(value) { + var e = Function._validateParams(arguments, [{name: "value", type: Boolean}]); + if (e) throw e; + if (!this._element) throw Error.invalidOperation(Sys.Res.cantBeCalledAfterDispose); + Sys.UI.DomElement.setVisible(this._element, value) + } + function Sys$UI$Control$addCssClass(className) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "className", type: String} + ]); + if (e) throw e; + if (!this._element) throw Error.invalidOperation(Sys.Res.cantBeCalledAfterDispose); + Sys.UI.DomElement.addCssClass(this._element, className); + } + function Sys$UI$Control$dispose() { + Sys.UI.Control.callBaseMethod(this, 'dispose'); + if (this._element) { + this._element.control = undefined; + delete this._element; + } + if (this._parent) delete this._parent; + } + function Sys$UI$Control$onBubbleEvent(source, args) { + /// + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "source"}, + {name: "args", type: Sys.EventArgs} + ]); + if (e) throw e; + return false; + } + function Sys$UI$Control$raiseBubbleEvent(source, args) { + /// + /// + /// + var e = Function._validateParams(arguments, [ + {name: "source"}, + {name: "args", type: Sys.EventArgs} + ]); + if (e) throw e; + var currentTarget = this.get_parent(); + while (currentTarget) { + if (currentTarget.onBubbleEvent(source, args)) { + return; + } + currentTarget = currentTarget.get_parent(); + } + } + function Sys$UI$Control$removeCssClass(className) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "className", type: String} + ]); + if (e) throw e; + if (!this._element) throw Error.invalidOperation(Sys.Res.cantBeCalledAfterDispose); + Sys.UI.DomElement.removeCssClass(this._element, className); + } + function Sys$UI$Control$toggleCssClass(className) { + /// + /// + var e = Function._validateParams(arguments, [ + {name: "className", type: String} + ]); + if (e) throw e; + if (!this._element) throw Error.invalidOperation(Sys.Res.cantBeCalledAfterDispose); + Sys.UI.DomElement.toggleCssClass(this._element, className); + } +Sys.UI.Control.prototype = { + _parent: null, + _visibilityMode: Sys.UI.VisibilityMode.hide, + get_element: Sys$UI$Control$get_element, + get_id: Sys$UI$Control$get_id, + set_id: Sys$UI$Control$set_id, + get_parent: Sys$UI$Control$get_parent, + set_parent: Sys$UI$Control$set_parent, + get_visibilityMode: Sys$UI$Control$get_visibilityMode, + set_visibilityMode: Sys$UI$Control$set_visibilityMode, + get_visible: Sys$UI$Control$get_visible, + set_visible: Sys$UI$Control$set_visible, + addCssClass: Sys$UI$Control$addCssClass, + dispose: Sys$UI$Control$dispose, + onBubbleEvent: Sys$UI$Control$onBubbleEvent, + raiseBubbleEvent: Sys$UI$Control$raiseBubbleEvent, + removeCssClass: Sys$UI$Control$removeCssClass, + toggleCssClass: Sys$UI$Control$toggleCssClass +} +Sys.UI.Control.registerClass('Sys.UI.Control', Sys.Component); + + +Type.registerNamespace('Sys'); + +Sys.Res={ +'urlMustBeLessThan1024chars':'The history state must be small enough to not make the url larger than 1024 characters.', +'argumentTypeName':'Value is not the name of an existing type.', +'methodRegisteredTwice':'Method {0} has already been registered.', +'cantSetIdAfterInit':'The id property can\'t be set on this object after initialization.', +'cantBeCalledAfterDispose':'Can\'t be called after dispose.', +'componentCantSetIdAfterAddedToApp':'The id property of a component can\'t be set after it\'s been added to the Application object.', +'behaviorDuplicateName':'A behavior with name \'{0}\' already exists or it is the name of an existing property on the target element.', +'notATypeName':'Value is not a valid type name.', +'typeShouldBeTypeOrString':'Value is not a valid type or a valid type name.', +'historyInvalidHistorySettingCombination':'Cannot set enableHistory to false when ScriptManager.EnableHistory is true.', +'stateMustBeStringDictionary':'The state object can only have null and string fields.', +'boolTrueOrFalse':'Value must be \'true\' or \'false\'.', +'scriptLoadFailedNoHead':'ScriptLoader requires pages to contain a element.', +'stringFormatInvalid':'The format string is invalid.', +'referenceNotFound':'Component \'{0}\' was not found.', +'enumReservedName':'\'{0}\' is a reserved name that can\'t be used as an enum value name.', +'eventHandlerNotFound':'Handler not found.', +'circularParentChain':'The chain of control parents can\'t have circular references.', +'undefinedEvent':'\'{0}\' is not an event.', +'notAMethod':'{0} is not a method.', +'propertyUndefined':'\'{0}\' is not a property or an existing field.', +'historyCannotEnableHistory':'Cannot set enableHistory after initialization.', +'eventHandlerInvalid':'Handler was not added through the Sys.UI.DomEvent.addHandler method.', +'scriptLoadFailedDebug':'The script \'{0}\' failed to load. Check for:\r\n Inaccessible path.\r\n Script errors. (IE) Enable \'Display a notification about every script error\' under advanced settings.\r\n Missing call to Sys.Application.notifyScriptLoaded().', +'propertyNotWritable':'\'{0}\' is not a writable property.', +'enumInvalidValueName':'\'{0}\' is not a valid name for an enum value.', +'controlAlreadyDefined':'A control is already associated with the element.', +'addHandlerCantBeUsedForError':'Can\'t add a handler for the error event using this method. Please set the window.onerror property instead.', +'namespaceContainsObject':'Object {0} already exists and is not a namespace.', +'cantAddNonFunctionhandler':'Can\'t add a handler that is not a function.', +'invalidNameSpace':'Value is not a valid namespace identifier.', +'notAnInterface':'Value is not a valid interface.', +'eventHandlerNotFunction':'Handler must be a function.', +'propertyNotAnArray':'\'{0}\' is not an Array property.', +'typeRegisteredTwice':'Type {0} has already been registered. The type may be defined multiple times or the script file that defines it may have already been loaded. A possible cause is a change of settings during a partial update.', +'cantSetNameAfterInit':'The name property can\'t be set on this object after initialization.', +'historyMissingFrame':'For the history feature to work in IE, the page must have an iFrame element with id \'__historyFrame\' pointed to a page that gets its title from the \'title\' query string parameter and calls Sys.Application._onIFrameLoad() on the parent window. This can be done by setting EnableHistory to true on ScriptManager.', +'appDuplicateComponent':'Two components with the same id \'{0}\' can\'t be added to the application.', +'historyCannotAddHistoryPointWithHistoryDisabled':'A history point can only be added if enableHistory is set to true.', +'appComponentMustBeInitialized':'Components must be initialized before they are added to the Application object.', +'baseNotAClass':'Value is not a class.', +'methodNotFound':'No method found with name \'{0}\'.', +'arrayParseBadFormat':'Value must be a valid string representation for an array. It must start with a \'[\' and end with a \']\'.', +'stateFieldNameInvalid':'State field names must not contain any \'=\' characters.', +'cantSetId':'The id property can\'t be set on this object.', +'historyMissingHiddenInput':'For the history feature to work in Safari 2, the page must have a hidden input element with id \'__history\'.', +'stringFormatBraceMismatch':'The format string contains an unmatched opening or closing brace.', +'enumValueNotInteger':'An enumeration definition can only contain integer values.', +'propertyNullOrUndefined':'Cannot set the properties of \'{0}\' because it returned a null value.', +'argumentDomNode':'Value must be a DOM element or a text node.', +'componentCantSetIdTwice':'The id property of a component can\'t be set more than once.', +'createComponentOnDom':'Value must be null for Components that are not Controls or Behaviors.', +'createNotComponent':'{0} does not derive from Sys.Component.', +'createNoDom':'Value must not be null for Controls and Behaviors.', +'cantAddWithoutId':'Can\'t add a component that doesn\'t have an id.', +'badTypeName':'Value is not the name of the type being registered or the name is a reserved word.', +'argumentInteger':'Value must be an integer.', +'scriptLoadMultipleCallbacks':'The script \'{0}\' contains multiple calls to Sys.Application.notifyScriptLoaded(). Only one is allowed.', +'invokeCalledTwice':'Cannot call invoke more than once.', +'webServiceFailed':'The server method \'{0}\' failed with the following error: {1}', +'webServiceInvalidJsonWrapper':'The server method \'{0}\' returned invalid data. The \'d\' property is missing from the JSON wrapper.', +'argumentType':'Object cannot be converted to the required type.', +'argumentNull':'Value cannot be null.', +'controlCantSetId':'The id property can\'t be set on a control.', +'formatBadFormatSpecifier':'Format specifier was invalid.', +'webServiceFailedNoMsg':'The server method \'{0}\' failed.', +'argumentDomElement':'Value must be a DOM element.', +'invalidExecutorType':'Could not create a valid Sys.Net.WebRequestExecutor from: {0}.', +'cannotCallBeforeResponse':'Cannot call {0} when responseAvailable is false.', +'actualValue':'Actual value was {0}.', +'enumInvalidValue':'\'{0}\' is not a valid value for enum {1}.', +'scriptLoadFailed':'The script \'{0}\' could not be loaded.', +'parameterCount':'Parameter count mismatch.', +'cannotDeserializeEmptyString':'Cannot deserialize empty string.', +'formatInvalidString':'Input string was not in a correct format.', +'invalidTimeout':'Value must be greater than or equal to zero.', +'cannotAbortBeforeStart':'Cannot abort when executor has not started.', +'argument':'Value does not fall within the expected range.', +'cannotDeserializeInvalidJson':'Cannot deserialize. The data does not correspond to valid JSON.', +'invalidHttpVerb':'httpVerb cannot be set to an empty or null string.', +'nullWebRequest':'Cannot call executeRequest with a null webRequest.', +'eventHandlerInvalid':'Handler was not added through the Sys.UI.DomEvent.addHandler method.', +'cannotSerializeNonFiniteNumbers':'Cannot serialize non finite numbers.', +'argumentUndefined':'Value cannot be undefined.', +'webServiceInvalidReturnType':'The server method \'{0}\' returned an invalid type. Expected type: {1}', +'servicePathNotSet':'The path to the web service has not been set.', +'argumentTypeWithTypes':'Object of type \'{0}\' cannot be converted to type \'{1}\'.', +'cannotCallOnceStarted':'Cannot call {0} once started.', +'badBaseUrl1':'Base URL does not contain ://.', +'badBaseUrl2':'Base URL does not contain another /.', +'badBaseUrl3':'Cannot find last / in base URL.', +'setExecutorAfterActive':'Cannot set executor after it has become active.', +'paramName':'Parameter name: {0}', +'cannotCallOutsideHandler':'Cannot call {0} outside of a completed event handler.', +'cannotSerializeObjectWithCycle':'Cannot serialize object with cyclic reference within child properties.', +'format':'One of the identified items was in an invalid format.', +'assertFailedCaller':'Assertion Failed: {0}\r\nat {1}', +'argumentOutOfRange':'Specified argument was out of the range of valid values.', +'webServiceTimedOut':'The server method \'{0}\' timed out.', +'notImplemented':'The method or operation is not implemented.', +'assertFailed':'Assertion Failed: {0}', +'invalidOperation':'Operation is not valid due to the current state of the object.', +'breakIntoDebugger':'{0}\r\n\r\nBreak into debugger?' +}; + +if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded(); diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftAjax.js b/aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftAjax.js new file mode 100644 index 0000000..646f633 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftAjax.js @@ -0,0 +1,7 @@ +//---------------------------------------------------------- +// Copyright (C) Microsoft Corporation. All rights reserved. +//---------------------------------------------------------- +// MicrosoftAjax.js +Function.__typeName="Function";Function.__class=true;Function.createCallback=function(b,a){return function(){var e=arguments.length;if(e>0){var d=[];for(var c=0;cc){var f=Error.parameterCount();f.popStackFrame();return f}return null};Function._validateParameter=function(c,a,h){var b,g=a.type,l=!!a.integer,k=!!a.domElement,m=!!a.mayBeNull;b=Function._validateParameterType(c,g,l,k,m,h);if(b){b.popStackFrame();return b}var e=a.elementType,f=!!a.elementMayBeNull;if(g===Array&&typeof c!=="undefined"&&c!==null&&(e||!f)){var j=!!a.elementInteger,i=!!a.elementDomElement;for(var d=0;d0&&(dc.Calendar.TwoDigitYearMax)return a-100}return a};Date._getParseRegExp=function(b,e){if(!b._parseRegExp)b._parseRegExp={};else if(b._parseRegExp[e])return b._parseRegExp[e];var c=Date._expandFormat(b,e);c=c.replace(/([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g,"\\\\$1");var a=new Sys.StringBuilder("^"),j=[],f=0,i=0,h=Date._getTokenRegExp(),d;while((d=h.exec(c))!==null){var l=c.slice(f,d.index);f=h.lastIndex;i+=Date._appendPreOrPostMatch(l,a);if(i%2===1){a.append(d[0]);continue}switch(d[0]){case "dddd":case "ddd":case "MMMM":case "MMM":a.append("(\\D+)");break;case "tt":case "t":a.append("(\\D*)");break;case "yyyy":a.append("(\\d{4})");break;case "fff":a.append("(\\d{3})");break;case "ff":a.append("(\\d{2})");break;case "f":a.append("(\\d)");break;case "dd":case "d":case "MM":case "M":case "yy":case "y":case "HH":case "H":case "hh":case "h":case "mm":case "m":case "ss":case "s":a.append("(\\d\\d?)");break;case "zzz":a.append("([+-]?\\d\\d?:\\d{2})");break;case "zz":case "z":a.append("([+-]?\\d\\d?)")}Array.add(j,d[0])}Date._appendPreOrPostMatch(c.slice(f),a);a.append("$");var k=a.toString().replace(/\s+/g,"\\s+"),g={"regExp":k,"groups":j};b._parseRegExp[e]=g;return g};Date._getTokenRegExp=function(){return /dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z/g};Date.parseLocale=function(a){return Date._parse(a,Sys.CultureInfo.CurrentCulture,arguments)};Date.parseInvariant=function(a){return Date._parse(a,Sys.CultureInfo.InvariantCulture,arguments)};Date._parse=function(g,c,h){var e=false;for(var a=1,i=h.length;a31)return null;break;case "MMMM":c=j._getMonthIndex(a);if(c<0||c>11)return null;break;case "MMM":c=j._getAbbrMonthIndex(a);if(c<0||c>11)return null;break;case "M":case "MM":var c=parseInt(a,10)-1;if(c<0||c>11)return null;break;case "y":case "yy":f=Date._expandYear(m,parseInt(a,10));if(f<0||f>9999)return null;break;case "yyyy":f=parseInt(a,10);if(f<0||f>9999)return null;break;case "h":case "hh":d=parseInt(a,10);if(d===12)d=0;if(d<0||d>11)return null;break;case "H":case "HH":d=parseInt(a,10);if(d<0||d>23)return null;break;case "m":case "mm":n=parseInt(a,10);if(n<0||n>59)return null;break;case "s":case "ss":o=parseInt(a,10);if(o<0||o>59)return null;break;case "tt":case "t":var u=a.toUpperCase();r=u===m.PMDesignator.toUpperCase();if(!r&&u!==m.AMDesignator.toUpperCase())return null;break;case "f":e=parseInt(a,10)*100;if(e<0||e>999)return null;break;case "ff":e=parseInt(a,10)*10;if(e<0||e>999)return null;break;case "fff":e=parseInt(a,10);if(e<0||e>999)return null;break;case "dddd":g=j._getDayIndex(a);if(g<0||g>6)return null;break;case "ddd":g=j._getAbbrDayIndex(a);if(g<0||g>6)return null;break;case "zzz":var q=a.split(/:/);if(q.length!==2)return null;var i=parseInt(q[0],10);if(i<-12||i>13)return null;var l=parseInt(q[1],10);if(l<0||l>59)return null;k=i*60+(a.startsWith("-")?-l:l);break;case "z":case "zz":var i=parseInt(a,10);if(i<-12||i>13)return null;k=i*60}}var b=new Date;if(f===null)f=b.getFullYear();if(c===null)c=b.getMonth();if(h===null)h=b.getDate();b.setFullYear(f,c,h);if(b.getDate()!==h)return null;if(g!==null&&b.getDay()!==g)return null;if(r&&d<12)d+=12;b.setHours(d,n,o,e);if(k!==null){var t=b.getMinutes()-(k+b.getTimezoneOffset());b.setHours(b.getHours()+parseInt(t/60,10),t%60)}return b};Date.prototype.format=function(a){return this._toFormattedString(a,Sys.CultureInfo.InvariantCulture)};Date.prototype.localeFormat=function(a){return this._toFormattedString(a,Sys.CultureInfo.CurrentCulture)};Date.prototype._toFormattedString=function(e,h){if(!e||e.length===0||e==="i")if(h&&h.name.length>0)return this.toLocaleString();else return this.toString();var d=h.dateTimeFormat;e=Date._expandFormat(d,e);var a=new Sys.StringBuilder,b;function c(a){if(a<10)return "0"+a;return a.toString()}function g(a){if(a<10)return "00"+a;if(a<100)return "0"+a;return a.toString()}var j=0,i=Date._getTokenRegExp();for(;true;){var l=i.lastIndex,f=i.exec(e),k=e.slice(l,f?f.index:e.length);j+=Date._appendPreOrPostMatch(k,a);if(!f)break;if(j%2===1){a.append(f[0]);continue}switch(f[0]){case "dddd":a.append(d.DayNames[this.getDay()]);break;case "ddd":a.append(d.AbbreviatedDayNames[this.getDay()]);break;case "dd":a.append(c(this.getDate()));break;case "d":a.append(this.getDate());break;case "MMMM":a.append(d.MonthNames[this.getMonth()]);break;case "MMM":a.append(d.AbbreviatedMonthNames[this.getMonth()]);break;case "MM":a.append(c(this.getMonth()+1));break;case "M":a.append(this.getMonth()+1);break;case "yyyy":a.append(this.getFullYear());break;case "yy":a.append(c(this.getFullYear()%100));break;case "y":a.append(this.getFullYear()%100);break;case "hh":b=this.getHours()%12;if(b===0)b=12;a.append(c(b));break;case "h":b=this.getHours()%12;if(b===0)b=12;a.append(b);break;case "HH":a.append(c(this.getHours()));break;case "H":a.append(this.getHours());break;case "mm":a.append(c(this.getMinutes()));break;case "m":a.append(this.getMinutes());break;case "ss":a.append(c(this.getSeconds()));break;case "s":a.append(this.getSeconds());break;case "tt":a.append(this.getHours()<12?d.AMDesignator:d.PMDesignator);break;case "t":a.append((this.getHours()<12?d.AMDesignator:d.PMDesignator).charAt(0));break;case "f":a.append(g(this.getMilliseconds()).charAt(0));break;case "ff":a.append(g(this.getMilliseconds()).substr(0,2));break;case "fff":a.append(g(this.getMilliseconds()));break;case "z":b=this.getTimezoneOffset()/60;a.append((b<=0?"+":"-")+Math.floor(Math.abs(b)));break;case "zz":b=this.getTimezoneOffset()/60;a.append((b<=0?"+":"-")+c(Math.floor(Math.abs(b))));break;case "zzz":b=this.getTimezoneOffset()/60;a.append((b<=0?"+":"-")+c(Math.floor(Math.abs(b)))+d.TimeSeparator+c(Math.abs(this.getTimezoneOffset()%60)))}}return a.toString()};Number.__typeName="Number";Number.__class=true;Number.parseLocale=function(a){return Number._parse(a,Sys.CultureInfo.CurrentCulture)};Number.parseInvariant=function(a){return Number._parse(a,Sys.CultureInfo.InvariantCulture)};Number._parse=function(b,o){b=b.trim();if(b.match(/^[+-]?infinity$/i))return parseFloat(b);if(b.match(/^0x[a-f0-9]+$/i))return parseInt(b);var a=o.numberFormat,g=Number._parseNumberNegativePattern(b,a,a.NumberNegativePattern),h=g[0],e=g[1];if(h===""&&a.NumberNegativePattern!==1){g=Number._parseNumberNegativePattern(b,a,1);h=g[0];e=g[1]}if(h==="")h="+";var j,d,f=e.indexOf("e");if(f<0)f=e.indexOf("E");if(f<0){d=e;j=null}else{d=e.substr(0,f);j=e.substr(f+1)}var c,k,m=d.indexOf(a.NumberDecimalSeparator);if(m<0){c=d;k=null}else{c=d.substr(0,m);k=d.substr(m+a.NumberDecimalSeparator.length)}c=c.split(a.NumberGroupSeparator).join("");var n=a.NumberGroupSeparator.replace(/\u00A0/g," ");if(a.NumberGroupSeparator!==n)c=c.split(n).join("");var l=h+c;if(k!==null)l+="."+k;if(j!==null){var i=Number._parseNumberNegativePattern(j,a,1);if(i[0]==="")i[0]="+";l+="e"+i[0]+i[1]}if(l.match(/^[+-]?\d*\.?\d*(e[+-]?\d+)?$/))return parseFloat(l);return Number.NaN};Number._parseNumberNegativePattern=function(a,d,e){var b=d.NegativeSign,c=d.PositiveSign;switch(e){case 4:b=" "+b;c=" "+c;case 3:if(a.endsWith(b))return ["-",a.substr(0,a.length-b.length)];else if(a.endsWith(c))return ["+",a.substr(0,a.length-c.length)];break;case 2:b+=" ";c+=" ";case 1:if(a.startsWith(b))return ["-",a.substr(b.length)];else if(a.startsWith(c))return ["+",a.substr(c.length)];break;case 0:if(a.startsWith("(")&&a.endsWith(")"))return ["-",a.substr(1,a.length-2)]}return ["",a]};Number.prototype.format=function(a){return this._toFormattedString(a,Sys.CultureInfo.InvariantCulture)};Number.prototype.localeFormat=function(a){return this._toFormattedString(a,Sys.CultureInfo.CurrentCulture)};Number.prototype._toFormattedString=function(d,j){if(!d||d.length===0||d==="i")if(j&&j.name.length>0)return this.toLocaleString();else return this.toString();var o=["n %","n%","%n"],n=["-n %","-n%","-%n"],p=["(n)","-n","- n","n-","n -"],m=["$n","n$","$ n","n $"],l=["($n)","-$n","$-n","$n-","(n$)","-n$","n-$","n$-","-n $","-$ n","n $-","$ n-","$ -n","n- $","($ n)","(n $)"];function g(a,c,d){for(var b=a.length;b1?parseInt(e[1]):0;e=b.split(".");b=e[0];a=e.length>1?e[1]:"";var q;if(c>0){a=g(a,c,false);b+=a.slice(0,c);a=a.substr(c)}else if(c<0){c=-c;b=g(b,c+1,true);a=b.slice(-c,b.length)+a;b=b.slice(0,-c)}if(i>0){if(a.length>i)a=a.slice(0,i);else a=g(a,i,false);a=p+a}else a="";var d=b.length-1,f="";while(d>=0){if(h===0||h>d)if(f.length>0)return b.slice(0,d+1)+n+f+a;else return b.slice(0,d+1)+a;if(f.length>0)f=b.slice(d-h+1,d+1)+n+f;else f=b.slice(d-h+1,d+1);d-=h;if(k1)b=parseInt(d.slice(1),10);var c;switch(d.charAt(0)){case "d":case "D":c="n";if(b!==-1)e=g(""+e,b,true);if(this<0)e=-e;break;case "c":case "C":if(this<0)c=l[a.CurrencyNegativePattern];else c=m[a.CurrencyPositivePattern];if(b===-1)b=a.CurrencyDecimalDigits;e=i(Math.abs(this),b,a.CurrencyGroupSizes,a.CurrencyGroupSeparator,a.CurrencyDecimalSeparator);break;case "n":case "N":if(this<0)c=p[a.NumberNegativePattern];else c="n";if(b===-1)b=a.NumberDecimalDigits;e=i(Math.abs(this),b,a.NumberGroupSizes,a.NumberGroupSeparator,a.NumberDecimalSeparator);break;case "p":case "P":if(this<0)c=n[a.PercentNegativePattern];else c=o[a.PercentPositivePattern];if(b===-1)b=a.PercentDecimalDigits;e=i(Math.abs(this)*100,b,a.PercentGroupSizes,a.PercentGroupSeparator,a.PercentDecimalSeparator);break;default:throw Error.format(Sys.Res.formatBadFormatSpecifier)}var k=/n|\$|-|%/g,f="";for(;true;){var q=k.lastIndex,h=k.exec(c);f+=c.slice(q,h?h.index:c.length);if(!h)break;switch(h[0]){case "n":f+=e;break;case "$":f+=a.CurrencySymbol;break;case "-":f+=a.NegativeSign;break;case "%":f+=a.PercentSymbol}}return f};RegExp.__typeName="RegExp";RegExp.__class=true;Array.__typeName="Array";Array.__class=true;Array.add=Array.enqueue=function(a,b){a[a.length]=b};Array.addRange=function(a,b){a.push.apply(a,b)};Array.clear=function(a){a.length=0};Array.clone=function(a){if(a.length===1)return [a[0]];else return Array.apply(null,a)};Array.contains=function(a,b){return Array.indexOf(a,b)>=0};Array.dequeue=function(a){return a.shift()};Array.forEach=function(b,e,d){for(var a=0,f=b.length;a=0)b.splice(a,1);return a>=0};Array.removeAt=function(a,b){a.splice(b,1)};if(!window)this.window=this;window.Type=Function;Type.prototype.callBaseMethod=function(a,d,b){var c=this.getBaseMethod(a,d);if(!b)return c.apply(a);else return c.apply(a,b)};Type.prototype.getBaseMethod=function(d,c){var b=this.getBaseType();if(b){var a=b.prototype[c];return a instanceof Function?a:null}return null};Type.prototype.getBaseType=function(){return typeof this.__baseType==="undefined"?null:this.__baseType};Type.prototype.getInterfaces=function(){var a=[],b=this;while(b){var c=b.__interfaces;if(c)for(var d=0,f=c.length;d-1){Sys.Browser.agent=Sys.Browser.InternetExplorer;Sys.Browser.version=parseFloat(navigator.userAgent.match(/MSIE (\d+\.\d+)/)[1]);if(Sys.Browser.version>=8)if(document.documentMode>=7)Sys.Browser.documentMode=document.documentMode;Sys.Browser.hasDebuggerStatement=true}else if(navigator.userAgent.indexOf(" Firefox/")>-1){Sys.Browser.agent=Sys.Browser.Firefox;Sys.Browser.version=parseFloat(navigator.userAgent.match(/Firefox\/(\d+\.\d+)/)[1]);Sys.Browser.name="Firefox";Sys.Browser.hasDebuggerStatement=true}else if(navigator.userAgent.indexOf(" AppleWebKit/")>-1){Sys.Browser.agent=Sys.Browser.Safari;Sys.Browser.version=parseFloat(navigator.userAgent.match(/AppleWebKit\/(\d+(\.\d+)?)/)[1]);Sys.Browser.name="Safari"}else if(navigator.userAgent.indexOf("Opera/")>-1)Sys.Browser.agent=Sys.Browser.Opera;Type.registerNamespace("Sys.UI");Sys._Debug=function(){};Sys._Debug.prototype={_appendConsole:function(a){if(typeof Debug!=="undefined"&&Debug.writeln)Debug.writeln(a);if(window.console&&window.console.log)window.console.log(a);if(window.opera)window.opera.postError(a);if(window.debugService)window.debugService.trace(a)},_appendTrace:function(b){var a=document.getElementById("TraceConsole");if(a&&a.tagName.toUpperCase()==="TEXTAREA")a.value+=b+"\n"},assert:function(c,a,b){if(!c){a=b&&this.assert.caller?String.format(Sys.Res.assertFailedCaller,a,this.assert.caller):String.format(Sys.Res.assertFailed,a);if(confirm(String.format(Sys.Res.breakIntoDebugger,a)))this.fail(a)}},clearTrace:function(){var a=document.getElementById("TraceConsole");if(a&&a.tagName.toUpperCase()==="TEXTAREA")a.value=""},fail:function(message){this._appendConsole(message);if(Sys.Browser.hasDebuggerStatement)eval("debugger")},trace:function(a){this._appendConsole(a);this._appendTrace(a)},traceDump:function(a,b){var c=this._traceDump(a,b,true)},_traceDump:function(a,c,f,b,d){c=c?c:"traceDump";b=b?b:"";if(a===null){this.trace(b+c+": null");return}switch(typeof a){case "undefined":this.trace(b+c+": Undefined");break;case "number":case "string":case "boolean":this.trace(b+c+": "+a);break;default:if(Date.isInstanceOfType(a)||RegExp.isInstanceOfType(a)){this.trace(b+c+": "+a.toString());break}if(!d)d=[];else if(Array.contains(d,a)){this.trace(b+c+": ...");return}Array.add(d,a);if(a==window||a===document||window.HTMLElement&&a instanceof HTMLElement||typeof a.nodeName==="string"){var k=a.tagName?a.tagName:"DomElement";if(a.id)k+=" - "+a.id;this.trace(b+c+" {"+k+"}")}else{var i=Object.getTypeName(a);this.trace(b+c+(typeof i==="string"?" {"+i+"}":""));if(b===""||f){b+=" ";var e,j,l,g,h;if(Array.isInstanceOfType(a)){j=a.length;for(e=0;e=0;d--){var k=h[d].trim();b=a[k];if(typeof b!=="number")throw Error.argument("value",String.format(Sys.Res.enumInvalidValue,c.split(",")[d].trim(),this.__typeName));j|=b}return j}}function Sys$Enum$toString(c){if(typeof c==="undefined"||c===null)return this.__string;var d=this.prototype,a;if(!this.__flags||c===0){for(a in d)if(d[a]===c)return a}else{var b=this.__sortedValues;if(!b){b=[];for(a in d)b[b.length]={key:a,value:d[a]};b.sort(function(a,b){return a.value-b.value});this.__sortedValues=b}var e=[],g=c;for(a=b.length-1;a>=0;a--){var h=b[a],f=h.value;if(f===0)continue;if((f&c)===f){e[e.length]=h.key;g-=f;if(g===0)break}}if(e.length&&g===0)return e.reverse().join(", ")}return ""}Type.prototype.registerEnum=function(b,c){Sys.__upperCaseTypes[b.toUpperCase()]=this;for(var a in this.prototype)this[a]=this.prototype[a];this.__typeName=b;this.parse=Sys$Enum$parse;this.__string=this.toString();this.toString=Sys$Enum$toString;this.__flags=c;this.__enum=true};Type.isEnum=function(a){if(typeof a==="undefined"||a===null)return false;return !!a.__enum};Type.isFlags=function(a){if(typeof a==="undefined"||a===null)return false;return !!a.__flags};Sys.EventHandlerList=function(){this._list={}};Sys.EventHandlerList.prototype={addHandler:function(b,a){Array.add(this._getEvent(b,true),a)},removeHandler:function(c,b){var a=this._getEvent(c);if(!a)return;Array.remove(a,b)},getHandler:function(b){var a=this._getEvent(b);if(!a||a.length===0)return null;a=Array.clone(a);return function(c,d){for(var b=0,e=a.length;b=0;c--)$removeHandler(a,b,d[c].handler)}a._events=null}},$removeHandler=Sys.UI.DomEvent.removeHandler=function(a,e,f){var d=null,c=a._events[e];for(var b=0,g=c.length;b=0)d.className=(a.substr(0,b)+" "+a.substring(b+c.length+1,a.length)).trim()};Sys.UI.DomElement.setLocation=function(b,c,d){var a=b.style;a.position="absolute";a.left=c+"px";a.top=d+"px"};Sys.UI.DomElement.toggleCssClass=function(b,a){if(Sys.UI.DomElement.containsCssClass(b,a))Sys.UI.DomElement.removeCssClass(b,a);else Sys.UI.DomElement.addCssClass(b,a)};Sys.UI.DomElement.getVisibilityMode=function(a){return a._visibilityMode===Sys.UI.VisibilityMode.hide?Sys.UI.VisibilityMode.hide:Sys.UI.VisibilityMode.collapse};Sys.UI.DomElement.setVisibilityMode=function(a,b){Sys.UI.DomElement._ensureOldDisplayMode(a);if(a._visibilityMode!==b){a._visibilityMode=b;if(Sys.UI.DomElement.getVisible(a)===false)if(a._visibilityMode===Sys.UI.VisibilityMode.hide)a.style.display=a._oldDisplayMode;else a.style.display="none";a._visibilityMode=b}};Sys.UI.DomElement.getVisible=function(b){var a=b.currentStyle||Sys.UI.DomElement._getCurrentStyle(b);if(!a)return true;return a.visibility!=="hidden"&&a.display!=="none"};Sys.UI.DomElement.setVisible=function(a,b){if(b!==Sys.UI.DomElement.getVisible(a)){Sys.UI.DomElement._ensureOldDisplayMode(a);a.style.visibility=b?"visible":"hidden";if(b||a._visibilityMode===Sys.UI.VisibilityMode.hide)a.style.display=a._oldDisplayMode;else a.style.display="none"}};Sys.UI.DomElement._ensureOldDisplayMode=function(a){if(!a._oldDisplayMode){var b=a.currentStyle||Sys.UI.DomElement._getCurrentStyle(a);a._oldDisplayMode=b?b.display:null;if(!a._oldDisplayMode||a._oldDisplayMode==="none")switch(a.tagName.toUpperCase()){case "DIV":case "P":case "ADDRESS":case "BLOCKQUOTE":case "BODY":case "COL":case "COLGROUP":case "DD":case "DL":case "DT":case "FIELDSET":case "FORM":case "H1":case "H2":case "H3":case "H4":case "H5":case "H6":case "HR":case "IFRAME":case "LEGEND":case "OL":case "PRE":case "TABLE":case "TD":case "TH":case "TR":case "UL":a._oldDisplayMode="block";break;case "LI":a._oldDisplayMode="list-item";break;default:a._oldDisplayMode="inline"}}};Sys.UI.DomElement._getWindow=function(a){var b=a.ownerDocument||a.document||a;return b.defaultView||b.parentWindow};Sys.UI.DomElement._getCurrentStyle=function(a){if(a.nodeType===3)return null;var c=Sys.UI.DomElement._getWindow(a);if(a.documentElement)a=a.documentElement;var b=c&&a!==c&&c.getComputedStyle?c.getComputedStyle(a,null):a.currentStyle||a.style;if(!b&&Sys.Browser.agent===Sys.Browser.Safari&&a.style){var g=a.style.display,f=a.style.position;a.style.position="absolute";a.style.display="block";var e=c.getComputedStyle(a,null);a.style.display=g;a.style.position=f;b={};for(var d in e)b[d]=e[d];b.display="none"}return b};Sys.IContainer=function(){};Sys.IContainer.prototype={};Sys.IContainer.registerInterface("Sys.IContainer");Sys._ScriptLoader=function(){this._scriptsToLoad=null;this._sessions=[];this._scriptLoadedDelegate=Function.createDelegate(this,this._scriptLoadedHandler)};Sys._ScriptLoader.prototype={dispose:function(){this._stopSession();this._loading=false;if(this._events)delete this._events;this._sessions=null;this._currentSession=null;this._scriptLoadedDelegate=null},loadScripts:function(d,b,c,a){var e={allScriptsLoadedCallback:b,scriptLoadFailedCallback:c,scriptLoadTimeoutCallback:a,scriptsToLoad:this._scriptsToLoad,scriptTimeout:d};this._scriptsToLoad=null;this._sessions[this._sessions.length]=e;if(!this._loading)this._nextSession()},notifyScriptLoaded:function(){if(!this._loading)return;this._currentTask._notified++;if(Sys.Browser.agent===Sys.Browser.Safari)if(this._currentTask._notified===1)window.setTimeout(Function.createDelegate(this,function(){this._scriptLoadedHandler(this._currentTask.get_scriptElement(),true)}),0)},queueCustomScriptTag:function(a){if(!this._scriptsToLoad)this._scriptsToLoad=[];Array.add(this._scriptsToLoad,a)},queueScriptBlock:function(a){if(!this._scriptsToLoad)this._scriptsToLoad=[];Array.add(this._scriptsToLoad,{text:a})},queueScriptReference:function(a){if(!this._scriptsToLoad)this._scriptsToLoad=[];Array.add(this._scriptsToLoad,{src:a})},_createScriptElement:function(c){var a=document.createElement("script");a.type="text/javascript";for(var b in c)a[b]=c[b];return a},_loadScriptsInternal:function(){var b=this._currentSession;if(b.scriptsToLoad&&b.scriptsToLoad.length>0){var c=Array.dequeue(b.scriptsToLoad),a=this._createScriptElement(c);if(a.text&&Sys.Browser.agent===Sys.Browser.Safari){a.innerHTML=a.text;delete a.text}if(typeof c.src==="string"){this._currentTask=new Sys._ScriptLoaderTask(a,this._scriptLoadedDelegate);this._currentTask.execute()}else{document.getElementsByTagName("head")[0].appendChild(a);Sys._ScriptLoader._clearScript(a);this._loadScriptsInternal()}}else{this._stopSession();var d=b.allScriptsLoadedCallback;if(d)d(this);this._nextSession()}},_nextSession:function(){if(this._sessions.length===0){this._loading=false;this._currentSession=null;return}this._loading=true;var a=Array.dequeue(this._sessions);this._currentSession=a;if(a.scriptTimeout>0)this._timeoutCookie=window.setTimeout(Function.createDelegate(this,this._scriptLoadTimeoutHandler),a.scriptTimeout*1000);this._loadScriptsInternal()},_raiseError:function(a){var c=this._currentSession.scriptLoadFailedCallback,b=this._currentTask.get_scriptElement();this._stopSession();if(c){c(this,b,a);this._nextSession()}else{this._loading=false;throw Sys._ScriptLoader._errorScriptLoadFailed(b.src,a)}},_scriptLoadedHandler:function(a,b){if(b&&this._currentTask._notified)if(this._currentTask._notified>1)this._raiseError(true);else{Array.add(Sys._ScriptLoader._getLoadedScripts(),a.src);this._currentTask.dispose();this._currentTask=null;this._loadScriptsInternal()}else this._raiseError(false)},_scriptLoadTimeoutHandler:function(){var a=this._currentSession.scriptLoadTimeoutCallback;this._stopSession();if(a)a(this);this._nextSession()},_stopSession:function(){if(this._timeoutCookie){window.clearTimeout(this._timeoutCookie);this._timeoutCookie=null}if(this._currentTask){this._currentTask.dispose();this._currentTask=null}}};Sys._ScriptLoader.registerClass("Sys._ScriptLoader",null,Sys.IDisposable);Sys._ScriptLoader.getInstance=function(){var a=Sys._ScriptLoader._activeInstance;if(!a)a=Sys._ScriptLoader._activeInstance=new Sys._ScriptLoader;return a};Sys._ScriptLoader.isScriptLoaded=function(b){var a=document.createElement("script");a.src=b;return Array.contains(Sys._ScriptLoader._getLoadedScripts(),a.src)};Sys._ScriptLoader.readLoadedScripts=function(){if(!Sys._ScriptLoader._referencedScripts){var b=Sys._ScriptLoader._referencedScripts=[],c=document.getElementsByTagName("script");for(i=c.length-1;i>=0;i--){var d=c[i],a=d.src;if(a.length)if(!Array.contains(b,a))Array.add(b,a)}}};Sys._ScriptLoader._clearScript=function(a){if(!Sys.Debug.isDebug)a.parentNode.removeChild(a)};Sys._ScriptLoader._errorScriptLoadFailed=function(b,d){var a;if(d)a=Sys.Res.scriptLoadMultipleCallbacks;else a=Sys.Res.scriptLoadFailed;var e="Sys.ScriptLoadFailedException: "+String.format(a,b),c=Error.create(e,{name:"Sys.ScriptLoadFailedException","scriptUrl":b});c.popStackFrame();return c};Sys._ScriptLoader._getLoadedScripts=function(){if(!Sys._ScriptLoader._referencedScripts){Sys._ScriptLoader._referencedScripts=[];Sys._ScriptLoader.readLoadedScripts()}return Sys._ScriptLoader._referencedScripts};Sys._ScriptLoaderTask=function(b,a){this._scriptElement=b;this._completedCallback=a;this._notified=0};Sys._ScriptLoaderTask.prototype={get_scriptElement:function(){return this._scriptElement},dispose:function(){if(this._disposed)return;this._disposed=true;this._removeScriptElementHandlers();Sys._ScriptLoader._clearScript(this._scriptElement);this._scriptElement=null},execute:function(){this._addScriptElementHandlers();document.getElementsByTagName("head")[0].appendChild(this._scriptElement)},_addScriptElementHandlers:function(){this._scriptLoadDelegate=Function.createDelegate(this,this._scriptLoadHandler);if(Sys.Browser.agent!==Sys.Browser.InternetExplorer){this._scriptElement.readyState="loaded";$addHandler(this._scriptElement,"load",this._scriptLoadDelegate)}else $addHandler(this._scriptElement,"readystatechange",this._scriptLoadDelegate);if(this._scriptElement.addEventListener){this._scriptErrorDelegate=Function.createDelegate(this,this._scriptErrorHandler);this._scriptElement.addEventListener("error",this._scriptErrorDelegate,false)}},_removeScriptElementHandlers:function(){if(this._scriptLoadDelegate){var a=this.get_scriptElement();if(Sys.Browser.agent!==Sys.Browser.InternetExplorer)$removeHandler(a,"load",this._scriptLoadDelegate);else $removeHandler(a,"readystatechange",this._scriptLoadDelegate);if(this._scriptErrorDelegate){this._scriptElement.removeEventListener("error",this._scriptErrorDelegate,false);this._scriptErrorDelegate=null}this._scriptLoadDelegate=null}},_scriptErrorHandler:function(){if(this._disposed)return;this._completedCallback(this.get_scriptElement(),false)},_scriptLoadHandler:function(){if(this._disposed)return;var a=this.get_scriptElement();if(a.readyState!=="loaded"&&a.readyState!=="complete")return;var b=this;window.setTimeout(function(){b._completedCallback(a,true)},0)}};Sys._ScriptLoaderTask.registerClass("Sys._ScriptLoaderTask",null,Sys.IDisposable);Sys.ApplicationLoadEventArgs=function(b,a){Sys.ApplicationLoadEventArgs.initializeBase(this);this._components=b;this._isPartialLoad=a};Sys.ApplicationLoadEventArgs.prototype={get_components:function(){return this._components},get_isPartialLoad:function(){return this._isPartialLoad}};Sys.ApplicationLoadEventArgs.registerClass("Sys.ApplicationLoadEventArgs",Sys.EventArgs);Sys.HistoryEventArgs=function(a){Sys.HistoryEventArgs.initializeBase(this);this._state=a};Sys.HistoryEventArgs.prototype={get_state:function(){return this._state}};Sys.HistoryEventArgs.registerClass("Sys.HistoryEventArgs",Sys.EventArgs);Sys._Application=function(){Sys._Application.initializeBase(this);this._disposableObjects=[];this._components={};this._createdComponents=[];this._secondPassComponents=[];this._appLoadHandler=null;this._beginRequestHandler=null;this._clientId=null;this._currentEntry="";this._endRequestHandler=null;this._history=null;this._enableHistory=false;this._historyFrame=null;this._historyInitialized=false;this._historyInitialLength=0;this._historyLength=0;this._historyPointIsNew=false;this._ignoreTimer=false;this._initialState=null;this._state={};this._timerCookie=0;this._timerHandler=null;this._uniqueId=null;this._unloadHandlerDelegate=Function.createDelegate(this,this._unloadHandler);this._loadHandlerDelegate=Function.createDelegate(this,this._loadHandler);Sys.UI.DomEvent.addHandler(window,"unload",this._unloadHandlerDelegate);Sys.UI.DomEvent.addHandler(window,"load",this._loadHandlerDelegate)};Sys._Application.prototype={_creatingComponents:false,_disposing:false,get_isCreatingComponents:function(){return this._creatingComponents},get_stateString:function(){var a=window.location.hash;if(this._isSafari2()){var b=this._getHistory();if(b)a=b[window.history.length-this._historyInitialLength]}if(a.length>0&&a.charAt(0)==="#")a=a.substring(1);if(Sys.Browser.agent===Sys.Browser.Firefox)a=this._serializeState(this._deserializeState(a,true));return a},get_enableHistory:function(){return this._enableHistory},set_enableHistory:function(a){this._enableHistory=a},add_init:function(a){if(this._initialized)a(this,Sys.EventArgs.Empty);else this.get_events().addHandler("init",a)},remove_init:function(a){this.get_events().removeHandler("init",a)},add_load:function(a){this.get_events().addHandler("load",a)},remove_load:function(a){this.get_events().removeHandler("load",a)},add_navigate:function(a){this.get_events().addHandler("navigate",a)},remove_navigate:function(a){this.get_events().removeHandler("navigate",a)},add_unload:function(a){this.get_events().addHandler("unload",a)},remove_unload:function(a){this.get_events().removeHandler("unload",a)},addComponent:function(a){this._components[a.get_id()]=a},addHistoryPoint:function(c,f){this._ensureHistory();var b=this._state;for(var a in c){var d=c[a];if(d===null){if(typeof b[a]!=="undefined")delete b[a]}else b[a]=d}var e=this._serializeState(b);this._historyPointIsNew=true;this._setState(e,f);this._raiseNavigate()},beginCreateComponents:function(){this._creatingComponents=true},dispose:function(){if(!this._disposing){this._disposing=true;if(this._timerCookie){window.clearTimeout(this._timerCookie);delete this._timerCookie}if(this._endRequestHandler){Sys.WebForms.PageRequestManager.getInstance().remove_endRequest(this._endRequestHandler);delete this._endRequestHandler}if(this._beginRequestHandler){Sys.WebForms.PageRequestManager.getInstance().remove_beginRequest(this._beginRequestHandler);delete this._beginRequestHandler}if(window.pageUnload)window.pageUnload(this,Sys.EventArgs.Empty);var c=this.get_events().getHandler("unload");if(c)c(this,Sys.EventArgs.Empty);var b=Array.clone(this._disposableObjects);for(var a=0,e=b.length;a'");d.write(""+(c||document.title)+"parent.Sys.Application._onIFrameLoad(\''+a+"');");d.close()}this._ignoreTimer=false;var h=this.get_stateString();this._currentEntry=a;if(a!==h){if(this._isSafari2()){var g=this._getHistory();g[window.history.length-this._historyInitialLength+1]=a;this._setHistory(g);this._historyLength=window.history.length+1;var b=document.createElement("form");b.method="get";b.action="#"+a;document.appendChild(b);b.submit();document.removeChild(b)}else window.location.hash=a;if(typeof c!=="undefined"&&c!==null)document.title=c}}},_unloadHandler:function(){this.dispose()},_updateHiddenField:function(b){if(this._clientId){var a=document.getElementById(this._clientId);if(a)a.value=b}}};Sys._Application.registerClass("Sys._Application",Sys.Component,Sys.IContainer);Sys.Application=new Sys._Application;var $find=Sys.Application.findComponent;Type.registerNamespace("Sys.Net");Sys.Net.WebRequestExecutor=function(){this._webRequest=null;this._resultObject=null};Sys.Net.WebRequestExecutor.prototype={get_webRequest:function(){return this._webRequest},_set_webRequest:function(a){this._webRequest=a},get_started:function(){throw Error.notImplemented()},get_responseAvailable:function(){throw Error.notImplemented()},get_timedOut:function(){throw Error.notImplemented()},get_aborted:function(){throw Error.notImplemented()},get_responseData:function(){throw Error.notImplemented()},get_statusCode:function(){throw Error.notImplemented()},get_statusText:function(){throw Error.notImplemented()},get_xml:function(){throw Error.notImplemented()},get_object:function(){if(!this._resultObject)this._resultObject=Sys.Serialization.JavaScriptSerializer.deserialize(this.get_responseData());return this._resultObject},executeRequest:function(){throw Error.notImplemented()},abort:function(){throw Error.notImplemented()},getResponseHeader:function(){throw Error.notImplemented()},getAllResponseHeaders:function(){throw Error.notImplemented()}};Sys.Net.WebRequestExecutor.registerClass("Sys.Net.WebRequestExecutor");Sys.Net.XMLDOM=function(d){if(!window.DOMParser){var c=["Msxml2.DOMDocument.3.0","Msxml2.DOMDocument"];for(var b=0,f=c.length;b0)this._timer=window.setTimeout(Function.createDelegate(this,this._onTimeout),d);this._xmlHttpRequest.send(c);this._started=true},getResponseHeader:function(b){var a;try{a=this._xmlHttpRequest.getResponseHeader(b)}catch(c){}if(!a)a="";return a},getAllResponseHeaders:function(){return this._xmlHttpRequest.getAllResponseHeaders()},get_responseData:function(){return this._xmlHttpRequest.responseText},get_statusCode:function(){var a=0;try{a=this._xmlHttpRequest.status}catch(b){}return a},get_statusText:function(){return this._xmlHttpRequest.statusText},get_xml:function(){var a=this._xmlHttpRequest.responseXML;if(!a||!a.documentElement){a=Sys.Net.XMLDOM(this._xmlHttpRequest.responseText);if(!a||!a.documentElement)return null}else if(navigator.userAgent.indexOf("MSIE")!==-1)a.setProperty("SelectionLanguage","XPath");if(a.documentElement.namespaceURI==="http://www.mozilla.org/newlayout/xml/parsererror.xml"&&a.documentElement.tagName==="parsererror")return null;if(a.documentElement.firstChild&&a.documentElement.firstChild.tagName==="parsererror")return null;return a},abort:function(){if(this._aborted||this._responseAvailable||this._timedOut)return;this._aborted=true;this._clearTimer();if(this._xmlHttpRequest&&!this._responseAvailable){this._xmlHttpRequest.onreadystatechange=Function.emptyMethod;this._xmlHttpRequest.abort();this._xmlHttpRequest=null;this._webRequest.completed(Sys.EventArgs.Empty)}}};Sys.Net.XMLHttpExecutor.registerClass("Sys.Net.XMLHttpExecutor",Sys.Net.WebRequestExecutor);Sys.Net._WebRequestManager=function(){this._defaultTimeout=0;this._defaultExecutorType="Sys.Net.XMLHttpExecutor"};Sys.Net._WebRequestManager.prototype={add_invokingRequest:function(a){this._get_eventHandlerList().addHandler("invokingRequest",a)},remove_invokingRequest:function(a){this._get_eventHandlerList().removeHandler("invokingRequest",a)},add_completedRequest:function(a){this._get_eventHandlerList().addHandler("completedRequest",a)},remove_completedRequest:function(a){this._get_eventHandlerList().removeHandler("completedRequest",a)},_get_eventHandlerList:function(){if(!this._events)this._events=new Sys.EventHandlerList;return this._events},get_defaultTimeout:function(){return this._defaultTimeout},set_defaultTimeout:function(a){this._defaultTimeout=a},get_defaultExecutorType:function(){return this._defaultExecutorType},set_defaultExecutorType:function(a){this._defaultExecutorType=a},executeRequest:function(webRequest){var executor=webRequest.get_executor();if(!executor){var failed=false;try{var executorType=eval(this._defaultExecutorType);executor=new executorType}catch(a){failed=true}webRequest.set_executor(executor)}if(executor.get_aborted())return;var evArgs=new Sys.Net.NetworkRequestEventArgs(webRequest),handler=this._get_eventHandlerList().getHandler("invokingRequest");if(handler)handler(this,evArgs);if(!evArgs.get_cancel())executor.executeRequest()}};Sys.Net._WebRequestManager.registerClass("Sys.Net._WebRequestManager");Sys.Net.WebRequestManager=new Sys.Net._WebRequestManager;Sys.Net.NetworkRequestEventArgs=function(a){Sys.Net.NetworkRequestEventArgs.initializeBase(this);this._webRequest=a};Sys.Net.NetworkRequestEventArgs.prototype={get_webRequest:function(){return this._webRequest}};Sys.Net.NetworkRequestEventArgs.registerClass("Sys.Net.NetworkRequestEventArgs",Sys.CancelEventArgs);Sys.Net.WebRequest=function(){this._url="";this._headers={};this._body=null;this._userContext=null;this._httpVerb=null;this._executor=null;this._invokeCalled=false;this._timeout=0};Sys.Net.WebRequest.prototype={add_completed:function(a){this._get_eventHandlerList().addHandler("completed",a)},remove_completed:function(a){this._get_eventHandlerList().removeHandler("completed",a)},completed:function(b){var a=Sys.Net.WebRequestManager._get_eventHandlerList().getHandler("completedRequest");if(a)a(this._executor,b);a=this._get_eventHandlerList().getHandler("completed");if(a)a(this._executor,b)},_get_eventHandlerList:function(){if(!this._events)this._events=new Sys.EventHandlerList;return this._events},get_url:function(){return this._url},set_url:function(a){this._url=a},get_headers:function(){return this._headers},get_httpVerb:function(){if(this._httpVerb===null){if(this._body===null)return "GET";return "POST"}return this._httpVerb},set_httpVerb:function(a){this._httpVerb=a},get_body:function(){return this._body},set_body:function(a){this._body=a},get_userContext:function(){return this._userContext},set_userContext:function(a){this._userContext=a},get_executor:function(){return this._executor},set_executor:function(a){this._executor=a;this._executor._set_webRequest(this)},get_timeout:function(){if(this._timeout===0)return Sys.Net.WebRequestManager.get_defaultTimeout();return this._timeout},set_timeout:function(a){this._timeout=a},getResolvedUrl:function(){return Sys.Net.WebRequest._resolveUrl(this._url)},invoke:function(){Sys.Net.WebRequestManager.executeRequest(this);this._invokeCalled=true}};Sys.Net.WebRequest._resolveUrl=function(b,a){if(b&&b.indexOf("://")!==-1)return b;if(!a||a.length===0){var d=document.getElementsByTagName("base")[0];if(d&&d.href&&d.href.length>0)a=d.href;else a=document.URL}var c=a.indexOf("?");if(c!==-1)a=a.substr(0,c);c=a.indexOf("#");if(c!==-1)a=a.substr(0,c);a=a.substr(0,a.lastIndexOf("/")+1);if(!b||b.length===0)return a;if(b.charAt(0)==="/"){var e=a.indexOf("://"),g=a.indexOf("/",e+3);return a.substr(0,g)+b}else{var f=a.lastIndexOf("/");return a.substr(0,f+1)+b}};Sys.Net.WebRequest._createQueryString=function(d,b){if(!b)b=encodeURIComponent;var a=new Sys.StringBuilder,f=0;for(var c in d){var e=d[c];if(typeof e==="function")continue;var g=Sys.Serialization.JavaScriptSerializer.serialize(e);if(f!==0)a.append("&");a.append(c);a.append("=");a.append(b(g));f++}return a.toString()};Sys.Net.WebRequest._createUrl=function(a,b){if(!b)return a;var d=Sys.Net.WebRequest._createQueryString(b);if(d.length>0){var c="?";if(a&&a.indexOf("?")!==-1)c="&";return a+c+d}else return a};Sys.Net.WebRequest.registerClass("Sys.Net.WebRequest");Sys.Net.WebServiceProxy=function(){};Sys.Net.WebServiceProxy.prototype={get_timeout:function(){return this._timeout},set_timeout:function(a){if(a<0)throw Error.argumentOutOfRange("value",a,Sys.Res.invalidTimeout);this._timeout=a},get_defaultUserContext:function(){return this._userContext},set_defaultUserContext:function(a){this._userContext=a},get_defaultSucceededCallback:function(){return this._succeeded},set_defaultSucceededCallback:function(a){this._succeeded=a},get_defaultFailedCallback:function(){return this._failed},set_defaultFailedCallback:function(a){this._failed=a},get_path:function(){return this._path},set_path:function(a){this._path=a},_invoke:function(d,e,g,f,c,b,a){if(c===null||typeof c==="undefined")c=this.get_defaultSucceededCallback();if(b===null||typeof b==="undefined")b=this.get_defaultFailedCallback();if(a===null||typeof a==="undefined")a=this.get_defaultUserContext();return Sys.Net.WebServiceProxy.invoke(d,e,g,f,c,b,a,this.get_timeout())}};Sys.Net.WebServiceProxy.registerClass("Sys.Net.WebServiceProxy");Sys.Net.WebServiceProxy.invoke=function(k,a,j,d,i,c,f,h){var b=new Sys.Net.WebRequest;b.get_headers()["Content-Type"]="application/json; charset=utf-8";if(!d)d={};var g=d;if(!j||!g)g={};b.set_url(Sys.Net.WebRequest._createUrl(k+"/"+encodeURIComponent(a),g));var e=null;if(!j){e=Sys.Serialization.JavaScriptSerializer.serialize(d);if(e==="{}")e=""}b.set_body(e);b.add_completed(l);if(h&&h>0)b.set_timeout(h);b.invoke();function l(d){if(d.get_responseAvailable()){var g=d.get_statusCode(),b=null;try{var e=d.getResponseHeader("Content-Type");if(e.startsWith("application/json"))b=d.get_object();else if(e.startsWith("text/xml"))b=d.get_xml();else b=d.get_responseData()}catch(m){}var k=d.getResponseHeader("jsonerror"),h=k==="true";if(h){if(b)b=new Sys.Net.WebServiceError(false,b.Message,b.StackTrace,b.ExceptionType)}else if(e.startsWith("application/json"))b=b.d;if(g<200||g>=300||h){if(c){if(!b||!h)b=new Sys.Net.WebServiceError(false,String.format(Sys.Res.webServiceFailedNoMsg,a),"","");b._statusCode=g;c(b,f,a)}}else if(i)i(b,f,a)}else{var j;if(d.get_timedOut())j=String.format(Sys.Res.webServiceTimedOut,a);else j=String.format(Sys.Res.webServiceFailedNoMsg,a);if(c)c(new Sys.Net.WebServiceError(d.get_timedOut(),j,"",""),f,a)}}return b};Sys.Net.WebServiceProxy._generateTypedConstructor=function(a){return function(b){if(b)for(var c in b)this[c]=b[c];this.__type=a}};Sys.Net.WebServiceError=function(c,d,b,a){this._timedOut=c;this._message=d;this._stackTrace=b;this._exceptionType=a;this._statusCode=-1};Sys.Net.WebServiceError.prototype={get_timedOut:function(){return this._timedOut},get_statusCode:function(){return this._statusCode},get_message:function(){return this._message},get_stackTrace:function(){return this._stackTrace},get_exceptionType:function(){return this._exceptionType}};Sys.Net.WebServiceError.registerClass("Sys.Net.WebServiceError");Type.registerNamespace("Sys.Services");Sys.Services._ProfileService=function(){Sys.Services._ProfileService.initializeBase(this);this.properties={}};Sys.Services._ProfileService.DefaultWebServicePath="";Sys.Services._ProfileService.prototype={_defaultLoadCompletedCallback:null,_defaultSaveCompletedCallback:null,_path:"",_timeout:0,get_defaultLoadCompletedCallback:function(){return this._defaultLoadCompletedCallback},set_defaultLoadCompletedCallback:function(a){this._defaultLoadCompletedCallback=a},get_defaultSaveCompletedCallback:function(){return this._defaultSaveCompletedCallback},set_defaultSaveCompletedCallback:function(a){this._defaultSaveCompletedCallback=a},get_path:function(){return this._path||""},load:function(c,d,e,f){var b,a;if(!c){a="GetAllPropertiesForCurrentUser";b={authenticatedUserOnly:false}}else{a="GetPropertiesForCurrentUser";b={properties:this._clonePropertyNames(c),authenticatedUserOnly:false}}this._invoke(this._get_path(),a,false,b,Function.createDelegate(this,this._onLoadComplete),Function.createDelegate(this,this._onLoadFailed),[d,e,f])},save:function(d,b,c,e){var a=this._flattenProperties(d,this.properties);this._invoke(this._get_path(),"SetPropertiesForCurrentUser",false,{values:a.value,authenticatedUserOnly:false},Function.createDelegate(this,this._onSaveComplete),Function.createDelegate(this,this._onSaveFailed),[b,c,e,a.count])},_clonePropertyNames:function(e){var c=[],d={};for(var b=0;b0)a.append(",");Sys.Serialization.JavaScriptSerializer._serializeWithBuilder(b[c],a,false,g)}a.append("]")}else{if(Date.isInstanceOfType(b)){a.append('"\\/Date(');a.append(b.getTime());a.append(')\\/"');break}var d=[],f=0;for(var e in b){if(e.startsWith("$"))continue;if(e===Sys.Serialization.JavaScriptSerializer._serverTypeFieldName&&f!==0){d[f++]=d[0];d[0]=e}else d[f++]=e}if(i)d.sort();a.append("{");var j=false;for(c=0;c + /// + /// + /// + /// + /// +}; +Sys.Mvc.InsertionMode.prototype = { + replace: 0, + insertBefore: 1, + insertAfter: 2 +} +Sys.Mvc.InsertionMode.registerEnum('Sys.Mvc.InsertionMode', false); + + +//////////////////////////////////////////////////////////////////////////////// +// Sys.Mvc.AjaxContext + +Sys.Mvc.AjaxContext = function Sys_Mvc_AjaxContext(request, updateTarget, loadingElement, insertionMode) { + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + this._request = request; + this._updateTarget = updateTarget; + this._loadingElement = loadingElement; + this._insertionMode = insertionMode; +} +Sys.Mvc.AjaxContext.prototype = { + _insertionMode: 0, + _loadingElement: null, + _response: null, + _request: null, + _updateTarget: null, + + get_data: function Sys_Mvc_AjaxContext$get_data() { + /// + if (this._response) { + return this._response.get_responseData(); + } + else { + return null; + } + }, + + get_insertionMode: function Sys_Mvc_AjaxContext$get_insertionMode() { + /// + return this._insertionMode; + }, + + get_loadingElement: function Sys_Mvc_AjaxContext$get_loadingElement() { + /// + return this._loadingElement; + }, + + get_response: function Sys_Mvc_AjaxContext$get_response() { + /// + return this._response; + }, + set_response: function Sys_Mvc_AjaxContext$set_response(value) { + /// + this._response = value; + return value; + }, + + get_request: function Sys_Mvc_AjaxContext$get_request() { + /// + return this._request; + }, + + get_updateTarget: function Sys_Mvc_AjaxContext$get_updateTarget() { + /// + return this._updateTarget; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// Sys.Mvc.AsyncHyperlink + +Sys.Mvc.AsyncHyperlink = function Sys_Mvc_AsyncHyperlink() { +} +Sys.Mvc.AsyncHyperlink.handleClick = function Sys_Mvc_AsyncHyperlink$handleClick(anchor, evt, ajaxOptions) { + /// + /// + /// + /// + /// + /// + evt.preventDefault(); + Sys.Mvc.MvcHelpers._asyncRequest(anchor.href, 'post', '', anchor, ajaxOptions); +} + + +//////////////////////////////////////////////////////////////////////////////// +// Sys.Mvc.MvcHelpers + +Sys.Mvc.MvcHelpers = function Sys_Mvc_MvcHelpers() { +} +Sys.Mvc.MvcHelpers._serializeForm = function Sys_Mvc_MvcHelpers$_serializeForm(form) { + /// + /// + /// + var formElements = form.elements; + var formBody = new Sys.StringBuilder(); + var count = formElements.length; + for (var i = 0; i < count; i++) { + var element = formElements[i]; + var name = element.name; + if (!name || !name.length) { + continue; + } + var tagName = element.tagName.toUpperCase(); + if (tagName === 'INPUT') { + var inputElement = element; + var type = inputElement.type; + if ((type === 'text') || (type === 'password') || (type === 'hidden') || (((type === 'checkbox') || (type === 'radio')) && element.checked)) { + formBody.append(encodeURIComponent(name)); + formBody.append('='); + formBody.append(encodeURIComponent(inputElement.value)); + formBody.append('&'); + } + } + else if (tagName === 'SELECT') { + var selectElement = element; + var optionCount = selectElement.options.length; + for (var j = 0; j < optionCount; j++) { + var optionElement = selectElement.options[j]; + if (optionElement.selected) { + formBody.append(encodeURIComponent(name)); + formBody.append('='); + formBody.append(encodeURIComponent(optionElement.value)); + formBody.append('&'); + } + } + } + else if (tagName === 'TEXTAREA') { + formBody.append(encodeURIComponent(name)); + formBody.append('='); + formBody.append(encodeURIComponent((element.value))); + formBody.append('&'); + } + } + return formBody.toString(); +} +Sys.Mvc.MvcHelpers._asyncRequest = function Sys_Mvc_MvcHelpers$_asyncRequest(url, verb, body, triggerElement, ajaxOptions) { + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + if (ajaxOptions.confirm) { + if (!confirm(ajaxOptions.confirm)) { + return; + } + } + if (ajaxOptions.url) { + url = ajaxOptions.url; + } + if (ajaxOptions.httpMethod) { + verb = ajaxOptions.httpMethod; + } + if (body.length > 0 && !body.endsWith('&')) { + body += '&'; + } + body += 'X-Requested-With=XMLHttpRequest'; + var requestBody = ''; + if (verb.toUpperCase() === 'GET' || verb.toUpperCase() === 'DELETE') { + if (url.indexOf('?') > -1) { + if (!url.endsWith('&')) { + url += '&'; + } + url += body; + } + else { + url += '?'; + url += body; + } + } + else { + requestBody = body; + } + var request = new Sys.Net.WebRequest(); + request.set_url(url); + request.set_httpVerb(verb); + request.set_body(requestBody); + if (verb.toUpperCase() === 'PUT') { + request.get_headers()['Content-Type'] = 'application/x-www-form-urlencoded;'; + } + request.get_headers()['X-Requested-With'] = 'XMLHttpRequest'; + var updateElement = null; + if (ajaxOptions.updateTargetId) { + updateElement = $get(ajaxOptions.updateTargetId); + } + var loadingElement = null; + if (ajaxOptions.loadingElementId) { + loadingElement = $get(ajaxOptions.loadingElementId); + } + var ajaxContext = new Sys.Mvc.AjaxContext(request, updateElement, loadingElement, ajaxOptions.insertionMode); + var continueRequest = true; + if (ajaxOptions.onBegin) { + continueRequest = ajaxOptions.onBegin(ajaxContext) !== false; + } + if (loadingElement) { + Sys.UI.DomElement.setVisible(ajaxContext.get_loadingElement(), true); + } + if (continueRequest) { + request.add_completed(Function.createDelegate(null, function(executor) { + Sys.Mvc.MvcHelpers._onComplete(request, ajaxOptions, ajaxContext); + })); + request.invoke(); + } +} +Sys.Mvc.MvcHelpers._onComplete = function Sys_Mvc_MvcHelpers$_onComplete(request, ajaxOptions, ajaxContext) { + /// + /// + /// + /// + /// + /// + ajaxContext.set_response(request.get_executor()); + if (ajaxOptions.onComplete && ajaxOptions.onComplete(ajaxContext) === false) { + return; + } + var statusCode = ajaxContext.get_response().get_statusCode(); + if ((statusCode >= 200 && statusCode < 300) || statusCode === 304 || statusCode === 1223) { + if (statusCode !== 204 && statusCode !== 304 && statusCode !== 1223) { + var contentType = ajaxContext.get_response().getResponseHeader('Content-Type'); + if ((contentType) && (contentType.indexOf('application/x-javascript') !== -1)) { + eval(ajaxContext.get_data()); + } + else { + Sys.Mvc.MvcHelpers.updateDomElement(ajaxContext.get_updateTarget(), ajaxContext.get_insertionMode(), ajaxContext.get_data()); + } + } + if (ajaxOptions.onSuccess) { + ajaxOptions.onSuccess(ajaxContext); + } + } + else { + if (ajaxOptions.onFailure) { + ajaxOptions.onFailure(ajaxContext); + } + } + if (ajaxContext.get_loadingElement()) { + Sys.UI.DomElement.setVisible(ajaxContext.get_loadingElement(), false); + } +} +Sys.Mvc.MvcHelpers.updateDomElement = function Sys_Mvc_MvcHelpers$updateDomElement(target, insertionMode, content) { + /// + /// + /// + /// + /// + /// + if (target) { + switch (insertionMode) { + case Sys.Mvc.InsertionMode.replace: + target.innerHTML = content; + break; + case Sys.Mvc.InsertionMode.insertBefore: + if (content && content.length > 0) { + target.innerHTML = content + target.innerHTML.trimStart(); + } + break; + case Sys.Mvc.InsertionMode.insertAfter: + if (content && content.length > 0) { + target.innerHTML = target.innerHTML.trimEnd() + content; + } + break; + } + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// Sys.Mvc.AsyncForm + +Sys.Mvc.AsyncForm = function Sys_Mvc_AsyncForm() { +} +Sys.Mvc.AsyncForm.handleSubmit = function Sys_Mvc_AsyncForm$handleSubmit(form, evt, ajaxOptions) { + /// + /// + /// + /// + /// + /// + evt.preventDefault(); + var body = Sys.Mvc.MvcHelpers._serializeForm(form); + Sys.Mvc.MvcHelpers._asyncRequest(form.action, form.method || 'post', body, form, ajaxOptions); +} + + +Sys.Mvc.AjaxContext.registerClass('Sys.Mvc.AjaxContext'); +Sys.Mvc.AsyncHyperlink.registerClass('Sys.Mvc.AsyncHyperlink'); +Sys.Mvc.MvcHelpers.registerClass('Sys.Mvc.MvcHelpers'); +Sys.Mvc.AsyncForm.registerClass('Sys.Mvc.AsyncForm'); + +// ---- Do not remove this footer ---- +// Generated using Script# v0.5.0.0 (http://projects.nikhilk.net) +// ----------------------------------- diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftMvcAjax.js b/aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftMvcAjax.js new file mode 100644 index 0000000..880c1b4 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftMvcAjax.js @@ -0,0 +1,23 @@ +//---------------------------------------------------------- +// Copyright (C) Microsoft Corporation. All rights reserved. +//---------------------------------------------------------- +// MicrosoftMvcAjax.js + +Type.registerNamespace('Sys.Mvc');Sys.Mvc.$create_AjaxOptions=function(){return {};} +Sys.Mvc.InsertionMode=function(){};Sys.Mvc.InsertionMode.prototype = {replace:0,insertBefore:1,insertAfter:2} +Sys.Mvc.InsertionMode.registerEnum('Sys.Mvc.InsertionMode',false);Sys.Mvc.AjaxContext=function(request,updateTarget,loadingElement,insertionMode){this.$3=request;this.$4=updateTarget;this.$1=loadingElement;this.$0=insertionMode;} +Sys.Mvc.AjaxContext.prototype={$0:0,$1:null,$2:null,$3:null,$4:null,get_data:function(){if(this.$2){return this.$2.get_responseData();}else{return null;}},get_insertionMode:function(){return this.$0;},get_loadingElement:function(){return this.$1;},get_response:function(){return this.$2;},set_response:function(value){this.$2=value;return value;},get_request:function(){return this.$3;},get_updateTarget:function(){return this.$4;}} +Sys.Mvc.AsyncHyperlink=function(){} +Sys.Mvc.AsyncHyperlink.handleClick=function(anchor,evt,ajaxOptions){evt.preventDefault();Sys.Mvc.MvcHelpers.$1(anchor.href,'post','',anchor,ajaxOptions);} +Sys.Mvc.MvcHelpers=function(){} +Sys.Mvc.MvcHelpers.$0=function($p0){var $0=$p0.elements;var $1=new Sys.StringBuilder();var $2=$0.length;for(var $3=0;$3<$2;$3++){var $4=$0[$3];var $5=$4.name;if(!$5||!$5.length){continue;}var $6=$4.tagName.toUpperCase();if($6==='INPUT'){var $7=$4;var $8=$7.type;if(($8==='text')||($8==='password')||($8==='hidden')||((($8==='checkbox')||($8==='radio'))&&$4.checked)){$1.append(encodeURIComponent($5));$1.append('=');$1.append(encodeURIComponent($7.value));$1.append('&');}}else if($6==='SELECT'){var $9=$4;var $A=$9.options.length;for(var $B=0;$B<$A;$B++){var $C=$9.options[$B];if($C.selected){$1.append(encodeURIComponent($5));$1.append('=');$1.append(encodeURIComponent($C.value));$1.append('&');}}}else if($6==='TEXTAREA'){$1.append(encodeURIComponent($5));$1.append('=');$1.append(encodeURIComponent(($4.value)));$1.append('&');}}return $1.toString();} +Sys.Mvc.MvcHelpers.$1=function($p0,$p1,$p2,$p3,$p4){if($p4.confirm){if(!confirm($p4.confirm)){return;}}if($p4.url){$p0=$p4.url;}if($p4.httpMethod){$p1=$p4.httpMethod;}if($p2.length>0&&!$p2.endsWith('&')){$p2+='&';}$p2+='X-Requested-With=XMLHttpRequest';var $0='';if($p1.toUpperCase()==='GET'||$p1.toUpperCase()==='DELETE'){if($p0.indexOf('?')>-1){if(!$p0.endsWith('&')){$p0+='&';}$p0+=$p2;}else{$p0+='?';$p0+=$p2;}}else{$0=$p2;}var $1=new Sys.Net.WebRequest();$1.set_url($p0);$1.set_httpVerb($p1);$1.set_body($0);if($p1.toUpperCase()==='PUT'){$1.get_headers()['Content-Type']='application/x-www-form-urlencoded;';}$1.get_headers()['X-Requested-With']='XMLHttpRequest';var $2=null;if($p4.updateTargetId){$2=$get($p4.updateTargetId);}var $3=null;if($p4.loadingElementId){$3=$get($p4.loadingElementId);}var $4=new Sys.Mvc.AjaxContext($1,$2,$3,$p4.insertionMode);var $5=true;if($p4.onBegin){$5=$p4.onBegin($4)!==false;}if($3){Sys.UI.DomElement.setVisible($4.get_loadingElement(),true);}if($5){$1.add_completed(Function.createDelegate(null,function($p1_0){ +Sys.Mvc.MvcHelpers.$2($1,$p4,$4);}));$1.invoke();}} +Sys.Mvc.MvcHelpers.$2=function($p0,$p1,$p2){$p2.set_response($p0.get_executor());if($p1.onComplete&&$p1.onComplete($p2)===false){return;}var $0=$p2.get_response().get_statusCode();if(($0>=200&&$0<300)||$0===304||$0===1223){if($0!==204&&$0!==304&&$0!==1223){var $1=$p2.get_response().getResponseHeader('Content-Type');if(($1)&&($1.indexOf('application/x-javascript')!==-1)){eval($p2.get_data());}else{Sys.Mvc.MvcHelpers.updateDomElement($p2.get_updateTarget(),$p2.get_insertionMode(),$p2.get_data());}}if($p1.onSuccess){$p1.onSuccess($p2);}}else{if($p1.onFailure){$p1.onFailure($p2);}}if($p2.get_loadingElement()){Sys.UI.DomElement.setVisible($p2.get_loadingElement(),false);}} +Sys.Mvc.MvcHelpers.updateDomElement=function(target,insertionMode,content){if(target){switch(insertionMode){case 0:target.innerHTML=content;break;case 1:if(content&&content.length>0){target.innerHTML=content+target.innerHTML.trimStart();}break;case 2:if(content&&content.length>0){target.innerHTML=target.innerHTML.trimEnd()+content;}break;}}} +Sys.Mvc.AsyncForm=function(){} +Sys.Mvc.AsyncForm.handleSubmit=function(form,evt,ajaxOptions){evt.preventDefault();var $0=Sys.Mvc.MvcHelpers.$0(form);Sys.Mvc.MvcHelpers.$1(form.action,form.method||'post',$0,form,ajaxOptions);} +Sys.Mvc.AjaxContext.registerClass('Sys.Mvc.AjaxContext');Sys.Mvc.AsyncHyperlink.registerClass('Sys.Mvc.AsyncHyperlink');Sys.Mvc.MvcHelpers.registerClass('Sys.Mvc.MvcHelpers');Sys.Mvc.AsyncForm.registerClass('Sys.Mvc.AsyncForm'); +// ---- Do not remove this footer ---- +// Generated using Script# v0.5.0.0 (http://projects.nikhilk.net) +// ----------------------------------- diff --git a/aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/jquery-1.3.2-vsdoc.js b/aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/jquery-1.3.2-vsdoc.js new file mode 100644 index 0000000..50705b2 --- /dev/null +++ b/aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/jquery-1.3.2-vsdoc.js @@ -0,0 +1,6255 @@ +/* + * This file has been commented to support Visual Studio Intellisense. + * You should not use this file at runtime inside the browser--it is only + * intended to be used only for design-time IntelliSense. Please use the + * standard jQuery library for all production use. + * + * Comment version: 1.3.2a + */ + +/* + * jQuery JavaScript Library v1.3.2 + * + * Copyright (c) 2009 John Resig, http://jquery.com/ + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ + +(function(){ + +var + // Will speed up references to window, and allows munging its name. + window = this, + // Will speed up references to undefined, and allows munging its name. + undefined, + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + // Map over the $ in case of overwrite + _$ = window.$, + + jQuery = window.jQuery = window.$ = function(selector, context) { + /// + /// 1: $(expression, context) - This function accepts a string containing a CSS selector which is then used to match a set of elements. + /// 2: $(html) - Create DOM elements on-the-fly from the provided String of raw HTML. + /// 3: $(elements) - Wrap jQuery functionality around a single or multiple DOM Element(s). + /// 4: $(callback) - A shorthand for $(document).ready(). + /// + /// + /// 1: expression - An expression to search with. + /// 2: html - A string of HTML to create on the fly. + /// 3: elements - DOM element(s) to be encapsulated by a jQuery object. + /// 4: callback - The function to execute when the DOM is ready. + /// + /// + /// 1: context - A DOM Element, Document or jQuery to use as context. + /// + /// + /// The DOM node context originally passed to jQuery() (if none was passed then context will be equal to the document). + /// + /// + /// A selector representing selector originally passed to jQuery(). + /// + /// + + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context ); + }, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, + // Is it a simple selector + isSimple = /^.[^:#\[\.,]*$/; + +jQuery.fn = jQuery.prototype = { + init: function( selector, context ) { + /// + /// 1: $(expression, context) - This function accepts a string containing a CSS selector which is then used to match a set of elements. + /// 2: $(html) - Create DOM elements on-the-fly from the provided String of raw HTML. + /// 3: $(elements) - Wrap jQuery functionality around a single or multiple DOM Element(s). + /// 4: $(callback) - A shorthand for $(document).ready(). + /// + /// + /// 1: expression - An expression to search with. + /// 2: html - A string of HTML to create on the fly. + /// 3: elements - DOM element(s) to be encapsulated by a jQuery object. + /// 4: callback - The function to execute when the DOM is ready. + /// + /// + /// 1: context - A DOM Element, Document or jQuery to use as context. + /// + /// + + // Make sure that a selection was provided + selector = selector || document; + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this[0] = selector; + this.length = 1; + this.context = selector; + return this; + } + // Handle HTML strings + if (typeof selector === "string") { + // Are we dealing with HTML string or an ID? + var match = quickExpr.exec(selector); + + // Verify a match, and that no context was specified for #id + if (match && (match[1] || !context)) { + + // HANDLE: $(html) -> $(array) + if (match[1]) + selector = jQuery.clean([match[1]], context); + + // HANDLE: $("#id") + else { + var elem = document.getElementById(match[3]); + + // Handle the case where IE and Opera return items + // by name instead of ID + if (elem && elem.id != match[3]) + return jQuery().find(selector); + + // Otherwise, we inject the element directly into the jQuery object + var ret = jQuery(elem || []); + ret.context = document; + ret.selector = selector; + return ret; + } + + // HANDLE: $(expr, [context]) + // (which is just equivalent to: $(content).find(expr) + } else + return jQuery(context).find(selector); + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) + return jQuery( document ).ready( selector ); + + // Make sure that old selector state is passed along + if ( selector.selector && selector.context ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return this.setArray(jQuery.isArray( selector ) ? + selector : + jQuery.makeArray(selector)); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.3.2", + + // The number of elements contained in the matched element set + size: function() { + /// + /// The number of elements currently matched. + /// Part of Core + /// + /// + + return this.length; + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + /// + /// Access a single matched element. num is used to access the + /// Nth element matched. + /// Part of Core + /// + /// + /// + /// Access the element in the Nth position. + /// + + return num == undefined ? + + // Return a 'clean' array + Array.prototype.slice.call( this ) : + + // Return just the object + this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + /// + /// Set the jQuery object to an array of elements, while maintaining + /// the stack. + /// Part of Core + /// + /// + /// + /// An array of elements + /// + + // Build a new jQuery matched element set + var ret = jQuery( elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) + ret.selector = this.selector + (this.selector ? " " : "") + selector; + else if ( name ) + ret.selector = this.selector + "." + name + "(" + selector + ")"; + + // Return the newly-formed element set + return ret; + }, + + // Force the current matched set of elements to become + // the specified array of elements (destroying the stack in the process) + // You should use pushStack() in order to do this, but maintain the stack + setArray: function( elems ) { + /// + /// Set the jQuery object to an array of elements. This operation is + /// completely destructive - be sure to use .pushStack() if you wish to maintain + /// the jQuery stack. + /// Part of Core + /// + /// + /// + /// An array of elements + /// + + // Resetting the length to 0, then using the native Array push + // is a super-fast way to populate an object with array-like properties + this.length = 0; + Array.prototype.push.apply( this, elems ); + + return this; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + /// + /// Execute a function within the context of every matched element. + /// This means that every time the passed-in function is executed + /// (which is once for every element matched) the 'this' keyword + /// points to the specific element. + /// Additionally, the function, when executed, is passed a single + /// argument representing the position of the element in the matched + /// set. + /// Part of Core + /// + /// + /// + /// A function to execute + /// + + return jQuery.each( this, callback, args ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + /// + /// Searches every matched element for the object and returns + /// the index of the element, if found, starting with zero. + /// Returns -1 if the object wasn't found. + /// Part of Core + /// + /// + /// + /// Object to search for + /// + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem && elem.jquery ? elem[0] : elem + , this ); + }, + + attr: function( name, value, type ) { + /// + /// Set a single property to a computed value, on all matched elements. + /// Instead of a value, a function is provided, that computes the value. + /// Part of DOM/Attributes + /// + /// + /// + /// The name of the property to set. + /// + /// + /// A function returning the value to set. + /// + + var options = name; + + // Look for the case where we're accessing a style value + if ( typeof name === "string" ) + if ( value === undefined ) + return this[0] && jQuery[ type || "attr" ]( this[0], name ); + + else { + options = {}; + options[ name ] = value; + } + + // Check to see if we're setting style values + return this.each(function(i){ + // Set all the styles + for ( name in options ) + jQuery.attr( + type ? + this.style : + this, + name, jQuery.prop( this, options[ name ], type, i, name ) + ); + }); + }, + + css: function( key, value ) { + /// + /// Set a single style property to a value, on all matched elements. + /// If a number is provided, it is automatically converted into a pixel value. + /// Part of CSS + /// + /// + /// + /// The name of the property to set. + /// + /// + /// The value to set the property to. + /// + + // ignore negative width and height values + if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) + value = undefined; + return this.attr( key, value, "curCSS" ); + }, + + text: function( text ) { + /// + /// Set the text contents of all matched elements. + /// Similar to html(), but escapes HTML (replace "<" and ">" with their + /// HTML entities). + /// Part of DOM/Attributes + /// + /// + /// + /// The text value to set the contents of the element to. + /// + + if ( typeof text !== "object" && text != null ) + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); + + var ret = ""; + + jQuery.each( text || this, function(){ + jQuery.each( this.childNodes, function(){ + if ( this.nodeType != 8 ) + ret += this.nodeType != 1 ? + this.nodeValue : + jQuery.fn.text( [ this ] ); + }); + }); + + return ret; + }, + + wrapAll: function( html ) { + /// + /// Wrap all matched elements with a structure of other elements. + /// This wrapping process is most useful for injecting additional + /// stucture into a document, without ruining the original semantic + /// qualities of a document. + /// This works by going through the first element + /// provided and finding the deepest ancestor element within its + /// structure - it is that element that will en-wrap everything else. + /// This does not work with elements that contain text. Any necessary text + /// must be added after the wrapping is done. + /// Part of DOM/Manipulation + /// + /// + /// + /// A DOM element that will be wrapped around the target. + /// + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).clone(); + + if ( this[0].parentNode ) + wrap.insertBefore( this[0] ); + + wrap.map(function(){ + var elem = this; + + while ( elem.firstChild ) + elem = elem.firstChild; + + return elem; + }).append(this); + } + + return this; + }, + + wrapInner: function( html ) { + /// + /// Wraps the inner child contents of each matched elemenht (including text nodes) with an HTML structure. + /// + /// + /// A string of HTML or a DOM element that will be wrapped around the target contents. + /// + /// + + return this.each(function(){ + jQuery( this ).contents().wrapAll( html ); + }); + }, + + wrap: function( html ) { + /// + /// Wrap all matched elements with a structure of other elements. + /// This wrapping process is most useful for injecting additional + /// stucture into a document, without ruining the original semantic + /// qualities of a document. + /// This works by going through the first element + /// provided and finding the deepest ancestor element within its + /// structure - it is that element that will en-wrap everything else. + /// This does not work with elements that contain text. Any necessary text + /// must be added after the wrapping is done. + /// Part of DOM/Manipulation + /// + /// + /// + /// A DOM element that will be wrapped around the target. + /// + + return this.each(function(){ + jQuery( this ).wrapAll( html ); + }); + }, + + append: function() { + /// + /// Append content to the inside of every matched element. + /// This operation is similar to doing an appendChild to all the + /// specified elements, adding them into the document. + /// Part of DOM/Manipulation + /// + /// + /// + /// Content to append to the target + /// + + return this.domManip(arguments, true, function(elem){ + if (this.nodeType == 1) + this.appendChild( elem ); + }); + }, + + prepend: function() { + /// + /// Prepend content to the inside of every matched element. + /// This operation is the best way to insert elements + /// inside, at the beginning, of all matched elements. + /// Part of DOM/Manipulation + /// + /// + /// + /// Content to prepend to the target. + /// + + return this.domManip(arguments, true, function(elem){ + if (this.nodeType == 1) + this.insertBefore( elem, this.firstChild ); + }); + }, + + before: function() { + /// + /// Insert content before each of the matched elements. + /// Part of DOM/Manipulation + /// + /// + /// + /// Content to insert before each target. + /// + + return this.domManip(arguments, false, function(elem){ + this.parentNode.insertBefore( elem, this ); + }); + }, + + after: function() { + /// + /// Insert content after each of the matched elements. + /// Part of DOM/Manipulation + /// + /// + /// + /// Content to insert after each target. + /// + + return this.domManip(arguments, false, function(elem){ + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + }, + + end: function() { + /// + /// End the most recent 'destructive' operation, reverting the list of matched elements + /// back to its previous state. After an end operation, the list of matched elements will + /// revert to the last state of matched elements. + /// If there was no destructive operation before, an empty set is returned. + /// Part of DOM/Traversing + /// + /// + + return this.prevObject || jQuery( [] ); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: [].push, + sort: [].sort, + splice: [].splice, + + find: function( selector ) { + /// + /// Searches for all elements that match the specified expression. + /// This method is a good way to find additional descendant + /// elements with which to process. + /// All searching is done using a jQuery expression. The expression can be + /// written using CSS 1-3 Selector syntax, or basic XPath. + /// Part of DOM/Traversing + /// + /// + /// + /// An expression to search with. + /// + /// + + if ( this.length === 1 ) { + var ret = this.pushStack( [], "find", selector ); + ret.length = 0; + jQuery.find( selector, this[0], ret ); + return ret; + } else { + return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){ + return jQuery.find( selector, elem ); + })), "find", selector ); + } + }, + + clone: function( events ) { + /// + /// Clone matched DOM Elements and select the clones. + /// This is useful for moving copies of the elements to another + /// location in the DOM. + /// Part of DOM/Manipulation + /// + /// + /// + /// (Optional) Set to false if you don't want to clone all descendant nodes, in addition to the element itself. + /// + + // Do the clone + var ret = this.map(function(){ + if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { + // IE copies events bound via attachEvent when + // using cloneNode. Calling detachEvent on the + // clone will also remove the events from the orignal + // In order to get around this, we use innerHTML. + // Unfortunately, this means some modifications to + // attributes in IE that are actually only stored + // as properties will not be copied (such as the + // the name attribute on an input). + var html = this.outerHTML; + if ( !html ) { + var div = this.ownerDocument.createElement("div"); + div.appendChild( this.cloneNode(true) ); + html = div.innerHTML; + } + + return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; + } else + return this.cloneNode(true); + }); + + // Copy the events from the original to the clone + if ( events === true ) { + var orig = this.find("*").andSelf(), i = 0; + + ret.find("*").andSelf().each(function(){ + if ( this.nodeName !== orig[i].nodeName ) + return; + + var events = jQuery.data( orig[i], "events" ); + + for ( var type in events ) { + for ( var handler in events[ type ] ) { + jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); + } + } + + i++; + }); + } + + // Return the cloned set + return ret; + }, + + filter: function( selector ) { + /// + /// Removes all elements from the set of matched elements that do not + /// pass the specified filter. This method is used to narrow down + /// the results of a search. + /// }) + /// Part of DOM/Traversing + /// + /// + /// + /// A function to use for filtering + /// + /// + + return this.pushStack( + jQuery.isFunction( selector ) && + jQuery.grep(this, function(elem, i){ + return selector.call( elem, i ); + }) || + + jQuery.multiFilter( selector, jQuery.grep(this, function(elem){ + return elem.nodeType === 1; + }) ), "filter", selector ); + }, + + closest: function( selector ) { + /// + /// Get a set of elements containing the closest parent element that matches the specified selector, the starting element included. + /// + /// + /// + /// An expression to filter the elements with. + /// + /// + + var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null, + closer = 0; + + return this.map(function(){ + var cur = this; + while ( cur && cur.ownerDocument ) { + if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) { + jQuery.data(cur, "closest", closer); + return cur; + } + cur = cur.parentNode; + closer++; + } + }); + }, + + not: function( selector ) { + /// + /// Removes any elements inside the array of elements from the set + /// of matched elements. This method is used to remove one or more + /// elements from a jQuery object. + /// Part of DOM/Traversing + /// + /// + /// A set of elements to remove from the jQuery set of matched elements. + /// + /// + + if ( typeof selector === "string" ) + // test special case where just one selector is passed in + if ( isSimple.test( selector ) ) + return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector ); + else + selector = jQuery.multiFilter( selector, this ); + + var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; + return this.filter(function() { + return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; + }); + }, + + add: function( selector ) { + /// + /// Adds one or more Elements to the set of matched elements. + /// Part of DOM/Traversing + /// + /// + /// One or more Elements to add + /// + /// + + return this.pushStack( jQuery.unique( jQuery.merge( + this.get(), + typeof selector === "string" ? + jQuery( selector ) : + jQuery.makeArray( selector ) + ))); + }, + + is: function( selector ) { + /// + /// Checks the current selection against an expression and returns true, + /// if at least one element of the selection fits the given expression. + /// Does return false, if no element fits or the expression is not valid. + /// filter(String) is used internally, therefore all rules that apply there + /// apply here, too. + /// Part of DOM/Traversing + /// + /// + /// + /// The expression with which to filter + /// + + return !!selector && jQuery.multiFilter( selector, this ).length > 0; + }, + + hasClass: function( selector ) { + /// + /// Checks the current selection against a class and returns whether at least one selection has a given class. + /// + /// The class to check against + /// True if at least one element in the selection has the class, otherwise false. + + return !!selector && this.is( "." + selector ); + }, + + val: function( value ) { + /// + /// Set the value of every matched element. + /// Part of DOM/Attributes + /// + /// + /// + /// Set the property to the specified value. + /// + + if ( value === undefined ) { + var elem = this[0]; + + if ( elem ) { + if( jQuery.nodeName( elem, 'option' ) ) + return (elem.attributes.value || {}).specified ? elem.value : elem.text; + + // We need to handle select boxes special + if ( jQuery.nodeName( elem, "select" ) ) { + var index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type == "select-one"; + + // Nothing was selected + if ( index < 0 ) + return null; + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + if ( option.selected ) { + // Get the specifc value for the option + value = jQuery(option).val(); + + // We don't need an array for one selects + if ( one ) + return value; + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + } + + // Everything else, we just grab the value + return (elem.value || "").replace(/\r/g, ""); + + } + + return undefined; + } + + if ( typeof value === "number" ) + value += ''; + + return this.each(function(){ + if ( this.nodeType != 1 ) + return; + + if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) ) + this.checked = (jQuery.inArray(this.value, value) >= 0 || + jQuery.inArray(this.name, value) >= 0); + + else if ( jQuery.nodeName( this, "select" ) ) { + var values = jQuery.makeArray(value); + + jQuery( "option", this ).each(function(){ + this.selected = (jQuery.inArray( this.value, values ) >= 0 || + jQuery.inArray( this.text, values ) >= 0); + }); + + if ( !values.length ) + this.selectedIndex = -1; + + } else + this.value = value; + }); + }, + + html: function( value ) { + /// + /// Set the html contents of every matched element. + /// This property is not available on XML documents. + /// Part of DOM/Attributes + /// + /// + /// + /// Set the html contents to the specified value. + /// + + return value === undefined ? + (this[0] ? + this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : + null) : + this.empty().append( value ); + }, + + replaceWith: function( value ) { + /// + /// Replaces all matched element with the specified HTML or DOM elements. + /// + /// + /// The content with which to replace the matched elements. + /// + /// The element that was just replaced. + + return this.after( value ).remove(); + }, + + eq: function( i ) { + /// + /// Reduce the set of matched elements to a single element. + /// The position of the element in the set of matched elements + /// starts at 0 and goes to length - 1. + /// Part of Core + /// + /// + /// + /// pos The index of the element that you wish to limit to. + /// + + return this.slice( i, +i + 1 ); + }, + + slice: function() { + /// + /// Selects a subset of the matched elements. Behaves exactly like the built-in Array slice method. + /// + /// Where to start the subset (0-based). + /// Where to end the subset (not including the end element itself). + /// If omitted, ends at the end of the selection + /// The sliced elements + + return this.pushStack( Array.prototype.slice.apply( this, arguments ), + "slice", Array.prototype.slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + /// + /// This member is internal. + /// + /// + /// + + return this.pushStack( jQuery.map(this, function(elem, i){ + return callback.call( elem, i, elem ); + })); + }, + + andSelf: function() { + /// + /// Adds the previous selection to the current selection. + /// + /// + + return this.add( this.prevObject ); + }, + + domManip: function( args, table, callback ) { + /// + /// Args + /// + /// + /// Insert TBODY in TABLEs if one is not found. + /// + /// + /// If dir<0, process args in reverse order. + /// + /// + /// The function doing the DOM manipulation. + /// + /// + /// + /// Part of Core + /// + + if ( this[0] ) { + var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), + scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), + first = fragment.firstChild; + + if ( first ) + for ( var i = 0, l = this.length; i < l; i++ ) + callback.call( root(this[i], first), this.length > 1 || i > 0 ? + fragment.cloneNode(true) : fragment ); + + if ( scripts ) + jQuery.each( scripts, evalScript ); + } + + return this; + + function root( elem, cur ) { + return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ? + (elem.getElementsByTagName("tbody")[0] || + elem.appendChild(elem.ownerDocument.createElement("tbody"))) : + elem; + } + } +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +function evalScript( i, elem ) { + /// + /// This method is internal. + /// + /// + + if ( elem.src ) + jQuery.ajax({ + url: elem.src, + async: false, + dataType: "script" + }); + + else + jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); + + if ( elem.parentNode ) + elem.parentNode.removeChild( elem ); +} + +function now(){ + /// + /// Gets the current date. + /// + /// The current date. + return +new Date; +} + +jQuery.extend = jQuery.fn.extend = function() { + /// + /// Extend one object with one or more others, returning the original, + /// modified, object. This is a great utility for simple inheritance. + /// jQuery.extend(settings, options); + /// var settings = jQuery.extend({}, defaults, options); + /// Part of JavaScript + /// + /// + /// The object to extend + /// + /// + /// The object that will be merged into the first. + /// + /// + /// (optional) More objects to merge into the first + /// + /// + + // copy reference to target object + var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) + target = {}; + + // extend jQuery itself if only one argument is passed + if ( length == i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) + // Extend the base object + for ( var name in options ) { + var src = target[ name ], copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) + continue; + + // Recurse if we're merging object values + if ( deep && copy && typeof copy === "object" && !copy.nodeType ) + target[ name ] = jQuery.extend( deep, + // Never move original objects, clone them + src || ( copy.length != null ? [ ] : { } ) + , copy ); + + // Don't bring in undefined values + else if ( copy !== undefined ) + target[ name ] = copy; + + } + + // Return the modified object + return target; +}; + +// exclude the following css properties to add px +var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, + // cache defaultView + defaultView = document.defaultView || {}, + toString = Object.prototype.toString; + +jQuery.extend({ + noConflict: function( deep ) { + /// + /// Run this function to give control of the $ variable back + /// to whichever library first implemented it. This helps to make + /// sure that jQuery doesn't conflict with the $ object + /// of other libraries. + /// By using this function, you will only be able to access jQuery + /// using the 'jQuery' variable. For example, where you used to do + /// $("div p"), you now must do jQuery("div p"). + /// Part of Core + /// + /// + + window.$ = _$; + + if ( deep ) + window.jQuery = _jQuery; + + return jQuery; + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + /// + /// Determines if the parameter passed is a function. + /// + /// The object to check + /// True if the parameter is a function; otherwise false. + + return toString.call(obj) === "[object Function]"; + }, + + isArray: function(obj) { + /// + /// Determine if the parameter passed is an array. + /// + /// Object to test whether or not it is an array. + /// True if the parameter is a function; otherwise false. + + return toString.call(obj) === "[object Array]"; + }, + + // check if an element is in a (or is an) XML document + isXMLDoc: function( elem ) { + /// + /// Determines if the parameter passed is an XML document. + /// + /// The object to test + /// True if the parameter is an XML document; otherwise false. + + return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || + !!elem.ownerDocument && jQuery.isXMLDoc(elem.ownerDocument); + }, + + // Evalulates a script in a global context + globalEval: function( data ) { + /// + /// Internally evaluates a script in a global context. + /// + /// + + if ( data && /\S/.test(data) ) { + // Inspired by code by Andrea Giammarchi + // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html + var head = document.getElementsByTagName("head")[0] || document.documentElement, + script = document.createElement("script"); + + script.type = "text/javascript"; + if ( jQuery.support.scriptEval ) + script.appendChild( document.createTextNode( data ) ); + else + script.text = data; + + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709). + head.insertBefore( script, head.firstChild ); + head.removeChild( script ); + } + }, + + nodeName: function( elem, name ) { + /// + /// Checks whether the specified element has the specified DOM node name. + /// + /// The element to examine + /// The node name to check + /// True if the specified node name matches the node's DOM node name; otherwise false + + return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + /// + /// A generic iterator function, which can be used to seemlessly + /// iterate over both objects and arrays. This function is not the same + /// as $().each() - which is used to iterate, exclusively, over a jQuery + /// object. This function can be used to iterate over anything. + /// The callback has two arguments:the key (objects) or index (arrays) as first + /// the first, and the value as the second. + /// Part of JavaScript + /// + /// + /// The object, or array, to iterate over. + /// + /// + /// The function that will be executed on every object. + /// + /// + + var name, i = 0, length = object.length; + + if ( args ) { + if ( length === undefined ) { + for ( name in object ) + if ( callback.apply( object[ name ], args ) === false ) + break; + } else + for ( ; i < length; ) + if ( callback.apply( object[ i++ ], args ) === false ) + break; + + // A special, fast, case for the most common use of each + } else { + if ( length === undefined ) { + for ( name in object ) + if ( callback.call( object[ name ], name, object[ name ] ) === false ) + break; + } else + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} + } + + return object; + }, + + prop: function( elem, value, type, i, name ) { + /// + /// This method is internal. + /// + /// + // This member is not documented within the jQuery API: http://docs.jquery.com/action/edit/Internals/jQuery.prop + + // Handle executable functions + if ( jQuery.isFunction( value ) ) + value = value.call( elem, i ); + + // Handle passing in a number to a CSS property + return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ? + value + "px" : + value; + }, + + className: { + // internal only, use addClass("class") + add: function( elem, classNames ) { + /// + /// Internal use only; use addClass('class') + /// + /// + + jQuery.each((classNames || "").split(/\s+/), function(i, className){ + if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) + elem.className += (elem.className ? " " : "") + className; + }); + }, + + // internal only, use removeClass("class") + remove: function( elem, classNames ) { + /// + /// Internal use only; use removeClass('class') + /// + /// + + if (elem.nodeType == 1) + elem.className = classNames !== undefined ? + jQuery.grep(elem.className.split(/\s+/), function(className){ + return !jQuery.className.has( classNames, className ); + }).join(" ") : + ""; + }, + + // internal only, use hasClass("class") + has: function( elem, className ) { + /// + /// Internal use only; use hasClass('class') + /// + /// + + return elem && jQuery.inArray(className, (elem.className || elem).toString().split(/\s+/)) > -1; + } + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback ) { + /// + /// Swap in/out style options. + /// + + var old = {}; + // Remember the old values, and insert the new ones + for ( var name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + callback.call( elem ); + + // Revert the old values + for ( var name in options ) + elem.style[ name ] = old[ name ]; + }, + + css: function( elem, name, force, extra ) { + /// + /// This method is internal only. + /// + /// + // This method is undocumented in jQuery API: http://docs.jquery.com/action/edit/Internals/jQuery.css + + if ( name == "width" || name == "height" ) { + var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; + + function getWH() { + val = name == "width" ? elem.offsetWidth : elem.offsetHeight; + + if ( extra === "border" ) + return; + + jQuery.each( which, function() { + if ( !extra ) + val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; + if ( extra === "margin" ) + val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0; + else + val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; + }); + } + + if ( elem.offsetWidth !== 0 ) + getWH(); + else + jQuery.swap( elem, props, getWH ); + + return Math.max(0, Math.round(val)); + } + + return jQuery.curCSS( elem, name, force ); + }, + + curCSS: function( elem, name, force ) { + /// + /// This method is internal only. + /// + /// + // This method is undocumented in jQuery API: http://docs.jquery.com/action/edit/Internals/jQuery.curCSS + + var ret, style = elem.style; + + // We need to handle opacity special in IE + if ( name == "opacity" && !jQuery.support.opacity ) { + ret = jQuery.attr( style, "opacity" ); + + return ret == "" ? + "1" : + ret; + } + + // Make sure we're using the right name for getting the float value + if ( name.match( /float/i ) ) + name = styleFloat; + + if ( !force && style && style[ name ] ) + ret = style[ name ]; + + else if ( defaultView.getComputedStyle ) { + + // Only "float" is needed here + if ( name.match( /float/i ) ) + name = "float"; + + name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); + + var computedStyle = defaultView.getComputedStyle( elem, null ); + + if ( computedStyle ) + ret = computedStyle.getPropertyValue( name ); + + // We should always get a number back from opacity + if ( name == "opacity" && ret == "" ) + ret = "1"; + + } else if ( elem.currentStyle ) { + var camelCase = name.replace(/\-(\w)/g, function(all, letter){ + return letter.toUpperCase(); + }); + + ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { + // Remember the original values + var left = style.left, rsLeft = elem.runtimeStyle.left; + + // Put in the new values to get a computed value out + elem.runtimeStyle.left = elem.currentStyle.left; + style.left = ret || 0; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + elem.runtimeStyle.left = rsLeft; + } + } + + return ret; + }, + + clean: function( elems, context, fragment ) { + /// + /// This method is internal only. + /// + /// + // This method is undocumented in the jQuery API: http://docs.jquery.com/action/edit/Internals/jQuery.clean + + + context = context || document; + + // !context.createElement fails in IE with an error but returns typeof 'object' + if ( typeof context.createElement === "undefined" ) + context = context.ownerDocument || context[0] && context[0].ownerDocument || document; + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) { + var match = /^<(\w+)\s*\/?>$/.exec(elems[0]); + if ( match ) + return [ context.createElement( match[1] ) ]; + } + + var ret = [], scripts = [], div = context.createElement("div"); + + jQuery.each(elems, function(i, elem){ + if ( typeof elem === "number" ) + elem += ''; + + if ( !elem ) + return; + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ + return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? + all : + front + ">"; + }); + + // Trim whitespace, otherwise indexOf won't work as expected + var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); + + var wrap = + // option or optgroup + !tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && + [ 1, "", "
" ] || + + !tags.indexOf("", "" ] || + + // matched above + (!tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + // IE can't serialize and