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 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 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 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 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 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 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 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 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