added option to add built-in template entries to database

This commit is contained in:
Philipp Crocoll 2016-01-08 20:18:34 +01:00
parent 5d8aa8afe0
commit 8fa15e3ba6
12 changed files with 556 additions and 24 deletions

View File

@ -23,5 +23,6 @@ namespace KeePassLib
bool SupportsTags { get; } bool SupportsTags { get; }
bool SupportsOverrideUrl { get; } bool SupportsOverrideUrl { get; }
bool CanRecycle { get; } bool CanRecycle { get; }
bool SupportsTemplates { get; }
} }
} }

View File

@ -56,6 +56,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="database\CheckDatabaseForChanges.cs" /> <Compile Include="database\CheckDatabaseForChanges.cs" />
<Compile Include="database\edit\AddTemplateEntries.cs" />
<Compile Include="database\edit\CopyEntry.cs" /> <Compile Include="database\edit\CopyEntry.cs" />
<Compile Include="database\edit\DeleteMultipleItems.cs" /> <Compile Include="database\edit\DeleteMultipleItems.cs" />
<Compile Include="database\edit\EditGroup.cs" /> <Compile Include="database\edit\EditGroup.cs" />

View File

@ -63,6 +63,24 @@ namespace keepass2android
AskDeletePermanentlyItems, AskDeletePermanentlyItems,
AskDeletePermanentlyItemsNoRecycle, AskDeletePermanentlyItemsNoRecycle,
InOfflineMode, InOfflineMode,
DuplicateTitle DuplicateTitle,
TemplateTitle_IdCard,
TemplateField_IdCard_Name,
TemplateField_IdCard_PlaceOfIssue,
TemplateField_IdCard_IssueDate,
TemplateTitle_EMail,
TemplateField_EMail_EMail,
TemplateTitle_WLan,
TemplateTitle_Notes,
TemplateField_WLan_SSID,
TemplateField_Number,
TemplateField_CreditCard_CVV,
TemplateField_CreditCard_PIN,
TemplateField_CreditCard_Owner,
TemplateTitle_CreditCard,
TemplateTitle_Membership,
TemplateGroupName,
AskAddTemplatesTitle,
AskAddTemplatesMessage
} }
} }

View File

@ -339,6 +339,11 @@ namespace keepass2android
get { return false; } get { return false; }
} }
public bool SupportsTemplates
{
get { return false; }
}
private void AssignParent(PwGroup kpParent, PwDatabaseV3 dbV3, Dictionary<int, PwGroupV3> groupV3s) private void AssignParent(PwGroup kpParent, PwDatabaseV3 dbV3, Dictionary<int, PwGroupV3> groupV3s)
{ {
PwGroupV3 parentV3; PwGroupV3 parentV3;

View File

@ -78,5 +78,10 @@ namespace keepass2android
{ {
get { return true; } get { return true; }
} }
public bool SupportsTemplates
{
get { return true; }
}
} }
} }

View File

