1 /*
2   KeePass Password Safe - The Open-Source Password Manager
3   Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
4 
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 
20 using System;
21 using System.Collections.Generic;
22 using System.Diagnostics;
23 using System.Drawing;
24 using System.IO;
25 using System.Text;
26 
27 using KeePass.Resources;
28 using KeePass.Util;
29 
30 using KeePassLib;
31 using KeePassLib.Interfaces;
32 using KeePassLib.Utility;
33 
34 namespace KeePass.DataExchange.Formats
35 {
36 	// 16.0.0+
37 	internal sealed class KeeperJson16 : FileFormatProvider
38 	{
39 		public override bool SupportsImport { get { return true; } }
40 		public override bool SupportsExport { get { return false; } }
41 
42 		public override string FormatName { get { return "Keeper JSON"; } }
43 		public override string DefaultExtension { get { return "json"; } }
44 		public override string ApplicationGroup { get { return KPRes.PasswordManagers; } }
45 
46 		public override Image SmallIcon
47 		{
48 			get { return KeePass.Properties.Resources.B16x16_Imp_Keeper; }
49 		}
50 
Import(PwDatabase pwStorage, Stream sInput, IStatusLogger slLogger)51 		public override void Import(PwDatabase pwStorage, Stream sInput,
52 			IStatusLogger slLogger)
53 		{
54 			using(StreamReader sr = new StreamReader(sInput, StrUtil.Utf8, true))
55 			{
56 				string str = sr.ReadToEnd();
57 				if(string.IsNullOrEmpty(str)) { Debug.Assert(false); return; }
58 
59 				CharStream cs = new CharStream(str);
60 				JsonObject joRoot = new JsonObject(cs);
61 
62 				JsonObject[] v = joRoot.GetValueArray<JsonObject>("records");
63 				ImportRecords(v, pwStorage);
64 			}
65 		}
66 
ImportRecords(JsonObject[] v, PwDatabase pd)67 		private static void ImportRecords(JsonObject[] v, PwDatabase pd)
68 		{
69 			if(v == null) { Debug.Assert(false); return; }
70 
71 			Dictionary<string, PwGroup> dGroups = new Dictionary<string, PwGroup>();
72 			string strGroupSep = MemUtil.ByteArrayToHexString(Guid.NewGuid().ToByteArray());
73 			string strBackspCode = MemUtil.ByteArrayToHexString(Guid.NewGuid().ToByteArray());
74 
75 			foreach(JsonObject jo in v)
76 			{
77 				if(jo == null) { Debug.Assert(false); continue; }
78 
79 				PwEntry pe = new PwEntry(true, true);
80 
81 				ImportUtil.AppendToField(pe, PwDefs.TitleField,
82 					jo.GetValue<string>("title"), pd);
83 				ImportUtil.AppendToField(pe, PwDefs.UserNameField,
84 					jo.GetValue<string>("login"), pd);
85 				ImportUtil.AppendToField(pe, PwDefs.PasswordField,
86 					jo.GetValue<string>("password"), pd);
87 				ImportUtil.AppendToField(pe, PwDefs.UrlField,
88 					jo.GetValue<string>("login_url"), pd);
89 				ImportUtil.AppendToField(pe, PwDefs.NotesField,
90 					jo.GetValue<string>("notes"), pd);
91 
92 				JsonObject joCustom = jo.GetValue<JsonObject>("custom_fields");
93 				if(joCustom != null)
94 				{
95 					foreach(KeyValuePair<string, object> kvp in joCustom.Items)
96 					{
97 						string strValue = (kvp.Value as string);
98 						if(strValue == null) { Debug.Assert(false); continue; }
99 
100 						if(kvp.Key == "TFC:Keeper")
101 							EntryUtil.ImportOtpAuth(pe, strValue, pd);
102 						else ImportUtil.AppendToField(pe, kvp.Key, strValue, pd);
103 					}
104 				}
105 
106 				PwGroup pg = null;
107 				JsonObject[] vFolders = jo.GetValueArray<JsonObject>("folders");
108 				if((vFolders != null) && (vFolders.Length >= 1))
109 				{
110 					JsonObject joFolder = vFolders[0];
111 					if(joFolder != null)
112 					{
113 						string strGroup = joFolder.GetValue<string>("folder");
114 						if(!string.IsNullOrEmpty(strGroup))
115 						{
116 							strGroup = strGroup.Replace("\\\\", strBackspCode);
117 							strGroup = strGroup.Replace("\\", strGroupSep);
118 							strGroup = strGroup.Replace(strBackspCode, "\\");
119 
120 							if(!dGroups.TryGetValue(strGroup, out pg))
121 							{
122 								pg = pd.RootGroup.FindCreateSubTree(strGroup,
123 									new string[] { strGroupSep }, true);
124 								dGroups[strGroup] = pg;
125 							}
126 						}
127 						else { Debug.Assert(false); }
128 					}
129 					else { Debug.Assert(false); }
130 				}
131 				if(pg == null) pg = pd.RootGroup;
132 
133 				pg.AddEntry(pe, true);
134 			}
135 		}
136 	}
137 }
138