shell32: Support for CLSID_ShellFSFolder in unixfs.
[wine] / dlls / shell32 / shfldr_unixfs.c
1 /*
2  * UNIXFS - Shell namespace extension for the unix filesystem
3  *
4  * Copyright (C) 2005 Michael Jung
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 /*
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 appart 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 should'nt 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 savely 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 <dirent.h>
132 #ifdef HAVE_UNISTD_H
133 # include <unistd.h>
134 #endif
135 #ifdef HAVE_SYS_STAT_H
136 # include <sys/stat.h>
137 #endif
138 #ifdef HAVE_PWD_H
139 # include <pwd.h>
140 #endif
141 #include <grp.h>
142 #include <limits.h>
143
144 #define COBJMACROS
145 #define NONAMELESSUNION
146 #define NONAMELESSSTRUCT
147
148 #include "windef.h"
149 #include "winbase.h"
150 #include "winuser.h"
151 #include "objbase.h"
152 #include "winreg.h"
153 #include "shlwapi.h"
154 #include "winternl.h"
155 #include "wine/debug.h"
156
157 #include "shell32_main.h"
158 #include "shellfolder.h"
159 #include "shfldr.h"
160 #include "shresdef.h"
161 #include "pidl.h"
162
163 WINE_DEFAULT_DEBUG_CHANNEL(shell);
164
165 const GUID CLSID_UnixFolder = {0xcc702eb2, 0x7dc5, 0x11d9, {0xc6, 0x87, 0x00, 0x04, 0x23, 0x8a, 0x01, 0xcd}};
166 const GUID CLSID_UnixDosFolder = {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
167
168 #define ADJUST_THIS(c,m,p) ((c*)(((long)p)-(long)&(((c*)0)->lp##m##Vtbl)))
169 #define STATIC_CAST(i,p) ((i*)&p->lp##i##Vtbl)
170
171 #define LEN_SHITEMID_FIXED_PART ((USHORT) \
172     ( sizeof(USHORT)      /* SHITEMID's cb field. */ \
173     + sizeof(PIDLTYPE)    /* PIDLDATA's type field. */ \
174     + sizeof(FileStruct)  /* Well, the FileStruct. */ \
175     - sizeof(char)        /* One char too much in FileStruct. */ \
176     + sizeof(FileStructW) /* You name it. */ \
177     - sizeof(WCHAR)       /* One WCHAR too much in FileStructW. */ \
178     + sizeof(WORD) ))     /* Offset of FileStructW field in PIDL. */
179
180 #define PATHMODE_UNIX 0
181 #define PATHMODE_DOS  1
182
183 /* UnixFolder object layout and typedef.
184  */
185 typedef struct _UnixFolder {
186     const IShellFolder2Vtbl       *lpIShellFolder2Vtbl;
187     const IPersistFolder3Vtbl     *lpIPersistFolder3Vtbl;
188     const IPersistPropertyBagVtbl *lpIPersistPropertyBagVtbl;
189     const IDropTargetVtbl         *lpIDropTargetVtbl;
190     const ISFHelperVtbl           *lpISFHelperVtbl;
191     LONG         m_cRef;
192     CHAR         *m_pszPath;     /* Target path of the shell folder (CP_UNIXCP) */
193     LPITEMIDLIST m_pidlLocation; /* Location in the shell namespace */
194     DWORD        m_dwPathMode;
195     DWORD        m_dwAttributes;
196     const CLSID  *m_pCLSID;
197     DWORD        m_dwDropEffectsMask;
198 } UnixFolder;
199
200 /* Will hold the registered clipboard format identifier for ITEMIDLISTS. */
201 static UINT cfShellIDList = 0;
202
203 /******************************************************************************
204  * UNIXFS_is_rooted_at_desktop [Internal]
205  *
206  * Checks if the unixfs namespace extension is rooted at desktop level.
207  *
208  * RETURNS
209  *  TRUE, if unixfs is rooted at desktop level
210  *  FALSE, if not.
211  */
212 BOOL UNIXFS_is_rooted_at_desktop(void) {
213     HKEY hKey;
214     WCHAR wszRootedAtDesktop[69 + CHARS_IN_GUID] = { 
215         'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
216         'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
217         'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
218         'N','a','m','e','S','p','a','c','e','\\',0 }; 
219
220     if (StringFromGUID2(&CLSID_UnixDosFolder, wszRootedAtDesktop + 69, CHARS_IN_GUID) &&
221         RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) == ERROR_SUCCESS) 
222     {
223         RegCloseKey(hKey);
224         return TRUE;
225     }
226     return FALSE;
227 }
228
229 /******************************************************************************
230  * UNIXFS_filename_from_shitemid [Internal]
231  *
232  *  Get CP_UNIXCP encoded filename corresponding to the first item of a pidl
233  * 
234  * PARAMS
235  *  pidl           [I] A simple SHITEMID
236  *  pszPathElement [O] Filename in CP_UNIXCP encoding will be stored here
237  *
238  * RETURNS
239  *  Success: Number of bytes necessary to store the CP_UNIXCP encoded filename 
240  *   _without_ the terminating NUL.
241  *  Failure: 0
242  *  
243  * NOTES
244  *  Size of the buffer at pszPathElement has to be FILENAME_MAX. pszPathElement
245  *  may be NULL, if you are only interested in the return value. 
246  */
247 static int UNIXFS_filename_from_shitemid(LPCITEMIDLIST pidl, char* pszPathElement) {
248     FileStructW *pFileStructW = _ILGetFileStructW(pidl);
249     int cLen = 0;
250
251     if (pFileStructW) {
252         cLen = WideCharToMultiByte(CP_UNIXCP, 0, pFileStructW->wszName, -1, pszPathElement,
253             pszPathElement ? FILENAME_MAX : 0, 0, 0);
254     } else {
255         /* There might be pidls slipping in from shfldr_fs.c, which don't contain the
256          * FileStructW field. In this case, we have to convert from CP_ACP to CP_UNIXCP. */
257         char *pszText = _ILGetTextPointer(pidl);
258         WCHAR *pwszPathElement = NULL;
259         int cWideChars;
260     
261         cWideChars = MultiByteToWideChar(CP_ACP, 0, pszText, -1, NULL, 0);
262         if (!cWideChars) goto cleanup;
263
264         pwszPathElement = SHAlloc(cWideChars * sizeof(WCHAR));
265         if (!pwszPathElement) goto cleanup;
266     
267         cWideChars = MultiByteToWideChar(CP_ACP, 0, pszText, -1, pwszPathElement, cWideChars);
268         if (!cWideChars) goto cleanup; 
269
270         cLen = WideCharToMultiByte(CP_UNIXCP, 0, pwszPathElement, -1, pszPathElement, 
271             pszPathElement ? FILENAME_MAX : 0, 0, 0);
272
273     cleanup:
274         SHFree(pwszPathElement);
275     }
276         
277     if (cLen) cLen--; /* Don't count terminating NUL! */
278     return cLen;
279 }
280
281 /******************************************************************************
282  * UNIXFS_shitemid_len_from_filename [Internal]
283  *
284  * Computes the necessary length of a pidl to hold a path element
285  *
286  * PARAMS
287  *  szPathElement    [I] The path element string in CP_UNIXCP encoding.
288  *  ppszPathElement  [O] Path element string in CP_ACP encoding.
289  *  ppwszPathElement [O] Path element string as WCHAR string.
290  *
291  * RETURNS
292  *  Success: Length in bytes of a SHITEMID representing szPathElement
293  *  Failure: 0
294  * 
295  * NOTES
296  *  Provide NULL values if not interested in pp(w)szPathElement. Otherwise
297  *  caller is responsible to free ppszPathElement and ppwszPathElement with
298  *  SHFree.
299  */
300 static USHORT UNIXFS_shitemid_len_from_filename(
301     const char *szPathElement, char **ppszPathElement, WCHAR **ppwszPathElement) 
302 {
303     USHORT cbPidlLen = 0;
304     WCHAR *pwszPathElement = NULL;
305     char *pszPathElement = NULL;
306     int cWideChars, cChars;
307
308     /* There and Back Again: A Hobbit's Holiday. CP_UNIXCP might be some ANSI
309      * codepage or it might be a real multi-byte encoding like utf-8. There is no
310      * other way to figure out the length of the corresponding WCHAR and CP_ACP 
311      * strings without actually doing the full CP_UNIXCP -> WCHAR -> CP_ACP cycle. */
312     
313     cWideChars = MultiByteToWideChar(CP_UNIXCP, 0, szPathElement, -1, NULL, 0);
314     if (!cWideChars) goto cleanup;
315
316     pwszPathElement = SHAlloc(cWideChars * sizeof(WCHAR));
317     if (!pwszPathElement) goto cleanup;
318
319     cWideChars = MultiByteToWideChar(CP_UNIXCP, 0, szPathElement, -1, pwszPathElement, cWideChars);
320     if (!cWideChars) goto cleanup; 
321
322     cChars = WideCharToMultiByte(CP_ACP, 0, pwszPathElement, -1, NULL, 0, 0, 0);
323     if (!cChars) goto cleanup;
324
325     pszPathElement = SHAlloc(cChars);
326     if (!pszPathElement) goto cleanup;
327
328     cChars = WideCharToMultiByte(CP_ACP, 0, pwszPathElement, -1, pszPathElement, cChars, 0, 0);
329     if (!cChars) goto cleanup;
330
331     /* (cChars & 0x1) is for the potential alignment byte */
332     cbPidlLen = LEN_SHITEMID_FIXED_PART + cChars + (cChars & 0x1) + cWideChars * sizeof(WCHAR);
333     
334 cleanup:
335     if (cbPidlLen && ppszPathElement) 
336         *ppszPathElement = pszPathElement;
337     else 
338         SHFree(pszPathElement);
339
340     if (cbPidlLen && ppwszPathElement)
341         *ppwszPathElement = pwszPathElement;
342     else
343         SHFree(pwszPathElement);
344
345     return cbPidlLen;
346 }
347
348 /******************************************************************************
349  * UNIXFS_is_pidl_of_type [Internal]
350  *
351  * Checks for the first SHITEMID of an ITEMIDLIST if it passes a filter.
352  *
353  * PARAMS
354  *  pIDL    [I] The ITEMIDLIST to be checked.
355  *  fFilter [I] Shell condition flags, which specify the filter.
356  *
357  * RETURNS
358  *  TRUE, if pIDL is accepted by fFilter
359  *  FALSE, otherwise
360  */
361 static inline BOOL UNIXFS_is_pidl_of_type(LPITEMIDLIST pIDL, SHCONTF fFilter) {
362     LPPIDLDATA pIDLData = _ILGetDataPointer(pIDL);
363     if (!(fFilter & SHCONTF_INCLUDEHIDDEN) && pIDLData && 
364         (pIDLData->u.file.uFileAttribs & FILE_ATTRIBUTE_HIDDEN)) 
365     {
366         return FALSE;
367     }
368     if (_ILIsFolder(pIDL) && (fFilter & SHCONTF_FOLDERS)) return TRUE;
369     if (_ILIsValue(pIDL) && (fFilter & SHCONTF_NONFOLDERS)) return TRUE;
370     return FALSE;
371 }
372
373 /******************************************************************************
374  * UNIXFS_is_dos_device [Internal]
375  *
376  * Determines if a unix directory corresponds to any dos device.
377  *
378  * PARAMS
379  *  statPath [I] The stat struct of the directory, as returned by stat(2).
380  *
381  * RETURNS
382  *  TRUE, if statPath corresponds to any dos drive letter
383  *  FALSE, otherwise
384  */
385 static BOOL UNIXFS_is_dos_device(const struct stat *statPath) {
386     struct stat statDrive;
387     char *pszDrivePath;
388     DWORD dwDriveMap;
389     WCHAR wszDosDevice[4] = { 'A', ':', '\\', 0 };
390
391     for (dwDriveMap = GetLogicalDrives(); dwDriveMap; dwDriveMap >>= 1, wszDosDevice[0]++) {
392         if (!(dwDriveMap & 0x1)) continue;
393         pszDrivePath = wine_get_unix_file_name(wszDosDevice);
394         if (pszDrivePath && !stat(pszDrivePath, &statDrive)) {
395             HeapFree(GetProcessHeap(), 0, pszDrivePath);
396             if ((statPath->st_dev == statDrive.st_dev) && (statPath->st_ino == statDrive.st_ino))
397                 return TRUE;
398         }
399     }
400     return FALSE;
401 }
402
403 /******************************************************************************
404  * UNIXFS_get_unix_path [Internal]
405  *
406  * Convert an absolute dos path to an absolute canonicalized unix path.
407  * Evaluate "/.", "/.." and symbolic links.
408  *
409  * PARAMS
410  *  pszDosPath       [I] An absolute dos path
411  *  pszCanonicalPath [O] Buffer of length FILENAME_MAX. Will receive the canonical path.
412  *
413  * RETURNS
414  *  Success, TRUE
415  *  Failure, FALSE - Path not existent, too long, insufficient rights, to many symlinks
416  */
417 static BOOL UNIXFS_get_unix_path(LPCWSTR pszDosPath, char *pszCanonicalPath)
418 {
419     char *pPathTail, *pElement, *pCanonicalTail, szPath[FILENAME_MAX], *pszUnixPath;
420     struct stat fileStat;
421     
422     TRACE("(pszDosPath=%s, pszCanonicalPath=%p)\n", debugstr_w(pszDosPath), pszCanonicalPath);
423     
424     if (!pszDosPath || pszDosPath[1] != ':')
425         return FALSE;
426
427     pszUnixPath = wine_get_unix_file_name(pszDosPath);
428     if (!pszUnixPath) return FALSE;   
429     strcpy(szPath, pszUnixPath);
430     HeapFree(GetProcessHeap(), 0, pszUnixPath);
431    
432     /* pCanonicalTail always points to the end of the canonical path constructed
433      * thus far. pPathTail points to the still to be processed part of the input
434      * path. pElement points to the path element currently investigated.
435      */
436     *pszCanonicalPath = '\0';
437     pCanonicalTail = pszCanonicalPath;
438     pPathTail = szPath;
439
440     do {
441         char cTemp;
442         int cLinks = 0;
443             
444         pElement = pPathTail;
445         pPathTail = strchr(pPathTail+1, '/');
446         if (!pPathTail) /* Last path element may not be terminated by '/'. */ 
447             pPathTail = pElement + strlen(pElement);
448         /* Temporarily terminate the current path element. Will be restored later. */
449         cTemp = *pPathTail;
450         *pPathTail = '\0';
451
452         /* Skip "/." path elements */
453         if (!strcmp("/.", pElement)) {
454             *pPathTail = cTemp;
455             continue;
456         }
457
458         /* Remove last element in canonical path for "/.." elements, then skip. */
459         if (!strcmp("/..", pElement)) {
460             char *pTemp = strrchr(pszCanonicalPath, '/');
461             if (pTemp)
462                 pCanonicalTail = pTemp;
463             *pCanonicalTail = '\0';
464             *pPathTail = cTemp;
465             continue;
466         }
467        
468         /* lstat returns zero on success. */
469         if (lstat(szPath, &fileStat)) 
470             return FALSE;
471
472         if (S_ISLNK(fileStat.st_mode)) {
473             char szSymlink[FILENAME_MAX];
474             int cLinkLen, cTailLen;
475           
476             /* Avoid infinite loop for recursive links. */
477             if (++cLinks > 64) 
478                 return FALSE;
479             
480             cLinkLen = readlink(szPath, szSymlink, FILENAME_MAX);
481             if (cLinkLen < 0) 
482                 return FALSE;
483
484             *pPathTail = cTemp;
485             cTailLen = strlen(pPathTail);
486            
487             if (szSymlink[0] == '/') {
488                 /* Absolute link. Copy to szPath, concat remaining path and start all over. */
489                 if (cLinkLen + cTailLen + 1 > FILENAME_MAX)
490                     return FALSE;
491                     
492                 /* Avoid double slashes. */
493                 if (szSymlink[cLinkLen-1] == '/' && pPathTail[0] == '/') {
494                     szSymlink[cLinkLen-1] = '\0';
495                     cLinkLen--;
496                 }
497                 
498                 memcpy(szSymlink + cLinkLen, pPathTail, cTailLen + 1);
499                 memcpy(szPath, szSymlink, cLinkLen + cTailLen + 1);
500                 *pszCanonicalPath = '\0';
501                 pCanonicalTail = pszCanonicalPath;
502                 pPathTail = szPath;
503             } else {
504                 /* Relative link. Expand into szPath and continue. */
505                 char szTemp[FILENAME_MAX];
506                 int cTailLen = strlen(pPathTail);
507
508                 if (pElement - szPath + 1 + cLinkLen + cTailLen + 1 > FILENAME_MAX)
509                     return FALSE;
510
511                 memcpy(szTemp, pPathTail, cTailLen + 1);
512                 memcpy(pElement + 1, szSymlink, cLinkLen);
513                 memcpy(pElement + 1 + cLinkLen, szTemp, cTailLen + 1);
514                 pPathTail = pElement;
515             }
516         } else {
517             /* Regular directory or file. Copy to canonical path */
518             if (pCanonicalTail - pszCanonicalPath + pPathTail - pElement + 1 > FILENAME_MAX)
519                 return FALSE;
520                 
521             memcpy(pCanonicalTail, pElement, pPathTail - pElement + 1);
522             pCanonicalTail += pPathTail - pElement;
523             *pPathTail = cTemp;
524         }
525     } while (pPathTail[0] == '/');
526    
527     TRACE("--> %s\n", debugstr_a(pszCanonicalPath));
528     
529     return TRUE;
530 }
531
532 /******************************************************************************
533  * UNIXFS_seconds_since_1970_to_dos_date_time [Internal]
534  *
535  * Convert unix time to FAT time
536  *
537  * PARAMS 
538  *  ss1970 [I] Unix time (seconds since 1970)
539  *  pDate  [O] Corresponding FAT date
540  *  pTime  [O] Corresponding FAT time
541  */
542 static inline void UNIXFS_seconds_since_1970_to_dos_date_time(
543     time_t ss1970, LPWORD pDate, LPWORD pTime)
544 {
545     LARGE_INTEGER time;
546     FILETIME fileTime;
547
548     RtlSecondsSince1970ToTime( ss1970, &time );
549     fileTime.dwLowDateTime = time.u.LowPart;
550     fileTime.dwHighDateTime = time.u.HighPart;
551     FileTimeToDosDateTime(&fileTime, pDate, pTime);
552 }
553
554 /******************************************************************************
555  * UNIXFS_build_shitemid [Internal]
556  *
557  * Constructs a new SHITEMID for the last component of path 'pszUnixPath' into 
558  * buffer 'pIDL'.
559  *
560  * PARAMS
561  *  pszUnixPath [I] An absolute path. The SHITEMID will be build for the last component.
562  *  pIDL        [O] SHITEMID will be constructed here.
563  *
564  * RETURNS
565  *  Success: A pointer to the terminating '\0' character of path.
566  *  Failure: NULL
567  *
568  * NOTES
569  *  Minimum size of pIDL is SHITEMID_LEN_FROM_NAME_LEN(strlen(last_component_of_path)).
570  *  If what you need is a PIDLLIST with a single SHITEMID, don't forget to append
571  *  a 0 USHORT value.
572  */
573 static char* UNIXFS_build_shitemid(char *pszUnixPath, void *pIDL) {
574     LPPIDLDATA pIDLData;
575     struct stat fileStat;
576     char *pszComponentU, *pszComponentA;
577     WCHAR *pwszComponentW;
578     int cComponentULen, cComponentALen;
579     USHORT cbLen;
580     FileStructW *pFileStructW;
581     WORD uOffsetW, *pOffsetW;
582
583     TRACE("(pszUnixPath=%s, pIDL=%p)\n", debugstr_a(pszUnixPath), pIDL);
584
585     /* We are only interested in regular files and directories. */
586     if (stat(pszUnixPath, &fileStat)) return NULL;
587     if (!S_ISDIR(fileStat.st_mode) && !S_ISREG(fileStat.st_mode)) return NULL;
588     
589     /* Compute the SHITEMID's length and wipe it. */
590     pszComponentU = strrchr(pszUnixPath, '/') + 1;
591     cComponentULen = strlen(pszComponentU);
592     cbLen = UNIXFS_shitemid_len_from_filename(pszComponentU, &pszComponentA, &pwszComponentW);
593     if (!cbLen) return NULL;
594     memset(pIDL, 0, cbLen);
595     ((LPSHITEMID)pIDL)->cb = cbLen;
596     
597     /* Set shell32's standard SHITEMID data fields. */
598     pIDLData = _ILGetDataPointer((LPCITEMIDLIST)pIDL);
599     pIDLData->type = S_ISDIR(fileStat.st_mode) ? PT_FOLDER : PT_VALUE;
600     pIDLData->u.file.dwFileSize = (DWORD)fileStat.st_size;
601     UNIXFS_seconds_since_1970_to_dos_date_time(fileStat.st_mtime, &pIDLData->u.file.uFileDate, 
602         &pIDLData->u.file.uFileTime);
603     pIDLData->u.file.uFileAttribs = 0;
604     if (S_ISDIR(fileStat.st_mode)) pIDLData->u.file.uFileAttribs |= FILE_ATTRIBUTE_DIRECTORY;
605     if (pszComponentU[0] == '.') pIDLData->u.file.uFileAttribs |=  FILE_ATTRIBUTE_HIDDEN;
606     cComponentALen = lstrlenA(pszComponentA) + 1;
607     memcpy(pIDLData->u.file.szNames, pszComponentA, cComponentALen);
608     
609     pFileStructW = (FileStructW*)(pIDLData->u.file.szNames + cComponentALen + (cComponentALen & 0x1));
610     uOffsetW = (WORD)(((LPBYTE)pFileStructW) - ((LPBYTE)pIDL));
611     pFileStructW->cbLen = cbLen - uOffsetW;
612     UNIXFS_seconds_since_1970_to_dos_date_time(fileStat.st_mtime, &pFileStructW->uCreationDate, 
613         &pFileStructW->uCreationTime);
614     UNIXFS_seconds_since_1970_to_dos_date_time(fileStat.st_atime, &pFileStructW->uLastAccessDate,
615         &pFileStructW->uLastAccessTime);
616     lstrcpyW(pFileStructW->wszName, pwszComponentW);
617
618     pOffsetW = (WORD*)(((LPBYTE)pIDL) + cbLen - sizeof(WORD));
619     *pOffsetW = uOffsetW;
620     
621     SHFree(pszComponentA);
622     SHFree(pwszComponentW);
623     
624     return pszComponentU + cComponentULen;
625 }
626
627 /******************************************************************************
628  * UNIXFS_path_to_pidl [Internal]
629  *
630  * PARAMS
631  *  pUnixFolder [I] If path is relative, pUnixFolder represents the base path
632  *  path        [I] An absolute unix or dos path or a path relativ to pUnixFolder
633  *  ppidl       [O] The corresponding ITEMIDLIST. Release with SHFree/ILFree
634  *  
635  * RETURNS
636  *  Success: TRUE
637  *  Failure: FALSE, invalid params or out of memory
638  *
639  * NOTES
640  *  pUnixFolder also carries the information if the path is expected to be unix or dos.
641  */
642 static BOOL UNIXFS_path_to_pidl(UnixFolder *pUnixFolder, const WCHAR *path, LPITEMIDLIST *ppidl) {
643     LPITEMIDLIST pidl;
644     int cPidlLen, cPathLen;
645     char *pSlash, *pNextSlash, szCompletePath[FILENAME_MAX], *pNextPathElement, *pszAPath;
646     WCHAR *pwszPath;
647
648     TRACE("pUnixFolder=%p, path=%s, ppidl=%p\n", pUnixFolder, debugstr_w(path), ppidl);
649    
650     if (!ppidl || !path)
651         return FALSE;
652
653     /* Build an absolute path and let pNextPathElement point to the interesting 
654      * relative sub-path. We need the absolute path to call 'stat', but the pidl
655      * will only contain the relative part.
656      */
657     if ((pUnixFolder->m_dwPathMode == PATHMODE_DOS) && (path[1] == ':')) 
658     {
659         /* Absolute dos path. Convert to unix */
660         if (!UNIXFS_get_unix_path(path, szCompletePath))
661             return FALSE;
662         pNextPathElement = szCompletePath;
663     } 
664     else if ((pUnixFolder->m_dwPathMode == PATHMODE_UNIX) && (path[0] == '/')) 
665     {
666         /* Absolute unix path. Just convert to ANSI. */
667         WideCharToMultiByte(CP_UNIXCP, 0, path, -1, szCompletePath, FILENAME_MAX, NULL, NULL); 
668         pNextPathElement = szCompletePath;
669     } 
670     else 
671     {
672         /* Relative dos or unix path. Concat with this folder's path */
673         int cBasePathLen = strlen(pUnixFolder->m_pszPath);
674         memcpy(szCompletePath, pUnixFolder->m_pszPath, cBasePathLen);
675         WideCharToMultiByte(CP_UNIXCP, 0, path, -1, szCompletePath + cBasePathLen, 
676                             FILENAME_MAX - cBasePathLen, NULL, NULL);
677         pNextPathElement = szCompletePath + cBasePathLen - 1;
678         
679         /* If in dos mode, replace '\' with '/' */
680         if (pUnixFolder->m_dwPathMode == PATHMODE_DOS) {
681             char *pBackslash = strchr(pNextPathElement, '\\');
682             while (pBackslash) {
683                 *pBackslash = '/';
684                 pBackslash = strchr(pBackslash, '\\');
685             }
686         }
687     }
688
689     /* Special case for the root folder. */
690     if (!strcmp(szCompletePath, "/")) {
691         *ppidl = pidl = (LPITEMIDLIST)SHAlloc(sizeof(USHORT));
692         if (!pidl) return FALSE;
693         pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
694         return TRUE;
695     }
696     
697     /* Remove trailing slash, if present */
698     cPathLen = strlen(szCompletePath);
699     if (szCompletePath[cPathLen-1] == '/') 
700         szCompletePath[cPathLen-1] = '\0';
701
702     if ((szCompletePath[0] != '/') || (pNextPathElement[0] != '/')) {
703         ERR("szCompletePath: %s, pNextPathElment: %s\n", szCompletePath, pNextPathElement);
704         return FALSE;
705     }
706     
707     /* At this point, we have an absolute unix path in szCompletePath 
708      * and the relative portion of it in pNextPathElement. Both starting with '/'
709      * and _not_ terminated by a '/'. */
710     TRACE("complete path: %s, relative path: %s\n", szCompletePath, pNextPathElement);
711     
712     /* Convert to CP_ACP and WCHAR */
713     if (!UNIXFS_shitemid_len_from_filename(pNextPathElement, &pszAPath, &pwszPath))
714         return 0;
715
716     /* Compute the length of the complete ITEMIDLIST */
717     cPidlLen = 0;
718     pSlash = pszAPath;
719     while (pSlash) {
720         pNextSlash = strchr(pSlash+1, '/');
721         cPidlLen += LEN_SHITEMID_FIXED_PART + /* Fixed part length plus potential alignment byte. */
722             (pNextSlash ? (pNextSlash - pSlash) & 0x1 : lstrlenA(pSlash) & 0x1); 
723         pSlash = pNextSlash;
724     }
725
726     /* The USHORT is for the ITEMIDLIST terminator. The NUL terminators for the sub-path-strings
727      * are accounted for by the '/' separators, which are not stored in the SHITEMIDs. Above we
728      * have ensured that the number of '/'s exactly matches the number of sub-path-strings. */
729     cPidlLen += lstrlenA(pszAPath) + lstrlenW(pwszPath) * sizeof(WCHAR) + sizeof(USHORT);
730
731     SHFree(pszAPath);
732     SHFree(pwszPath);
733
734     *ppidl = pidl = (LPITEMIDLIST)SHAlloc(cPidlLen);
735     if (!pidl) return FALSE;
736
737     /* Concatenate the SHITEMIDs of the sub-directories. */
738     while (*pNextPathElement) {
739         pSlash = strchr(pNextPathElement+1, '/');
740         if (pSlash) *pSlash = '\0';
741         pNextPathElement = UNIXFS_build_shitemid(szCompletePath, pidl);
742         if (pSlash) *pSlash = '/';
743             
744         if (!pNextPathElement) {
745             SHFree(*ppidl);
746             return FALSE;
747         }
748         pidl = ILGetNext(pidl);
749     }
750     pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
751
752     if ((char *)pidl-(char *)*ppidl+sizeof(USHORT) != cPidlLen) /* We've corrupted the heap :( */ 
753         ERR("Computed length of pidl incorrect. Please report.\n");
754     
755     return TRUE;
756 }
757
758 /******************************************************************************
759  * UNIXFS_initialize_target_folder [Internal]
760  *
761  *  Initialize the m_pszPath member of an UnixFolder, given an absolute unix
762  *  base path and a relative ITEMIDLIST. Leave the m_pidlLocation member, which
763  *  specifies the location in the shell namespace alone. 
764  * 
765  * PARAMS
766  *  This          [IO] The UnixFolder, whose target path is to be initialized
767  *  szBasePath    [I]  The absolute base path
768  *  pidlSubFolder [I]  Relative part of the path, given as an ITEMIDLIST
769  *  dwAttributes  [I]  Attributes to add to the Folders m_dwAttributes member 
770  *                     (Used to pass the SFGAO_FILESYSTEM flag down the path)
771  * RETURNS
772  *  Success: S_OK,
773  *  Failure: E_FAIL
774  */
775 static HRESULT UNIXFS_initialize_target_folder(UnixFolder *This, const char *szBasePath,
776     LPCITEMIDLIST pidlSubFolder, DWORD dwAttributes)
777 {
778     LPCITEMIDLIST current = pidlSubFolder;
779     DWORD dwPathLen = strlen(szBasePath)+1;
780     struct stat statPrefix;
781     char *pNextDir;
782         
783     /* Determine the path's length bytes */
784     while (current && current->mkid.cb) {
785         dwPathLen += UNIXFS_filename_from_shitemid(current, NULL) + 1; /* For the '/' */
786         current = ILGetNext(current);
787     };
788
789     /* Build the path and compute the attributes*/
790     This->m_dwAttributes = 
791             dwAttributes|SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|SFGAO_CANRENAME;
792     This->m_pszPath = pNextDir = SHAlloc(dwPathLen);
793     if (!This->m_pszPath) {
794         WARN("SHAlloc failed!\n");
795         return E_FAIL;
796     }
797     current = pidlSubFolder;
798     strcpy(pNextDir, szBasePath);
799     pNextDir += strlen(szBasePath);
800     if (This->m_dwPathMode == PATHMODE_UNIX || IsEqualCLSID(&CLSID_MyDocuments, This->m_pCLSID))
801         This->m_dwAttributes |= SFGAO_FILESYSTEM;
802     if (!(This->m_dwAttributes & SFGAO_FILESYSTEM)) {
803         *pNextDir = '\0';
804         if (!stat(This->m_pszPath, &statPrefix) && UNIXFS_is_dos_device(&statPrefix))
805             This->m_dwAttributes |= SFGAO_FILESYSTEM;
806     }
807     while (current && current->mkid.cb) {
808         pNextDir += UNIXFS_filename_from_shitemid(current, pNextDir);
809         if (!(This->m_dwAttributes & SFGAO_FILESYSTEM)) {
810             *pNextDir = '\0';
811             if (!stat(This->m_pszPath, &statPrefix) && UNIXFS_is_dos_device(&statPrefix))
812                 This->m_dwAttributes |= SFGAO_FILESYSTEM;
813         }
814         *pNextDir++ = '/';
815         current = ILGetNext(current);
816     }
817     *pNextDir='\0';
818  
819     return S_OK;
820 }
821
822 /******************************************************************************
823  * UnixFolder
824  *
825  * Class whose heap based instances represent unix filesystem directories.
826  */
827
828 static void UnixFolder_Destroy(UnixFolder *pUnixFolder) {
829     TRACE("(pUnixFolder=%p)\n", pUnixFolder);
830     
831     SHFree(pUnixFolder->m_pszPath);
832     ILFree(pUnixFolder->m_pidlLocation);
833     SHFree(pUnixFolder);
834 }
835
836 static HRESULT WINAPI UnixFolder_IShellFolder2_QueryInterface(IShellFolder2 *iface, REFIID riid, 
837     void **ppv) 
838 {
839     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
840         
841     TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
842     
843     if (!ppv) return E_INVALIDARG;
844     
845     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IShellFolder, riid) || 
846         IsEqualIID(&IID_IShellFolder2, riid)) 
847     {
848         *ppv = STATIC_CAST(IShellFolder2, This);
849     } else if (IsEqualIID(&IID_IPersistFolder3, riid) || IsEqualIID(&IID_IPersistFolder2, riid) || 
850                IsEqualIID(&IID_IPersistFolder, riid) || IsEqualIID(&IID_IPersist, riid)) 
851     {
852         *ppv = STATIC_CAST(IPersistFolder3, This);
853     } else if (IsEqualIID(&IID_IPersistPropertyBag, riid)) {
854         *ppv = STATIC_CAST(IPersistPropertyBag, This);
855     } else if (IsEqualIID(&IID_ISFHelper, riid)) {
856         *ppv = STATIC_CAST(ISFHelper, This);
857     } else if (IsEqualIID(&IID_IDropTarget, riid)) {
858         *ppv = STATIC_CAST(IDropTarget, This);
859         if (!cfShellIDList) 
860             cfShellIDList = RegisterClipboardFormatA(CFSTR_SHELLIDLIST);
861     } else {
862         *ppv = NULL;
863         return E_NOINTERFACE;
864     }
865
866     IUnknown_AddRef((IUnknown*)*ppv);
867     return S_OK;
868 }
869
870 static ULONG WINAPI UnixFolder_IShellFolder2_AddRef(IShellFolder2 *iface) {
871     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
872
873     TRACE("(iface=%p)\n", iface);
874
875     return InterlockedIncrement(&This->m_cRef);
876 }
877
878 static ULONG WINAPI UnixFolder_IShellFolder2_Release(IShellFolder2 *iface) {
879     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
880     ULONG cRef;
881     
882     TRACE("(iface=%p)\n", iface);
883
884     cRef = InterlockedDecrement(&This->m_cRef);
885     
886     if (!cRef) 
887         UnixFolder_Destroy(This);
888
889     return cRef;
890 }
891
892 static HRESULT WINAPI UnixFolder_IShellFolder2_ParseDisplayName(IShellFolder2* iface, HWND hwndOwner, 
893     LPBC pbcReserved, LPOLESTR lpszDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl, 
894     ULONG* pdwAttributes)
895 {
896     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
897     BOOL result;
898
899     TRACE("(iface=%p, hwndOwner=%p, pbcReserved=%p, lpszDisplayName=%s, pchEaten=%p, ppidl=%p, "
900           "pdwAttributes=%p) stub\n", iface, hwndOwner, pbcReserved, debugstr_w(lpszDisplayName), 
901           pchEaten, ppidl, pdwAttributes);
902
903     result = UNIXFS_path_to_pidl(This, lpszDisplayName, ppidl);
904     if (result && pdwAttributes && *pdwAttributes)
905     {
906         IShellFolder *pParentSF;
907         LPCITEMIDLIST pidlLast;
908         LPITEMIDLIST pidlComplete = ILCombine(This->m_pidlLocation, *ppidl);
909         HRESULT hr;
910         
911         hr = SHBindToParent(pidlComplete, &IID_IShellFolder, (LPVOID*)&pParentSF, &pidlLast);
912         if (FAILED(hr)) {
913             FIXME("SHBindToParent failed! hr = %08lx\n", hr);
914             ILFree(pidlComplete);
915             return E_FAIL;
916         }
917         IShellFolder_GetAttributesOf(pParentSF, 1, &pidlLast, pdwAttributes);
918         IShellFolder_Release(pParentSF);
919         ILFree(pidlComplete);
920     }
921
922     if (!result) TRACE("FAILED!\n");
923     return result ? S_OK : E_FAIL;
924 }
925
926 static IUnknown *UnixSubFolderIterator_Constructor(UnixFolder *pUnixFolder, SHCONTF fFilter);
927
928 static HRESULT WINAPI UnixFolder_IShellFolder2_EnumObjects(IShellFolder2* iface, HWND hwndOwner, 
929     SHCONTF grfFlags, IEnumIDList** ppEnumIDList)
930 {
931     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
932     IUnknown *newIterator;
933     HRESULT hr;
934     
935     TRACE("(iface=%p, hwndOwner=%p, grfFlags=%08lx, ppEnumIDList=%p)\n", 
936             iface, hwndOwner, grfFlags, ppEnumIDList);
937
938     if (!This->m_pszPath) {
939         WARN("EnumObjects called on uninitialized UnixFolder-object!\n");
940         return E_UNEXPECTED;
941     }
942
943     newIterator = UnixSubFolderIterator_Constructor(This, grfFlags);
944     hr = IUnknown_QueryInterface(newIterator, &IID_IEnumIDList, (void**)ppEnumIDList);
945     IUnknown_Release(newIterator);
946     
947     return hr;
948 }
949
950 static HRESULT CreateUnixFolder(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv, const CLSID *pCLSID);
951
952 static HRESULT WINAPI UnixFolder_IShellFolder2_BindToObject(IShellFolder2* iface, LPCITEMIDLIST pidl,
953     LPBC pbcReserved, REFIID riid, void** ppvOut)
954 {
955     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
956     IPersistFolder3 *persistFolder;
957     HRESULT hr;
958     const CLSID *clsidChild;
959         
960     TRACE("(iface=%p, pidl=%p, pbcReserver=%p, riid=%p, ppvOut=%p)\n", 
961             iface, pidl, pbcReserved, riid, ppvOut);
962
963     if (!pidl || !pidl->mkid.cb)
964         return E_INVALIDARG;
965    
966     if (IsEqualCLSID(This->m_pCLSID, &CLSID_FolderShortcut)) {
967         /* Children of FolderShortcuts are ShellFSFolders on Windows. 
968          * Unixfs' counterpart is UnixDosFolder. */
969         clsidChild = &CLSID_UnixDosFolder;    
970     } else {
971         clsidChild = This->m_pCLSID;
972     }
973     
974     hr = CreateUnixFolder(NULL, &IID_IPersistFolder3, (void**)&persistFolder, clsidChild);
975     if (!SUCCEEDED(hr)) return hr;
976     hr = IPersistFolder_QueryInterface(persistFolder, riid, (void**)ppvOut);
977    
978     if (SUCCEEDED(hr)) {
979         UnixFolder *subfolder = ADJUST_THIS(UnixFolder, IPersistFolder3, persistFolder);
980         subfolder->m_pidlLocation = ILCombine(This->m_pidlLocation, pidl);
981         hr = UNIXFS_initialize_target_folder(subfolder, This->m_pszPath, pidl,
982                                              This->m_dwAttributes & SFGAO_FILESYSTEM);
983     } 
984
985     IPersistFolder3_Release(persistFolder);
986     
987     return hr;
988 }
989
990 static HRESULT WINAPI UnixFolder_IShellFolder2_BindToStorage(IShellFolder2* This, LPCITEMIDLIST pidl, 
991     LPBC pbcReserved, REFIID riid, void** ppvObj)
992 {
993     FIXME("stub\n");
994     return E_NOTIMPL;
995 }
996
997 static HRESULT WINAPI UnixFolder_IShellFolder2_CompareIDs(IShellFolder2* iface, LPARAM lParam, 
998     LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
999 {
1000     BOOL isEmpty1, isEmpty2;
1001     HRESULT hr = E_FAIL;
1002     LPITEMIDLIST firstpidl;
1003     IShellFolder2 *psf;
1004     int compare;
1005
1006     TRACE("(iface=%p, lParam=%ld, pidl1=%p, pidl2=%p)\n", iface, lParam, pidl1, pidl2);
1007     
1008     isEmpty1 = !pidl1 || !pidl1->mkid.cb;
1009     isEmpty2 = !pidl2 || !pidl2->mkid.cb;
1010
1011     if (isEmpty1 && isEmpty2) 
1012         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
1013     else if (isEmpty1)
1014         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
1015     else if (isEmpty2)
1016         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
1017
1018     if (_ILIsFolder(pidl1) && !_ILIsFolder(pidl2)) 
1019         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
1020     if (!_ILIsFolder(pidl1) && _ILIsFolder(pidl2))
1021         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
1022
1023     compare = CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE, 
1024                              _ILGetTextPointer(pidl1), -1,
1025                              _ILGetTextPointer(pidl2), -1);
1026     
1027     if ((compare == CSTR_LESS_THAN) || (compare == CSTR_GREATER_THAN)) 
1028         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)((compare == CSTR_LESS_THAN)?-1:1));
1029
1030     if (pidl1->mkid.cb < pidl2->mkid.cb)
1031         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
1032     else if (pidl1->mkid.cb > pidl2->mkid.cb)
1033         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
1034
1035     firstpidl = ILCloneFirst(pidl1);
1036     pidl1 = ILGetNext(pidl1);
1037     pidl2 = ILGetNext(pidl2);
1038     
1039     hr = IShellFolder2_BindToObject(iface, firstpidl, NULL, &IID_IShellFolder, (LPVOID*)&psf);
1040     if (SUCCEEDED(hr)) {
1041         hr = IShellFolder_CompareIDs(psf, lParam, pidl1, pidl2);
1042         IShellFolder2_Release(psf);
1043     }
1044
1045     ILFree(firstpidl);
1046     return hr;
1047 }
1048
1049 static HRESULT WINAPI UnixFolder_IShellFolder2_CreateViewObject(IShellFolder2* iface, HWND hwndOwner,
1050     REFIID riid, void** ppv)
1051 {
1052     HRESULT hr = E_INVALIDARG;
1053         
1054     TRACE("(iface=%p, hwndOwner=%p, riid=%p, ppv=%p) stub\n", iface, hwndOwner, riid, ppv);
1055     
1056     if (!ppv) return E_INVALIDARG;
1057     *ppv = NULL;
1058     
1059     if (IsEqualIID(&IID_IShellView, riid)) {
1060         LPSHELLVIEW pShellView;
1061         
1062         pShellView = IShellView_Constructor((IShellFolder*)iface);
1063         if (pShellView) {
1064             hr = IShellView_QueryInterface(pShellView, riid, ppv);
1065             IShellView_Release(pShellView);
1066         }
1067     } else if (IsEqualIID(&IID_IDropTarget, riid)) {
1068         hr = IShellFolder2_QueryInterface(iface, &IID_IDropTarget, ppv);
1069     }
1070     
1071     return hr;
1072 }
1073
1074 static HRESULT WINAPI UnixFolder_IShellFolder2_GetAttributesOf(IShellFolder2* iface, UINT cidl, 
1075     LPCITEMIDLIST* apidl, SFGAOF* rgfInOut)
1076 {
1077     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
1078     HRESULT hr = S_OK;
1079         
1080     TRACE("(iface=%p, cidl=%u, apidl=%p, rgfInOut=%p)\n", iface, cidl, apidl, rgfInOut);
1081  
1082     if (!rgfInOut || (cidl && !apidl)) 
1083         return E_INVALIDARG;
1084     
1085     if (cidl == 0) {
1086         *rgfInOut &= This->m_dwAttributes;
1087     } else {
1088         char szAbsolutePath[FILENAME_MAX], *pszRelativePath;
1089         UINT i;
1090
1091         *rgfInOut = SFGAO_CANCOPY|SFGAO_CANMOVE|SFGAO_CANLINK|SFGAO_CANRENAME|SFGAO_CANDELETE|
1092                     SFGAO_HASPROPSHEET|SFGAO_DROPTARGET|SFGAO_FILESYSTEM;
1093         lstrcpyA(szAbsolutePath, This->m_pszPath);
1094         pszRelativePath = szAbsolutePath + lstrlenA(szAbsolutePath);
1095         for (i=0; i<cidl; i++) {
1096             if (!(This->m_dwAttributes & SFGAO_FILESYSTEM)) {
1097                 struct stat fileStat;
1098                 if (!UNIXFS_filename_from_shitemid(apidl[i], pszRelativePath)) 
1099                     return E_INVALIDARG;
1100                 if (stat(szAbsolutePath, &fileStat) || !UNIXFS_is_dos_device(&fileStat))
1101                     *rgfInOut &= ~SFGAO_FILESYSTEM;
1102             }
1103             if (_ILIsFolder(apidl[i])) 
1104                 *rgfInOut |= SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR;
1105         }
1106     }
1107     
1108     return hr;
1109 }
1110
1111 static HRESULT WINAPI UnixFolder_IShellFolder2_GetUIObjectOf(IShellFolder2* iface, HWND hwndOwner, 
1112     UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void** ppvOut)
1113 {
1114     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
1115     UINT i;
1116     
1117     TRACE("(iface=%p, hwndOwner=%p, cidl=%d, apidl=%p, riid=%s, prgfInOut=%p, ppv=%p)\n",
1118         iface, hwndOwner, cidl, apidl, debugstr_guid(riid), prgfInOut, ppvOut);
1119
1120     if (!cidl || !apidl || !riid || !ppvOut) 
1121         return E_INVALIDARG;
1122
1123     for (i=0; i<cidl; i++) 
1124         if (!apidl[i]) 
1125             return E_INVALIDARG;
1126     
1127     if (IsEqualIID(&IID_IContextMenu, riid)) {
1128         *ppvOut = ISvItemCm_Constructor((IShellFolder*)iface, This->m_pidlLocation, apidl, cidl);
1129         return S_OK;
1130     } else if (IsEqualIID(&IID_IDataObject, riid)) {
1131         *ppvOut = IDataObject_Constructor(hwndOwner, This->m_pidlLocation, apidl, cidl);
1132         return S_OK;
1133     } else if (IsEqualIID(&IID_IExtractIconA, riid)) {
1134         LPITEMIDLIST pidl;
1135         if (cidl != 1) return E_INVALIDARG;
1136         pidl = ILCombine(This->m_pidlLocation, apidl[0]);
1137         *ppvOut = (LPVOID)IExtractIconA_Constructor(pidl);
1138         SHFree(pidl);
1139         return S_OK;
1140     } else if (IsEqualIID(&IID_IExtractIconW, riid)) {
1141         LPITEMIDLIST pidl;
1142         if (cidl != 1) return E_INVALIDARG;
1143         pidl = ILCombine(This->m_pidlLocation, apidl[0]);
1144         *ppvOut = (LPVOID)IExtractIconW_Constructor(pidl);
1145         SHFree(pidl);
1146         return S_OK;
1147     } else if (IsEqualIID(&IID_IDropTarget, riid)) {
1148         if (cidl != 1) return E_INVALIDARG;
1149         return IShellFolder2_BindToObject(iface, apidl[0], NULL, &IID_IDropTarget, ppvOut);
1150     } else if (IsEqualIID(&IID_IShellLinkW, riid)) {
1151         FIXME("IShellLinkW\n");
1152         return E_FAIL;
1153     } else if (IsEqualIID(&IID_IShellLinkA, riid)) {
1154         FIXME("IShellLinkA\n");
1155         return E_FAIL;
1156     } else {
1157         FIXME("Unknown interface %s in GetUIObjectOf\n", debugstr_guid(riid));
1158         return E_NOINTERFACE;
1159     }
1160 }
1161
1162 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDisplayNameOf(IShellFolder2* iface, 
1163     LPCITEMIDLIST pidl, SHGDNF uFlags, STRRET* lpName)
1164 {
1165     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
1166     HRESULT hr = S_OK;    
1167
1168     TRACE("(iface=%p, pidl=%p, uFlags=%lx, lpName=%p)\n", iface, pidl, uFlags, lpName);
1169     
1170     if ((GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING) &&
1171         (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER))
1172     {
1173         if (!pidl || !pidl->mkid.cb) {
1174             lpName->uType = STRRET_WSTR;
1175             if (This->m_dwPathMode == PATHMODE_UNIX) {
1176                 UINT len = MultiByteToWideChar(CP_UNIXCP, 0, This->m_pszPath, -1, NULL, 0);
1177                 lpName->u.pOleStr = SHAlloc(len * sizeof(WCHAR));
1178                 if (!lpName->u.pOleStr) return HRESULT_FROM_WIN32(GetLastError());
1179                 MultiByteToWideChar(CP_UNIXCP, 0, This->m_pszPath, -1, lpName->u.pOleStr, len);
1180             } else {
1181                 LPWSTR pwszDosFileName = wine_get_dos_file_name(This->m_pszPath);
1182                 if (!pwszDosFileName) return HRESULT_FROM_WIN32(GetLastError());
1183                 lpName->u.pOleStr = SHAlloc((lstrlenW(pwszDosFileName) + 1) * sizeof(WCHAR));
1184                 if (!lpName->u.pOleStr) return HRESULT_FROM_WIN32(GetLastError());
1185                 lstrcpyW(lpName->u.pOleStr, pwszDosFileName);
1186                 PathRemoveBackslashW(lpName->u.pOleStr);
1187                 HeapFree(GetProcessHeap(), 0, pwszDosFileName);
1188             }
1189         } else {
1190             IShellFolder *pSubFolder;
1191             SHITEMID emptyIDL = { 0, { 0 } };
1192
1193             hr = IShellFolder_BindToObject(iface, pidl, NULL, &IID_IShellFolder, (void**)&pSubFolder);
1194             if (!SUCCEEDED(hr)) return hr;
1195        
1196             hr = IShellFolder_GetDisplayNameOf(pSubFolder, (LPITEMIDLIST)&emptyIDL, uFlags, lpName);
1197             IShellFolder_Release(pSubFolder);
1198         }
1199     } else {
1200         WCHAR wszFileName[MAX_PATH];
1201         if (!_ILSimpleGetTextW(pidl, wszFileName, MAX_PATH)) return E_INVALIDARG;
1202         lpName->uType = STRRET_WSTR;
1203         lpName->u.pOleStr = SHAlloc((lstrlenW(wszFileName)+1)*sizeof(WCHAR));
1204         if (!lpName->u.pOleStr) return HRESULT_FROM_WIN32(GetLastError());
1205         lstrcpyW(lpName->u.pOleStr, wszFileName);
1206         if (!(GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING) && This->m_dwPathMode == PATHMODE_DOS && 
1207             !_ILIsFolder(pidl) && wszFileName[0] != '.' && SHELL_FS_HideExtension(wszFileName))
1208         {
1209             PathRemoveExtensionW(lpName->u.pOleStr);
1210         }
1211     }
1212
1213     TRACE("--> %s\n", debugstr_w(lpName->u.pOleStr));
1214     
1215     return hr;
1216 }
1217
1218 static HRESULT WINAPI UnixFolder_IShellFolder2_SetNameOf(IShellFolder2* iface, HWND hwnd, 
1219     LPCITEMIDLIST pidl, LPCOLESTR lpcwszName, SHGDNF uFlags, LPITEMIDLIST* ppidlOut)
1220 {
1221     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
1222
1223     static const WCHAR awcInvalidChars[] = { '\\', '/', ':', '*', '?', '"', '<', '>', '|' };
1224     char szSrc[FILENAME_MAX], szDest[FILENAME_MAX];
1225     WCHAR wszSrcRelative[MAX_PATH];
1226     int cBasePathLen = lstrlenA(This->m_pszPath), i;
1227     struct stat statDest;
1228     LPITEMIDLIST pidlSrc, pidlDest, pidlRelativeDest;
1229     LPOLESTR lpwszName;
1230     HRESULT hr;
1231    
1232     TRACE("(iface=%p, hwnd=%p, pidl=%p, lpcwszName=%s, uFlags=0x%08lx, ppidlOut=%p)\n",
1233           iface, hwnd, pidl, debugstr_w(lpcwszName), uFlags, ppidlOut); 
1234
1235     /* prepare to fail */
1236     if (ppidlOut)
1237         *ppidlOut = NULL;
1238     
1239     /* pidl has to contain a single non-empty SHITEMID */
1240     if (_ILIsDesktop(pidl) || !_ILIsPidlSimple(pidl) || !_ILGetTextPointer(pidl))
1241         return E_INVALIDARG;
1242  
1243     /* check for invalid characters in lpcwszName. */
1244     for (i=0; i < sizeof(awcInvalidChars)/sizeof(*awcInvalidChars); i++)
1245         if (StrChrW(lpcwszName, awcInvalidChars[i]))
1246             return HRESULT_FROM_WIN32(ERROR_CANCELLED);
1247
1248     /* build source path */
1249     memcpy(szSrc, This->m_pszPath, cBasePathLen);
1250     UNIXFS_filename_from_shitemid(pidl, szSrc + cBasePathLen);
1251
1252     /* build destination path */
1253     memcpy(szDest, This->m_pszPath, cBasePathLen);
1254     WideCharToMultiByte(CP_UNIXCP, 0, lpcwszName, -1, szDest+cBasePathLen, 
1255                         FILENAME_MAX-cBasePathLen, NULL, NULL);
1256
1257     /* If the filename's extension is hidden to the user, we have to append it. */
1258     if (!(uFlags & SHGDN_FORPARSING) && 
1259         _ILSimpleGetTextW(pidl, wszSrcRelative, MAX_PATH) && 
1260         SHELL_FS_HideExtension(wszSrcRelative))
1261     {
1262         WCHAR *pwszExt = PathFindExtensionW(wszSrcRelative);
1263         int cLenDest = strlen(szDest);
1264         WideCharToMultiByte(CP_UNIXCP, 0, pwszExt, -1, szDest + cLenDest, 
1265             FILENAME_MAX - cLenDest, NULL, NULL);
1266     }
1267
1268     TRACE("src=%s dest=%s\n", szSrc, szDest);
1269
1270     /* Fail, if destination does already exist */
1271     if (!stat(szDest, &statDest)) 
1272         return E_FAIL;
1273
1274     /* Rename the file */
1275     if (rename(szSrc, szDest)) 
1276         return E_FAIL;
1277     
1278     /* Build a pidl for the path of the renamed file */
1279     lpwszName = SHAlloc((lstrlenW(lpcwszName)+1)*sizeof(WCHAR)); /* due to const correctness. */
1280     lstrcpyW(lpwszName, lpcwszName);
1281     hr = IShellFolder2_ParseDisplayName(iface, NULL, NULL, lpwszName, NULL, &pidlRelativeDest, NULL);
1282     SHFree(lpwszName);
1283     if (FAILED(hr)) {
1284         rename(szDest, szSrc); /* Undo the renaming */
1285         return E_FAIL;
1286     }
1287     pidlDest = ILCombine(This->m_pidlLocation, pidlRelativeDest);
1288     ILFree(pidlRelativeDest);
1289     pidlSrc = ILCombine(This->m_pidlLocation, pidl);
1290     
1291     /* Inform the shell */
1292     if (_ILIsFolder(ILFindLastID(pidlDest))) 
1293         SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_IDLIST, pidlSrc, pidlDest);
1294     else 
1295         SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_IDLIST, pidlSrc, pidlDest);
1296     
1297     if (ppidlOut) 
1298         *ppidlOut = ILClone(ILFindLastID(pidlDest));
1299         
1300     ILFree(pidlSrc);
1301     ILFree(pidlDest);
1302     
1303     return S_OK;
1304 }
1305
1306 static HRESULT WINAPI UnixFolder_IShellFolder2_EnumSearches(IShellFolder2* iface, 
1307     IEnumExtraSearch **ppEnum) 
1308 {
1309     FIXME("stub\n");
1310     return E_NOTIMPL;
1311 }
1312
1313 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumn(IShellFolder2* iface, 
1314     DWORD dwReserved, ULONG *pSort, ULONG *pDisplay) 
1315 {
1316     FIXME("stub\n");
1317     return E_NOTIMPL;
1318 }
1319
1320 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumnState(IShellFolder2* iface, 
1321     UINT iColumn, SHCOLSTATEF *pcsFlags)
1322 {
1323     FIXME("stub\n");
1324     return E_NOTIMPL;
1325 }
1326
1327 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultSearchGUID(IShellFolder2* iface, 
1328     GUID *pguid)
1329 {
1330     FIXME("stub\n");
1331     return E_NOTIMPL;
1332 }
1333
1334 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsEx(IShellFolder2* iface, 
1335     LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
1336 {
1337     FIXME("stub\n");
1338     return E_NOTIMPL;
1339 }
1340
1341 #define SHELLVIEWCOLUMNS 7 
1342
1343 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsOf(IShellFolder2* iface, 
1344     LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *psd)
1345 {
1346     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
1347     HRESULT hr = E_FAIL;
1348     struct passwd *pPasswd;
1349     struct group *pGroup;
1350     static const shvheader SFHeader[SHELLVIEWCOLUMNS] = {
1351         {IDS_SHV_COLUMN1,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
1352         {IDS_SHV_COLUMN2,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
1353         {IDS_SHV_COLUMN3,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
1354         {IDS_SHV_COLUMN4,  SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 12},
1355         {IDS_SHV_COLUMN5,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 9},
1356         {IDS_SHV_COLUMN10, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7},
1357         {IDS_SHV_COLUMN11, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7}
1358     };
1359
1360     TRACE("(iface=%p, pidl=%p, iColumn=%d, psd=%p) stub\n", iface, pidl, iColumn, psd);
1361     
1362     if (!psd || iColumn >= SHELLVIEWCOLUMNS)
1363         return E_INVALIDARG;
1364
1365     if (!pidl) {
1366         psd->fmt = SFHeader[iColumn].fmt;
1367         psd->cxChar = SFHeader[iColumn].cxChar;
1368         psd->str.uType = STRRET_CSTR;
1369         LoadStringA(shell32_hInstance, SFHeader[iColumn].colnameid, psd->str.u.cStr, MAX_PATH);
1370         return S_OK;
1371     } else {
1372         struct stat statItem;
1373         if (iColumn == 4 || iColumn == 5 || iColumn == 6) {
1374             char szPath[FILENAME_MAX];
1375             strcpy(szPath, This->m_pszPath);
1376             if (!UNIXFS_filename_from_shitemid(pidl, szPath + strlen(szPath)))
1377                 return E_INVALIDARG;
1378             if (stat(szPath, &statItem)) 
1379                 return E_INVALIDARG;
1380         }
1381         psd->str.u.cStr[0] = '\0';
1382         psd->str.uType = STRRET_CSTR;
1383         switch (iColumn) {
1384             case 0:
1385                 hr = IShellFolder2_GetDisplayNameOf(iface, pidl, SHGDN_NORMAL|SHGDN_INFOLDER, &psd->str);
1386                 break;
1387             case 1:
1388                 _ILGetFileSize(pidl, psd->str.u.cStr, MAX_PATH);
1389                 break;
1390             case 2:
1391                 _ILGetFileType (pidl, psd->str.u.cStr, MAX_PATH);
1392                 break;
1393             case 3:
1394                 _ILGetFileDate(pidl, psd->str.u.cStr, MAX_PATH);
1395                 break;
1396             case 4:
1397                 psd->str.u.cStr[0] = S_ISDIR(statItem.st_mode) ? 'd' : '-';
1398                 psd->str.u.cStr[1] = (statItem.st_mode & S_IRUSR) ? 'r' : '-';
1399                 psd->str.u.cStr[2] = (statItem.st_mode & S_IWUSR) ? 'w' : '-';
1400                 psd->str.u.cStr[3] = (statItem.st_mode & S_IXUSR) ? 'x' : '-';
1401                 psd->str.u.cStr[4] = (statItem.st_mode & S_IRGRP) ? 'r' : '-';
1402                 psd->str.u.cStr[5] = (statItem.st_mode & S_IWGRP) ? 'w' : '-';
1403                 psd->str.u.cStr[6] = (statItem.st_mode & S_IXGRP) ? 'x' : '-';
1404                 psd->str.u.cStr[7] = (statItem.st_mode & S_IROTH) ? 'r' : '-';
1405                 psd->str.u.cStr[8] = (statItem.st_mode & S_IWOTH) ? 'w' : '-';
1406                 psd->str.u.cStr[9] = (statItem.st_mode & S_IXOTH) ? 'x' : '-';
1407                 psd->str.u.cStr[10] = '\0';
1408                 break;
1409             case 5:
1410                 pPasswd = getpwuid(statItem.st_uid);
1411                 if (pPasswd) strcpy(psd->str.u.cStr, pPasswd->pw_name);
1412                 break;
1413             case 6:
1414                 pGroup = getgrgid(statItem.st_gid);
1415                 if (pGroup) strcpy(psd->str.u.cStr, pGroup->gr_name);
1416                 break;
1417         }
1418     }
1419     
1420     return hr;
1421 }
1422
1423 static HRESULT WINAPI UnixFolder_IShellFolder2_MapColumnToSCID(IShellFolder2* iface, UINT iColumn,
1424     SHCOLUMNID *pscid) 
1425 {
1426     FIXME("stub\n");
1427     return E_NOTIMPL;
1428 }
1429
1430 /* VTable for UnixFolder's IShellFolder2 interface.
1431  */
1432 static const IShellFolder2Vtbl UnixFolder_IShellFolder2_Vtbl = {
1433     UnixFolder_IShellFolder2_QueryInterface,
1434     UnixFolder_IShellFolder2_AddRef,
1435     UnixFolder_IShellFolder2_Release,
1436     UnixFolder_IShellFolder2_ParseDisplayName,
1437     UnixFolder_IShellFolder2_EnumObjects,
1438     UnixFolder_IShellFolder2_BindToObject,
1439     UnixFolder_IShellFolder2_BindToStorage,
1440     UnixFolder_IShellFolder2_CompareIDs,
1441     UnixFolder_IShellFolder2_CreateViewObject,
1442     UnixFolder_IShellFolder2_GetAttributesOf,
1443     UnixFolder_IShellFolder2_GetUIObjectOf,
1444     UnixFolder_IShellFolder2_GetDisplayNameOf,
1445     UnixFolder_IShellFolder2_SetNameOf,
1446     UnixFolder_IShellFolder2_GetDefaultSearchGUID,
1447     UnixFolder_IShellFolder2_EnumSearches,
1448     UnixFolder_IShellFolder2_GetDefaultColumn,
1449     UnixFolder_IShellFolder2_GetDefaultColumnState,
1450     UnixFolder_IShellFolder2_GetDetailsEx,
1451     UnixFolder_IShellFolder2_GetDetailsOf,
1452     UnixFolder_IShellFolder2_MapColumnToSCID
1453 };
1454
1455 static HRESULT WINAPI UnixFolder_IPersistFolder3_QueryInterface(IPersistFolder3* iface, REFIID riid, 
1456     void** ppvObject)
1457 {
1458     return UnixFolder_IShellFolder2_QueryInterface(
1459         STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistFolder3, iface)), riid, ppvObject);
1460 }
1461
1462 static ULONG WINAPI UnixFolder_IPersistFolder3_AddRef(IPersistFolder3* iface)
1463 {
1464     return UnixFolder_IShellFolder2_AddRef(
1465         STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistFolder3, iface)));
1466 }
1467
1468 static ULONG WINAPI UnixFolder_IPersistFolder3_Release(IPersistFolder3* iface)
1469 {
1470     return UnixFolder_IShellFolder2_Release(
1471         STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistFolder3, iface)));
1472 }
1473
1474 static HRESULT WINAPI UnixFolder_IPersistFolder3_GetClassID(IPersistFolder3* iface, CLSID* pClassID)
1475 {    
1476     UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder3, iface);
1477     
1478     TRACE("(iface=%p, pClassId=%p)\n", iface, pClassID);
1479     
1480     if (!pClassID)
1481         return E_INVALIDARG;
1482
1483     memcpy(pClassID, This->m_pCLSID, sizeof(CLSID));
1484     return S_OK;
1485 }
1486
1487 static HRESULT WINAPI UnixFolder_IPersistFolder3_Initialize(IPersistFolder3* iface, LPCITEMIDLIST pidl)
1488 {
1489     UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder3, iface);
1490     LPCITEMIDLIST current = pidl;
1491     char szBasePath[FILENAME_MAX] = "/";
1492     
1493     TRACE("(iface=%p, pidl=%p)\n", iface, pidl);
1494
1495     /* Find the UnixFolderClass root */
1496     while (current->mkid.cb) {
1497         if ((_ILIsDrive(current) && IsEqualCLSID(This->m_pCLSID, &CLSID_ShellFSFolder)) ||
1498             (_ILIsSpecialFolder(current) && IsEqualCLSID(This->m_pCLSID, _ILGetGUIDPointer(current))))
1499         {
1500             break;
1501         }
1502         current = ILGetNext(current);
1503     }
1504
1505     if (current && current->mkid.cb) {
1506         if (_ILIsDrive(current)) {
1507             WCHAR wszDrive[4] = { '?', ':', '\\', 0 };
1508             wszDrive[0] = (WCHAR)*_ILGetTextPointer(current);
1509             if (!UNIXFS_get_unix_path(wszDrive, szBasePath))
1510                 return E_FAIL;
1511         } else if (IsEqualIID(&CLSID_MyDocuments, _ILGetGUIDPointer(current))) {
1512             WCHAR wszMyDocumentsPath[MAX_PATH];
1513             if (!SHGetSpecialFolderPathW(0, wszMyDocumentsPath, CSIDL_PERSONAL, FALSE))
1514                 return E_FAIL;
1515             PathAddBackslashW(wszMyDocumentsPath);
1516             if (!UNIXFS_get_unix_path(wszMyDocumentsPath, szBasePath))
1517                 return E_FAIL;
1518         } 
1519         current = ILGetNext(current);
1520     } else if (_ILIsDesktop(pidl) || _ILIsValue(pidl) || _ILIsFolder(pidl)) {
1521         /* Path rooted at Desktop */
1522         WCHAR wszDesktopPath[MAX_PATH];
1523         if (!SHGetSpecialFolderPathW(0, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE)) 
1524             return E_FAIL;
1525         PathAddBackslashW(wszDesktopPath);
1526         if (!UNIXFS_get_unix_path(wszDesktopPath, szBasePath))
1527             return E_FAIL;
1528         current = pidl;
1529     } else if (IsEqualCLSID(This->m_pCLSID, &CLSID_FolderShortcut)) {
1530         /* FolderShortcuts' Initialize method only sets the ITEMIDLIST, which
1531          * specifies the location in the shell namespace, but leaves the
1532          * target folder (m_pszPath) alone. See unit tests in tests/shlfolder.c */
1533         This->m_pidlLocation = ILClone(pidl);
1534         return S_OK;
1535     } else {
1536         ERR("Unknown pidl type!\n");
1537         pdump(pidl);
1538         return E_INVALIDARG;
1539     }
1540   
1541     This->m_pidlLocation = ILClone(pidl);
1542     return UNIXFS_initialize_target_folder(This, szBasePath, current, 0); 
1543 }
1544
1545 static HRESULT WINAPI UnixFolder_IPersistFolder3_GetCurFolder(IPersistFolder3* iface, LPITEMIDLIST* ppidl)
1546 {
1547     UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder3, iface);
1548     
1549     TRACE ("(iface=%p, ppidl=%p)\n", iface, ppidl);
1550
1551     if (!ppidl)
1552         return E_POINTER;
1553     *ppidl = ILClone (This->m_pidlLocation);
1554     return S_OK;
1555 }
1556
1557 static HRESULT WINAPI UnixFolder_IPersistFolder3_InitializeEx(IPersistFolder3 *iface, IBindCtx *pbc, 
1558     LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *ppfti)
1559 {
1560     UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder3, iface);
1561     WCHAR wszTargetDosPath[MAX_PATH];
1562     char szTargetPath[FILENAME_MAX] = "";
1563     
1564     TRACE("(iface=%p, pbc=%p, pidlRoot=%p, ppfti=%p)\n", iface, pbc, pidlRoot, ppfti);
1565
1566     /* If no PERSIST_FOLDER_TARGET_INFO is given InitializeEx is equivalent to Initialize. */
1567     if (!ppfti) 
1568         return IPersistFolder3_Initialize(iface, pidlRoot);
1569
1570     if (ppfti->csidl != -1) {
1571         if (FAILED(SHGetFolderPathW(0, ppfti->csidl, NULL, 0, wszTargetDosPath)) ||
1572             !UNIXFS_get_unix_path(wszTargetDosPath, szTargetPath))
1573         {
1574             return E_FAIL;
1575         }
1576     } else if (*ppfti->szTargetParsingName) {
1577         lstrcpyW(wszTargetDosPath, ppfti->szTargetParsingName);
1578         PathAddBackslashW(wszTargetDosPath);
1579         if (!UNIXFS_get_unix_path(wszTargetDosPath, szTargetPath)) {
1580             return E_FAIL;
1581         }
1582     } else if (ppfti->pidlTargetFolder) {
1583         if (!SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTargetDosPath) ||
1584             !UNIXFS_get_unix_path(wszTargetDosPath, szTargetPath))
1585         {
1586             return E_FAIL;
1587         }
1588     } else {
1589         return E_FAIL;
1590     }
1591
1592     This->m_pszPath = SHAlloc(lstrlenA(szTargetPath)+1);
1593     if (!This->m_pszPath) 
1594         return E_FAIL;
1595     lstrcpyA(This->m_pszPath, szTargetPath);
1596     This->m_pidlLocation = ILClone(pidlRoot);
1597     This->m_dwAttributes = (ppfti->dwAttributes != -1) ? ppfti->dwAttributes :
1598         (SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|SFGAO_CANRENAME|SFGAO_FILESYSTEM);
1599
1600     return S_OK;
1601 }
1602
1603 static HRESULT WINAPI UnixFolder_IPersistFolder3_GetFolderTargetInfo(IPersistFolder3 *iface, 
1604     PERSIST_FOLDER_TARGET_INFO *ppfti)
1605 {
1606     FIXME("(iface=%p, ppfti=%p) stub\n", iface, ppfti);
1607     return E_NOTIMPL;
1608 }
1609
1610 /* VTable for UnixFolder's IPersistFolder interface.
1611  */
1612 static const IPersistFolder3Vtbl UnixFolder_IPersistFolder3_Vtbl = {
1613     UnixFolder_IPersistFolder3_QueryInterface,
1614     UnixFolder_IPersistFolder3_AddRef,
1615     UnixFolder_IPersistFolder3_Release,
1616     UnixFolder_IPersistFolder3_GetClassID,
1617     UnixFolder_IPersistFolder3_Initialize,
1618     UnixFolder_IPersistFolder3_GetCurFolder,
1619     UnixFolder_IPersistFolder3_InitializeEx,
1620     UnixFolder_IPersistFolder3_GetFolderTargetInfo
1621 };
1622
1623 static HRESULT WINAPI UnixFolder_IPersistPropertyBag_QueryInterface(IPersistPropertyBag* iface,
1624     REFIID riid, void** ppv)
1625 {
1626     return UnixFolder_IShellFolder2_QueryInterface(
1627         STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface)), riid, ppv);
1628 }
1629
1630 static ULONG WINAPI UnixFolder_IPersistPropertyBag_AddRef(IPersistPropertyBag* iface)
1631 {
1632     return UnixFolder_IShellFolder2_AddRef(
1633         STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface)));
1634 }
1635
1636 static ULONG WINAPI UnixFolder_IPersistPropertyBag_Release(IPersistPropertyBag* iface)
1637 {
1638     return UnixFolder_IShellFolder2_Release(
1639         STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface)));
1640 }
1641
1642 static HRESULT WINAPI UnixFolder_IPersistPropertyBag_GetClassID(IPersistPropertyBag* iface, 
1643     CLSID* pClassID)
1644 {
1645     return UnixFolder_IPersistFolder3_GetClassID(
1646         STATIC_CAST(IPersistFolder3, ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface)), pClassID);
1647 }
1648
1649 static HRESULT WINAPI UnixFolder_IPersistPropertyBag_InitNew(IPersistPropertyBag* iface)
1650 {
1651     FIXME("() stub\n");
1652     return E_NOTIMPL;
1653 }
1654
1655 static HRESULT WINAPI UnixFolder_IPersistPropertyBag_Load(IPersistPropertyBag *iface, 
1656     IPropertyBag *pPropertyBag, IErrorLog *pErrorLog)
1657 {
1658      UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface);
1659      static const WCHAR wszTarget[] = { 'T','a','r','g','e','t', 0 }, wszNull[] = { 0 };
1660      PERSIST_FOLDER_TARGET_INFO pftiTarget;
1661      VARIANT var;
1662      HRESULT hr;
1663      
1664      TRACE("(iface=%p, pPropertyBag=%p, pErrorLog=%p)\n", iface, pPropertyBag, pErrorLog);
1665  
1666      if (!pPropertyBag)
1667          return E_POINTER;
1668  
1669      /* Get 'Target' property from the property bag. */
1670      V_VT(&var) = VT_BSTR;
1671      hr = IPropertyBag_Read(pPropertyBag, wszTarget, &var, NULL);
1672      if (FAILED(hr)) 
1673          return E_FAIL;
1674      lstrcpyW(pftiTarget.szTargetParsingName, V_BSTR(&var));
1675      SysFreeString(V_BSTR(&var));
1676  
1677      pftiTarget.pidlTargetFolder = NULL;
1678      lstrcpyW(pftiTarget.szNetworkProvider, wszNull);
1679      pftiTarget.dwAttributes = -1;
1680      pftiTarget.csidl = -1;
1681  
1682      return UnixFolder_IPersistFolder3_InitializeEx(
1683                  STATIC_CAST(IPersistFolder3, This), NULL, NULL, &pftiTarget);
1684 }
1685
1686 static HRESULT WINAPI UnixFolder_IPersistPropertyBag_Save(IPersistPropertyBag *iface,
1687     IPropertyBag *pPropertyBag, BOOL fClearDirty, BOOL fSaveAllProperties)
1688 {
1689     FIXME("() stub\n");
1690     return E_NOTIMPL;
1691 }
1692
1693 /* VTable for UnixFolder's IPersistPropertyBag interface.
1694  */
1695 static const IPersistPropertyBagVtbl UnixFolder_IPersistPropertyBag_Vtbl = {
1696     UnixFolder_IPersistPropertyBag_QueryInterface,
1697     UnixFolder_IPersistPropertyBag_AddRef,
1698     UnixFolder_IPersistPropertyBag_Release,
1699     UnixFolder_IPersistPropertyBag_GetClassID,
1700     UnixFolder_IPersistPropertyBag_InitNew,
1701     UnixFolder_IPersistPropertyBag_Load,
1702     UnixFolder_IPersistPropertyBag_Save
1703 };
1704
1705 static HRESULT WINAPI UnixFolder_ISFHelper_QueryInterface(ISFHelper* iface, REFIID riid, 
1706     void** ppvObject)
1707 {
1708     return UnixFolder_IShellFolder2_QueryInterface(
1709         STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, ISFHelper, iface)), riid, ppvObject);
1710 }
1711
1712 static ULONG WINAPI UnixFolder_ISFHelper_AddRef(ISFHelper* iface)
1713 {
1714     return UnixFolder_IShellFolder2_AddRef(
1715         STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, ISFHelper, iface)));
1716 }
1717
1718 static ULONG WINAPI UnixFolder_ISFHelper_Release(ISFHelper* iface)
1719 {
1720     return UnixFolder_IShellFolder2_Release(
1721         STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, ISFHelper, iface)));
1722 }
1723
1724 static HRESULT WINAPI UnixFolder_ISFHelper_GetUniqueName(ISFHelper* iface, LPSTR lpName, UINT uLen)
1725 {
1726     UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
1727     IEnumIDList *pEnum;
1728     HRESULT hr;
1729     LPITEMIDLIST pidlElem;
1730     DWORD dwFetched;
1731     int i;
1732     static const char szNewFolder[] = "New Folder";
1733
1734     TRACE("(iface=%p, lpName=%p, uLen=%u)\n", iface, lpName, uLen);
1735     
1736     if (uLen < sizeof(szNewFolder)+3)
1737         return E_INVALIDARG;
1738
1739     hr = IShellFolder2_EnumObjects(STATIC_CAST(IShellFolder2, This), 0,
1740                                    SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN, &pEnum);
1741     if (SUCCEEDED(hr)) {
1742         lstrcpyA(lpName, szNewFolder);
1743         IEnumIDList_Reset(pEnum);
1744         i = 2;
1745         while ((IEnumIDList_Next(pEnum, 1, &pidlElem, &dwFetched) == S_OK) && (dwFetched == 1)) {
1746             if (!strcasecmp(_ILGetTextPointer(pidlElem), lpName)) {
1747                 IEnumIDList_Reset(pEnum);
1748                 sprintf(lpName, "%s %d", szNewFolder, i++);
1749                 if (i > 99) {
1750                     hr = E_FAIL;
1751                     break;
1752                 }
1753             }
1754         }
1755         IEnumIDList_Release(pEnum);
1756     }
1757     return hr;
1758 }
1759
1760 static HRESULT WINAPI UnixFolder_ISFHelper_AddFolder(ISFHelper* iface, HWND hwnd, LPCSTR pszName, 
1761     LPITEMIDLIST* ppidlOut)
1762 {
1763     UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
1764     char szNewDir[FILENAME_MAX];
1765
1766     TRACE("(iface=%p, hwnd=%p, pszName=%s, ppidlOut=%p)\n", iface, hwnd, pszName, ppidlOut);
1767
1768     if (ppidlOut)
1769         *ppidlOut = NULL;
1770     
1771     lstrcpyA(szNewDir, This->m_pszPath);
1772     lstrcatA(szNewDir, pszName);
1773
1774     if (mkdir(szNewDir, 0755)) {
1775         char szMessage[256 + FILENAME_MAX];
1776         char szCaption[256];
1777
1778         LoadStringA(shell32_hInstance, IDS_CREATEFOLDER_DENIED, szCaption, sizeof(szCaption));
1779         sprintf(szMessage, szCaption, szNewDir);
1780         LoadStringA(shell32_hInstance, IDS_CREATEFOLDER_CAPTION, szCaption, sizeof(szCaption));
1781         MessageBoxA(hwnd, szMessage, szCaption, MB_OK | MB_ICONEXCLAMATION);
1782
1783         return E_FAIL;
1784     } else {
1785         LPITEMIDLIST pidlRelative;
1786         WCHAR wszName[MAX_PATH];
1787
1788         /* Inform the shell */
1789         MultiByteToWideChar(CP_UNIXCP, 0, pszName, -1, wszName, MAX_PATH);
1790         if (UNIXFS_path_to_pidl(This, wszName, &pidlRelative)) {
1791             LPITEMIDLIST pidlAbsolute = ILCombine(This->m_pidlLocation, pidlRelative);
1792             if (ppidlOut)
1793                 *ppidlOut = pidlRelative;
1794             else
1795                 ILFree(pidlRelative);
1796             SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, pidlAbsolute, NULL);
1797             ILFree(pidlAbsolute);
1798         }
1799         return S_OK;
1800     }
1801 }
1802
1803 static HRESULT WINAPI UnixFolder_ISFHelper_DeleteItems(ISFHelper* iface, UINT cidl, 
1804     LPCITEMIDLIST* apidl)
1805 {
1806     UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
1807     char szAbsolute[FILENAME_MAX], *pszRelative;
1808     LPITEMIDLIST pidlAbsolute;
1809     HRESULT hr = S_OK;
1810     UINT i;
1811     
1812     TRACE("(iface=%p, cidl=%d, apidl=%p)\n", iface, cidl, apidl);
1813
1814     lstrcpyA(szAbsolute, This->m_pszPath);
1815     pszRelative = szAbsolute + lstrlenA(szAbsolute);
1816     
1817     for (i=0; i<cidl && SUCCEEDED(hr); i++) {
1818         if (!UNIXFS_filename_from_shitemid(apidl[i], pszRelative))
1819             return E_INVALIDARG;
1820         pidlAbsolute = ILCombine(This->m_pidlLocation, apidl[i]);
1821         if (_ILIsFolder(apidl[i])) {
1822             if (rmdir(szAbsolute)) {
1823                 hr = E_FAIL;
1824             } else {
1825                 SHChangeNotify(SHCNE_RMDIR, SHCNF_IDLIST, pidlAbsolute, NULL);
1826             }
1827         } else if (_ILIsValue(apidl[i])) {
1828             if (unlink(szAbsolute)) {
1829                 hr = E_FAIL;
1830             } else {
1831                 SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlAbsolute, NULL);
1832             }
1833         }
1834         ILFree(pidlAbsolute);
1835     }
1836         
1837     return hr;
1838 }
1839
1840 static HRESULT WINAPI UnixFolder_ISFHelper_CopyItems(ISFHelper* iface, IShellFolder *psfFrom, 
1841     UINT cidl, LPCITEMIDLIST *apidl)
1842 {
1843     UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
1844     DWORD dwAttributes;
1845     UINT i;
1846     HRESULT hr;
1847     char szAbsoluteDst[FILENAME_MAX], *pszRelativeDst;
1848     
1849     TRACE("(iface=%p, psfFrom=%p, cidl=%d, apidl=%p): semi-stub\n", iface, psfFrom, cidl, apidl);
1850
1851     if (!psfFrom || !cidl || !apidl)
1852         return E_INVALIDARG;
1853
1854     /* All source items have to be filesystem items. */
1855     dwAttributes = SFGAO_FILESYSTEM;
1856     hr = IShellFolder_GetAttributesOf(psfFrom, cidl, apidl, &dwAttributes);
1857     if (FAILED(hr) || !(dwAttributes & SFGAO_FILESYSTEM)) 
1858         return E_INVALIDARG;
1859
1860     lstrcpyA(szAbsoluteDst, This->m_pszPath);
1861     pszRelativeDst = szAbsoluteDst + strlen(szAbsoluteDst);
1862     
1863     for (i=0; i<cidl; i++) {
1864         WCHAR wszSrc[MAX_PATH];
1865         char szSrc[FILENAME_MAX];
1866         STRRET strret;
1867
1868         /* Build the unix path of the current source item. */
1869         if (FAILED(IShellFolder_GetDisplayNameOf(psfFrom, apidl[i], SHGDN_FORPARSING, &strret)))
1870             return E_FAIL;
1871         if (FAILED(StrRetToBufW(&strret, apidl[i], wszSrc, MAX_PATH)))
1872             return E_FAIL;
1873         if (!UNIXFS_get_unix_path(wszSrc, szSrc)) 
1874             return E_FAIL;
1875
1876         /* Build the unix path of the current destination item */
1877         UNIXFS_filename_from_shitemid(apidl[i], pszRelativeDst);
1878
1879         FIXME("Would copy %s to %s. Not yet implemented.\n", szSrc, szAbsoluteDst);
1880     }
1881     return S_OK;
1882 }
1883
1884 /* VTable for UnixFolder's ISFHelper interface
1885  */
1886 static const ISFHelperVtbl UnixFolder_ISFHelper_Vtbl = {
1887     UnixFolder_ISFHelper_QueryInterface,
1888     UnixFolder_ISFHelper_AddRef,
1889     UnixFolder_ISFHelper_Release,
1890     UnixFolder_ISFHelper_GetUniqueName,
1891     UnixFolder_ISFHelper_AddFolder,
1892     UnixFolder_ISFHelper_DeleteItems,
1893     UnixFolder_ISFHelper_CopyItems
1894 };
1895
1896 static HRESULT WINAPI UnixFolder_IDropTarget_QueryInterface(IDropTarget* iface, REFIID riid, 
1897     void** ppvObject)
1898 {
1899     return UnixFolder_IShellFolder2_QueryInterface(
1900         STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IDropTarget, iface)), riid, ppvObject);
1901 }
1902
1903 static ULONG WINAPI UnixFolder_IDropTarget_AddRef(IDropTarget* iface)
1904 {
1905     return UnixFolder_IShellFolder2_AddRef(
1906         STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IDropTarget, iface)));
1907 }
1908
1909 static ULONG WINAPI UnixFolder_IDropTarget_Release(IDropTarget* iface)
1910 {
1911     return UnixFolder_IShellFolder2_Release(
1912         STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IDropTarget, iface)));
1913 }
1914
1915 #define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
1916 #define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
1917
1918 static HRESULT WINAPI UnixFolder_IDropTarget_DragEnter(IDropTarget *iface, IDataObject *pDataObject,
1919     DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1920 {
1921     UnixFolder *This = ADJUST_THIS(UnixFolder, IDropTarget, iface);
1922     FORMATETC format;
1923     STGMEDIUM medium; 
1924         
1925     TRACE("(iface=%p, pDataObject=%p, dwKeyState=%08lx, pt={.x=%ld, .y=%ld}, pdwEffect=%p)\n",
1926         iface, pDataObject, dwKeyState, pt.x, pt.y, pdwEffect);
1927
1928     if (!pdwEffect || !pDataObject)
1929         return E_INVALIDARG;
1930   
1931     /* Compute a mask of supported drop-effects for this shellfolder object and the given data 
1932      * object. Dropping is only supported on folders, which represent filesystem locations. One
1933      * can't drop on file objects. And the 'move' drop effect is only supported, if the source
1934      * folder is not identical to the target folder. */
1935     This->m_dwDropEffectsMask = DROPEFFECT_NONE;
1936     InitFormatEtc(format, cfShellIDList, TYMED_HGLOBAL);
1937     if ((This->m_dwAttributes & SFGAO_FILESYSTEM) && /* Only drop to filesystem folders */
1938         _ILIsFolder(ILFindLastID(This->m_pidlLocation)) && /* Only drop to folders, not to files */
1939         SUCCEEDED(IDataObject_GetData(pDataObject, &format, &medium))) /* Only ShellIDList format */
1940     {
1941         LPIDA pidaShellIDList = GlobalLock(medium.u.hGlobal);
1942         This->m_dwDropEffectsMask |= DROPEFFECT_COPY|DROPEFFECT_LINK;
1943
1944         if (pidaShellIDList) { /* Files can only be moved between two different folders */
1945             if (!ILIsEqual(HIDA_GetPIDLFolder(pidaShellIDList), This->m_pidlLocation))
1946                 This->m_dwDropEffectsMask |= DROPEFFECT_MOVE;
1947             GlobalUnlock(medium.u.hGlobal);
1948         }
1949     }
1950
1951     *pdwEffect = KeyStateToDropEffect(dwKeyState) & This->m_dwDropEffectsMask;
1952     
1953     return S_OK;
1954 }
1955
1956 static HRESULT WINAPI UnixFolder_IDropTarget_DragOver(IDropTarget *iface, DWORD dwKeyState, 
1957     POINTL pt, DWORD *pdwEffect)
1958 {
1959     UnixFolder *This = ADJUST_THIS(UnixFolder, IDropTarget, iface);
1960     
1961     TRACE("(iface=%p, dwKeyState=%08lx, pt={.x=%ld, .y=%ld}, pdwEffect=%p)\n", iface, dwKeyState, 
1962         pt.x, pt.y, pdwEffect);
1963
1964     if (!pdwEffect)
1965         return E_INVALIDARG;
1966
1967     *pdwEffect = KeyStateToDropEffect(dwKeyState) & This->m_dwDropEffectsMask;
1968     
1969     return S_OK;
1970 }
1971
1972 static HRESULT WINAPI UnixFolder_IDropTarget_DragLeave(IDropTarget *iface) {
1973     UnixFolder *This = ADJUST_THIS(UnixFolder, IDropTarget, iface);
1974     
1975     TRACE("(iface=%p)\n", iface);
1976  
1977     This->m_dwDropEffectsMask = DROPEFFECT_NONE;
1978     
1979     return S_OK;
1980 }
1981
1982 static HRESULT WINAPI UnixFolder_IDropTarget_Drop(IDropTarget *iface, IDataObject *pDataObject,
1983     DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1984 {
1985     UnixFolder *This = ADJUST_THIS(UnixFolder, IDropTarget, iface);
1986     FORMATETC format;
1987     STGMEDIUM medium; 
1988     HRESULT hr;
1989
1990     TRACE("(iface=%p, pDataObject=%p, dwKeyState=%ld, pt={.x=%ld, .y=%ld}, pdwEffect=%p) semi-stub\n",
1991         iface, pDataObject, dwKeyState, pt.x, pt.y, pdwEffect);
1992
1993     InitFormatEtc(format, cfShellIDList, TYMED_HGLOBAL);
1994     hr = IDataObject_GetData(pDataObject, &format, &medium);
1995     if (!SUCCEEDED(hr)) 
1996         return hr;
1997
1998     if (medium.tymed == TYMED_HGLOBAL) {
1999         IShellFolder *psfSourceFolder, *psfDesktopFolder;
2000         LPIDA pidaShellIDList = GlobalLock(medium.u.hGlobal);
2001         STRRET strret;
2002         UINT i;
2003     
2004         if (!pidaShellIDList) 
2005             return HRESULT_FROM_WIN32(GetLastError());
2006         
2007         hr = SHGetDesktopFolder(&psfDesktopFolder);
2008         if (FAILED(hr)) {
2009             GlobalUnlock(medium.u.hGlobal);
2010             return hr;
2011         }
2012
2013         hr = IShellFolder_BindToObject(psfDesktopFolder, HIDA_GetPIDLFolder(pidaShellIDList), NULL, 
2014                                        &IID_IShellFolder, (LPVOID*)&psfSourceFolder);
2015         IShellFolder_Release(psfDesktopFolder);
2016         if (FAILED(hr)) {
2017             GlobalUnlock(medium.u.hGlobal);
2018             return hr;
2019         }
2020
2021         for (i = 0; i < pidaShellIDList->cidl; i++) {
2022             WCHAR wszSourcePath[MAX_PATH];
2023
2024             hr = IShellFolder_GetDisplayNameOf(psfSourceFolder, HIDA_GetPIDLItem(pidaShellIDList, i),
2025                                                SHGDN_FORPARSING, &strret);
2026             if (FAILED(hr)) 
2027                 break;
2028
2029             hr = StrRetToBufW(&strret, NULL, wszSourcePath, MAX_PATH);
2030             if (FAILED(hr)) 
2031                 break;
2032
2033             switch (*pdwEffect) {
2034                 case DROPEFFECT_MOVE:
2035                     FIXME("Move %s to %s!\n", debugstr_w(wszSourcePath), This->m_pszPath);
2036                     break;
2037                 case DROPEFFECT_COPY:
2038                     FIXME("Copy %s to %s!\n", debugstr_w(wszSourcePath), This->m_pszPath);
2039                     break;
2040                 case DROPEFFECT_LINK:
2041                     FIXME("Link %s from %s!\n", debugstr_w(wszSourcePath), This->m_pszPath);
2042                     break;
2043             }
2044         }
2045     
2046         IShellFolder_Release(psfSourceFolder);
2047         GlobalUnlock(medium.u.hGlobal);
2048         return hr;
2049     }
2050  
2051     return E_NOTIMPL;
2052 }
2053
2054 /* VTable for UnixFolder's IDropTarget interface
2055  */
2056 static const IDropTargetVtbl UnixFolder_IDropTarget_Vtbl = {
2057     UnixFolder_IDropTarget_QueryInterface,
2058     UnixFolder_IDropTarget_AddRef,
2059     UnixFolder_IDropTarget_Release,
2060     UnixFolder_IDropTarget_DragEnter,
2061     UnixFolder_IDropTarget_DragOver,
2062     UnixFolder_IDropTarget_DragLeave,
2063     UnixFolder_IDropTarget_Drop
2064 };
2065
2066 /******************************************************************************
2067  * Unix[Dos]Folder_Constructor [Internal]
2068  *
2069  * PARAMS
2070  *  pUnkOuter [I] Outer class for aggregation. Currently ignored.
2071  *  riid      [I] Interface asked for by the client.
2072  *  ppv       [O] Pointer to an riid interface to the UnixFolder object.
2073  *
2074  * NOTES
2075  *  Those are the only functions exported from shfldr_unixfs.c. They are called from
2076  *  shellole.c's default class factory and thus have to exhibit a LPFNCREATEINSTANCE
2077  *  compatible signature.
2078  *
2079  *  The UnixDosFolder_Constructor sets the dwPathMode member to PATHMODE_DOS. This
2080  *  means that paths are converted from dos to unix and back at the interfaces.
2081  */
2082 static HRESULT CreateUnixFolder(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv, const CLSID *pCLSID) 
2083 {
2084     HRESULT hr = E_FAIL;
2085     UnixFolder *pUnixFolder = SHAlloc((ULONG)sizeof(UnixFolder));
2086    
2087     if (pUnkOuter) {
2088         FIXME("Aggregation not yet implemented!\n");
2089         return CLASS_E_NOAGGREGATION;
2090     }
2091     
2092     if(pUnixFolder) {
2093         pUnixFolder->lpIShellFolder2Vtbl = &UnixFolder_IShellFolder2_Vtbl;
2094         pUnixFolder->lpIPersistFolder3Vtbl = &UnixFolder_IPersistFolder3_Vtbl;
2095         pUnixFolder->lpIPersistPropertyBagVtbl = &UnixFolder_IPersistPropertyBag_Vtbl;
2096         pUnixFolder->lpISFHelperVtbl = &UnixFolder_ISFHelper_Vtbl;
2097         pUnixFolder->lpIDropTargetVtbl = &UnixFolder_IDropTarget_Vtbl;
2098         pUnixFolder->m_cRef = 0;
2099         pUnixFolder->m_pszPath = NULL;
2100         pUnixFolder->m_pidlLocation = NULL;
2101         pUnixFolder->m_dwPathMode = IsEqualCLSID(&CLSID_UnixFolder, pCLSID) ? PATHMODE_UNIX : PATHMODE_DOS;
2102         pUnixFolder->m_dwAttributes = 0;
2103         pUnixFolder->m_pCLSID = pCLSID;
2104         pUnixFolder->m_dwDropEffectsMask = DROPEFFECT_NONE;
2105
2106         UnixFolder_IShellFolder2_AddRef(STATIC_CAST(IShellFolder2, pUnixFolder));
2107         hr = UnixFolder_IShellFolder2_QueryInterface(STATIC_CAST(IShellFolder2, pUnixFolder), riid, ppv);
2108         UnixFolder_IShellFolder2_Release(STATIC_CAST(IShellFolder2, pUnixFolder));
2109     }
2110     return hr;
2111 }
2112
2113 HRESULT WINAPI UnixFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
2114     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2115     return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_UnixFolder);
2116 }
2117
2118 HRESULT WINAPI UnixDosFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
2119     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2120     return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_UnixDosFolder);
2121 }
2122
2123 HRESULT WINAPI FolderShortcut_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
2124     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2125     return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_FolderShortcut);
2126 }
2127
2128 HRESULT WINAPI MyDocuments_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
2129     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2130     return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_MyDocuments);
2131 }
2132
2133 HRESULT WINAPI ShellFSFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
2134     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2135     return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_ShellFSFolder);
2136 }
2137
2138 /******************************************************************************
2139  * UnixSubFolderIterator
2140  *
2141  * Class whose heap based objects represent iterators over the sub-directories
2142  * of a given UnixFolder object. 
2143  */
2144
2145 /* UnixSubFolderIterator object layout and typedef.
2146  */
2147 typedef struct _UnixSubFolderIterator {
2148     const IEnumIDListVtbl *lpIEnumIDListVtbl;
2149     LONG m_cRef;
2150     SHCONTF m_fFilter;
2151     DIR *m_dirFolder;
2152     char m_szFolder[FILENAME_MAX];
2153 } UnixSubFolderIterator;
2154
2155 static void UnixSubFolderIterator_Destroy(UnixSubFolderIterator *iterator) {
2156     TRACE("(iterator=%p)\n", iterator);
2157
2158     if (iterator->m_dirFolder)
2159         closedir(iterator->m_dirFolder);
2160     SHFree(iterator);
2161 }
2162
2163 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_QueryInterface(IEnumIDList* iface, 
2164     REFIID riid, void** ppv)
2165 {
2166     TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
2167     
2168     if (!ppv) return E_INVALIDARG;
2169     
2170     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IEnumIDList, riid)) {
2171         *ppv = iface;
2172     } else {
2173         *ppv = NULL;
2174         return E_NOINTERFACE;
2175     }
2176
2177     IEnumIDList_AddRef(iface);
2178     return S_OK;
2179 }
2180                             
2181 static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_AddRef(IEnumIDList* iface)
2182 {
2183     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
2184
2185     TRACE("(iface=%p)\n", iface);
2186    
2187     return InterlockedIncrement(&This->m_cRef);
2188 }
2189
2190 static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_Release(IEnumIDList* iface)
2191 {
2192     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
2193     ULONG cRef;
2194     
2195     TRACE("(iface=%p)\n", iface);
2196
2197     cRef = InterlockedDecrement(&This->m_cRef);
2198     
2199     if (!cRef) 
2200         UnixSubFolderIterator_Destroy(This);
2201
2202     return cRef;
2203 }
2204
2205 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Next(IEnumIDList* iface, ULONG celt, 
2206     LPITEMIDLIST* rgelt, ULONG* pceltFetched)
2207 {
2208     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
2209     ULONG i = 0;
2210
2211     /* This->m_dirFolder will be NULL if the user doesn't have access rights for the dir. */
2212     if (This->m_dirFolder) {
2213         char *pszRelativePath = This->m_szFolder + lstrlenA(This->m_szFolder);
2214         struct dirent *pDirEntry;
2215
2216         while (i < celt) {
2217             pDirEntry = readdir(This->m_dirFolder);
2218             if (!pDirEntry) break; /* No more entries */
2219             if (!strcmp(pDirEntry->d_name, ".") || !strcmp(pDirEntry->d_name, "..")) continue;
2220
2221             /* Temporarily build absolute path in This->m_szFolder. Then construct a pidl
2222              * and see if it passes the filter. 
2223              */
2224             lstrcpyA(pszRelativePath, pDirEntry->d_name);
2225             rgelt[i] = (LPITEMIDLIST)SHAlloc(
2226                 UNIXFS_shitemid_len_from_filename(pszRelativePath, NULL, NULL)+sizeof(USHORT));
2227             if (!UNIXFS_build_shitemid(This->m_szFolder, rgelt[i]) ||
2228                 !UNIXFS_is_pidl_of_type(rgelt[i], This->m_fFilter)) 
2229             {
2230                 SHFree(rgelt[i]);
2231                 continue;
2232             }
2233             memset(((PBYTE)rgelt[i])+rgelt[i]->mkid.cb, 0, sizeof(USHORT));
2234             i++;
2235         }
2236         *pszRelativePath = '\0'; /* Restore the original path in This->m_szFolder. */
2237     }
2238     
2239     if (pceltFetched)
2240         *pceltFetched = i;
2241
2242     return (i == 0) ? S_FALSE : S_OK;
2243 }
2244     
2245 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Skip(IEnumIDList* iface, ULONG celt)
2246 {
2247     LPITEMIDLIST *apidl;
2248     ULONG cFetched;
2249     HRESULT hr;
2250     
2251     TRACE("(iface=%p, celt=%ld)\n", iface, celt);
2252
2253     /* Call IEnumIDList::Next and delete the resulting pidls. */
2254     apidl = (LPITEMIDLIST*)SHAlloc(celt * sizeof(LPITEMIDLIST));
2255     hr = IEnumIDList_Next(iface, celt, apidl, &cFetched);
2256     if (SUCCEEDED(hr))
2257         while (cFetched--) 
2258             SHFree(apidl[cFetched]);
2259     SHFree(apidl);
2260
2261     return hr;
2262 }
2263
2264 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Reset(IEnumIDList* iface)
2265 {
2266     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
2267         
2268     TRACE("(iface=%p)\n", iface);
2269
2270     if (This->m_dirFolder)
2271         rewinddir(This->m_dirFolder);
2272     
2273     return S_OK;
2274 }
2275
2276 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Clone(IEnumIDList* This, 
2277     IEnumIDList** ppenum)
2278 {
2279     FIXME("stub\n");
2280     return E_NOTIMPL;
2281 }
2282
2283 /* VTable for UnixSubFolderIterator's IEnumIDList interface.
2284  */
2285 static const IEnumIDListVtbl UnixSubFolderIterator_IEnumIDList_Vtbl = {
2286     UnixSubFolderIterator_IEnumIDList_QueryInterface,
2287     UnixSubFolderIterator_IEnumIDList_AddRef,
2288     UnixSubFolderIterator_IEnumIDList_Release,
2289     UnixSubFolderIterator_IEnumIDList_Next,
2290     UnixSubFolderIterator_IEnumIDList_Skip,
2291     UnixSubFolderIterator_IEnumIDList_Reset,
2292     UnixSubFolderIterator_IEnumIDList_Clone
2293 };
2294
2295 static IUnknown *UnixSubFolderIterator_Constructor(UnixFolder *pUnixFolder, SHCONTF fFilter) {
2296     UnixSubFolderIterator *iterator;
2297
2298     TRACE("(pUnixFolder=%p)\n", pUnixFolder);
2299     
2300     iterator = SHAlloc((ULONG)sizeof(UnixSubFolderIterator));
2301     iterator->lpIEnumIDListVtbl = &UnixSubFolderIterator_IEnumIDList_Vtbl;
2302     iterator->m_cRef = 0;
2303     iterator->m_fFilter = fFilter;
2304     iterator->m_dirFolder = opendir(pUnixFolder->m_pszPath);
2305     lstrcpyA(iterator->m_szFolder, pUnixFolder->m_pszPath);
2306
2307     UnixSubFolderIterator_IEnumIDList_AddRef((IEnumIDList*)iterator);
2308     
2309     return (IUnknown*)iterator;
2310 }