Click here to Skip to main content
15,886,567 members
Articles / Mobile Apps

Windows Mobile Password Safe

Rate me:
Please Sign up or sign in to vote.
4.87/5 (58 votes)
12 Jan 2009CPOL16 min read 159.6K   3.1K   139  
A password safe with a touch screen UI introducing Fluid Controls.
using System;

using System.Collections.Generic;
using System.Text;
using PasswordSafe.Xml;
using Fluid.Controls;
using PasswordSafe.Properties;

namespace PasswordSafe.Classes
{
    /// <summary>
    /// Builds lists from data retreived from a PasswordReader to be used in ListBoxes.
    /// </summary>
    public class ListBuilder : IComparer<PasswordData>, IComparer<object>
    {
        private PasswordReader reader;

        private PasswordCategory defaultCategory;

        public PasswordCategory DefaultCategory
        {
            get
            {
                if (defaultCategory == null)
                {
                    CreateDefaultCategory();
                }
                return defaultCategory;
            }
        }

        /// <summary>
        /// Creates a new ListBuilder instance with data from a PasswordReader.
        /// </summary>
        /// <param name="reader">The PasswordReader class from which to retreive the data.</param>
        public ListBuilder(PasswordReader reader)
        {
            this.reader = reader;
        }

        private void CreateDefaultCategory()
        {
            PasswordCategory c = new PasswordCategory("Not Specified");
            c.IsDefault = true;
            c.Fields.Add(new FieldInfo("user", "Username"));
            c.Fields.Add(new FieldInfo("password", "Password"));
            c.Fields.Add(new FieldInfo("email", "EMail"));
            c.Fields.Add(new FieldInfo("url", "Url"));
            c.Fields.Add(new Separator());
            c.Fields.Add(new FieldInfo("info", "Info"));
            defaultCategory = c;
        }

        private NotifyList listBoxCategories;

        /// <summary>
        /// Gets an enumeration of all PasswordCategories.
        /// </summary>
        public IEnumerable<PasswordCategory> Categories
        {
            get
            {
                foreach (object item in listBoxCategories)
                {
                    PasswordCategory category = item as PasswordCategory;
                    if (category != null) yield return category;
                }
            }
        }

        /// <summary>
        /// Gets the list of categories including group headers for listboxes.
        /// </summary>
        public NotifyList ListBoxCategories
        {
            get { return GetCategories(); }
        }

        private NotifyList GetCategories()
        {
            if (listBoxCategories != null) return listBoxCategories;
            List<object> sortList = new List<object>();
            listBoxCategories = new NotifyList();
            foreach (PasswordCategory c in reader.Categories)
            {
                sortList.Add(c);
            }
            sortList.Add(DefaultCategory);
            sortList.Sort(this);
            foreach (object o in sortList) listBoxCategories.Add(o);
            all = new PasswordCategory("all");
            all.passwords = this.Passwords;
            all.ReadOnly = true;
            all.Title = "All Passwords";
            all.IsDefault = true;
            listBoxCategories.Insert(0, all);
            listBoxCategories.Insert(1, new GroupHeader());
            listBoxCategories.ModifyChanged += new EventHandler(ListModifyChanged);

            return listBoxCategories;
        }

        private PasswordCategory all;

        private List<PasswordData> passwords;

        public List<PasswordData> Passwords
        {
            get
            {
                EnsurePasswordList();
                return passwords;
            }
        }

        private bool modified;
        /// <summary>
        /// Gets if the data was modified and needs to be saved.
        /// </summary>
        public bool Modified
        {
            get { return modified; }
            set
            {
                if (modified != value)
                {
                    modified = value;
                    OnModifiedChanged();
                }
            }
        }

        private void OnModifiedChanged()
        {
            if (ModifiedChanged != null) ModifiedChanged(this, EventArgs.Empty);
        }


        public event EventHandler ModifiedChanged;

        void EnsurePasswordList()
        {
            if (passwords == null)
            {
                passwords = new List<PasswordData>();
                PasswordCategory defautCategory = DefaultCategory;
                foreach (PasswordData d in reader.Passwords)
                {
                    if (d.Category == null)
                    {
                        d.Category = defaultCategory;
                    }
                    passwords.Add(d);
                }
                passwords.Sort(this);
            }
        }

        private NotifyList currentPasswords;
        private List<object> passwordCopy = new List<object>();

