SqlProfileProvider.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. //
  2. // System.Web.UI.WebControls.SqlProfileProvider.cs
  3. //
  4. // Authors:
  5. // Chris Toshok ([email protected])
  6. // Vladimir Krasnov ([email protected])
  7. //
  8. // (C) 2006 Novell, Inc (http://www.novell.com)
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. #if NET_2_0
  30. using System;
  31. using System.Data;
  32. using System.Data.Common;
  33. using System.Collections;
  34. using System.Globalization;
  35. using System.Configuration;
  36. using System.Configuration.Provider;
  37. using System.Web.Configuration;
  38. using System.Collections.Specialized;
  39. using System.IO;
  40. using System.Text;
  41. namespace System.Web.Profile
  42. {
  43. public class SqlProfileProvider : ProfileProvider
  44. {
  45. ConnectionStringSettings connectionString;
  46. DbProviderFactory factory;
  47. string applicationName;
  48. public override int DeleteInactiveProfiles (ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
  49. {
  50. using (DbConnection connection = CreateConnection ()) {
  51. DbCommand command = factory.CreateCommand ();
  52. command.Connection = connection;
  53. command.CommandType = CommandType.StoredProcedure;
  54. command.CommandText = @"aspnet_Profile_DeleteInactiveProfiles";
  55. AddParameter (command, "ApplicationName", ApplicationName);
  56. AddParameter (command, "ProfileAuthOptions", authenticationOption);
  57. AddParameter (command, "InactiveSinceDate", userInactiveSinceDate);
  58. DbParameter returnValue = AddParameter (command, null, ParameterDirection.ReturnValue, null);
  59. command.ExecuteNonQuery ();
  60. int retVal = GetReturnValue (returnValue);
  61. return retVal;
  62. }
  63. }
  64. public override int DeleteProfiles (ProfileInfoCollection profiles)
  65. {
  66. if (profiles == null)
  67. throw new ArgumentNullException ("prfoles");
  68. if (profiles.Count == 0)
  69. throw new ArgumentException ("prfoles");
  70. string [] usernames = new string [profiles.Count];
  71. int i = 0;
  72. foreach (ProfileInfo pi in profiles) {
  73. if (pi.UserName == null)
  74. throw new ArgumentNullException ("element in profiles collection is null");
  75. if (pi.UserName.Length == 0 || pi.UserName.Length > 256 || pi.UserName.IndexOf (",") != -1)
  76. throw new ArgumentException ("element in profiles collection in illegal format");
  77. usernames [i++] = pi.UserName;
  78. }
  79. return DeleteProfilesInternal (usernames);
  80. }
  81. public override int DeleteProfiles (string [] usernames)
  82. {
  83. if (usernames == null)
  84. throw new ArgumentNullException ("usernames");
  85. Hashtable users = new Hashtable ();
  86. foreach (string username in usernames) {
  87. if (username == null)
  88. throw new ArgumentNullException ("element in usernames array is null");
  89. if (username.Length == 0 || username.Length > 256 || username.IndexOf (",") != -1)
  90. throw new ArgumentException ("element in usernames array in illegal format");
  91. if (users.ContainsKey(username))
  92. throw new ArgumentException ("duplicate element in usernames array");
  93. users.Add (username, username);
  94. }
  95. return DeleteProfilesInternal (usernames);
  96. }
  97. private int DeleteProfilesInternal (string [] usernames)
  98. {
  99. using (DbConnection connection = CreateConnection ()) {
  100. DbCommand command = factory.CreateCommand ();
  101. command.Connection = connection;
  102. command.CommandType = CommandType.StoredProcedure;
  103. command.CommandText = @"aspnet_Profile_DeleteProfiles";
  104. AddParameter (command, "ApplicationName", ApplicationName);
  105. AddParameter (command, "UserNames", string.Join (",", usernames));
  106. DbParameter returnValue = AddParameter (command, null, ParameterDirection.ReturnValue, null);
  107. command.ExecuteNonQuery ();
  108. int retVal = GetReturnValue (returnValue);
  109. return retVal;
  110. }
  111. }
  112. public override ProfileInfoCollection FindInactiveProfilesByUserName (ProfileAuthenticationOption authenticationOption,
  113. string usernameToMatch,
  114. DateTime userInactiveSinceDate,
  115. int pageIndex,
  116. int pageSize,
  117. out int totalRecords)
  118. {
  119. CheckParam ("usernameToMatch", usernameToMatch, 256);
  120. if (pageIndex < 0)
  121. throw new ArgumentException("pageIndex is less than zero");
  122. if (pageSize < 1)
  123. throw new ArgumentException ("pageIndex is less than one");
  124. if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
  125. throw new ArgumentException ("pageIndex and pageSize are too large");
  126. using (DbConnection connection = CreateConnection ()) {
  127. DbCommand command = factory.CreateCommand ();
  128. command.Connection = connection;
  129. command.CommandType = CommandType.StoredProcedure;
  130. command.CommandText = @"aspnet_Profile_GetProfiles";
  131. AddParameter (command, "ApplicationName", ApplicationName);
  132. AddParameter (command, "ProfileAuthOptions", authenticationOption);
  133. AddParameter (command, "PageIndex", pageIndex);
  134. AddParameter (command, "PageSize", pageSize);
  135. AddParameter (command, "UserNameToMatch", usernameToMatch);
  136. AddParameter (command, "InactiveSinceDate", userInactiveSinceDate);
  137. using (DbDataReader reader = command.ExecuteReader ()) {
  138. return BuildProfileInfoCollection (reader, out totalRecords);
  139. }
  140. }
  141. }
  142. public override ProfileInfoCollection FindProfilesByUserName (ProfileAuthenticationOption authenticationOption,
  143. string usernameToMatch,
  144. int pageIndex,
  145. int pageSize,
  146. out int totalRecords)
  147. {
  148. CheckParam ("usernameToMatch", usernameToMatch, 256);
  149. if (pageIndex < 0)
  150. throw new ArgumentException ("pageIndex is less than zero");
  151. if (pageSize < 1)
  152. throw new ArgumentException ("pageIndex is less than one");
  153. if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
  154. throw new ArgumentException ("pageIndex and pageSize are too large");
  155. using (DbConnection connection = CreateConnection ()) {
  156. DbCommand command = factory.CreateCommand ();
  157. command.Connection = connection;
  158. command.CommandType = CommandType.StoredProcedure;
  159. command.CommandText = @"aspnet_Profile_GetProfiles";
  160. AddParameter (command, "ApplicationName", ApplicationName);
  161. AddParameter (command, "ProfileAuthOptions", authenticationOption);
  162. AddParameter (command, "PageIndex", pageIndex);
  163. AddParameter (command, "PageSize", pageSize);
  164. AddParameter (command, "UserNameToMatch", usernameToMatch);
  165. AddParameter (command, "InactiveSinceDate", null);
  166. using (DbDataReader reader = command.ExecuteReader ()) {
  167. return BuildProfileInfoCollection (reader, out totalRecords);
  168. }
  169. }
  170. }
  171. public override ProfileInfoCollection GetAllInactiveProfiles (ProfileAuthenticationOption authenticationOption,
  172. DateTime userInactiveSinceDate,
  173. int pageIndex,
  174. int pageSize,
  175. out int totalRecords)
  176. {
  177. if (pageIndex < 0)
  178. throw new ArgumentException ("pageIndex is less than zero");
  179. if (pageSize < 1)
  180. throw new ArgumentException ("pageIndex is less than one");
  181. if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
  182. throw new ArgumentException ("pageIndex and pageSize are too large");
  183. using (DbConnection connection = CreateConnection ()) {
  184. DbCommand command = factory.CreateCommand ();
  185. command.Connection = connection;
  186. command.CommandType = CommandType.StoredProcedure;
  187. command.CommandText = @"aspnet_Profile_GetProfiles";
  188. AddParameter (command, "ApplicationName", ApplicationName);
  189. AddParameter (command, "ProfileAuthOptions", authenticationOption);
  190. AddParameter (command, "PageIndex", pageIndex);
  191. AddParameter (command, "PageSize", pageSize);
  192. AddParameter (command, "UserNameToMatch", null);
  193. AddParameter (command, "InactiveSinceDate", null);
  194. using (DbDataReader reader = command.ExecuteReader ()) {
  195. return BuildProfileInfoCollection (reader, out totalRecords);
  196. }
  197. }
  198. }
  199. public override ProfileInfoCollection GetAllProfiles (ProfileAuthenticationOption authenticationOption,
  200. int pageIndex,
  201. int pageSize,
  202. out int totalRecords)
  203. {
  204. if (pageIndex < 0)
  205. throw new ArgumentException ("pageIndex is less than zero");
  206. if (pageSize < 1)
  207. throw new ArgumentException ("pageIndex is less than one");
  208. if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
  209. throw new ArgumentException ("pageIndex and pageSize are too large");
  210. using (DbConnection connection = CreateConnection ()) {
  211. DbCommand command = factory.CreateCommand ();
  212. command.Connection = connection;
  213. command.CommandType = CommandType.StoredProcedure;
  214. command.CommandText = @"aspnet_Profile_GetProfiles";
  215. AddParameter (command, "ApplicationName", ApplicationName);
  216. AddParameter (command, "ProfileAuthOptions", authenticationOption);
  217. AddParameter (command, "PageIndex", pageIndex);
  218. AddParameter (command, "PageSize", pageSize);
  219. AddParameter (command, "UserNameToMatch", null);
  220. AddParameter (command, "InactiveSinceDate", null);
  221. using (DbDataReader reader = command.ExecuteReader ()) {
  222. return BuildProfileInfoCollection (reader, out totalRecords);
  223. }
  224. }
  225. }
  226. public override int GetNumberOfInactiveProfiles (ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
  227. {
  228. using (DbConnection connection = CreateConnection ()) {
  229. DbCommand command = factory.CreateCommand ();
  230. command.Connection = connection;
  231. command.CommandType = CommandType.StoredProcedure;
  232. command.CommandText = @"aspnet_Profile_GetNumberOfInactiveProfiles";
  233. AddParameter (command, "ApplicationName", ApplicationName);
  234. AddParameter (command, "ProfileAuthOptions", authenticationOption);
  235. AddParameter (command, "InactiveSinceDate", userInactiveSinceDate);
  236. int returnValue = 0;
  237. using (DbDataReader reader = command.ExecuteReader ()) {
  238. if (reader.Read ())
  239. returnValue = reader.GetInt32 (0);
  240. }
  241. return returnValue;
  242. }
  243. }
  244. public override SettingsPropertyValueCollection GetPropertyValues (SettingsContext sc, SettingsPropertyCollection properties)
  245. {
  246. SettingsPropertyValueCollection settings = new SettingsPropertyValueCollection ();
  247. if (properties.Count == 0)
  248. return settings;
  249. foreach (SettingsProperty property in properties) {
  250. if (property.SerializeAs == SettingsSerializeAs.ProviderSpecific)
  251. if (property.PropertyType.IsPrimitive || property.PropertyType == typeof (String))
  252. property.SerializeAs = SettingsSerializeAs.String;
  253. else
  254. property.SerializeAs = SettingsSerializeAs.Xml;
  255. settings.Add (new SettingsPropertyValue (property));
  256. }
  257. string username = (string) sc ["UserName"];
  258. using (DbConnection connection = CreateConnection ()) {
  259. DbCommand command = factory.CreateCommand ();
  260. command.Connection = connection;
  261. command.CommandType = CommandType.StoredProcedure;
  262. command.CommandText = @"aspnet_Profile_GetProperties";
  263. AddParameter (command, "ApplicationName", ApplicationName);
  264. AddParameter (command, "UserName", username);
  265. AddParameter (command, "CurrentTimeUtc", DateTime.UtcNow);
  266. using (DbDataReader reader = command.ExecuteReader ()) {
  267. if (reader.Read ()) {
  268. string allnames = reader.GetString (0);
  269. string allvalues = reader.GetString (1);
  270. int binaryLen = (int) reader.GetBytes (2, 0, null, 0, 0);
  271. byte [] binaryvalues = new byte [binaryLen];
  272. reader.GetBytes (2, 0, binaryvalues, 0, binaryLen);
  273. DecodeProfileData (allnames, allvalues, binaryvalues, settings);
  274. }
  275. }
  276. }
  277. return settings;
  278. }
  279. public override void SetPropertyValues (SettingsContext sc, SettingsPropertyValueCollection properties)
  280. {
  281. string username = (string) sc ["UserName"];
  282. bool isAnonymous = !(bool) sc ["IsAuthenticated"];
  283. string names = String.Empty;
  284. string values = String.Empty;
  285. byte [] buf = null;
  286. EncodeProfileData (ref names, ref values, ref buf, properties, !isAnonymous);
  287. using (DbConnection connection = CreateConnection ()) {
  288. DbCommand command = factory.CreateCommand ();
  289. command.Connection = connection;
  290. command.CommandType = CommandType.StoredProcedure;
  291. command.CommandText = @"aspnet_Profile_SetProperties";
  292. AddParameter (command, "ApplicationName", ApplicationName);
  293. AddParameter (command, "PropertyNames", names);
  294. AddParameter (command, "PropertyValuesString", values);
  295. AddParameter (command, "PropertyValuesBinary", buf);
  296. AddParameter (command, "UserName", username);
  297. AddParameter (command, "IsUserAnonymous", isAnonymous);
  298. AddParameter (command, "CurrentTimeUtc", DateTime.UtcNow);
  299. // Return value
  300. AddParameter (command, null, ParameterDirection.ReturnValue, null);
  301. command.ExecuteNonQuery ();
  302. return;
  303. }
  304. }
  305. public override void Initialize (string name, NameValueCollection config)
  306. {
  307. if (config == null)
  308. throw new ArgumentNullException ("config");
  309. base.Initialize (name, config);
  310. applicationName = GetStringConfigValue (config, "applicationName", "/");
  311. ProfileSection section = (ProfileSection) WebConfigurationManager.GetSection ("system.web/profile");
  312. string connectionStringName = config ["connectionStringName"];
  313. if (applicationName.Length > 256)
  314. throw new ProviderException ("The ApplicationName attribute must be 256 characters long or less.");
  315. if (connectionStringName == null || connectionStringName.Length == 0)
  316. throw new ProviderException ("The ConnectionStringName attribute must be present and non-zero length.");
  317. connectionString = WebConfigurationManager.ConnectionStrings [connectionStringName];
  318. factory = connectionString == null || String.IsNullOrEmpty (connectionString.ProviderName) ?
  319. System.Data.SqlClient.SqlClientFactory.Instance :
  320. ProvidersHelper.GetDbProviderFactory (connectionString.ProviderName);
  321. }
  322. public override string ApplicationName {
  323. get { return applicationName; }
  324. set { applicationName = value; }
  325. }
  326. DbConnection CreateConnection ()
  327. {
  328. DbConnection connection = factory.CreateConnection ();
  329. connection.ConnectionString = connectionString.ConnectionString;
  330. connection.Open ();
  331. return connection;
  332. }
  333. DbParameter AddParameter (DbCommand command, string parameterName, object parameterValue)
  334. {
  335. return AddParameter (command, parameterName, ParameterDirection.Input, parameterValue);
  336. }
  337. DbParameter AddParameter (DbCommand command, string parameterName, ParameterDirection direction, object parameterValue)
  338. {
  339. DbParameter dbp = command.CreateParameter ();
  340. dbp.ParameterName = parameterName;
  341. dbp.Value = parameterValue;
  342. dbp.Direction = direction;
  343. command.Parameters.Add (dbp);
  344. return dbp;
  345. }
  346. void CheckParam (string pName, string p, int length)
  347. {
  348. if (p == null)
  349. throw new ArgumentNullException (pName);
  350. if (p.Length == 0 || p.Length > length || p.IndexOf (",") != -1)
  351. throw new ArgumentException (String.Format ("invalid format for {0}", pName));
  352. }
  353. static int GetReturnValue (DbParameter returnValue)
  354. {
  355. object value = returnValue.Value;
  356. return value is int ? (int) value : -1;
  357. }
  358. private ProfileInfo ReadProfileInfo (DbDataReader reader)
  359. {
  360. ProfileInfo pi = null;
  361. try {
  362. string username = reader.GetString (0);
  363. bool anonymous = reader.GetBoolean (1);
  364. DateTime lastUpdate = reader.GetDateTime (2);
  365. DateTime lastActivity = reader.GetDateTime (3);
  366. int size = reader.GetInt32 (4);
  367. pi = new ProfileInfo (username, anonymous, lastActivity, lastUpdate, size);
  368. }
  369. catch {
  370. }
  371. return pi;
  372. }
  373. private ProfileInfoCollection BuildProfileInfoCollection (DbDataReader reader, out int totalRecords)
  374. {
  375. ProfileInfoCollection pic = new ProfileInfoCollection ();
  376. while (reader.Read ()) {
  377. ProfileInfo pi = ReadProfileInfo (reader);
  378. if (pi != null)
  379. pic.Add (pi);
  380. }
  381. totalRecords = 0;
  382. if (reader.NextResult ()) {
  383. if (reader.Read ())
  384. totalRecords = reader.GetInt32 (0);
  385. }
  386. return pic;
  387. }
  388. string GetStringConfigValue (NameValueCollection config, string name, string def)
  389. {
  390. string retVal = def;
  391. string val = config [name];
  392. if (val != null)
  393. retVal = val;
  394. return retVal;
  395. }
  396. // Helper methods
  397. private void DecodeProfileData (string allnames, string values, byte [] buf, SettingsPropertyValueCollection properties)
  398. {
  399. if (allnames == null || values == null || buf == null || properties == null)
  400. return;
  401. string [] names = allnames.Split (':');
  402. for (int i = 0; i < names.Length; i += 4) {
  403. string name = names [i];
  404. SettingsPropertyValue pp = properties [name];
  405. if (pp == null)
  406. continue;
  407. int pos = Int32.Parse (names [i + 2], CultureInfo.InvariantCulture);
  408. int len = Int32.Parse (names [i + 3], CultureInfo.InvariantCulture);
  409. if (len == -1 && !pp.Property.PropertyType.IsValueType) {
  410. pp.PropertyValue = null;
  411. pp.IsDirty = false;
  412. pp.Deserialized = true;
  413. }
  414. else if (names [i + 1] == "S" && pos >= 0 && len > 0 && values.Length >= pos + len) {
  415. pp.SerializedValue = values.Substring (pos, len);
  416. }
  417. else if (names [i + 1] == "B" && pos >= 0 && len > 0 && buf.Length >= pos + len) {
  418. byte [] buf2 = new byte [len];
  419. Buffer.BlockCopy (buf, pos, buf2, 0, len);
  420. pp.SerializedValue = buf2;
  421. }
  422. }
  423. }
  424. private void EncodeProfileData (ref string allNames, ref string allValues, ref byte [] buf, SettingsPropertyValueCollection properties, bool userIsAuthenticated)
  425. {
  426. StringBuilder names = new StringBuilder ();
  427. StringBuilder values = new StringBuilder ();
  428. MemoryStream stream = new MemoryStream ();
  429. try {
  430. foreach (SettingsPropertyValue pp in properties) {
  431. if (!userIsAuthenticated && !(bool) pp.Property.Attributes ["AllowAnonymous"])
  432. continue;
  433. if (!pp.IsDirty && pp.UsingDefaultValue)
  434. continue;
  435. int len = 0, pos = 0;
  436. string propValue = null;
  437. if (pp.Deserialized && pp.PropertyValue == null)
  438. len = -1;
  439. else {
  440. object sVal = pp.SerializedValue;
  441. if (sVal == null)
  442. len = -1;
  443. else if (sVal is string) {
  444. propValue = (string) sVal;
  445. len = propValue.Length;
  446. pos = values.Length;
  447. }
  448. else {
  449. byte [] b2 = (byte []) sVal;
  450. pos = (int) stream.Position;
  451. stream.Write (b2, 0, b2.Length);
  452. stream.Position = pos + b2.Length;
  453. len = b2.Length;
  454. }
  455. }
  456. names.Append (pp.Name + ":" + ((propValue != null) ? "S" : "B") + ":" + pos.ToString (CultureInfo.InvariantCulture) + ":" + len.ToString (CultureInfo.InvariantCulture) + ":");
  457. if (propValue != null)
  458. values.Append (propValue);
  459. }
  460. buf = stream.ToArray ();
  461. }
  462. finally {
  463. if (stream != null)
  464. stream.Close ();
  465. }
  466. allNames = names.ToString ();
  467. allValues = values.ToString ();
  468. }
  469. }
  470. }
  471. #endif