Fix SEGV for non-fully-qualified destination paths.
[wine] / dlls / shell32 / shfldr_unixfs.c
1 /*
2  * UNIXFS - Shell namespace extension for the unix filesystem
3  *
4  * Copyright (C) 2005 Michael Jung
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 <stdio.h>
22 #include <stdarg.h>
23 #include <dirent.h>
24
25 #define COBJMACROS
26 #define NONAMELESSUNION
27 #define NONAMELESSSTRUCT
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "objbase.h"
33 #include "wine/debug.h"
34
35 #include "shell32_main.h"
36 #include "shfldr.h"
37 #include "shresdef.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(shell);
40
41 const GUID CLSID_UnixFolder = {0xcc702eb2, 0x7dc5, 0x11d9, {0xc6, 0x87, 0x00, 0x04, 0x23, 0x8a, 0x01, 0xcd}};
42
43 #define ADJUST_THIS(c,m,p) ((c*)(((long)p)-(long)&(((c*)0)->lp##m##Vtbl)))
44 #define STATIC_CAST(i,p) ((i*)&p->lp##i##Vtbl)
45
46 /******************************************************************************
47  * UNIXFS_path_to_pidl [Internal]
48  *
49  * PARAMS
50  *  path  [I] An absolute unix path 
51  *  ppidl [O] The corresponding ITEMIDLIST. Release with SHFree/ILFree
52  *  
53  * RETURNS
54  *  Success: TRUE
55  *  Failure: FALSE, invalid params, path not absolute or out of memory
56  *
57  * NOTES
58  *  'path' has to be an absolute unix filesystem path starting and
59  *  ending with a slash ('/'). Currently, only directories (no files)
60  *  are accepted.
61  */
62 static BOOL UNIXFS_path_to_pidl(char *path, LPITEMIDLIST *ppidl) {
63     LPITEMIDLIST pidl;
64     int cSubDirs, cPidlLen;
65     char *pSlash, *pSubDir;
66
67     TRACE("path=%s, ppidl=%p", debugstr_a(path), ppidl);
68     
69     /* Fail, if no absolute path given */
70     if (!ppidl || !path || path[0] != '/') return FALSE;
71    
72     /* Count the number of sub-directories in the path */
73     cSubDirs = 0;
74     pSlash = strchr(path, '/');
75     while (pSlash) {
76         cSubDirs++;
77         pSlash = strchr(pSlash+1, '/');
78     }
79    
80     /* Allocate enough memory to hold the path */
81     cPidlLen = strlen(path) + cSubDirs * sizeof(USHORT) + sizeof(USHORT);
82     *ppidl = pidl = (LPITEMIDLIST)SHAlloc(cPidlLen);
83     if (!pidl) return FALSE;
84
85     /* Start with a SHITEMID for the root directory */
86     pidl->mkid.cb = 3;
87     pidl->mkid.abID[0] = '/';
88     pidl = ILGetNext(pidl);
89
90     /* Append SHITEMIDs for the sub-directories */
91     pSubDir = path + 1;
92     pSlash = strchr(pSubDir, '/');
93     while (pSlash) {
94         pidl->mkid.cb = (USHORT)(pSlash+3-pSubDir);
95         memcpy(pidl->mkid.abID, pSubDir, pidl->mkid.cb);
96         pSubDir = pSlash + 1;
97         pSlash = strchr(pSubDir, '/');
98         pidl = ILGetNext(pidl);
99     }
100     pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
101    
102     /* Path doesn't end with a '/' */
103     if (*pSubDir) 
104         WARN("Path '%s' not in canonical form.\n", path);
105     
106     return TRUE;
107 }
108
109 /******************************************************************************
110  * UNIXFS_pidl_to_path [Internal]
111  *
112  * Construct the unix path that corresponds to a fully qualified ITEMIDLIST
113  *
114  * PARAMS
115  *  pidl [I] ITEMIDLIST that specifies the absolute location of the folder
116  *  path [O] The corresponding unix path as a zero terminated ascii string
117  *
118  * RETURNS
119  *  Success: TRUE
120  *  Failure: FALSE, pidl doesn't specify a unix path or out of memory
121  */
122 static BOOL UNIXFS_pidl_to_path(LPCITEMIDLIST pidl, PSZ *path) {
123     LPCITEMIDLIST current = pidl, root;
124     DWORD dwPathLen;
125     char *pNextDir;
126
127     TRACE("(pidl=%p, path=%p)\n", pidl, path);
128     
129     *path = NULL;
130
131     /* Find the UnixFolderClass root */
132     while (current->mkid.cb) {
133         if (current->mkid.cb < sizeof(GUID)+4) return FALSE;
134         if (IsEqualIID(&CLSID_UnixFolder, &current->mkid.abID[2])) break;
135         current = ILGetNext(current);
136     }
137     if (!current->mkid.cb) return FALSE;
138     root = ILGetNext(current);
139
140     /* Determine the path's length bytes */
141     dwPathLen = 1; /* For the terminating '\0' */
142     current = root;
143     while (current->mkid.cb) {
144         dwPathLen += current->mkid.cb - sizeof(USHORT);
145         current = ILGetNext(current);
146     };
147
148     /* Build the path */
149     *path = pNextDir = SHAlloc(dwPathLen);
150     if (!path) {
151         WARN("SHAlloc failed!\n");
152         return FALSE;
153     }
154     current = root;
155     while (current->mkid.cb) {
156         memcpy(pNextDir, current->mkid.abID, current->mkid.cb - sizeof(USHORT));
157         pNextDir += current->mkid.cb - sizeof(USHORT);
158         current = ILGetNext(current);
159     }
160     *pNextDir='\0';
161     
162     TRACE("resulting path: %s\n", *path);
163     return TRUE;
164 }
165
166 /******************************************************************************
167  * UNIXFS_build_subfolder_pidls [Internal]
168  *
169  * Builds an array of subfolder PIDLs relative to a unix directory
170  *
171  * PARAMS
172  *  path   [I] Name of a unix directory as a zero terminated ascii string
173  *  apidl  [O] The array of PIDLs
174  *  pCount [O] Size of apidl
175  *
176  * RETURNS
177  *  Success: TRUE
178  *  Failure: FALSE, path is not a valid unix directory or out of memory
179  *
180  * NOTES
181  *  The array of PIDLs and each PIDL are allocated with SHAlloc. You'll have
182  *  to release each PIDL as well as the array itself with SHFree.
183  */
184 static BOOL UNIXFS_build_subfolder_pidls(const char *path, LPITEMIDLIST **apidl, DWORD *pCount)
185 {
186     DIR *dir;
187     struct dirent *pSubDir;
188     DWORD cSubDirs, i;
189     USHORT sLen;
190
191     TRACE("(path=%s, apidl=%p, pCount=%p)\n", path, apidl, pCount);
192     
193     *apidl = NULL;
194     *pCount = 0;
195    
196     /* Special case for 'My UNIX Filesystem' shell folder:
197      * The unix root directory is the only child of 'My UNIX Filesystem'. 
198      */
199     if (!strlen(path)) {
200         LPSHITEMID pid;
201         
202         pid = (LPSHITEMID)SHAlloc(1 + 2 * sizeof(USHORT));
203         pid->cb = 3;
204         pid->abID[0] = '/';
205         memset(((PBYTE)pid)+pid->cb, 0, sizeof(USHORT));
206         
207         *apidl = SHAlloc(sizeof(LPITEMIDLIST));
208         (*apidl)[0] = (LPITEMIDLIST)pid;
209         *pCount = 1;
210     
211         return TRUE;
212     }
213     
214     dir = opendir(path);
215     if (!dir) {
216         WARN("Failed to open directory '%s'.\n", path);
217         return FALSE;
218     }
219
220     /* Count number of sub directories.
221      */
222     for (cSubDirs = 0, pSubDir = readdir(dir); pSubDir; pSubDir = readdir(dir)) { 
223         if (strcmp(pSubDir->d_name, ".") && 
224             strcmp(pSubDir->d_name, "..") && 
225             pSubDir->d_type == DT_DIR) 
226         {
227             cSubDirs++;
228         }
229     }
230
231     /* If there are no subdirectories, we are done. */
232     if (cSubDirs == 0) {
233         closedir(dir);
234         return TRUE;
235     }
236
237     /* Allocate the array of PIDLs */
238     *apidl = SHAlloc(cSubDirs * sizeof(LPITEMIDLIST));
239     if (!apidl) {
240         WARN("SHAlloc failed!\n");
241         return FALSE;
242     }
243    
244     /* Allocate and initialize one SHITEMID per sub-directory. */
245     for (rewinddir(dir), pSubDir = readdir(dir), i = 0; pSubDir; pSubDir = readdir(dir)) {
246         LPSHITEMID pid;
247             
248         if (!strcmp(pSubDir->d_name, ".") ||
249             !strcmp(pSubDir->d_name, "..") || 
250             pSubDir->d_type != DT_DIR) 
251         {
252             continue;
253         }
254     
255         sLen = strlen(pSubDir->d_name)+1; /* For the trailing '/' */
256         pid = (LPSHITEMID)SHAlloc(sLen + 2 * sizeof(USHORT));
257         if (!pid) {
258             WARN("SHAlloc failed!\n");
259             return FALSE;
260         }
261
262         pid->cb = (USHORT)(sLen + sizeof(USHORT));
263         memcpy(pid->abID, pSubDir->d_name, sLen-1);
264         pid->abID[sLen-1] = '/';
265         memset(((PBYTE)pid)+pid->cb, 0, sizeof(USHORT));
266
267         (*apidl)[i++] = (LPITEMIDLIST)pid;
268     }
269     
270     *pCount = i;    
271     closedir(dir);
272
273     return TRUE;
274 }
275
276 /******************************************************************************
277  * UnixFolderIcon 
278  *
279  * Singleton class, which is used by the shell to extract icons to represent
280  * folders in tree- and listviews. Currently, all this singleton does is to
281  * provide the shell with the absolute path to "shell32.dll" and with the 
282  * indices of the closed and opened folder icons in the resources of this dll.
283  */
284
285 /* UnixFolderIcon object layout and typedef.
286  */
287 typedef struct _UnixFolderIcon {
288     const IExtractIconWVtbl *lpIExtractIconWVtbl;
289 } UnixFolderIcon;
290
291 static HRESULT WINAPI UnixFolderIcon_IExtractIconW_QueryInterface(IExtractIconW *iface, REFIID riid, 
292     void **ppv) 
293 {
294     TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
295     
296     if (!ppv) return E_INVALIDARG;
297     
298     if (IsEqualIID(&IID_IUnknown, riid) ||
299         IsEqualIID(&IID_IExtractIconW, riid))
300     {
301         *ppv = iface;
302     } else {
303         *ppv = NULL;
304         return E_NOINTERFACE;
305     }
306
307     IExtractIconW_AddRef(iface);
308     return S_OK;
309 }
310
311 static ULONG WINAPI UnixFolderIcon_IExtractIconW_AddRef(IExtractIconW *iface) {
312     TRACE("(iface=%p)\n", iface);
313     return 2;
314 }
315
316 static ULONG WINAPI UnixFolderIcon_IExtractIconW_Release(IExtractIconW *iface) {
317     TRACE("(iface=%p)\n", iface);
318     return 1;
319 }
320
321 static HRESULT WINAPI UnixFolderIcon_IExtractIconW_GetIconLocation(IExtractIconW *iface, 
322     UINT uFlags, LPWSTR szIconFile, UINT cchMax, INT* piIndex, UINT* pwFlags)
323 {
324     TRACE("(iface=%p, uFlags=%u, szIconFile=%s, cchMax=%u, piIndex=%p, pwFlags=%p)\n",
325             iface, uFlags, debugstr_w(szIconFile), cchMax, piIndex, pwFlags);
326     
327     lstrcpynW(szIconFile, swShell32Name, cchMax);
328     *piIndex = (uFlags & GIL_OPENICON) ? 4 : 3;
329     *pwFlags = 0;
330
331     return S_OK;
332 }
333
334 static HRESULT WINAPI UnixFolderIcon_IExtractIconW_Extract(
335     IExtractIconW *iface, LPCWSTR pszFile, UINT nIconIndex, HICON* phiconLarge, HICON* phiconSmall, 
336     UINT nIconSize)
337 {
338     TRACE("(iface=%p, pszFile=%s, nIconIndex=%u, phiconLarge=%p, phiconSmall=%p, nIconSize=%u)"
339           "stub\n", iface, debugstr_w(pszFile), nIconIndex, phiconLarge, phiconSmall, nIconSize);
340
341     return E_NOTIMPL;
342 }
343
344 /* VTable for the IExtractIconW interface of the UnixFolderIcon class. 
345  */
346 static const IExtractIconWVtbl UnixFolderIcon_IExtractIconW_Vtbl = {
347     UnixFolderIcon_IExtractIconW_QueryInterface,
348     UnixFolderIcon_IExtractIconW_AddRef,
349     UnixFolderIcon_IExtractIconW_Release,
350     UnixFolderIcon_IExtractIconW_GetIconLocation,
351     UnixFolderIcon_IExtractIconW_Extract
352 };
353
354 /* The singleton instance
355  */
356 UnixFolderIcon UnixFolderIconSingleton = { &UnixFolderIcon_IExtractIconW_Vtbl };
357
358 /******************************************************************************
359  * UnixFolder
360  *
361  * Class whose heap based instances represent unix filesystem directories.
362  */
363
364 static shvheader GenericSFHeader[] = {
365     {IDS_SHV_COLUMN1, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
366     {IDS_SHV_COLUMN2, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
367     {IDS_SHV_COLUMN3, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
368     {IDS_SHV_COLUMN4, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 12},
369     {IDS_SHV_COLUMN5, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 5}
370 };
371
372 #define GENERICSHELLVIEWCOLUMNS 5
373
374 /* UnixFolder object layout and typedef.
375  */
376 typedef struct _UnixFolder {
377     const IShellFolder2Vtbl  *lpIShellFolder2Vtbl;
378     const IPersistFolderVtbl *lpIPersistFolderVtbl;
379     ULONG m_cRef;
380     CHAR *m_pszPath;
381     LPITEMIDLIST m_pidlLocation;
382     LPITEMIDLIST *m_apidlSubDirs;
383     DWORD m_cSubDirs;
384 } UnixFolder;
385
386 static void UnixFolder_Destroy(UnixFolder *pUnixFolder) {
387     DWORD i;
388
389     TRACE("(pUnixFolder=%p)\n", pUnixFolder);
390     
391     if (pUnixFolder->m_apidlSubDirs) 
392         for (i=0; i < pUnixFolder->m_cSubDirs; i++) 
393             SHFree(pUnixFolder->m_apidlSubDirs[i]);
394     SHFree(pUnixFolder->m_apidlSubDirs);
395     SHFree(pUnixFolder->m_pszPath);
396     ILFree(pUnixFolder->m_pidlLocation);
397     SHFree(pUnixFolder);
398 }
399
400 static HRESULT WINAPI UnixFolder_IShellFolder2_QueryInterface(IShellFolder2 *iface, REFIID riid, 
401     void **ppv) 
402 {
403     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
404         
405     TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
406     
407     if (!ppv) return E_INVALIDARG;
408     
409     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IShellFolder, riid) || 
410         IsEqualIID(&IID_IShellFolder2, riid)) 
411     {
412         *ppv = &This->lpIShellFolder2Vtbl;
413     } else if (IsEqualIID(&IID_IPersistFolder, riid) || IsEqualIID(&IID_IPersist, riid)) {
414         *ppv = &This->lpIPersistFolderVtbl;
415     } else {
416         *ppv = NULL;
417         return E_NOINTERFACE;
418     }
419
420     IUnknown_AddRef((IUnknown*)*ppv);
421     return S_OK;
422 }
423
424 static ULONG WINAPI UnixFolder_IShellFolder2_AddRef(IShellFolder2 *iface) {
425     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
426
427     TRACE("(iface=%p)\n", iface);
428
429     return InterlockedIncrement(&This->m_cRef);
430 }
431
432 static ULONG WINAPI UnixFolder_IShellFolder2_Release(IShellFolder2 *iface) {
433     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
434     ULONG cRef;
435     
436     TRACE("(iface=%p)\n", iface);
437
438     cRef = InterlockedDecrement(&This->m_cRef);
439     
440     if (!cRef) 
441         UnixFolder_Destroy(This);
442
443     return cRef;
444 }
445
446 static HRESULT WINAPI UnixFolder_IShellFolder2_ParseDisplayName(IShellFolder2* iface, HWND hwndOwner, 
447     LPBC pbcReserved, LPOLESTR lpszDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl, 
448     ULONG* pdwAttributes)
449 {
450     int cPathLen;
451     char *pszAnsiPath;
452     BOOL result;
453
454     TRACE("(iface=%p, hwndOwner=%p, pbcReserved=%p, lpszDisplayName=%s, pchEaten=%p, ppidl=%p, "
455           "pdwAttributes=%p) stub\n", iface, hwndOwner, pbcReserved, debugstr_w(lpszDisplayName), 
456           pchEaten, ppidl, pdwAttributes);
457
458     cPathLen = lstrlenW(lpszDisplayName);
459     pszAnsiPath = (char*)SHAlloc(cPathLen+1);
460     WideCharToMultiByte(CP_ACP, 0, lpszDisplayName, -1, pszAnsiPath, cPathLen+1, NULL, NULL);
461
462     result = UNIXFS_path_to_pidl(pszAnsiPath, ppidl);
463
464     SHFree(pszAnsiPath);
465     
466     return result ? S_OK : E_FAIL;
467 }
468
469 static IUnknown *UnixSubFolderIterator_Construct(UnixFolder *pUnixFolder);
470
471 static HRESULT WINAPI UnixFolder_IShellFolder2_EnumObjects(IShellFolder2* iface, HWND hwndOwner, 
472     SHCONTF grfFlags, IEnumIDList** ppEnumIDList)
473 {
474     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
475     IUnknown *newIterator;
476     HRESULT hr;
477     
478     TRACE("(iface=%p, hwndOwner=%p, grfFlags=%08lx, ppEnumIDList=%p)\n", 
479             iface, hwndOwner, grfFlags, ppEnumIDList);
480
481     newIterator = UnixSubFolderIterator_Construct(This);
482     hr = IUnknown_QueryInterface(newIterator, &IID_IEnumIDList, (void**)ppEnumIDList);
483     IUnknown_Release(newIterator);
484     
485     return hr;
486 }
487
488 static HRESULT WINAPI UnixFolder_IShellFolder2_BindToObject(IShellFolder2* iface, LPCITEMIDLIST pidl,
489     LPBC pbcReserved, REFIID riid, void** ppvOut)
490 {
491     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
492     IPersistFolder *persistFolder;
493     LPITEMIDLIST pidlSubFolder;
494     HRESULT hr;
495         
496     TRACE("(iface=%p, pidl=%p, pbcReserver=%p, riid=%p, ppvOut=%p)\n", 
497             iface, pidl, pbcReserved, riid, ppvOut);
498
499     hr = UnixFolder_Constructor(NULL, &IID_IPersistFolder, (void**)&persistFolder);
500     if (!SUCCEEDED(hr)) return hr;
501     hr = IPersistFolder_QueryInterface(persistFolder, riid, (void**)ppvOut);
502     
503     pidlSubFolder = ILCombine(This->m_pidlLocation, pidl);
504     IPersistFolder_Initialize(persistFolder, pidlSubFolder);
505     IPersistFolder_Release(persistFolder);
506     ILFree(pidlSubFolder);
507
508     return hr;
509 }
510
511 static HRESULT WINAPI UnixFolder_IShellFolder2_BindToStorage(IShellFolder2* This, LPCITEMIDLIST pidl, 
512     LPBC pbcReserved, REFIID riid, void** ppvObj)
513 {
514     TRACE("stub\n");
515     return E_NOTIMPL;
516 }
517
518 static HRESULT WINAPI UnixFolder_IShellFolder2_CompareIDs(IShellFolder2* iface, LPARAM lParam, 
519     LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
520 {
521     BOOL isEmpty1, isEmpty2;
522     HRESULT hr = E_FAIL;
523     LPITEMIDLIST firstpidl;
524     IShellFolder2 *psf;
525     int compare;
526
527     TRACE("(iface=%p, lParam=%ld, pidl1=%p, pidl2=%p)\n", iface, lParam, pidl1, pidl2);
528     
529     isEmpty1 = !pidl1 || !pidl1->mkid.cb;
530     isEmpty2 = !pidl2 || !pidl2->mkid.cb;
531
532     if (isEmpty1 && isEmpty2) 
533         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
534     else if (isEmpty1)
535         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
536     else if (isEmpty2)
537         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
538
539     compare = CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE, pidl1->mkid.abID, pidl1->mkid.cb, 
540                              pidl2->mkid.abID, pidl2->mkid.cb);
541     if ((compare == CSTR_LESS_THAN) || (compare == CSTR_GREATER_THAN)) 
542         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)(compare == CSTR_LESS_THAN)?-1:1);
543
544     if (pidl1->mkid.cb < pidl2->mkid.cb)
545         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
546     else if (pidl1->mkid.cb > pidl2->mkid.cb)
547         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
548
549     firstpidl = ILCloneFirst(pidl1);
550     pidl1 = ILGetNext(pidl1);
551     pidl2 = ILGetNext(pidl2);
552     
553     hr = IShellFolder2_BindToObject(iface, firstpidl, NULL, &IID_IShellFolder, (LPVOID*)&psf);
554     if (SUCCEEDED(hr)) {
555         hr = IShellFolder_CompareIDs(psf, lParam, pidl1, pidl2);
556         IShellFolder2_Release(psf);
557     }
558
559     ILFree(firstpidl);
560     return hr;
561 }
562
563 static HRESULT WINAPI UnixFolder_IShellFolder2_CreateViewObject(IShellFolder2* iface, HWND hwndOwner,
564     REFIID riid, void** ppv)
565 {
566     HRESULT hr = E_INVALIDARG;
567         
568     TRACE("(iface=%p, hwndOwner=%p, riid=%p, ppv=%p) stub\n", iface, hwndOwner, riid, ppv);
569     
570     if (!ppv) return E_INVALIDARG;
571     *ppv = NULL;
572     
573     if (IsEqualIID(&IID_IShellView, riid)) {
574         LPSHELLVIEW pShellView;
575         
576         pShellView = IShellView_Constructor((IShellFolder*)iface);
577         if (pShellView) {
578             hr = IShellView_QueryInterface(pShellView, riid, ppv);
579             IShellView_Release(pShellView);
580         }
581     } 
582     
583     return hr;
584 }
585
586 static HRESULT WINAPI UnixFolder_IShellFolder2_GetAttributesOf(IShellFolder2* iface, UINT cidl, 
587     LPCITEMIDLIST* apidl, SFGAOF* rgfInOut)
588 {
589     TRACE("(iface=%p, cidl=%u, apidl=%p, rgfInOut=%p) semi-stub\n", iface, cidl, apidl, rgfInOut);
590     
591     *rgfInOut = *rgfInOut & (SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
592             
593     return S_OK;
594 }
595
596 static HRESULT WINAPI UnixFolder_IShellFolder2_GetUIObjectOf(IShellFolder2* iface, HWND hwndOwner, 
597     UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void** ppvOut)
598 {
599     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
600     
601     TRACE("(iface=%p, hwndOwner=%p, cidl=%d, apidl=%p, riid=%s, prgfInOut=%p, ppv=%p)\n",
602         iface, hwndOwner, cidl, apidl, debugstr_guid(riid), prgfInOut, ppvOut);
603
604     if (IsEqualIID(&IID_IContextMenu, riid)) {
605         *ppvOut = ISvItemCm_Constructor((IShellFolder*)iface, This->m_pidlLocation, apidl, cidl);
606         return S_OK;
607     } else if (IsEqualIID(&IID_IDataObject, riid)) {
608         *ppvOut = IDataObject_Constructor(hwndOwner, This->m_pidlLocation, apidl, cidl);
609         return S_OK;
610     } else if (IsEqualIID(&IID_IExtractIconA, riid)) {
611         FIXME("IExtractIconA\n");
612         return E_FAIL;
613     } else if (IsEqualIID(&IID_IExtractIconW, riid)) {
614         *ppvOut = &UnixFolderIconSingleton;
615         return S_OK;
616     } else if (IsEqualIID(&IID_IDropTarget, riid)) {
617         FIXME("IDropTarget\n");
618         return E_FAIL;
619     } else if (IsEqualIID(&IID_IShellLinkW, riid)) {
620         FIXME("IShellLinkW\n");
621         return E_FAIL;
622     } else if (IsEqualIID(&IID_IShellLinkA, riid)) {
623         FIXME("IShellLinkA\n");
624         return E_FAIL;
625     } else {
626         FIXME("Unknown interface %s in GetUIObjectOf\n", debugstr_guid(riid));
627         return E_NOINTERFACE;
628     }
629 }
630
631 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDisplayNameOf(IShellFolder2* iface, 
632     LPCITEMIDLIST pidl, SHGDNF uFlags, STRRET* lpName)
633 {
634     LPCSHITEMID pSHItem = (LPCSHITEMID)pidl;
635     char szName[MAX_PATH];
636
637     lpName->uType = STRRET_CSTR;
638     if (!pidl->mkid.cb) {
639         strcpy(lpName->u.cStr, "");
640         return S_OK;
641     }
642
643     memcpy(szName, pSHItem->abID, pSHItem->cb-sizeof(USHORT));
644     szName[pSHItem->cb-sizeof(USHORT)] = '\0';
645     
646     TRACE("(iface=%p, pidl=%p, uFlags=%lx, lpName=%p)\n", iface, pidl, uFlags, lpName);
647    
648     if ((uFlags & SHGDN_FORPARSING) && !(uFlags & SHGDN_INFOLDER)) {
649         STRRET strSubfolderName;
650         IShellFolder *pSubFolder;
651         HRESULT hr;
652         LPITEMIDLIST pidlFirst;
653
654         pidlFirst = ILCloneFirst(pidl);
655         if (!pidlFirst) {
656             WARN("ILCloneFirst failed!\n");
657             return E_FAIL;
658         }
659
660         hr = IShellFolder_BindToObject(iface, pidlFirst, NULL, &IID_IShellFolder, 
661                                        (void**)&pSubFolder);
662         if (!SUCCEEDED(hr)) {
663             WARN("BindToObject failed!\n");
664             ILFree(pidlFirst);
665             return hr;
666         }
667
668         ILFree(pidlFirst);
669         
670         hr = IShellFolder_GetDisplayNameOf(pSubFolder, ILGetNext(pidl), uFlags, &strSubfolderName);
671         if (!SUCCEEDED(hr)) {
672             WARN("GetDisplayNameOf failed!\n");
673             return hr;
674         }
675         
676         snprintf(lpName->u.cStr, MAX_PATH, "%s%s", szName, strSubfolderName.u.cStr);
677
678         IShellFolder_Release(pSubFolder);
679     } else {
680         size_t len;
681         strcpy(lpName->u.cStr, szName);
682         len = strlen(lpName->u.cStr);
683         if (len > 1) lpName->u.cStr[len-1] = '\0';
684     }
685
686     return S_OK;
687 }
688
689 static HRESULT WINAPI UnixFolder_IShellFolder2_SetNameOf(IShellFolder2* This, HWND hwnd, 
690     LPCITEMIDLIST pidl, LPCOLESTR lpszName, SHGDNF uFlags, LPITEMIDLIST* ppidlOut)
691 {
692     TRACE("stub\n");
693     return E_NOTIMPL;
694 }
695
696 static HRESULT WINAPI UnixFolder_IShellFolder2_EnumSearches(IShellFolder2* iface, 
697     IEnumExtraSearch **ppEnum) 
698 {
699     TRACE("stub\n");
700     return E_NOTIMPL;
701 }
702
703 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumn(IShellFolder2* iface, 
704     DWORD dwReserved, ULONG *pSort, ULONG *pDisplay) 
705 {
706     TRACE("stub\n");
707     return E_NOTIMPL;
708 }
709
710 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumnState(IShellFolder2* iface, 
711     UINT iColumn, SHCOLSTATEF *pcsFlags)
712 {
713     TRACE("stub\n");
714     return E_NOTIMPL;
715 }
716
717 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultSearchGUID(IShellFolder2* iface, 
718     GUID *pguid)
719 {
720     TRACE("stub\n");
721     return E_NOTIMPL;
722 }
723
724 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsEx(IShellFolder2* iface, 
725     LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
726 {
727     TRACE("stub\n");
728     return E_NOTIMPL;
729 }
730
731 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsOf(IShellFolder2* iface, 
732     LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *psd)
733 {
734     HRESULT hr = E_FAIL;
735         
736     TRACE("(iface=%p, pidl=%p, iColumn=%d, psd=%p) stub\n", iface, pidl, iColumn, psd);
737     
738     if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
739         return E_INVALIDARG;
740
741     if (!pidl) {
742         psd->fmt = GenericSFHeader[iColumn].fmt;
743         psd->cxChar = GenericSFHeader[iColumn].cxChar;
744         psd->str.uType = STRRET_CSTR;
745         LoadStringA(shell32_hInstance, GenericSFHeader[iColumn].colnameid, psd->str.u.cStr, MAX_PATH);
746         return S_OK;
747     } else {
748         switch (iColumn) {
749             case 0:
750                 hr = IShellFolder2_GetDisplayNameOf(iface, pidl, SHGDN_NORMAL|SHGDN_INFOLDER, &psd->str);
751                 break;
752         }
753         psd->str.uType = STRRET_CSTR;
754     }
755     
756     return hr;
757 }
758
759 static HRESULT WINAPI UnixFolder_IShellFolder2_MapColumnToSCID(IShellFolder2* iface, UINT iColumn,
760     SHCOLUMNID *pscid) 
761 {
762     TRACE("stub\n");
763     return E_NOTIMPL;
764 }
765
766 /* VTable for UnixFolder's IShellFolder2 interface.
767  */
768 static const IShellFolder2Vtbl UnixFolder_IShellFolder2_Vtbl = {
769     UnixFolder_IShellFolder2_QueryInterface,
770     UnixFolder_IShellFolder2_AddRef,
771     UnixFolder_IShellFolder2_Release,
772     UnixFolder_IShellFolder2_ParseDisplayName,
773     UnixFolder_IShellFolder2_EnumObjects,
774     UnixFolder_IShellFolder2_BindToObject,
775     UnixFolder_IShellFolder2_BindToStorage,
776     UnixFolder_IShellFolder2_CompareIDs,
777     UnixFolder_IShellFolder2_CreateViewObject,
778     UnixFolder_IShellFolder2_GetAttributesOf,
779     UnixFolder_IShellFolder2_GetUIObjectOf,
780     UnixFolder_IShellFolder2_GetDisplayNameOf,
781     UnixFolder_IShellFolder2_SetNameOf,
782     UnixFolder_IShellFolder2_GetDefaultSearchGUID,
783     UnixFolder_IShellFolder2_EnumSearches,
784     UnixFolder_IShellFolder2_GetDefaultColumn,
785     UnixFolder_IShellFolder2_GetDefaultColumnState,
786     UnixFolder_IShellFolder2_GetDetailsEx,
787     UnixFolder_IShellFolder2_GetDetailsOf,
788     UnixFolder_IShellFolder2_MapColumnToSCID
789 };
790
791 static HRESULT WINAPI UnixFolder_IPersistFolder_QueryInterface(IPersistFolder* This, REFIID riid, 
792     void** ppvObject)
793 {
794     return UnixFolder_IShellFolder2_QueryInterface(
795                 (IShellFolder2*)ADJUST_THIS(UnixFolder, IPersistFolder, This), riid, ppvObject);
796 }
797
798 static ULONG WINAPI UnixFolder_IPersistFolder_AddRef(IPersistFolder* This)
799 {
800     return UnixFolder_IShellFolder2_AddRef(
801                 (IShellFolder2*)ADJUST_THIS(UnixFolder, IPersistFolder, This));
802 }
803
804 static ULONG WINAPI UnixFolder_IPersistFolder_Release(IPersistFolder* This)
805 {
806     return UnixFolder_IShellFolder2_Release(
807                 (IShellFolder2*)ADJUST_THIS(UnixFolder, IPersistFolder, This));
808 }
809
810 static HRESULT WINAPI UnixFolder_IPersistFolder_GetClassID(IPersistFolder* This, CLSID* pClassID)
811 {
812     TRACE("stub\n");
813     return E_NOTIMPL;
814 }
815
816 static HRESULT WINAPI UnixFolder_IPersistFolder_Initialize(IPersistFolder* iface, LPCITEMIDLIST pidl)
817 {
818     UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder, iface);
819     
820     TRACE("(iface=%p, pidl=%p)\n", iface, pidl);
821
822     This->m_pidlLocation = ILClone(pidl);
823     UNIXFS_pidl_to_path(pidl, &This->m_pszPath);
824     UNIXFS_build_subfolder_pidls(This->m_pszPath, &This->m_apidlSubDirs, &This->m_cSubDirs);
825     
826     return S_OK;
827 }
828
829 /* VTable for UnixFolder's IPersistFolder interface.
830  */
831 static const IPersistFolderVtbl UnixFolder_IPersistFolder_Vtbl = {
832     UnixFolder_IPersistFolder_QueryInterface,
833     UnixFolder_IPersistFolder_AddRef,
834     UnixFolder_IPersistFolder_Release,
835     UnixFolder_IPersistFolder_GetClassID,
836     UnixFolder_IPersistFolder_Initialize
837 };
838
839 /******************************************************************************
840  * UnixFolder_Constructor [Internal]
841  *
842  * PARAMS
843  *  pUnkOuter [I] Outer class for aggregation. Currently ignored.
844  *  riid      [I] Interface asked for by the client.
845  *  ppv       [O] Pointer to an riid interface to the UnixFolder object.
846  *
847  * NOTES
848  *  This is the only function exported from shfldr_unixfs.c. It's called from
849  *  shellole.c's default class factory and thus has to exhibit a LPFNCREATEINSTANCE
850  *  compatible signature.
851  */
852 HRESULT WINAPI UnixFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
853     HRESULT hr;
854     UnixFolder *pUnixFolder;
855     
856     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
857
858     pUnixFolder = SHAlloc((ULONG)sizeof(UnixFolder));
859     pUnixFolder->lpIShellFolder2Vtbl = &UnixFolder_IShellFolder2_Vtbl;
860     pUnixFolder->lpIPersistFolderVtbl = &UnixFolder_IPersistFolder_Vtbl;
861     pUnixFolder->m_cRef = 0;
862     pUnixFolder->m_pszPath = NULL;
863     pUnixFolder->m_apidlSubDirs = NULL;
864     pUnixFolder->m_cSubDirs = 0;
865
866     UnixFolder_IShellFolder2_AddRef(STATIC_CAST(IShellFolder2, pUnixFolder));
867     hr = UnixFolder_IShellFolder2_QueryInterface(STATIC_CAST(IShellFolder2, pUnixFolder), riid, ppv);
868     UnixFolder_IShellFolder2_Release(STATIC_CAST(IShellFolder2, pUnixFolder));
869     return hr;
870 }
871
872 /******************************************************************************
873  * UnixSubFolderIterator
874  *
875  * Class whose heap based objects represent iterators over the sub-directories
876  * of a given UnixFolder object. 
877  */
878
879 /* UnixSubFolderIterator object layout and typedef.
880  */
881 typedef struct _UnixSubFolderIterator {
882     const IEnumIDListVtbl *lpIEnumIDListVtbl;
883     ULONG m_cRef;
884     UnixFolder *m_pUnixFolder;
885     ULONG m_cIdx;
886 } UnixSubFolderIterator;
887
888 static void UnixSubFolderIterator_Destroy(UnixSubFolderIterator *iterator) {
889     TRACE("(iterator=%p)\n", iterator);
890         
891     UnixFolder_IShellFolder2_Release((IShellFolder2*)iterator->m_pUnixFolder);
892     SHFree(iterator);
893 }
894
895 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_QueryInterface(IEnumIDList* iface, 
896     REFIID riid, void** ppv)
897 {
898     TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
899     
900     if (!ppv) return E_INVALIDARG;
901     
902     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IEnumIDList, riid)) {
903         *ppv = iface;
904     } else {
905         *ppv = NULL;
906         return E_NOINTERFACE;
907     }
908
909     IEnumIDList_AddRef(iface);
910     return S_OK;
911 }
912                             
913 static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_AddRef(IEnumIDList* iface)
914 {
915     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
916
917     TRACE("(iface=%p)\n", iface);
918    
919     return InterlockedIncrement(&This->m_cRef);
920 }
921
922 static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_Release(IEnumIDList* iface)
923 {
924     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
925     ULONG cRef;
926     
927     TRACE("(iface=%p)\n", iface);
928
929     cRef = InterlockedDecrement(&This->m_cRef);
930     
931     if (!cRef) 
932         UnixSubFolderIterator_Destroy(This);
933
934     return cRef;
935 }
936
937 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Next(IEnumIDList* iface, ULONG celt, 
938     LPITEMIDLIST* rgelt, ULONG* pceltFetched)
939 {
940     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
941     ULONG i;
942
943     TRACE("(iface=%p, celt=%ld, rgelt=%p, pceltFetched=%p)\n", iface, celt, rgelt, pceltFetched);
944    
945     for (i=0; (i < celt) && (This->m_cIdx < This->m_pUnixFolder->m_cSubDirs); i++, This->m_cIdx++) {
946         rgelt[i] = ILClone(This->m_pUnixFolder->m_apidlSubDirs[This->m_cIdx]);
947     }
948
949     if (pceltFetched)
950         *pceltFetched = i;
951
952     return i == celt ? S_OK : S_FALSE;
953 }
954
955 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Skip(IEnumIDList* iface, ULONG celt)
956 {
957     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
958     
959     TRACE("(iface=%p, celt=%ld)\n", iface, celt);
960
961     if (This->m_cIdx + celt > This->m_pUnixFolder->m_cSubDirs) {
962         This->m_cIdx = This->m_pUnixFolder->m_cSubDirs;
963         return S_FALSE;
964     } else {
965         This->m_cIdx += celt;
966         return S_OK;
967     }
968 }
969
970 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Reset(IEnumIDList* iface)
971 {
972     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
973         
974     TRACE("(iface=%p)\n", iface);
975
976     This->m_cIdx = 0;
977     
978     return S_OK;
979 }
980
981 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Clone(IEnumIDList* This, 
982     IEnumIDList** ppenum)
983 {
984     TRACE("stub\n");
985     return E_NOTIMPL;
986 }
987
988 /* VTable for UnixSubFolderIterator's IEnumIDList interface.
989  */
990 static const IEnumIDListVtbl UnixSubFolderIterator_IEnumIDList_Vtbl = {
991     UnixSubFolderIterator_IEnumIDList_QueryInterface,
992     UnixSubFolderIterator_IEnumIDList_AddRef,
993     UnixSubFolderIterator_IEnumIDList_Release,
994     UnixSubFolderIterator_IEnumIDList_Next,
995     UnixSubFolderIterator_IEnumIDList_Skip,
996     UnixSubFolderIterator_IEnumIDList_Reset,
997     UnixSubFolderIterator_IEnumIDList_Clone
998 };
999
1000 static IUnknown *UnixSubFolderIterator_Construct(UnixFolder *pUnixFolder) {
1001     UnixSubFolderIterator *iterator;
1002
1003     TRACE("(pUnixFolder=%p)\n", pUnixFolder);
1004     
1005     iterator = SHAlloc((ULONG)sizeof(UnixSubFolderIterator));
1006     iterator->lpIEnumIDListVtbl = &UnixSubFolderIterator_IEnumIDList_Vtbl;
1007     iterator->m_cRef = 0;
1008     iterator->m_cIdx = 0;
1009     iterator->m_pUnixFolder = pUnixFolder;
1010
1011     UnixSubFolderIterator_IEnumIDList_AddRef((IEnumIDList*)iterator);
1012     UnixFolder_IShellFolder2_AddRef((IShellFolder2*)pUnixFolder);
1013     
1014     return (IUnknown*)iterator;
1015 }