2 // Mainsoft.Web.Profile.DerbyProfileProvider
\r
5 // Vladimir Krasnov (vladimirk@mainsoft.com)
\r
9 // Permission is hereby granted, free of charge, to any person obtaining
\r
10 // a copy of this software and associated documentation files (the
\r
11 // "Software"), to deal in the Software without restriction, including
\r
12 // without limitation the rights to use, copy, modify, merge, publish,
\r
13 // distribute, sublicense, and/or sell copies of the Software, and to
\r
14 // permit persons to whom the Software is furnished to do so, subject to
\r
15 // the following conditions:
\r
17 // The above copyright notice and this permission notice shall be
\r
18 // included in all copies or substantial portions of the Software.
\r
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
32 using System.Data.OleDb;
\r
33 using System.Data.Common;
\r
34 using System.Collections;
\r
35 using System.Configuration;
\r
36 using System.Globalization;
\r
37 using System.Web.Profile;
\r
38 using System.Web.Configuration;
\r
39 using System.Collections.Specialized;
\r
43 using Mainsoft.Web.Security;
\r
44 using System.Configuration.Provider;
\r
46 namespace Mainsoft.Web.Profile
\r
49 /// <para>This class supports the Framework infrastructure and is not intended to be used directly from your code.</para>
\r
50 /// <para>Manages storage of profile information for an ASP.NET application in a Derby database.</para>
\r
52 public class DerbyProfileProvider : ProfileProvider
\r
54 ConnectionStringSettings _connectionString;
\r
55 string _applicationName = string.Empty;
\r
56 bool _schemaChecked = false;
\r
57 DerbyUnloadManager.DerbyShutDownPolicy _shutDownPolicy = DerbyUnloadManager.DerbyShutDownPolicy.Default;
\r
59 public DerbyProfileProvider ()
\r
63 public override string ApplicationName
\r
65 get { return _applicationName; }
\r
66 set { _applicationName = value; }
\r
69 DbConnection CreateConnection ()
\r
71 if (!_schemaChecked) {
\r
72 DerbyDBSchema.CheckSchema (_connectionString.ConnectionString);
\r
73 _schemaChecked = true;
\r
75 DerbyUnloadManager.RegisterUnloadHandler (_connectionString.ConnectionString, _shutDownPolicy);
\r
78 OleDbConnection connection = new OleDbConnection (_connectionString.ConnectionString);
\r
83 public override int DeleteInactiveProfiles (ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
\r
85 using (DbConnection connection = CreateConnection ()) {
\r
86 return DerbyProfileHelper.Profile_DeleteInactiveProfiles (connection, ApplicationName, (int) authenticationOption, userInactiveSinceDate);
\r
90 public override int DeleteProfiles (ProfileInfoCollection profiles)
\r
92 if (profiles == null)
\r
93 throw new ArgumentNullException ("profiles");
\r
94 if (profiles.Count == 0)
\r
95 throw new ArgumentException ("profiles");
\r
97 string [] usernames = new string [profiles.Count];
\r
100 foreach (ProfileInfo pi in profiles) {
\r
101 if (pi.UserName == null)
\r
102 throw new ArgumentNullException ("element in profiles collection is null");
\r
104 if (pi.UserName.Length == 0 || pi.UserName.Length > 256 || pi.UserName.IndexOf (",") != -1)
\r
105 throw new ArgumentException ("element in profiles collection in illegal format");
\r
107 usernames [i++] = pi.UserName;
\r
110 return DeleteProfilesInternal (usernames);
\r
113 public override int DeleteProfiles (string [] usernames)
\r
115 if (usernames == null)
\r
116 throw new ArgumentNullException ("usernames");
\r
118 Hashtable users = new Hashtable ();
\r
119 foreach (string username in usernames) {
\r
120 if (username == null)
\r
121 throw new ArgumentNullException ("element in usernames array is null");
\r
123 if (username.Length == 0 || username.Length > 256 || username.IndexOf (",") != -1)
\r
124 throw new ArgumentException ("element in usernames array in illegal format");
\r
126 if (users.ContainsKey (username))
\r
127 throw new ArgumentException ("duplicate element in usernames array");
\r
129 users.Add (username, username);
\r
132 return DeleteProfilesInternal (usernames);
\r
135 private int DeleteProfilesInternal (string[] usernames)
\r
137 using (DbConnection connection = CreateConnection ()) {
\r
138 return DerbyProfileHelper.Profile_DeleteProfiles (connection, ApplicationName, usernames);
\r
142 public override ProfileInfoCollection FindInactiveProfilesByUserName (ProfileAuthenticationOption authenticationOption,
\r
143 string usernameToMatch,
\r
144 DateTime userInactiveSinceDate,
\r
147 out int totalRecords)
\r
149 CheckParam ("usernameToMatch", usernameToMatch, 256);
\r
151 throw new ArgumentException ("pageIndex is less than zero");
\r
153 throw new ArgumentException ("pageIndex is less than one");
\r
154 if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
\r
155 throw new ArgumentException ("pageIndex and pageSize are too large");
\r
157 DbDataReader reader = null;
\r
158 using (DbConnection connection = CreateConnection ()) {
\r
159 totalRecords = DerbyProfileHelper.Profile_GetInactiveProfiles (connection, ApplicationName, (int) authenticationOption, pageIndex, pageSize, usernameToMatch, userInactiveSinceDate, out reader);
\r
162 return BuildProfileInfoCollection (reader, pageIndex, pageSize, out totalRecords);
\r
167 public override ProfileInfoCollection FindProfilesByUserName (ProfileAuthenticationOption authenticationOption,
\r
168 string usernameToMatch,
\r
171 out int totalRecords)
\r
173 CheckParam ("usernameToMatch", usernameToMatch, 256);
\r
175 throw new ArgumentException ("pageIndex is less than zero");
\r
177 throw new ArgumentException ("pageIndex is less than one");
\r
178 if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
\r
179 throw new ArgumentException ("pageIndex and pageSize are too large");
\r
181 DbDataReader reader = null;
\r
182 using (DbConnection connection = CreateConnection ()) {
\r
183 totalRecords = DerbyProfileHelper.Profile_GetProfiles (connection, ApplicationName, (int) authenticationOption, pageIndex, pageSize, usernameToMatch, out reader);
\r
186 return BuildProfileInfoCollection (reader, pageIndex, pageSize, out totalRecords);
\r
191 public override ProfileInfoCollection GetAllInactiveProfiles (ProfileAuthenticationOption authenticationOption,
\r
192 DateTime userInactiveSinceDate,
\r
195 out int totalRecords)
\r
198 throw new ArgumentException ("pageIndex is less than zero");
\r
200 throw new ArgumentException ("pageIndex is less than one");
\r
201 if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
\r
202 throw new ArgumentException ("pageIndex and pageSize are too large");
\r
204 DbDataReader reader = null;
\r
205 using (DbConnection connection = CreateConnection ()) {
\r
206 totalRecords = DerbyProfileHelper.Profile_GetInactiveProfiles (
\r
207 connection, ApplicationName, (int) authenticationOption,
\r
208 pageIndex, pageSize, null, userInactiveSinceDate, out reader);
\r
211 return BuildProfileInfoCollection (reader, pageIndex, pageSize, out totalRecords);
\r
216 public override ProfileInfoCollection GetAllProfiles (ProfileAuthenticationOption authenticationOption,
\r
219 out int totalRecords)
\r
222 throw new ArgumentException ("pageIndex is less than zero");
\r
224 throw new ArgumentException ("pageIndex is less than one");
\r
225 if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
\r
226 throw new ArgumentException ("pageIndex and pageSize are too large");
\r
228 DbDataReader reader = null;
\r
229 using (DbConnection connection = CreateConnection ()) {
\r
230 totalRecords = DerbyProfileHelper.Profile_GetProfiles (
\r
231 connection, ApplicationName, (int) authenticationOption,
\r
232 pageIndex, pageSize, null, out reader);
\r
235 return BuildProfileInfoCollection (reader, pageIndex, pageSize, out totalRecords);
\r
240 public override int GetNumberOfInactiveProfiles (ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
\r
242 using (DbConnection connection = CreateConnection ()) {
\r
243 return DerbyProfileHelper.Profile_GetNumberOfInactiveProfiles (
\r
244 connection, ApplicationName, (int) authenticationOption, userInactiveSinceDate);
\r
248 public override SettingsPropertyValueCollection GetPropertyValues (SettingsContext sc, SettingsPropertyCollection properties)
\r
250 SettingsPropertyValueCollection settings = new SettingsPropertyValueCollection ();
\r
252 if (properties.Count == 0)
\r
255 foreach (SettingsProperty property in properties) {
\r
256 if (property.SerializeAs == SettingsSerializeAs.ProviderSpecific)
\r
257 if (property.PropertyType.IsPrimitive || property.PropertyType == typeof (String))
\r
258 property.SerializeAs = SettingsSerializeAs.String;
\r
260 property.SerializeAs = SettingsSerializeAs.Xml;
\r
262 settings.Add (new SettingsPropertyValue (property));
\r
265 string username = (string) sc ["UserName"];
\r
267 DbDataReader reader;
\r
268 using (DbConnection connection = CreateConnection ()) {
\r
269 DerbyProfileHelper.Profile_GetProperties (connection, ApplicationName, username, DateTime.UtcNow, out reader);
\r
270 if (reader != null) {
\r
272 if (reader.Read ()) {
\r
273 string allnames = reader.GetString (0);
\r
274 string allvalues = reader.GetString (1);
\r
275 int binaryLen = (int) reader.GetBytes (2, 0, null, 0, 0);
\r
276 byte [] binaryvalues = new byte [binaryLen];
\r
277 reader.GetBytes (2, 0, binaryvalues, 0, binaryLen);
\r
279 DecodeProfileData (allnames, allvalues, binaryvalues, settings);
\r
287 public override void SetPropertyValues (SettingsContext sc, SettingsPropertyValueCollection properties)
\r
289 string username = (string) sc ["UserName"];
\r
290 bool authenticated = (bool) sc ["IsAuthenticated"];
\r
292 string names = String.Empty;
\r
293 string values = String.Empty;
\r
294 byte [] buf = null;
\r
296 EncodeProfileData (ref names, ref values, ref buf, properties, authenticated);
\r
298 using (DbConnection connection = CreateConnection ()) {
\r
299 DerbyProfileHelper.Profile_SetProperties (
\r
300 connection, _applicationName, names, values,
\r
301 buf, username, authenticated, DateTime.UtcNow);
\r
305 public override void Initialize (string name, NameValueCollection config)
\r
307 if (config == null)
\r
308 throw new ArgumentNullException ("config");
\r
310 if (string.IsNullOrEmpty (name))
\r
311 name = "DerbyProfileProvider";
\r
313 if (string.IsNullOrEmpty (config ["description"])) {
\r
314 config.Remove ("description");
\r
315 config.Add ("description", "Derby profile provider");
\r
317 base.Initialize (name, config);
\r
319 _applicationName = GetStringConfigValue (config, "applicationName", "/");
\r
321 ProfileSection profileSection = (ProfileSection) WebConfigurationManager.GetSection ("system.web/profile");
\r
322 string connectionStringName = config ["connectionStringName"];
\r
323 _connectionString = WebConfigurationManager.ConnectionStrings [connectionStringName];
\r
324 if (_connectionString == null)
\r
325 throw new ProviderException (String.Format ("The connection name '{0}' was not found in the applications configuration or the connection string is empty.", connectionStringName));
\r
327 string shutdown = config ["shutdown"];
\r
328 if (!String.IsNullOrEmpty (shutdown))
\r
329 _shutDownPolicy = (DerbyUnloadManager.DerbyShutDownPolicy) Enum.Parse (typeof (DerbyUnloadManager.DerbyShutDownPolicy), shutdown, true);
\r
332 private ProfileInfoCollection BuildProfileInfoCollection (DbDataReader reader, int pageIndex, int pageSize, out int totalRecords)
\r
336 int num_to_skip = pageIndex * pageSize;
\r
337 ProfileInfoCollection pic = new ProfileInfoCollection ();
\r
339 while (reader.Read ()) {
\r
340 if (num_read >= num_to_skip && num_added < pageSize) {
\r
341 ProfileInfo pi = ReadProfileInfo (reader);
\r
349 totalRecords = num_read;
\r
353 private ProfileInfo ReadProfileInfo (DbDataReader reader)
\r
355 string username = reader.GetString (0);
\r
356 bool anonymous = reader.GetInt32 (1) > 0;
\r
357 DateTime lastUpdate = reader.GetDateTime (2);
\r
358 DateTime lastActivity = reader.GetDateTime (3);
\r
359 int size = reader.GetInt32 (4);
\r
361 return new ProfileInfo (username, anonymous, lastActivity, lastUpdate, size);
\r
365 private void DecodeProfileData (string allnames, string values, byte [] buf, SettingsPropertyValueCollection properties)
\r
367 if (allnames == null || values == null || buf == null || properties == null)
\r
370 string [] names = allnames.Split (':');
\r
371 for (int i = 0; i < names.Length; i += 4) {
\r
372 string name = names [i];
\r
373 SettingsPropertyValue pp = properties [name];
\r
378 int pos = Int32.Parse (names [i + 2], CultureInfo.InvariantCulture);
\r
379 int len = Int32.Parse (names [i + 3], CultureInfo.InvariantCulture);
\r
381 if (len == -1 && !pp.Property.PropertyType.IsValueType) {
\r
382 pp.PropertyValue = null;
\r
383 pp.IsDirty = false;
\r
384 pp.Deserialized = true;
\r
386 else if (names [i + 1] == "S" && pos >= 0 && len > 0 && values.Length >= pos + len) {
\r
387 pp.SerializedValue = values.Substring (pos, len);
\r
389 else if (names [i + 1] == "B" && pos >= 0 && len > 0 && buf.Length >= pos + len) {
\r
390 byte [] buf2 = new byte [len];
\r
391 Buffer.BlockCopy (buf, pos, buf2, 0, len);
\r
392 pp.SerializedValue = buf2;
\r
397 private void EncodeProfileData (ref string allNames, ref string allValues, ref byte [] buf, SettingsPropertyValueCollection properties, bool userIsAuthenticated)
\r
399 StringBuilder names = new StringBuilder ();
\r
400 StringBuilder values = new StringBuilder ();
\r
401 MemoryStream stream = new MemoryStream ();
\r
404 foreach (SettingsPropertyValue pp in properties) {
\r
405 if (!userIsAuthenticated && !(bool) pp.Property.Attributes ["AllowAnonymous"])
\r
408 if (!pp.IsDirty && pp.UsingDefaultValue)
\r
411 int len = 0, pos = 0;
\r
412 string propValue = null;
\r
414 if (pp.Deserialized && pp.PropertyValue == null)
\r
417 object sVal = pp.SerializedValue;
\r
421 else if (sVal is string) {
\r
422 propValue = (string) sVal;
\r
423 len = propValue.Length;
\r
424 pos = values.Length;
\r
427 byte [] b2 = (byte []) sVal;
\r
428 pos = (int) stream.Position;
\r
429 stream.Write (b2, 0, b2.Length);
\r
430 stream.Position = pos + b2.Length;
\r
435 names.Append (pp.Name + ":" + ((propValue != null) ? "S" : "B") + ":" + pos.ToString (CultureInfo.InvariantCulture) + ":" + len.ToString (CultureInfo.InvariantCulture) + ":");
\r
437 if (propValue != null)
\r
438 values.Append (propValue);
\r
441 buf = stream.ToArray ();
\r
444 if (stream != null)
\r
448 allNames = names.ToString ();
\r
449 allValues = values.ToString ();
\r
452 string GetStringConfigValue (NameValueCollection config, string name, string def)
\r
455 string val = config [name];
\r
461 void CheckParam (string pName, string p, int length)
\r
464 throw new ArgumentNullException (pName);
\r
465 if (p.Length == 0 || p.Length > length || p.IndexOf (",") != -1)
\r
466 throw new ArgumentException (String.Format ("invalid format for {0}", pName));
\r