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.ComponentModel;
23 using System.Diagnostics;
24 using System.Drawing;
25 using System.IO;
26 using System.Text;
27 using System.Windows.Forms;
28 
29 using KeePass.App;
30 using KeePass.DataExchange;
31 using KeePass.Resources;
32 using KeePass.UI;
33 
34 using KeePassLib;
35 using KeePassLib.Serialization;
36 using KeePassLib.Utility;
37 
38 namespace KeePass.Forms
39 {
40 	public partial class ExchangeDataForm : Form
41 	{
42 		private bool m_bExport = false;
43 		private PwDatabase m_pd = null;
44 		private PwGroup m_pg = null;
45 
46 		private ImageList m_ilFormats = null;
47 
48 		private FileFormatProvider m_fmtCur = null; // Current selection
49 
50 		private FileFormatProvider m_fmtFinal = null; // Returned as result
51 		private string[] m_vFiles = null;
52 
53 		private sealed class FormatGroupEx
54 		{
55 			private ListViewGroup m_lvg;
56 			public ListViewGroup Group { get { return m_lvg; } }
57 
58 			private List<ListViewItem> m_lItems = new List<ListViewItem>();
59 			public List<ListViewItem> Items { get { return m_lItems; } }
60 
FormatGroupEx(string strGroupName)61 			public FormatGroupEx(string strGroupName)
62 			{
63 				m_lvg = new ListViewGroup(strGroupName);
64 			}
65 		}
66 
67 		public FileFormatProvider ResultFormat
68 		{
69 			get { return m_fmtFinal; }
70 		}
71 
72 		public string[] ResultFiles
73 		{
74 			get { return m_vFiles; }
75 		}
76 
77 		private PwExportInfo m_piExport = null;
78 		internal PwExportInfo ExportInfo
79 		{
80 			get { return m_piExport; }
81 			set { m_piExport = value; }
82 		}
83 
InitEx(bool bExport, PwDatabase pd, PwGroup pg)84 		public void InitEx(bool bExport, PwDatabase pd, PwGroup pg)
85 		{
86 			m_bExport = bExport;
87 			m_pd = pd;
88 			m_pg = pg;
89 		}
90 
ExchangeDataForm()91 		public ExchangeDataForm()
92 		{
93 			InitializeComponent();
94 			GlobalWindowManager.InitializeForm(this);
95 		}
96 
OnFormLoad(object sender, EventArgs e)97 		private void OnFormLoad(object sender, EventArgs e)
98 		{
99 			if((m_pd == null) && (m_pg == null))
100 			{
101 				Debug.Assert(false);
102 				throw new InvalidOperationException();
103 			}
104 
105 			GlobalWindowManager.AddWindow(this);
106 
107 			string strTitle = (m_bExport ? KPRes.ExportFileTitle : KPRes.ImportFileTitle);
108 			string strDesc = (m_bExport ? KPRes.ExportFileDesc : KPRes.ImportFileDesc);
109 			Image img = (m_bExport ? Properties.Resources.B48x48_Folder_Txt :
110 				Properties.Resources.B48x48_Folder_Download);
111 			BannerFactory.CreateBannerEx(this, m_bannerImage, img, strTitle, strDesc);
112 
113 			this.Icon = AppIcons.Default;
114 			this.Text = strTitle;
115 
116 			UIUtil.ConfigureToolTip(m_ttRect);
117 			UIUtil.SetToolTip(m_ttRect, m_btnSelFile, StrUtil.TrimDots(
118 				KPRes.SelectFile, true), true);
119 
120 			m_lvFormats.ShowGroups = true;
121 
122 			int w = m_lvFormats.ClientSize.Width - UIUtil.GetVScrollBarWidth();
123 			m_lvFormats.Columns.Add(string.Empty, w - 1);
124 
125 			List<Image> lImages = new List<Image>();
126 			Dictionary<string, FormatGroupEx> dictGroups =
127 				new Dictionary<string, FormatGroupEx>();
128 
129 			foreach(FileFormatProvider f in Program.FileFormatPool)
130 			{
131 				if(m_bExport && !f.SupportsExport) continue;
132 				if(!m_bExport && !f.SupportsImport) continue;
133 
134 				string strDisplayName = f.DisplayName;
135 				if(string.IsNullOrEmpty(strDisplayName)) { Debug.Assert(false); continue; }
136 
137 				string strAppGroup = f.ApplicationGroup;
138 				if(string.IsNullOrEmpty(strAppGroup)) strAppGroup = KPRes.General;
139 
140 				FormatGroupEx grp;
141 				if(!dictGroups.TryGetValue(strAppGroup, out grp))
142 				{
143 					grp = new FormatGroupEx(strAppGroup);
144 					dictGroups[strAppGroup] = grp;
145 				}
146 
147 				ListViewItem lvi = new ListViewItem(strDisplayName);
148 				lvi.Group = grp.Group;
149 				lvi.Tag = f;
150 
151 				Image imgSmallIcon = f.SmallIcon;
152 				if(imgSmallIcon == null)
153 					imgSmallIcon = Properties.Resources.B16x16_Folder_Inbox;
154 
155 				lvi.ImageIndex = lImages.Count;
156 				lImages.Add(imgSmallIcon);
157 
158 				grp.Items.Add(lvi);
159 			}
160 
161 			foreach(FormatGroupEx formatGroup in dictGroups.Values)
162 			{
163 				m_lvFormats.Groups.Add(formatGroup.Group);
164 				foreach(ListViewItem lvi in formatGroup.Items)
165 					m_lvFormats.Items.Add(lvi);
166 			}
167 
168 			m_ilFormats = UIUtil.BuildImageListUnscaled(lImages,
169 				DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
170 			m_lvFormats.SmallImageList = m_ilFormats;
171 
172 			if(m_bExport)
173 			{
174 				m_grpFiles.Text = KPRes.Destination;
175 				UIUtil.SetText(m_lblFiles, KPRes.File + ":");
176 				UIUtil.SetButtonImage(m_btnSelFile,
177 					Properties.Resources.B16x16_FileSaveAs, false);
178 
179 				m_lnkFileFormats.Enabled = false;
180 				m_lnkFileFormats.Visible = false;
181 			}
182 			else // Import
183 			{
184 				m_grpFiles.Text = KPRes.Source;
185 				UIUtil.SetButtonImage(m_btnSelFile,
186 					Properties.Resources.B16x16_Folder_Yellow_Open, false);
187 
188 				m_grpExport.Enabled = false;
189 				m_grpExportPost.Enabled = false;
190 			}
191 
192 			m_cbExportMasterKeySpec.Checked = Program.Config.Defaults.ExportMasterKeySpec;
193 			m_cbExportParentGroups.Checked = Program.Config.Defaults.ExportParentGroups;
194 			m_cbExportPostOpen.Checked = Program.Config.Defaults.ExportPostOpen;
195 			m_cbExportPostShow.Checked = Program.Config.Defaults.ExportPostShow;
196 
197 			UpdateUIState();
198 		}
199 
CleanUpEx()200 		private void CleanUpEx()
201 		{
202 			Program.Config.Defaults.ExportMasterKeySpec = m_cbExportMasterKeySpec.Checked;
203 			Program.Config.Defaults.ExportParentGroups = m_cbExportParentGroups.Checked;
204 			Program.Config.Defaults.ExportPostOpen = m_cbExportPostOpen.Checked;
205 			Program.Config.Defaults.ExportPostShow = m_cbExportPostShow.Checked;
206 
207 			if(m_ilFormats != null)
208 			{
209 				m_lvFormats.SmallImageList = null; // Detach event handlers
210 				m_ilFormats.Dispose();
211 				m_ilFormats = null;
212 			}
213 		}
214 
OnLinkFileFormats(object sender, LinkLabelLinkClickedEventArgs e)215 		private void OnLinkFileFormats(object sender, LinkLabelLinkClickedEventArgs e)
216 		{
217 			AppHelp.ShowHelp(AppDefs.HelpTopics.ImportExport, null);
218 		}
219 
CheckFilePath(string strPath)220 		private static bool CheckFilePath(string strPath)
221 		{
222 			if(string.IsNullOrEmpty(strPath)) { Debug.Assert(false); return false; }
223 
224 			if(strPath.IndexOf(';') >= 0)
225 			{
226 				MessageService.ShowWarning(strPath, KPRes.FileNameContainsSemicolonError);
227 				return false;
228 			}
229 
230 			return true;
231 		}
232 
OnBtnSelFile(object sender, EventArgs e)233 		private void OnBtnSelFile(object sender, EventArgs e)
234 		{
235 			UpdateUIState();
236 			if(m_fmtCur == null) { Debug.Assert(false); return; }
237 			if(!m_fmtCur.RequiresFile) return; // Break on double-click
238 
239 			string strFormat = m_fmtCur.FormatName;
240 			if(string.IsNullOrEmpty(strFormat)) strFormat = KPRes.Data;
241 
242 			string strExts = m_fmtCur.DefaultExtension;
243 			if(string.IsNullOrEmpty(strExts)) strExts = "export";
244 			string strPriExt = UIUtil.GetPrimaryFileTypeExt(strExts);
245 			if(strPriExt.Length == 0) strPriExt = "export"; // In case of "|"
246 
247 			string strFilter = UIUtil.CreateFileTypeFilter(strExts, strFormat, true);
248 
249 			if(!m_bExport) // Import
250 			{
251 				OpenFileDialogEx ofd = UIUtil.CreateOpenFileDialog(KPRes.Import +
252 					": " + strFormat, strFilter, 1, strPriExt, true,
253 					AppDefs.FileDialogContext.Import);
254 
255 				if(ofd.ShowDialog() != DialogResult.OK) return;
256 
257 				StringBuilder sb = new StringBuilder();
258 				foreach(string str in ofd.FileNames)
259 				{
260 					if(!CheckFilePath(str)) continue;
261 
262 					if(sb.Length != 0) sb.Append(';');
263 					sb.Append(str);
264 				}
265 
266 				string strFiles = sb.ToString();
267 				if(strFiles.Length >= m_tbFile.MaxLength)
268 				{
269 					MessageService.ShowWarning(KPRes.TooManyFilesError);
270 					return;
271 				}
272 
273 				m_tbFile.Text = strFiles;
274 			}
275 			else // Export
276 			{
277 				SaveFileDialogEx sfd = UIUtil.CreateSaveFileDialog(KPRes.Export +
278 					": " + strFormat, null, strFilter, 1, strPriExt,
279 					AppDefs.FileDialogContext.Export);
280 
281 				string strSuggestion = KPRes.Database;
282 				if((m_pd != null) && (m_pd.IOConnectionInfo.Path.Length > 0))
283 					strSuggestion = UrlUtil.StripExtension(UrlUtil.GetFileName(
284 						m_pd.IOConnectionInfo.Path));
285 				sfd.FileName = strSuggestion + "." + strPriExt;
286 
287 				if(sfd.ShowDialog() != DialogResult.OK) return;
288 
289 				string strFile = sfd.FileName;
290 				if(!CheckFilePath(strFile)) return;
291 				m_tbFile.Text = strFile;
292 			}
293 
294 			UpdateUIState();
295 		}
296 
UpdateUIState()297 		private void UpdateUIState()
298 		{
299 			ListView.SelectedListViewItemCollection lvsc = m_lvFormats.SelectedItems;
300 			m_fmtCur = (((lvsc != null) && (lvsc.Count == 1)) ?
301 				(lvsc[0].Tag as FileFormatProvider) : null);
302 			bool bFormat = (m_fmtCur != null);
303 
304 			bool bFileReq = (bFormat && m_fmtCur.RequiresFile);
305 			UIUtil.SetEnabledFast(bFileReq, m_lblFiles, m_tbFile, m_btnSelFile);
306 
307 			bool bExportExt = (m_bExport && (m_piExport != null));
308 			UIUtil.SetEnabledFast((bExportExt && bFormat && m_fmtCur.RequiresKey),
309 				m_cbExportMasterKeySpec, m_lblExportMasterKeySpec);
310 
311 			UIUtil.SetEnabledFast((bExportExt && bFormat && (m_pd != null) &&
312 				(m_pg != m_pd.RootGroup)),
313 				m_cbExportParentGroups, m_lnkExportParentGroups);
314 
315 			UIUtil.SetEnabledFast((bExportExt && bFileReq), m_cbExportPostOpen,
316 				m_cbExportPostShow);
317 
318 			m_btnOK.Enabled = (bFormat && ((m_tbFile.Text.Length != 0) ||
319 				!m_fmtCur.RequiresFile));
320 		}
321 
PrepareExchangeEx()322 		private bool PrepareExchangeEx()
323 		{
324 			UpdateUIState();
325 			if(m_fmtCur == null) return false;
326 
327 			string strFiles = m_tbFile.Text;
328 			string[] vFiles = strFiles.Split(new char[] { ';' },
329 				StringSplitOptions.RemoveEmptyEntries);
330 
331 			if(m_fmtCur.RequiresFile)
332 			{
333 				if(vFiles.Length == 0) return false;
334 
335 				foreach(string strFile in vFiles)
336 				{
337 					IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
338 					if(ioc.IsLocalFile() && !UrlUtil.IsAbsolutePath(strFile))
339 					{
340 						MessageService.ShowWarning(strFile, KPRes.FilePathFullReq);
341 						return false;
342 					}
343 				}
344 
345 				// Allow only one file when exporting
346 				if(m_bExport && !CheckFilePath(strFiles)) return false;
347 			}
348 			else vFiles = new string[0];
349 
350 			if(m_piExport != null)
351 			{
352 				m_piExport.ExportMasterKeySpec = m_cbExportMasterKeySpec.Checked;
353 				m_piExport.ExportParentGroups = m_cbExportParentGroups.Checked;
354 				m_piExport.ExportPostOpen = m_cbExportPostOpen.Checked;
355 				m_piExport.ExportPostShow = m_cbExportPostShow.Checked;
356 			}
357 
358 			m_fmtFinal = m_fmtCur;
359 			m_vFiles = vFiles;
360 			return true;
361 		}
362 
OnBtnOK(object sender, EventArgs e)363 		private void OnBtnOK(object sender, EventArgs e)
364 		{
365 			if(!PrepareExchangeEx()) this.DialogResult = DialogResult.None;
366 		}
367 
OnBtnCancel(object sender, EventArgs e)368 		private void OnBtnCancel(object sender, EventArgs e)
369 		{
370 		}
371 
OnFormatsSelectedIndexChanged(object sender, EventArgs e)372 		private void OnFormatsSelectedIndexChanged(object sender, EventArgs e)
373 		{
374 			UpdateUIState();
375 		}
376 
OnFormClosed(object sender, FormClosedEventArgs e)377 		private void OnFormClosed(object sender, FormClosedEventArgs e)
378 		{
379 			CleanUpEx();
380 			GlobalWindowManager.RemoveWindow(this);
381 		}
382 
OnImportFileTextChanged(object sender, EventArgs e)383 		private void OnImportFileTextChanged(object sender, EventArgs e)
384 		{
385 			UpdateUIState();
386 		}
387 
OnFormatsItemActivate(object sender, EventArgs e)388 		private void OnFormatsItemActivate(object sender, EventArgs e)
389 		{
390 			OnBtnSelFile(sender, e);
391 		}
392 
OnLinkExportParentGroups(object sender, LinkLabelLinkClickedEventArgs e)393 		private void OnLinkExportParentGroups(object sender, LinkLabelLinkClickedEventArgs e)
394 		{
395 			AppHelp.ShowHelp(AppDefs.HelpTopics.ImportExport,
396 				AppDefs.HelpTopics.ImportExportParents);
397 		}
398 	}
399 }
400