Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Extensions / ClientServices / Providers / ClientData.cs / 1305376 / ClientData.cs
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------------
namespace System.Web.ClientServices.Providers
{
using System;
using System.Security;
using System.Security.Permissions;
using System.Security.AccessControl;
using System.Globalization;
using System.Data;
using System.Data.Common;
using System.Data.OleDb;
using System.IO;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Xml.Serialization;
using System.Diagnostics.CodeAnalysis;
using System.Xml.Schema;
using System.Xml;
using System.Collections.Specialized;
using System.IO.IsolatedStorage;
internal class ClientData
{
internal enum ClientDateStoreOrderEnum {
LastLoggedInUserName = 0,
LastLoggedInDateUtc = 1,
PasswordHash = 2,
PasswordSalt = 3,
Roles = 4,
RolesCachedDateUtc = 5,
SettingsNames = 6,
SettingsStoredAs = 7,
SettingsValues = 8,
SettingsNeedReset = 9,
SettingsCacheIsMoreFresh = 10,
CookieNames = 11,
CookieValues = 12
}
private const int _NumStoredValues = 13;
private static string[] _StoredValueNames = new string[_NumStoredValues] {
"LastLoggedInUserName",
"LastLoggedInDateUtc",
"PasswordHash",
"PasswordSalt",
"Roles",
"RolesCachedDateUtc",
"SettingsNames",
"SettingsStoredAs",
"SettingsValues",
"SettingsNeedReset",
"SettingsCacheIsMoreFresh",
"CookieNames",
"CookieValues"};
private object[] _StoredValues = new object[_NumStoredValues] {
"",
DateTime.UtcNow.AddYears(-1),
string.Empty,
string.Empty,
new string[0],
DateTime.UtcNow.AddYears(-1),
new string[0],
new string[0],
new string[0],
false,
false,
new string[0],
new string[0]};
private ClientData() { }
private ClientData(XmlReader reader)
{
reader.ReadStartElement("ClientData");
for (int iter = 0; iter < _NumStoredValues; iter++) {
reader.ReadStartElement(_StoredValueNames[iter]);
if (_StoredValues[iter] is string)
_StoredValues[iter] = reader.ReadContentAsString();
else if (_StoredValues[iter] is DateTime) {
string s = reader.ReadContentAsString();
long l = long.Parse(s, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
_StoredValues[iter] = DateTime.FromFileTimeUtc(l);
} else if (_StoredValues[iter] is bool) {
string s = reader.ReadContentAsString();
_StoredValues[iter] = !(string.IsNullOrEmpty(s) || s != "1");
} else {
_StoredValues[iter] = ReadStringArray(reader);
}
reader.ReadEndElement();
}
reader.ReadEndElement();
}
private static string[] ReadStringArray(XmlReader reader)
{
//string count = reader.GetAttribute("count");
//if (string.IsNullOrEmpty(count))
// return new string[0];
StringCollection sc = new StringCollection();
while (reader.IsStartElement())
{
reader.ReadStartElement("item");
sc.Add(reader.ReadContentAsString());
reader.ReadEndElement();
}
string[] retValue = new string[sc.Count];
sc.CopyTo(retValue, 0);
return retValue;
}
private static void WriteStringArray(XmlWriter writer, string [] arrToWrite)
{
//writer.WriteAttributeString("count", arrToWrite.Length.ToString());
if (arrToWrite.Length == 0)
writer.WriteValue(string.Empty);
for (int iter = 0; iter < arrToWrite.Length; iter++) {
writer.WriteStartElement("item");
writer.WriteValue((arrToWrite[iter]==null) ? string.Empty : arrToWrite[iter]);
writer.WriteEndElement();
}
}
// App data
internal string LastLoggedInUserName { get { return (string)_StoredValues[(int)ClientDateStoreOrderEnum.LastLoggedInUserName]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.LastLoggedInUserName] = value; } }
internal DateTime LastLoggedInDateUtc { get { return (DateTime)_StoredValues[(int)ClientDateStoreOrderEnum.LastLoggedInDateUtc]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.LastLoggedInDateUtc] = value; } }
// Membership data
internal string PasswordHash { get { return (string)_StoredValues[(int)ClientDateStoreOrderEnum.PasswordHash]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.PasswordHash] = value; } }
internal string PasswordSalt { get { return (string)_StoredValues[(int)ClientDateStoreOrderEnum.PasswordSalt]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.PasswordSalt] = value; } }
// Roles data
internal string[] Roles { get { return (string[])_StoredValues[(int)ClientDateStoreOrderEnum.Roles]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.Roles] = value; } }
internal DateTime RolesCachedDateUtc { get { return (DateTime)_StoredValues[(int)ClientDateStoreOrderEnum.RolesCachedDateUtc]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.RolesCachedDateUtc] = value; } }
// Settings data
internal string[] SettingsNames { get { return (string[])_StoredValues[(int)ClientDateStoreOrderEnum.SettingsNames]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.SettingsNames] = value; } }
internal string[] SettingsStoredAs{ get { return (string[])_StoredValues[(int)ClientDateStoreOrderEnum.SettingsStoredAs]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.SettingsStoredAs] = value; } }
internal string[] SettingsValues { get { return (string[])_StoredValues[(int)ClientDateStoreOrderEnum.SettingsValues]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.SettingsValues] = value; } }
internal bool SettingsNeedReset { get { return (bool)_StoredValues[(int)ClientDateStoreOrderEnum.SettingsNeedReset]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.SettingsNeedReset] = value; } }
internal bool SettingsCacheIsMoreFresh { get { return (bool)_StoredValues[(int)ClientDateStoreOrderEnum.SettingsCacheIsMoreFresh]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.SettingsCacheIsMoreFresh] = value; } }
// Cookie data
internal string[] CookieNames { get { return (string[])_StoredValues[(int)ClientDateStoreOrderEnum.CookieNames]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.CookieNames] = value; } }
internal string[] CookieValues { get { return (string[])_StoredValues[(int)ClientDateStoreOrderEnum.CookieValues]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.CookieValues] = value; } }
// Filename
private string FileName = string.Empty;
private bool UsingIsolatedStorage = false;
private const string _IsolatedDir = "System.Web.Extensions.ClientServices.ClientData";
internal void Save()
{
if (!UsingIsolatedStorage) {
using (XmlWriter writer = XmlWriter.Create(FileName)) {
Save(writer);
}
} else {
using(IsolatedStorageFile f = IsolatedStorageFile.GetUserStoreForAssembly()) {
if (f.GetDirectoryNames(_IsolatedDir).Length == 0)
f.CreateDirectory(_IsolatedDir);
using(IsolatedStorageFileStream fs = new IsolatedStorageFileStream(FileName, FileMode.Create, f)) {
using (XmlWriter writer = XmlWriter.Create(fs)) {
Save(writer);
}
}
}
}
}
private void Save(XmlWriter writer)
{
writer.WriteStartElement("ClientData");
for (int iter = 0; iter < _NumStoredValues; iter++) {
writer.WriteStartElement(_StoredValueNames[iter]);
if (_StoredValues[iter] == null) {
writer.WriteValue(string.Empty);
} else if (_StoredValues[iter] is string) {
writer.WriteValue(_StoredValues[iter]);
} else if (_StoredValues[iter] is bool) {
writer.WriteValue(((bool)_StoredValues[iter]) ? "1" : "0");
} else if (_StoredValues[iter] is DateTime) {
writer.WriteValue(((DateTime)_StoredValues[iter]).ToFileTimeUtc().ToString("X", CultureInfo.InvariantCulture));
} else {
WriteStringArray(writer, (string[])_StoredValues[iter]);
}
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.Flush();
}
internal static ClientData Load(string username, bool useIsolatedStorage)
{
ClientData cd = null;
string fileName = null;
if (useIsolatedStorage) {
fileName = _IsolatedDir + "\\" + SqlHelper.GetPartialDBFileName(username, ".clientdata");
try {
using(IsolatedStorageFile f = IsolatedStorageFile.GetUserStoreForAssembly()) {
using(IsolatedStorageFileStream fs = new IsolatedStorageFileStream(fileName, FileMode.Open, f)) {
using (XmlReader xr = XmlReader.Create(fs)) {
cd = new ClientData(xr);
}
}
}
} catch {} // ignore exceptions
} else {
fileName = SqlHelper.GetFullDBFileName(username, ".clientdata");
try {
if (File.Exists(fileName)) {
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) {
using (XmlReader xr = XmlReader.Create(fs)) {
cd = new ClientData(xr);
}
}
}
} catch {} // ignore exceptions
}
if (cd == null)
cd = new ClientData();
cd.UsingIsolatedStorage = useIsolatedStorage;
cd.FileName = fileName;
return cd;
}
}
internal static class ClientDataManager
{
static private ClientData _applicationClientData;
static private ClientData _userClientData;
static private string _curUserName;
internal static ClientData GetAppClientData(bool useIsolatedStore)
{
if (_applicationClientData == null)
_applicationClientData = ClientData.Load(null, useIsolatedStore);
return _applicationClientData;
}
internal static ClientData GetUserClientData(string username, bool useIsolatedStore)
{
if (username != _curUserName) {
_curUserName = username;
_userClientData = ClientData.Load(username, useIsolatedStore);
}
return _userClientData;
}
internal static string GetCookie(string username, string cookieName, bool useIsolatedStore)
{
ClientData cd = GetUserClientData(username, useIsolatedStore);
if (cd.CookieNames == null) {
cd.CookieNames = new string[0];
cd.CookieValues = new string[0];
return null;
}
for(int iter=0; iter
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------------
namespace System.Web.ClientServices.Providers
{
using System;
using System.Security;
using System.Security.Permissions;
using System.Security.AccessControl;
using System.Globalization;
using System.Data;
using System.Data.Common;
using System.Data.OleDb;
using System.IO;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Xml.Serialization;
using System.Diagnostics.CodeAnalysis;
using System.Xml.Schema;
using System.Xml;
using System.Collections.Specialized;
using System.IO.IsolatedStorage;
internal class ClientData
{
internal enum ClientDateStoreOrderEnum {
LastLoggedInUserName = 0,
LastLoggedInDateUtc = 1,
PasswordHash = 2,
PasswordSalt = 3,
Roles = 4,
RolesCachedDateUtc = 5,
SettingsNames = 6,
SettingsStoredAs = 7,
SettingsValues = 8,
SettingsNeedReset = 9,
SettingsCacheIsMoreFresh = 10,
CookieNames = 11,
CookieValues = 12
}
private const int _NumStoredValues = 13;
private static string[] _StoredValueNames = new string[_NumStoredValues] {
"LastLoggedInUserName",
"LastLoggedInDateUtc",
"PasswordHash",
"PasswordSalt",
"Roles",
"RolesCachedDateUtc",
"SettingsNames",
"SettingsStoredAs",
"SettingsValues",
"SettingsNeedReset",
"SettingsCacheIsMoreFresh",
"CookieNames",
"CookieValues"};
private object[] _StoredValues = new object[_NumStoredValues] {
"",
DateTime.UtcNow.AddYears(-1),
string.Empty,
string.Empty,
new string[0],
DateTime.UtcNow.AddYears(-1),
new string[0],
new string[0],
new string[0],
false,
false,
new string[0],
new string[0]};
private ClientData() { }
private ClientData(XmlReader reader)
{
reader.ReadStartElement("ClientData");
for (int iter = 0; iter < _NumStoredValues; iter++) {
reader.ReadStartElement(_StoredValueNames[iter]);
if (_StoredValues[iter] is string)
_StoredValues[iter] = reader.ReadContentAsString();
else if (_StoredValues[iter] is DateTime) {
string s = reader.ReadContentAsString();
long l = long.Parse(s, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
_StoredValues[iter] = DateTime.FromFileTimeUtc(l);
} else if (_StoredValues[iter] is bool) {
string s = reader.ReadContentAsString();
_StoredValues[iter] = !(string.IsNullOrEmpty(s) || s != "1");
} else {
_StoredValues[iter] = ReadStringArray(reader);
}
reader.ReadEndElement();
}
reader.ReadEndElement();
}
private static string[] ReadStringArray(XmlReader reader)
{
//string count = reader.GetAttribute("count");
//if (string.IsNullOrEmpty(count))
// return new string[0];
StringCollection sc = new StringCollection();
while (reader.IsStartElement())
{
reader.ReadStartElement("item");
sc.Add(reader.ReadContentAsString());
reader.ReadEndElement();
}
string[] retValue = new string[sc.Count];
sc.CopyTo(retValue, 0);
return retValue;
}
private static void WriteStringArray(XmlWriter writer, string [] arrToWrite)
{
//writer.WriteAttributeString("count", arrToWrite.Length.ToString());
if (arrToWrite.Length == 0)
writer.WriteValue(string.Empty);
for (int iter = 0; iter < arrToWrite.Length; iter++) {
writer.WriteStartElement("item");
writer.WriteValue((arrToWrite[iter]==null) ? string.Empty : arrToWrite[iter]);
writer.WriteEndElement();
}
}
// App data
internal string LastLoggedInUserName { get { return (string)_StoredValues[(int)ClientDateStoreOrderEnum.LastLoggedInUserName]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.LastLoggedInUserName] = value; } }
internal DateTime LastLoggedInDateUtc { get { return (DateTime)_StoredValues[(int)ClientDateStoreOrderEnum.LastLoggedInDateUtc]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.LastLoggedInDateUtc] = value; } }
// Membership data
internal string PasswordHash { get { return (string)_StoredValues[(int)ClientDateStoreOrderEnum.PasswordHash]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.PasswordHash] = value; } }
internal string PasswordSalt { get { return (string)_StoredValues[(int)ClientDateStoreOrderEnum.PasswordSalt]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.PasswordSalt] = value; } }
// Roles data
internal string[] Roles { get { return (string[])_StoredValues[(int)ClientDateStoreOrderEnum.Roles]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.Roles] = value; } }
internal DateTime RolesCachedDateUtc { get { return (DateTime)_StoredValues[(int)ClientDateStoreOrderEnum.RolesCachedDateUtc]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.RolesCachedDateUtc] = value; } }
// Settings data
internal string[] SettingsNames { get { return (string[])_StoredValues[(int)ClientDateStoreOrderEnum.SettingsNames]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.SettingsNames] = value; } }
internal string[] SettingsStoredAs{ get { return (string[])_StoredValues[(int)ClientDateStoreOrderEnum.SettingsStoredAs]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.SettingsStoredAs] = value; } }
internal string[] SettingsValues { get { return (string[])_StoredValues[(int)ClientDateStoreOrderEnum.SettingsValues]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.SettingsValues] = value; } }
internal bool SettingsNeedReset { get { return (bool)_StoredValues[(int)ClientDateStoreOrderEnum.SettingsNeedReset]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.SettingsNeedReset] = value; } }
internal bool SettingsCacheIsMoreFresh { get { return (bool)_StoredValues[(int)ClientDateStoreOrderEnum.SettingsCacheIsMoreFresh]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.SettingsCacheIsMoreFresh] = value; } }
// Cookie data
internal string[] CookieNames { get { return (string[])_StoredValues[(int)ClientDateStoreOrderEnum.CookieNames]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.CookieNames] = value; } }
internal string[] CookieValues { get { return (string[])_StoredValues[(int)ClientDateStoreOrderEnum.CookieValues]; } set { _StoredValues[(int)ClientDateStoreOrderEnum.CookieValues] = value; } }
// Filename
private string FileName = string.Empty;
private bool UsingIsolatedStorage = false;
private const string _IsolatedDir = "System.Web.Extensions.ClientServices.ClientData";
internal void Save()
{
if (!UsingIsolatedStorage) {
using (XmlWriter writer = XmlWriter.Create(FileName)) {
Save(writer);
}
} else {
using(IsolatedStorageFile f = IsolatedStorageFile.GetUserStoreForAssembly()) {
if (f.GetDirectoryNames(_IsolatedDir).Length == 0)
f.CreateDirectory(_IsolatedDir);
using(IsolatedStorageFileStream fs = new IsolatedStorageFileStream(FileName, FileMode.Create, f)) {
using (XmlWriter writer = XmlWriter.Create(fs)) {
Save(writer);
}
}
}
}
}
private void Save(XmlWriter writer)
{
writer.WriteStartElement("ClientData");
for (int iter = 0; iter < _NumStoredValues; iter++) {
writer.WriteStartElement(_StoredValueNames[iter]);
if (_StoredValues[iter] == null) {
writer.WriteValue(string.Empty);
} else if (_StoredValues[iter] is string) {
writer.WriteValue(_StoredValues[iter]);
} else if (_StoredValues[iter] is bool) {
writer.WriteValue(((bool)_StoredValues[iter]) ? "1" : "0");
} else if (_StoredValues[iter] is DateTime) {
writer.WriteValue(((DateTime)_StoredValues[iter]).ToFileTimeUtc().ToString("X", CultureInfo.InvariantCulture));
} else {
WriteStringArray(writer, (string[])_StoredValues[iter]);
}
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.Flush();
}
internal static ClientData Load(string username, bool useIsolatedStorage)
{
ClientData cd = null;
string fileName = null;
if (useIsolatedStorage) {
fileName = _IsolatedDir + "\\" + SqlHelper.GetPartialDBFileName(username, ".clientdata");
try {
using(IsolatedStorageFile f = IsolatedStorageFile.GetUserStoreForAssembly()) {
using(IsolatedStorageFileStream fs = new IsolatedStorageFileStream(fileName, FileMode.Open, f)) {
using (XmlReader xr = XmlReader.Create(fs)) {
cd = new ClientData(xr);
}
}
}
} catch {} // ignore exceptions
} else {
fileName = SqlHelper.GetFullDBFileName(username, ".clientdata");
try {
if (File.Exists(fileName)) {
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) {
using (XmlReader xr = XmlReader.Create(fs)) {
cd = new ClientData(xr);
}
}
}
} catch {} // ignore exceptions
}
if (cd == null)
cd = new ClientData();
cd.UsingIsolatedStorage = useIsolatedStorage;
cd.FileName = fileName;
return cd;
}
}
internal static class ClientDataManager
{
static private ClientData _applicationClientData;
static private ClientData _userClientData;
static private string _curUserName;
internal static ClientData GetAppClientData(bool useIsolatedStore)
{
if (_applicationClientData == null)
_applicationClientData = ClientData.Load(null, useIsolatedStore);
return _applicationClientData;
}
internal static ClientData GetUserClientData(string username, bool useIsolatedStore)
{
if (username != _curUserName) {
_curUserName = username;
_userClientData = ClientData.Load(username, useIsolatedStore);
}
return _userClientData;
}
internal static string GetCookie(string username, string cookieName, bool useIsolatedStore)
{
ClientData cd = GetUserClientData(username, useIsolatedStore);
if (cd.CookieNames == null) {
cd.CookieNames = new string[0];
cd.CookieValues = new string[0];
return null;
}
for(int iter=0; iter
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- VisualStyleTypesAndProperties.cs
- WSHttpTransportSecurityElement.cs
- DynamicResourceExtensionConverter.cs
- MappingMetadataHelper.cs
- BinaryObjectWriter.cs
- HttpHandler.cs
- XmlReaderSettings.cs
- RayMeshGeometry3DHitTestResult.cs
- HostProtectionException.cs
- OwnerDrawPropertyBag.cs
- EntityViewGenerationConstants.cs
- SocketAddress.cs
- ConversionContext.cs
- OnOperation.cs
- FileEnumerator.cs
- CellLabel.cs
- SubMenuStyle.cs
- TextTreeRootNode.cs
- mediaeventargs.cs
- StylusPointProperties.cs
- XmlSchemaDatatype.cs
- DataGridViewRowPostPaintEventArgs.cs
- BuilderInfo.cs
- Triplet.cs
- APCustomTypeDescriptor.cs
- FontFaceLayoutInfo.cs
- XmlTypeMapping.cs
- CompilerError.cs
- TextPenaltyModule.cs
- IdentityHolder.cs
- WebPartHeaderCloseVerb.cs
- _SecureChannel.cs
- GridViewHeaderRowPresenterAutomationPeer.cs
- TreeViewBindingsEditor.cs
- D3DImage.cs
- ApplicationDirectoryMembershipCondition.cs
- WebPartChrome.cs
- FilterElement.cs
- LinqDataSourceSelectEventArgs.cs
- Clock.cs
- Style.cs
- ScrollItemPatternIdentifiers.cs
- securitycriticaldataClass.cs
- PDBReader.cs
- CaseExpr.cs
- Restrictions.cs
- MenuItemCollectionEditor.cs
- CloudCollection.cs
- ParameterModifier.cs
- RowUpdatingEventArgs.cs
- DataBoundControl.cs
- RegexGroup.cs
- EventKeyword.cs
- AsmxEndpointPickerExtension.cs
- HandlerMappingMemo.cs
- PaintValueEventArgs.cs
- JoinElimination.cs
- HtmlInputFile.cs
- HttpRawResponse.cs
- BaseTemplatedMobileComponentEditor.cs
- ModifiableIteratorCollection.cs
- SafeBuffer.cs
- EntityDataSourceWrapperCollection.cs
- UIElementParaClient.cs
- TableLayoutColumnStyleCollection.cs
- DrawingAttributeSerializer.cs
- TemplateControl.cs
- StatusBar.cs
- RemoteWebConfigurationHostServer.cs
- TransformGroup.cs
- GlyphingCache.cs
- DocumentViewerBase.cs
- DocComment.cs
- StylusEditingBehavior.cs
- Misc.cs
- coordinatorfactory.cs
- MethodExpr.cs
- ExpressionBindings.cs
- ObjectDataSourceMethodEventArgs.cs
- HwndKeyboardInputProvider.cs
- TrackPointCollection.cs
- Pair.cs
- SystemColors.cs
- PropertyGroupDescription.cs
- ZipIOExtraFieldZip64Element.cs
- OrderByQueryOptionExpression.cs
- DataServiceContext.cs
- Visual.cs
- ListBoxChrome.cs
- Events.cs
- EdmToObjectNamespaceMap.cs
- OdbcConnectionPoolProviderInfo.cs
- SqlRowUpdatingEvent.cs
- SynchronizationValidator.cs
- _LazyAsyncResult.cs
- ToolStripSplitStackLayout.cs
- UrlPropertyAttribute.cs
- JsonSerializer.cs
- FilterElement.cs
- Odbc32.cs