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