using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ScrewTurn.Wiki.PluginFramework;
namespace ScrewTurn.Wiki {
///
/// Implements a Users Storage Provider.
///
public class UsersStorageProvider : IUsersStorageProviderV30 {
private const string UsersFile = "Users.cs";
private const string UsersDataFile = "UsersData.cs";
private const string GroupsFile = "Groups.cs";
private readonly ComponentInformation info = new ComponentInformation("Local Users Provider",
"ScrewTurn Software", Settings.WikiVersion, "http://www.screwturn.eu", null);
private IHostV30 host;
private UserGroup[] groupsCache = null;
private UserInfo[] usersCache = null;
private string GetFullPath(string filename) {
return Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), filename);
}
///
/// Initializes the Provider.
///
/// The Host of the Provider.
/// The Configuration data, if any.
/// If host or config are null.
/// If config is not valid or is incorrect.
public void Init(IHostV30 host, string config) {
if(host == null) throw new ArgumentNullException("host");
if(config == null) throw new ArgumentNullException("config");
this.host = host;
if(!LocalProvidersTools.CheckWritePermissions(host.GetSettingValue(SettingName.PublicDirectory))) {
throw new InvalidConfigurationException("Cannot write into the public directory - check permissions");
}
if(!File.Exists(GetFullPath(UsersFile))) {
File.Create(GetFullPath(UsersFile)).Close();
}
if(!File.Exists(GetFullPath(UsersDataFile))) {
File.Create(GetFullPath(UsersDataFile)).Close();
}
if(!File.Exists(GetFullPath(GroupsFile))) {
File.Create(GetFullPath(GroupsFile)).Close();
}
VerifyAndPerformUpgrade();
}
///
/// Verifies the need for a data upgrade, and performs it when needed.
///
private void VerifyAndPerformUpgrade() {
// Load file lines
// Parse first line (if any) with old (v2) algorithm
// If parsing is successful, then the file must be converted
// Conversion consists in removing the 'ADMIN|USER' field, creating the proper default groups and setting user membership
// Structure v2:
// Username|PasswordHash|Email|Active-Inactive|DateTime|Admin-User
//string[] lines = File.ReadAllLines(GetFullPath(UsersFile));
// Use this method because version 2.0 file might have started with a blank line
string[] lines = File.ReadAllText(GetFullPath(UsersFile)).Replace("\r", "").Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
if(lines.Length > 0) {
bool upgradeIsNeeded = false;
LocalUserInfo[] users = new LocalUserInfo[lines.Length];
bool[] oldStyleAdmin = new bool[lines.Length]; // Values are valid only if upgradeIsNeeded=true
char[] splitter = new char[] { '|' };
for(int i = 0; i < lines.Length; i++) {
string line = lines[i];
string[] fields = line.Split(splitter, StringSplitOptions.RemoveEmptyEntries);
string displayName = null;
if(fields.Length == 6) {
if(fields[5] == "ADMIN" || fields[5] == "USER") {
// Version 2.0
upgradeIsNeeded = true;
oldStyleAdmin[i] = fields[5] == "ADMIN";
}
else {
// Version 3.0 with DisplayName specified
oldStyleAdmin[i] = false;
displayName = fields[5];
}
}
else {
// Can be a version 3.0 file, with empty DisplayName
oldStyleAdmin[i] = false;
}
users[i] = new LocalUserInfo(fields[0], displayName, fields[2],
fields[3].ToLowerInvariant() == "active", DateTime.Parse(fields[4]), this, fields[1]);
}
if(upgradeIsNeeded) {
// Dump users
// Create default groups
// Set membership for old users
// Tell the host to set the permissions for the default groups
string backupFile = GetFullPath(Path.GetFileNameWithoutExtension(UsersFile) + "_v2" + Path.GetExtension(UsersFile));
File.Copy(GetFullPath(UsersFile), backupFile);
host.LogEntry("Upgrading users format from 2.0 to 3.0", LogEntryType.General, null, this);
DumpUsers(users);
UserGroup adminsGroup = AddUserGroup(host.GetSettingValue(SettingName.AdministratorsGroup), "Built-in Administrators");
UserGroup usersGroup = AddUserGroup(host.GetSettingValue(SettingName.UsersGroup), "Built-in Users");
for(int i = 0; i < users.Length; i++) {
if(oldStyleAdmin[i]) {
SetUserMembership(users[i], new string[] { adminsGroup.Name });
}
else {
SetUserMembership(users[i], new string[] { usersGroup.Name });
}
}
host.UpgradeSecurityFlagsToGroupsAcl(adminsGroup, usersGroup);
}
}
}
///
/// Method invoked on shutdown.
///
/// This method might not be invoked in some cases.
public void Shutdown() { }
///
/// Gets the Information about the Provider.
///
public ComponentInformation Information {
get { return info; }
}
///
/// Gets a brief summary of the configuration string format, in HTML. Returns null if no configuration is needed.
///
public string ConfigHelpHtml {
get { return null; }
}
///
/// Gets a value indicating whether user accounts are read-only.
///
public bool UserAccountsReadOnly {
get { return false; }
}
///
/// Gets a value indicating whether user groups are read-only. If so, the provider
/// should support default user groups as defined in the wiki configuration.
///
public bool UserGroupsReadOnly {
get { return false; }
}
///
/// Gets a value indicating whether group membership is read-only (if
/// is false, then this property must be false). If this property is true, the provider
/// should return membership data compatible with default user groups.
///
public bool GroupMembershipReadOnly {
get { return false; }
}
///
/// Gets a value indicating whether users' data is read-only.
///
public bool UsersDataReadOnly {
get { return false; }
}
///
/// Tests a Password for a User account.
///
/// The User account.
/// The Password to test.
/// True if the Password is correct.
/// If user or password are null.
public bool TestAccount(UserInfo user, string password) {
if(user == null) throw new ArgumentNullException("user");
if(password == null) throw new ArgumentNullException("password");
return TryManualLogin(user.Username, password) != null;
}
///
/// Gets the complete list of Users.
///
/// All the Users, sorted by username.
public UserInfo[] GetUsers() {
lock(this) {
if(usersCache == null) {
UserGroup[] groups = GetUserGroups();
string[] lines = File.ReadAllLines(GetFullPath(UsersFile));
UserInfo[] result = new UserInfo[lines.Length];
char[] splitter = new char[] { '|' };
string[] fields;
for(int i = 0; i < lines.Length; i++) {
fields = lines[i].Split(splitter, StringSplitOptions.RemoveEmptyEntries);
// Structure (version 3.0 - file previously converted):
// Username|PasswordHash|Email|Active-Inactive|DateTime[|DisplayName]
string displayName = fields.Length == 6 ? fields[5] : null;
result[i] = new LocalUserInfo(fields[0], displayName, fields[2], fields[3].ToLowerInvariant().Equals("active"),
DateTime.Parse(fields[4]), this, fields[1]);
result[i].Groups = GetGroupsForUser(result[i].Username, groups);
}
Array.Sort(result, new UsernameComparer());
usersCache = result;
}
return usersCache;
}
}
///
/// Gets the names of all the groups a user is member of.
///
/// The username.
/// The groups.
/// The names of the groups the user is member of.
private string[] GetGroupsForUser(string user, UserGroup[] groups) {
List result = new List(3);
foreach(UserGroup group in groups) {
if(Array.Find(group.Users, delegate(string u) { return u == user; }) != null) {
result.Add(group.Name);
}
}
return result.ToArray();
}
///
/// Loads a proper local instance of a user account.
///
/// The user account.
/// The local instance, or null.
private LocalUserInfo LoadLocalInstance(UserInfo user) {
UserInfo[] users = GetUsers();
UsernameComparer comp = new UsernameComparer();
for(int i = 0; i < users.Length; i++) {
if(comp.Compare(users[i], user) == 0) return users[i] as LocalUserInfo;
}
return null;
}
///
/// Searches for a User.
///
/// The User to search for.
/// True if the User already exists.
private bool UserExists(UserInfo user) {
UserInfo[] users = GetUsers();
UsernameComparer comp = new UsernameComparer();
for(int i = 0; i < users.Length; i++) {
if(comp.Compare(users[i], user) == 0) return true;
}
return false;
}
///
/// Adds a new User.
///
/// The Username.
/// The display name (can be null).
/// The Password.
/// The Email address.
/// A value specifying whether or not the account is active.
/// The Account creation Date/Time.
/// The correct object or null.
/// If username, password or email are null.
/// If username, password or email are empty.
public UserInfo AddUser(string username, string displayName, string password, string email, bool active, DateTime dateTime) {
if(username == null) throw new ArgumentNullException("username");
if(username.Length == 0) throw new ArgumentException("Username cannot be empty", "username");
if(password == null) throw new ArgumentNullException("password");
if(password.Length == 0) throw new ArgumentException("Password cannot be empty", "password");
if(email == null) throw new ArgumentNullException("email");
if(email.Length == 0) throw new ArgumentException("Email cannot be empty", "email");
lock(this) {
if(UserExists(new UserInfo(username, displayName, "", true, DateTime.Now, this))) return null;
BackupUsersFile();
StringBuilder sb = new StringBuilder();
sb.Append(username);
sb.Append("|");
sb.Append(Hash.Compute(password));
sb.Append("|");
sb.Append(email);
sb.Append("|");
sb.Append(active ? "ACTIVE" : "INACTIVE");
sb.Append("|");
sb.Append(dateTime.ToString("yyyy'/'MM'/'dd' 'HH':'mm':'ss"));
// ADMIN|USER no more used in version 3.0
//sb.Append("|");
//sb.Append(admin ? "ADMIN" : "USER");
if(!string.IsNullOrEmpty(displayName)) {
sb.Append("|");
sb.Append(displayName);
}
sb.Append("\r\n");
File.AppendAllText(GetFullPath(UsersFile), sb.ToString());
usersCache = null;
return new LocalUserInfo(username, displayName, email, active, dateTime, this, Hash.Compute(password));
}
}
///
/// Modifies a User.
///
/// The Username of the user to modify.
/// The new display name (can be null).
/// The new Password (null or blank to keep the current password).
/// The new Email address.
/// A value indicating whether the account is active.
/// The correct object or null.
/// If user or newEmail are null.
/// If newEmail is empty.
public UserInfo ModifyUser(UserInfo user, string newDisplayName, string newPassword, string newEmail, bool newActive) {
if(user == null) throw new ArgumentNullException("user");
if(newEmail == null) throw new ArgumentNullException("newEmail");
if(newEmail.Length == 0) throw new ArgumentException("New Email cannot be empty", "newEmail");
lock(this) {
LocalUserInfo local = LoadLocalInstance(user);
if(local == null) return null;
UserInfo[] allUsers = GetUsers();
UsernameComparer comp = new UsernameComparer();
usersCache = null;
for(int i = 0; i < allUsers.Length; i++) {
if(comp.Compare(allUsers[i], user) == 0) {
LocalUserInfo result = new LocalUserInfo(user.Username, newDisplayName, newEmail,
newActive, user.DateTime, this,
string.IsNullOrEmpty(newPassword) ? local.PasswordHash : Hash.Compute(newPassword));
result.Groups = allUsers[i].Groups;
allUsers[i] = result;
DumpUsers(allUsers);
return result;
}
}
}
return null;
}
///
/// Removes a User.
///
/// The User to remove.
/// True if the User has been removed successfully.
/// If user is null.
public bool RemoveUser(UserInfo user) {
if(user == null) throw new ArgumentNullException("user");
lock(this) {
UserInfo[] users = GetUsers();
UsernameComparer comp = new UsernameComparer();
int idx = -1;
for(int i = 0; i < users.Length; i++) {
if(comp.Compare(users[i], user) == 0) {
idx = i;
break;
}
}
if(idx < 0) return false;
// Remove user's data
string lowercaseUsername = user.Username.ToLowerInvariant();
string[] lines = File.ReadAllLines(GetFullPath(UsersDataFile));
List newLines = new List(lines.Length);
string[] fields;
for(int i = 0; i < lines.Length; i++) {
fields = lines[i].Split('|');
if(fields[0].ToLowerInvariant() != lowercaseUsername) {
newLines.Add(lines[i]);
}
}
File.WriteAllLines(GetFullPath(UsersDataFile), newLines.ToArray());
// Remove user
List tmp = new List(users);
tmp.Remove(tmp[idx]);
DumpUsers(tmp.ToArray());
usersCache = null;
}
return true;
}
private void BackupUsersFile() {
lock(this) {
File.Copy(GetFullPath(UsersFile),
GetFullPath(Path.GetFileNameWithoutExtension(UsersFile) +
".bak" + Path.GetExtension(UsersFile)), true);
}
}
///
/// Writes on disk all the Users.
///
/// The User list.
/// This method does not lock resources, therefore a lock is need in the caller.
private void DumpUsers(UserInfo[] users) {
lock(this) {
BackupUsersFile();
StringBuilder sb = new StringBuilder();
for(int i = 0; i < users.Length; i++) {
LocalUserInfo u = (LocalUserInfo)users[i];
sb.Append(u.Username);
sb.Append("|");
sb.Append(u.PasswordHash);
sb.Append("|");
sb.Append(u.Email);
sb.Append("|");
sb.Append(u.Active ? "ACTIVE" : "INACTIVE");
sb.Append("|");
sb.Append(u.DateTime.ToString("yyyy'/'MM'/'dd' 'HH':'mm':'ss"));
// ADMIN|USER no more used in version 3.0
//sb.Append("|");
//sb.Append(u.Admin ? "ADMIN" : "USER");
if(!string.IsNullOrEmpty(u.DisplayName)) {
sb.Append("|");
sb.Append(u.DisplayName);
}
sb.Append("\r\n");
}
File.WriteAllText(GetFullPath(UsersFile), sb.ToString());
}
}
///
/// Finds a user group.
///
/// The name of the group to find.
/// The or null if no data is found.
private UserGroup FindGroup(string name) {
lock(this) {
UserGroup[] allUsers = GetUserGroups();
UserGroupComparer comp = new UserGroupComparer();
UserGroup target = new UserGroup(name, "", this);
foreach(UserGroup g in allUsers) {
if(comp.Compare(g, target) == 0) return g;
}
return null;
}
}
///
/// Gets all the user groups.
///
/// All the groups, sorted by name.
public UserGroup[] GetUserGroups() {
lock(this) {
if(groupsCache == null) {
string[] lines = File.ReadAllLines(GetFullPath(GroupsFile));
UserGroup[] result = new UserGroup[lines.Length];
string[] fields;
string[] users;
for(int count = 0; count < lines.Length; count++) {
// Structure - description can be empty
// Name|Description|User1|User2|...
fields = lines[count].Split('|');
users = new string[fields.Length - 2];
for(int i = 0; i < fields.Length - 2; i++) {
users[i] = fields[i + 2];
}
result[count] = new UserGroup(fields[0], fields[1], this);
result[count].Users = users;
}
Array.Sort(result, new UserGroupComparer());
groupsCache = result;
}
return groupsCache;
}
}
///
/// Adds a new user group.
///
/// The name of the group.
/// The description of the group.
/// The correct object or null.
/// If name or description are null.
/// If name is empty.
public UserGroup AddUserGroup(string name, string description) {
if(name == null) throw new ArgumentNullException("name");
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
if(description == null) throw new ArgumentNullException("description");
lock(this) {
if(FindGroup(name) != null) return null;
BackupGroupsFile();
groupsCache = null;
// Structure - description can be empty
// Name|Description|User1|User2|...
File.AppendAllText(GetFullPath(GroupsFile),
name + "|" + description + "\r\n");
return new UserGroup(name, description, this);
}
}
private void BackupGroupsFile() {
lock(this) {
File.Copy(GetFullPath(GroupsFile),
GetFullPath(Path.GetFileNameWithoutExtension(GroupsFile) +
".bak" + Path.GetExtension(GroupsFile)), true);
}
}
///
/// Dumps user groups on disk.
///
/// The user groups to dump.
private void DumpUserGroups(UserGroup[] groups) {
lock(this) {
StringBuilder sb = new StringBuilder(1000);
foreach(UserGroup group in groups) {
// Structure - description can be empty
// Name|Description|User1|User2|...
sb.Append(group.Name);
sb.Append("|");
sb.Append(group.Description);
if(group.Users.Length > 0) {
foreach(string user in group.Users) {
sb.Append("|");
sb.Append(user);
}
}
sb.Append("\r\n");
}
BackupGroupsFile();
File.WriteAllText(GetFullPath(GroupsFile), sb.ToString());
}
}
///
/// Modifies a user group.
///
/// The group to modify.
/// The new description of the group.
/// The correct object or null.
/// If group or description are null.
public UserGroup ModifyUserGroup(UserGroup group, string description) {
if(group == null) throw new ArgumentNullException("group");
if(description == null) throw new ArgumentNullException("description");
lock(this) {
UserGroup[] allGroups = GetUserGroups();
groupsCache = null;
UserGroupComparer comp = new UserGroupComparer();
for(int i = 0; i < allGroups.Length; i++) {
if(comp.Compare(allGroups[i], group) == 0) {
allGroups[i].Description = description;
DumpUserGroups(allGroups);
return allGroups[i];
}
}
return null;
}
}
///
/// Removes a user group.
///
/// The group to remove.
/// true if the group is removed, false otherwise.
/// If group is null.
public bool RemoveUserGroup(UserGroup group) {
if(group == null) throw new ArgumentNullException("group");
lock(this) {
UserGroup[] allGroups = GetUserGroups();
List result = new List(allGroups.Length);
UserGroupComparer comp = new UserGroupComparer();
foreach(UserGroup g in allGroups) {
if(comp.Compare(g, group) != 0) {
result.Add(g);
}
}
DumpUserGroups(result.ToArray());
groupsCache = null;
return result.Count == allGroups.Length - 1;
}
}
///
/// Sets the group memberships of a user account.
///
/// The user account.
/// The groups the user account is member of.
/// The correct object or null.
/// If user or groups are null.
public UserInfo SetUserMembership(UserInfo user, string[] groups) {
if(user == null) throw new ArgumentNullException("user");
if(groups == null) throw new ArgumentNullException("groups");
lock(this) {
foreach(string g in groups) {
if(FindGroup(g) == null) return null;
}
LocalUserInfo local = LoadLocalInstance(user);
if(local == null) return null;
UserGroup[] allGroups = GetUserGroups();
List users;
for(int i = 0; i < allGroups.Length; i++) {
users = new List(allGroups[i].Users);
if(IsSelected(allGroups[i], groups)) {
// Current group is one of the selected, add user to it
if(!users.Contains(user.Username)) users.Add(user.Username);
}
else {
// Current group is not bound with the user, remove user
users.Remove(user.Username);
}
allGroups[i].Users = users.ToArray();
}
groupsCache = null;
usersCache = null;
DumpUserGroups(allGroups);
LocalUserInfo result = new LocalUserInfo(local.Username, local.DisplayName, local.Email, local.Active,
local.DateTime, this, local.PasswordHash);
result.Groups = groups;
return result;
}
}
///
/// Determines whether a user group is contained in an array of user group names.
///
/// The user group to check.
/// The user group names array.
/// true if users contains user.Name, false otherwise.
private static bool IsSelected(UserGroup group, string[] groups) {
StringComparer comp = StringComparer.OrdinalIgnoreCase;
return Array.Find(groups, delegate(string g) { return comp.Compare(g, group.Name) == 0; }) != null;
}
///
/// Tries to login a user directly through the provider.
///
/// The username.
/// The password.
/// The correct UserInfo object, or null.
/// If username or password are null.
public UserInfo TryManualLogin(string username, string password) {
if(username == null) throw new ArgumentNullException("username");
if(password == null) throw new ArgumentNullException("password");
// Shortcut
if(username.Length == 0) return null;
if(password.Length == 0) return null;
lock(this) {
string hash = Hash.Compute(password);
UserInfo[] all = GetUsers();
foreach(UserInfo u in all) {
if(u.Active &&
//string.Compare(u.Username, username, false, System.Globalization.CultureInfo.InvariantCulture) == 0 &&
//string.Compare(((LocalUserInfo)u).PasswordHash, hash, false, System.Globalization.CultureInfo.InvariantCulture) == 0) {
string.CompareOrdinal(u.Username, username) == 0 &&
string.CompareOrdinal(((LocalUserInfo)u).PasswordHash, hash) == 0) {
return u;
}
}
}
return null;
}
///
/// Tries to login a user directly through the provider using
/// the current HttpContext and without username/password.
///
/// The current HttpContext.
/// The correct UserInfo object, or null.
/// If context is null.
public UserInfo TryAutoLogin(System.Web.HttpContext context) {
if(context == null) throw new ArgumentNullException("context");
return null;
}
///
/// Tries to retrieve the information about a user account.
///
/// The username.
/// The correct UserInfo object, or null.
/// If username is null.
/// If username is empty.
public UserInfo GetUser(string username) {
if(username == null) throw new ArgumentNullException("username");
if(username.Length == 0) throw new ArgumentException("Username cannot be empty", "username");
lock(this) {
UserInfo[] all = GetUsers();
foreach(UserInfo u in all) {
if(string.Compare(u.Username, username, false, System.Globalization.CultureInfo.InvariantCulture) == 0) {
return u;
}
}
}
return null;
}
///
/// Tries to retrieve the information about a user account.
///
/// The email address.
/// The first user found with the specified email address, or null.
/// If email is null.
/// If email is empty.
public UserInfo GetUserByEmail(string email) {
if(email == null) throw new ArgumentNullException("email");
if(email.Length == 0) throw new ArgumentException("Email cannot be empty", "email");
lock(this) {
foreach(UserInfo user in GetUsers()) {
if(user.Email == email) return user;
}
}
return null;
}
///
/// Notifies the provider that a user has logged in through the authentication cookie.
///
/// The user who has logged in.
/// If user is null.
public void NotifyCookieLogin(UserInfo user) {
if(user == null) throw new ArgumentNullException("user");
// Nothing to do
}
///
/// Notifies the provider that a user has logged out.
///
/// The user who has logged out.
/// If user is null.
public void NotifyLogout(UserInfo user) {
if(user == null) throw new ArgumentNullException("user");
// Nothing to do
}
///
/// Stores a user data element, overwriting the previous one if present.
///
/// The user the data belongs to.
/// The key of the data element (case insensitive).
/// The value of the data element, null for deleting the data.
/// true if the data element is stored, false otherwise.
/// If user or key are null.
/// If key is empty.
public bool StoreUserData(UserInfo user, string key, string value) {
if(user == null) throw new ArgumentNullException("user");
if(key == null) throw new ArgumentNullException("key");
if(key.Length == 0) throw new ArgumentException("Key cannot be empty", "key");
// Format
// User|Key|Value
lock(this) {
if(GetUser(user.Username) == null) return false;
// Find a previously existing key and replace it if found
// If not found, add a new line
string lowercaseUsername = user.Username.ToLowerInvariant();
string lowercaseKey = key.ToLowerInvariant();
string[] lines = File.ReadAllLines(GetFullPath(UsersDataFile));
string[] fields;
for(int i = 0; i < lines.Length; i++) {
fields = lines[i].Split('|');
if(fields[0].ToLowerInvariant() == lowercaseUsername && fields[1].ToLowerInvariant() == lowercaseKey) {
if(value != null) {
// Replace the value, then save file
lines[i] = fields[0] + "|" + fields[1] + "|" + value;
}
else {
// Remove the element
string[] newLines = new string[lines.Length - 1];
Array.Copy(lines, newLines, i);
Array.Copy(lines, i + 1, newLines, i, lines.Length - i - 1);
lines = newLines;
}
File.WriteAllLines(GetFullPath(UsersDataFile), lines);
return true;
}
}
// If the program gets here, the element was not present, append it
File.AppendAllText(GetFullPath(UsersDataFile), user.Username + "|" + key + "|" + value + "\r\n");
return true;
}
}
///
/// Gets a user data element, if any.
///
/// The user the data belongs to.
/// The key of the data element.
/// The value of the data element, or null if the element is not found.
/// If user or key are null.
/// If key is empty.
public string RetrieveUserData(UserInfo user, string key) {
if(user == null) throw new ArgumentNullException("user");
if(key == null) throw new ArgumentNullException("key");
if(key.Length == 0) throw new ArgumentException("Key cannot be empty", "key");
lock(this) {
string lowercaseUsername = user.Username.ToLowerInvariant();
string lowercaseKey = key.ToLowerInvariant();
string[] lines = File.ReadAllLines(GetFullPath(UsersDataFile));
string[] fields;
foreach(string line in lines) {
fields = line.Split('|');
if(fields[0].ToLowerInvariant() == lowercaseUsername && fields[1].ToLowerInvariant() == lowercaseKey) {
return fields[2];
}
}
}
return null;
}
///
/// Retrieves all the user data elements for a user.
///
/// The user.
/// The user data elements (key->value).
/// If user is null.
public IDictionary RetrieveAllUserData(UserInfo user) {
if(user == null) throw new ArgumentNullException("user");
lock(this) {
string lowercaseUsername = user.Username.ToLowerInvariant();
string[] lines = File.ReadAllLines(GetFullPath(UsersDataFile));
Dictionary result = new Dictionary(10);
string[] fields;
foreach(string line in lines) {
fields = line.Split('|');
if(fields[0].ToLowerInvariant() == lowercaseUsername) {
result.Add(fields[1], fields[2]);
}
}
return result;
}
}
///
/// Gets all the users that have the specified element in their data.
///
/// The key of the data.
/// The users and the data.
/// If key is null.
/// If key is empty.
public IDictionary GetUsersWithData(string key) {
if(key == null) throw new ArgumentNullException("key");
if(key.Length == 0) throw new ArgumentException("Key cannot be empty", "key");
lock(this) {
UserInfo[] allUsers = GetUsers();
string[] lines = File.ReadAllLines(GetFullPath(UsersDataFile));
Dictionary result = new Dictionary(lines.Length / 4);
string[] fields;
foreach(string line in lines) {
fields = line.Split('|');
if(fields[1] == key) {
UserInfo currentUser = Array.Find(allUsers, delegate(UserInfo user) {
return user.Username == fields[0];
});
if(currentUser != null) result.Add(currentUser, fields[2]);
}
}
return result;
}
}
}
}