Sort entry points alphabetically.
[wine] / dlls / shell32 / shfldr_unixfs.c
1 /*
2  * UNIXFS - Shell namespace extension for the unix filesystem
3  *
4  * Copyright (C) 2005 Michael Jung
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <limits.h>
25 #include <dirent.h>
26 #ifdef HAVE_UNISTD_H
27 # include <unistd.h>
28 #endif
29 #ifdef HAVE_SYS_STAT_H
30 # include <sys/stat.h>
31 #endif
32 #ifdef HAVE_PWD_H
33 # include <pwd.h>
34 #endif
35 #include <grp.h>
36 #include <limits.h>
37
38 #define COBJMACROS
39 #define NONAMELESSUNION
40 #define NONAMELESSSTRUCT
41
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winuser.h"
45 #include "objbase.h"
46 #include "winreg.h"
47 #include "shlwapi.h"
48 #include "winternl.h"
49 #include "wine/debug.h"
50
51 #include "shell32_main.h"
52 #include "shfldr.h"
53 #include "shresdef.h"
54 #include "pidl.h"
55
56 WINE_DEFAULT_DEBUG_CHANNEL(shell);
57
58 const GUID CLSID_UnixFolder = {0xcc702eb2, 0x7dc5, 0x11d9, {0xc6, 0x87, 0x00, 0x04, 0x23, 0x8a, 0x01, 0xcd}};
59 const GUID CLSID_UnixDosFolder = {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
60
61 #define ADJUST_THIS(c,m,p) ((c*)(((long)p)-(long)&(((c*)0)->lp##m##Vtbl)))
62 #define STATIC_CAST(i,p) ((i*)&p->lp##i##Vtbl)
63
64 /* FileStruct reserves one byte for szNames, thus we don't have to 
65  * alloc a byte for the terminating '\0' of 'name'. Two of the
66  * additional bytes are for SHITEMID's cb field. One is for IDLDATA's
67  * type field. One is for FileStruct's szNames field, to terminate
68  * the alternate DOS name, which we don't use here. And then there's
69  * the additional StatStruct.
70  */
71 #define SHITEMID_LEN_FROM_NAME_LEN(n) \
72     (sizeof(USHORT)+sizeof(PIDLTYPE)+sizeof(FileStruct)+(n)+sizeof(char)+sizeof(StatStruct))
73 #define NAME_LEN_FROM_LPSHITEMID(s) \
74     (((LPSHITEMID)s)->cb-sizeof(USHORT)-sizeof(PIDLTYPE)-sizeof(FileStruct)-sizeof(char)-sizeof(StatStruct))
75 #define LPSTATSTRUCT_FROM_LPSHITEMID(s) \
76     (((s) && (((LPSHITEMID)s)->cb > SHITEMID_LEN_FROM_NAME_LEN(0))) ? \
77      ((StatStruct*)(((LPBYTE)s)+((LPSHITEMID)s)->cb-sizeof(StatStruct))) : \
78      (NULL))
79
80 #define PATHMODE_UNIX 0
81 #define PATHMODE_DOS  1
82
83 /* ShellFolder attributes of the root folder ('/') */
84 static DWORD dwRootAttr = 0;
85
86 /* This structure is appended to shell32's FileStruct type in IDLs to store unix
87  * filesystem specific informationen extracted with the stat system call.
88  */
89 typedef struct tagStatStruct {
90     mode_t st_mode;
91     uid_t  st_uid;
92     gid_t  st_gid;
93     SFGAOF sfAttr;
94 } StatStruct;
95
96 /* UnixFolder object layout and typedef.
97  */
98 typedef struct _UnixFolder {
99     const IShellFolder2Vtbl  *lpIShellFolder2Vtbl;
100     const IPersistFolder2Vtbl *lpIPersistFolder2Vtbl;
101     ULONG m_cRef;
102     CHAR *m_pszPath;
103     LPITEMIDLIST m_pidlLocation;
104     LPITEMIDLIST *m_apidlSubDirs;
105     DWORD m_cSubDirs;
106     DWORD m_dwPathMode;
107 } UnixFolder;
108
109 /******************************************************************************
110  * UNIXFS_is_pidl_of_type [INTERNAL]
111  *
112  * Checks for the first SHITEMID of an ITEMIDLIST if it passes a filter.
113  *
114  * PARAMS
115  *  pIDL    [I] The ITEMIDLIST to be checked.
116  *  fFilter [I] Shell condition flags, which specify the filter.
117  *
118  * RETURNS
119  *  TRUE, if pIDL is accepted by fFilter
120  *  FALSE, otherwise
121  */
122 static inline BOOL UNIXFS_is_pidl_of_type(LPITEMIDLIST pIDL, SHCONTF fFilter) {
123     LPPIDLDATA pIDLData = _ILGetDataPointer(pIDL);
124     if (!(fFilter & SHCONTF_INCLUDEHIDDEN) && pIDLData && 
125         (pIDLData->u.file.uFileAttribs & FILE_ATTRIBUTE_HIDDEN)) 
126     {
127         return FALSE;
128     }
129     if (_ILIsFolder(pIDL) && (fFilter & SHCONTF_FOLDERS)) return TRUE;
130     if (_ILIsValue(pIDL) && (fFilter & SHCONTF_NONFOLDERS)) return TRUE;
131     return FALSE;
132 }
133
134 /******************************************************************************
135  * UNIXFS_is_dos_device [Internal]
136  *
137  * Determines if a unix directory corresponds to any dos device.
138  *
139  * PARAMS
140  *  statPath [I] The stat struct of the directory, as returned by stat(2).
141  *
142  * RETURNS
143  *  TRUE, if statPath corresponds to any dos drive letter
144  *  FALSE, otherwise
145  */
146 static BOOL UNIXFS_is_dos_device(const struct stat *statPath) {
147     struct stat statDrive;
148     char *pszDrivePath;
149     DWORD dwDriveMap;
150     WCHAR wszDosDevice[4] = { 'A', ':', '\\', 0 };
151
152     for (dwDriveMap = GetLogicalDrives(); dwDriveMap; dwDriveMap >>= 1, wszDosDevice[0]++) {
153         if (!(dwDriveMap & 0x1)) continue;
154         pszDrivePath = wine_get_unix_file_name(wszDosDevice);
155         if (pszDrivePath && !stat(pszDrivePath, &statDrive)) {
156             HeapFree(GetProcessHeap(), 0, pszDrivePath);
157             if ((statPath->st_dev == statDrive.st_dev) && (statPath->st_ino == statDrive.st_ino))
158                 return TRUE;
159         }
160     }
161     return FALSE;
162 }
163
164 /******************************************************************************
165  * UNIXFS_get_unix_path [Internal]
166  *
167  * Convert an absolute dos path to an absolute canonicalized unix path.
168  * Evaluate "/.", "/.." and symbolic links.
169  *
170  * PARAMS
171  *  pszDosPath       [I] An absolute dos path
172  *  pszCanonicalPath [O] Buffer of length FILENAME_MAX. Will receive the canonical path.
173  *
174  * RETURNS
175  *  Success, TRUE
176  *  Failure, FALSE - Path not existent, too long, insufficient rights, to many symlinks
177  */
178 static BOOL UNIXFS_get_unix_path(LPCWSTR pszDosPath, char *pszCanonicalPath)
179 {
180     char *pPathTail, *pElement, *pCanonicalTail, szPath[FILENAME_MAX], *pszUnixPath;
181     struct stat fileStat;
182     
183     TRACE("(pszDosPath=%s, pszCanonicalPath=%p)\n", debugstr_w(pszDosPath), pszCanonicalPath);
184     
185     if (!pszDosPath || pszDosPath[1] != ':')
186         return FALSE;
187
188     pszUnixPath = wine_get_unix_file_name(pszDosPath);
189     if (!pszUnixPath) return FALSE;   
190     strcpy(szPath, pszUnixPath);
191     HeapFree(GetProcessHeap(), 0, pszUnixPath);
192    
193     /* pCanonicalTail always points to the end of the canonical path constructed
194      * thus far. pPathTail points to the still to be processed part of the input
195      * path. pElement points to the path element currently investigated.
196      */
197     *pszCanonicalPath = '\0';
198     pCanonicalTail = pszCanonicalPath;
199     pPathTail = szPath;
200
201     do {
202         char cTemp;
203         int cLinks = 0;
204             
205         pElement = pPathTail;
206         pPathTail = strchr(pPathTail+1, '/');
207         if (!pPathTail) /* Last path element may not be terminated by '/'. */ 
208             pPathTail = pElement + strlen(pElement);
209         /* Temporarily terminate the current path element. Will be restored later. */
210         cTemp = *pPathTail;
211         *pPathTail = '\0';
212
213         /* Skip "/." path elements */
214         if (!strcmp("/.", pElement)) {
215             *pPathTail = cTemp;
216             continue;
217         }
218
219         /* Remove last element in canonical path for "/.." elements, then skip. */
220         if (!strcmp("/..", pElement)) {
221             char *pTemp = strrchr(pszCanonicalPath, '/');
222             if (pTemp)
223                 pCanonicalTail = pTemp;
224             *pCanonicalTail = '\0';
225             *pPathTail = cTemp;
226             continue;
227         }
228        
229         /* lstat returns zero on success. */
230         if (lstat(szPath, &fileStat)) 
231             return FALSE;
232
233         if (S_ISLNK(fileStat.st_mode)) {
234             char szSymlink[FILENAME_MAX];
235             int cLinkLen, cTailLen;
236           
237             /* Avoid infinite loop for recursive links. */
238             if (++cLinks > 64) 
239                 return FALSE;
240             
241             cLinkLen = readlink(szPath, szSymlink, FILENAME_MAX);
242             if (cLinkLen < 0) 
243                 return FALSE;
244
245             *pPathTail = cTemp;
246             cTailLen = strlen(pPathTail);
247            
248             if (szSymlink[0] == '/') {
249                 /* Absolute link. Copy to szPath, concat remaining path and start all over. */
250                 if (cLinkLen + cTailLen + 1 > FILENAME_MAX)
251                     return FALSE;
252                     
253                 memcpy(szSymlink + cLinkLen, pPathTail, cTailLen + 1);
254                 memcpy(szPath, szSymlink, cLinkLen + cTailLen + 1);
255                 *pszCanonicalPath = '\0';
256                     pCanonicalTail = pszCanonicalPath;
257                     pPathTail = szPath;
258             } else {
259                 /* Relative link. Expand into szPath and continue. */
260                 char szTemp[FILENAME_MAX];
261                 int cTailLen = strlen(pPathTail);
262
263                 if (pElement - szPath + 1 + cLinkLen + cTailLen + 1 > FILENAME_MAX)
264                     return FALSE;
265
266                 memcpy(szTemp, pPathTail, cTailLen + 1);
267                 memcpy(pElement + 1, szSymlink, cLinkLen);
268                 memcpy(pElement + 1 + cLinkLen, szTemp, cTailLen + 1);
269                 pPathTail = pElement;
270             }
271         } else {
272             /* Regular directory or file. Copy to canonical path */
273             if (pCanonicalTail - pszCanonicalPath + pPathTail - pElement + 1 > FILENAME_MAX)
274                 return FALSE;
275                 
276             memcpy(pCanonicalTail, pElement, pPathTail - pElement + 1);
277             pCanonicalTail += pPathTail - pElement;
278             *pPathTail = cTemp;
279         }
280     } while (pPathTail[0] == '/' && pPathTail[1]); /* Also handles paths terminated by '/' */
281    
282     TRACE("--> %s\n", debugstr_a(pszCanonicalPath));
283     
284     return TRUE;
285 }
286
287 /******************************************************************************
288  * UNIXFS_build_shitemid [Internal]
289  *
290  * Constructs a new SHITEMID for the last component of path 'pszUnixPath' into 
291  * buffer 'pIDL'. Decorates the SHITEMID with information from a stat system call.
292  *
293  * PARAMS
294  *  pszUnixPath [I] An absolute path. The SHITEMID will be build for the last component.
295  *  bParentIsFS [I] Set, if the parent of the last path component is within the wine filesystem.
296  *  pIDL        [O] SHITEMID will be constructed here.
297  *
298  * RETURNS
299  *  Success: A pointer to the terminating '\0' character of path.
300  *  Failure: NULL
301  *
302  * NOTES
303  *  Minimum size of pIDL is SHITEMID_LEN_FROM_NAME_LEN(strlen(last_component_of_path)).
304  *  If what you need is a PIDLLIST with a single SHITEMID, don't forget to append
305  *  a 0 USHORT value.
306  */
307 static char* UNIXFS_build_shitemid(char *pszUnixPath, BOOL bParentIsFS, void *pIDL) {
308     LARGE_INTEGER time;
309     FILETIME fileTime;
310     LPPIDLDATA pIDLData;
311     struct stat fileStat;
312     StatStruct *pStatStruct;
313     char *pszComponent;
314     int cComponentLen;
315
316     TRACE("(pszUnixPath=%s, pIDL=%p)\n", debugstr_a(pszUnixPath), pIDL);
317
318     /* Compute the SHITEMID's length and wipe it. */
319     pszComponent = strrchr(pszUnixPath, '/') + 1;
320     cComponentLen = strlen(pszComponent);
321     memset(pIDL, 0, SHITEMID_LEN_FROM_NAME_LEN(cComponentLen));
322     ((LPSHITEMID)pIDL)->cb = SHITEMID_LEN_FROM_NAME_LEN(cComponentLen) ;
323    
324     /* We are only interested in regular files and directories. */
325     if (stat(pszUnixPath, &fileStat)) return NULL;
326     if (!S_ISDIR(fileStat.st_mode) && !S_ISREG(fileStat.st_mode)) return NULL;
327     
328     pStatStruct = LPSTATSTRUCT_FROM_LPSHITEMID(pIDL);
329     pStatStruct->st_mode = fileStat.st_mode;
330     pStatStruct->st_uid = fileStat.st_uid;
331     pStatStruct->st_gid = fileStat.st_gid;
332     pStatStruct->sfAttr = S_ISDIR(fileStat.st_mode) ? (SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR) : 0;
333     if (bParentIsFS || UNIXFS_is_dos_device(&fileStat)) pStatStruct->sfAttr |= SFGAO_FILESYSTEM;
334
335     /* Set shell32's standard SHITEMID data fields. */
336     pIDLData = _ILGetDataPointer((LPCITEMIDLIST)pIDL);
337     pIDLData->type = S_ISDIR(fileStat.st_mode) ? PT_FOLDER : PT_VALUE;
338     pIDLData->u.file.dwFileSize = (DWORD)fileStat.st_size;
339     RtlSecondsSince1970ToTime( fileStat.st_mtime, &time );
340     fileTime.dwLowDateTime = time.u.LowPart;
341     fileTime.dwHighDateTime = time.u.HighPart;
342     FileTimeToDosDateTime(&fileTime, &pIDLData->u.file.uFileDate, &pIDLData->u.file.uFileTime);
343     pIDLData->u.file.uFileAttribs = 0;
344     if (S_ISDIR(fileStat.st_mode)) pIDLData->u.file.uFileAttribs |= FILE_ATTRIBUTE_DIRECTORY;
345     if (pszComponent[0] == '.') pIDLData->u.file.uFileAttribs |=  FILE_ATTRIBUTE_HIDDEN;
346     memcpy(pIDLData->u.file.szNames, pszComponent, cComponentLen);
347
348     return pszComponent + cComponentLen;
349 }
350
351 /******************************************************************************
352  * UNIXFS_path_to_pidl [Internal]
353  *
354  * PARAMS
355  *  pUnixFolder [I] If path is relative, pUnixFolder represents the base path
356  *  path        [I] An absolute unix or dos path or a path relativ to pUnixFolder
357  *  ppidl       [O] The corresponding ITEMIDLIST. Release with SHFree/ILFree
358  *  
359  * RETURNS
360  *  Success: TRUE
361  *  Failure: FALSE, invalid params or out of memory
362  *
363  * NOTES
364  *  pUnixFolder also carries the information if the path is expected to be unix or dos.
365  */
366 static BOOL UNIXFS_path_to_pidl(UnixFolder *pUnixFolder, const WCHAR *path, LPITEMIDLIST *ppidl) {
367     LPITEMIDLIST pidl;
368     int cSubDirs, cPidlLen, cPathLen;
369     char *pSlash, szCompletePath[FILENAME_MAX], *pNextPathElement;
370     BOOL bParentIsFS = dwRootAttr & SFGAO_FILESYSTEM;
371
372     TRACE("pUnixFolder=%p, path=%s, ppidl=%p\n", pUnixFolder, debugstr_w(path), ppidl);
373    
374     if (!ppidl || !path)
375         return FALSE;
376
377     cPathLen = lstrlenW(path);
378     
379     /* Build an absolute path and let pNextPathElement point to the interesting 
380      * relative sub-path. We need the absolute path to call 'stat', but the pidl
381      * will only contain the relative part.
382      */
383     if ((pUnixFolder->m_dwPathMode == PATHMODE_DOS) && (path[1] == ':')) 
384     {
385         /* Absolute dos path. Convert to unix */
386         if (!UNIXFS_get_unix_path(path, szCompletePath))
387             return FALSE;
388         pNextPathElement = szCompletePath + 1;
389     } 
390     else if ((pUnixFolder->m_dwPathMode == PATHMODE_UNIX) && (path[0] == '/')) 
391     {
392         /* Absolute unix path. Just convert to ANSI. */
393         WideCharToMultiByte(CP_ACP, 0, path, -1, szCompletePath, FILENAME_MAX, NULL, NULL); 
394         pNextPathElement = szCompletePath + 1;
395     } 
396     else 
397     {
398         /* Relative dos or unix path. Concat with this folder's path */
399         int cBasePathLen = strlen(pUnixFolder->m_pszPath);
400         memcpy(szCompletePath, pUnixFolder->m_pszPath, cBasePathLen);
401         WideCharToMultiByte(CP_ACP, 0, path, -1, szCompletePath + cBasePathLen, 
402                             FILENAME_MAX - cBasePathLen, NULL, NULL);
403         pNextPathElement = szCompletePath + cBasePathLen;
404         
405         /* If in dos mode, replace '\' with '/' */
406         if (pUnixFolder->m_dwPathMode == PATHMODE_DOS) {
407             char *pBackslash = strchr(pNextPathElement, '\\');
408             while (pBackslash) {
409                 *pBackslash = '/';
410                 pBackslash = strchr(pBackslash, '\\');
411             }
412         }
413     }
414
415     /* At this point, we have an absolute unix path in szCompletePath. */
416     TRACE("complete path: %s\n", szCompletePath);
417     
418     /* Count the number of sub-directories in the path */
419     cSubDirs = 1; /* Path may not be terminated with '/', thus start with 1 */
420     pSlash = strchr(pNextPathElement, '/');
421     while (pSlash && pSlash[1]) {
422         cSubDirs++;
423         pSlash = strchr(pSlash+1, '/');
424     }
425   
426     /* Allocate enough memory to hold the path. The -cSubDirs is for the '/' 
427      * characters, which are not stored in the ITEMIDLIST. */
428     cPidlLen = strlen(pNextPathElement) - cSubDirs + 1 + cSubDirs * SHITEMID_LEN_FROM_NAME_LEN(0) + sizeof(USHORT);
429     *ppidl = pidl = (LPITEMIDLIST)SHAlloc(cPidlLen);
430     if (!pidl) return FALSE;
431
432     /* Concatenate the SHITEMIDs of the sub-directories. */
433     while (*pNextPathElement) {
434         pSlash = strchr(pNextPathElement, '/');
435         if (pSlash) *pSlash = '\0';
436         pNextPathElement = UNIXFS_build_shitemid(szCompletePath, bParentIsFS, pidl);
437         if (pSlash) *pSlash = '/';
438             
439         if (!pNextPathElement) {
440             SHFree(pidl);
441             return FALSE;
442         }
443         if (!bParentIsFS && (LPSTATSTRUCT_FROM_LPSHITEMID(pidl)->sfAttr & SFGAO_FILESYSTEM)) 
444             bParentIsFS = TRUE;
445         pidl = ILGetNext(pidl);
446     }
447     pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
448
449     if ((int)pidl-(int)*ppidl+sizeof(USHORT) > cPidlLen) /* We've corrupted the heap :( */ 
450         ERR("Computed length of pidl to small. Please report.\n");
451     
452     return TRUE;
453 }
454
455 /******************************************************************************
456  * UNIXFS_pidl_to_path [Internal]
457  *
458  * Construct the unix path that corresponds to a fully qualified ITEMIDLIST
459  *
460  * PARAMS
461  *  pidl [I] ITEMIDLIST that specifies the absolute location of the folder
462  *  path [O] The corresponding unix path as a zero terminated ascii string
463  *
464  * RETURNS
465  *  Success: TRUE
466  *  Failure: FALSE, pidl doesn't specify a unix path or out of memory
467  */
468 static BOOL UNIXFS_pidl_to_path(LPCITEMIDLIST pidl, UnixFolder *pUnixFolder) {
469     LPCITEMIDLIST current = pidl, root;
470     DWORD dwPathLen;
471     char *pNextDir, szBasePath[FILENAME_MAX] = "/";
472
473     TRACE("(pidl=%p, pUnixFolder=%p)\n", pidl, pUnixFolder);
474     pdump(pidl);
475     
476     pUnixFolder->m_pszPath = NULL;
477
478     /* Find the UnixFolderClass root */
479     while (current->mkid.cb) {
480         if (_ILIsDrive(current) || /* The dos drive, which maps to '/' */
481             (_ILIsSpecialFolder(current) && 
482              (IsEqualIID(&CLSID_UnixFolder, _ILGetGUIDPointer(current)) ||
483               IsEqualIID(&CLSID_UnixDosFolder, _ILGetGUIDPointer(current)))))
484         {
485             break;
486         }
487         current = ILGetNext(current);
488     }
489
490     if (current && current->mkid.cb) {
491         root = current = ILGetNext(current);
492         dwPathLen = 2; /* For the '/' prefix and the terminating '\0' */
493     } else if (_ILIsDesktop(pidl) || _ILIsValue(pidl) || _ILIsFolder(pidl)) {
494         /* Path rooted at Desktop */
495         WCHAR wszDesktopPath[MAX_PATH];
496         if (FAILED(SHGetSpecialFolderPathW(0, wszDesktopPath, CSIDL_DESKTOP, FALSE)))
497            return FALSE;
498         if (!UNIXFS_get_unix_path(wszDesktopPath, szBasePath))
499             return FALSE;
500         dwPathLen = strlen(szBasePath) + 1;
501         root = current;
502     } else {
503         ERR("Unknown pidl type!\n");
504         pdump(pidl);
505         return FALSE;
506     }
507     
508     /* Determine the path's length bytes */
509     while (current && current->mkid.cb) {
510         dwPathLen += NAME_LEN_FROM_LPSHITEMID(current) + 1; /* For the '/' */
511         current = ILGetNext(current);
512     };
513
514     /* Build the path */
515     pUnixFolder->m_pszPath = pNextDir = SHAlloc(dwPathLen);
516     if (!pUnixFolder->m_pszPath) {
517         WARN("SHAlloc failed!\n");
518         return FALSE;
519     }
520     current = root;
521     strcpy(pNextDir, szBasePath);
522     pNextDir += strlen(szBasePath);
523     while (current && current->mkid.cb) {
524         memcpy(pNextDir, _ILGetTextPointer(current), NAME_LEN_FROM_LPSHITEMID(current));
525         pNextDir += NAME_LEN_FROM_LPSHITEMID(current);
526         *pNextDir++ = '/';
527         current = ILGetNext(current);
528     }
529     *pNextDir='\0';
530     
531     TRACE("--> %s\n", pUnixFolder->m_pszPath);
532     return TRUE;
533 }
534
535 /******************************************************************************
536  * UNIXFS_build_subfolder_pidls [Internal]
537  *
538  * Builds an array of subfolder PIDLs relative to a unix directory
539  *
540  * PARAMS
541  *  path   [I] Name of a unix directory as a zero terminated ascii string
542  *  apidl  [O] The array of PIDLs
543  *  pCount [O] Size of apidl
544  *
545  * RETURNS
546  *  Success: TRUE
547  *  Failure: FALSE, path is not a valid unix directory or out of memory
548  *
549  * NOTES
550  *  The array of PIDLs and each PIDL are allocated with SHAlloc. You'll have
551  *  to release each PIDL as well as the array itself with SHFree.
552  */
553 static BOOL UNIXFS_build_subfolder_pidls(UnixFolder *pUnixFolder)
554 {
555     struct dirent *pDirEntry;
556     struct stat fileStat;
557     DIR *dir;
558     DWORD cDirEntries, i;
559     USHORT sLen;
560     char *pszFQPath;
561     BOOL bParentIsFS = dwRootAttr & SFGAO_FILESYSTEM;
562     
563     TRACE("(pUnixFolder=%p)\n", pUnixFolder);
564     
565     pUnixFolder->m_apidlSubDirs = NULL;
566     pUnixFolder->m_cSubDirs = 0;
567
568     if (pUnixFolder->m_pidlLocation) {
569         StatStruct *statStruct = 
570             LPSTATSTRUCT_FROM_LPSHITEMID(ILFindLastID(pUnixFolder->m_pidlLocation));
571         if (statStruct) bParentIsFS = statStruct->sfAttr & SFGAO_FILESYSTEM;
572     }
573   
574     dir = opendir(pUnixFolder->m_pszPath);
575     if (!dir) {
576         WARN("Failed to open directory '%s'.\n", pUnixFolder->m_pszPath);
577         return FALSE;
578     }
579
580     /* Allocate space for fully qualified paths */
581     pszFQPath = SHAlloc(strlen(pUnixFolder->m_pszPath) + PATH_MAX);
582     if (!pszFQPath) {
583         WARN("SHAlloc failed!\n");
584         return FALSE;
585     }
586  
587     /* Count number of directory entries. */
588     for (cDirEntries = 0, pDirEntry = readdir(dir); pDirEntry; pDirEntry = readdir(dir)) { 
589         if (!strcmp(pDirEntry->d_name, ".") || !strcmp(pDirEntry->d_name, "..")) continue;
590         sprintf(pszFQPath, "%s%s", pUnixFolder->m_pszPath, pDirEntry->d_name);
591         if (!stat(pszFQPath, &fileStat) && (S_ISDIR(fileStat.st_mode) || S_ISREG(fileStat.st_mode))) cDirEntries++;
592     }
593
594     /* If there are no entries, we are done. */
595     if (cDirEntries == 0) {
596         closedir(dir);
597         SHFree(pszFQPath);
598         return TRUE;
599     }
600
601     /* Allocate the array of PIDLs */
602     pUnixFolder->m_apidlSubDirs = SHAlloc(cDirEntries * sizeof(LPITEMIDLIST));
603     if (!pUnixFolder->m_apidlSubDirs) {
604         WARN("SHAlloc failed!\n");
605         return FALSE;
606     }
607   
608     /* Allocate and initialize one SHITEMID per sub-directory. */
609     for (rewinddir(dir), pDirEntry = readdir(dir), i = 0; pDirEntry; pDirEntry = readdir(dir)) {
610         LPSHITEMID pid;
611             
612         if (!strcmp(pDirEntry->d_name, ".") || !strcmp(pDirEntry->d_name, "..")) continue;
613
614         sprintf(pszFQPath, "%s%s", pUnixFolder->m_pszPath, pDirEntry->d_name);
615    
616         sLen = strlen(pDirEntry->d_name);
617         pid = (LPSHITEMID)SHAlloc(SHITEMID_LEN_FROM_NAME_LEN(sLen)+sizeof(USHORT));
618         if (!pid) {
619             WARN("SHAlloc failed!\n");
620             return FALSE;
621         }
622         if (!UNIXFS_build_shitemid(pszFQPath, bParentIsFS, pid)) {
623             SHFree(pid);
624             continue;
625         }
626         memset(((PBYTE)pid)+pid->cb, 0, sizeof(USHORT));
627
628         pUnixFolder->m_apidlSubDirs[i++] = (LPITEMIDLIST)pid;
629     }
630     
631     pUnixFolder->m_cSubDirs = i;    
632     closedir(dir);
633     SHFree(pszFQPath);
634
635     return TRUE;
636 }
637
638 /******************************************************************************
639  * UnixFolder
640  *
641  * Class whose heap based instances represent unix filesystem directories.
642  */
643
644 static void UnixFolder_Destroy(UnixFolder *pUnixFolder) {
645     DWORD i;
646
647     TRACE("(pUnixFolder=%p)\n", pUnixFolder);
648     
649     if (pUnixFolder->m_apidlSubDirs) 
650         for (i=0; i < pUnixFolder->m_cSubDirs; i++) 
651             SHFree(pUnixFolder->m_apidlSubDirs[i]);
652     SHFree(pUnixFolder->m_apidlSubDirs);
653     SHFree(pUnixFolder->m_pszPath);
654     ILFree(pUnixFolder->m_pidlLocation);
655     SHFree(pUnixFolder);
656 }
657
658 static HRESULT WINAPI UnixFolder_IShellFolder2_QueryInterface(IShellFolder2 *iface, REFIID riid, 
659     void **ppv) 
660 {
661     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
662         
663     TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
664     
665     if (!ppv) return E_INVALIDARG;
666     
667     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IShellFolder, riid) || 
668         IsEqualIID(&IID_IShellFolder2, riid)) 
669     {
670         *ppv = &This->lpIShellFolder2Vtbl;
671     } else if (IsEqualIID(&IID_IPersistFolder2, riid) || IsEqualIID(&IID_IPersistFolder, riid) || 
672                IsEqualIID(&IID_IPersist, riid)) 
673     {
674         *ppv = &This->lpIPersistFolder2Vtbl;
675     } else {
676         *ppv = NULL;
677         return E_NOINTERFACE;
678     }
679
680     IUnknown_AddRef((IUnknown*)*ppv);
681     return S_OK;
682 }
683
684 static ULONG WINAPI UnixFolder_IShellFolder2_AddRef(IShellFolder2 *iface) {
685     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
686
687     TRACE("(iface=%p)\n", iface);
688
689     return InterlockedIncrement(&This->m_cRef);
690 }
691
692 static ULONG WINAPI UnixFolder_IShellFolder2_Release(IShellFolder2 *iface) {
693     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
694     ULONG cRef;
695     
696     TRACE("(iface=%p)\n", iface);
697
698     cRef = InterlockedDecrement(&This->m_cRef);
699     
700     if (!cRef) 
701         UnixFolder_Destroy(This);
702
703     return cRef;
704 }
705
706 static HRESULT WINAPI UnixFolder_IShellFolder2_ParseDisplayName(IShellFolder2* iface, HWND hwndOwner, 
707     LPBC pbcReserved, LPOLESTR lpszDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl, 
708     ULONG* pdwAttributes)
709 {
710     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
711     BOOL result;
712
713     TRACE("(iface=%p, hwndOwner=%p, pbcReserved=%p, lpszDisplayName=%s, pchEaten=%p, ppidl=%p, "
714           "pdwAttributes=%p) stub\n", iface, hwndOwner, pbcReserved, debugstr_w(lpszDisplayName), 
715           pchEaten, ppidl, pdwAttributes);
716
717     result = UNIXFS_path_to_pidl(This, lpszDisplayName, ppidl);
718     if (result && pdwAttributes && *pdwAttributes)
719     {
720         /* need to traverse to the last element for the attribute */
721         LPCITEMIDLIST pidl, last_pidl;
722         pidl = last_pidl = *ppidl;
723         while(pidl && pidl->mkid.cb)
724         {
725             last_pidl = pidl;
726             pidl = ILGetNext(pidl);
727         }
728         SHELL32_GetItemAttributes((IShellFolder*)iface, last_pidl, pdwAttributes);
729     }
730
731     if (!result) TRACE("FAILED!\n");
732     return result ? S_OK : E_FAIL;
733 }
734
735 static IUnknown *UnixSubFolderIterator_Construct(UnixFolder *pUnixFolder, SHCONTF fFilter);
736
737 static HRESULT WINAPI UnixFolder_IShellFolder2_EnumObjects(IShellFolder2* iface, HWND hwndOwner, 
738     SHCONTF grfFlags, IEnumIDList** ppEnumIDList)
739 {
740     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
741     IUnknown *newIterator;
742     HRESULT hr;
743     
744     TRACE("(iface=%p, hwndOwner=%p, grfFlags=%08lx, ppEnumIDList=%p)\n", 
745             iface, hwndOwner, grfFlags, ppEnumIDList);
746
747     if (This->m_cSubDirs == -1) 
748         UNIXFS_build_subfolder_pidls(This);
749     
750     newIterator = UnixSubFolderIterator_Construct(This, grfFlags);
751     hr = IUnknown_QueryInterface(newIterator, &IID_IEnumIDList, (void**)ppEnumIDList);
752     IUnknown_Release(newIterator);
753     
754     return hr;
755 }
756
757 static HRESULT WINAPI UnixFolder_IShellFolder2_BindToObject(IShellFolder2* iface, LPCITEMIDLIST pidl,
758     LPBC pbcReserved, REFIID riid, void** ppvOut)
759 {
760     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
761     IPersistFolder2 *persistFolder;
762     LPITEMIDLIST pidlSubFolder;
763     HRESULT hr;
764         
765     TRACE("(iface=%p, pidl=%p, pbcReserver=%p, riid=%p, ppvOut=%p)\n", 
766             iface, pidl, pbcReserved, riid, ppvOut);
767
768     if (!pidl || !pidl->mkid.cb)
769         return E_INVALIDARG;
770     
771     if (This->m_dwPathMode == PATHMODE_DOS)
772         hr = UnixDosFolder_Constructor(NULL, &IID_IPersistFolder2, (void**)&persistFolder);
773     else
774         hr = UnixFolder_Constructor(NULL, &IID_IPersistFolder2, (void**)&persistFolder);
775         
776     if (!SUCCEEDED(hr)) return hr;
777     hr = IPersistFolder_QueryInterface(persistFolder, riid, (void**)ppvOut);
778     
779     pidlSubFolder = ILCombine(This->m_pidlLocation, pidl);
780     IPersistFolder2_Initialize(persistFolder, pidlSubFolder);
781     IPersistFolder2_Release(persistFolder);
782     ILFree(pidlSubFolder);
783
784     return hr;
785 }
786
787 static HRESULT WINAPI UnixFolder_IShellFolder2_BindToStorage(IShellFolder2* This, LPCITEMIDLIST pidl, 
788     LPBC pbcReserved, REFIID riid, void** ppvObj)
789 {
790     TRACE("stub\n");
791     return E_NOTIMPL;
792 }
793
794 static HRESULT WINAPI UnixFolder_IShellFolder2_CompareIDs(IShellFolder2* iface, LPARAM lParam, 
795     LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
796 {
797     BOOL isEmpty1, isEmpty2;
798     HRESULT hr = E_FAIL;
799     LPITEMIDLIST firstpidl;
800     IShellFolder2 *psf;
801     int compare;
802
803     TRACE("(iface=%p, lParam=%ld, pidl1=%p, pidl2=%p)\n", iface, lParam, pidl1, pidl2);
804     
805     isEmpty1 = !pidl1 || !pidl1->mkid.cb;
806     isEmpty2 = !pidl2 || !pidl2->mkid.cb;
807
808     if (isEmpty1 && isEmpty2) 
809         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
810     else if (isEmpty1)
811         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
812     else if (isEmpty2)
813         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
814
815     if (_ILIsFolder(pidl1) && !_ILIsFolder(pidl2)) 
816         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
817     if (!_ILIsFolder(pidl1) && _ILIsFolder(pidl2))
818         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
819
820     compare = CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE, 
821                              _ILGetTextPointer(pidl1), NAME_LEN_FROM_LPSHITEMID(pidl1),
822                              _ILGetTextPointer(pidl2), NAME_LEN_FROM_LPSHITEMID(pidl2));
823     
824     if ((compare == CSTR_LESS_THAN) || (compare == CSTR_GREATER_THAN)) 
825         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)((compare == CSTR_LESS_THAN)?-1:1));
826
827     if (pidl1->mkid.cb < pidl2->mkid.cb)
828         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
829     else if (pidl1->mkid.cb > pidl2->mkid.cb)
830         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
831
832     firstpidl = ILCloneFirst(pidl1);
833     pidl1 = ILGetNext(pidl1);
834     pidl2 = ILGetNext(pidl2);
835     
836     hr = IShellFolder2_BindToObject(iface, firstpidl, NULL, &IID_IShellFolder, (LPVOID*)&psf);
837     if (SUCCEEDED(hr)) {
838         hr = IShellFolder_CompareIDs(psf, lParam, pidl1, pidl2);
839         IShellFolder2_Release(psf);
840     }
841
842     ILFree(firstpidl);
843     return hr;
844 }
845
846 static HRESULT WINAPI UnixFolder_IShellFolder2_CreateViewObject(IShellFolder2* iface, HWND hwndOwner,
847     REFIID riid, void** ppv)
848 {
849     HRESULT hr = E_INVALIDARG;
850         
851     TRACE("(iface=%p, hwndOwner=%p, riid=%p, ppv=%p) stub\n", iface, hwndOwner, riid, ppv);
852     
853     if (!ppv) return E_INVALIDARG;
854     *ppv = NULL;
855     
856     if (IsEqualIID(&IID_IShellView, riid)) {
857         LPSHELLVIEW pShellView;
858         
859         pShellView = IShellView_Constructor((IShellFolder*)iface);
860         if (pShellView) {
861             hr = IShellView_QueryInterface(pShellView, riid, ppv);
862             IShellView_Release(pShellView);
863         }
864     } 
865     
866     return hr;
867 }
868
869 static HRESULT WINAPI UnixFolder_IShellFolder2_GetAttributesOf(IShellFolder2* iface, UINT cidl, 
870     LPCITEMIDLIST* apidl, SFGAOF* rgfInOut)
871 {
872     UINT i;
873     SFGAOF flags= ~(SFGAOF)0;
874         
875     TRACE("(iface=%p, cidl=%u, apidl=%p, rgfInOut=%p) semi-stub\n", iface, cidl, apidl, rgfInOut);
876    
877     for (i=0; i<cidl; i++) 
878         flags &= LPSTATSTRUCT_FROM_LPSHITEMID(apidl[i])->sfAttr;
879     
880     *rgfInOut = *rgfInOut & flags;
881             
882     return S_OK;
883 }
884
885 static HRESULT WINAPI UnixFolder_IShellFolder2_GetUIObjectOf(IShellFolder2* iface, HWND hwndOwner, 
886     UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void** ppvOut)
887 {
888     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
889     
890     TRACE("(iface=%p, hwndOwner=%p, cidl=%d, apidl=%p, riid=%s, prgfInOut=%p, ppv=%p)\n",
891         iface, hwndOwner, cidl, apidl, debugstr_guid(riid), prgfInOut, ppvOut);
892
893     if (IsEqualIID(&IID_IContextMenu, riid)) {
894         *ppvOut = ISvItemCm_Constructor((IShellFolder*)iface, This->m_pidlLocation, apidl, cidl);
895         return S_OK;
896     } else if (IsEqualIID(&IID_IDataObject, riid)) {
897         *ppvOut = IDataObject_Constructor(hwndOwner, This->m_pidlLocation, apidl, cidl);
898         return S_OK;
899     } else if (IsEqualIID(&IID_IExtractIconA, riid)) {
900         LPITEMIDLIST pidl;
901         if (cidl != 1) return E_FAIL;
902         pidl = ILCombine(This->m_pidlLocation, apidl[0]);
903         *ppvOut = (LPVOID)IExtractIconA_Constructor(pidl);
904         SHFree(pidl);
905         return S_OK;
906     } else if (IsEqualIID(&IID_IExtractIconW, riid)) {
907         LPITEMIDLIST pidl;
908         if (cidl != 1) return E_FAIL;
909         pidl = ILCombine(This->m_pidlLocation, apidl[0]);
910         *ppvOut = (LPVOID)IExtractIconW_Constructor(pidl);
911         SHFree(pidl);
912         return S_OK;
913     } else if (IsEqualIID(&IID_IDropTarget, riid)) {
914         FIXME("IDropTarget\n");
915         return E_FAIL;
916     } else if (IsEqualIID(&IID_IShellLinkW, riid)) {
917         FIXME("IShellLinkW\n");
918         return E_FAIL;
919     } else if (IsEqualIID(&IID_IShellLinkA, riid)) {
920         FIXME("IShellLinkA\n");
921         return E_FAIL;
922     } else {
923         FIXME("Unknown interface %s in GetUIObjectOf\n", debugstr_guid(riid));
924         return E_NOINTERFACE;
925     }
926 }
927
928 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDisplayNameOf(IShellFolder2* iface, 
929     LPCITEMIDLIST pidl, SHGDNF uFlags, STRRET* lpName)
930 {
931     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
932     HRESULT hr = S_OK;    
933
934     TRACE("(iface=%p, pidl=%p, uFlags=%lx, lpName=%p)\n", iface, pidl, uFlags, lpName);
935     
936     if ((GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING) &&
937         (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER))
938     {
939         if (!pidl->mkid.cb) {
940             lpName->uType = STRRET_CSTR;
941             strcpy(lpName->u.cStr, This->m_pszPath);
942             if (This->m_dwPathMode == PATHMODE_DOS) {
943                 char path[MAX_PATH];
944                 GetFullPathNameA(lpName->u.cStr, MAX_PATH, path, NULL);
945                 PathRemoveBackslashA(path);
946                 strcpy(lpName->u.cStr, path);
947             }
948         } else {
949             IShellFolder *pSubFolder;
950             USHORT emptyIDL = 0;
951
952             hr = IShellFolder_BindToObject(iface, pidl, NULL, &IID_IShellFolder, (void**)&pSubFolder);
953             if (!SUCCEEDED(hr)) return hr;
954        
955             hr = IShellFolder_GetDisplayNameOf(pSubFolder, (LPITEMIDLIST)&emptyIDL, uFlags, lpName);
956             IShellFolder_Release(pSubFolder);
957         }
958     } else {
959         char *pszFileName = _ILGetTextPointer(pidl);
960         lpName->uType = STRRET_CSTR;
961         strcpy(lpName->u.cStr, pszFileName ? pszFileName : "");
962     }
963
964     TRACE("--> %s\n", lpName->u.cStr);
965     
966     return hr;
967 }
968
969 static HRESULT WINAPI UnixFolder_IShellFolder2_SetNameOf(IShellFolder2* This, HWND hwnd, 
970     LPCITEMIDLIST pidl, LPCOLESTR lpszName, SHGDNF uFlags, LPITEMIDLIST* ppidlOut)
971 {
972     TRACE("stub\n");
973     return E_NOTIMPL;
974 }
975
976 static HRESULT WINAPI UnixFolder_IShellFolder2_EnumSearches(IShellFolder2* iface, 
977     IEnumExtraSearch **ppEnum) 
978 {
979     TRACE("stub\n");
980     return E_NOTIMPL;
981 }
982
983 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumn(IShellFolder2* iface, 
984     DWORD dwReserved, ULONG *pSort, ULONG *pDisplay) 
985 {
986     TRACE("stub\n");
987     return E_NOTIMPL;
988 }
989
990 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumnState(IShellFolder2* iface, 
991     UINT iColumn, SHCOLSTATEF *pcsFlags)
992 {
993     TRACE("stub\n");
994     return E_NOTIMPL;
995 }
996
997 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultSearchGUID(IShellFolder2* iface, 
998     GUID *pguid)
999 {
1000     TRACE("stub\n");
1001     return E_NOTIMPL;
1002 }
1003
1004 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsEx(IShellFolder2* iface, 
1005     LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
1006 {
1007     TRACE("stub\n");
1008     return E_NOTIMPL;
1009 }
1010
1011 #define SHELLVIEWCOLUMNS 6 
1012
1013 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsOf(IShellFolder2* iface, 
1014     LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *psd)
1015 {
1016     HRESULT hr = E_FAIL;
1017     struct passwd *pPasswd;
1018     struct group *pGroup;
1019     static const shvheader SFHeader[SHELLVIEWCOLUMNS] = {
1020         {IDS_SHV_COLUMN1,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
1021         {IDS_SHV_COLUMN5,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
1022         {IDS_SHV_COLUMN10, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7},
1023         {IDS_SHV_COLUMN11, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7},
1024         {IDS_SHV_COLUMN2,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 8},
1025         {IDS_SHV_COLUMN4,  SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10}
1026     };
1027
1028     TRACE("(iface=%p, pidl=%p, iColumn=%d, psd=%p) stub\n", iface, pidl, iColumn, psd);
1029     
1030     if (!psd || iColumn >= SHELLVIEWCOLUMNS)
1031         return E_INVALIDARG;
1032
1033     if (!pidl) {
1034         psd->fmt = SFHeader[iColumn].fmt;
1035         psd->cxChar = SFHeader[iColumn].cxChar;
1036         psd->str.uType = STRRET_CSTR;
1037         LoadStringA(shell32_hInstance, SFHeader[iColumn].colnameid, psd->str.u.cStr, MAX_PATH);
1038         return S_OK;
1039     } else {
1040         StatStruct *pStatStruct = LPSTATSTRUCT_FROM_LPSHITEMID(pidl);
1041         psd->str.u.cStr[0] = '\0';
1042         psd->str.uType = STRRET_CSTR;
1043         switch (iColumn) {
1044             case 0:
1045                 hr = IShellFolder2_GetDisplayNameOf(iface, pidl, SHGDN_NORMAL|SHGDN_INFOLDER, &psd->str);
1046                 break;
1047             case 1:
1048                 psd->str.u.cStr[0] = S_ISDIR(pStatStruct->st_mode) ? 'd' : '-';
1049                 psd->str.u.cStr[1] = (pStatStruct->st_mode & S_IRUSR) ? 'r' : '-';
1050                 psd->str.u.cStr[2] = (pStatStruct->st_mode & S_IWUSR) ? 'w' : '-';
1051                 psd->str.u.cStr[3] = (pStatStruct->st_mode & S_IXUSR) ? 'x' : '-';
1052                 psd->str.u.cStr[4] = (pStatStruct->st_mode & S_IRGRP) ? 'r' : '-';
1053                 psd->str.u.cStr[5] = (pStatStruct->st_mode & S_IWGRP) ? 'w' : '-';
1054                 psd->str.u.cStr[6] = (pStatStruct->st_mode & S_IXGRP) ? 'x' : '-';
1055                 psd->str.u.cStr[7] = (pStatStruct->st_mode & S_IROTH) ? 'r' : '-';
1056                 psd->str.u.cStr[8] = (pStatStruct->st_mode & S_IWOTH) ? 'w' : '-';
1057                 psd->str.u.cStr[9] = (pStatStruct->st_mode & S_IXOTH) ? 'x' : '-';
1058                 psd->str.u.cStr[10] = '\0';
1059                 break;
1060             case 2:
1061                 pPasswd = getpwuid(pStatStruct->st_uid);
1062                 if (pPasswd) strcpy(psd->str.u.cStr, pPasswd->pw_name);
1063                 break;
1064             case 3:
1065                 pGroup = getgrgid(pStatStruct->st_gid);
1066                 if (pGroup) strcpy(psd->str.u.cStr, pGroup->gr_name);
1067                 break;
1068             case 4:
1069                 _ILGetFileSize(pidl, psd->str.u.cStr, MAX_PATH);
1070                 break;
1071             case 5:
1072                 _ILGetFileDate(pidl, psd->str.u.cStr, MAX_PATH);
1073                 break;
1074         }
1075     }
1076     
1077     return hr;
1078 }
1079
1080 static HRESULT WINAPI UnixFolder_IShellFolder2_MapColumnToSCID(IShellFolder2* iface, UINT iColumn,
1081     SHCOLUMNID *pscid) 
1082 {
1083     TRACE("stub\n");
1084     return E_NOTIMPL;
1085 }
1086
1087 /* VTable for UnixFolder's IShellFolder2 interface.
1088  */
1089 static const IShellFolder2Vtbl UnixFolder_IShellFolder2_Vtbl = {
1090     UnixFolder_IShellFolder2_QueryInterface,
1091     UnixFolder_IShellFolder2_AddRef,
1092     UnixFolder_IShellFolder2_Release,
1093     UnixFolder_IShellFolder2_ParseDisplayName,
1094     UnixFolder_IShellFolder2_EnumObjects,
1095     UnixFolder_IShellFolder2_BindToObject,
1096     UnixFolder_IShellFolder2_BindToStorage,
1097     UnixFolder_IShellFolder2_CompareIDs,
1098     UnixFolder_IShellFolder2_CreateViewObject,
1099     UnixFolder_IShellFolder2_GetAttributesOf,
1100     UnixFolder_IShellFolder2_GetUIObjectOf,
1101     UnixFolder_IShellFolder2_GetDisplayNameOf,
1102     UnixFolder_IShellFolder2_SetNameOf,
1103     UnixFolder_IShellFolder2_GetDefaultSearchGUID,
1104     UnixFolder_IShellFolder2_EnumSearches,
1105     UnixFolder_IShellFolder2_GetDefaultColumn,
1106     UnixFolder_IShellFolder2_GetDefaultColumnState,
1107     UnixFolder_IShellFolder2_GetDetailsEx,
1108     UnixFolder_IShellFolder2_GetDetailsOf,
1109     UnixFolder_IShellFolder2_MapColumnToSCID
1110 };
1111
1112 static HRESULT WINAPI UnixFolder_IPersistFolder2_QueryInterface(IPersistFolder2* This, REFIID riid, 
1113     void** ppvObject)
1114 {
1115     return UnixFolder_IShellFolder2_QueryInterface(
1116                 (IShellFolder2*)ADJUST_THIS(UnixFolder, IPersistFolder2, This), riid, ppvObject);
1117 }
1118
1119 static ULONG WINAPI UnixFolder_IPersistFolder2_AddRef(IPersistFolder2* This)
1120 {
1121     return UnixFolder_IShellFolder2_AddRef(
1122                 (IShellFolder2*)ADJUST_THIS(UnixFolder, IPersistFolder2, This));
1123 }
1124
1125 static ULONG WINAPI UnixFolder_IPersistFolder2_Release(IPersistFolder2* This)
1126 {
1127     return UnixFolder_IShellFolder2_Release(
1128                 (IShellFolder2*)ADJUST_THIS(UnixFolder, IPersistFolder2, This));
1129 }
1130
1131 static HRESULT WINAPI UnixFolder_IPersistFolder2_GetClassID(IPersistFolder2* This, CLSID* pClassID)
1132 {
1133     TRACE("stub\n");
1134     return E_NOTIMPL;
1135 }
1136
1137 static HRESULT WINAPI UnixFolder_IPersistFolder2_Initialize(IPersistFolder2* iface, LPCITEMIDLIST pidl)
1138 {
1139     UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder2, iface);
1140     
1141     TRACE("(iface=%p, pidl=%p)\n", iface, pidl);
1142
1143     This->m_pidlLocation = ILClone(pidl);
1144     
1145     pdump(pidl);
1146     
1147     if (!UNIXFS_pidl_to_path(pidl, This))
1148         return E_FAIL;
1149   
1150     /* Attributes of a shell namespace extension's root folder (in this case the '/' folder)
1151      * are not queried for with ShellFolder's GetAttributesOf, but looked up in the registry
1152      * (see MSDN: Creating a Shell Namespace Extension > Implementing the Basic Folder Object
1153      * Interface > Registering an Extension). When they change, we have to write them there.
1154      */
1155     if (!strcmp(This->m_pszPath, "/")) {
1156         DWORD dwTempAttr = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR;
1157         struct stat statRoot;
1158         
1159         if (stat(This->m_pszPath, &statRoot)) return E_FAIL;
1160         if (UNIXFS_is_dos_device(&statRoot)) dwTempAttr |= SFGAO_FILESYSTEM;
1161         
1162         if (dwRootAttr != dwTempAttr) {
1163             HKEY hKey;
1164             static const WCHAR wszFolderAttrValue[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
1165             static const WCHAR wszFolderAttrKey[] = { 'C','L','S','I','D','\\',
1166                 '{','9','D','2','0','A','A','E','8','-','0','6','2','5','-','4','4','B','0','-',
1167                 '9','C','A','7','-','7','1','8','8','9','C','2','2','5','4','D','9','}','\\',
1168                 'S','h','e','l','l','F','o','l','d','e','r',0 };
1169             
1170             dwRootAttr = dwTempAttr;
1171             if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszFolderAttrKey, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS) {
1172                 RegSetValueExW(hKey, wszFolderAttrValue, 0, REG_DWORD, (BYTE*)&dwTempAttr, sizeof(DWORD));
1173                 RegCloseKey(hKey);
1174             }    
1175         }
1176     }
1177     
1178     return S_OK;
1179 }
1180
1181 static HRESULT WINAPI UnixFolder_IPersistFolder2_GetCurFolder(IPersistFolder2* iface, LPITEMIDLIST* ppidl)
1182 {
1183     UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder2, iface);
1184     
1185     TRACE ("(iface=%p, ppidl=%p)\n", iface, ppidl);
1186
1187     if (!ppidl)
1188         return E_POINTER;
1189     *ppidl = ILClone (This->m_pidlLocation);
1190     return S_OK;
1191 }
1192     
1193 /* VTable for UnixFolder's IPersistFolder interface.
1194  */
1195 static const IPersistFolder2Vtbl UnixFolder_IPersistFolder2_Vtbl = {
1196     UnixFolder_IPersistFolder2_QueryInterface,
1197     UnixFolder_IPersistFolder2_AddRef,
1198     UnixFolder_IPersistFolder2_Release,
1199     UnixFolder_IPersistFolder2_GetClassID,
1200     UnixFolder_IPersistFolder2_Initialize,
1201     UnixFolder_IPersistFolder2_GetCurFolder
1202 };
1203
1204 /******************************************************************************
1205  * Unix[Dos]Folder_Constructor [Internal]
1206  *
1207  * PARAMS
1208  *  pUnkOuter [I] Outer class for aggregation. Currently ignored.
1209  *  riid      [I] Interface asked for by the client.
1210  *  ppv       [O] Pointer to an riid interface to the UnixFolder object.
1211  *
1212  * NOTES
1213  *  Those are the only functions exported from shfldr_unixfs.c. They are called from
1214  *  shellole.c's default class factory and thus have to exhibit a LPFNCREATEINSTANCE
1215  *  compatible signature.
1216  *
1217  *  The UnixDosFolder_Constructor sets the dwPathMode member to PATHMODE_DOS. This
1218  *  means that paths are converted from dos to unix and back at the interfaces.
1219  */
1220 static HRESULT CreateUnixFolder(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv, DWORD dwPathMode) {
1221     HRESULT hr = E_FAIL;
1222     UnixFolder *pUnixFolder = SHAlloc((ULONG)sizeof(UnixFolder));
1223     
1224     if(pUnixFolder) {
1225         pUnixFolder->lpIShellFolder2Vtbl = &UnixFolder_IShellFolder2_Vtbl;
1226         pUnixFolder->lpIPersistFolder2Vtbl = &UnixFolder_IPersistFolder2_Vtbl;
1227         pUnixFolder->m_cRef = 0;
1228         pUnixFolder->m_pszPath = NULL;
1229         pUnixFolder->m_apidlSubDirs = NULL;
1230         pUnixFolder->m_cSubDirs = -1;
1231         pUnixFolder->m_dwPathMode = dwPathMode;
1232
1233         UnixFolder_IShellFolder2_AddRef(STATIC_CAST(IShellFolder2, pUnixFolder));
1234         hr = UnixFolder_IShellFolder2_QueryInterface(STATIC_CAST(IShellFolder2, pUnixFolder), riid, ppv);
1235         UnixFolder_IShellFolder2_Release(STATIC_CAST(IShellFolder2, pUnixFolder));
1236     }
1237     return hr;
1238 }
1239
1240 HRESULT WINAPI UnixFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
1241     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
1242     return CreateUnixFolder(pUnkOuter, riid, ppv, PATHMODE_UNIX);
1243 }
1244
1245 HRESULT WINAPI UnixDosFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
1246     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
1247     return CreateUnixFolder(pUnkOuter, riid, ppv, PATHMODE_DOS);
1248 }
1249
1250 /******************************************************************************
1251  * UnixSubFolderIterator
1252  *
1253  * Class whose heap based objects represent iterators over the sub-directories
1254  * of a given UnixFolder object. 
1255  */
1256
1257 /* UnixSubFolderIterator object layout and typedef.
1258  */
1259 typedef struct _UnixSubFolderIterator {
1260     const IEnumIDListVtbl *lpIEnumIDListVtbl;
1261     ULONG m_cRef;
1262     UnixFolder *m_pUnixFolder;
1263     ULONG m_cIdx;
1264     SHCONTF m_fFilter;
1265 } UnixSubFolderIterator;
1266
1267 static void UnixSubFolderIterator_Destroy(UnixSubFolderIterator *iterator) {
1268     TRACE("(iterator=%p)\n", iterator);
1269         
1270     UnixFolder_IShellFolder2_Release((IShellFolder2*)iterator->m_pUnixFolder);
1271     SHFree(iterator);
1272 }
1273
1274 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_QueryInterface(IEnumIDList* iface, 
1275     REFIID riid, void** ppv)
1276 {
1277     TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
1278     
1279     if (!ppv) return E_INVALIDARG;
1280     
1281     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IEnumIDList, riid)) {
1282         *ppv = iface;
1283     } else {
1284         *ppv = NULL;
1285         return E_NOINTERFACE;
1286     }
1287
1288     IEnumIDList_AddRef(iface);
1289     return S_OK;
1290 }
1291                             
1292 static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_AddRef(IEnumIDList* iface)
1293 {
1294     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1295
1296     TRACE("(iface=%p)\n", iface);
1297    
1298     return InterlockedIncrement(&This->m_cRef);
1299 }
1300
1301 static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_Release(IEnumIDList* iface)
1302 {
1303     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1304     ULONG cRef;
1305     
1306     TRACE("(iface=%p)\n", iface);
1307
1308     cRef = InterlockedDecrement(&This->m_cRef);
1309     
1310     if (!cRef) 
1311         UnixSubFolderIterator_Destroy(This);
1312
1313     return cRef;
1314 }
1315
1316 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Next(IEnumIDList* iface, ULONG celt, 
1317     LPITEMIDLIST* rgelt, ULONG* pceltFetched)
1318 {
1319     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1320     ULONG i;
1321
1322     TRACE("(iface=%p, celt=%ld, rgelt=%p, pceltFetched=%p)\n", iface, celt, rgelt, pceltFetched);
1323    
1324     for (i=0; (i < celt) && (This->m_cIdx < This->m_pUnixFolder->m_cSubDirs); This->m_cIdx++) {
1325         LPITEMIDLIST pCurrent = This->m_pUnixFolder->m_apidlSubDirs[This->m_cIdx];
1326         if (UNIXFS_is_pidl_of_type(pCurrent, This->m_fFilter)) {
1327             rgelt[i] = ILClone(pCurrent);
1328             i++;
1329         }
1330     }
1331
1332     if (pceltFetched)
1333         *pceltFetched = i;
1334
1335     return i == celt ? S_OK : S_FALSE;
1336 }
1337
1338 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Skip(IEnumIDList* iface, ULONG celt)
1339 {
1340     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1341     ULONG i;
1342     
1343     TRACE("(iface=%p, celt=%ld)\n", iface, celt);
1344
1345     for (i=0; i < celt; i++) {
1346         while (This->m_cIdx < This->m_pUnixFolder->m_cSubDirs &&
1347                !UNIXFS_is_pidl_of_type(This->m_pUnixFolder->m_apidlSubDirs[This->m_cIdx], This->m_fFilter)) 
1348         {
1349             This->m_cIdx++;
1350         }
1351         This->m_cIdx++;
1352     }
1353     
1354     if (This->m_cIdx > This->m_pUnixFolder->m_cSubDirs) {
1355         This->m_cIdx = This->m_pUnixFolder->m_cSubDirs;
1356         return S_FALSE;
1357     } else {
1358         return S_OK;
1359     }
1360 }
1361
1362 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Reset(IEnumIDList* iface)
1363 {
1364     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1365         
1366     TRACE("(iface=%p)\n", iface);
1367
1368     This->m_cIdx = 0;
1369     
1370     return S_OK;
1371 }
1372
1373 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Clone(IEnumIDList* This, 
1374     IEnumIDList** ppenum)
1375 {
1376     TRACE("stub\n");
1377     return E_NOTIMPL;
1378 }
1379
1380 /* VTable for UnixSubFolderIterator's IEnumIDList interface.
1381  */
1382 static const IEnumIDListVtbl UnixSubFolderIterator_IEnumIDList_Vtbl = {
1383     UnixSubFolderIterator_IEnumIDList_QueryInterface,
1384     UnixSubFolderIterator_IEnumIDList_AddRef,
1385     UnixSubFolderIterator_IEnumIDList_Release,
1386     UnixSubFolderIterator_IEnumIDList_Next,
1387     UnixSubFolderIterator_IEnumIDList_Skip,
1388     UnixSubFolderIterator_IEnumIDList_Reset,
1389     UnixSubFolderIterator_IEnumIDList_Clone
1390 };
1391
1392 static IUnknown *UnixSubFolderIterator_Construct(UnixFolder *pUnixFolder, SHCONTF fFilter) {
1393     UnixSubFolderIterator *iterator;
1394
1395     TRACE("(pUnixFolder=%p)\n", pUnixFolder);
1396     
1397     iterator = SHAlloc((ULONG)sizeof(UnixSubFolderIterator));
1398     iterator->lpIEnumIDListVtbl = &UnixSubFolderIterator_IEnumIDList_Vtbl;
1399     iterator->m_cRef = 0;
1400     iterator->m_cIdx = 0;
1401     iterator->m_pUnixFolder = pUnixFolder;
1402     iterator->m_fFilter = fFilter;
1403
1404     UnixSubFolderIterator_IEnumIDList_AddRef((IEnumIDList*)iterator);
1405     UnixFolder_IShellFolder2_AddRef((IShellFolder2*)pUnixFolder);
1406     
1407     return (IUnknown*)iterator;
1408 }