        public NotifyList GetPasswords(PasswordCategory category, bool headers)
        {
            if (category == all) category = null;
            EnsurePasswordList();
            List<PasswordData> passwords = new List<PasswordData>();
            foreach (PasswordData d in this.passwords)
            {
                if (category == null || d.Category == category)
                {
                    passwords.Add(d);
                }
            }
            passwords.Sort(this);
            NotifyList sorted = new NotifyList();

            foreach (PasswordData d in passwords)
            {
                sorted.Add(d);
            }
            passwords.Clear();
            if (headers && sorted.Count > 3) sorted = AddHeaders(sorted);
            sorted.ModifyChanged += new EventHandler(ListModifyChanged);

            currentPasswords = sorted;
            passwordCopy.Clear();
            foreach (var p in sorted)
            {
                passwordCopy.Add(p);
            }

            return sorted;
        }


        void ListModifyChanged(object sender, EventArgs e)
        {
            Modified = true;
        }

        private NotifyList AddHeaders(NotifyList passwords)
        {
            NotifyList list = new NotifyList();

            string current = "";

            foreach (PasswordData d in passwords)
            {
                string c = d.Name.Substring(0, 1).ToUpper();

                if (current != c)
                {
                    current = c;
                    list.Add(new GroupHeader(c));
                }
                list.Add(d);
            }
            list.ModifyChanged += new EventHandler(ListModifyChanged);
            return list;

        }

        #region IComparer<PasswordData> Members

        int IComparer<PasswordData>.Compare(PasswordData x, PasswordData y)
        {
            return x.Name.CompareTo(y.Name);
        }

        #endregion

        #region IComparer<object> Members

        int IComparer<object>.Compare(object x, object y)
        {
            PasswordCategory a = x as PasswordCategory;
            if (a != null)
            {
                PasswordCategory b = y as PasswordCategory;
                return a.Name.CompareTo(b.Name);
            }
            return 0;
        }

        #endregion

        /// <summary>
        /// Gets or sets the instance for global use.
        /// </summary>
        /// <remarks>
        /// The main form sets the instance while other controls access the instance.
        /// </remarks>
        public static ListBuilder Instance;

        /// <summary>
        /// Creates a list of details of a password.
        /// </summary>
        /// <param name="password">the data of the password.</param>
        /// <param name="newItem">true if this password is new, otherwise false.</param>
        /// <returns></returns>
        public NotifyList GetDetails(PasswordData password, bool newItem)
        {
            NotifyList list = new NotifyList();

            PasswordCategory c = password.Category;

            if (c.Fields.Count == 0) c = DefaultCategory;

            foreach (FieldInfo field in c.Fields)
            {
                Separator separator = field as Separator;
                if (separator == null)
                {
                    string name = field.Name;
                    string value = password.Values[name];

                    PWNameValue item = new PWNameValue(field.Title + ":", field.Name, password);
                    list.Add(item);
                }
                else
                {
                    GroupHeader header = new GroupHeader(separator.Title);
                    list.Add(header);
                }
            }
            if (newItem)
            {
                PWNameValue nameValue = new PWNameValue("Name", "name", password);
                list.Insert(0, nameValue);
                GroupHeader gh = list[1] as GroupHeader;
                if (gh == null)
                {
                    list.Insert(1, new GroupHeader());
                }
            }
            list.Modified = false;
            list.ModifyChanged += new EventHandler(ListModifyChanged);


            return list;
        }

        public PasswordData CreateNewPassword(PasswordCategory category)
        {
            EnsurePasswordList();
            PasswordData data = new PasswordData();
            data.Category = category;
            passwords.Add(data);
            return data;
        }

        public void DeletePassword(PasswordData password)
        {
            password.Category = null;
            Passwords.Remove(password);
        }

        public void AddCategory(PasswordCategory category)
        {
            string text = category.Title;
            for (int i = 2; i < listBoxCategories.Count; i++)
            {
                PasswordCategory c = listBoxCategories[i] as PasswordCategory;
                if (c != null)
                {
                    if (c.Name == category.Name) return; //already in list, so return.
                    if (c.Title.CompareTo(text) > 0)
                    {
                        listBoxCategories.Insert(i, category);
                        return;
                    }
                }
            }
            listBoxCategories.Add(category);
        }

        public void RemoveCategory(PasswordCategory category)
        {
            listBoxCategories.Remove(category);
        }


        private List<CategoryListBoxItem> defaultCategories;

        /// <summary>
        /// Gets the list of all default categories.
        /// </summary>
        public List<CategoryListBoxItem> DefaultCategories
        {
            get
            {
                return GetDefaultCategories();
            }
        }

