1 /*
2 * ReactOS browseui
3 *
4 * Copyright 2014 David Quintana <gigaherz@gmail.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "precomp.h"
22 #include <strsafe.h>
23
24 extern "C"
25 BOOL WINAPI GUIDFromStringW(
26 _In_ PCWSTR psz,
27 _Out_ LPGUID pguid
28 );
29
_CopyAndUnquoteText(LPCWSTR strFieldSource,LPWSTR strField,size_t cchField)30 static BOOL _CopyAndUnquoteText(LPCWSTR strFieldSource, LPWSTR strField, size_t cchField)
31 {
32 WCHAR cChar;
33 PWSTR tmpField = strField;
34 size_t lenField = 1;
35 BOOL inQuote = FALSE;
36
37 // Remove leading whitespace
38 cChar = *strFieldSource;
39 while (cChar == L' ' || cChar == L'\t' || cChar == L'\n' || cChar == L'\r')
40 {
41 strFieldSource = CharNextW(strFieldSource);
42 cChar = *strFieldSource;
43 }
44
45 while (cChar && cChar != L'=' && cChar != L',')
46 {
47 if (cChar == L'"')
48 {
49 // [1] is always valid read because of null-termination
50 if (inQuote && strFieldSource[1] == L'"')
51 {
52 if (lenField < cchField)
53 {
54 // Append
55 *(tmpField++) = L'"';
56 ++lenField;
57 }
58
59 // Skip second quote
60 strFieldSource++;
61 }
62 else
63 {
64 inQuote = !inQuote;
65 }
66 }
67 else
68 {
69 if (inQuote || (cChar != L'=' && cChar != L','))
70 {
71 if (lenField < cchField)
72 {
73 // Append
74 *(tmpField++) = cChar;
75 ++lenField;
76 }
77 }
78 }
79
80 strFieldSource = CharNextW(strFieldSource);
81 cChar = *strFieldSource;
82 }
83
84 // Remove trailing whitespace
85 while (tmpField > strField)
86 {
87 tmpField = CharPrevW(strField, tmpField);
88 cChar = *tmpField;
89 if (cChar != L' ' && cChar != L'\t' && cChar != L'\n' && cChar != L'\r')
90 {
91 tmpField = CharNextW(tmpField);
92 break;
93 }
94 }
95
96 // Terminate output string
97 *tmpField = 0;
98
99 return TRUE;
100 }
101
_FindNextArg(PCWSTR * pstrFieldSource)102 static BOOL _FindNextArg(PCWSTR * pstrFieldSource)
103 {
104 PCWSTR strFieldSource = *pstrFieldSource;
105 WCHAR cChar = *strFieldSource;
106 BOOL inQuote = FALSE;
107
108 while (cChar)
109 {
110 if (!inQuote && (cChar == L'=' || cChar == L','))
111 break;
112
113 if (cChar == L'"')
114 inQuote = !inQuote;
115
116 strFieldSource = CharNextW(strFieldSource);
117 cChar = *strFieldSource;
118 }
119
120 if (cChar == 0)
121 {
122 *pstrFieldSource = strFieldSource;
123 return FALSE;
124 }
125
126 *pstrFieldSource = CharNextW(strFieldSource);
127 return TRUE;
128 }
129
_FindFirstField(PCWSTR strFieldSource)130 static PCWSTR _FindFirstField(PCWSTR strFieldSource)
131 {
132 //Find end of first arg, because
133 // behaviour is different if the first separator is an '='
134 BOOL inQuote = FALSE;
135 PCWSTR tmpArgs = strFieldSource;
136 WCHAR cChar = *tmpArgs;
137 while (cChar)
138 {
139 if (cChar == L'=')
140 break;
141
142 if (cChar == L',')
143 break;
144
145 if (cChar == L'\"')
146 inQuote = !inQuote;
147
148 tmpArgs = CharNextW(tmpArgs);
149 cChar = *tmpArgs;
150 }
151
152 // Skip the text before the first equal sign, if not quoted, unless the arg 0 was requested.
153 if (*tmpArgs == L'=' && !inQuote)
154 {
155 strFieldSource = ++tmpArgs;
156 TRACE("Skipped content before the first '=', remainder=%S\n", strFieldSource);
157 }
158
159 return strFieldSource;
160 }
161
_ReadNextArg(PCWSTR * pstrFieldSource,PWSTR strField,size_t cchField)162 static BOOL _ReadNextArg(PCWSTR * pstrFieldSource, PWSTR strField, size_t cchField)
163 {
164 // Copy and unquote text
165 _CopyAndUnquoteText(*pstrFieldSource, strField, cchField);
166
167 return _FindNextArg(pstrFieldSource);
168 }
169
_ILReadFromSharedMemory(PCWSTR strField)170 static LPITEMIDLIST _ILReadFromSharedMemory(PCWSTR strField)
171 {
172 LPITEMIDLIST ret = NULL;
173
174 // Ensure it really is an IDLIST-formatted parameter
175 // Format for IDLIST params: ":pid:shared"
176 if (*strField != L':')
177 return NULL;
178
179 HANDLE hData = IntToPtr(StrToIntW(strField + 1));
180 PWSTR strSecond = StrChrW(strField + 1, L':');
181
182 if (strSecond)
183 {
184 int pid = StrToIntW(strSecond + 1);
185 void* pvShared = SHLockShared(hData, pid);
186 if (pvShared)
187 {
188 ret = ILClone((LPCITEMIDLIST) pvShared);
189 SHUnlockShared(pvShared);
190 SHFreeShared(hData, pid);
191 }
192 }
193 return ret;
194 }
195
_ParsePathToPidl(PWSTR strPath,LPITEMIDLIST * pidl)196 static HRESULT _ParsePathToPidl(PWSTR strPath, LPITEMIDLIST * pidl)
197 {
198 CComPtr<IShellFolder> psfDesktop;
199
200 HRESULT hr = SHGetDesktopFolder(&psfDesktop);
201 if (FAILED(hr))
202 return hr;
203
204 return psfDesktop->ParseDisplayName(NULL, NULL, strPath, NULL, pidl, NULL);
205 }
206
_GetDocumentsPidl()207 static LPITEMIDLIST _GetDocumentsPidl()
208 {
209 CComPtr<IShellFolder> ppshf;
210 LPITEMIDLIST pidl;
211 WCHAR guid [] = L"::{450d8fba-ad25-11d0-98a8-0800361b1103}";
212
213 if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_MYDOCUMENTS, &pidl)))
214 return pidl;
215
216 if (FAILED(SHGetDesktopFolder(&ppshf)))
217 return NULL;
218
219 if (FAILED(ppshf->ParseDisplayName(NULL, NULL, guid, NULL, &pidl, NULL)))
220 return NULL;
221
222 return pidl;
223 }
224
225 /*************************************************************************
226 * SHExplorerParseCmdLine [BROWSEUI.107]
227 */
228 // Returns FALSE, TRUE or an address.
229 extern "C"
230 UINT_PTR
231 WINAPI
SHExplorerParseCmdLine(_Out_ PEXPLORER_CMDLINE_PARSE_RESULTS pInfo)232 SHExplorerParseCmdLine(_Out_ PEXPLORER_CMDLINE_PARSE_RESULTS pInfo)
233 {
234 WCHAR strField[MAX_PATH];
235 WCHAR strDir[MAX_PATH];
236
237 PCWSTR strCmdLine = GetCommandLineW();
238 PCWSTR strFieldArray = PathGetArgsW(strCmdLine);
239
240 if (!*strFieldArray)
241 {
242 pInfo->dwFlags = 9;
243 pInfo->pidlPath = _GetDocumentsPidl();
244 if (!pInfo->pidlPath)
245 {
246 GetWindowsDirectoryW(strDir, MAX_PATH);
247 PathStripToRootW(strDir);
248 pInfo->pidlPath = ILCreateFromPathW(strDir);
249 }
250 return (UINT_PTR)pInfo->pidlPath;
251 }
252
253 PCWSTR strNextArg = _FindFirstField(strFieldArray);
254
255 BOOL hasNext = _ReadNextArg(&strNextArg, strField, _countof(strField));
256
257 while (TRUE)
258 {
259 // Basic flags-only params first
260 if (!StrCmpIW(strField, L"/N"))
261 {
262 pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_NEWWND | SH_EXPLORER_CMDLINE_FLAG_NOREUSE;
263 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
264 }
265 else if (!StrCmpIW(strField, L"/S"))
266 {
267 pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_S;
268 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
269 }
270 else if (!StrCmpIW(strField, L"/E"))
271 {
272 pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_E;
273 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
274 }
275 else if (!StrCmpIW(strField, L"/SELECT"))
276 {
277 pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_SELECT;
278 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
279 }
280 else if (!StrCmpIW(strField, L"/NOUI"))
281 {
282 pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_NOUI;
283 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
284 }
285 else if (!StrCmpIW(strField, L"-embedding"))
286 {
287 pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_EMBED;
288 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
289 }
290 else if (!StrCmpIW(strField, L"/SEPARATE"))
291 {
292 pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_SEPARATE;
293 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
294 }
295 else if (!StrCmpIW(strField, L"/INPROC"))
296 {
297 // No idea what Inproc is supposed to do, but it gets a GUID, and parses it.
298
299 TRACE("CmdLine Parser: Found %S flag\n", strField);
300
301 if (!hasNext)
302 return FALSE;
303
304 hasNext = _ReadNextArg(&strNextArg, strField, _countof(strField));
305
306 if (!GUIDFromStringW(strField, &(pInfo->guidInproc)))
307 return FALSE;
308
309 pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_INPROC;
310
311 TRACE("CmdLine Parser: Parsed /INPROC flag. dwFlags=%08lx, guidInproc=%S\n", pInfo->dwFlags, strField);
312 }
313 else if (!StrCmpIW(strField, L"/ROOT"))
314 {
315 LPITEMIDLIST pidlRoot = NULL;
316
317 // The window should be rooted
318
319 TRACE("CmdLine Parser: Found %S flag\n", strField);
320
321 if (!hasNext)
322 return FALSE;
323
324 hasNext = _ReadNextArg(&strNextArg, strField, _countof(strField));
325
326 // Root may be a pidl
327 if (!StrCmpIW(strField, L"/IDLIST"))
328 {
329 if (hasNext)
330 {
331 hasNext = _ReadNextArg(&strNextArg, strField, _countof(strField));
332 }
333 pidlRoot = _ILReadFromSharedMemory(strField);
334 }
335 else
336 {
337 // Or just a path string
338 _ParsePathToPidl(strField, &pidlRoot);
339 }
340
341 pInfo->pidlRoot = pidlRoot;
342
343 // The root defaults to the desktop
344 if (!pidlRoot)
345 {
346 if (FAILED(SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, &(pInfo->pidlRoot))))
347 pInfo->pidlRoot = NULL;
348 }
349
350 // TODO: Create rooted PIDL from pInfo->pidlPath and pInfo->pidlRoot
351
352 TRACE("CmdLine Parser: Parsed /ROOT flag. dwFlags=%08lx, pidlRoot=%p\n", pInfo->dwFlags, pInfo->pidlRoot);
353 }
354 else
355 {
356 // Anything else is part of the target path to browse to
357 TRACE("CmdLine Parser: Found target path %S\n", strField);
358
359 // Which can be a shared-memory itemidlist
360 if (!StrCmpIW(strField, L"/IDLIST"))
361 {
362 LPITEMIDLIST pidlArg;
363
364 if (!hasNext)
365 return FALSE;
366
367 hasNext = _ReadNextArg(&strNextArg, strField, _countof(strField));
368 pidlArg = _ILReadFromSharedMemory(strField);
369 if (!pidlArg)
370 return FALSE;
371
372 if (pInfo->pidlPath)
373 ILFree(pInfo->pidlPath);
374 pInfo->pidlPath = pidlArg;
375
376 TRACE("CmdLine Parser: Parsed target path. dwFlags=%08lx, pidlPath=%p\n", pInfo->dwFlags, pInfo->pidlPath);
377 }
378 else
379 {
380 // Or just a plain old string.
381
382 if (PathIsDirectoryW(strField))
383 PathAddBackslash(strField);
384
385 WCHAR szPath[MAX_PATH];
386 DWORD result = GetFullPathNameW(strField, _countof(szPath), szPath, NULL);
387
388 if (result != 0 && result <= _countof(szPath) && PathFileExistsW(szPath))
389 StringCchCopyW(strField, _countof(strField), szPath);
390
391 LPITEMIDLIST pidlPath = ILCreateFromPathW(strField);
392
393 pInfo->pidlPath = pidlPath;
394
395 if (pidlPath)
396 {
397 pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_IDLIST;
398 TRACE("CmdLine Parser: Parsed target path. dwFlags=%08lx, pidlPath=%p\n", pInfo->dwFlags, pInfo->pidlPath);
399 }
400 else
401 {
402 // The path could not be parsed into an ID List,
403 // so pass it on as a plain string.
404
405 PWSTR field;
406 SHStrDupW(strField, &field);
407 pInfo->strPath = field;
408 if (field)
409 {
410 pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_STRING;
411 TRACE("CmdLine Parser: Parsed target path. dwFlags=%08lx, strPath=%S\n", pInfo->dwFlags, field);
412 }
413 }
414 }
415 }
416
417 if (!hasNext)
418 break;
419 hasNext = _ReadNextArg(&strNextArg, strField, _countof(strField));
420 }
421
422 return TRUE;
423 }
424