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.Text;
23 using System.Runtime.InteropServices;
24 using System.Runtime.InteropServices.ComTypes;
25 using System.Diagnostics;
26 
27 using KeePassLib.Utility;
28 
29 namespace KeePass.Native
30 {
31 	internal sealed class ShellLinkEx
32 	{
33 		private string m_strPath = null;
34 		public string Path
35 		{
36 			get { return m_strPath; }
37 			set { m_strPath = value; }
38 		}
39 
40 		private string m_strArgs = null;
41 		public string Arguments
42 		{
43 			get { return m_strArgs; }
44 			set { m_strArgs = value; }
45 		}
46 
47 		private string m_strDesc = null;
48 		public string Description
49 		{
50 			get { return m_strDesc; }
51 			set
52 			{
53 				if((value != null) && (value.Length >= NativeMethods.INFOTIPSIZE))
54 				{
55 					Debug.Assert(false);
56 					m_strDesc = StrUtil.CompactString3Dots(value,
57 						NativeMethods.INFOTIPSIZE - 1);
58 				}
59 				else m_strDesc = value;
60 			}
61 		}
62 
ShellLinkEx()63 		public ShellLinkEx()
64 		{
65 		}
66 
ShellLinkEx(string strPath, string strArgs, string strDesc)67 		public ShellLinkEx(string strPath, string strArgs, string strDesc)
68 		{
69 			m_strPath = strPath;
70 			m_strArgs = strArgs;
71 			this.Description = strDesc; // Shortens description if necessary
72 		}
73 
Load(string strLnkFilePath)74 		public static ShellLinkEx Load(string strLnkFilePath)
75 		{
76 			try
77 			{
78 				CShellLink csl = new CShellLink();
79 
80 				IShellLinkW sl = (csl as IShellLinkW);
81 				if(sl == null) { Debug.Assert(false); return null; }
82 				IPersistFile pf = (csl as IPersistFile);
83 				if(pf == null) { Debug.Assert(false); return null; }
84 
85 				pf.Load(strLnkFilePath, (int)(NativeMethods.STGM.Read |
86 					NativeMethods.STGM.ShareDenyWrite));
87 
88 				const int ccMaxPath = KeePassLib.Native.NativeMethods.MAX_PATH;
89 				const int ccInfoTip = NativeMethods.INFOTIPSIZE;
90 
91 				ShellLinkEx r = new ShellLinkEx();
92 
93 				StringBuilder sb = new StringBuilder(ccMaxPath + 1);
94 				sl.GetPath(sb, sb.Capacity, IntPtr.Zero, 0);
95 				r.Path = sb.ToString();
96 
97 				sb = new StringBuilder(ccInfoTip + 1);
98 				sl.GetArguments(sb, sb.Capacity);
99 				r.Arguments = sb.ToString();
100 
101 				sb = new StringBuilder(ccInfoTip + 1);
102 				sl.GetDescription(sb, sb.Capacity);
103 				r.Description = sb.ToString();
104 
105 				return r;
106 			}
107 			catch(Exception) { Debug.Assert(false); }
108 
109 			return null;
110 		}
111 
Save(string strLnkFilePath)112 		public bool Save(string strLnkFilePath)
113 		{
114 			try
115 			{
116 				CShellLink csl = new CShellLink();
117 
118 				IShellLinkW sl = (csl as IShellLinkW);
119 				if(sl == null) { Debug.Assert(false); return false; }
120 				IPersistFile pf = (csl as IPersistFile);
121 				if(pf == null) { Debug.Assert(false); return false; }
122 
123 				if(!string.IsNullOrEmpty(m_strPath))
124 					sl.SetPath(m_strPath);
125 				if(!string.IsNullOrEmpty(m_strArgs))
126 					sl.SetArguments(m_strArgs);
127 				if(!string.IsNullOrEmpty(m_strDesc))
128 					sl.SetDescription(m_strDesc);
129 
130 				pf.Save(strLnkFilePath, true);
131 				return true;
132 			}
133 			catch(Exception) { Debug.Assert(false); }
134 
135 			return false;
136 		}
137 	}
138 
139 	[ComImport]
140 	[Guid("000214F9-0000-0000-C000-000000000046")]
141 	[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
142 	internal interface IShellLinkW
143 	{
GetPath([MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cch, IntPtr pfd, uint fFlags)144 		void GetPath([MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile,
145 			int cch, IntPtr pfd, uint fFlags);
146 
GetIDList(out IntPtr ppidl)147 		void GetIDList(out IntPtr ppidl);
SetIDList(IntPtr pidl)148 		void SetIDList(IntPtr pidl);
149 
GetDescription([MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cch)150 		void GetDescription([MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName,
151 			int cch);
SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName)152 		void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
153 
GetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cch)154 		void GetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir,
155 			int cch);
SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir)156 		void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
157 
GetArguments([MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cch)158 		void GetArguments([MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cch);
SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs)159 		void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
160 
GetHotkey(out ushort pwHotkey)161 		void GetHotkey(out ushort pwHotkey);
SetHotkey(ushort wHotkey)162 		void SetHotkey(ushort wHotkey);
163 
GetShowCmd(out int piShowCmd)164 		void GetShowCmd(out int piShowCmd);
SetShowCmd(int iShowCmd)165 		void SetShowCmd(int iShowCmd);
166 
GetIconLocation([MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cch, out int piIcon)167 		void GetIconLocation([MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
168 			int cch, out int piIcon);
SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon)169 		void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath,
170 			int iIcon);
171 
SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved)172 		void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel,
173 			uint dwReserved);
174 
Resolve(IntPtr hwnd, uint fFlags)175 		void Resolve(IntPtr hwnd, uint fFlags);
176 
SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile)177 		void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
178 	}
179 
180 	[ComImport]
181 	[Guid("00021401-0000-0000-C000-000000000046")]
182 	internal class CShellLink { }
183 }
184