merge -r 98047:98048
[mono.git] / mcs / class / System.Web / System.Web.Profile / SqliteProfileProvider.cs
1 //
2 // $Id: PgProfileProvider.cs 36 2007-11-24 09:44:42Z dna $
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
23 // Copyright © 2006, 2007 Nauck IT KG           http://www.nauck-it.de
24 //
25 // Author:
26 //      Daniel Nauck            <d.nauck(at)nauck-it.de>
27 //
28 // Adapted to Sqlite by Marek Habersack <mhabersack@novell.com>
29 //
30
31 #if NET_2_0
32 using System;
33 using System.Data;
34 using System.Data.Common;
35 using System.Configuration;
36 using System.Configuration.Provider;
37 using System.Collections.Generic;
38 using System.Collections.Specialized;
39 using System.Diagnostics;
40 using System.Text;
41 using System.Web.Hosting;
42 using System.Web.Util;
43
44 using Mono.Data.Sqlite;
45
46 namespace System.Web.Profile
47 {
48         internal class SqliteProfileProvider : ProfileProvider
49         {
50                 private const string m_ProfilesTableName = "Profiles";
51                 private const string m_ProfileDataTableName = "ProfileData";
52                 private string m_ConnectionString = string.Empty;
53
54                 private SerializationHelper m_serializationHelper = new SerializationHelper();
55
56                 DbParameter AddParameter (DbCommand command, string parameterName)
57                 {
58                         return AddParameter (command, parameterName, null);
59                 }
60                 
61                 DbParameter AddParameter (DbCommand command, string parameterName, object parameterValue)
62                 {
63                         return AddParameter (command, parameterName, ParameterDirection.Input, parameterValue);
64                 }
65
66                 DbParameter AddParameter (DbCommand command, string parameterName, ParameterDirection direction, object parameterValue)
67                 {
68                         DbParameter dbp = command.CreateParameter ();
69                         dbp.ParameterName = parameterName;
70                         dbp.Value = parameterValue;
71                         dbp.Direction = direction;
72                         command.Parameters.Add (dbp);
73                         return dbp;
74                 }
75
76                 DbParameter AddParameter (DbCommand command, string parameterName, ParameterDirection direction, DbType type, object parameterValue)
77                 {
78                         DbParameter dbp = command.CreateParameter ();
79                         dbp.ParameterName = parameterName;
80                         dbp.Value = parameterValue;
81                         dbp.Direction = direction;
82                         dbp.DbType = type;
83                         command.Parameters.Add (dbp);
84                         return dbp;
85                 }
86                 
87                 /// <summary>
88                 /// System.Configuration.Provider.ProviderBase.Initialize Method
89                 /// </summary>
90                 public override void Initialize(string name, NameValueCollection config)
91                 {
92                         // Initialize values from web.config.
93                         if (config == null)
94                                 throw new ArgumentNullException("Config", Properties.Resources.ErrArgumentNull);
95
96                         if (string.IsNullOrEmpty(name))
97                                 name = Properties.Resources.ProfileProviderDefaultName;
98
99                         if (string.IsNullOrEmpty(config["description"]))
100                         {
101                                 config.Remove("description");
102                                 config.Add("description", Properties.Resources.ProfileProviderDefaultDescription);
103                         }
104
105                         // Initialize the abstract base class.
106                         base.Initialize(name, config);
107
108                         m_ApplicationName = GetConfigValue(config["applicationName"], HostingEnvironment.ApplicationVirtualPath);
109
110                         // Get connection string.
111                         string connStrName = config["connectionStringName"];
112
113                         if (string.IsNullOrEmpty(connStrName))
114                         {
115                                 throw new ArgumentOutOfRangeException("ConnectionStringName", Properties.Resources.ErrArgumentNullOrEmpty);
116                         }
117                         else
118                         {
119                                 ConnectionStringSettings ConnectionStringSettings = ConfigurationManager.ConnectionStrings[connStrName];
120
121                                 if (ConnectionStringSettings == null || string.IsNullOrEmpty(ConnectionStringSettings.ConnectionString.Trim()))
122                                 {
123                                         throw new ProviderException(Properties.Resources.ErrConnectionStringNullOrEmpty);
124                                 }
125
126                                 m_ConnectionString = ConnectionStringSettings.ConnectionString;
127                         }
128                 }
129
130                 /// <summary>
131                 /// System.Web.Profile.ProfileProvider properties.
132                 /// </summary>
133                 #region System.Web.Security.ProfileProvider properties
134                 private string m_ApplicationName = string.Empty;
135
136                 public override string ApplicationName
137                 {
138                         get { return m_ApplicationName; }
139                         set { m_ApplicationName = value; }
140                 }
141                 #endregion
142
143                 /// <summary>
144                 /// System.Web.Profile.ProfileProvider methods.
145                 /// </summary>
146                 #region System.Web.Security.ProfileProvider methods
147
148                 /// <summary>
149                 /// ProfileProvider.DeleteInactiveProfiles
150                 /// </summary>
151                 public override int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
152                 {
153                         throw new Exception("DeleteInactiveProfiles: The method or operation is not implemented.");
154                 }
155
156                 public override int DeleteProfiles(string[] usernames)
157                 {
158                         throw new Exception("DeleteProfiles1: The method or operation is not implemented.");
159                 }
160
161                 public override int DeleteProfiles(ProfileInfoCollection profiles)
162                 {
163                         throw new Exception("DeleteProfiles2: The method or operation is not implemented.");
164                 }
165
166                 public override ProfileInfoCollection FindInactiveProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
167                 {
168                         throw new Exception("FindInactiveProfilesByUserName: The method or operation is not implemented.");
169                 }
170
171                 public override ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
172                 {
173                         throw new Exception("FindProfilesByUserName: The method or operation is not implemented.");
174                 }
175
176                 public override ProfileInfoCollection GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
177                 {
178                         throw new Exception("GetAllInactiveProfiles: The method or operation is not implemented.");
179                 }
180
181                 public override ProfileInfoCollection GetAllProfiles(ProfileAuthenticationOption authenticationOption, int pageIndex, int pageSize, out int totalRecords)
182                 {
183                         throw new Exception("GetAllProfiles: The method or operation is not implemented.");
184                 }
185
186                 public override int GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
187                 {
188                         throw new Exception("GetNumberOfInactiveProfiles: The method or operation is not implemented.");
189                 }
190                 #endregion
191
192                 /// <summary>
193                 /// System.Configuration.SettingsProvider methods.
194                 /// </summary>
195                 #region System.Web.Security.SettingsProvider methods
196
197                 /// <summary>
198                 /// 
199                 /// </summary>
200                 public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
201                 {
202                         SettingsPropertyValueCollection result = new SettingsPropertyValueCollection();
203                         string username = (string)context["UserName"];
204                         bool isAuthenticated = (bool)context["IsAuthenticated"];
205                         Dictionary<string, object> databaseResult = new Dictionary<string, object>();
206
207                         using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
208                         {
209                                 using (SqliteCommand dbCommand = dbConn.CreateCommand())
210                                 {
211                                         dbCommand.CommandText = string.Format("SELECT \"Name\", \"ValueString\", \"ValueBinary\" FROM \"{0}\" WHERE \"Profile\" = (SELECT \"pId\" FROM \"{1}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated)", m_ProfileDataTableName, m_ProfilesTableName);
212
213                                         AddParameter (dbCommand, "@Username", username);
214                                         AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
215                                         AddParameter (dbCommand, "@IsAuthenticated", !isAuthenticated);
216
217                                         try
218                                         {
219                                                 dbConn.Open();
220                                                 dbCommand.Prepare();
221
222                                                 using (SqliteDataReader reader = dbCommand.ExecuteReader())
223                                                 {
224                                                         while (reader.Read())
225                                                         {
226                                                                 object resultData = null;
227                                                                 if(!reader.IsDBNull(1))
228                                                                         resultData = reader.GetValue(1);
229                                                                 else if(!reader.IsDBNull(2))
230                                                                         resultData = reader.GetValue(2);
231
232                                                                 databaseResult.Add(reader.GetString(0), resultData);
233                                                         }
234                                                 }
235                                         }
236                                         catch (SqliteException e)
237                                         {
238                                                 Trace.WriteLine(e.ToString());
239                                                 throw new ProviderException(Properties.Resources.ErrOperationAborted);
240                                         }
241                                         finally
242                                         {
243                                                 if (dbConn != null)
244                                                         dbConn.Close();
245                                         }
246                                 }
247                         }
248
249                         foreach (SettingsProperty item in collection)
250                         {
251                                 if (item.SerializeAs == SettingsSerializeAs.ProviderSpecific)
252                                 {
253                                         if (item.PropertyType.IsPrimitive || item.PropertyType.Equals(typeof(string)))
254                                                 item.SerializeAs = SettingsSerializeAs.String;
255                                         else
256                                                 item.SerializeAs = SettingsSerializeAs.Xml;
257                                 }
258
259                                 SettingsPropertyValue itemValue = new SettingsPropertyValue(item);
260
261                                 if ((databaseResult.ContainsKey(item.Name)) && (databaseResult[item.Name] != null))
262                                 {
263                                         if(item.SerializeAs == SettingsSerializeAs.String)
264                                                 itemValue.PropertyValue = m_serializationHelper.DeserializeFromBase64((string)databaseResult[item.Name]);
265                                         
266                                         else if (item.SerializeAs == SettingsSerializeAs.Xml)
267                                                 itemValue.PropertyValue = m_serializationHelper.DeserializeFromXml((string)databaseResult[item.Name]);
268
269                                         else if (item.SerializeAs == SettingsSerializeAs.Binary)
270                                                 itemValue.PropertyValue = m_serializationHelper.DeserializeFromBinary((byte[])databaseResult[item.Name]);
271                                 }
272                                 itemValue.IsDirty = false;                              
273                                 result.Add(itemValue);
274                         }
275
276                         UpdateActivityDates(username, isAuthenticated, true);
277
278                         return result;
279                 }
280
281                 public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
282                 {
283                         string username = (string)context["UserName"];
284                         bool isAuthenticated = (bool)context["IsAuthenticated"];
285
286                         if (collection.Count < 1)
287                                 return;
288
289                         if (!ProfileExists(username))
290                                 CreateProfileForUser(username, isAuthenticated);
291
292                         using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
293                         {
294                                 using (SqliteCommand deleteCommand = dbConn.CreateCommand(),
295                                         insertCommand = dbConn.CreateCommand())
296                                 {
297                                         deleteCommand.CommandText = string.Format("DELETE FROM \"{0}\" WHERE \"Name\" = @Name AND \"Profile\" = (SELECT \"pId\" FROM \"{1}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated)", m_ProfileDataTableName, m_ProfilesTableName);
298
299                                         AddParameter (deleteCommand, "@Name");
300                                         AddParameter (deleteCommand, "@Username", username);
301                                         AddParameter (deleteCommand, "@ApplicationName", m_ApplicationName);
302                                         AddParameter (deleteCommand, "@IsAuthenticated", !isAuthenticated);
303
304
305                                         insertCommand.CommandText = string.Format("INSERT INTO \"{0}\" (\"pId\", \"Profile\", \"Name\", \"ValueString\", \"ValueBinary\") VALUES (@pId, (SELECT \"pId\" FROM \"{1}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated), @Name, @ValueString, @ValueBinary)", m_ProfileDataTableName, m_ProfilesTableName);
306
307                                         AddParameter (insertCommand, "@pId");
308                                         AddParameter (insertCommand, "@Name");
309                                         AddParameter (insertCommand, "@ValueString");
310                                         insertCommand.Parameters["@ValueString"].IsNullable = true;
311                                         AddParameter (insertCommand, "@ValueBinary");
312                                         insertCommand.Parameters["@ValueBinary"].IsNullable = true;
313                                         AddParameter (insertCommand, "@Username", username);
314                                         AddParameter (insertCommand, "@ApplicationName", m_ApplicationName);
315                                         AddParameter (insertCommand, "@IsAuthenticated", !isAuthenticated);
316
317                                         SqliteTransaction dbTrans = null;
318
319                                         try
320                                         {
321                                                 dbConn.Open();
322                                                 deleteCommand.Prepare();
323                                                 insertCommand.Prepare();
324
325                                                 using (dbTrans = dbConn.BeginTransaction())
326                                                 {
327
328                                                         foreach (SettingsPropertyValue item in collection)
329                                                         {
330                                                                 if (!item.IsDirty)
331                                                                         continue;
332
333                                                                 deleteCommand.Parameters["@Name"].Value = item.Name;
334
335                                                                 insertCommand.Parameters["@pId"].Value = Guid.NewGuid().ToString();
336                                                                 insertCommand.Parameters["@Name"].Value = item.Name;
337
338                                                                 if (item.Property.SerializeAs == SettingsSerializeAs.String)
339                                                                 {
340                                                                         insertCommand.Parameters["@ValueString"].Value = m_serializationHelper.SerializeToBase64(item.PropertyValue);
341                                                                         insertCommand.Parameters["@ValueBinary"].Value = DBNull.Value; //new byte[0];//DBNull.Value;
342                                                                 }
343                                                                 else if (item.Property.SerializeAs == SettingsSerializeAs.Xml)
344                                                                 {
345                                                                         item.SerializedValue = m_serializationHelper.SerializeToXml(item.PropertyValue);
346                                                                         insertCommand.Parameters["@ValueString"].Value = item.SerializedValue;
347                                                                         insertCommand.Parameters["@ValueBinary"].Value = DBNull.Value; //new byte[0];//DBNull.Value;
348                                                                 }
349                                                                 else if (item.Property.SerializeAs == SettingsSerializeAs.Binary)
350                                                                 {
351                                                                         item.SerializedValue = m_serializationHelper.SerializeToBinary(item.PropertyValue);
352                                                                         insertCommand.Parameters["@ValueString"].Value = DBNull.Value; //string.Empty;//DBNull.Value;
353                                                                         insertCommand.Parameters["@ValueBinary"].Value = item.SerializedValue;
354                                                                 }
355
356                                                                 deleteCommand.ExecuteNonQuery();
357                                                                 insertCommand.ExecuteNonQuery();
358                                                         }
359
360                                                         UpdateActivityDates(username, isAuthenticated, false);
361
362                                                         // Attempt to commit the transaction
363                                                         dbTrans.Commit();
364                                                 }
365                                         }
366                                         catch (SqliteException e)
367                                         {
368                                                 Trace.WriteLine(e.ToString());
369
370                                                 try
371                                                 {
372                                                         // Attempt to roll back the transaction
373                                                         Trace.WriteLine(Properties.Resources.LogRollbackAttempt);
374                                                         dbTrans.Rollback();
375                                                 }
376                                                 catch (SqliteException re)
377                                                 {
378                                                         // Rollback failed
379                                                         Trace.WriteLine(Properties.Resources.ErrRollbackFailed);
380                                                         Trace.WriteLine(re.ToString());
381                                                 }
382
383                                                 throw new ProviderException(Properties.Resources.ErrOperationAborted);
384                                         }
385                                         finally
386                                         {
387                                                 if (dbConn != null)
388                                                         dbConn.Close();
389                                         }
390                                 }
391                         }
392                 }
393                 #endregion
394
395                 #region private methods
396                 /// <summary>
397                 /// Create a empty user profile
398                 /// </summary>
399                 /// <param name="username"></param>
400                 /// <param name="isAuthenticated"></param>
401                 private void CreateProfileForUser(string username, bool isAuthenticated)
402                 {
403                         if (ProfileExists(username))
404                         {
405                                 throw new ProviderException(string.Format(Properties.Resources.ErrProfileAlreadyExist, username));
406                         }
407
408                         using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
409                         {
410                                 using (SqliteCommand dbCommand = dbConn.CreateCommand())
411                                 {
412                                         dbCommand.CommandText = string.Format("INSERT INTO \"{0}\" (\"pId\", \"Username\", \"ApplicationName\", \"IsAnonymous\", \"LastActivityDate\", \"LastUpdatedDate\") Values (@pId, @Username, @ApplicationName, @IsAuthenticated, @LastActivityDate, @LastUpdatedDate)", m_ProfilesTableName);
413
414                                         AddParameter (dbCommand, "@pId", Guid.NewGuid().ToString());
415                                         AddParameter (dbCommand, "@Username", username);
416                                         AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
417                                         AddParameter (dbCommand, "@IsAuthenticated", !isAuthenticated);
418                                         AddParameter (dbCommand, "@LastActivityDate", DateTime.Now);
419                                         AddParameter (dbCommand, "@LastUpdatedDate", DateTime.Now);
420
421                                         try
422                                         {
423                                                 dbConn.Open();
424                                                 dbCommand.Prepare();
425
426                                                 dbCommand.ExecuteNonQuery();
427                                         }
428                                         catch (SqliteException e)
429                                         {
430                                                 Trace.WriteLine(e.ToString());
431                                                 throw new ProviderException(Properties.Resources.ErrOperationAborted);
432                                         }
433                                         finally
434                                         {
435                                                 if (dbConn != null)
436                                                         dbConn.Close();
437                                         }
438                                 }
439                         }
440                 }
441
442
443                 private bool ProfileExists(string username)
444                 {
445                         using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
446                         {
447                                 using (SqliteCommand dbCommand = dbConn.CreateCommand())
448                                 {
449                                         dbCommand.CommandText = string.Format("SELECT COUNT(*) FROM \"{0}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_ProfilesTableName);
450
451                                         AddParameter (dbCommand, "@Username", username);
452                                         AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
453
454                                         try
455                                         {
456                                                 dbConn.Open();
457                                                 dbCommand.Prepare();
458
459                                                 int numRecs = 0;
460                                                 Int32.TryParse(dbCommand.ExecuteScalar().ToString(), out numRecs);
461
462                                                 if (numRecs > 0)
463                                                         return true;
464                                         }
465                                         catch (SqliteException e)
466                                         {
467                                                 Trace.WriteLine(e.ToString());
468                                                 throw new ProviderException(Properties.Resources.ErrOperationAborted);
469                                         }
470                                         finally
471                                         {
472                                                 if (dbConn != null)
473                                                         dbConn.Close();
474                                         }
475                                 }
476                         }
477
478                         return false;
479                 }
480
481                 /// <summary>
482                 /// Updates the LastActivityDate and LastUpdatedDate values when profile properties are accessed by the
483                 /// GetPropertyValues and SetPropertyValues methods.
484                 /// Passing true as the activityOnly parameter will update only the LastActivityDate.
485                 /// </summary>
486                 /// <param name="username"></param>
487                 /// <param name="isAuthenticated"></param>
488                 /// <param name="activityOnly"></param>
489                 private void UpdateActivityDates(string username, bool isAuthenticated, bool activityOnly)
490                 {
491                         using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
492                         {
493                                 using (SqliteCommand dbCommand = dbConn.CreateCommand())
494                                 {
495                                         if (activityOnly)
496                                         {
497                                                 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"LastActivityDate\" = @LastActivityDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated", m_ProfilesTableName);
498
499                                                 AddParameter (dbCommand, "@LastActivityDate", DateTime.Now);
500                                         }
501                                         else
502                                         {
503                                                 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"LastActivityDate\" = @LastActivityDate, \"LastUpdatedDate\" = @LastUpdatedDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated", m_ProfilesTableName);
504
505                                                 AddParameter (dbCommand, "@LastActivityDate", DateTime.Now);
506                                                 AddParameter (dbCommand, "@LastUpdatedDate", DateTime.Now);
507                                         }
508                                         
509                                         AddParameter (dbCommand, "@Username", username);
510                                         AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
511                                         AddParameter (dbCommand, "@IsAuthenticated", !isAuthenticated);
512
513                                         try
514                                         {
515                                                 dbConn.Open();
516                                                 dbCommand.Prepare();
517
518                                                 dbCommand.ExecuteNonQuery();
519                                         }
520                                         catch (SqliteException e)
521                                         {
522                                                 Trace.WriteLine(e.ToString());
523                                                 throw new ProviderException(Properties.Resources.ErrOperationAborted);
524                                         }
525                                         finally
526                                         {
527                                                 if (dbConn != null)
528                                                         dbConn.Close();
529                                         }
530                                 }
531                         }
532                 }
533
534                 /// <summary>
535                 /// A helper function to retrieve config values from the configuration file.
536                 /// </summary>
537                 /// <param name="configValue"></param>
538                 /// <param name="defaultValue"></param>
539                 /// <returns></returns>
540                 private string GetConfigValue(string configValue, string defaultValue)
541                 {
542                         if (string.IsNullOrEmpty(configValue))
543                                 return defaultValue;
544
545                         return configValue;
546                 }
547                 #endregion
548         }
549 }
550 #endif