wmiutils: Implement IWbemPath::GetNamespaceAt.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 /*
22  * As you know, windows and unix do have a different philosophy with regard to
23  * the question of how a filesystem should be laid out. While we unix geeks
24  * learned to love the 'one-tree-rooted-at-/' approach, windows has in fact
25  * a whole forest of filesystem trees, each of which is typically identified by
26  * a drive letter.
27  *
28  * We would like wine to integrate as smoothly as possible (that is without
29  * sacrificing win32 compatibility) into the unix environment. For the
30  * filesystem question, this means we really would like those windows
31  * applications to work with unix path- and file-names. Unfortunately, this
32  * seems to be impossible in general. Therefore we have those symbolic links
33  * in wine's 'dosdevices' directory, which are used to simulate drives
34  * to keep windows applications happy. And as a consequence, we have those
35  * drive letters show up now and then in GUI applications running under wine,
36  * which gets the unix hardcore fans all angry, shouting at us @#!&$%* wine
37  * hackers that we are seducing the big companies not to port their applications
38  * to unix.
39  *
40  * DOS paths do appear at various places in GUI applications. Sometimes, they
41  * show up in the title bar of an application's window. They tend to accumulate
42  * in the most-recently-used section of the file-menu. And I've even seen some
43  * in a configuration dialog's edit control. In those examples, wine can't do a
44  * lot about this, since path-names can't be told apart from ordinary strings
45  * here. That's different in the file dialogs, though.
46  *
47  * With the introduction of the 'shell' in win32, Microsoft established an 
48  * abstraction layer on top of the filesystem, called the shell namespace (I was
49  * told that GNOME's virtual filesystem is conceptually similar). In the shell
50  * namespace, one doesn't use ascii- or unicode-strings to uniquely identify
51  * objects. Instead Microsoft introduced item-identifier-lists (The c type is 
52  * called ITEMIDLIST) as an abstraction of path-names. As you probably would
53  * have guessed, an item-identifier-list is a list of item-identifiers (whose
54  * c type's funny name is SHITEMID), which are opaque binary objects. This means
55  * that no application (apart from Microsoft Office) should make any assumptions
56  * on the internal structure of these SHITEMIDs. 
57  *
58  * Since the user prefers to be presented the good-old DOS file-names instead of 
59  * binary ITEMIDLISTs, a translation method between string-based file-names and
60  * ITEMIDLISTs was established. At the core of this are the COM-Interface
61  * IShellFolder and especially it's methods ParseDisplayName and 
62  * GetDisplayNameOf. Basically, you give a DOS-path (let's say C:\windows) to
63  * ParseDisplayName and get a SHITEMID similar to <Desktop|My Computer|C:|windows|>.
64  * Since it's opaque, you can't see the 'C', the 'windows' and the other stuff.
65  * You can only figure out that the ITEMIDLIST is composed of four SHITEMIDS.
66  * The file dialog applies IShellFolder's BindToObject method to bind to each of
67  * those four objects (Desktop, My Computer, C: and windows. All of them have to 
68  * implement the IShellFolder interface.) and asks them how they would like to be 
69  * displayed (basically their icon and the string displayed). If the file dialog 
70  * asks <Desktop|My Computer|C:|windows> which sub-objects it contains (via 
71  * EnumObjects) it gets a list of opaque SHITEMIDs, which can be concatenated to 
72  * <Desktop|...|windows> to build a new ITEMIDLIST and browse, for instance, 
73  * into <system32>. This means the file dialog browses the shell namespace by 
74  * identifying objects via ITEMIDLISTs. Once the user has selected a location to 
75  * save his valuable file, the file dialog calls IShellFolder's GetDisplayNameOf
76  * method to translate the ITEMIDLIST back to a DOS filename.
77  * 
78  * It seems that one intention of the shell namespace concept was to make it 
79  * possible to have objects in the namespace, which don't have any counterpart 
80  * in the filesystem. The 'My Computer' shell folder object is one instance
81  * which comes to mind (Go try to save a file into 'My Computer' on windows.)
82  * So, to make matters a little more complex, before the file dialog asks a
83  * shell namespace object for it's DOS path, it asks if it actually has one.
84  * This is done via the IShellFolder::GetAttributesOf method, which sets the
85  * SFGAO_FILESYSTEM if - and only if - it has.
86  *
87  * The two things, described in the previous two paragraphs, are what unixfs is
88  * based on. So basically, if UnixDosFolder's ParseDisplayName method is called 
89  * with a 'c:\windows' path-name, it doesn't return an 
90  * <Desktop|My Computer|C:|windows|> ITEMIDLIST. Instead, it uses 
91  * shell32's wine_get_unix_path_name and the _posix_ (which means not the win32) 
92  * fileio api's to figure out that c: is mapped to - let's say - 
93  * /home/mjung/.wine/drive_c and then constructs a 
94  * <Desktop|/|home|mjung|.wine|drive_c> ITEMIDLIST. Which is what the file 
95  * dialog uses to display the folder and file objects, which is why you see a 
96  * unix path. When the user has found a nice place for his file and hits the
97  * save button, the ITEMIDLIST of the selected folder object is passed to 
98  * GetDisplayNameOf, which returns a _DOS_ path name 
99  * (like H:\home_of_my_new_file out of <|Desktop|/|home|mjung|home_of_my_new_file|>).
100  * Unixfs basically mounts your dos devices together in order to construct
101  * a copy of your unix filesystem structure.
102  *
103  * But what if none of the symbolic links in 'dosdevices' points to '/', you 
104  * might ask ("And I don't want wine have access to my complete hard drive, you 
105  * *%&1#!"). No problem, as I stated above, unixfs uses the _posix_ apis to 
106  * construct the ITEMIDLISTs. Folders, which aren't accessible via a drive letter,
107  * don't have the SFGAO_FILESYSTEM flag set. So the file dialogs shouldn't allow
108  * the user to select such a folder for file storage (And if it does anyhow, it 
109  * will not be able to return a valid path, since there is none). Think of those 
110  * folders as a hierarchy of 'My Computer'-like folders, which happen to be a 
111  * shadow of your unix filesystem tree. And since all of this stuff doesn't 
112  * change anything at all in wine's fileio api's, windows applications will have 
113  * no more access rights as they had before. 
114  *
115  * To sum it all up, you can still safely run wine with you root account (Just
116  * kidding, don't do it.)
117  *
118  * If you are now standing in front of your computer, shouting hotly 
119  * "I am not convinced, Mr. Rumsfeld^H^H^H^H^H^H^H^H^H^H^H^H", fire up regedit
120  * and delete HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\
121  * Explorer\Desktop\Namespace\{9D20AAE8-0625-44B0-9CA7-71889C2254D9} and you 
122  * will be back in the pre-unixfs days.
123  */
124
125 #include "config.h"
126 #include "wine/port.h"
127
128 #include <stdio.h>
129 #include <stdarg.h>
130 #include <limits.h>
131 #include <errno.h>
132 #ifdef HAVE_DIRENT_H
133 # include <dirent.h>
134 #endif
135 #include <stdlib.h>
136 #ifdef HAVE_UNISTD_H
137 # include <unistd.h>
138 #endif
139 #ifdef HAVE_SYS_STAT_H
140 # include <sys/stat.h>
141 #endif
142 #ifdef HAVE_PWD_H
143 # include <pwd.h>
144 #endif
145 #ifdef HAVE_GRP_H
146 # include <grp.h>
147 #endif
148
149 #define COBJMACROS
150 #define NONAMELESSUNION
151 #define NONAMELESSSTRUCT
152
153 #include "windef.h"
154 #include "winbase.h"
155 #include "winuser.h"
156 #include "objbase.h"
157 #include "winreg.h"
158 #include "shlwapi.h"
159 #include "winternl.h"
160 #include "wine/debug.h"
161
162 #include "shell32_main.h"
163 #include "shellfolder.h"
164 #include "shfldr.h"
165 #include "shresdef.h"
166 #include "pidl.h"
167 #include "debughlp.h"
168
169 WINE_DEFAULT_DEBUG_CHANNEL(shell);
170
171 #if !defined(__MINGW32__) && !defined(_MSC_VER)
172
173 #define ADJUST_THIS(c,m,p) ((c*)(((long)p)-(long)&(((c*)0)->lp##m##Vtbl)))
174 #define STATIC_CAST(i,p) ((i*)&p->lp##i##Vtbl)
175
176 #define LEN_SHITEMID_FIXED_PART ((USHORT) \
177     ( sizeof(USHORT)      /* SHITEMID's cb field. */ \
178     + sizeof(PIDLTYPE)    /* PIDLDATA's type field. */ \
179     + sizeof(FileStruct)  /* Well, the FileStruct. */ \
180     - sizeof(char)        /* One char too much in FileStruct. */ \
181     + sizeof(FileStructW) /* You name it. */ \
182     - sizeof(WCHAR)       /* One WCHAR too much in FileStructW. */ \
183     + sizeof(WORD) ))     /* Offset of FileStructW field in PIDL. */
184
185 #define PATHMODE_UNIX 0
186 #define PATHMODE_DOS  1
187
188 static const WCHAR wFileSystemBindData[] = {
189     'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d',' ','D','a','t','a',0};
190
191 typedef struct {
192     IShellFolder2       IShellFolder2_iface;
193     IPersistFolder3     IPersistFolder3_iface;
194     IPersistPropertyBag IPersistPropertyBag_iface;
195     IDropTarget         IDropTarget_iface;
196     ISFHelper           ISFHelper_iface;
197
198     LONG         ref;
199     CHAR         *m_pszPath;     /* Target path of the shell folder (CP_UNIXCP) */
200     LPITEMIDLIST m_pidlLocation; /* Location in the shell namespace */
201     DWORD        m_dwPathMode;
202     DWORD        m_dwAttributes;
203     const CLSID  *m_pCLSID;
204     DWORD        m_dwDropEffectsMask;
205 } UnixFolder;
206
207 static inline UnixFolder *impl_from_IShellFolder2(IShellFolder2 *iface)
208 {
209     return CONTAINING_RECORD(iface, UnixFolder, IShellFolder2_iface);
210 }
211
212 static inline UnixFolder *impl_from_IPersistFolder3(IPersistFolder3 *iface)
213 {
214     return CONTAINING_RECORD(iface, UnixFolder, IPersistFolder3_iface);
215 }
216
217 static inline UnixFolder *impl_from_IPersistPropertyBag(IPersistPropertyBag *iface)
218 {
219     return CONTAINING_RECORD(iface, UnixFolder, IPersistPropertyBag_iface);
220 }
221
222 static inline UnixFolder *impl_from_ISFHelper(ISFHelper *iface)
223 {
224     return CONTAINING_RECORD(iface, UnixFolder, ISFHelper_iface);
225 }
226
227 static inline UnixFolder *impl_from_IDropTarget(IDropTarget *iface)
228 {
229     return CONTAINING_RECORD(iface, UnixFolder, IDropTarget_iface);
230 }
231
232 /* Will hold the registered clipboard format identifier for ITEMIDLISTS. */
233 static UINT cfShellIDList = 0;
234
235 /******************************************************************************
236  * UNIXFS_filename_from_shitemid [Internal]
237  *
238  *  Get CP_UNIXCP encoded filename corresponding to the first item of a pidl
239  * 
240  * PARAMS
241  *  pidl           [I] A simple SHITEMID
242  *  pszPathElement [O] Filename in CP_UNIXCP encoding will be stored here
243  *
244  * RETURNS
245  *  Success: Number of bytes necessary to store the CP_UNIXCP encoded filename 
246  *   _without_ the terminating NUL.
247  *  Failure: 0
248  *  
249  * NOTES
250  *  Size of the buffer at pszPathElement has to be FILENAME_MAX. pszPathElement
251  *  may be NULL, if you are only interested in the return value. 
252  */
253 static int UNIXFS_filename_from_shitemid(LPCITEMIDLIST pidl, char* pszPathElement) {
254     FileStructW *pFileStructW = _ILGetFileStructW(pidl);
255     int cLen = 0;
256
257     if (pFileStructW) {
258         cLen = WideCharToMultiByte(CP_UNIXCP, 0, pFileStructW->wszName, -1, pszPathElement,
259             pszPathElement ? FILENAME_MAX : 0, 0, 0);
260     } else {
261         /* There might be pidls slipping in from shfldr_fs.c, which don't contain the
262          * FileStructW field. In this case, we have to convert from CP_ACP to CP_UNIXCP. */
263         char *pszText = _ILGetTextPointer(pidl);
264         WCHAR *pwszPathElement = NULL;
265         int cWideChars;
266     
267         cWideChars = MultiByteToWideChar(CP_ACP, 0, pszText, -1, NULL, 0);
268         if (!cWideChars) goto cleanup;
269
270         pwszPathElement = SHAlloc(cWideChars * sizeof(WCHAR));
271         if (!pwszPathElement) goto cleanup;
272     
273         cWideChars = MultiByteToWideChar(CP_ACP, 0, pszText, -1, pwszPathElement, cWideChars);
274         if (!cWideChars) goto cleanup; 
275
276         cLen = WideCharToMultiByte(CP_UNIXCP, 0, pwszPathElement, -1, pszPathElement, 
277             pszPathElement ? FILENAME_MAX : 0, 0, 0);
278
279     cleanup:
280         SHFree(pwszPathElement);
281     }
282         
283     if (cLen) cLen--; /* Don't count terminating NUL! */
284     return cLen;
285 }
286
287 /******************************************************************************
288  * UNIXFS_shitemid_len_from_filename [Internal]
289  *
290  * Computes the necessary length of a pidl to hold a path element
291  *
292  * PARAMS
293  *  szPathElement    [I] The path element string in CP_UNIXCP encoding.
294  *  ppszPathElement  [O] Path element string in CP_ACP encoding.
295  *  ppwszPathElement [O] Path element string as WCHAR string.
296  *
297  * RETURNS
298  *  Success: Length in bytes of a SHITEMID representing szPathElement
299  *  Failure: 0
300  * 
301  * NOTES
302  *  Provide NULL values if not interested in pp(w)szPathElement. Otherwise
303  *  caller is responsible to free ppszPathElement and ppwszPathElement with
304  *  SHFree.
305  */
306 static USHORT UNIXFS_shitemid_len_from_filename(
307     const char *szPathElement, char **ppszPathElement, WCHAR **ppwszPathElement) 
308 {
309     USHORT cbPidlLen = 0;
310     WCHAR *pwszPathElement = NULL;
311     char *pszPathElement = NULL;
312     int cWideChars, cChars;
313
314     /* There and Back Again: A Hobbit's Holiday. CP_UNIXCP might be some ANSI
315      * codepage or it might be a real multi-byte encoding like utf-8. There is no
316      * other way to figure out the length of the corresponding WCHAR and CP_ACP 
317      * strings without actually doing the full CP_UNIXCP -> WCHAR -> CP_ACP cycle. */
318     
319     cWideChars = MultiByteToWideChar(CP_UNIXCP, 0, szPathElement, -1, NULL, 0);
320     if (!cWideChars) goto cleanup;
321
322     pwszPathElement = SHAlloc(cWideChars * sizeof(WCHAR));
323     if (!pwszPathElement) goto cleanup;
324
325     cWideChars = MultiByteToWideChar(CP_UNIXCP, 0, szPathElement, -1, pwszPathElement, cWideChars);
326     if (!cWideChars) goto cleanup; 
327
328     cChars = WideCharToMultiByte(CP_ACP, 0, pwszPathElement, -1, NULL, 0, 0, 0);
329     if (!cChars) goto cleanup;
330
331     pszPathElement = SHAlloc(cChars);
332     if (!pszPathElement) goto cleanup;
333
334     cChars = WideCharToMultiByte(CP_ACP, 0, pwszPathElement, -1, pszPathElement, cChars, 0, 0);
335     if (!cChars) goto cleanup;
336
337     /* (cChars & 0x1) is for the potential alignment byte */
338     cbPidlLen = LEN_SHITEMID_FIXED_PART + cChars + (cChars & 0x1) + cWideChars * sizeof(WCHAR);
339     
340 cleanup:
341     if (cbPidlLen && ppszPathElement) 
342         *ppszPathElement = pszPathElement;
343     else 
344         SHFree(pszPathElement);
345
346     if (cbPidlLen && ppwszPathElement)
347         *ppwszPathElement = pwszPathElement;
348     else
349         SHFree(pwszPathElement);
350
351     return cbPidlLen;
352 }
353
354 /******************************************************************************
355  * UNIXFS_is_pidl_of_type [Internal]
356  *
357  * Checks for the first SHITEMID of an ITEMIDLIST if it passes a filter.
358  *
359  * PARAMS
360  *  pIDL    [I] The ITEMIDLIST to be checked.
361  *  fFilter [I] Shell condition flags, which specify the filter.
362  *
363  * RETURNS
364  *  TRUE, if pIDL is accepted by fFilter
365  *  FALSE, otherwise
366  */
367 static inline BOOL UNIXFS_is_pidl_of_type(LPCITEMIDLIST pIDL, SHCONTF fFilter) {
368     const PIDLDATA *pIDLData = _ILGetDataPointer(pIDL);
369     if (!(fFilter & SHCONTF_INCLUDEHIDDEN) && pIDLData && 
370         (pIDLData->u.file.uFileAttribs & FILE_ATTRIBUTE_HIDDEN)) 
371     {
372         return FALSE;
373     }
374     if (_ILIsFolder(pIDL) && (fFilter & SHCONTF_FOLDERS)) return TRUE;
375     if (_ILIsValue(pIDL) && (fFilter & SHCONTF_NONFOLDERS)) return TRUE;
376     return FALSE;
377 }
378
379 /******************************************************************************
380  * UNIXFS_get_unix_path [Internal]
381  *
382  * Convert an absolute dos path to an absolute unix path.
383  * Evaluate "/.", "/.." and the symbolic links in $WINEPREFIX/dosdevices.
384  *
385  * PARAMS
386  *  pszDosPath       [I] An absolute dos path
387  *  pszCanonicalPath [O] Buffer of length FILENAME_MAX. Will receive the canonical path.
388  *
389  * RETURNS
390  *  Success, TRUE
391  *  Failure, FALSE - Path not existent, too long, insufficient rights, to many symlinks
392  */
393 static BOOL UNIXFS_get_unix_path(LPCWSTR pszDosPath, char *pszCanonicalPath)
394 {
395     char *pPathTail, *pElement, *pCanonicalTail, szPath[FILENAME_MAX], *pszUnixPath, has_failed = 0, mb_path[FILENAME_MAX];
396     WCHAR wszDrive[] = { '?', ':', '\\', 0 }, dospath[MAX_PATH], *dospath_end;
397     int cDriveSymlinkLen;
398     void *redir;
399
400     TRACE("(pszDosPath=%s, pszCanonicalPath=%p)\n", debugstr_w(pszDosPath), pszCanonicalPath);
401
402     if (!pszDosPath || pszDosPath[1] != ':')
403         return FALSE;
404
405     /* Get the canonicalized unix path corresponding to the drive letter. */
406     wszDrive[0] = pszDosPath[0];
407     pszUnixPath = wine_get_unix_file_name(wszDrive);
408     if (!pszUnixPath) return FALSE;
409     cDriveSymlinkLen = strlen(pszUnixPath);
410     pElement = realpath(pszUnixPath, szPath);
411     HeapFree(GetProcessHeap(), 0, pszUnixPath);
412     if (!pElement) return FALSE;
413     if (szPath[strlen(szPath)-1] != '/') strcat(szPath, "/");
414
415     /* Append the part relative to the drive symbolic link target. */
416     lstrcpyW(dospath, pszDosPath);
417     dospath_end = dospath + lstrlenW(dospath);
418     /* search for the most valid UNIX path possible, then append missing
419      * path parts */
420     Wow64DisableWow64FsRedirection(&redir);
421     while(!(pszUnixPath = wine_get_unix_file_name(dospath))){
422         if(has_failed){
423             *dospath_end = '/';
424             --dospath_end;
425         }else
426             has_failed = 1;
427         while(*dospath_end != '\\' && *dospath_end != '/'){
428             --dospath_end;
429             if(dospath_end < dospath)
430                 break;
431         }
432         *dospath_end = '\0';
433     }
434     Wow64RevertWow64FsRedirection(redir);
435     if(dospath_end < dospath)
436         return FALSE;
437     strcat(szPath, pszUnixPath + cDriveSymlinkLen);
438     HeapFree(GetProcessHeap(), 0, pszUnixPath);
439
440     if(has_failed && WideCharToMultiByte(CP_UNIXCP, 0, dospath_end + 1, -1,
441                 mb_path, FILENAME_MAX, NULL, NULL) > 0){
442         strcat(szPath, "/");
443         strcat(szPath, mb_path);
444     }
445
446     /* pCanonicalTail always points to the end of the canonical path constructed
447      * thus far. pPathTail points to the still to be processed part of the input
448      * path. pElement points to the path element currently investigated.
449      */
450     *pszCanonicalPath = '\0';
451     pCanonicalTail = pszCanonicalPath;
452     pPathTail = szPath;
453
454     do {
455         char cTemp;
456             
457         pElement = pPathTail;
458         pPathTail = strchr(pPathTail+1, '/');
459         if (!pPathTail) /* Last path element may not be terminated by '/'. */ 
460             pPathTail = pElement + strlen(pElement);
461         /* Temporarily terminate the current path element. Will be restored later. */
462         cTemp = *pPathTail;
463         *pPathTail = '\0';
464
465         /* Skip "/." path elements */
466         if (!strcmp("/.", pElement)) {
467             *pPathTail = cTemp;
468         } else if (!strcmp("/..", pElement)) {
469             /* Remove last element in canonical path for "/.." elements, then skip. */
470             char *pTemp = strrchr(pszCanonicalPath, '/');
471             if (pTemp)
472                 pCanonicalTail = pTemp;
473             *pCanonicalTail = '\0';
474             *pPathTail = cTemp;
475         } else {
476             /* Directory or file. Copy to canonical path */
477             if (pCanonicalTail - pszCanonicalPath + pPathTail - pElement + 1 > FILENAME_MAX)
478                 return FALSE;
479                 
480             memcpy(pCanonicalTail, pElement, pPathTail - pElement + 1);
481             pCanonicalTail += pPathTail - pElement;
482             *pPathTail = cTemp;
483         }
484     } while (pPathTail[0] == '/');
485    
486     TRACE("--> %s\n", debugstr_a(pszCanonicalPath));
487     
488     return TRUE;
489 }
490
491 /******************************************************************************
492  * UNIXFS_build_shitemid [Internal]
493  *
494  * Constructs a new SHITEMID for the last component of path 'pszUnixPath' into 
495  * buffer 'pIDL'.
496  *
497  * PARAMS
498  *  pszUnixPath [I] An absolute path. The SHITEMID will be built for the last component.
499  *  pbc         [I] Bind context for this action, used to determine if the file must exist
500  *  pIDL        [O] SHITEMID will be constructed here.
501  *
502  * RETURNS
503  *  Success: A pointer to the terminating '\0' character of path.
504  *  Failure: NULL
505  *
506  * NOTES
507  *  Minimum size of pIDL is SHITEMID_LEN_FROM_NAME_LEN(strlen(last_component_of_path)).
508  *  If what you need is a PIDLLIST with a single SHITEMID, don't forget to append
509  *  a 0 USHORT value.
510  */
511 static char* UNIXFS_build_shitemid(char *pszUnixPath, BOOL bMustExist, WIN32_FIND_DATAW *pFindData, void *pIDL) {
512     LPPIDLDATA pIDLData;
513     struct stat fileStat;
514     WIN32_FIND_DATAW findData;
515     char *pszComponentU, *pszComponentA;
516     WCHAR *pwszComponentW;
517     int cComponentULen, cComponentALen;
518     USHORT cbLen;
519     FileStructW *pFileStructW;
520     WORD uOffsetW, *pOffsetW;
521
522     TRACE("(pszUnixPath=%s, bMustExist=%s, pFindData=%p, pIDL=%p)\n",
523             debugstr_a(pszUnixPath), bMustExist ? "T" : "F", pFindData, pIDL);
524
525     if (pFindData)
526         memcpy(&findData, pFindData, sizeof(WIN32_FIND_DATAW));
527     else {
528         memset(&findData, 0, sizeof(WIN32_FIND_DATAW));
529         findData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
530     }
531
532     /* We are only interested in regular files and directories. */
533     if (stat(pszUnixPath, &fileStat)){
534         if (bMustExist || errno != ENOENT)
535             return NULL;
536     } else {
537         LARGE_INTEGER time;
538
539         if (S_ISDIR(fileStat.st_mode))
540             findData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
541         else if (S_ISREG(fileStat.st_mode))
542             findData.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
543         else
544             return NULL;
545
546         findData.nFileSizeLow = (DWORD)fileStat.st_size;
547         findData.nFileSizeHigh = fileStat.st_size >> 32;
548
549         RtlSecondsSince1970ToTime(fileStat.st_mtime, &time);
550         findData.ftLastWriteTime.dwLowDateTime = time.u.LowPart;
551         findData.ftLastWriteTime.dwHighDateTime = time.u.HighPart;
552         RtlSecondsSince1970ToTime(fileStat.st_atime, &time);
553         findData.ftLastAccessTime.dwLowDateTime = time.u.LowPart;
554         findData.ftLastAccessTime.dwHighDateTime = time.u.HighPart;
555     }
556     
557     /* Compute the SHITEMID's length and wipe it. */
558     pszComponentU = strrchr(pszUnixPath, '/') + 1;
559     cComponentULen = strlen(pszComponentU);
560     cbLen = UNIXFS_shitemid_len_from_filename(pszComponentU, &pszComponentA, &pwszComponentW);
561     if (!cbLen) return NULL;
562     memset(pIDL, 0, cbLen);
563     ((LPSHITEMID)pIDL)->cb = cbLen;
564     
565     /* Set shell32's standard SHITEMID data fields. */
566     pIDLData = _ILGetDataPointer(pIDL);
567     pIDLData->type = (findData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) ? PT_FOLDER : PT_VALUE;
568     pIDLData->u.file.dwFileSize = findData.nFileSizeLow;
569     FileTimeToDosDateTime(&findData.ftLastWriteTime, &pIDLData->u.file.uFileDate,
570             &pIDLData->u.file.uFileTime);
571     pIDLData->u.file.uFileAttribs = 0;
572     pIDLData->u.file.uFileAttribs |= findData.dwFileAttributes;
573     if (pszComponentU[0] == '.') pIDLData->u.file.uFileAttribs |=  FILE_ATTRIBUTE_HIDDEN;
574     cComponentALen = lstrlenA(pszComponentA) + 1;
575     memcpy(pIDLData->u.file.szNames, pszComponentA, cComponentALen);
576     
577     pFileStructW = (FileStructW*)(pIDLData->u.file.szNames + cComponentALen + (cComponentALen & 0x1));
578     uOffsetW = (WORD)(((LPBYTE)pFileStructW) - ((LPBYTE)pIDL));
579     pFileStructW->cbLen = cbLen - uOffsetW;
580     FileTimeToDosDateTime(&findData.ftLastWriteTime, &pFileStructW->uCreationDate,
581         &pFileStructW->uCreationTime);
582     FileTimeToDosDateTime(&findData.ftLastAccessTime, &pFileStructW->uLastAccessDate,
583         &pFileStructW->uLastAccessTime);
584     lstrcpyW(pFileStructW->wszName, pwszComponentW);
585
586     pOffsetW = (WORD*)(((LPBYTE)pIDL) + cbLen - sizeof(WORD));
587     *pOffsetW = uOffsetW;
588     
589     SHFree(pszComponentA);
590     SHFree(pwszComponentW);
591     
592     return pszComponentU + cComponentULen;
593 }
594
595 /******************************************************************************
596  * UNIXFS_path_to_pidl [Internal]
597  *
598  * PARAMS
599  *  pUnixFolder [I] If path is relative, pUnixFolder represents the base path
600  *  path        [I] An absolute unix or dos path or a path relative to pUnixFolder
601  *  ppidl       [O] The corresponding ITEMIDLIST. Release with SHFree/ILFree
602  *  
603  * RETURNS
604  *  Success: S_OK
605  *  Failure: Error code, invalid params or out of memory
606  *
607  * NOTES
608  *  pUnixFolder also carries the information if the path is expected to be unix or dos.
609  */
610 static HRESULT UNIXFS_path_to_pidl(UnixFolder *pUnixFolder, LPBC pbc, const WCHAR *path,
611         LPITEMIDLIST *ppidl) {
612     LPITEMIDLIST pidl;
613     int cPidlLen, cPathLen;
614     char *pSlash, *pNextSlash, szCompletePath[FILENAME_MAX], *pNextPathElement, *pszAPath;
615     WCHAR *pwszPath;
616     WIN32_FIND_DATAW find_data;
617     BOOL must_exist = TRUE;
618
619     TRACE("pUnixFolder=%p, pbc=%p, path=%s, ppidl=%p\n", pUnixFolder, pbc, debugstr_w(path), ppidl);
620    
621     if (!ppidl || !path)
622         return E_INVALIDARG;
623
624     /* Build an absolute path and let pNextPathElement point to the interesting 
625      * relative sub-path. We need the absolute path to call 'stat', but the pidl
626      * will only contain the relative part.
627      */
628     if ((pUnixFolder->m_dwPathMode == PATHMODE_DOS) && (path[1] == ':')) 
629     {
630         /* Absolute dos path. Convert to unix */
631         if (!UNIXFS_get_unix_path(path, szCompletePath))
632             return E_FAIL;
633         pNextPathElement = szCompletePath;
634     } 
635     else if ((pUnixFolder->m_dwPathMode == PATHMODE_UNIX) && (path[0] == '/')) 
636     {
637         /* Absolute unix path. Just convert to ANSI. */
638         WideCharToMultiByte(CP_UNIXCP, 0, path, -1, szCompletePath, FILENAME_MAX, NULL, NULL); 
639         pNextPathElement = szCompletePath;
640     } 
641     else 
642     {
643         /* Relative dos or unix path. Concat with this folder's path */
644         int cBasePathLen = strlen(pUnixFolder->m_pszPath);
645         memcpy(szCompletePath, pUnixFolder->m_pszPath, cBasePathLen);
646         WideCharToMultiByte(CP_UNIXCP, 0, path, -1, szCompletePath + cBasePathLen, 
647                             FILENAME_MAX - cBasePathLen, NULL, NULL);
648         pNextPathElement = szCompletePath + cBasePathLen - 1;
649         
650         /* If in dos mode, replace '\' with '/' */
651         if (pUnixFolder->m_dwPathMode == PATHMODE_DOS) {
652             char *pBackslash = strchr(pNextPathElement, '\\');
653             while (pBackslash) {
654                 *pBackslash = '/';
655                 pBackslash = strchr(pBackslash, '\\');
656             }
657         }
658     }
659
660     /* Special case for the root folder. */
661     if (!strcmp(szCompletePath, "/")) {
662         *ppidl = pidl = SHAlloc(sizeof(USHORT));
663         if (!pidl) return E_FAIL;
664         pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
665         return S_OK;
666     }
667     
668     /* Remove trailing slash, if present */
669     cPathLen = strlen(szCompletePath);
670     if (szCompletePath[cPathLen-1] == '/') 
671         szCompletePath[cPathLen-1] = '\0';
672
673     if ((szCompletePath[0] != '/') || (pNextPathElement[0] != '/')) {
674         ERR("szCompletePath: %s, pNextPathElment: %s\n", szCompletePath, pNextPathElement);
675         return E_FAIL;
676     }
677     
678     /* At this point, we have an absolute unix path in szCompletePath 
679      * and the relative portion of it in pNextPathElement. Both starting with '/'
680      * and _not_ terminated by a '/'. */
681     TRACE("complete path: %s, relative path: %s\n", szCompletePath, pNextPathElement);
682     
683     /* Convert to CP_ACP and WCHAR */
684     if (!UNIXFS_shitemid_len_from_filename(pNextPathElement, &pszAPath, &pwszPath))
685         return E_FAIL;
686
687     /* Compute the length of the complete ITEMIDLIST */
688     cPidlLen = 0;
689     pSlash = pszAPath;
690     while (pSlash) {
691         pNextSlash = strchr(pSlash+1, '/');
692         cPidlLen += LEN_SHITEMID_FIXED_PART + /* Fixed part length plus potential alignment byte. */
693             (pNextSlash ? (pNextSlash - pSlash) & 0x1 : lstrlenA(pSlash) & 0x1); 
694         pSlash = pNextSlash;
695     }
696
697     /* The USHORT is for the ITEMIDLIST terminator. The NUL terminators for the sub-path-strings
698      * are accounted for by the '/' separators, which are not stored in the SHITEMIDs. Above we
699      * have ensured that the number of '/'s exactly matches the number of sub-path-strings. */
700     cPidlLen += lstrlenA(pszAPath) + lstrlenW(pwszPath) * sizeof(WCHAR) + sizeof(USHORT);
701
702     SHFree(pszAPath);
703     SHFree(pwszPath);
704
705     *ppidl = pidl = SHAlloc(cPidlLen);
706     if (!pidl) return E_FAIL;
707
708     if (pbc) {
709         IUnknown *unk;
710         IFileSystemBindData *fsb;
711         HRESULT hr;
712
713         hr = IBindCtx_GetObjectParam(pbc, (LPOLESTR)wFileSystemBindData, &unk);
714         if (SUCCEEDED(hr)) {
715             hr = IUnknown_QueryInterface(unk, &IID_IFileSystemBindData, (LPVOID*)&fsb);
716             if (SUCCEEDED(hr)) {
717                 hr = IFileSystemBindData_GetFindData(fsb, &find_data);
718                 if (FAILED(hr))
719                     memset(&find_data, 0, sizeof(WIN32_FIND_DATAW));
720
721                 must_exist = FALSE;
722                 IFileSystemBindData_Release(fsb);
723             }
724             IUnknown_Release(unk);
725         }
726     }
727
728     /* Concatenate the SHITEMIDs of the sub-directories. */
729     while (*pNextPathElement) {
730         pSlash = strchr(pNextPathElement+1, '/');
731         if (pSlash) *pSlash = '\0';
732         pNextPathElement = UNIXFS_build_shitemid(szCompletePath, must_exist,
733                 must_exist&&!pSlash ? &find_data : NULL, pidl);
734         if (pSlash) *pSlash = '/';
735             
736         if (!pNextPathElement) {
737             SHFree(*ppidl);
738             *ppidl = NULL;
739             return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
740         }
741         pidl = ILGetNext(pidl);
742     }
743     pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
744
745     if ((char *)pidl-(char *)*ppidl+sizeof(USHORT) != cPidlLen) /* We've corrupted the heap :( */ 
746         ERR("Computed length of pidl incorrect. Please report.\n");
747     
748     return S_OK;
749 }
750
751 /******************************************************************************
752  * UNIXFS_initialize_target_folder [Internal]
753  *
754  *  Initialize the m_pszPath member of an UnixFolder, given an absolute unix
755  *  base path and a relative ITEMIDLIST. Leave the m_pidlLocation member, which
756  *  specifies the location in the shell namespace alone. 
757  * 
758  * PARAMS
759  *  This          [IO] The UnixFolder, whose target path is to be initialized
760  *  szBasePath    [I]  The absolute base path
761  *  pidlSubFolder [I]  Relative part of the path, given as an ITEMIDLIST
762  *  dwAttributes  [I]  Attributes to add to the Folders m_dwAttributes member 
763  *                     (Used to pass the SFGAO_FILESYSTEM flag down the path)
764  * RETURNS
765  *  Success: S_OK,
766  *  Failure: E_FAIL
767  */
768 static HRESULT UNIXFS_initialize_target_folder(UnixFolder *This, const char *szBasePath,
769     LPCITEMIDLIST pidlSubFolder, DWORD dwAttributes)
770 {
771     LPCITEMIDLIST current = pidlSubFolder;
772     DWORD dwPathLen = strlen(szBasePath)+1;
773     char *pNextDir;
774     WCHAR *dos_name;
775
776     /* Determine the path's length bytes */
777     while (!_ILIsEmpty(current)) {
778         dwPathLen += UNIXFS_filename_from_shitemid(current, NULL) + 1; /* For the '/' */
779         current = ILGetNext(current);
780     };
781
782     /* Build the path and compute the attributes*/
783     This->m_dwAttributes = 
784             dwAttributes|SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|SFGAO_CANRENAME;
785     This->m_pszPath = pNextDir = SHAlloc(dwPathLen);
786     if (!This->m_pszPath) {
787         WARN("SHAlloc failed!\n");
788         return E_FAIL;
789     }
790     current = pidlSubFolder;
791     strcpy(pNextDir, szBasePath);
792     pNextDir += strlen(szBasePath);
793     if (This->m_dwPathMode == PATHMODE_UNIX || IsEqualCLSID(&CLSID_MyDocuments, This->m_pCLSID))
794         This->m_dwAttributes |= SFGAO_FILESYSTEM;
795     while (!_ILIsEmpty(current)) {
796         pNextDir += UNIXFS_filename_from_shitemid(current, pNextDir);
797         *pNextDir++ = '/';
798         current = ILGetNext(current);
799     }
800     *pNextDir='\0';
801
802     if (!(This->m_dwAttributes & SFGAO_FILESYSTEM) &&
803         ((dos_name = wine_get_dos_file_name(This->m_pszPath))))
804     {
805         This->m_dwAttributes |= SFGAO_FILESYSTEM;
806         HeapFree( GetProcessHeap(), 0, dos_name );
807     }
808
809     return S_OK;
810 }
811
812 /******************************************************************************
813  * UNIXFS_copy [Internal]
814  *
815  *  Copy pwszDosSrc to pwszDosDst.
816  *
817  * PARAMS
818  *  pwszDosSrc [I]  absolute path of the source
819  *  pwszDosDst [I]  absolute path of the destination
820  *
821  * RETURNS
822  *  Success: S_OK,
823  *  Failure: E_FAIL
824  */
825 static HRESULT UNIXFS_copy(LPCWSTR pwszDosSrc, LPCWSTR pwszDosDst)
826 {
827     SHFILEOPSTRUCTW op;
828     LPWSTR pwszSrc, pwszDst;
829     HRESULT res = E_OUTOFMEMORY;
830     UINT iSrcLen, iDstLen;
831
832     if (!pwszDosSrc || !pwszDosDst)
833         return E_FAIL;
834
835     iSrcLen = lstrlenW(pwszDosSrc);
836     iDstLen = lstrlenW(pwszDosDst);
837     pwszSrc = HeapAlloc(GetProcessHeap(), 0, (iSrcLen + 2) * sizeof(WCHAR));
838     pwszDst = HeapAlloc(GetProcessHeap(), 0, (iDstLen + 2) * sizeof(WCHAR));
839
840     if (pwszSrc && pwszDst) {
841         lstrcpyW(pwszSrc, pwszDosSrc);
842         lstrcpyW(pwszDst, pwszDosDst);
843         /* double null termination */
844         pwszSrc[iSrcLen + 1] = 0;
845         pwszDst[iDstLen + 1] = 0;
846
847         ZeroMemory(&op, sizeof(op));
848         op.hwnd = GetActiveWindow();
849         op.wFunc = FO_COPY;
850         op.pFrom = pwszSrc;
851         op.pTo = pwszDst;
852         op.fFlags = FOF_ALLOWUNDO;
853         if (!SHFileOperationW(&op))
854         {
855             WARN("SHFileOperationW failed\n");
856             res = E_FAIL;
857         }
858         else
859             res = S_OK;
860     }
861
862     HeapFree(GetProcessHeap(), 0, pwszSrc);
863     HeapFree(GetProcessHeap(), 0, pwszDst);
864     return res;
865 }
866
867 /******************************************************************************
868  * UnixFolder
869  *
870  * Class whose heap based instances represent unix filesystem directories.
871  */
872
873 static void UnixFolder_Destroy(UnixFolder *pUnixFolder) {
874     TRACE("(pUnixFolder=%p)\n", pUnixFolder);
875     
876     SHFree(pUnixFolder->m_pszPath);
877     ILFree(pUnixFolder->m_pidlLocation);
878     SHFree(pUnixFolder);
879 }
880
881 static HRESULT WINAPI ShellFolder2_QueryInterface(IShellFolder2 *iface, REFIID riid,
882     void **ppv) 
883 {
884     UnixFolder *This = impl_from_IShellFolder2(iface);
885         
886     TRACE("(%p)->(%s %p)\n", This, shdebugstr_guid(riid), ppv);
887     
888     if (!ppv) return E_INVALIDARG;
889     
890     if (IsEqualIID(&IID_IUnknown, riid)     ||
891         IsEqualIID(&IID_IShellFolder, riid) ||
892         IsEqualIID(&IID_IShellFolder2, riid)) 
893     {
894         *ppv = &This->IShellFolder2_iface;
895     } else if (IsEqualIID(&IID_IPersistFolder3, riid) ||
896                IsEqualIID(&IID_IPersistFolder2, riid) ||
897                IsEqualIID(&IID_IPersistFolder, riid)  ||
898                IsEqualIID(&IID_IPersist, riid))
899     {
900         *ppv = &This->IPersistFolder3_iface;
901     } else if (IsEqualIID(&IID_IPersistPropertyBag, riid)) {
902         *ppv = &This->IPersistPropertyBag_iface;
903     } else if (IsEqualIID(&IID_ISFHelper, riid)) {
904         *ppv = &This->ISFHelper_iface;
905     } else if (IsEqualIID(&IID_IDropTarget, riid)) {
906         *ppv = &This->IDropTarget_iface;
907         if (!cfShellIDList) 
908             cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLISTW);
909     } else {
910         *ppv = NULL;
911         TRACE("Unimplemented interface %s\n", shdebugstr_guid(riid));
912         return E_NOINTERFACE;
913     }
914
915     IUnknown_AddRef((IUnknown*)*ppv);
916     return S_OK;
917 }
918
919 static ULONG WINAPI ShellFolder2_AddRef(IShellFolder2 *iface)
920 {
921     UnixFolder *This = impl_from_IShellFolder2(iface);
922     ULONG ref = InterlockedIncrement(&This->ref);
923     TRACE("(%p)->(%u)\n", This, ref);
924     return ref;
925 }
926
927 static ULONG WINAPI ShellFolder2_Release(IShellFolder2 *iface)
928 {
929     UnixFolder *This = impl_from_IShellFolder2(iface);
930     ULONG ref = InterlockedDecrement(&This->ref);
931
932     TRACE("(%p)->(%u)\n", This, ref);
933
934     if (!ref)
935         UnixFolder_Destroy(This);
936
937     return ref;
938 }
939
940 static HRESULT WINAPI ShellFolder2_ParseDisplayName(IShellFolder2* iface, HWND hwndOwner,
941     LPBC pbc, LPOLESTR display_name, ULONG* pchEaten, LPITEMIDLIST* ppidl,
942     ULONG* attrs)
943 {
944     UnixFolder *This = impl_from_IShellFolder2(iface);
945     HRESULT result;
946
947     TRACE("(%p)->(%p %p %s %p %p %p)\n", This, hwndOwner, pbc, debugstr_w(display_name),
948           pchEaten, ppidl, attrs);
949
950     result = UNIXFS_path_to_pidl(This, pbc, display_name, ppidl);
951     if (SUCCEEDED(result) && attrs && *attrs)
952     {
953         IShellFolder *parent;
954         LPCITEMIDLIST pidlLast;
955         LPITEMIDLIST pidlComplete = ILCombine(This->m_pidlLocation, *ppidl);
956         HRESULT hr;
957         
958         hr = SHBindToParent(pidlComplete, &IID_IShellFolder, (void**)&parent, &pidlLast);
959         if (FAILED(hr)) {
960             FIXME("SHBindToParent failed! hr = 0x%08x\n", hr);
961             ILFree(pidlComplete);
962             return E_FAIL;
963         }
964         IShellFolder_GetAttributesOf(parent, 1, &pidlLast, attrs);
965         IShellFolder_Release(parent);
966         ILFree(pidlComplete);
967     }
968
969     if (FAILED(result)) TRACE("FAILED!\n");
970     return result;
971 }
972
973 static IUnknown *UnixSubFolderIterator_Constructor(UnixFolder *pUnixFolder, SHCONTF fFilter);
974
975 static HRESULT WINAPI ShellFolder2_EnumObjects(IShellFolder2* iface, HWND hwndOwner,
976     SHCONTF grfFlags, IEnumIDList** ppEnumIDList)
977 {
978     UnixFolder *This = impl_from_IShellFolder2(iface);
979     IUnknown *newIterator;
980     HRESULT hr;
981
982     TRACE("(%p)->(%p 0x%08x %p)\n", This, hwndOwner, grfFlags, ppEnumIDList);
983
984     if (!This->m_pszPath) {
985         WARN("EnumObjects called on uninitialized UnixFolder-object!\n");
986         return E_UNEXPECTED;
987     }
988
989     newIterator = UnixSubFolderIterator_Constructor(This, grfFlags);
990     hr = IUnknown_QueryInterface(newIterator, &IID_IEnumIDList, (void**)ppEnumIDList);
991     IUnknown_Release(newIterator);
992
993     return hr;
994 }
995
996 static HRESULT CreateUnixFolder(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv, const CLSID *pCLSID);
997
998 static HRESULT WINAPI ShellFolder2_BindToObject(IShellFolder2* iface, LPCITEMIDLIST pidl,
999     LPBC pbcReserved, REFIID riid, void** ppvOut)
1000 {
1001     UnixFolder *This = impl_from_IShellFolder2(iface);
1002     IPersistFolder3 *persistFolder;
1003     const CLSID *clsidChild;
1004     HRESULT hr;
1005         
1006     TRACE("(%p)->(%p %p %s %p)\n", This, pidl, pbcReserved, debugstr_guid(riid), ppvOut);
1007
1008     if (_ILIsEmpty(pidl))
1009         return E_INVALIDARG;
1010    
1011     /* Don't bind to files */
1012     if (_ILIsValue(ILFindLastID(pidl)))
1013         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
1014
1015     if (IsEqualCLSID(This->m_pCLSID, &CLSID_FolderShortcut)) {
1016         /* Children of FolderShortcuts are ShellFSFolders on Windows. 
1017          * Unixfs' counterpart is UnixDosFolder. */
1018         clsidChild = &CLSID_UnixDosFolder;    
1019     } else {
1020         clsidChild = This->m_pCLSID;
1021     }
1022
1023     hr = CreateUnixFolder(NULL, &IID_IPersistFolder3, (void**)&persistFolder, clsidChild);
1024     if (FAILED(hr)) return hr;
1025     hr = IPersistFolder3_QueryInterface(persistFolder, riid, ppvOut);
1026
1027     if (SUCCEEDED(hr)) {
1028         UnixFolder *subfolder = impl_from_IPersistFolder3(persistFolder);
1029         subfolder->m_pidlLocation = ILCombine(This->m_pidlLocation, pidl);
1030         hr = UNIXFS_initialize_target_folder(subfolder, This->m_pszPath, pidl,
1031                                              This->m_dwAttributes & SFGAO_FILESYSTEM);
1032     } 
1033
1034     IPersistFolder3_Release(persistFolder);
1035     
1036     return hr;
1037 }
1038
1039 static HRESULT WINAPI ShellFolder2_BindToStorage(IShellFolder2* iface, LPCITEMIDLIST pidl,
1040     LPBC pbcReserved, REFIID riid, void** ppvObj)
1041 {
1042     UnixFolder *This = impl_from_IShellFolder2(iface);
1043     FIXME("(%p)->(%p %p %s %p): stub\n", This, pidl, pbcReserved, debugstr_guid(riid), ppvObj);
1044     return E_NOTIMPL;
1045 }
1046
1047 static HRESULT WINAPI ShellFolder2_CompareIDs(IShellFolder2* iface, LPARAM lParam,
1048     LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
1049 {
1050     UnixFolder *This = impl_from_IShellFolder2(iface);
1051     BOOL isEmpty1, isEmpty2;
1052     HRESULT hr = E_FAIL;
1053     LPCITEMIDLIST firstpidl;
1054     IShellFolder2 *psf;
1055     int compare;
1056
1057     TRACE("(%p)->(%ld %p %p)\n", This, lParam, pidl1, pidl2);
1058     
1059     isEmpty1 = _ILIsEmpty(pidl1);
1060     isEmpty2 = _ILIsEmpty(pidl2);
1061
1062     if (isEmpty1 && isEmpty2) 
1063         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
1064     else if (isEmpty1)
1065         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
1066     else if (isEmpty2)
1067         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
1068
1069     compare = CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE, 
1070                              _ILGetTextPointer(pidl1), -1,
1071                              _ILGetTextPointer(pidl2), -1);
1072
1073     if ((compare != CSTR_EQUAL) && _ILIsFolder(pidl1) && !_ILIsFolder(pidl2))
1074         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
1075     if ((compare != CSTR_EQUAL) && !_ILIsFolder(pidl1) && _ILIsFolder(pidl2))
1076         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
1077
1078     if ((compare == CSTR_LESS_THAN) || (compare == CSTR_GREATER_THAN)) 
1079         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)((compare == CSTR_LESS_THAN)?-1:1));
1080
1081     if (pidl1->mkid.cb < pidl2->mkid.cb)
1082         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
1083     else if (pidl1->mkid.cb > pidl2->mkid.cb)
1084         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
1085
1086     firstpidl = pidl1;
1087     pidl1 = ILGetNext(pidl1);
1088     pidl2 = ILGetNext(pidl2);
1089
1090     isEmpty1 = _ILIsEmpty(pidl1);
1091     isEmpty2 = _ILIsEmpty(pidl2);
1092
1093     if (isEmpty1 && isEmpty2)
1094         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
1095     else if (isEmpty1)
1096         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
1097     else if (isEmpty2)
1098         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
1099     else if (SUCCEEDED(IShellFolder2_BindToObject(iface, firstpidl, NULL, &IID_IShellFolder, (void**)&psf))) {
1100         hr = IShellFolder2_CompareIDs(psf, lParam, pidl1, pidl2);
1101         IShellFolder2_Release(psf);
1102     }
1103
1104     return hr;
1105 }
1106
1107 static HRESULT WINAPI ShellFolder2_CreateViewObject(IShellFolder2* iface, HWND hwndOwner,
1108     REFIID riid, void** ppv)
1109 {
1110     UnixFolder *This = impl_from_IShellFolder2(iface);
1111     HRESULT hr = E_INVALIDARG;
1112
1113     TRACE("(%p)->(%p %s %p)\n", This, hwndOwner, debugstr_guid(riid), ppv);
1114
1115     if (!ppv) return E_INVALIDARG;
1116     *ppv = NULL;
1117
1118     if (IsEqualIID(&IID_IShellView, riid)) {
1119         IShellView *view;
1120         
1121         view = IShellView_Constructor((IShellFolder*)iface);
1122         if (view) {
1123             hr = IShellView_QueryInterface(view, riid, ppv);
1124             IShellView_Release(view);
1125         }
1126     } else if (IsEqualIID(&IID_IDropTarget, riid)) {
1127         hr = IShellFolder2_QueryInterface(iface, &IID_IDropTarget, ppv);
1128     }
1129
1130     return hr;
1131 }
1132
1133 static HRESULT WINAPI ShellFolder2_GetAttributesOf(IShellFolder2* iface, UINT cidl,
1134     LPCITEMIDLIST* apidl, SFGAOF* rgfInOut)
1135 {
1136     UnixFolder *This = impl_from_IShellFolder2(iface);
1137     HRESULT hr = S_OK;
1138         
1139     TRACE("(%p)->(%u %p %p)\n", This, cidl, apidl, rgfInOut);
1140  
1141     if (!rgfInOut || (cidl && !apidl)) 
1142         return E_INVALIDARG;
1143     
1144     if (cidl == 0) {
1145         *rgfInOut &= This->m_dwAttributes;
1146     } else {
1147         char szAbsolutePath[FILENAME_MAX], *pszRelativePath;
1148         UINT i;
1149
1150         *rgfInOut = SFGAO_CANCOPY|SFGAO_CANMOVE|SFGAO_CANLINK|SFGAO_CANRENAME|SFGAO_CANDELETE|
1151                     SFGAO_HASPROPSHEET|SFGAO_DROPTARGET|SFGAO_FILESYSTEM;
1152         lstrcpyA(szAbsolutePath, This->m_pszPath);
1153         pszRelativePath = szAbsolutePath + lstrlenA(szAbsolutePath);
1154         for (i=0; i<cidl; i++) {
1155             if (!(This->m_dwAttributes & SFGAO_FILESYSTEM)) {
1156                 WCHAR *dos_name;
1157                 if (!UNIXFS_filename_from_shitemid(apidl[i], pszRelativePath)) 
1158                     return E_INVALIDARG;
1159                 if (!(dos_name = wine_get_dos_file_name( szAbsolutePath )))
1160                     *rgfInOut &= ~SFGAO_FILESYSTEM;
1161                 else
1162                     HeapFree( GetProcessHeap(), 0, dos_name );
1163             }
1164             if (_ILIsFolder(apidl[i])) 
1165                 *rgfInOut |= SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR;
1166         }
1167     }
1168
1169     return hr;
1170 }
1171
1172 static HRESULT WINAPI ShellFolder2_GetUIObjectOf(IShellFolder2* iface, HWND hwndOwner,
1173     UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void** ppvOut)
1174 {
1175     UnixFolder *This = impl_from_IShellFolder2(iface);
1176     HRESULT hr;
1177     UINT i;
1178     
1179     TRACE("(%p)->(%p %d %p riid=%s %p %p)\n",
1180         This, hwndOwner, cidl, apidl, debugstr_guid(riid), prgfInOut, ppvOut);
1181
1182     if (!cidl || !apidl || !riid || !ppvOut) 
1183         return E_INVALIDARG;
1184
1185     for (i=0; i<cidl; i++) 
1186         if (!apidl[i]) 
1187             return E_INVALIDARG;
1188
1189     if(cidl == 1) {
1190         hr = SHELL32_CreateExtensionUIObject(iface, *apidl, riid, ppvOut);
1191         if(hr != S_FALSE)
1192             return hr;
1193     }
1194
1195     if (IsEqualIID(&IID_IContextMenu, riid)) {
1196         return ItemMenu_Constructor((IShellFolder*)iface, This->m_pidlLocation, apidl, cidl, riid, ppvOut);
1197     } else if (IsEqualIID(&IID_IDataObject, riid)) {
1198         *ppvOut = IDataObject_Constructor(hwndOwner, This->m_pidlLocation, apidl, cidl);
1199         return S_OK;
1200     } else if (IsEqualIID(&IID_IExtractIconA, riid)) {
1201         LPITEMIDLIST pidl;
1202         if (cidl != 1) return E_INVALIDARG;
1203         pidl = ILCombine(This->m_pidlLocation, apidl[0]);
1204         *ppvOut = IExtractIconA_Constructor(pidl);
1205         SHFree(pidl);
1206         return S_OK;
1207     } else if (IsEqualIID(&IID_IExtractIconW, riid)) {
1208         LPITEMIDLIST pidl;
1209         if (cidl != 1) return E_INVALIDARG;
1210         pidl = ILCombine(This->m_pidlLocation, apidl[0]);
1211         *ppvOut = IExtractIconW_Constructor(pidl);
1212         SHFree(pidl);
1213         return S_OK;
1214     } else if (IsEqualIID(&IID_IDropTarget, riid)) {
1215         if (cidl != 1) return E_INVALIDARG;
1216         return IShellFolder2_BindToObject(iface, apidl[0], NULL, &IID_IDropTarget, ppvOut);
1217     } else if (IsEqualIID(&IID_IShellLinkW, riid)) {
1218         FIXME("IShellLinkW\n");
1219         return E_FAIL;
1220     } else if (IsEqualIID(&IID_IShellLinkA, riid)) {
1221         FIXME("IShellLinkA\n");
1222         return E_FAIL;
1223     } else {
1224         FIXME("Unknown interface %s in GetUIObjectOf\n", debugstr_guid(riid));
1225         return E_NOINTERFACE;
1226     }
1227 }
1228
1229 static HRESULT WINAPI ShellFolder2_GetDisplayNameOf(IShellFolder2* iface,
1230     LPCITEMIDLIST pidl, SHGDNF uFlags, STRRET* lpName)
1231 {
1232     UnixFolder *This = impl_from_IShellFolder2(iface);
1233     SHITEMID emptyIDL = { 0, { 0 } };
1234     HRESULT hr = S_OK;    
1235
1236     TRACE("(%p)->(%p 0x%x %p)\n", This, pidl, uFlags, lpName);
1237     
1238     if ((GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING) &&
1239         (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER))
1240     {
1241         if (_ILIsEmpty(pidl)) {
1242             lpName->uType = STRRET_WSTR;
1243             if (This->m_dwPathMode == PATHMODE_UNIX) {
1244                 UINT len = MultiByteToWideChar(CP_UNIXCP, 0, This->m_pszPath, -1, NULL, 0);
1245                 lpName->u.pOleStr = SHAlloc(len * sizeof(WCHAR));
1246                 if (!lpName->u.pOleStr) return HRESULT_FROM_WIN32(GetLastError());
1247                 MultiByteToWideChar(CP_UNIXCP, 0, This->m_pszPath, -1, lpName->u.pOleStr, len);
1248             } else {
1249                 LPWSTR pwszDosFileName = wine_get_dos_file_name(This->m_pszPath);
1250                 if (!pwszDosFileName) return HRESULT_FROM_WIN32(GetLastError());
1251                 lpName->u.pOleStr = SHAlloc((lstrlenW(pwszDosFileName) + 1) * sizeof(WCHAR));
1252                 if (!lpName->u.pOleStr) return HRESULT_FROM_WIN32(GetLastError());
1253                 lstrcpyW(lpName->u.pOleStr, pwszDosFileName);
1254                 PathRemoveBackslashW(lpName->u.pOleStr);
1255                 HeapFree(GetProcessHeap(), 0, pwszDosFileName);
1256             }
1257         } else if (_ILIsValue(pidl)) {
1258             STRRET str;
1259             PWSTR path, file;
1260
1261             /* We are looking for the complete path to a file */
1262
1263             /* Get the complete path for the current folder object */
1264             hr = IShellFolder2_GetDisplayNameOf(iface, (LPITEMIDLIST)&emptyIDL, uFlags, &str);
1265             if (SUCCEEDED(hr)) {
1266                 hr = StrRetToStrW(&str, NULL, &path);
1267                 if (SUCCEEDED(hr)) {
1268
1269                     /* Get the child filename */
1270                     hr = IShellFolder2_GetDisplayNameOf(iface, pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str);
1271                     if (SUCCEEDED(hr)) {
1272                         hr = StrRetToStrW(&str, NULL, &file);
1273                         if (SUCCEEDED(hr)) {
1274                             static const WCHAR slashW = '/';
1275                             UINT len_path = strlenW(path), len_file = strlenW(file);
1276
1277                             /* Now, combine them */
1278                             lpName->uType = STRRET_WSTR;
1279                             lpName->u.pOleStr = SHAlloc( (len_path + len_file + 2)*sizeof(WCHAR) );
1280                             lstrcpyW(lpName->u.pOleStr, path);
1281                             if (This->m_dwPathMode == PATHMODE_UNIX &&
1282                                lpName->u.pOleStr[len_path-1] != slashW) {
1283                                 lpName->u.pOleStr[len_path] = slashW;
1284                                 lpName->u.pOleStr[len_path+1] = '\0';
1285                             } else
1286                                 PathAddBackslashW(lpName->u.pOleStr);
1287                             lstrcatW(lpName->u.pOleStr, file);
1288
1289                             CoTaskMemFree(file);
1290                         } else
1291                             WARN("Failed to convert strret (file)\n");
1292                     }
1293                     CoTaskMemFree(path);
1294                 } else
1295                     WARN("Failed to convert strret (path)\n");
1296             }
1297         } else {
1298             IShellFolder *pSubFolder;
1299
1300             hr = IShellFolder2_BindToObject(iface, pidl, NULL, &IID_IShellFolder, (void**)&pSubFolder);
1301             if (SUCCEEDED(hr)) {
1302                 hr = IShellFolder_GetDisplayNameOf(pSubFolder, (LPITEMIDLIST)&emptyIDL, uFlags, lpName);
1303                 IShellFolder_Release(pSubFolder);
1304             } else if (FAILED(hr) && !_ILIsPidlSimple(pidl)) {
1305                 LPITEMIDLIST pidl_parent = ILClone(pidl);
1306                 LPITEMIDLIST pidl_child = ILFindLastID(pidl);
1307
1308                 /* Might be a file, try binding to its parent */
1309                 ILRemoveLastID(pidl_parent);
1310                 hr = IShellFolder2_BindToObject(iface, pidl_parent, NULL, &IID_IShellFolder, (void**)&pSubFolder);
1311                 if (SUCCEEDED(hr)) {
1312                     hr = IShellFolder_GetDisplayNameOf(pSubFolder, pidl_child, uFlags, lpName);
1313                     IShellFolder_Release(pSubFolder);
1314                 }
1315                 ILFree(pidl_parent);
1316             }
1317         }
1318     } else {
1319         WCHAR wszFileName[MAX_PATH];
1320         if (!_ILSimpleGetTextW(pidl, wszFileName, MAX_PATH)) return E_INVALIDARG;
1321         lpName->uType = STRRET_WSTR;
1322         lpName->u.pOleStr = SHAlloc((lstrlenW(wszFileName)+1)*sizeof(WCHAR));
1323         if (!lpName->u.pOleStr) return HRESULT_FROM_WIN32(GetLastError());
1324         lstrcpyW(lpName->u.pOleStr, wszFileName);
1325         if (!(GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING) && This->m_dwPathMode == PATHMODE_DOS && 
1326             !_ILIsFolder(pidl) && wszFileName[0] != '.' && SHELL_FS_HideExtension(wszFileName))
1327         {
1328             PathRemoveExtensionW(lpName->u.pOleStr);
1329         }
1330     }
1331
1332     TRACE("--> %s\n", debugstr_w(lpName->u.pOleStr));
1333     
1334     return hr;
1335 }
1336
1337 static HRESULT WINAPI ShellFolder2_SetNameOf(IShellFolder2* iface, HWND hwnd,
1338     LPCITEMIDLIST pidl, LPCOLESTR lpcwszName, SHGDNF uFlags, LPITEMIDLIST* ppidlOut)
1339 {
1340     UnixFolder *This = impl_from_IShellFolder2(iface);
1341
1342     static const WCHAR awcInvalidChars[] = { '\\', '/', ':', '*', '?', '"', '<', '>', '|' };
1343     char szSrc[FILENAME_MAX], szDest[FILENAME_MAX];
1344     WCHAR wszSrcRelative[MAX_PATH], *pwszExt = NULL;
1345     unsigned int i;
1346     int cBasePathLen = lstrlenA(This->m_pszPath), cNameLen;
1347     struct stat statDest;
1348     LPITEMIDLIST pidlSrc, pidlDest, pidlRelativeDest;
1349     LPOLESTR lpwszName;
1350     HRESULT hr;
1351    
1352     TRACE("(%p)->(%p %p %s 0x%08x %p)\n", This, hwnd, pidl, debugstr_w(lpcwszName), uFlags, ppidlOut);
1353
1354     /* prepare to fail */
1355     if (ppidlOut)
1356         *ppidlOut = NULL;
1357     
1358     /* pidl has to contain a single non-empty SHITEMID */
1359     if (_ILIsDesktop(pidl) || !_ILIsPidlSimple(pidl) || !_ILGetTextPointer(pidl))
1360         return E_INVALIDARG;
1361  
1362     /* check for invalid characters in lpcwszName. */
1363     for (i=0; i < sizeof(awcInvalidChars)/sizeof(*awcInvalidChars); i++)
1364         if (StrChrW(lpcwszName, awcInvalidChars[i]))
1365             return HRESULT_FROM_WIN32(ERROR_CANCELLED);
1366
1367     /* build source path */
1368     memcpy(szSrc, This->m_pszPath, cBasePathLen);
1369     UNIXFS_filename_from_shitemid(pidl, szSrc + cBasePathLen);
1370
1371     /* build destination path */
1372     memcpy(szDest, This->m_pszPath, cBasePathLen);
1373     WideCharToMultiByte(CP_UNIXCP, 0, lpcwszName, -1, szDest+cBasePathLen, 
1374                         FILENAME_MAX-cBasePathLen, NULL, NULL);
1375
1376     /* If the filename's extension is hidden to the user, we have to append it. */
1377     if (!(uFlags & SHGDN_FORPARSING) && 
1378         _ILSimpleGetTextW(pidl, wszSrcRelative, MAX_PATH) && 
1379         SHELL_FS_HideExtension(wszSrcRelative))
1380     {
1381         int cLenDest = strlen(szDest);
1382         pwszExt = PathFindExtensionW(wszSrcRelative);
1383         WideCharToMultiByte(CP_UNIXCP, 0, pwszExt, -1, szDest + cLenDest, 
1384             FILENAME_MAX - cLenDest, NULL, NULL);
1385     }
1386
1387     TRACE("src=%s dest=%s\n", szSrc, szDest);
1388
1389     /* Fail, if destination does already exist */
1390     if (!stat(szDest, &statDest)) 
1391         return E_FAIL;
1392
1393     /* Rename the file */
1394     if (rename(szSrc, szDest)) 
1395         return E_FAIL;
1396     
1397     /* Build a pidl for the path of the renamed file */
1398     cNameLen = lstrlenW(lpcwszName) + 1;
1399     if(pwszExt)
1400         cNameLen += lstrlenW(pwszExt);
1401     lpwszName = SHAlloc(cNameLen*sizeof(WCHAR)); /* due to const correctness. */
1402     lstrcpyW(lpwszName, lpcwszName);
1403     if(pwszExt)
1404         lstrcatW(lpwszName, pwszExt);
1405
1406     hr = IShellFolder2_ParseDisplayName(iface, NULL, NULL, lpwszName, NULL, &pidlRelativeDest, NULL);
1407     SHFree(lpwszName);
1408     if (FAILED(hr)) {
1409         rename(szDest, szSrc); /* Undo the renaming */
1410         return E_FAIL;
1411     }
1412     pidlDest = ILCombine(This->m_pidlLocation, pidlRelativeDest);
1413     ILFree(pidlRelativeDest);
1414     pidlSrc = ILCombine(This->m_pidlLocation, pidl);
1415     
1416     /* Inform the shell */
1417     if (_ILIsFolder(ILFindLastID(pidlDest))) 
1418         SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_IDLIST, pidlSrc, pidlDest);
1419     else 
1420         SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_IDLIST, pidlSrc, pidlDest);
1421     
1422     if (ppidlOut) 
1423         *ppidlOut = ILClone(ILFindLastID(pidlDest));
1424         
1425     ILFree(pidlSrc);
1426     ILFree(pidlDest);
1427     
1428     return S_OK;
1429 }
1430
1431 static HRESULT WINAPI ShellFolder2_EnumSearches(IShellFolder2* iface, IEnumExtraSearch **ppEnum)
1432 {
1433     UnixFolder *This = impl_from_IShellFolder2(iface);
1434     FIXME("(%p)->(%p): stub\n", This, ppEnum);
1435     return E_NOTIMPL;
1436 }
1437
1438 static HRESULT WINAPI ShellFolder2_GetDefaultColumn(IShellFolder2* iface,
1439     DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
1440 {
1441     UnixFolder *This = impl_from_IShellFolder2(iface);
1442
1443     TRACE("(%p)->(0x%x %p %p)\n", This, dwReserved, pSort, pDisplay);
1444
1445     if (pSort)
1446         *pSort = 0;
1447     if (pDisplay)
1448         *pDisplay = 0;
1449
1450     return S_OK;
1451 }
1452
1453 static HRESULT WINAPI ShellFolder2_GetDefaultColumnState(IShellFolder2* iface,
1454     UINT column, SHCOLSTATEF *flags)
1455 {
1456     UnixFolder *This = impl_from_IShellFolder2(iface);
1457     FIXME("(%p)->(%u %p): stub\n", This, column, flags);
1458     return E_NOTIMPL;
1459 }
1460
1461 static HRESULT WINAPI ShellFolder2_GetDefaultSearchGUID(IShellFolder2* iface,
1462     GUID *guid)
1463 {
1464     UnixFolder *This = impl_from_IShellFolder2(iface);
1465     FIXME("(%p)->(%p): stub\n", This, guid);
1466     return E_NOTIMPL;
1467 }
1468
1469 static HRESULT WINAPI ShellFolder2_GetDetailsEx(IShellFolder2* iface,
1470     LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
1471 {
1472     UnixFolder *This = impl_from_IShellFolder2(iface);
1473     FIXME("(%p)->(%p %p %p): stub\n", This, pidl, pscid, pv);
1474     return E_NOTIMPL;
1475 }
1476
1477 #define SHELLVIEWCOLUMNS 7 
1478
1479 static HRESULT WINAPI ShellFolder2_GetDetailsOf(IShellFolder2* iface,
1480     LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *psd)
1481 {
1482     UnixFolder *This = impl_from_IShellFolder2(iface);
1483     HRESULT hr = E_FAIL;
1484     struct passwd *pPasswd;
1485     struct group *pGroup;
1486     struct stat statItem;
1487
1488     static const shvheader unixfs_header[SHELLVIEWCOLUMNS] = {
1489         {IDS_SHV_COLUMN1,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
1490         {IDS_SHV_COLUMN2,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
1491         {IDS_SHV_COLUMN3,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
1492         {IDS_SHV_COLUMN4,  SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 12},
1493         {IDS_SHV_COLUMN5,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 9},
1494         {IDS_SHV_COLUMN10, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7},
1495         {IDS_SHV_COLUMN11, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7}
1496     };
1497
1498     TRACE("(%p)->(%p %d %p)\n", This, pidl, iColumn, psd);
1499     
1500     if (!psd || iColumn >= SHELLVIEWCOLUMNS)
1501         return E_INVALIDARG;
1502
1503     if (!pidl)
1504         return SHELL32_GetColumnDetails(unixfs_header, iColumn, psd);
1505
1506     if (iColumn == 4 || iColumn == 5 || iColumn == 6) {
1507         char szPath[FILENAME_MAX];
1508         strcpy(szPath, This->m_pszPath);
1509         if (!UNIXFS_filename_from_shitemid(pidl, szPath + strlen(szPath)))
1510             return E_INVALIDARG;
1511         if (stat(szPath, &statItem))
1512             return E_INVALIDARG;
1513     }
1514
1515     psd->str.u.cStr[0] = '\0';
1516     psd->str.uType = STRRET_CSTR;
1517
1518     switch (iColumn) {
1519         case 0:
1520             hr = IShellFolder2_GetDisplayNameOf(iface, pidl, SHGDN_NORMAL|SHGDN_INFOLDER, &psd->str);
1521             break;
1522         case 1:
1523             _ILGetFileSize(pidl, psd->str.u.cStr, MAX_PATH);
1524             break;
1525         case 2:
1526             _ILGetFileType (pidl, psd->str.u.cStr, MAX_PATH);
1527             break;
1528         case 3:
1529             _ILGetFileDate(pidl, psd->str.u.cStr, MAX_PATH);
1530             break;
1531         case 4:
1532             psd->str.u.cStr[0] = S_ISDIR(statItem.st_mode) ? 'd' : '-';
1533             psd->str.u.cStr[1] = (statItem.st_mode & S_IRUSR) ? 'r' : '-';
1534             psd->str.u.cStr[2] = (statItem.st_mode & S_IWUSR) ? 'w' : '-';
1535             psd->str.u.cStr[3] = (statItem.st_mode & S_IXUSR) ? 'x' : '-';
1536             psd->str.u.cStr[4] = (statItem.st_mode & S_IRGRP) ? 'r' : '-';
1537             psd->str.u.cStr[5] = (statItem.st_mode & S_IWGRP) ? 'w' : '-';
1538             psd->str.u.cStr[6] = (statItem.st_mode & S_IXGRP) ? 'x' : '-';
1539             psd->str.u.cStr[7] = (statItem.st_mode & S_IROTH) ? 'r' : '-';
1540             psd->str.u.cStr[8] = (statItem.st_mode & S_IWOTH) ? 'w' : '-';
1541             psd->str.u.cStr[9] = (statItem.st_mode & S_IXOTH) ? 'x' : '-';
1542             psd->str.u.cStr[10] = '\0';
1543             break;
1544         case 5:
1545             pPasswd = getpwuid(statItem.st_uid);
1546             if (pPasswd) strcpy(psd->str.u.cStr, pPasswd->pw_name);
1547             break;
1548         case 6:
1549             pGroup = getgrgid(statItem.st_gid);
1550             if (pGroup) strcpy(psd->str.u.cStr, pGroup->gr_name);
1551             break;
1552     }
1553     
1554     return hr;
1555 }
1556
1557 static HRESULT WINAPI ShellFolder2_MapColumnToSCID(IShellFolder2* iface, UINT column,
1558     SHCOLUMNID *pscid)
1559 {
1560     UnixFolder *This = impl_from_IShellFolder2(iface);
1561     FIXME("(%p)->(%u %p): stub\n", This, column, pscid);
1562     return E_NOTIMPL;
1563 }
1564
1565 static const IShellFolder2Vtbl ShellFolder2Vtbl = {
1566     ShellFolder2_QueryInterface,
1567     ShellFolder2_AddRef,
1568     ShellFolder2_Release,
1569     ShellFolder2_ParseDisplayName,
1570     ShellFolder2_EnumObjects,
1571     ShellFolder2_BindToObject,
1572     ShellFolder2_BindToStorage,
1573     ShellFolder2_CompareIDs,
1574     ShellFolder2_CreateViewObject,
1575     ShellFolder2_GetAttributesOf,
1576     ShellFolder2_GetUIObjectOf,
1577     ShellFolder2_GetDisplayNameOf,
1578     ShellFolder2_SetNameOf,
1579     ShellFolder2_GetDefaultSearchGUID,
1580     ShellFolder2_EnumSearches,
1581     ShellFolder2_GetDefaultColumn,
1582     ShellFolder2_GetDefaultColumnState,
1583     ShellFolder2_GetDetailsEx,
1584     ShellFolder2_GetDetailsOf,
1585     ShellFolder2_MapColumnToSCID
1586 };
1587
1588 static HRESULT WINAPI PersistFolder3_QueryInterface(IPersistFolder3* iface, REFIID riid,
1589     void** ppvObject)
1590 {
1591     UnixFolder *This = impl_from_IPersistFolder3(iface);
1592     return IShellFolder2_QueryInterface(&This->IShellFolder2_iface, riid, ppvObject);
1593 }
1594
1595 static ULONG WINAPI PersistFolder3_AddRef(IPersistFolder3* iface)
1596 {
1597     UnixFolder *This = impl_from_IPersistFolder3(iface);
1598     return IShellFolder2_AddRef(&This->IShellFolder2_iface);
1599 }
1600
1601 static ULONG WINAPI PersistFolder3_Release(IPersistFolder3* iface)
1602 {
1603     UnixFolder *This = impl_from_IPersistFolder3(iface);
1604     return IShellFolder2_Release(&This->IShellFolder2_iface);
1605 }
1606
1607 static HRESULT WINAPI PersistFolder3_GetClassID(IPersistFolder3* iface, CLSID* pClassID)
1608 {
1609     UnixFolder *This = impl_from_IPersistFolder3(iface);
1610     
1611     TRACE("(%p)->(%p)\n", This, pClassID);
1612     
1613     if (!pClassID)
1614         return E_INVALIDARG;
1615
1616     *pClassID = *This->m_pCLSID;
1617     return S_OK;
1618 }
1619
1620 static HRESULT WINAPI PersistFolder3_Initialize(IPersistFolder3* iface, LPCITEMIDLIST pidl)
1621 {
1622     UnixFolder *This = impl_from_IPersistFolder3(iface);
1623     LPCITEMIDLIST current = pidl;
1624     char szBasePath[FILENAME_MAX] = "/";
1625     
1626     TRACE("(%p)->(%p)\n", This, pidl);
1627
1628     /* Find the UnixFolderClass root */
1629     while (current->mkid.cb) {
1630         if ((_ILIsDrive(current) && IsEqualCLSID(This->m_pCLSID, &CLSID_ShellFSFolder)) ||
1631             (_ILIsSpecialFolder(current) && IsEqualCLSID(This->m_pCLSID, _ILGetGUIDPointer(current))))
1632         {
1633             break;
1634         }
1635         current = ILGetNext(current);
1636     }
1637
1638     if (current->mkid.cb) {
1639         if (_ILIsDrive(current)) {
1640             WCHAR wszDrive[] = { '?', ':', '\\', 0 };
1641             wszDrive[0] = (WCHAR)*_ILGetTextPointer(current);
1642             if (!UNIXFS_get_unix_path(wszDrive, szBasePath))
1643                 return E_FAIL;
1644         } else if (IsEqualIID(&CLSID_MyDocuments, _ILGetGUIDPointer(current))) {
1645             WCHAR wszMyDocumentsPath[MAX_PATH];
1646             if (!SHGetSpecialFolderPathW(0, wszMyDocumentsPath, CSIDL_PERSONAL, FALSE))
1647                 return E_FAIL;
1648             PathAddBackslashW(wszMyDocumentsPath);
1649             if (!UNIXFS_get_unix_path(wszMyDocumentsPath, szBasePath))
1650                 return E_FAIL;
1651         } 
1652         current = ILGetNext(current);
1653     } else if (_ILIsDesktop(pidl) || _ILIsValue(pidl) || _ILIsFolder(pidl)) {
1654         /* Path rooted at Desktop */
1655         WCHAR wszDesktopPath[MAX_PATH];
1656         if (!SHGetSpecialFolderPathW(0, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE)) 
1657             return E_FAIL;
1658         PathAddBackslashW(wszDesktopPath);
1659         if (!UNIXFS_get_unix_path(wszDesktopPath, szBasePath))
1660             return E_FAIL;
1661         current = pidl;
1662     } else if (IsEqualCLSID(This->m_pCLSID, &CLSID_FolderShortcut)) {
1663         /* FolderShortcuts' Initialize method only sets the ITEMIDLIST, which
1664          * specifies the location in the shell namespace, but leaves the
1665          * target folder (m_pszPath) alone. See unit tests in tests/shlfolder.c */
1666         This->m_pidlLocation = ILClone(pidl);
1667         return S_OK;
1668     } else {
1669         ERR("Unknown pidl type!\n");
1670         pdump(pidl);
1671         return E_INVALIDARG;
1672     }
1673   
1674     This->m_pidlLocation = ILClone(pidl);
1675     return UNIXFS_initialize_target_folder(This, szBasePath, current, 0); 
1676 }
1677
1678 static HRESULT WINAPI PersistFolder3_GetCurFolder(IPersistFolder3* iface, LPITEMIDLIST* ppidl)
1679 {
1680     UnixFolder *This = impl_from_IPersistFolder3(iface);
1681     
1682     TRACE ("(iface=%p, ppidl=%p)\n", iface, ppidl);
1683
1684     if (!ppidl)
1685         return E_POINTER;
1686     *ppidl = ILClone (This->m_pidlLocation);
1687     return S_OK;
1688 }
1689
1690 static HRESULT WINAPI PersistFolder3_InitializeEx(IPersistFolder3 *iface, IBindCtx *pbc,
1691     LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *ppfti)
1692 {
1693     UnixFolder *This = impl_from_IPersistFolder3(iface);
1694     WCHAR wszTargetDosPath[MAX_PATH];
1695     char szTargetPath[FILENAME_MAX] = "";
1696     
1697     TRACE("(%p)->(%p %p %p)\n", This, pbc, pidlRoot, ppfti);
1698
1699     /* If no PERSIST_FOLDER_TARGET_INFO is given InitializeEx is equivalent to Initialize. */
1700     if (!ppfti) 
1701         return IPersistFolder3_Initialize(iface, pidlRoot);
1702
1703     if (ppfti->csidl != -1) {
1704         if (FAILED(SHGetFolderPathW(0, ppfti->csidl, NULL, 0, wszTargetDosPath)) ||
1705             !UNIXFS_get_unix_path(wszTargetDosPath, szTargetPath))
1706         {
1707             return E_FAIL;
1708         }
1709     } else if (*ppfti->szTargetParsingName) {
1710         lstrcpyW(wszTargetDosPath, ppfti->szTargetParsingName);
1711         PathAddBackslashW(wszTargetDosPath);
1712         if (!UNIXFS_get_unix_path(wszTargetDosPath, szTargetPath)) {
1713             return E_FAIL;
1714         }
1715     } else if (ppfti->pidlTargetFolder) {
1716         if (!SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTargetDosPath) ||
1717             !UNIXFS_get_unix_path(wszTargetDosPath, szTargetPath))
1718         {
1719             return E_FAIL;
1720         }
1721     } else {
1722         return E_FAIL;
1723     }
1724
1725     This->m_pszPath = SHAlloc(lstrlenA(szTargetPath)+1);
1726     if (!This->m_pszPath) 
1727         return E_FAIL;
1728     lstrcpyA(This->m_pszPath, szTargetPath);
1729     This->m_pidlLocation = ILClone(pidlRoot);
1730     This->m_dwAttributes = (ppfti->dwAttributes != -1) ? ppfti->dwAttributes :
1731         (SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|SFGAO_CANRENAME|SFGAO_FILESYSTEM);
1732
1733     return S_OK;
1734 }
1735
1736 static HRESULT WINAPI PersistFolder3_GetFolderTargetInfo(IPersistFolder3 *iface,
1737     PERSIST_FOLDER_TARGET_INFO *ppfti)
1738 {
1739     UnixFolder *This = impl_from_IPersistFolder3(iface);
1740     FIXME("(%p)->(%p): stub\n", This, ppfti);
1741     return E_NOTIMPL;
1742 }
1743
1744 static const IPersistFolder3Vtbl PersistFolder3Vtbl = {
1745     PersistFolder3_QueryInterface,
1746     PersistFolder3_AddRef,
1747     PersistFolder3_Release,
1748     PersistFolder3_GetClassID,
1749     PersistFolder3_Initialize,
1750     PersistFolder3_GetCurFolder,
1751     PersistFolder3_InitializeEx,
1752     PersistFolder3_GetFolderTargetInfo
1753 };
1754
1755 static HRESULT WINAPI PersistPropertyBag_QueryInterface(IPersistPropertyBag* iface,
1756     REFIID riid, void** ppv)
1757 {
1758     UnixFolder *This = impl_from_IPersistPropertyBag(iface);
1759     return IShellFolder2_QueryInterface(&This->IShellFolder2_iface, riid, ppv);
1760 }
1761
1762 static ULONG WINAPI PersistPropertyBag_AddRef(IPersistPropertyBag* iface)
1763 {
1764     UnixFolder *This = impl_from_IPersistPropertyBag(iface);
1765     return IShellFolder2_AddRef(&This->IShellFolder2_iface);
1766 }
1767
1768 static ULONG WINAPI PersistPropertyBag_Release(IPersistPropertyBag* iface)
1769 {
1770     UnixFolder *This = impl_from_IPersistPropertyBag(iface);
1771     return IShellFolder2_Release(&This->IShellFolder2_iface);
1772 }
1773
1774 static HRESULT WINAPI PersistPropertyBag_GetClassID(IPersistPropertyBag* iface, CLSID* pClassID)
1775 {
1776     UnixFolder *This = impl_from_IPersistPropertyBag(iface);
1777     return IPersistFolder3_GetClassID(&This->IPersistFolder3_iface, pClassID);
1778 }
1779
1780 static HRESULT WINAPI PersistPropertyBag_InitNew(IPersistPropertyBag* iface)
1781 {
1782     UnixFolder *This = impl_from_IPersistPropertyBag(iface);
1783     FIXME("(%p): stub\n", This);
1784     return E_NOTIMPL;
1785 }
1786
1787 static HRESULT WINAPI PersistPropertyBag_Load(IPersistPropertyBag *iface,
1788     IPropertyBag *pPropertyBag, IErrorLog *pErrorLog)
1789 {
1790     UnixFolder *This = impl_from_IPersistPropertyBag(iface);
1791
1792      static const WCHAR wszTarget[] = { 'T','a','r','g','e','t', 0 }, wszNull[] = { 0 };
1793      PERSIST_FOLDER_TARGET_INFO pftiTarget;
1794      VARIANT var;
1795      HRESULT hr;
1796      
1797      TRACE("(%p)->(%p %p)\n", This, pPropertyBag, pErrorLog);
1798  
1799      if (!pPropertyBag)
1800          return E_POINTER;
1801  
1802      /* Get 'Target' property from the property bag. */
1803      V_VT(&var) = VT_BSTR;
1804      hr = IPropertyBag_Read(pPropertyBag, wszTarget, &var, NULL);
1805      if (FAILED(hr)) 
1806          return E_FAIL;
1807      lstrcpyW(pftiTarget.szTargetParsingName, V_BSTR(&var));
1808      SysFreeString(V_BSTR(&var));
1809  
1810      pftiTarget.pidlTargetFolder = NULL;
1811      lstrcpyW(pftiTarget.szNetworkProvider, wszNull);
1812      pftiTarget.dwAttributes = -1;
1813      pftiTarget.csidl = -1;
1814  
1815      return IPersistFolder3_InitializeEx(&This->IPersistFolder3_iface, NULL, NULL, &pftiTarget);
1816 }
1817
1818 static HRESULT WINAPI PersistPropertyBag_Save(IPersistPropertyBag *iface,
1819     IPropertyBag *pPropertyBag, BOOL fClearDirty, BOOL fSaveAllProperties)
1820 {
1821     UnixFolder *This = impl_from_IPersistPropertyBag(iface);
1822     FIXME("(%p): stub\n", This);
1823     return E_NOTIMPL;
1824 }
1825
1826 static const IPersistPropertyBagVtbl PersistPropertyBagVtbl = {
1827     PersistPropertyBag_QueryInterface,
1828     PersistPropertyBag_AddRef,
1829     PersistPropertyBag_Release,
1830     PersistPropertyBag_GetClassID,
1831     PersistPropertyBag_InitNew,
1832     PersistPropertyBag_Load,
1833     PersistPropertyBag_Save
1834 };
1835
1836 static HRESULT WINAPI SFHelper_QueryInterface(ISFHelper* iface, REFIID riid, void** ppvObject)
1837 {
1838     UnixFolder *This = impl_from_ISFHelper(iface);
1839     return IShellFolder2_QueryInterface(&This->IShellFolder2_iface, riid, ppvObject);
1840 }
1841
1842 static ULONG WINAPI SFHelper_AddRef(ISFHelper* iface)
1843 {
1844     UnixFolder *This = impl_from_ISFHelper(iface);
1845     return IShellFolder2_AddRef(&This->IShellFolder2_iface);
1846 }
1847
1848 static ULONG WINAPI SFHelper_Release(ISFHelper* iface)
1849 {
1850     UnixFolder *This = impl_from_ISFHelper(iface);
1851     return IShellFolder2_Release(&This->IShellFolder2_iface);
1852 }
1853
1854 static HRESULT WINAPI SFHelper_GetUniqueName(ISFHelper* iface, LPWSTR pwszName, UINT uLen)
1855 {
1856     UnixFolder *This = impl_from_ISFHelper(iface);
1857     IEnumIDList *pEnum;
1858     HRESULT hr;
1859     LPITEMIDLIST pidlElem;
1860     DWORD dwFetched;
1861     int i;
1862     WCHAR wszNewFolder[25];
1863     static const WCHAR wszFormat[] = { '%','s',' ','%','d',0 };
1864
1865     TRACE("(%p)->(%p %u)\n", This, pwszName, uLen);
1866
1867     LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, sizeof(wszNewFolder)/sizeof(WCHAR));
1868
1869     if (uLen < sizeof(wszNewFolder)/sizeof(WCHAR)+3)
1870         return E_INVALIDARG;
1871
1872     hr = IShellFolder2_EnumObjects(&This->IShellFolder2_iface, 0,
1873                                    SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN, &pEnum);
1874     if (SUCCEEDED(hr)) {
1875         lstrcpynW(pwszName, wszNewFolder, uLen);
1876         IEnumIDList_Reset(pEnum);
1877         i = 2;
1878         while ((IEnumIDList_Next(pEnum, 1, &pidlElem, &dwFetched) == S_OK) && (dwFetched == 1)) {
1879             WCHAR wszTemp[MAX_PATH];
1880             _ILSimpleGetTextW(pidlElem, wszTemp, MAX_PATH);
1881             if (!lstrcmpiW(wszTemp, pwszName)) {
1882                 IEnumIDList_Reset(pEnum);
1883                 snprintfW(pwszName, uLen, wszFormat, wszNewFolder, i++);
1884                 if (i > 99) {
1885                     hr = E_FAIL;
1886                     break;
1887                 }
1888             }
1889         }
1890         IEnumIDList_Release(pEnum);
1891     }
1892     return hr;
1893 }
1894
1895 static HRESULT WINAPI SFHelper_AddFolder(ISFHelper* iface, HWND hwnd, LPCWSTR pwszName,
1896     LPITEMIDLIST* ppidlOut)
1897 {
1898     UnixFolder *This = impl_from_ISFHelper(iface);
1899     char szNewDir[FILENAME_MAX];
1900     int cBaseLen;
1901
1902     TRACE("(%p)->(%p %s %p)\n", This, hwnd, debugstr_w(pwszName), ppidlOut);
1903
1904     if (ppidlOut)
1905         *ppidlOut = NULL;
1906
1907     if (!This->m_pszPath || !(This->m_dwAttributes & SFGAO_FILESYSTEM))
1908         return E_FAIL;
1909     
1910     lstrcpynA(szNewDir, This->m_pszPath, FILENAME_MAX);
1911     cBaseLen = lstrlenA(szNewDir);
1912     WideCharToMultiByte(CP_UNIXCP, 0, pwszName, -1, szNewDir+cBaseLen, FILENAME_MAX-cBaseLen, 0, 0);
1913    
1914     if (mkdir(szNewDir, 0777)) {
1915         char szMessage[256 + FILENAME_MAX];
1916         char szCaption[256];
1917
1918         LoadStringA(shell32_hInstance, IDS_CREATEFOLDER_DENIED, szCaption, sizeof(szCaption));
1919         sprintf(szMessage, szCaption, szNewDir);
1920         LoadStringA(shell32_hInstance, IDS_CREATEFOLDER_CAPTION, szCaption, sizeof(szCaption));
1921         MessageBoxA(hwnd, szMessage, szCaption, MB_OK | MB_ICONEXCLAMATION);
1922
1923         return E_FAIL;
1924     } else {
1925         LPITEMIDLIST pidlRelative;
1926
1927         /* Inform the shell */
1928         if (SUCCEEDED(UNIXFS_path_to_pidl(This, NULL, pwszName, &pidlRelative))) {
1929             LPITEMIDLIST pidlAbsolute = ILCombine(This->m_pidlLocation, pidlRelative);
1930             if (ppidlOut)
1931                 *ppidlOut = pidlRelative;
1932             else
1933                 ILFree(pidlRelative);
1934             SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, pidlAbsolute, NULL);
1935             ILFree(pidlAbsolute);
1936         } else return E_FAIL;
1937         return S_OK;
1938     }
1939 }
1940
1941 /*
1942  * Delete specified files by converting the path to DOS paths and calling
1943  * SHFileOperationW. If an error occurs it returns an error code. If the paths can't
1944  * be converted, S_FALSE is returned. In such situation DeleteItems will try to delete
1945  * the files using syscalls
1946  */
1947 static HRESULT UNIXFS_delete_with_shfileop(UnixFolder *This, UINT cidl, const LPCITEMIDLIST *apidl)
1948 {
1949     char szAbsolute[FILENAME_MAX], *pszRelative;
1950     LPWSTR wszPathsList, wszListPos;
1951     SHFILEOPSTRUCTW op;
1952     HRESULT ret;
1953     UINT i;
1954     
1955     lstrcpyA(szAbsolute, This->m_pszPath);
1956     pszRelative = szAbsolute + lstrlenA(szAbsolute);
1957     
1958     wszListPos = wszPathsList = HeapAlloc(GetProcessHeap(), 0, cidl*MAX_PATH*sizeof(WCHAR)+1);
1959     if (wszPathsList == NULL)
1960         return E_OUTOFMEMORY;
1961     for (i=0; i<cidl; i++) {
1962         LPWSTR wszDosPath;
1963         
1964         if (!_ILIsFolder(apidl[i]) && !_ILIsValue(apidl[i]))
1965             continue;
1966         if (!UNIXFS_filename_from_shitemid(apidl[i], pszRelative))
1967         {
1968             HeapFree(GetProcessHeap(), 0, wszPathsList);
1969             return E_INVALIDARG;
1970         }
1971         wszDosPath = wine_get_dos_file_name(szAbsolute);
1972         if (wszDosPath == NULL || lstrlenW(wszDosPath) >= MAX_PATH)
1973         {
1974             HeapFree(GetProcessHeap(), 0, wszPathsList);
1975             HeapFree(GetProcessHeap(), 0, wszDosPath);
1976             return S_FALSE;
1977         }
1978         lstrcpyW(wszListPos, wszDosPath);
1979         wszListPos += lstrlenW(wszListPos)+1;
1980         HeapFree(GetProcessHeap(), 0, wszDosPath);
1981     }
1982     *wszListPos = 0;
1983     
1984     ZeroMemory(&op, sizeof(op));
1985     op.hwnd = GetActiveWindow();
1986     op.wFunc = FO_DELETE;
1987     op.pFrom = wszPathsList;
1988     op.fFlags = FOF_ALLOWUNDO;
1989     if (!SHFileOperationW(&op))
1990     {
1991         WARN("SHFileOperationW failed\n");
1992         ret = E_FAIL;
1993     }
1994     else
1995         ret = S_OK;
1996
1997     HeapFree(GetProcessHeap(), 0, wszPathsList);
1998     return ret;
1999 }
2000
2001 static HRESULT UNIXFS_delete_with_syscalls(UnixFolder *This, UINT cidl, const LPCITEMIDLIST *apidl)
2002 {
2003     char szAbsolute[FILENAME_MAX], *pszRelative;
2004     static const WCHAR empty[] = {0};
2005     UINT i;
2006     
2007     if (!SHELL_ConfirmYesNoW(GetActiveWindow(), ASK_DELETE_SELECTED, empty))
2008         return S_OK;
2009     
2010     lstrcpyA(szAbsolute, This->m_pszPath);
2011     pszRelative = szAbsolute + lstrlenA(szAbsolute);
2012     
2013     for (i=0; i<cidl; i++) {
2014         if (!UNIXFS_filename_from_shitemid(apidl[i], pszRelative))
2015             return E_INVALIDARG;
2016         if (_ILIsFolder(apidl[i])) {
2017             if (rmdir(szAbsolute))
2018                 return E_FAIL;
2019         } else if (_ILIsValue(apidl[i])) {
2020             if (unlink(szAbsolute))
2021                 return E_FAIL;
2022         }
2023     }
2024     return S_OK;
2025 }
2026
2027 static HRESULT WINAPI SFHelper_DeleteItems(ISFHelper* iface, UINT cidl, LPCITEMIDLIST* apidl)
2028 {
2029     UnixFolder *This = impl_from_ISFHelper(iface);
2030     char szAbsolute[FILENAME_MAX], *pszRelative;
2031     LPITEMIDLIST pidlAbsolute;
2032     HRESULT hr = S_OK;
2033     UINT i;
2034     struct stat st;
2035     
2036     TRACE("(%p)->(%d %p)\n", This, cidl, apidl);
2037
2038     hr = UNIXFS_delete_with_shfileop(This, cidl, apidl);
2039     if (hr == S_FALSE)
2040         hr = UNIXFS_delete_with_syscalls(This, cidl, apidl);
2041
2042     lstrcpyA(szAbsolute, This->m_pszPath);
2043     pszRelative = szAbsolute + lstrlenA(szAbsolute);
2044     
2045     /* we need to manually send the notifies if the files doesn't exist */
2046     for (i=0; i<cidl; i++) {
2047         if (!UNIXFS_filename_from_shitemid(apidl[i], pszRelative))
2048             continue;
2049         pidlAbsolute = ILCombine(This->m_pidlLocation, apidl[i]);
2050         if (stat(szAbsolute, &st))
2051         {
2052             if (_ILIsFolder(apidl[i])) {
2053                 SHChangeNotify(SHCNE_RMDIR, SHCNF_IDLIST, pidlAbsolute, NULL);
2054             } else if (_ILIsValue(apidl[i])) {
2055                 SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlAbsolute, NULL);
2056             }
2057         }
2058         ILFree(pidlAbsolute);
2059     }
2060         
2061     return hr;
2062 }
2063
2064 static HRESULT WINAPI SFHelper_CopyItems(ISFHelper* iface, IShellFolder *psfFrom,
2065     UINT cidl, LPCITEMIDLIST *apidl)
2066 {
2067     UnixFolder *This = impl_from_ISFHelper(iface);
2068     DWORD dwAttributes;
2069     UINT i;
2070     HRESULT hr;
2071     char szAbsoluteDst[FILENAME_MAX], *pszRelativeDst;
2072     
2073     TRACE("(%p)->(%p %d %p)\n", This, psfFrom, cidl, apidl);
2074
2075     if (!psfFrom || !cidl || !apidl)
2076         return E_INVALIDARG;
2077
2078     /* All source items have to be filesystem items. */
2079     dwAttributes = SFGAO_FILESYSTEM;
2080     hr = IShellFolder_GetAttributesOf(psfFrom, cidl, apidl, &dwAttributes);
2081     if (FAILED(hr) || !(dwAttributes & SFGAO_FILESYSTEM)) 
2082         return E_INVALIDARG;
2083
2084     lstrcpyA(szAbsoluteDst, This->m_pszPath);
2085     pszRelativeDst = szAbsoluteDst + strlen(szAbsoluteDst);
2086     
2087     for (i=0; i<cidl; i++) {
2088         WCHAR wszSrc[MAX_PATH];
2089         char szSrc[FILENAME_MAX];
2090         STRRET strret;
2091         HRESULT res;
2092         WCHAR *pwszDosSrc, *pwszDosDst;
2093
2094         /* Build the unix path of the current source item. */
2095         if (FAILED(IShellFolder_GetDisplayNameOf(psfFrom, apidl[i], SHGDN_FORPARSING, &strret)))
2096             return E_FAIL;
2097         if (FAILED(StrRetToBufW(&strret, apidl[i], wszSrc, MAX_PATH)))
2098             return E_FAIL;
2099         if (!UNIXFS_get_unix_path(wszSrc, szSrc)) 
2100             return E_FAIL;
2101
2102         /* Build the unix path of the current destination item */
2103         UNIXFS_filename_from_shitemid(apidl[i], pszRelativeDst);
2104
2105         pwszDosSrc = wine_get_dos_file_name(szSrc);
2106         pwszDosDst = wine_get_dos_file_name(szAbsoluteDst);
2107
2108         if (pwszDosSrc && pwszDosDst)
2109             res = UNIXFS_copy(pwszDosSrc, pwszDosDst);
2110         else
2111             res = E_OUTOFMEMORY;
2112
2113         HeapFree(GetProcessHeap(), 0, pwszDosSrc);
2114         HeapFree(GetProcessHeap(), 0, pwszDosDst);
2115
2116         if (res != S_OK)
2117             return res;
2118     }
2119     return S_OK;
2120 }
2121
2122 static const ISFHelperVtbl SFHelperVtbl = {
2123     SFHelper_QueryInterface,
2124     SFHelper_AddRef,
2125     SFHelper_Release,
2126     SFHelper_GetUniqueName,
2127     SFHelper_AddFolder,
2128     SFHelper_DeleteItems,
2129     SFHelper_CopyItems
2130 };
2131
2132 static HRESULT WINAPI DropTarget_QueryInterface(IDropTarget* iface, REFIID riid, void** ppvObject)
2133 {
2134     UnixFolder *This = impl_from_IDropTarget(iface);
2135     return IShellFolder2_QueryInterface(&This->IShellFolder2_iface, riid, ppvObject);
2136 }
2137
2138 static ULONG WINAPI DropTarget_AddRef(IDropTarget* iface)
2139 {
2140     UnixFolder *This = impl_from_IDropTarget(iface);
2141     return IShellFolder2_AddRef(&This->IShellFolder2_iface);
2142 }
2143
2144 static ULONG WINAPI DropTarget_Release(IDropTarget* iface)
2145 {
2146     UnixFolder *This = impl_from_IDropTarget(iface);
2147     return IShellFolder2_Release(&This->IShellFolder2_iface);
2148 }
2149
2150 #define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
2151 #define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
2152
2153 static HRESULT WINAPI DropTarget_DragEnter(IDropTarget *iface, IDataObject *pDataObject,
2154     DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
2155 {
2156     UnixFolder *This = impl_from_IDropTarget(iface);
2157     FORMATETC format;
2158     STGMEDIUM medium; 
2159         
2160     TRACE("(%p)->(%p 0x%08x {.x=%d, .y=%d} %p)\n", This, pDataObject, dwKeyState, pt.x, pt.y, pdwEffect);
2161
2162     if (!pdwEffect || !pDataObject)
2163         return E_INVALIDARG;
2164   
2165     /* Compute a mask of supported drop-effects for this shellfolder object and the given data 
2166      * object. Dropping is only supported on folders, which represent filesystem locations. One
2167      * can't drop on file objects. And the 'move' drop effect is only supported, if the source
2168      * folder is not identical to the target folder. */
2169     This->m_dwDropEffectsMask = DROPEFFECT_NONE;
2170     InitFormatEtc(format, cfShellIDList, TYMED_HGLOBAL);
2171     if ((This->m_dwAttributes & SFGAO_FILESYSTEM) && /* Only drop to filesystem folders */
2172         _ILIsFolder(ILFindLastID(This->m_pidlLocation)) && /* Only drop to folders, not to files */
2173         SUCCEEDED(IDataObject_GetData(pDataObject, &format, &medium))) /* Only ShellIDList format */
2174     {
2175         LPIDA pidaShellIDList = GlobalLock(medium.u.hGlobal);
2176         This->m_dwDropEffectsMask |= DROPEFFECT_COPY|DROPEFFECT_LINK;
2177
2178         if (pidaShellIDList) { /* Files can only be moved between two different folders */
2179             if (!ILIsEqual(HIDA_GetPIDLFolder(pidaShellIDList), This->m_pidlLocation))
2180                 This->m_dwDropEffectsMask |= DROPEFFECT_MOVE;
2181             GlobalUnlock(medium.u.hGlobal);
2182         }
2183     }
2184
2185     *pdwEffect = KeyStateToDropEffect(dwKeyState) & This->m_dwDropEffectsMask;
2186     
2187     return S_OK;
2188 }
2189
2190 static HRESULT WINAPI DropTarget_DragOver(IDropTarget *iface, DWORD dwKeyState,
2191     POINTL pt, DWORD *pdwEffect)
2192 {
2193     UnixFolder *This = impl_from_IDropTarget(iface);
2194
2195     TRACE("(%p)->(0x%08x {.x=%d, .y=%d} %p)\n", This, dwKeyState, pt.x, pt.y, pdwEffect);
2196
2197     if (!pdwEffect)
2198         return E_INVALIDARG;
2199
2200     *pdwEffect = KeyStateToDropEffect(dwKeyState) & This->m_dwDropEffectsMask;
2201     
2202     return S_OK;
2203 }
2204
2205 static HRESULT WINAPI DropTarget_DragLeave(IDropTarget *iface)
2206 {
2207     UnixFolder *This = impl_from_IDropTarget(iface);
2208
2209     TRACE("(%p)\n", This);
2210
2211     This->m_dwDropEffectsMask = DROPEFFECT_NONE;
2212     return S_OK;
2213 }
2214
2215 static HRESULT WINAPI DropTarget_Drop(IDropTarget *iface, IDataObject *pDataObject,
2216     DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
2217 {
2218     UnixFolder *This = impl_from_IDropTarget(iface);
2219     FORMATETC format;
2220     STGMEDIUM medium; 
2221     HRESULT hr;
2222
2223     TRACE("(%p)->(%p %d {.x=%d, .y=%d} %p) semi-stub\n",
2224         This, pDataObject, dwKeyState, pt.x, pt.y, pdwEffect);
2225
2226     InitFormatEtc(format, cfShellIDList, TYMED_HGLOBAL);
2227     hr = IDataObject_GetData(pDataObject, &format, &medium);
2228     if (FAILED(hr))
2229         return hr;
2230
2231     if (medium.tymed == TYMED_HGLOBAL) {
2232         IShellFolder *psfSourceFolder, *psfDesktopFolder;
2233         LPIDA pidaShellIDList = GlobalLock(medium.u.hGlobal);
2234         STRRET strret;
2235         UINT i;
2236     
2237         if (!pidaShellIDList) 
2238             return HRESULT_FROM_WIN32(GetLastError());
2239         
2240         hr = SHGetDesktopFolder(&psfDesktopFolder);
2241         if (FAILED(hr)) {
2242             GlobalUnlock(medium.u.hGlobal);
2243             return hr;
2244         }
2245
2246         hr = IShellFolder_BindToObject(psfDesktopFolder, HIDA_GetPIDLFolder(pidaShellIDList), NULL, 
2247                                        &IID_IShellFolder, (LPVOID*)&psfSourceFolder);
2248         IShellFolder_Release(psfDesktopFolder);
2249         if (FAILED(hr)) {
2250             GlobalUnlock(medium.u.hGlobal);
2251             return hr;
2252         }
2253
2254         for (i = 0; i < pidaShellIDList->cidl; i++) {
2255             WCHAR wszSourcePath[MAX_PATH];
2256
2257             hr = IShellFolder_GetDisplayNameOf(psfSourceFolder, HIDA_GetPIDLItem(pidaShellIDList, i),
2258                                                SHGDN_FORPARSING, &strret);
2259             if (FAILED(hr)) 
2260                 break;
2261
2262             hr = StrRetToBufW(&strret, NULL, wszSourcePath, MAX_PATH);
2263             if (FAILED(hr)) 
2264                 break;
2265
2266             switch (*pdwEffect) {
2267                 case DROPEFFECT_MOVE:
2268                     FIXME("Move %s to %s!\n", debugstr_w(wszSourcePath), This->m_pszPath);
2269                     break;
2270                 case DROPEFFECT_COPY:
2271                     FIXME("Copy %s to %s!\n", debugstr_w(wszSourcePath), This->m_pszPath);
2272                     break;
2273                 case DROPEFFECT_LINK:
2274                     FIXME("Link %s from %s!\n", debugstr_w(wszSourcePath), This->m_pszPath);
2275                     break;
2276             }
2277         }
2278     
2279         IShellFolder_Release(psfSourceFolder);
2280         GlobalUnlock(medium.u.hGlobal);
2281         return hr;
2282     }
2283  
2284     return E_NOTIMPL;
2285 }
2286
2287 static const IDropTargetVtbl DropTargetVtbl = {
2288     DropTarget_QueryInterface,
2289     DropTarget_AddRef,
2290     DropTarget_Release,
2291     DropTarget_DragEnter,
2292     DropTarget_DragOver,
2293     DropTarget_DragLeave,
2294     DropTarget_Drop
2295 };
2296
2297 /******************************************************************************
2298  * Unix[Dos]Folder_Constructor [Internal]
2299  *
2300  * PARAMS
2301  *  pUnkOuter [I] Outer class for aggregation. Currently ignored.
2302  *  riid      [I] Interface asked for by the client.
2303  *  ppv       [O] Pointer to an riid interface to the UnixFolder object.
2304  *
2305  * NOTES
2306  *  Those are the only functions exported from shfldr_unixfs.c. They are called from
2307  *  shellole.c's default class factory and thus have to exhibit a LPFNCREATEINSTANCE
2308  *  compatible signature.
2309  *
2310  *  The UnixDosFolder_Constructor sets the dwPathMode member to PATHMODE_DOS. This
2311  *  means that paths are converted from dos to unix and back at the interfaces.
2312  */
2313 static HRESULT CreateUnixFolder(IUnknown *outer, REFIID riid, void **ppv, const CLSID *clsid)
2314 {
2315     UnixFolder *This;
2316     HRESULT hr;
2317    
2318     if (outer) {
2319         FIXME("Aggregation not yet implemented!\n");
2320         return CLASS_E_NOAGGREGATION;
2321     }
2322     
2323     This = SHAlloc((ULONG)sizeof(UnixFolder));
2324     if (!This) return E_OUTOFMEMORY;
2325
2326     This->IShellFolder2_iface.lpVtbl = &ShellFolder2Vtbl;
2327     This->IPersistFolder3_iface.lpVtbl = &PersistFolder3Vtbl;
2328     This->IPersistPropertyBag_iface.lpVtbl = &PersistPropertyBagVtbl;
2329     This->ISFHelper_iface.lpVtbl = &SFHelperVtbl;
2330     This->IDropTarget_iface.lpVtbl = &DropTargetVtbl;
2331     This->ref = 1;
2332     This->m_pszPath = NULL;
2333     This->m_pidlLocation = NULL;
2334     This->m_dwPathMode = IsEqualCLSID(&CLSID_UnixFolder, clsid) ? PATHMODE_UNIX : PATHMODE_DOS;
2335     This->m_dwAttributes = 0;
2336     This->m_pCLSID = clsid;
2337     This->m_dwDropEffectsMask = DROPEFFECT_NONE;
2338
2339     hr = IShellFolder2_QueryInterface(&This->IShellFolder2_iface, riid, ppv);
2340     IShellFolder2_Release(&This->IShellFolder2_iface);
2341
2342     return hr;
2343 }
2344
2345 HRESULT WINAPI UnixFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
2346     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2347     return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_UnixFolder);
2348 }
2349
2350 HRESULT WINAPI UnixDosFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
2351     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2352     return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_UnixDosFolder);
2353 }
2354
2355 HRESULT WINAPI FolderShortcut_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
2356     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2357     return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_FolderShortcut);
2358 }
2359
2360 HRESULT WINAPI MyDocuments_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
2361     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2362     return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_MyDocuments);
2363 }
2364
2365 /******************************************************************************
2366  * UnixSubFolderIterator
2367  *
2368  * Class whose heap based objects represent iterators over the sub-directories
2369  * of a given UnixFolder object. 
2370  */
2371
2372 /* UnixSubFolderIterator object layout and typedef.
2373  */
2374 typedef struct _UnixSubFolderIterator {
2375     const IEnumIDListVtbl *lpIEnumIDListVtbl;
2376     LONG m_cRef;
2377     SHCONTF m_fFilter;
2378     DIR *m_dirFolder;
2379     char m_szFolder[FILENAME_MAX];
2380 } UnixSubFolderIterator;
2381
2382 static void UnixSubFolderIterator_Destroy(UnixSubFolderIterator *iterator) {
2383     TRACE("(iterator=%p)\n", iterator);
2384
2385     if (iterator->m_dirFolder)
2386         closedir(iterator->m_dirFolder);
2387     SHFree(iterator);
2388 }
2389
2390 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_QueryInterface(IEnumIDList* iface, 
2391     REFIID riid, void** ppv)
2392 {
2393     TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
2394     
2395     if (!ppv) return E_INVALIDARG;
2396     
2397     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IEnumIDList, riid)) {
2398         *ppv = iface;
2399     } else {
2400         *ppv = NULL;
2401         return E_NOINTERFACE;
2402     }
2403
2404     IEnumIDList_AddRef(iface);
2405     return S_OK;
2406 }
2407                             
2408 static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_AddRef(IEnumIDList* iface)
2409 {
2410     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
2411
2412     TRACE("(iface=%p)\n", iface);
2413    
2414     return InterlockedIncrement(&This->m_cRef);
2415 }
2416
2417 static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_Release(IEnumIDList* iface)
2418 {
2419     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
2420     ULONG cRef;
2421     
2422     TRACE("(iface=%p)\n", iface);
2423
2424     cRef = InterlockedDecrement(&This->m_cRef);
2425     
2426     if (!cRef) 
2427         UnixSubFolderIterator_Destroy(This);
2428
2429     return cRef;
2430 }
2431
2432 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Next(IEnumIDList* iface, ULONG celt, 
2433     LPITEMIDLIST* rgelt, ULONG* pceltFetched)
2434 {
2435     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
2436     ULONG i = 0;
2437
2438     /* This->m_dirFolder will be NULL if the user doesn't have access rights for the dir. */
2439     if (This->m_dirFolder) {
2440         char *pszRelativePath = This->m_szFolder + lstrlenA(This->m_szFolder);
2441         struct dirent *pDirEntry;
2442
2443         while (i < celt) {
2444             pDirEntry = readdir(This->m_dirFolder);
2445             if (!pDirEntry) break; /* No more entries */
2446             if (!strcmp(pDirEntry->d_name, ".") || !strcmp(pDirEntry->d_name, "..")) continue;
2447
2448             /* Temporarily build absolute path in This->m_szFolder. Then construct a pidl
2449              * and see if it passes the filter. 
2450              */
2451             lstrcpyA(pszRelativePath, pDirEntry->d_name);
2452             rgelt[i] = SHAlloc(
2453                 UNIXFS_shitemid_len_from_filename(pszRelativePath, NULL, NULL)+sizeof(USHORT));
2454             if (!UNIXFS_build_shitemid(This->m_szFolder, TRUE, NULL, rgelt[i]) ||
2455                 !UNIXFS_is_pidl_of_type(rgelt[i], This->m_fFilter)) 
2456             {
2457                 SHFree(rgelt[i]);
2458                 rgelt[i] = NULL;
2459                 continue;
2460             }
2461             memset(((PBYTE)rgelt[i])+rgelt[i]->mkid.cb, 0, sizeof(USHORT));
2462             i++;
2463         }
2464         *pszRelativePath = '\0'; /* Restore the original path in This->m_szFolder. */
2465     }
2466     
2467     if (pceltFetched)
2468         *pceltFetched = i;
2469
2470     return (i == 0) ? S_FALSE : S_OK;
2471 }
2472     
2473 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Skip(IEnumIDList* iface, ULONG celt)
2474 {
2475     LPITEMIDLIST *apidl;
2476     ULONG cFetched;
2477     HRESULT hr;
2478     
2479     TRACE("(iface=%p, celt=%d)\n", iface, celt);
2480
2481     /* Call IEnumIDList::Next and delete the resulting pidls. */
2482     apidl = SHAlloc(celt * sizeof(LPITEMIDLIST));
2483     hr = IEnumIDList_Next(iface, celt, apidl, &cFetched);
2484     if (SUCCEEDED(hr))
2485         while (cFetched--) 
2486             SHFree(apidl[cFetched]);
2487     SHFree(apidl);
2488
2489     return hr;
2490 }
2491
2492 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Reset(IEnumIDList* iface)
2493 {
2494     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
2495         
2496     TRACE("(iface=%p)\n", iface);
2497
2498     if (This->m_dirFolder)
2499         rewinddir(This->m_dirFolder);
2500     
2501     return S_OK;
2502 }
2503
2504 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Clone(IEnumIDList* This, 
2505     IEnumIDList** ppenum)
2506 {
2507     FIXME("stub\n");
2508     return E_NOTIMPL;
2509 }
2510
2511 /* VTable for UnixSubFolderIterator's IEnumIDList interface.
2512  */
2513 static const IEnumIDListVtbl UnixSubFolderIterator_IEnumIDList_Vtbl = {
2514     UnixSubFolderIterator_IEnumIDList_QueryInterface,
2515     UnixSubFolderIterator_IEnumIDList_AddRef,
2516     UnixSubFolderIterator_IEnumIDList_Release,
2517     UnixSubFolderIterator_IEnumIDList_Next,
2518     UnixSubFolderIterator_IEnumIDList_Skip,
2519     UnixSubFolderIterator_IEnumIDList_Reset,
2520     UnixSubFolderIterator_IEnumIDList_Clone
2521 };
2522
2523 static IUnknown *UnixSubFolderIterator_Constructor(UnixFolder *pUnixFolder, SHCONTF fFilter) {
2524     UnixSubFolderIterator *iterator;
2525
2526     TRACE("(pUnixFolder=%p)\n", pUnixFolder);
2527     
2528     iterator = SHAlloc((ULONG)sizeof(UnixSubFolderIterator));
2529     iterator->lpIEnumIDListVtbl = &UnixSubFolderIterator_IEnumIDList_Vtbl;
2530     iterator->m_cRef = 0;
2531     iterator->m_fFilter = fFilter;
2532     iterator->m_dirFolder = opendir(pUnixFolder->m_pszPath);
2533     lstrcpyA(iterator->m_szFolder, pUnixFolder->m_pszPath);
2534
2535     UnixSubFolderIterator_IEnumIDList_AddRef((IEnumIDList*)iterator);
2536     
2537     return (IUnknown*)iterator;
2538 }
2539
2540 #else /* __MINGW32__ || _MSC_VER */
2541
2542 HRESULT WINAPI UnixFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv)
2543 {
2544     return E_NOTIMPL;
2545 }
2546
2547 HRESULT WINAPI UnixDosFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv)
2548 {
2549     return E_NOTIMPL;
2550 }
2551
2552 HRESULT WINAPI FolderShortcut_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv)
2553 {
2554     return E_NOTIMPL;
2555 }
2556
2557 HRESULT WINAPI MyDocuments_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv)
2558 {
2559     return E_NOTIMPL;
2560 }
2561
2562 #endif /* __MINGW32__ || _MSC_VER */
2563
2564 /******************************************************************************
2565  * UNIXFS_is_rooted_at_desktop [Internal]
2566  *
2567  * Checks if the unixfs namespace extension is rooted at desktop level.
2568  *
2569  * RETURNS
2570  *  TRUE, if unixfs is rooted at desktop level
2571  *  FALSE, if not.
2572  */
2573 BOOL UNIXFS_is_rooted_at_desktop(void) {
2574     HKEY hKey;
2575     WCHAR wszRootedAtDesktop[69 + CHARS_IN_GUID] = {
2576         'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
2577         'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2578         'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
2579         'N','a','m','e','S','p','a','c','e','\\',0 };
2580
2581     if (StringFromGUID2(&CLSID_UnixDosFolder, wszRootedAtDesktop + 69, CHARS_IN_GUID) &&
2582         RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
2583     {
2584         RegCloseKey(hKey);
2585         return TRUE;
2586     }
2587     return FALSE;
2588 }