@ -0,0 +1,373 @@
/*
This file is part of Keepass2Android, Copyright 2016 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using Android.Content;
using KeePassLib;
using KeePassLib.Security;
using KeePassLib.Utility;
namespace keepass2android
{
public class AddTemplateEntries : RunnableOnFinish {
class TemplateEntry
{
public UiStringKey Title { get; set; }
public PwIcon Icon { get; set; }
public PwUuid Uuid { get; set; }
public List<ITemplateField> Fields
{
get;
set;
}
public interface ITemplateField
{
void AddToEntry(IKp2aApp app, PwEntry entry, int position);
}
internal enum FieldType
{
Inline, ProtectedInline
}
internal enum SpecialFieldKey
{
ExpDate, OverrideUrl, Tags
}
public class CustomField : ITemplateField
{
public UiStringKey FieldName { get; set; }
public FieldType Type { get; set; }
public void AddToEntry(IKp2aApp app, PwEntry entry, int position)
{
Dictionary<FieldType, string> fn = new Dictionary<FieldType, string>()
{
{ FieldType.ProtectedInline, "Protected Inline"},
{ FieldType.Inline, "Inline"}
};
string fieldKey = app.GetResourceString(FieldName);
entry.Strings.Set("_etm_position_"+fieldKey, new ProtectedString(false, position.ToString()));
entry.Strings.Set("_etm_title_"+fieldKey, new ProtectedString(false, fieldKey));
entry.Strings.Set("_etm_type_"+fieldKey, new ProtectedString(false, fn[Type]));
}
}
public class StandardField : ITemplateField
{
public string FieldName { get; set; }
public void AddToEntry(IKp2aApp app, PwEntry entry, int position)
{
string fieldKey = FieldName;
entry.Strings.Set("_etm_position_"+fieldKey, new ProtectedString(false, position.ToString()));
entry.Strings.Set("_etm_title_"+fieldKey, new ProtectedString(false, fieldKey));
entry.Strings.Set("_etm_type_"+fieldKey, new ProtectedString(false, FieldName == PwDefs.PasswordField ? "Protected Inline" : "Inline"));
}
}
public class SpecialField : ITemplateField
{
public SpecialFieldKey FieldName { get; set; }
public const string TagsKey = "@tags";
public const string OverrideUrlKey = "@override";
public const string ExpDateKey = "@exp_date";
public void AddToEntry(IKp2aApp app, PwEntry entry, int position)
{
string fieldKey = "";
string type = "Inline";
switch (FieldName)
{
case SpecialFieldKey.ExpDate:
fieldKey = ExpDateKey;
type = "Date Time";
break;
case SpecialFieldKey.OverrideUrl:
fieldKey = OverrideUrlKey;
break;
case SpecialFieldKey.Tags:
fieldKey = TagsKey;
break;
}
entry.Strings.Set("_etm_position_" + fieldKey, new ProtectedString(false, position.ToString()));
entry.Strings.Set("_etm_title_" + fieldKey, new ProtectedString(false, fieldKey));
entry.Strings.Set("_etm_type_" + fieldKey, new ProtectedString(false, type));
}
}
}
protected Database Db
{
get { return _app.GetDb(); }
}
private readonly IKp2aApp _app;
private readonly Context _ctx;
public AddTemplateEntries(Context ctx, IKp2aApp app, OnFinish finish)
: base(finish)
{
_ctx = ctx;
_app = app;
//_onFinishToRun = new AfterAdd(this, OnFinishToRun);
}
static readonly List<TemplateEntry> TemplateEntries = new List<TemplateEntry>()
{
new TemplateEntry()
{
Title = UiStringKey.TemplateTitle_IdCard,
Icon = PwIcon.Identity,
Uuid = new PwUuid(MemUtil.HexStringToByteArray("A7B525BD0CECC84EB9F0CEDC0B49B5B8")),
Fields = new List<TemplateEntry.ITemplateField>()
{
new TemplateEntry.CustomField()
{
FieldName = UiStringKey.TemplateField_Number,
Type = TemplateEntry.FieldType.Inline
},
new TemplateEntry.CustomField()
{
FieldName = UiStringKey.TemplateField_IdCard_Name,
Type = TemplateEntry.FieldType.Inline
},
new TemplateEntry.CustomField()
{
FieldName = UiStringKey.TemplateField_IdCard_PlaceOfIssue,
Type = TemplateEntry.FieldType.Inline
},
new TemplateEntry.CustomField()
{
FieldName = UiStringKey.TemplateField_IdCard_IssueDate,
Type = TemplateEntry.FieldType.Inline
},
new TemplateEntry.SpecialField()
{
FieldName = TemplateEntry.SpecialFieldKey.ExpDate
}
}
},
new TemplateEntry()
{
Title = UiStringKey.TemplateTitle_EMail,
Icon = PwIcon.EMail,
Uuid = new PwUuid(MemUtil.HexStringToByteArray("0B84EC3029E330478CD99B670942295B")),
Fields = new List<TemplateEntry.ITemplateField>()
{
new TemplateEntry.CustomField()
{
FieldName = UiStringKey.TemplateField_EMail_EMail,
Type = TemplateEntry.FieldType.Inline
},
new TemplateEntry.StandardField()
{
FieldName = PwDefs.UrlField
},
new TemplateEntry.StandardField()
{
FieldName = PwDefs.PasswordField
}
}
},
new TemplateEntry()
{
Title = UiStringKey.TemplateTitle_WLan,
Icon = PwIcon.IRCommunication,
Uuid = new PwUuid(MemUtil.HexStringToByteArray("46B56A7E90407545B646E8DC488A5FA2")),
Fields = new List<TemplateEntry.ITemplateField>()
{
new TemplateEntry.CustomField()
{
FieldName = UiStringKey.TemplateField_WLan_SSID,
Type = TemplateEntry.FieldType.Inline
},
new TemplateEntry.StandardField()
{
FieldName = PwDefs.PasswordField
}
}
},
new TemplateEntry()
{
Title = UiStringKey.TemplateTitle_Notes,
Icon = PwIcon.Notepad,
Uuid = new PwUuid(MemUtil.HexStringToByteArray("10F8C25C26AE9B49A47FDA7CDACACEE2")),
Fields = new List<TemplateEntry.ITemplateField>()
{
new TemplateEntry.StandardField()
{
FieldName = PwDefs.NotesField
}
}
},
new TemplateEntry()
{
Title = UiStringKey.TemplateTitle_CreditCard,
Icon = PwIcon.Homebanking,
Uuid = new PwUuid(MemUtil.HexStringToByteArray("49DD48DBFF149445B3392CE90EA75309")),
Fields = new List<TemplateEntry.ITemplateField>()
{
new TemplateEntry.CustomField()
{
FieldName = UiStringKey.TemplateField_Number,
Type = TemplateEntry.FieldType.Inline
},
new TemplateEntry.CustomField()
{
FieldName = UiStringKey.TemplateField_CreditCard_CVV,
Type = TemplateEntry.FieldType.ProtectedInline
},
new TemplateEntry.CustomField()
{
FieldName = UiStringKey.TemplateField_CreditCard_PIN,
Type = TemplateEntry.FieldType.ProtectedInline
},
new TemplateEntry.CustomField()
{
FieldName = UiStringKey.TemplateField_CreditCard_Owner,
Type = TemplateEntry.FieldType.Inline
},
new TemplateEntry.SpecialField() { FieldName = TemplateEntry.SpecialFieldKey.ExpDate}
}
},
new TemplateEntry()
{
Title = UiStringKey.TemplateTitle_Membership,
Icon = PwIcon.UserKey,
Uuid = new PwUuid(MemUtil.HexStringToByteArray("DD5C627BC66C28498FDEC70740D29168")),
Fields = new List<TemplateEntry.ITemplateField>()
{
new TemplateEntry.CustomField()
{
FieldName = UiStringKey.TemplateField_Number,
Type = TemplateEntry.FieldType.Inline
},
new TemplateEntry.StandardField()
{
FieldName = PwDefs.UrlField
},
new TemplateEntry.SpecialField()
{
FieldName = TemplateEntry.SpecialFieldKey.ExpDate
}
}}
};
public static bool ContainsAllTemplates(IKp2aApp app)
{
return TemplateEntries.All(t => app.GetDb().Entries.ContainsKey(t.Uuid));
}
public override void Run() {
StatusLogger.UpdateMessage(UiStringKey.AddingEntry);
if (TemplateEntries.GroupBy(e => e.Uuid).Any(g => g.Count() > 1))
{
throw new Exception("invalid UUIDs in template list!");
}
PwGroup templateGroup;
if (!_app.GetDb().Groups.TryGetValue(_app.GetDb().KpDatabase.EntryTemplatesGroup, out templateGroup))
{
//create template group
templateGroup = new PwGroup(true, true, _app.GetResourceString(UiStringKey.TemplateGroupName), PwIcon.Folder);
_app.GetDb().KpDatabase.RootGroup.AddGroup(templateGroup, true);
_app.GetDb().KpDatabase.EntryTemplatesGroup = templateGroup.Uuid;
_app.GetDb().KpDatabase.EntryTemplatesGroupChanged = DateTime.Now;
_app.GetDb().Dirty.Add(_app.GetDb().KpDatabase.RootGroup);
_app.GetDb().Groups[templateGroup.Uuid] = templateGroup;
}
List<PwEntry> addedEntries = new List<PwEntry>();
foreach (var template in TemplateEntries)
{
if (_app.GetDb().Entries.ContainsKey(template.Uuid))
continue;
PwEntry entry = CreateEntry(template);
templateGroup.AddEntry(entry, true);
addedEntries.Add(entry);
_app.GetDb().Entries[entry.Uuid] = entry;
}
if (addedEntries.Any())
{
_app.GetDb().Dirty.Add(templateGroup);
// Commit to disk
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
save.SetStatusLogger(StatusLogger);
save.Run();
}
}
private PwEntry CreateEntry(TemplateEntry template)
{
PwEntry entry = new PwEntry(false, true);
entry.Uuid = template.Uuid;
entry.IconId = template.Icon;
entry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, _app.GetResourceString(template.Title)));
entry.Strings.Set("_etm_template", new ProtectedString(false, "1"));
int position = 0;
foreach (var field in template.Fields)
{
field.AddToEntry(_app, entry, position);
position++;
}
return entry;
}
private class AfterAdd : OnFinish {
private readonly Database _db;
private readonly List<PwEntry> _entries;
public AfterAdd(Database db, List<PwEntry> entries, OnFinish finish):base(finish) {
_db = db;
_entries = entries;
}
public override void Run() {
base.Run();
}
}
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using KeePassLib;
using KeePassLib.Security;
namespace keepass2android.database.edit
{
public class CopyEntry: AddEntry
{
public CopyEntry(Context ctx, IKp2aApp app, PwEntry entry, OnFinish finish)
: base(ctx, app, CreateCopy(entry, app), entry.ParentGroup, finish)
{
}
private static PwEntry CreateCopy(PwEntry entry, IKp2aApp app)
{
var newEntry = entry.CloneDeep();
newEntry.SetUuid(new PwUuid(true), true); // Create new UUID
string strTitle = newEntry.Strings.ReadSafe(PwDefs.TitleField);
newEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(
false, strTitle + " - " + app.GetResourceString(UiStringKey.DuplicateTitle)));
return newEntry;
}
}
}

View File

@ -249,6 +249,12 @@ namespace keepass2android
PwDatabase pd = Db.KpDatabase; PwDatabase pd = Db.KpDatabase;
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true); PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
if (pg.Uuid.Equals(pd.EntryTemplatesGroup))
{
pd.EntryTemplatesGroup = PwUuid.Zero;
pd.EntryTemplatesGroupChanged = DateTime.Now;
}
pgParent.Groups.Remove(pg); pgParent.Groups.Remove(pg);
touchedGroups.Add(pgParent); touchedGroups.Add(pgParent);
if ((DeletePermanently) || (!CanRecycle)) if ((DeletePermanently) || (!CanRecycle))

View File

@ -60,7 +60,8 @@ namespace keepass2android
private const String Tag = "Group Activity:"; private const String Tag = "Group Activity:";
private const string Askaddtemplates = "AskAddTemplates";
public static void Launch(Activity act, AppTask appTask) { public static void Launch(Activity act, AppTask appTask) {
Launch(act, null, appTask); Launch(act, null, appTask);
} }
@ -220,26 +221,34 @@ namespace keepass2android
View addEntry = FindViewById (Resource.Id.fabAddNewEntry); View addEntry = FindViewById (Resource.Id.fabAddNewEntry);
addEntry.Click += (sender, e) => addEntry.Click += (sender, e) =>
{ {
PwEntry defaultTemplate = new PwEntry(false, false); if (App.Kp2a.GetDb().DatabaseFormat.SupportsTemplates &&
defaultTemplate.IconId = PwIcon.Key; !AddTemplateEntries.ContainsAllTemplates(App.Kp2a) &&
defaultTemplate.Strings.Set(PwDefs.TitleField, new ProtectedString(false, GetString(Resource.String.DefaultTemplate))); PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(Askaddtemplates, true))
List<PwEntry> templates = new List<PwEntry>() { defaultTemplate };
if (!PwUuid.Zero.Equals(App.Kp2a.GetDb().KpDatabase.EntryTemplatesGroup))
{ {
templates.AddRange(App.Kp2a.GetDb().Groups[App.Kp2a.GetDb().KpDatabase.EntryTemplatesGroup].Entries.OrderBy(entr => entr.Strings.ReadSafe(PwDefs.TitleField))); App.Kp2a.AskYesNoCancel(UiStringKey.AskAddTemplatesTitle, UiStringKey.AskAddTemplatesMessage,UiStringKey.yes, UiStringKey.no,
} (o, args) =>
new AlertDialog.Builder(this)
.SetAdapter(new TemplateListAdapter(this, Android.Resource.Layout.SelectDialogItem,
Android.Resource.Id.Text1, templates), (o, args) =>
{ {
//yes
EntryEditActivity.Launch(this, Group, templates[args.Which].Uuid, AppTask); ProgressTask pt = new ProgressTask(App.Kp2a, this,
}) new AddTemplateEntries(this, App.Kp2a, new ActionOnFinish(
.Show(); delegate
{
StartAddEntry();
})));
pt.Run();
},
(o, args) =>
{
var edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit();
edit.PutBoolean(Askaddtemplates, false);
edit.Commit();
//no
StartAddEntry();
},null, this);
}
else
StartAddEntry();
}; };
} }
@ -252,6 +261,33 @@ namespace keepass2android
} }
private void StartAddEntry()
{
PwEntry defaultTemplate = new PwEntry(false, false);
defaultTemplate.IconId = PwIcon.Key;
defaultTemplate.Strings.Set(PwDefs.TitleField, new ProtectedString(false, GetString(Resource.String.DefaultTemplate)));
List<PwEntry> templates = new List<PwEntry>() {defaultTemplate};
if ((!PwUuid.Zero.Equals(App.Kp2a.GetDb().KpDatabase.EntryTemplatesGroup))
&& (App.Kp2a.GetDb().KpDatabase.RootGroup.FindGroup(App.Kp2a.GetDb().KpDatabase.EntryTemplatesGroup, true) != null))
{
templates.AddRange(
App.Kp2a.GetDb().Groups[App.Kp2a.GetDb().KpDatabase.EntryTemplatesGroup].Entries.OrderBy(
entr => entr.Strings.ReadSafe(PwDefs.TitleField)));
}
if (templates.Count > 1)
{
new AlertDialog.Builder(this)
.SetAdapter(new TemplateListAdapter(this, Android.Resource.Layout.SelectDialogItem,
Android.Resource.Id.Text1, templates),
(o, args) => { EntryEditActivity.Launch(this, Group, templates[args.Which].Uuid, AppTask); })
.Show();
}
else
{
EntryEditActivity.Launch(this, Group, PwUuid.Zero, AppTask);
}
}
public override void OnCreateContextMenu(IContextMenu menu, View v, public override void OnCreateContextMenu(IContextMenu menu, View v,
IContextMenuContextMenuInfo menuInfo) { IContextMenuContextMenuInfo menuInfo) {

View File

@ -579,10 +579,33 @@
<string name="DuplicateTitle">Copy</string> <string name="DuplicateTitle">Copy</string>
<string name="DefaultTemplate">Standard-Eintrag</string> <string name="DefaultTemplate">Standard entry</string>
<string name="TemplateGroupName">Templates</string>
<string name="TemplateTitle_IdCard">ID card</string>
<string name="TemplateField_IdCard_Name">Name</string>
<string name="TemplateField_IdCard_PlaceOfIssue">Place of issue</string>
<string name="TemplateField_IdCard_IssueDate">Date of issue</string>
<string name="TemplateTitle_EMail">E-Mail</string>
<string name="TemplateField_EMail_EMail">E-Mail address</string>
<string name="TemplateTitle_WLan">Wireless LAN</string>
<string name="TemplateTitle_Notes">Secure note</string>
<string name="TemplateField_WLan_SSID">SSID</string>
<string name="TemplateField_Number">Number</string>
<string name="TemplateField_CreditCard_CVV">CVV</string>
<string name="TemplateField_CreditCard_PIN">PIN</string>
<string name="TemplateField_CreditCard_Owner">Card holder</string>
<string name="TemplateTitle_CreditCard">Credit card</string>
<string name="TemplateTitle_Membership">Membership</string>
<string name="ChangeLog_title">Change log</string> <string name="ChangeLog_title">Change log</string>
<string name="AskAddTemplatesTitle">Add templates?</string>
<string name="AskAddTemplatesMessage">Keepass2Android contains entry templates for E-Mail accounts, Wireless-LAN passwords, secure notes and more. Would you like to add these to your database? If you choose No, you can add them later in the database settings.</string>
<string name="AddTemplates_pref">Add templates to database</string>
<string name="PreviewWarning">Please note! This is a preview release and might come with some flaws! If you experience *anything* unexpected, please let me know (on Codeplex or by email).</string> <string name="PreviewWarning">Please note! This is a preview release and might come with some flaws! If you experience *anything* unexpected, please let me know (on Codeplex or by email).</string>

View File

@ -58,8 +58,13 @@
> >
<intent android:action="kp2a.action.FingerprintSetupActivity"/> <intent android:action="kp2a.action.FingerprintSetupActivity"/>
</PreferenceScreen> </PreferenceScreen>
<Preference android:key="AddTemplates_pref_key"
android:title="@string/AddTemplates_pref"
/>
<PreferenceScreen
<PreferenceScreen
android:key="export_prefs" android:key="export_prefs"
android:title="@string/export_prefs" android:title="@string/export_prefs"
> >

View File

@ -351,6 +351,7 @@ namespace keepass2android
PrepareDefaultUsername(db); PrepareDefaultUsername(db);
PrepareDatabaseName(db); PrepareDatabaseName(db);
PrepareMasterPassword(); PrepareMasterPassword();
PrepareTemplates(db);
Preference algorithm = FindPreference(GetString(Resource.String.algorithm_key)); Preference algorithm = FindPreference(GetString(Resource.String.algorithm_key));
SetAlgorithm(db, algorithm); SetAlgorithm(db, algorithm);
@ -434,7 +435,30 @@ namespace keepass2android
} }
private void PrepareMasterPassword() private void PrepareTemplates(Database db)
{
Preference pref = FindPreference("AddTemplates_pref_key");
if ((!db.DatabaseFormat.SupportsTemplates) || (AddTemplateEntries.ContainsAllTemplates(App.Kp2a)))
{
pref.Enabled = false;
}
else
{
pref.PreferenceClick += (sender, args) =>
{
ProgressTask pt = new ProgressTask(App.Kp2a, Activity,
new AddTemplateEntries(Activity, App.Kp2a, new ActionOnFinish(
delegate
{
pref.Enabled = false;
})));
pt.Run();
};
}
}
private void PrepareMasterPassword()
{ {
Preference changeMaster = FindPreference(GetString(Resource.String.master_pwd_key)); Preference changeMaster = FindPreference(GetString(Resource.String.master_pwd_key));
if (App.Kp2a.GetDb().CanWrite) if (App.Kp2a.GetDb().CanWrite)