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.Globalization;
26 using System.IO;
27 using System.Text;
28 using System.Threading;
29 using System.Windows.Forms;
30 using System.Xml;
31 
32 using KeePass.App;
33 using KeePass.Resources;
34 using KeePass.UI;
35 using KeePass.Util;
36 
37 using KeePassLib;
38 using KeePassLib.Delegates;
39 using KeePassLib.Native;
40 using KeePassLib.Serialization;
41 using KeePassLib.Utility;
42 
43 namespace KeePass.Forms
44 {
45 	public partial class IOConnectionForm : Form
46 	{
47 		private bool m_bSave = false;
48 		private IOConnectionInfo m_ioc = new IOConnectionInfo();
49 		private bool m_bCanRememberCred = true;
50 		private bool m_bTestConnection = false;
51 
52 		private List<KeyValuePair<IocPropertyInfo, Control>> m_lProps =
53 			new List<KeyValuePair<IocPropertyInfo, Control>>();
54 
55 		public IOConnectionInfo IOConnectionInfo
56 		{
57 			get { return m_ioc; }
58 		}
59 
InitEx(bool bSave, IOConnectionInfo ioc, bool bCanRememberCred, bool bTestConnection)60 		public void InitEx(bool bSave, IOConnectionInfo ioc, bool bCanRememberCred,
61 			bool bTestConnection)
62 		{
63 			m_bSave = bSave;
64 			if(ioc != null) m_ioc = ioc.CloneDeep();
65 			m_bCanRememberCred = bCanRememberCred;
66 			m_bTestConnection = bTestConnection;
67 		}
68 
IOConnectionForm()69 		public IOConnectionForm()
70 		{
71 			InitializeComponent();
72 
73 			SecureTextBoxEx.InitEx(ref m_tbPassword);
74 			GlobalWindowManager.InitializeForm(this);
75 		}
76 
OnFormLoad(object sender, EventArgs e)77 		private void OnFormLoad(object sender, EventArgs e)
78 		{
79 			// Must work without a parent window
80 			Debug.Assert(this.StartPosition == FormStartPosition.CenterScreen);
81 
82 			// The password text box should not be focused by default
83 			// in order to avoid a Caps Lock warning tooltip bug;
84 			// https://sourceforge.net/p/keepass/bugs/1807/
85 			Debug.Assert((m_tbPassword.TabIndex >= 2) && !m_tbPassword.Focused);
86 
87 			InitAdvancedTab(); // After translation, before resize
88 
89 			GlobalWindowManager.AddWindow(this);
90 
91 			string strTitle = (m_bSave ? KPRes.UrlSaveTitle : KPRes.UrlOpenTitle);
92 			string strDesc = (m_bSave ? KPRes.UrlSaveDesc : KPRes.UrlOpenDesc);
93 
94 			BannerFactory.CreateBannerEx(this, m_bannerImage,
95 				KeePass.Properties.Resources.B48x48_WWW, strTitle, strDesc);
96 			this.Icon = AppIcons.Default;
97 			this.Text = strTitle;
98 
99 			FontUtil.AssignDefaultBold(m_lblUrl);
100 			FontUtil.AssignDefaultBold(m_lblUserName);
101 			FontUtil.AssignDefaultBold(m_lblPassword);
102 			FontUtil.AssignDefaultBold(m_lblRemember);
103 
104 			m_tbUrl.Text = (m_ioc.IsLocalFile() ? string.Empty : m_ioc.Path);
105 			m_tbUserName.Text = m_ioc.UserName;
106 			m_tbPassword.Text = m_ioc.Password;
107 
108 			m_cmbCredSaveMode.Items.Add(KPRes.CredSaveNone);
109 			m_cmbCredSaveMode.Items.Add(KPRes.CredSaveUserOnly);
110 			m_cmbCredSaveMode.Items.Add(KPRes.CredSaveAll);
111 
112 			if(m_ioc.CredSaveMode == IOCredSaveMode.UserNameOnly)
113 				m_cmbCredSaveMode.SelectedIndex = 1;
114 			else if(m_ioc.CredSaveMode == IOCredSaveMode.SaveCred)
115 				m_cmbCredSaveMode.SelectedIndex = 2;
116 			else
117 				m_cmbCredSaveMode.SelectedIndex = 0;
118 
119 			if(!m_bCanRememberCred)
120 			{
121 				m_cmbCredSaveMode.SelectedIndex = 0;
122 				m_cmbCredSaveMode.Enabled = false;
123 			}
124 
125 			ThreadPool.QueueUserWorkItem(delegate(object state)
126 			{
127 				try { InitAutoCompletions(); }
128 				catch(Exception) { Debug.Assert(false); }
129 			});
130 		}
131 
OnFormShown(object sender, EventArgs e)132 		private void OnFormShown(object sender, EventArgs e)
133 		{
134 			if((m_tbUrl.TextLength > 0) && (m_tbUserName.TextLength > 0))
135 				UIUtil.ResetFocus(m_tbPassword, this);
136 			else if(m_tbUrl.TextLength > 0)
137 				UIUtil.SetFocus(m_tbUserName, this);
138 			else UIUtil.SetFocus(m_tbUrl, this);
139 		}
140 
OnBtnOK(object sender, EventArgs e)141 		private void OnBtnOK(object sender, EventArgs e)
142 		{
143 			string strUrl = m_tbUrl.Text;
144 			if(strUrl.IndexOf("://") < 0)
145 			{
146 				// m_ttInvalidUrl.Show(KPRes.InvalidUrl, m_tbUrl);
147 				MessageService.ShowWarning(strUrl, KPRes.InvalidUrl);
148 				this.DialogResult = DialogResult.None;
149 				return;
150 			}
151 
152 			m_ioc.Path = strUrl;
153 			m_ioc.UserName = m_tbUserName.Text;
154 			m_ioc.Password = m_tbPassword.TextEx.ReadString();
155 
156 			if(m_cmbCredSaveMode.SelectedIndex == 1)
157 				m_ioc.CredSaveMode = IOCredSaveMode.UserNameOnly;
158 			else if(m_cmbCredSaveMode.SelectedIndex == 2)
159 				m_ioc.CredSaveMode = IOCredSaveMode.SaveCred;
160 			else
161 				m_ioc.CredSaveMode = IOCredSaveMode.NoSave;
162 
163 			IocProperties p = m_ioc.Properties;
164 			foreach(KeyValuePair<IocPropertyInfo, Control> kvp in m_lProps)
165 			{
166 				IocPropertyInfo pi = kvp.Key;
167 				string strName = pi.Name;
168 				Type t = pi.Type;
169 				Control c = kvp.Value;
170 
171 				try
172 				{
173 					if(t == typeof(string))
174 					{
175 						TextBox tb = (c as TextBox);
176 						if(tb != null) p.Set(strName, tb.Text.Trim());
177 						else { Debug.Assert(false); }
178 					}
179 					else if(t == typeof(bool))
180 					{
181 						ComboBox cmb = (c as ComboBox);
182 						if(cmb != null)
183 						{
184 							int iSel = cmb.SelectedIndex;
185 							if(iSel == 0) p.SetBool(strName, null);
186 							else p.SetBool(strName, (iSel == 1));
187 						}
188 						else { Debug.Assert(false); }
189 					}
190 					else if(t == typeof(long))
191 					{
192 						TextBox tb = (c as TextBox);
193 						if(tb != null)
194 						{
195 							string str = tb.Text.Trim();
196 							if(str.Length == 0) p.SetLong(strName, null);
197 							else
198 							{
199 								// Validate and store number
200 								long l = long.Parse(str, NumberFormatInfo.InvariantInfo);
201 								p.SetLong(strName, l);
202 							}
203 						}
204 						else { Debug.Assert(false); }
205 					}
206 					else { Debug.Assert(false); }
207 				}
208 				catch(Exception ex)
209 				{
210 					string strMsg = KPRes.Field + @" '" + pi.DisplayName +
211 						@"':" + MessageService.NewLine + ex.Message;
212 					// if(!VistaTaskDialog.ShowMessageBox(strMsg, KPRes.ValidationFailed,
213 					//	PwDefs.ShortProductName, VtdIcon.Warning, this))
214 					MessageService.ShowWarning(strMsg);
215 
216 					this.DialogResult = DialogResult.None;
217 					return;
218 				}
219 			}
220 
221 			if(m_bTestConnection && !m_bSave)
222 			{
223 				if(!TestConnectionEx())
224 					this.DialogResult = DialogResult.None;
225 			}
226 		}
227 
TestConnectionEx()228 		private bool TestConnectionEx()
229 		{
230 			bool bResult = true;
231 			bool bOK = m_btnOK.Enabled, bCancel = m_btnCancel.Enabled;
232 			bool bCombo = m_cmbCredSaveMode.Enabled;
233 
234 			m_btnOK.Enabled = m_btnCancel.Enabled = m_tbUrl.Enabled =
235 				m_tbUserName.Enabled = m_tbPassword.Enabled =
236 				m_btnHelp.Enabled = m_cmbCredSaveMode.Enabled = false;
237 
238 			Application.DoEvents();
239 
240 			try
241 			{
242 				if(!IOConnection.FileExists(m_ioc, true))
243 					throw new FileNotFoundException();
244 			}
245 			catch(Exception exTest)
246 			{
247 				if(Program.CommandLineArgs[AppDefs.CommandLineOptions.Debug] != null)
248 					MessageService.ShowWarningExcp(m_ioc.GetDisplayName(), exTest);
249 				else
250 				{
251 					string strError = exTest.Message;
252 					if((exTest.InnerException != null) &&
253 						!string.IsNullOrEmpty(exTest.InnerException.Message))
254 						strError += MessageService.NewParagraph +
255 							exTest.InnerException.Message;
256 
257 					MessageService.ShowWarning(m_ioc.GetDisplayName(), strError);
258 				}
259 
260 				bResult = false;
261 			}
262 
263 			m_btnOK.Enabled = bOK;
264 			m_btnCancel.Enabled = bCancel;
265 			m_cmbCredSaveMode.Enabled = bCombo;
266 			m_btnHelp.Enabled = m_tbUserName.Enabled = m_tbUrl.Enabled =
267 				m_tbPassword.Enabled = true;
268 			return bResult;
269 		}
270 
OnBtnCancel(object sender, EventArgs e)271 		private void OnBtnCancel(object sender, EventArgs e)
272 		{
273 		}
274 
OnBtnHelp(object sender, EventArgs e)275 		private void OnBtnHelp(object sender, EventArgs e)
276 		{
277 			AppHelp.ShowHelp(AppDefs.HelpTopics.IOConnections, null);
278 		}
279 
OnFormClosed(object sender, FormClosedEventArgs e)280 		private void OnFormClosed(object sender, FormClosedEventArgs e)
281 		{
282 			GlobalWindowManager.RemoveWindow(this);
283 		}
284 
GetPropertyInfos()285 		private static Dictionary<string, List<IocPropertyInfo>> GetPropertyInfos()
286 		{
287 			Dictionary<string, List<IocPropertyInfo>> d =
288 				new Dictionary<string, List<IocPropertyInfo>>(StrUtil.CaseIgnoreComparer);
289 
290 			foreach(IocPropertyInfo pi in IocPropertyInfoPool.PropertyInfos)
291 			{
292 				if(pi == null) { Debug.Assert(false); continue; }
293 
294 				List<string> lPrt = new List<string>(pi.Protocols);
295 				lPrt.Sort(StrUtil.CaseIgnoreComparer);
296 				StringBuilder sbPrt = new StringBuilder();
297 				foreach(string str in lPrt)
298 				{
299 					if(sbPrt.Length > 0) sbPrt.Append(" / ");
300 					sbPrt.Append(str);
301 				}
302 				string strPrt = sbPrt.ToString();
303 
304 				List<IocPropertyInfo> l;
305 				if(d.TryGetValue(strPrt, out l)) l.Add(pi);
306 				else
307 				{
308 					l = new List<IocPropertyInfo>();
309 					l.Add(pi);
310 					d[strPrt] = l;
311 				}
312 			}
313 
314 			return d;
315 		}
316 
InitAdvancedTab()317 		private void InitAdvancedTab()
318 		{
319 			IocProperties p = m_ioc.Properties;
320 			Dictionary<string, List<IocPropertyInfo>> dProps = GetPropertyInfos();
321 
322 			const int d = 7;
323 			int hLabel = m_lblUrl.Height;
324 			int hTextBox = m_tbUrl.Height;
325 			int hComboBox = m_cmbCredSaveMode.Height;
326 			Font f = m_lblUrl.Font;
327 
328 			Debug.Assert(m_pnlAdv.AutoScroll);
329 			m_pnlAdv.AutoScrollMargin = new Size(1, d);
330 
331 			int wPanel = m_pnlAdv.ClientSize.Width - UIUtil.GetVScrollBarWidth() - 1;
332 			int wGroup = wPanel - (2 * d);
333 			int wCell = (wPanel - (3 * d)) / 2;
334 			int xText = d - 1;
335 			int xInput = d + wCell + d - 1;
336 			int xGroup = xText;
337 
338 			if(this.RightToLeft == RightToLeft.Yes)
339 			{
340 				int iTemp = xText;
341 				xText = xInput;
342 				xInput = iTemp;
343 			}
344 
345 			int y = 1;
346 			int iID = 0;
347 
348 			m_pnlAdv.SuspendLayout();
349 
350 			foreach(KeyValuePair<string, List<IocPropertyInfo>> kvp in dProps)
351 			{
352 				string strGroup = kvp.Key;
353 				y += d;
354 
355 				Label lblGroup = new Label();
356 				lblGroup.Name = "cGroup" + iID.ToString(NumberFormatInfo.InvariantInfo);
357 				++iID;
358 				lblGroup.AutoEllipsis = true;
359 				lblGroup.AutoSize = false;
360 				lblGroup.Dock = DockStyle.None;
361 				lblGroup.Location = new Point(xGroup, y);
362 				lblGroup.Size = new Size(wGroup, hLabel);
363 				lblGroup.Text = strGroup;
364 				lblGroup.TextAlign = ContentAlignment.MiddleLeft;
365 				// lblGroup.BackColor = Color.Red;
366 				FontUtil.AssignDefaultBold(lblGroup);
367 
368 				m_pnlAdv.Controls.Add(lblGroup);
369 				y += hLabel;
370 
371 				foreach(IocPropertyInfo pi in kvp.Value)
372 				{
373 					string strName = pi.Name;
374 					string strText = pi.DisplayName + ":";
375 					Type t = pi.Type;
376 					y += d;
377 
378 					int hText = hLabel;
379 					int wRem = TextRenderer.MeasureText(strText, f).Width;
380 					while(wRem >= wCell)
381 					{
382 						hText += (hLabel + 1);
383 						wRem -= wCell;
384 					}
385 
386 					Label lblText = new Label();
387 					lblText.Name = "cText" + iID.ToString(NumberFormatInfo.InvariantInfo);
388 					++iID;
389 					lblText.AutoEllipsis = true;
390 					lblText.AutoSize = false;
391 					lblText.Dock = DockStyle.None;
392 					lblText.Size = new Size(wCell, hText);
393 					lblText.Text = strText;
394 					lblText.TextAlign = ContentAlignment.MiddleLeft;
395 					// lblText.BackColor = Color.Green;
396 
397 					Control cInput = null;
398 					if((t == typeof(string)) || (t == typeof(long)))
399 					{
400 						TextBox tb = new TextBox();
401 						tb.Size = new Size(wCell, hTextBox);
402 
403 						tb.Text = (p.Get(strName) ?? string.Empty);
404 
405 						cInput = tb;
406 					}
407 					else if(t == typeof(bool))
408 					{
409 						ComboBox cmb = new ComboBox();
410 						cmb.DropDownStyle = ComboBoxStyle.DropDownList;
411 						cmb.Size = new Size(wCell, hComboBox);
412 
413 						cmb.Items.Add(KPRes.Auto);
414 						cmb.Items.Add(KPRes.Yes);
415 						cmb.Items.Add(KPRes.No);
416 
417 						bool? ob = p.GetBool(strName);
418 						if(ob.HasValue) cmb.SelectedIndex = (ob.Value ? 1 : 2);
419 						else cmb.SelectedIndex = 0;
420 
421 						cInput = cmb;
422 					}
423 					else { Debug.Assert(false); continue; }
424 
425 					cInput.Dock = DockStyle.None;
426 					cInput.Name = "cInput" + iID.ToString(NumberFormatInfo.InvariantInfo);
427 					++iID;
428 
429 					int hDiff = lblText.Height - cInput.Height;
430 					if(hDiff >= 0)
431 					{
432 						lblText.Location = new Point(xText, y);
433 						cInput.Location = new Point(xInput, y + (hDiff / 2));
434 
435 						y += lblText.Height;
436 					}
437 					else
438 					{
439 						lblText.Location = new Point(xText, y - (hDiff / 2));
440 						cInput.Location = new Point(xInput, y);
441 
442 						y += cInput.Height;
443 					}
444 
445 					m_pnlAdv.Controls.Add(lblText);
446 					m_pnlAdv.Controls.Add(cInput);
447 
448 					m_lProps.Add(new KeyValuePair<IocPropertyInfo, Control>(
449 						pi, cInput));
450 				}
451 			}
452 
453 			m_pnlAdv.ResumeLayout(true);
454 		}
455 
InitAutoCompletion(TextBox tb, Dictionary<string, bool> d)456 		private static void InitAutoCompletion(TextBox tb, Dictionary<string, bool> d)
457 		{
458 			if(d.Count == 0) return;
459 
460 			string[] v = new string[d.Count];
461 			d.Keys.CopyTo(v, 0);
462 			Array.Sort<string>(v, StrUtil.CaseIgnoreComparer);
463 
464 			// Do not append, because long suggestions hide the start
465 			UIUtil.EnableAutoCompletion(tb, false, v); // Invokes
466 		}
467 
InitAutoCompletions()468 		private void InitAutoCompletions()
469 		{
470 			Dictionary<string, bool> dUrls = new Dictionary<string, bool>();
471 			Dictionary<string, bool> dUsers = new Dictionary<string, bool>();
472 
473 			MainForm mf = Program.MainForm;
474 			MruList l = ((mf != null) ? mf.FileMruList : null);
475 			if(l == null) { Debug.Assert(false); return; }
476 
477 			for(uint u = 0; u < l.ItemCount; ++u)
478 			{
479 				IOConnectionInfo ioc = (l.GetItem(u).Value as IOConnectionInfo);
480 				if(ioc == null) { Debug.Assert(false); continue; }
481 				if(ioc.IsLocalFile()) continue;
482 
483 				string str = ioc.Path;
484 				if(!string.IsNullOrEmpty(str)) dUrls[str] = true;
485 
486 				str = ioc.UserName;
487 				if(!string.IsNullOrEmpty(str)) dUsers[str] = true;
488 			}
489 
490 			InitAutoCompletion(m_tbUrl, dUrls);
491 			InitAutoCompletion(m_tbUserName, dUsers);
492 		}
493 	}
494 }
495