        private List<CategoryListBoxItem> GetDefaultCategories()
        {
            if (defaultCategories == null)
            {
                defaultCategories = new List<CategoryListBoxItem>();
                string xml = Resources.Categories;
                foreach (PasswordCategory c in PasswordReader.ReadCategories(xml))
                {
                    bool isUsed = false;
                    CategoryListBoxItem item = new CategoryListBoxItem { Category = c, IsUsed = isUsed };
                    defaultCategories.Add(item);
                }
            }
            foreach (var c in defaultCategories)
            {
                c.IsUsed = IsCategoryUsed(c.Category.Name);
            }
            return defaultCategories;
        }

        private bool IsCategoryUsed(string categoryName)
        {
            foreach (var c in Categories)
            {
                if (c.Name.Equals(categoryName, StringComparison.InvariantCultureIgnoreCase)) return true;
            }
            return false;
        }

        internal void AddCategories(List<CategoryListBoxItem> categories)
        {
            listBoxCategories.RaiseListChangedEvents = false;
            foreach (var item in categories)
            {
                if (item.IsUsed)
                {
                    AddCategory(item.Category);
                }
                else
                {
                    PasswordCategory category = GetCategory(item.Category.Name);
                    if (category != null) RemoveCategory(category);
                }
            }
            listBoxCategories.RaiseListChangedEvents = true;
            listBoxCategories.ResetBindings();
        }

        private PasswordCategory GetCategory(string name)
        {
            foreach (var c in this.Categories)
            {
                if (c.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) return c;
            }
            return null;
        }

        char[] matrix;

        private char[] EnsureMatrix()
        {
            if (matrix == null)
            {
                matrix = new char[256];
                for (int i = 0; i <= 255; i++)
                {
                    char c = Char.ToLower(Convert.ToChar(i));

                    if ("1".IndexOf(c) >= 0) c = '1';
                    else if ("2abc".IndexOf(c) >= 0) c = '2';
                    else if ("3def".IndexOf(c) >= 0) c = '3';
                    else if ("3ghi".IndexOf(c) >= 0) c = '4';
                    else if ("5jkl".IndexOf(c) >= 0) c = '5';
                    else if ("6mno".IndexOf(c) >= 0) c = '6';
                    else if ("7pqrs".IndexOf(c) >= 0) c = '7';
                    else if ("8tuv".IndexOf(c) >= 0) c = '8';
                    else if ("9wxyz".IndexOf(c) >= 0) c = '9';
                    else if ("# ".IndexOf(c) >= 0) c = '#';
                    else if ("0+".IndexOf(c) >= 0) c = '0';
                    else if (c == '*') c = '*';
                    else c = '\0';

                    matrix[i] = c;
                }
            }
            return matrix;
        }

        private string MakeSearchable(string text)
        {
            StringBuilder sb = new StringBuilder();

            char[] matrix = EnsureMatrix();
            foreach (char c in text)
            {
                int i = (int)c;
                if (i > 255) i = 255;
                char mc = matrix[i];
                if (mc != '\0')
                {
                    sb.Append(mc);
                }
            }
            return sb.ToString();
        }

        public void Filter(string text)
        {
            if (currentPasswords == null) return;

            bool modified = this.Modified;
            bool empty = string.IsNullOrEmpty(text);
            List<object> toAdd = new List<object>();
            foreach (object o in passwordCopy)
            {
                bool match = empty;
                if (!match)
                {
                    PasswordData p = o as PasswordData;
                    if (p != null)
                    {
                        string compare = MakeSearchable(p.Name);
                        match = compare.IndexOf(text) >= 0;
                    }
                }
                if (match)
                {
                    toAdd.Add(o);
                }
            }
            currentPasswords.RaiseListChangedEvents = false;
            currentPasswords.Clear();
            foreach (object o in toAdd)
            {
                currentPasswords.Add(o);
            }
            currentPasswords.RaiseListChangedEvents = true;
            currentPasswords.ResetBindings();

            // do not accidantely mark the data as begin modified:
            this.Modified = modified;
        }

        public void UnModified()
        {
            Modified = false;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Germany Germany
MCPD
Enterprise Application Developer 3.5
Windows Developer 3.5
.ASP.NET Developer 3.5
.NET 2.0 Windows Developer
.NET 2.0 Web Developer
.NET 2.0 Enterprise Application Developer


MCTS
.NET 3.5 Windows Forms Applications
.NET 3.5 ASP.NET Applications
.NET 3.5, ADO.NET Application Development
.NET 3.5 WCF
.NET 3.5 WPF
.NET 3.5 WF
Microsoft SQL Server 2008, Database Development
.NET 2.0 Windows Applications
.NET 2.0 Web Applications
.NET 2.0 Distributed Applications
SQL Server 2005
Sharepoint Services 3.0 Application Development
Windows Vista Client Configuration

Comments and Discussions