shfldr_unixfs.c 88.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * UNIXFS - Shell namespace extension for the unix filesystem
 *
 * Copyright (C) 2005 Michael Jung
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20
 */

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
/*
 * As you know, windows and unix do have a different philosophy with regard to
 * the question of how a filesystem should be laid out. While we unix geeks
 * learned to love the 'one-tree-rooted-at-/' approach, windows has in fact
 * a whole forest of filesystem trees, each of which is typically identified by
 * a drive letter.
 *
 * We would like wine to integrate as smoothly as possible (that is without
 * sacrificing win32 compatibility) into the unix environment. For the
 * filesystem question, this means we really would like those windows
 * applications to work with unix path- and file-names. Unfortunately, this
 * seems to be impossible in general. Therefore we have those symbolic links
 * in wine's 'dosdevices' directory, which are used to simulate drives
 * to keep windows applications happy. And as a consequence, we have those
 * drive letters show up now and then in GUI applications running under wine,
 * which gets the unix hardcore fans all angry, shouting at us @#!&$%* wine
 * hackers that we are seducing the big companies not to port their applications
 * to unix.
 *
 * DOS paths do appear at various places in GUI applications. Sometimes, they
 * show up in the title bar of an application's window. They tend to accumulate
 * in the most-recently-used section of the file-menu. And I've even seen some
 * in a configuration dialog's edit control. In those examples, wine can't do a
44
 * lot about this, since path-names can't be told apart from ordinary strings
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
 * here. That's different in the file dialogs, though.
 *
 * With the introduction of the 'shell' in win32, Microsoft established an 
 * abstraction layer on top of the filesystem, called the shell namespace (I was
 * told that Gnome's virtual filesystem is conceptually similar). In the shell
 * namespace, one doesn't use ascii- or unicode-strings to uniquely identify
 * objects. Instead Microsoft introduced item-identifier-lists (The c type is 
 * called ITEMIDLIST) as an abstraction of path-names. As you probably would
 * have guessed, an item-identifier-list is a list of item-identifiers (whose
 * c type's funny name is SHITEMID), which are opaque binary objects. This means
 * that no application (apart from Microsoft Office) should make any assumptions
 * on the internal structure of these SHITEMIDs. 
 *
 * Since the user prefers to be presented the good-old DOS file-names instead of 
 * binary ITEMIDLISTs, a translation method between string-based file-names and
 * ITEMIDLISTs was established. At the core of this are the COM-Interface
 * IShellFolder and especially it's methods ParseDisplayName and 
 * GetDisplayNameOf. Basically, you give a DOS-path (let's say C:\windows) to
 * ParseDisplayName and get a SHITEMID similar to <Desktop|My Computer|C:|windows|>.
 * Since it's opaque, you can't see the 'C', the 'windows' and the other stuff.
 * You can only figure out that the ITEMIDLIST is composed of four SHITEMIDS.
 * The file dialog applies IShellFolder's BindToObject method to bind to each of
 * those four objects (Desktop, My Computer, C: and windows. All of them have to 
 * implement the IShellFolder interface.) and asks them how they would like to be 
 * displayed (basically their icon and the string displayed). If the file dialog 
 * asks <Desktop|My Computer|C:|windows> which sub-objects it contains (via 
 * EnumObjects) it gets a list of opaque SHITEMIDs, which can be concatenated to 
 * <Desktop|...|windows> to build a new ITEMIDLIST and browse, for instance, 
 * into <system32>. This means the file dialog browses the shell namespace by 
 * identifying objects via ITEMIDLISTs. Once the user has selected a location to 
 * save his valuable file, the file dialog calls IShellFolder's GetDisplayNameOf
 * method to translate the ITEMIDLIST back to a DOS filename.
 * 
 * It seems that one intention of the shell namespace concept was to make it 
 * possible to have objects in the namespace, which don't have any counterpart 
 * in the filesystem. The 'My Computer' shell folder object is one instance
 * which comes to mind (Go try to save a file into 'My Computer' on windows.)
 * So, to make matters a little more complex, before the file dialog asks a
 * shell namespace object for it's DOS path, it asks if it actually has one.
 * This is done via the IShellFolder::GetAttributesOf method, which sets the
 * SFGAO_FILESYSTEM if - and only if - it has.
 *
 * The two things, described in the previous two paragraphs, are what unixfs is
 * based on. So basically, if UnixDosFolder's ParseDisplayName method is called 
 * with a 'c:\windows' path-name, it doesn't return an 
 * <Desktop|My Computer|C:|windows|> ITEMIDLIST. Instead, it uses 
 * shell32's wine_get_unix_path_name and the _posix_ (which means not the win32) 
 * fileio api's to figure out that c: is mapped to - let's say - 
 * /home/mjung/.wine/drive_c and then constructs a 
 * <Desktop|/|home|mjung|.wine|drive_c> ITEMIDLIST. Which is what the file 
 * dialog uses to display the folder and file objects, which is why you see a 
 * unix path. When the user has found a nice place for his file and hits the
 * save button, the ITEMIDLIST of the selected folder object is passed to 
 * GetDisplayNameOf, which returns a _DOS_ path name 
 * (like H:\home_of_my_new_file out of <|Desktop|/|home|mjung|home_of_my_new_file|>).
 * Unixfs basically mounts your dos devices together in order to construct
 * a copy of your unix filesystem structure.
 *
 * But what if none of the symbolic links in 'dosdevices' points to '/', you 
 * might ask ("And I don't want wine have access to my complete hard drive, you 
 * *%&1#!"). No problem, as I stated above, unixfs uses the _posix_ apis to 
 * construct the ITEMIDLISTs. Folders, which aren't accessible via a drive letter,
107
 * don't have the SFGAO_FILESYSTEM flag set. So the file dialogs shouldn't allow
108 109 110 111 112 113 114
 * the user to select such a folder for file storage (And if it does anyhow, it 
 * will not be able to return a valid path, since there is none). Think of those 
 * folders as a hierarchy of 'My Computer'-like folders, which happen to be a 
 * shadow of your unix filesystem tree. And since all of this stuff doesn't 
 * change anything at all in wine's fileio api's, windows applications will have 
 * no more access rights as they had before. 
 *
115
 * To sum it all up, you can still safely run wine with you root account (Just
116 117 118 119 120 121 122 123 124
 * kidding, don't do it.)
 *
 * If you are now standing in front of your computer, shouting hotly 
 * "I am not convinced, Mr. Rumsfeld^H^H^H^H^H^H^H^H^H^H^H^H", fire up regedit
 * and delete HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\
 * Explorer\Desktop\Namespace\{9D20AAE8-0625-44B0-9CA7-71889C2254D9} and you 
 * will be back in the pre-unixfs days.
 */

125
#include "config.h"
126 127
#include "wine/port.h"

128 129
#include <stdio.h>
#include <stdarg.h>
130
#include <limits.h>
131 132 133
#ifdef HAVE_DIRENT_H
# include <dirent.h>
#endif
134
#include <stdlib.h>
135 136 137
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
138 139 140 141 142 143
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
144 145 146
#ifdef HAVE_GRP_H
# include <grp.h>
#endif
147 148 149 150 151 152 153 154 155

#define COBJMACROS
#define NONAMELESSUNION
#define NONAMELESSSTRUCT

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "objbase.h"
156
#include "winreg.h"
157
#include "shlwapi.h"
158
#include "winternl.h"
159 160 161
#include "wine/debug.h"

#include "shell32_main.h"
162
#include "shellfolder.h"
163 164
#include "shfldr.h"
#include "shresdef.h"
165
#include "pidl.h"
166 167 168

WINE_DEFAULT_DEBUG_CHANNEL(shell);

169 170
#if !defined(__MINGW32__) && !defined(_MSC_VER)

171 172 173
#define ADJUST_THIS(c,m,p) ((c*)(((long)p)-(long)&(((c*)0)->lp##m##Vtbl)))
#define STATIC_CAST(i,p) ((i*)&p->lp##i##Vtbl)

174 175 176 177 178 179 180 181
#define LEN_SHITEMID_FIXED_PART ((USHORT) \
    ( sizeof(USHORT)      /* SHITEMID's cb field. */ \
    + sizeof(PIDLTYPE)    /* PIDLDATA's type field. */ \
    + sizeof(FileStruct)  /* Well, the FileStruct. */ \
    - sizeof(char)        /* One char too much in FileStruct. */ \
    + sizeof(FileStructW) /* You name it. */ \
    - sizeof(WCHAR)       /* One WCHAR too much in FileStructW. */ \
    + sizeof(WORD) ))     /* Offset of FileStructW field in PIDL. */
182

183 184 185 186 187 188
#define PATHMODE_UNIX 0
#define PATHMODE_DOS  1

/* UnixFolder object layout and typedef.
 */
typedef struct _UnixFolder {
189 190
    const IShellFolder2Vtbl       *lpIShellFolder2Vtbl;
    const IPersistFolder3Vtbl     *lpIPersistFolder3Vtbl;
191
    const IPersistPropertyBagVtbl *lpIPersistPropertyBagVtbl;
192 193
    const IDropTargetVtbl         *lpIDropTargetVtbl;
    const ISFHelperVtbl           *lpISFHelperVtbl;
194
    LONG         m_cRef;
195
    CHAR         *m_pszPath;     /* Target path of the shell folder (CP_UNIXCP) */
196
    LPITEMIDLIST m_pidlLocation; /* Location in the shell namespace */
197 198 199 200
    DWORD        m_dwPathMode;
    DWORD        m_dwAttributes;
    const CLSID  *m_pCLSID;
    DWORD        m_dwDropEffectsMask;
201 202
} UnixFolder;

203 204 205
/* Will hold the registered clipboard format identifier for ITEMIDLISTS. */
static UINT cfShellIDList = 0;

206
/******************************************************************************
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
 * UNIXFS_filename_from_shitemid [Internal]
 *
 *  Get CP_UNIXCP encoded filename corresponding to the first item of a pidl
 * 
 * PARAMS
 *  pidl           [I] A simple SHITEMID
 *  pszPathElement [O] Filename in CP_UNIXCP encoding will be stored here
 *
 * RETURNS
 *  Success: Number of bytes necessary to store the CP_UNIXCP encoded filename 
 *   _without_ the terminating NUL.
 *  Failure: 0
 *  
 * NOTES
 *  Size of the buffer at pszPathElement has to be FILENAME_MAX. pszPathElement
 *  may be NULL, if you are only interested in the return value. 
 */
static int UNIXFS_filename_from_shitemid(LPCITEMIDLIST pidl, char* pszPathElement) {
    FileStructW *pFileStructW = _ILGetFileStructW(pidl);
    int cLen = 0;

    if (pFileStructW) {
        cLen = WideCharToMultiByte(CP_UNIXCP, 0, pFileStructW->wszName, -1, pszPathElement,
            pszPathElement ? FILENAME_MAX : 0, 0, 0);
    } else {
        /* There might be pidls slipping in from shfldr_fs.c, which don't contain the
         * FileStructW field. In this case, we have to convert from CP_ACP to CP_UNIXCP. */
        char *pszText = _ILGetTextPointer(pidl);
        WCHAR *pwszPathElement = NULL;
        int cWideChars;
    
        cWideChars = MultiByteToWideChar(CP_ACP, 0, pszText, -1, NULL, 0);
        if (!cWideChars) goto cleanup;

        pwszPathElement = SHAlloc(cWideChars * sizeof(WCHAR));
        if (!pwszPathElement) goto cleanup;
    
        cWideChars = MultiByteToWideChar(CP_ACP, 0, pszText, -1, pwszPathElement, cWideChars);
        if (!cWideChars) goto cleanup; 

        cLen = WideCharToMultiByte(CP_UNIXCP, 0, pwszPathElement, -1, pszPathElement, 
            pszPathElement ? FILENAME_MAX : 0, 0, 0);

    cleanup:
        SHFree(pwszPathElement);
    }
        
    if (cLen) cLen--; /* Don't count terminating NUL! */
    return cLen;
}

/******************************************************************************
 * UNIXFS_shitemid_len_from_filename [Internal]
 *
 * Computes the necessary length of a pidl to hold a path element
 *
 * PARAMS
 *  szPathElement    [I] The path element string in CP_UNIXCP encoding.
 *  ppszPathElement  [O] Path element string in CP_ACP encoding.
 *  ppwszPathElement [O] Path element string as WCHAR string.
 *
 * RETURNS
 *  Success: Length in bytes of a SHITEMID representing szPathElement
 *  Failure: 0
 * 
 * NOTES
 *  Provide NULL values if not interested in pp(w)szPathElement. Otherwise
 *  caller is responsible to free ppszPathElement and ppwszPathElement with
 *  SHFree.
 */
static USHORT UNIXFS_shitemid_len_from_filename(
    const char *szPathElement, char **ppszPathElement, WCHAR **ppwszPathElement) 
{
    USHORT cbPidlLen = 0;
    WCHAR *pwszPathElement = NULL;
    char *pszPathElement = NULL;
    int cWideChars, cChars;

    /* There and Back Again: A Hobbit's Holiday. CP_UNIXCP might be some ANSI
     * codepage or it might be a real multi-byte encoding like utf-8. There is no
     * other way to figure out the length of the corresponding WCHAR and CP_ACP 
     * strings without actually doing the full CP_UNIXCP -> WCHAR -> CP_ACP cycle. */
    
    cWideChars = MultiByteToWideChar(CP_UNIXCP, 0, szPathElement, -1, NULL, 0);
    if (!cWideChars) goto cleanup;

    pwszPathElement = SHAlloc(cWideChars * sizeof(WCHAR));
    if (!pwszPathElement) goto cleanup;

    cWideChars = MultiByteToWideChar(CP_UNIXCP, 0, szPathElement, -1, pwszPathElement, cWideChars);
    if (!cWideChars) goto cleanup; 

    cChars = WideCharToMultiByte(CP_ACP, 0, pwszPathElement, -1, NULL, 0, 0, 0);
    if (!cChars) goto cleanup;

    pszPathElement = SHAlloc(cChars);
    if (!pszPathElement) goto cleanup;

    cChars = WideCharToMultiByte(CP_ACP, 0, pwszPathElement, -1, pszPathElement, cChars, 0, 0);
    if (!cChars) goto cleanup;

    /* (cChars & 0x1) is for the potential alignment byte */
    cbPidlLen = LEN_SHITEMID_FIXED_PART + cChars + (cChars & 0x1) + cWideChars * sizeof(WCHAR);
    
cleanup:
    if (cbPidlLen && ppszPathElement) 
        *ppszPathElement = pszPathElement;
    else 
        SHFree(pszPathElement);

    if (cbPidlLen && ppwszPathElement)
        *ppwszPathElement = pwszPathElement;
    else
        SHFree(pwszPathElement);

    return cbPidlLen;
}

/******************************************************************************
 * UNIXFS_is_pidl_of_type [Internal]
327 328 329 330 331 332 333 334 335 336 337
 *
 * Checks for the first SHITEMID of an ITEMIDLIST if it passes a filter.
 *
 * PARAMS
 *  pIDL    [I] The ITEMIDLIST to be checked.
 *  fFilter [I] Shell condition flags, which specify the filter.
 *
 * RETURNS
 *  TRUE, if pIDL is accepted by fFilter
 *  FALSE, otherwise
 */
338 339
static inline BOOL UNIXFS_is_pidl_of_type(LPCITEMIDLIST pIDL, SHCONTF fFilter) {
    const PIDLDATA *pIDLData = _ILGetDataPointer(pIDL);
340 341 342 343 344
    if (!(fFilter & SHCONTF_INCLUDEHIDDEN) && pIDLData && 
        (pIDLData->u.file.uFileAttribs & FILE_ATTRIBUTE_HIDDEN)) 
    {
        return FALSE;
    }
345 346 347 348
    if (_ILIsFolder(pIDL) && (fFilter & SHCONTF_FOLDERS)) return TRUE;
    if (_ILIsValue(pIDL) && (fFilter & SHCONTF_NONFOLDERS)) return TRUE;
    return FALSE;
}
349

350
/******************************************************************************
351
 * UNIXFS_get_unix_path [Internal]
352
 *
353 354
 * Convert an absolute dos path to an absolute unix path.
 * Evaluate "/.", "/.." and the symbolic links in $WINEPREFIX/dosdevices.
355 356
 *
 * PARAMS
357
 *  pszDosPath       [I] An absolute dos path
358 359 360 361 362 363
 *  pszCanonicalPath [O] Buffer of length FILENAME_MAX. Will receive the canonical path.
 *
 * RETURNS
 *  Success, TRUE
 *  Failure, FALSE - Path not existent, too long, insufficient rights, to many symlinks
 */
364
static BOOL UNIXFS_get_unix_path(LPCWSTR pszDosPath, char *pszCanonicalPath)
365
{
366
    char *pPathTail, *pElement, *pCanonicalTail, szPath[FILENAME_MAX], *pszUnixPath;
367 368
    WCHAR wszDrive[] = { '?', ':', '\\', 0 };
    int cDriveSymlinkLen;
369
    
370
    TRACE("(pszDosPath=%s, pszCanonicalPath=%p)\n", debugstr_w(pszDosPath), pszCanonicalPath);
371

372
    if (!pszDosPath || pszDosPath[1] != ':')
373 374
        return FALSE;

375 376 377 378 379
    /* Get the canonicalized unix path corresponding to the drive letter. */
    wszDrive[0] = pszDosPath[0];
    pszUnixPath = wine_get_unix_file_name(wszDrive);
    if (!pszUnixPath) return FALSE;
    cDriveSymlinkLen = strlen(pszUnixPath);
380
    pElement = realpath(pszUnixPath, szPath);
381 382
    HeapFree(GetProcessHeap(), 0, pszUnixPath);
    if (!pElement) return FALSE;
383
    if (szPath[strlen(szPath)-1] != '/') strcat(szPath, "/");
384 385

    /* Append the part relative to the drive symbolic link target. */
386
    pszUnixPath = wine_get_unix_file_name(pszDosPath);
387 388
    if (!pszUnixPath) return FALSE;
    strcat(szPath, pszUnixPath + cDriveSymlinkLen);
389
    HeapFree(GetProcessHeap(), 0, pszUnixPath);
390
    
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
    /* pCanonicalTail always points to the end of the canonical path constructed
     * thus far. pPathTail points to the still to be processed part of the input
     * path. pElement points to the path element currently investigated.
     */
    *pszCanonicalPath = '\0';
    pCanonicalTail = pszCanonicalPath;
    pPathTail = szPath;

    do {
        char cTemp;
            
        pElement = pPathTail;
        pPathTail = strchr(pPathTail+1, '/');
        if (!pPathTail) /* Last path element may not be terminated by '/'. */ 
            pPathTail = pElement + strlen(pElement);
        /* Temporarily terminate the current path element. Will be restored later. */
        cTemp = *pPathTail;
        *pPathTail = '\0';

        /* Skip "/." path elements */
        if (!strcmp("/.", pElement)) {
            *pPathTail = cTemp;
413 414
        } else if (!strcmp("/..", pElement)) {
            /* Remove last element in canonical path for "/.." elements, then skip. */
415 416 417 418 419 420
            char *pTemp = strrchr(pszCanonicalPath, '/');
            if (pTemp)
                pCanonicalTail = pTemp;
            *pCanonicalTail = '\0';
            *pPathTail = cTemp;
        } else {
421
            /* Directory or file. Copy to canonical path */
422 423 424 425 426 427 428
            if (pCanonicalTail - pszCanonicalPath + pPathTail - pElement + 1 > FILENAME_MAX)
                return FALSE;
                
            memcpy(pCanonicalTail, pElement, pPathTail - pElement + 1);
            pCanonicalTail += pPathTail - pElement;
            *pPathTail = cTemp;
        }
429
    } while (pPathTail[0] == '/');
430 431 432 433 434 435
   
    TRACE("--> %s\n", debugstr_a(pszCanonicalPath));
    
    return TRUE;
}

436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
/******************************************************************************
 * UNIXFS_seconds_since_1970_to_dos_date_time [Internal]
 *
 * Convert unix time to FAT time
 *
 * PARAMS 
 *  ss1970 [I] Unix time (seconds since 1970)
 *  pDate  [O] Corresponding FAT date
 *  pTime  [O] Corresponding FAT time
 */
static inline void UNIXFS_seconds_since_1970_to_dos_date_time(
    time_t ss1970, LPWORD pDate, LPWORD pTime)
{
    LARGE_INTEGER time;
    FILETIME fileTime;

    RtlSecondsSince1970ToTime( ss1970, &time );
    fileTime.dwLowDateTime = time.u.LowPart;
    fileTime.dwHighDateTime = time.u.HighPart;
    FileTimeToDosDateTime(&fileTime, pDate, pTime);
}

458 459 460 461
/******************************************************************************
 * UNIXFS_build_shitemid [Internal]
 *
 * Constructs a new SHITEMID for the last component of path 'pszUnixPath' into 
462
 * buffer 'pIDL'.
463 464
 *
 * PARAMS
465
 *  pszUnixPath [I] An absolute path. The SHITEMID will be built for the last component.
466 467 468 469 470 471 472 473 474 475 476
 *  pIDL        [O] SHITEMID will be constructed here.
 *
 * RETURNS
 *  Success: A pointer to the terminating '\0' character of path.
 *  Failure: NULL
 *
 * NOTES
 *  Minimum size of pIDL is SHITEMID_LEN_FROM_NAME_LEN(strlen(last_component_of_path)).
 *  If what you need is a PIDLLIST with a single SHITEMID, don't forget to append
 *  a 0 USHORT value.
 */
477
static char* UNIXFS_build_shitemid(char *pszUnixPath, void *pIDL) {
478 479
    LPPIDLDATA pIDLData;
    struct stat fileStat;
480 481 482 483 484 485
    char *pszComponentU, *pszComponentA;
    WCHAR *pwszComponentW;
    int cComponentULen, cComponentALen;
    USHORT cbLen;
    FileStructW *pFileStructW;
    WORD uOffsetW, *pOffsetW;
486 487 488 489 490 491 492

    TRACE("(pszUnixPath=%s, pIDL=%p)\n", debugstr_a(pszUnixPath), pIDL);

    /* We are only interested in regular files and directories. */
    if (stat(pszUnixPath, &fileStat)) return NULL;
    if (!S_ISDIR(fileStat.st_mode) && !S_ISREG(fileStat.st_mode)) return NULL;
    
493 494 495 496 497 498 499 500
    /* Compute the SHITEMID's length and wipe it. */
    pszComponentU = strrchr(pszUnixPath, '/') + 1;
    cComponentULen = strlen(pszComponentU);
    cbLen = UNIXFS_shitemid_len_from_filename(pszComponentU, &pszComponentA, &pwszComponentW);
    if (!cbLen) return NULL;
    memset(pIDL, 0, cbLen);
    ((LPSHITEMID)pIDL)->cb = cbLen;
    
501
    /* Set shell32's standard SHITEMID data fields. */
502
    pIDLData = _ILGetDataPointer(pIDL);
503 504
    pIDLData->type = S_ISDIR(fileStat.st_mode) ? PT_FOLDER : PT_VALUE;
    pIDLData->u.file.dwFileSize = (DWORD)fileStat.st_size;
505 506
    UNIXFS_seconds_since_1970_to_dos_date_time(fileStat.st_mtime, &pIDLData->u.file.uFileDate, 
        &pIDLData->u.file.uFileTime);
507 508
    pIDLData->u.file.uFileAttribs = 0;
    if (S_ISDIR(fileStat.st_mode)) pIDLData->u.file.uFileAttribs |= FILE_ATTRIBUTE_DIRECTORY;
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
    if (pszComponentU[0] == '.') pIDLData->u.file.uFileAttribs |=  FILE_ATTRIBUTE_HIDDEN;
    cComponentALen = lstrlenA(pszComponentA) + 1;
    memcpy(pIDLData->u.file.szNames, pszComponentA, cComponentALen);
    
    pFileStructW = (FileStructW*)(pIDLData->u.file.szNames + cComponentALen + (cComponentALen & 0x1));
    uOffsetW = (WORD)(((LPBYTE)pFileStructW) - ((LPBYTE)pIDL));
    pFileStructW->cbLen = cbLen - uOffsetW;
    UNIXFS_seconds_since_1970_to_dos_date_time(fileStat.st_mtime, &pFileStructW->uCreationDate, 
        &pFileStructW->uCreationTime);
    UNIXFS_seconds_since_1970_to_dos_date_time(fileStat.st_atime, &pFileStructW->uLastAccessDate,
        &pFileStructW->uLastAccessTime);
    lstrcpyW(pFileStructW->wszName, pwszComponentW);

    pOffsetW = (WORD*)(((LPBYTE)pIDL) + cbLen - sizeof(WORD));
    *pOffsetW = uOffsetW;
    
    SHFree(pszComponentA);
    SHFree(pwszComponentW);
    
    return pszComponentU + cComponentULen;
529 530
}

531 532 533 534
/******************************************************************************
 * UNIXFS_path_to_pidl [Internal]
 *
 * PARAMS
535
 *  pUnixFolder [I] If path is relative, pUnixFolder represents the base path
536
 *  path        [I] An absolute unix or dos path or a path relative to pUnixFolder
537
 *  ppidl       [O] The corresponding ITEMIDLIST. Release with SHFree/ILFree
538 539
 *  
 * RETURNS
540 541
 *  Success: S_OK
 *  Failure: Error code, invalid params or out of memory
542 543 544
 *
 * NOTES
 *  pUnixFolder also carries the information if the path is expected to be unix or dos.
545
 */
546
static HRESULT UNIXFS_path_to_pidl(UnixFolder *pUnixFolder, const WCHAR *path, LPITEMIDLIST *ppidl) {
547
    LPITEMIDLIST pidl;
548 549 550
    int cPidlLen, cPathLen;
    char *pSlash, *pNextSlash, szCompletePath[FILENAME_MAX], *pNextPathElement, *pszAPath;
    WCHAR *pwszPath;
551 552 553 554

    TRACE("pUnixFolder=%p, path=%s, ppidl=%p\n", pUnixFolder, debugstr_w(path), ppidl);
   
    if (!ppidl || !path)
555
        return E_INVALIDARG;
556

557 558 559 560 561 562 563
    /* Build an absolute path and let pNextPathElement point to the interesting 
     * relative sub-path. We need the absolute path to call 'stat', but the pidl
     * will only contain the relative part.
     */
    if ((pUnixFolder->m_dwPathMode == PATHMODE_DOS) && (path[1] == ':')) 
    {
        /* Absolute dos path. Convert to unix */
564
        if (!UNIXFS_get_unix_path(path, szCompletePath))
565
            return E_FAIL;
566
        pNextPathElement = szCompletePath;
567 568 569 570
    } 
    else if ((pUnixFolder->m_dwPathMode == PATHMODE_UNIX) && (path[0] == '/')) 
    {
        /* Absolute unix path. Just convert to ANSI. */
571
        WideCharToMultiByte(CP_UNIXCP, 0, path, -1, szCompletePath, FILENAME_MAX, NULL, NULL); 
572
        pNextPathElement = szCompletePath;
573 574 575 576 577 578
    } 
    else 
    {
        /* Relative dos or unix path. Concat with this folder's path */
        int cBasePathLen = strlen(pUnixFolder->m_pszPath);
        memcpy(szCompletePath, pUnixFolder->m_pszPath, cBasePathLen);
579
        WideCharToMultiByte(CP_UNIXCP, 0, path, -1, szCompletePath + cBasePathLen, 
580
                            FILENAME_MAX - cBasePathLen, NULL, NULL);
581
        pNextPathElement = szCompletePath + cBasePathLen - 1;
582 583 584 585 586 587 588 589 590
        
        /* If in dos mode, replace '\' with '/' */
        if (pUnixFolder->m_dwPathMode == PATHMODE_DOS) {
            char *pBackslash = strchr(pNextPathElement, '\\');
            while (pBackslash) {
                *pBackslash = '/';
                pBackslash = strchr(pBackslash, '\\');
            }
        }
591
    }
592

593 594
    /* Special case for the root folder. */
    if (!strcmp(szCompletePath, "/")) {
595
        *ppidl = pidl = SHAlloc(sizeof(USHORT));
596
        if (!pidl) return E_FAIL;
597
        pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
598
        return S_OK;
599 600
    }
    
601 602 603 604 605 606 607
    /* Remove trailing slash, if present */
    cPathLen = strlen(szCompletePath);
    if (szCompletePath[cPathLen-1] == '/') 
        szCompletePath[cPathLen-1] = '\0';

    if ((szCompletePath[0] != '/') || (pNextPathElement[0] != '/')) {
        ERR("szCompletePath: %s, pNextPathElment: %s\n", szCompletePath, pNextPathElement);
608
        return E_FAIL;
609 610 611 612 613 614
    }
    
    /* At this point, we have an absolute unix path in szCompletePath 
     * and the relative portion of it in pNextPathElement. Both starting with '/'
     * and _not_ terminated by a '/'. */
    TRACE("complete path: %s, relative path: %s\n", szCompletePath, pNextPathElement);
615
    
616 617
    /* Convert to CP_ACP and WCHAR */
    if (!UNIXFS_shitemid_len_from_filename(pNextPathElement, &pszAPath, &pwszPath))
618
        return E_FAIL;
619 620 621 622

    /* Compute the length of the complete ITEMIDLIST */
    cPidlLen = 0;
    pSlash = pszAPath;
623
    while (pSlash) {
624 625 626 627
        pNextSlash = strchr(pSlash+1, '/');
        cPidlLen += LEN_SHITEMID_FIXED_PART + /* Fixed part length plus potential alignment byte. */
            (pNextSlash ? (pNextSlash - pSlash) & 0x1 : lstrlenA(pSlash) & 0x1); 
        pSlash = pNextSlash;
628
    }
629 630 631 632 633 634 635 636 637

    /* The USHORT is for the ITEMIDLIST terminator. The NUL terminators for the sub-path-strings
     * are accounted for by the '/' separators, which are not stored in the SHITEMIDs. Above we
     * have ensured that the number of '/'s exactly matches the number of sub-path-strings. */
    cPidlLen += lstrlenA(pszAPath) + lstrlenW(pwszPath) * sizeof(WCHAR) + sizeof(USHORT);

    SHFree(pszAPath);
    SHFree(pwszPath);

638
    *ppidl = pidl = SHAlloc(cPidlLen);
639
    if (!pidl) return E_FAIL;
640

641
    /* Concatenate the SHITEMIDs of the sub-directories. */
642
    while (*pNextPathElement) {
643
        pSlash = strchr(pNextPathElement+1, '/');
644
        if (pSlash) *pSlash = '\0';
645
        pNextPathElement = UNIXFS_build_shitemid(szCompletePath, pidl);
646 647 648
        if (pSlash) *pSlash = '/';
            
        if (!pNextPathElement) {
649
            SHFree(*ppidl);
650
            *ppidl = NULL;
651
            return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
652
        }
653 654 655
        pidl = ILGetNext(pidl);
    }
    pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
656

657
    if ((char *)pidl-(char *)*ppidl+sizeof(USHORT) != cPidlLen) /* We've corrupted the heap :( */ 
658
        ERR("Computed length of pidl incorrect. Please report.\n");
659
    
660
    return S_OK;
661 662
}

663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
/******************************************************************************
 * UNIXFS_initialize_target_folder [Internal]
 *
 *  Initialize the m_pszPath member of an UnixFolder, given an absolute unix
 *  base path and a relative ITEMIDLIST. Leave the m_pidlLocation member, which
 *  specifies the location in the shell namespace alone. 
 * 
 * PARAMS
 *  This          [IO] The UnixFolder, whose target path is to be initialized
 *  szBasePath    [I]  The absolute base path
 *  pidlSubFolder [I]  Relative part of the path, given as an ITEMIDLIST
 *  dwAttributes  [I]  Attributes to add to the Folders m_dwAttributes member 
 *                     (Used to pass the SFGAO_FILESYSTEM flag down the path)
 * RETURNS
 *  Success: S_OK,
 *  Failure: E_FAIL
 */
static HRESULT UNIXFS_initialize_target_folder(UnixFolder *This, const char *szBasePath,
    LPCITEMIDLIST pidlSubFolder, DWORD dwAttributes)
{
    LPCITEMIDLIST current = pidlSubFolder;
    DWORD dwPathLen = strlen(szBasePath)+1;
    char *pNextDir;
686 687
    WCHAR *dos_name;

688 689
    /* Determine the path's length bytes */
    while (current && current->mkid.cb) {
690
        dwPathLen += UNIXFS_filename_from_shitemid(current, NULL) + 1; /* For the '/' */
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
        current = ILGetNext(current);
    };

    /* Build the path and compute the attributes*/
    This->m_dwAttributes = 
            dwAttributes|SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|SFGAO_CANRENAME;
    This->m_pszPath = pNextDir = SHAlloc(dwPathLen);
    if (!This->m_pszPath) {
        WARN("SHAlloc failed!\n");
        return E_FAIL;
    }
    current = pidlSubFolder;
    strcpy(pNextDir, szBasePath);
    pNextDir += strlen(szBasePath);
    if (This->m_dwPathMode == PATHMODE_UNIX || IsEqualCLSID(&CLSID_MyDocuments, This->m_pCLSID))
        This->m_dwAttributes |= SFGAO_FILESYSTEM;
    while (current && current->mkid.cb) {
708
        pNextDir += UNIXFS_filename_from_shitemid(current, pNextDir);
709 710 711 712
        *pNextDir++ = '/';
        current = ILGetNext(current);
    }
    *pNextDir='\0';
713 714 715 716 717 718 719 720

    if (!(This->m_dwAttributes & SFGAO_FILESYSTEM) &&
        ((dos_name = wine_get_dos_file_name(This->m_pszPath))))
    {
        This->m_dwAttributes |= SFGAO_FILESYSTEM;
        HeapFree( GetProcessHeap(), 0, dos_name );
    }

721 722 723
    return S_OK;
}

724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
/******************************************************************************
 * UNIXFS_copy [Internal]
 *
 *  Copy pwszDosSrc to pwszDosDst.
 *
 * PARAMS
 *  pwszDosSrc [I]  absolute path of the source
 *  pwszDosDst [I]  absolute path of the destination
 *
 * RETURNS
 *  Success: S_OK,
 *  Failure: E_FAIL
 */
static HRESULT UNIXFS_copy(LPCWSTR pwszDosSrc, LPCWSTR pwszDosDst)
{
    SHFILEOPSTRUCTW op;
    LPWSTR pwszSrc, pwszDst;
    HRESULT res = E_OUTOFMEMORY;
    UINT iSrcLen, iDstLen;

    if (!pwszDosSrc || !pwszDosDst)
        return E_FAIL;

    iSrcLen = lstrlenW(pwszDosSrc);
    iDstLen = lstrlenW(pwszDosDst);
    pwszSrc = HeapAlloc(GetProcessHeap(), 0, (iSrcLen + 2) * sizeof(WCHAR));
    pwszDst = HeapAlloc(GetProcessHeap(), 0, (iDstLen + 2) * sizeof(WCHAR));

    if (pwszSrc && pwszDst) {
        lstrcpyW(pwszSrc, pwszDosSrc);
        lstrcpyW(pwszDst, pwszDosDst);
        /* double null termination */
        pwszSrc[iSrcLen + 1] = 0;
        pwszDst[iDstLen + 1] = 0;

        ZeroMemory(&op, sizeof(op));
        op.hwnd = GetActiveWindow();
        op.wFunc = FO_COPY;
        op.pFrom = pwszSrc;
        op.pTo = pwszDst;
        op.fFlags = FOF_ALLOWUNDO;
        if (!SHFileOperationW(&op))
        {
            WARN("SHFileOperationW failed\n");
            res = E_FAIL;
        }
        else
            res = S_OK;
    }

    HeapFree(GetProcessHeap(), 0, pwszSrc);
    HeapFree(GetProcessHeap(), 0, pwszDst);
    return res;
}

779 780 781 782 783 784 785 786 787 788 789 790 791 792
/******************************************************************************
 * UnixFolder
 *
 * Class whose heap based instances represent unix filesystem directories.
 */

static void UnixFolder_Destroy(UnixFolder *pUnixFolder) {
    TRACE("(pUnixFolder=%p)\n", pUnixFolder);
    
    SHFree(pUnixFolder->m_pszPath);
    ILFree(pUnixFolder->m_pidlLocation);
    SHFree(pUnixFolder);
}

793
static HRESULT WINAPI UnixFolder_IShellFolder2_QueryInterface(IShellFolder2 *iface, REFIID riid, 
794 795
    void **ppv) 
{
796
    UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
797 798 799 800 801
        
    TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
    
    if (!ppv) return E_INVALIDARG;
    
802 803 804
    if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IShellFolder, riid) || 
        IsEqualIID(&IID_IShellFolder2, riid)) 
    {
805
        *ppv = STATIC_CAST(IShellFolder2, This);
806 807
    } else if (IsEqualIID(&IID_IPersistFolder3, riid) || IsEqualIID(&IID_IPersistFolder2, riid) || 
               IsEqualIID(&IID_IPersistFolder, riid) || IsEqualIID(&IID_IPersist, riid)) 
808
    {
809
        *ppv = STATIC_CAST(IPersistFolder3, This);
810
    } else if (IsEqualIID(&IID_IPersistPropertyBag, riid)) {
811
        *ppv = STATIC_CAST(IPersistPropertyBag, This);
812
    } else if (IsEqualIID(&IID_ISFHelper, riid)) {
813 814 815
        *ppv = STATIC_CAST(ISFHelper, This);
    } else if (IsEqualIID(&IID_IDropTarget, riid)) {
        *ppv = STATIC_CAST(IDropTarget, This);
816
        if (!cfShellIDList) 
817
            cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLISTW);
818 819 820 821 822 823 824 825 826
    } else {
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

827 828
static ULONG WINAPI UnixFolder_IShellFolder2_AddRef(IShellFolder2 *iface) {
    UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
829 830 831 832 833 834

    TRACE("(iface=%p)\n", iface);

    return InterlockedIncrement(&This->m_cRef);
}

835 836
static ULONG WINAPI UnixFolder_IShellFolder2_Release(IShellFolder2 *iface) {
    UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
837 838 839 840 841 842 843 844 845 846 847 848
    ULONG cRef;
    
    TRACE("(iface=%p)\n", iface);

    cRef = InterlockedDecrement(&This->m_cRef);
    
    if (!cRef) 
        UnixFolder_Destroy(This);

    return cRef;
}

849
static HRESULT WINAPI UnixFolder_IShellFolder2_ParseDisplayName(IShellFolder2* iface, HWND hwndOwner, 
850 851 852
    LPBC pbcReserved, LPOLESTR lpszDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl, 
    ULONG* pdwAttributes)
{
853
    UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
854
    HRESULT result;
855 856 857 858 859

    TRACE("(iface=%p, hwndOwner=%p, pbcReserved=%p, lpszDisplayName=%s, pchEaten=%p, ppidl=%p, "
          "pdwAttributes=%p) stub\n", iface, hwndOwner, pbcReserved, debugstr_w(lpszDisplayName), 
          pchEaten, ppidl, pdwAttributes);

860
    result = UNIXFS_path_to_pidl(This, lpszDisplayName, ppidl);
861
    if (SUCCEEDED(result) && pdwAttributes && *pdwAttributes)
862
    {
863 864
        IShellFolder *pParentSF;
        LPCITEMIDLIST pidlLast;
865
        LPITEMIDLIST pidlComplete = ILCombine(This->m_pidlLocation, *ppidl);
866 867
        HRESULT hr;
        
868 869
        hr = SHBindToParent(pidlComplete, &IID_IShellFolder, (LPVOID*)&pParentSF, &pidlLast);
        if (FAILED(hr)) {
870
            FIXME("SHBindToParent failed! hr = %08x\n", hr);
871 872 873
            ILFree(pidlComplete);
            return E_FAIL;
        }
874 875
        IShellFolder_GetAttributesOf(pParentSF, 1, &pidlLast, pdwAttributes);
        IShellFolder_Release(pParentSF);
876
        ILFree(pidlComplete);
877
    }
878

879 880
    if (FAILED(result)) TRACE("FAILED!\n");
    return result;
881 882
}

883
static IUnknown *UnixSubFolderIterator_Constructor(UnixFolder *pUnixFolder, SHCONTF fFilter);
884

885
static HRESULT WINAPI UnixFolder_IShellFolder2_EnumObjects(IShellFolder2* iface, HWND hwndOwner, 
886 887
    SHCONTF grfFlags, IEnumIDList** ppEnumIDList)
{
888
    UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
889 890 891
    IUnknown *newIterator;
    HRESULT hr;
    
892
    TRACE("(iface=%p, hwndOwner=%p, grfFlags=%08x, ppEnumIDList=%p)\n", 
893 894
            iface, hwndOwner, grfFlags, ppEnumIDList);

895
    if (!This->m_pszPath) {
896 897
        WARN("EnumObjects called on uninitialized UnixFolder-object!\n");
        return E_UNEXPECTED;
898 899
    }

900
    newIterator = UnixSubFolderIterator_Constructor(This, grfFlags);
901 902 903 904 905 906
    hr = IUnknown_QueryInterface(newIterator, &IID_IEnumIDList, (void**)ppEnumIDList);
    IUnknown_Release(newIterator);
    
    return hr;
}

907
static HRESULT CreateUnixFolder(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv, const CLSID *pCLSID);
908

909
static HRESULT WINAPI UnixFolder_IShellFolder2_BindToObject(IShellFolder2* iface, LPCITEMIDLIST pidl,
910 911
    LPBC pbcReserved, REFIID riid, void** ppvOut)
{
912
    UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
913
    IPersistFolder3 *persistFolder;
914
    HRESULT hr;
915
    const CLSID *clsidChild;
916 917 918 919
        
    TRACE("(iface=%p, pidl=%p, pbcReserver=%p, riid=%p, ppvOut=%p)\n", 
            iface, pidl, pbcReserved, riid, ppvOut);

920 921
    if (!pidl || !pidl->mkid.cb)
        return E_INVALIDARG;
922 923 924 925 926 927 928 929
   
    if (IsEqualCLSID(This->m_pCLSID, &CLSID_FolderShortcut)) {
        /* Children of FolderShortcuts are ShellFSFolders on Windows. 
         * Unixfs' counterpart is UnixDosFolder. */
        clsidChild = &CLSID_UnixDosFolder;    
    } else {
        clsidChild = This->m_pCLSID;
    }
930

931
    hr = CreateUnixFolder(NULL, &IID_IPersistFolder3, (void**)&persistFolder, clsidChild);
932
    if (FAILED(hr)) return hr;
933 934
    hr = IPersistFolder_QueryInterface(persistFolder, riid, ppvOut);

935
    if (SUCCEEDED(hr)) {
936 937 938 939 940 941
        UnixFolder *subfolder = ADJUST_THIS(UnixFolder, IPersistFolder3, persistFolder);
        subfolder->m_pidlLocation = ILCombine(This->m_pidlLocation, pidl);
        hr = UNIXFS_initialize_target_folder(subfolder, This->m_pszPath, pidl,
                                             This->m_dwAttributes & SFGAO_FILESYSTEM);
    } 

942
    IPersistFolder3_Release(persistFolder);
943
    
944 945 946
    return hr;
}

947
static HRESULT WINAPI UnixFolder_IShellFolder2_BindToStorage(IShellFolder2* This, LPCITEMIDLIST pidl, 
948 949
    LPBC pbcReserved, REFIID riid, void** ppvObj)
{
950
    FIXME("stub\n");
951 952 953
    return E_NOTIMPL;
}

954
static HRESULT WINAPI UnixFolder_IShellFolder2_CompareIDs(IShellFolder2* iface, LPARAM lParam, 
955 956
    LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
    BOOL isEmpty1, isEmpty2;
    HRESULT hr = E_FAIL;
    LPITEMIDLIST firstpidl;
    IShellFolder2 *psf;
    int compare;

    TRACE("(iface=%p, lParam=%ld, pidl1=%p, pidl2=%p)\n", iface, lParam, pidl1, pidl2);
    
    isEmpty1 = !pidl1 || !pidl1->mkid.cb;
    isEmpty2 = !pidl2 || !pidl2->mkid.cb;

    if (isEmpty1 && isEmpty2) 
        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
    else if (isEmpty1)
        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
    else if (isEmpty2)
        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);

975 976 977 978 979
    if (_ILIsFolder(pidl1) && !_ILIsFolder(pidl2)) 
        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
    if (!_ILIsFolder(pidl1) && _ILIsFolder(pidl2))
        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);

980
    compare = CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE, 
981 982
                             _ILGetTextPointer(pidl1), -1,
                             _ILGetTextPointer(pidl2), -1);
983
    
984
    if ((compare == CSTR_LESS_THAN) || (compare == CSTR_GREATER_THAN)) 
985
        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)((compare == CSTR_LESS_THAN)?-1:1));
986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003

    if (pidl1->mkid.cb < pidl2->mkid.cb)
        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
    else if (pidl1->mkid.cb > pidl2->mkid.cb)
        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);

    firstpidl = ILCloneFirst(pidl1);
    pidl1 = ILGetNext(pidl1);
    pidl2 = ILGetNext(pidl2);
    
    hr = IShellFolder2_BindToObject(iface, firstpidl, NULL, &IID_IShellFolder, (LPVOID*)&psf);
    if (SUCCEEDED(hr)) {
        hr = IShellFolder_CompareIDs(psf, lParam, pidl1, pidl2);
        IShellFolder2_Release(psf);
    }

    ILFree(firstpidl);
    return hr;
1004 1005
}

1006 1007
static HRESULT WINAPI UnixFolder_IShellFolder2_CreateViewObject(IShellFolder2* iface, HWND hwndOwner,
    REFIID riid, void** ppv)
1008
{
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
    HRESULT hr = E_INVALIDARG;
        
    TRACE("(iface=%p, hwndOwner=%p, riid=%p, ppv=%p) stub\n", iface, hwndOwner, riid, ppv);
    
    if (!ppv) return E_INVALIDARG;
    *ppv = NULL;
    
    if (IsEqualIID(&IID_IShellView, riid)) {
        LPSHELLVIEW pShellView;
        
        pShellView = IShellView_Constructor((IShellFolder*)iface);
        if (pShellView) {
            hr = IShellView_QueryInterface(pShellView, riid, ppv);
            IShellView_Release(pShellView);
        }
1024 1025 1026
    } else if (IsEqualIID(&IID_IDropTarget, riid)) {
        hr = IShellFolder2_QueryInterface(iface, &IID_IDropTarget, ppv);
    }
1027 1028
    
    return hr;
1029 1030
}

1031
static HRESULT WINAPI UnixFolder_IShellFolder2_GetAttributesOf(IShellFolder2* iface, UINT cidl, 
1032 1033
    LPCITEMIDLIST* apidl, SFGAOF* rgfInOut)
{
1034
    UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
1035
    HRESULT hr = S_OK;
1036
        
1037 1038 1039 1040 1041 1042
    TRACE("(iface=%p, cidl=%u, apidl=%p, rgfInOut=%p)\n", iface, cidl, apidl, rgfInOut);
 
    if (!rgfInOut || (cidl && !apidl)) 
        return E_INVALIDARG;
    
    if (cidl == 0) {
1043
        *rgfInOut &= This->m_dwAttributes;
1044
    } else {
1045
        char szAbsolutePath[FILENAME_MAX], *pszRelativePath;
1046
        UINT i;
1047

1048 1049
        *rgfInOut = SFGAO_CANCOPY|SFGAO_CANMOVE|SFGAO_CANLINK|SFGAO_CANRENAME|SFGAO_CANDELETE|
                    SFGAO_HASPROPSHEET|SFGAO_DROPTARGET|SFGAO_FILESYSTEM;
1050 1051
        lstrcpyA(szAbsolutePath, This->m_pszPath);
        pszRelativePath = szAbsolutePath + lstrlenA(szAbsolutePath);
1052
        for (i=0; i<cidl; i++) {
1053
            if (!(This->m_dwAttributes & SFGAO_FILESYSTEM)) {
1054
                WCHAR *dos_name;
1055 1056
                if (!UNIXFS_filename_from_shitemid(apidl[i], pszRelativePath)) 
                    return E_INVALIDARG;
1057
                if (!(dos_name = wine_get_dos_file_name( szAbsolutePath )))
1058
                    *rgfInOut &= ~SFGAO_FILESYSTEM;
1059 1060
                else
                    HeapFree( GetProcessHeap(), 0, dos_name );
1061
            }
1062 1063
            if (_ILIsFolder(apidl[i])) 
                *rgfInOut |= SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR;
1064 1065
        }
    }
1066
    
1067
    return hr;
1068 1069
}

1070
static HRESULT WINAPI UnixFolder_IShellFolder2_GetUIObjectOf(IShellFolder2* iface, HWND hwndOwner, 
1071 1072
    UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void** ppvOut)
{
1073
    UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
1074
    UINT i;
1075
    
1076 1077 1078
    TRACE("(iface=%p, hwndOwner=%p, cidl=%d, apidl=%p, riid=%s, prgfInOut=%p, ppv=%p)\n",
        iface, hwndOwner, cidl, apidl, debugstr_guid(riid), prgfInOut, ppvOut);

1079 1080 1081 1082 1083 1084 1085
    if (!cidl || !apidl || !riid || !ppvOut) 
        return E_INVALIDARG;

    for (i=0; i<cidl; i++) 
        if (!apidl[i]) 
            return E_INVALIDARG;
    
1086 1087 1088 1089 1090 1091 1092
    if (IsEqualIID(&IID_IContextMenu, riid)) {
        *ppvOut = ISvItemCm_Constructor((IShellFolder*)iface, This->m_pidlLocation, apidl, cidl);
        return S_OK;
    } else if (IsEqualIID(&IID_IDataObject, riid)) {
        *ppvOut = IDataObject_Constructor(hwndOwner, This->m_pidlLocation, apidl, cidl);
        return S_OK;
    } else if (IsEqualIID(&IID_IExtractIconA, riid)) {
1093
        LPITEMIDLIST pidl;
1094
        if (cidl != 1) return E_INVALIDARG;
1095
        pidl = ILCombine(This->m_pidlLocation, apidl[0]);
1096
        *ppvOut = IExtractIconA_Constructor(pidl);
1097 1098
        SHFree(pidl);
        return S_OK;
1099
    } else if (IsEqualIID(&IID_IExtractIconW, riid)) {
1100
        LPITEMIDLIST pidl;
1101
        if (cidl != 1) return E_INVALIDARG;
1102
        pidl = ILCombine(This->m_pidlLocation, apidl[0]);
1103
        *ppvOut = IExtractIconW_Constructor(pidl);
1104
        SHFree(pidl);
1105
        return S_OK;
1106
    } else if (IsEqualIID(&IID_IDropTarget, riid)) {
1107 1108
        if (cidl != 1) return E_INVALIDARG;
        return IShellFolder2_BindToObject(iface, apidl[0], NULL, &IID_IDropTarget, ppvOut);
1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
    } else if (IsEqualIID(&IID_IShellLinkW, riid)) {
        FIXME("IShellLinkW\n");
        return E_FAIL;
    } else if (IsEqualIID(&IID_IShellLinkA, riid)) {
        FIXME("IShellLinkA\n");
        return E_FAIL;
    } else {
        FIXME("Unknown interface %s in GetUIObjectOf\n", debugstr_guid(riid));
        return E_NOINTERFACE;
    }
1119 1120
}

1121
static HRESULT WINAPI UnixFolder_IShellFolder2_GetDisplayNameOf(IShellFolder2* iface, 
1122 1123
    LPCITEMIDLIST pidl, SHGDNF uFlags, STRRET* lpName)
{
1124 1125
    UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
    HRESULT hr = S_OK;    
1126

1127
    TRACE("(iface=%p, pidl=%p, uFlags=%x, lpName=%p)\n", iface, pidl, uFlags, lpName);
1128
    
1129
    if ((GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING) &&
1130 1131
        (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER))
    {
1132
        if (!pidl || !pidl->mkid.cb) {
1133
            lpName->uType = STRRET_WSTR;
1134
            if (This->m_dwPathMode == PATHMODE_UNIX) {
1135 1136 1137 1138
                UINT len = MultiByteToWideChar(CP_UNIXCP, 0, This->m_pszPath, -1, NULL, 0);
                lpName->u.pOleStr = SHAlloc(len * sizeof(WCHAR));
                if (!lpName->u.pOleStr) return HRESULT_FROM_WIN32(GetLastError());
                MultiByteToWideChar(CP_UNIXCP, 0, This->m_pszPath, -1, lpName->u.pOleStr, len);
1139
            } else {
1140 1141 1142 1143 1144 1145 1146
                LPWSTR pwszDosFileName = wine_get_dos_file_name(This->m_pszPath);
                if (!pwszDosFileName) return HRESULT_FROM_WIN32(GetLastError());
                lpName->u.pOleStr = SHAlloc((lstrlenW(pwszDosFileName) + 1) * sizeof(WCHAR));
                if (!lpName->u.pOleStr) return HRESULT_FROM_WIN32(GetLastError());
                lstrcpyW(lpName->u.pOleStr, pwszDosFileName);
                PathRemoveBackslashW(lpName->u.pOleStr);
                HeapFree(GetProcessHeap(), 0, pwszDosFileName);
1147
            }
1148 1149
        } else {
            IShellFolder *pSubFolder;
1150
            SHITEMID emptyIDL = { 0, { 0 } };
1151 1152

            hr = IShellFolder_BindToObject(iface, pidl, NULL, &IID_IShellFolder, (void**)&pSubFolder);
1153 1154
            if (FAILED(hr)) return hr;

1155 1156
            hr = IShellFolder_GetDisplayNameOf(pSubFolder, (LPITEMIDLIST)&emptyIDL, uFlags, lpName);
            IShellFolder_Release(pSubFolder);
1157 1158
        }
    } else {
1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
        WCHAR wszFileName[MAX_PATH];
        if (!_ILSimpleGetTextW(pidl, wszFileName, MAX_PATH)) return E_INVALIDARG;
        lpName->uType = STRRET_WSTR;
        lpName->u.pOleStr = SHAlloc((lstrlenW(wszFileName)+1)*sizeof(WCHAR));
        if (!lpName->u.pOleStr) return HRESULT_FROM_WIN32(GetLastError());
        lstrcpyW(lpName->u.pOleStr, wszFileName);
        if (!(GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING) && This->m_dwPathMode == PATHMODE_DOS && 
            !_ILIsFolder(pidl) && wszFileName[0] != '.' && SHELL_FS_HideExtension(wszFileName))
        {
            PathRemoveExtensionW(lpName->u.pOleStr);
        }
1170 1171
    }

1172
    TRACE("--> %s\n", debugstr_w(lpName->u.pOleStr));
1173
    
1174
    return hr;
1175 1176
}

1177
static HRESULT WINAPI UnixFolder_IShellFolder2_SetNameOf(IShellFolder2* iface, HWND hwnd, 
Michael Jung's avatar
Michael Jung committed
1178
    LPCITEMIDLIST pidl, LPCOLESTR lpcwszName, SHGDNF uFlags, LPITEMIDLIST* ppidlOut)
1179
{
1180 1181
    UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);

Michael Jung's avatar
Michael Jung committed
1182
    static const WCHAR awcInvalidChars[] = { '\\', '/', ':', '*', '?', '"', '<', '>', '|' };
1183
    char szSrc[FILENAME_MAX], szDest[FILENAME_MAX];
Michael Jung's avatar
Michael Jung committed
1184
    WCHAR wszSrcRelative[MAX_PATH];
1185 1186
    unsigned int i;
    int cBasePathLen = lstrlenA(This->m_pszPath);
1187
    struct stat statDest;
Michael Jung's avatar
Michael Jung committed
1188 1189 1190
    LPITEMIDLIST pidlSrc, pidlDest, pidlRelativeDest;
    LPOLESTR lpwszName;
    HRESULT hr;
1191
   
1192
    TRACE("(iface=%p, hwnd=%p, pidl=%p, lpcwszName=%s, uFlags=0x%08x, ppidlOut=%p)\n",
Michael Jung's avatar
Michael Jung committed
1193
          iface, hwnd, pidl, debugstr_w(lpcwszName), uFlags, ppidlOut); 
1194

Michael Jung's avatar
Michael Jung committed
1195
    /* prepare to fail */
1196 1197 1198
    if (ppidlOut)
        *ppidlOut = NULL;
    
Michael Jung's avatar
Michael Jung committed
1199 1200 1201 1202 1203 1204 1205 1206 1207
    /* pidl has to contain a single non-empty SHITEMID */
    if (_ILIsDesktop(pidl) || !_ILIsPidlSimple(pidl) || !_ILGetTextPointer(pidl))
        return E_INVALIDARG;
 
    /* check for invalid characters in lpcwszName. */
    for (i=0; i < sizeof(awcInvalidChars)/sizeof(*awcInvalidChars); i++)
        if (StrChrW(lpcwszName, awcInvalidChars[i]))
            return HRESULT_FROM_WIN32(ERROR_CANCELLED);

1208 1209
    /* build source path */
    memcpy(szSrc, This->m_pszPath, cBasePathLen);
1210
    UNIXFS_filename_from_shitemid(pidl, szSrc + cBasePathLen);
1211 1212

    /* build destination path */
Michael Jung's avatar
Michael Jung committed
1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
    memcpy(szDest, This->m_pszPath, cBasePathLen);
    WideCharToMultiByte(CP_UNIXCP, 0, lpcwszName, -1, szDest+cBasePathLen, 
                        FILENAME_MAX-cBasePathLen, NULL, NULL);

    /* If the filename's extension is hidden to the user, we have to append it. */
    if (!(uFlags & SHGDN_FORPARSING) && 
        _ILSimpleGetTextW(pidl, wszSrcRelative, MAX_PATH) && 
        SHELL_FS_HideExtension(wszSrcRelative))
    {
        WCHAR *pwszExt = PathFindExtensionW(wszSrcRelative);
        int cLenDest = strlen(szDest);
        WideCharToMultiByte(CP_UNIXCP, 0, pwszExt, -1, szDest + cLenDest, 
            FILENAME_MAX - cLenDest, NULL, NULL);
    }
1227 1228 1229 1230 1231 1232

    TRACE("src=%s dest=%s\n", szSrc, szDest);

    /* Fail, if destination does already exist */
    if (!stat(szDest, &statDest)) 
        return E_FAIL;
1233 1234 1235 1236 1237 1238

    /* Rename the file */
    if (rename(szSrc, szDest)) 
        return E_FAIL;
    
    /* Build a pidl for the path of the renamed file */
Michael Jung's avatar
Michael Jung committed
1239 1240 1241 1242 1243
    lpwszName = SHAlloc((lstrlenW(lpcwszName)+1)*sizeof(WCHAR)); /* due to const correctness. */
    lstrcpyW(lpwszName, lpcwszName);
    hr = IShellFolder2_ParseDisplayName(iface, NULL, NULL, lpwszName, NULL, &pidlRelativeDest, NULL);
    SHFree(lpwszName);
    if (FAILED(hr)) {
1244 1245
        rename(szDest, szSrc); /* Undo the renaming */
        return E_FAIL;
1246
    }
Michael Jung's avatar
Michael Jung committed
1247 1248 1249
    pidlDest = ILCombine(This->m_pidlLocation, pidlRelativeDest);
    ILFree(pidlRelativeDest);
    pidlSrc = ILCombine(This->m_pidlLocation, pidl);
1250 1251 1252 1253 1254 1255 1256 1257
    
    /* Inform the shell */
    if (_ILIsFolder(ILFindLastID(pidlDest))) 
        SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_IDLIST, pidlSrc, pidlDest);
    else 
        SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_IDLIST, pidlSrc, pidlDest);
    
    if (ppidlOut) 
Michael Jung's avatar
Michael Jung committed
1258 1259 1260 1261
        *ppidlOut = ILClone(ILFindLastID(pidlDest));
        
    ILFree(pidlSrc);
    ILFree(pidlDest);
1262 1263
    
    return S_OK;
1264 1265
}

1266 1267 1268
static HRESULT WINAPI UnixFolder_IShellFolder2_EnumSearches(IShellFolder2* iface, 
    IEnumExtraSearch **ppEnum) 
{
1269
    FIXME("stub\n");
1270 1271 1272 1273 1274 1275
    return E_NOTIMPL;
}

static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumn(IShellFolder2* iface, 
    DWORD dwReserved, ULONG *pSort, ULONG *pDisplay) 
{
1276 1277 1278 1279 1280 1281 1282 1283
    TRACE("(iface=%p,dwReserved=%x,pSort=%p,pDisplay=%p)\n", iface, dwReserved, pSort, pDisplay);

    if (pSort)
        *pSort = 0;
    if (pDisplay)
        *pDisplay = 0;

    return S_OK;
1284 1285 1286 1287 1288
}

static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumnState(IShellFolder2* iface, 
    UINT iColumn, SHCOLSTATEF *pcsFlags)
{
1289
    FIXME("stub\n");
1290 1291 1292 1293 1294 1295
    return E_NOTIMPL;
}

static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultSearchGUID(IShellFolder2* iface, 
    GUID *pguid)
{
1296
    FIXME("stub\n");
1297 1298 1299 1300 1301 1302
    return E_NOTIMPL;
}

static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsEx(IShellFolder2* iface, 
    LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
{
1303
    FIXME("stub\n");
1304 1305 1306
    return E_NOTIMPL;
}

1307
#define SHELLVIEWCOLUMNS 7 
1308

1309 1310 1311
static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsOf(IShellFolder2* iface, 
    LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *psd)
{
1312
    UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
1313
    HRESULT hr = E_FAIL;
1314 1315 1316 1317
    struct passwd *pPasswd;
    struct group *pGroup;
    static const shvheader SFHeader[SHELLVIEWCOLUMNS] = {
        {IDS_SHV_COLUMN1,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
1318 1319 1320 1321
        {IDS_SHV_COLUMN2,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
        {IDS_SHV_COLUMN3,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
        {IDS_SHV_COLUMN4,  SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 12},
        {IDS_SHV_COLUMN5,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 9},
1322
        {IDS_SHV_COLUMN10, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7},
1323
        {IDS_SHV_COLUMN11, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7}
1324 1325
    };

1326 1327
    TRACE("(iface=%p, pidl=%p, iColumn=%d, psd=%p) stub\n", iface, pidl, iColumn, psd);
    
1328
    if (!psd || iColumn >= SHELLVIEWCOLUMNS)
1329 1330 1331
        return E_INVALIDARG;

    if (!pidl) {
1332 1333
        psd->fmt = SFHeader[iColumn].fmt;
        psd->cxChar = SFHeader[iColumn].cxChar;
1334
        psd->str.uType = STRRET_CSTR;
1335
        LoadStringA(shell32_hInstance, SFHeader[iColumn].colnameid, psd->str.u.cStr, MAX_PATH);
1336 1337
        return S_OK;
    } else {
1338
        struct stat statItem;
1339
        if (iColumn == 4 || iColumn == 5 || iColumn == 6) {
1340 1341 1342
            char szPath[FILENAME_MAX];
            strcpy(szPath, This->m_pszPath);
            if (!UNIXFS_filename_from_shitemid(pidl, szPath + strlen(szPath)))
1343 1344 1345 1346
                return E_INVALIDARG;
            if (stat(szPath, &statItem)) 
                return E_INVALIDARG;
        }
1347 1348
        psd->str.u.cStr[0] = '\0';
        psd->str.uType = STRRET_CSTR;
1349 1350 1351 1352
        switch (iColumn) {
            case 0:
                hr = IShellFolder2_GetDisplayNameOf(iface, pidl, SHGDN_NORMAL|SHGDN_INFOLDER, &psd->str);
                break;
1353
            case 1:
1354 1355 1356 1357 1358 1359 1360 1361 1362
                _ILGetFileSize(pidl, psd->str.u.cStr, MAX_PATH);
                break;
            case 2:
                _ILGetFileType (pidl, psd->str.u.cStr, MAX_PATH);
                break;
            case 3:
                _ILGetFileDate(pidl, psd->str.u.cStr, MAX_PATH);
                break;
            case 4:
1363 1364 1365 1366 1367 1368 1369 1370 1371 1372
                psd->str.u.cStr[0] = S_ISDIR(statItem.st_mode) ? 'd' : '-';
                psd->str.u.cStr[1] = (statItem.st_mode & S_IRUSR) ? 'r' : '-';
                psd->str.u.cStr[2] = (statItem.st_mode & S_IWUSR) ? 'w' : '-';
                psd->str.u.cStr[3] = (statItem.st_mode & S_IXUSR) ? 'x' : '-';
                psd->str.u.cStr[4] = (statItem.st_mode & S_IRGRP) ? 'r' : '-';
                psd->str.u.cStr[5] = (statItem.st_mode & S_IWGRP) ? 'w' : '-';
                psd->str.u.cStr[6] = (statItem.st_mode & S_IXGRP) ? 'x' : '-';
                psd->str.u.cStr[7] = (statItem.st_mode & S_IROTH) ? 'r' : '-';
                psd->str.u.cStr[8] = (statItem.st_mode & S_IWOTH) ? 'w' : '-';
                psd->str.u.cStr[9] = (statItem.st_mode & S_IXOTH) ? 'x' : '-';
1373 1374
                psd->str.u.cStr[10] = '\0';
                break;
1375
            case 5:
1376
                pPasswd = getpwuid(statItem.st_uid);
1377 1378
                if (pPasswd) strcpy(psd->str.u.cStr, pPasswd->pw_name);
                break;
1379
            case 6:
1380
                pGroup = getgrgid(statItem.st_gid);
1381 1382
                if (pGroup) strcpy(psd->str.u.cStr, pGroup->gr_name);
                break;
1383 1384 1385 1386 1387 1388 1389 1390 1391
        }
    }
    
    return hr;
}

static HRESULT WINAPI UnixFolder_IShellFolder2_MapColumnToSCID(IShellFolder2* iface, UINT iColumn,
    SHCOLUMNID *pscid) 
{
1392
    FIXME("stub\n");
1393 1394 1395 1396
    return E_NOTIMPL;
}

/* VTable for UnixFolder's IShellFolder2 interface.
1397
 */
1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418
static const IShellFolder2Vtbl UnixFolder_IShellFolder2_Vtbl = {
    UnixFolder_IShellFolder2_QueryInterface,
    UnixFolder_IShellFolder2_AddRef,
    UnixFolder_IShellFolder2_Release,
    UnixFolder_IShellFolder2_ParseDisplayName,
    UnixFolder_IShellFolder2_EnumObjects,
    UnixFolder_IShellFolder2_BindToObject,
    UnixFolder_IShellFolder2_BindToStorage,
    UnixFolder_IShellFolder2_CompareIDs,
    UnixFolder_IShellFolder2_CreateViewObject,
    UnixFolder_IShellFolder2_GetAttributesOf,
    UnixFolder_IShellFolder2_GetUIObjectOf,
    UnixFolder_IShellFolder2_GetDisplayNameOf,
    UnixFolder_IShellFolder2_SetNameOf,
    UnixFolder_IShellFolder2_GetDefaultSearchGUID,
    UnixFolder_IShellFolder2_EnumSearches,
    UnixFolder_IShellFolder2_GetDefaultColumn,
    UnixFolder_IShellFolder2_GetDefaultColumnState,
    UnixFolder_IShellFolder2_GetDetailsEx,
    UnixFolder_IShellFolder2_GetDetailsOf,
    UnixFolder_IShellFolder2_MapColumnToSCID
1419 1420
};

1421
static HRESULT WINAPI UnixFolder_IPersistFolder3_QueryInterface(IPersistFolder3* iface, REFIID riid, 
1422 1423
    void** ppvObject)
{
1424
    return UnixFolder_IShellFolder2_QueryInterface(
1425
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistFolder3, iface)), riid, ppvObject);
1426 1427
}

1428
static ULONG WINAPI UnixFolder_IPersistFolder3_AddRef(IPersistFolder3* iface)
1429
{
1430
    return UnixFolder_IShellFolder2_AddRef(
1431
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistFolder3, iface)));
1432 1433
}

1434
static ULONG WINAPI UnixFolder_IPersistFolder3_Release(IPersistFolder3* iface)
1435
{
1436
    return UnixFolder_IShellFolder2_Release(
1437
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistFolder3, iface)));
1438 1439
}

1440 1441 1442 1443 1444 1445 1446 1447 1448
static HRESULT WINAPI UnixFolder_IPersistFolder3_GetClassID(IPersistFolder3* iface, CLSID* pClassID)
{    
    UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder3, iface);
    
    TRACE("(iface=%p, pClassId=%p)\n", iface, pClassID);
    
    if (!pClassID)
        return E_INVALIDARG;

1449
    *pClassID = *This->m_pCLSID;
1450
    return S_OK;
1451 1452
}

1453
static HRESULT WINAPI UnixFolder_IPersistFolder3_Initialize(IPersistFolder3* iface, LPCITEMIDLIST pidl)
1454
{
1455
    UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder3, iface);
1456 1457
    LPCITEMIDLIST current = pidl;
    char szBasePath[FILENAME_MAX] = "/";
1458 1459 1460
    
    TRACE("(iface=%p, pidl=%p)\n", iface, pidl);

1461 1462
    /* Find the UnixFolderClass root */
    while (current->mkid.cb) {
1463 1464 1465
        if ((_ILIsDrive(current) && IsEqualCLSID(This->m_pCLSID, &CLSID_ShellFSFolder)) ||
            (_ILIsSpecialFolder(current) && IsEqualCLSID(This->m_pCLSID, _ILGetGUIDPointer(current))))
        {
1466
            break;
1467
        }
1468 1469
        current = ILGetNext(current);
    }
1470

1471
    if (current && current->mkid.cb) {
1472 1473 1474 1475 1476 1477
        if (_ILIsDrive(current)) {
            WCHAR wszDrive[4] = { '?', ':', '\\', 0 };
            wszDrive[0] = (WCHAR)*_ILGetTextPointer(current);
            if (!UNIXFS_get_unix_path(wszDrive, szBasePath))
                return E_FAIL;
        } else if (IsEqualIID(&CLSID_MyDocuments, _ILGetGUIDPointer(current))) {
1478 1479 1480 1481 1482 1483
            WCHAR wszMyDocumentsPath[MAX_PATH];
            if (!SHGetSpecialFolderPathW(0, wszMyDocumentsPath, CSIDL_PERSONAL, FALSE))
                return E_FAIL;
            PathAddBackslashW(wszMyDocumentsPath);
            if (!UNIXFS_get_unix_path(wszMyDocumentsPath, szBasePath))
                return E_FAIL;
1484 1485
        } 
        current = ILGetNext(current);
1486 1487 1488
    } else if (_ILIsDesktop(pidl) || _ILIsValue(pidl) || _ILIsFolder(pidl)) {
        /* Path rooted at Desktop */
        WCHAR wszDesktopPath[MAX_PATH];
1489 1490
        if (!SHGetSpecialFolderPathW(0, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE)) 
            return E_FAIL;
1491 1492 1493
        PathAddBackslashW(wszDesktopPath);
        if (!UNIXFS_get_unix_path(wszDesktopPath, szBasePath))
            return E_FAIL;
1494 1495 1496 1497 1498 1499 1500
        current = pidl;
    } else if (IsEqualCLSID(This->m_pCLSID, &CLSID_FolderShortcut)) {
        /* FolderShortcuts' Initialize method only sets the ITEMIDLIST, which
         * specifies the location in the shell namespace, but leaves the
         * target folder (m_pszPath) alone. See unit tests in tests/shlfolder.c */
        This->m_pidlLocation = ILClone(pidl);
        return S_OK;
1501 1502 1503 1504
    } else {
        ERR("Unknown pidl type!\n");
        pdump(pidl);
        return E_INVALIDARG;
1505
    }
1506
  
1507
    This->m_pidlLocation = ILClone(pidl);
1508
    return UNIXFS_initialize_target_folder(This, szBasePath, current, 0); 
1509 1510
}

1511
static HRESULT WINAPI UnixFolder_IPersistFolder3_GetCurFolder(IPersistFolder3* iface, LPITEMIDLIST* ppidl)
1512
{
1513
    UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder3, iface);
1514 1515
    
    TRACE ("(iface=%p, ppidl=%p)\n", iface, ppidl);
1516 1517 1518 1519 1520 1521

    if (!ppidl)
        return E_POINTER;
    *ppidl = ILClone (This->m_pidlLocation);
    return S_OK;
}
1522 1523 1524 1525 1526 1527 1528

static HRESULT WINAPI UnixFolder_IPersistFolder3_InitializeEx(IPersistFolder3 *iface, IBindCtx *pbc, 
    LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *ppfti)
{
    UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder3, iface);
    WCHAR wszTargetDosPath[MAX_PATH];
    char szTargetPath[FILENAME_MAX] = "";
1529
    
1530 1531 1532 1533 1534 1535 1536
    TRACE("(iface=%p, pbc=%p, pidlRoot=%p, ppfti=%p)\n", iface, pbc, pidlRoot, ppfti);

    /* If no PERSIST_FOLDER_TARGET_INFO is given InitializeEx is equivalent to Initialize. */
    if (!ppfti) 
        return IPersistFolder3_Initialize(iface, pidlRoot);

    if (ppfti->csidl != -1) {
1537 1538 1539 1540
        if (FAILED(SHGetFolderPathW(0, ppfti->csidl, NULL, 0, wszTargetDosPath)) ||
            !UNIXFS_get_unix_path(wszTargetDosPath, szTargetPath))
        {
            return E_FAIL;
1541 1542
        }
    } else if (*ppfti->szTargetParsingName) {
1543 1544 1545
        lstrcpyW(wszTargetDosPath, ppfti->szTargetParsingName);
        PathAddBackslashW(wszTargetDosPath);
        if (!UNIXFS_get_unix_path(wszTargetDosPath, szTargetPath)) {
1546 1547
            return E_FAIL;
        }
1548
    } else if (ppfti->pidlTargetFolder) {
1549 1550 1551 1552
        if (!SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTargetDosPath) ||
            !UNIXFS_get_unix_path(wszTargetDosPath, szTargetPath))
        {
            return E_FAIL;
1553
        }
1554 1555
    } else {
        return E_FAIL;
1556 1557 1558 1559 1560 1561 1562
    }

    This->m_pszPath = SHAlloc(lstrlenA(szTargetPath)+1);
    if (!This->m_pszPath) 
        return E_FAIL;
    lstrcpyA(This->m_pszPath, szTargetPath);
    This->m_pidlLocation = ILClone(pidlRoot);
1563 1564
    This->m_dwAttributes = (ppfti->dwAttributes != -1) ? ppfti->dwAttributes :
        (SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|SFGAO_CANRENAME|SFGAO_FILESYSTEM);
1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575

    return S_OK;
}

static HRESULT WINAPI UnixFolder_IPersistFolder3_GetFolderTargetInfo(IPersistFolder3 *iface, 
    PERSIST_FOLDER_TARGET_INFO *ppfti)
{
    FIXME("(iface=%p, ppfti=%p) stub\n", iface, ppfti);
    return E_NOTIMPL;
}

1576 1577
/* VTable for UnixFolder's IPersistFolder interface.
 */
1578 1579 1580 1581 1582 1583 1584 1585 1586
static const IPersistFolder3Vtbl UnixFolder_IPersistFolder3_Vtbl = {
    UnixFolder_IPersistFolder3_QueryInterface,
    UnixFolder_IPersistFolder3_AddRef,
    UnixFolder_IPersistFolder3_Release,
    UnixFolder_IPersistFolder3_GetClassID,
    UnixFolder_IPersistFolder3_Initialize,
    UnixFolder_IPersistFolder3_GetCurFolder,
    UnixFolder_IPersistFolder3_InitializeEx,
    UnixFolder_IPersistFolder3_GetFolderTargetInfo
1587 1588
};

1589 1590
static HRESULT WINAPI UnixFolder_IPersistPropertyBag_QueryInterface(IPersistPropertyBag* iface,
    REFIID riid, void** ppv)
1591 1592
{
    return UnixFolder_IShellFolder2_QueryInterface(
1593
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface)), riid, ppv);
1594 1595
}

1596
static ULONG WINAPI UnixFolder_IPersistPropertyBag_AddRef(IPersistPropertyBag* iface)
1597 1598
{
    return UnixFolder_IShellFolder2_AddRef(
1599
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface)));
1600 1601
}

1602
static ULONG WINAPI UnixFolder_IPersistPropertyBag_Release(IPersistPropertyBag* iface)
1603 1604
{
    return UnixFolder_IShellFolder2_Release(
1605
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface)));
1606 1607 1608 1609 1610 1611
}

static HRESULT WINAPI UnixFolder_IPersistPropertyBag_GetClassID(IPersistPropertyBag* iface, 
    CLSID* pClassID)
{
    return UnixFolder_IPersistFolder3_GetClassID(
1612
        STATIC_CAST(IPersistFolder3, ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface)), pClassID);
1613 1614 1615 1616
}

static HRESULT WINAPI UnixFolder_IPersistPropertyBag_InitNew(IPersistPropertyBag* iface)
{
1617
    FIXME("() stub\n");
1618 1619 1620 1621 1622 1623
    return E_NOTIMPL;
}

static HRESULT WINAPI UnixFolder_IPersistPropertyBag_Load(IPersistPropertyBag *iface, 
    IPropertyBag *pPropertyBag, IErrorLog *pErrorLog)
{
1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649
     UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface);
     static const WCHAR wszTarget[] = { 'T','a','r','g','e','t', 0 }, wszNull[] = { 0 };
     PERSIST_FOLDER_TARGET_INFO pftiTarget;
     VARIANT var;
     HRESULT hr;
     
     TRACE("(iface=%p, pPropertyBag=%p, pErrorLog=%p)\n", iface, pPropertyBag, pErrorLog);
 
     if (!pPropertyBag)
         return E_POINTER;
 
     /* Get 'Target' property from the property bag. */
     V_VT(&var) = VT_BSTR;
     hr = IPropertyBag_Read(pPropertyBag, wszTarget, &var, NULL);
     if (FAILED(hr)) 
         return E_FAIL;
     lstrcpyW(pftiTarget.szTargetParsingName, V_BSTR(&var));
     SysFreeString(V_BSTR(&var));
 
     pftiTarget.pidlTargetFolder = NULL;
     lstrcpyW(pftiTarget.szNetworkProvider, wszNull);
     pftiTarget.dwAttributes = -1;
     pftiTarget.csidl = -1;
 
     return UnixFolder_IPersistFolder3_InitializeEx(
                 STATIC_CAST(IPersistFolder3, This), NULL, NULL, &pftiTarget);
1650 1651 1652 1653 1654
}

static HRESULT WINAPI UnixFolder_IPersistPropertyBag_Save(IPersistPropertyBag *iface,
    IPropertyBag *pPropertyBag, BOOL fClearDirty, BOOL fSaveAllProperties)
{
1655
    FIXME("() stub\n");
1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670
    return E_NOTIMPL;
}

/* VTable for UnixFolder's IPersistPropertyBag interface.
 */
static const IPersistPropertyBagVtbl UnixFolder_IPersistPropertyBag_Vtbl = {
    UnixFolder_IPersistPropertyBag_QueryInterface,
    UnixFolder_IPersistPropertyBag_AddRef,
    UnixFolder_IPersistPropertyBag_Release,
    UnixFolder_IPersistPropertyBag_GetClassID,
    UnixFolder_IPersistPropertyBag_InitNew,
    UnixFolder_IPersistPropertyBag_Load,
    UnixFolder_IPersistPropertyBag_Save
};

1671 1672 1673 1674
static HRESULT WINAPI UnixFolder_ISFHelper_QueryInterface(ISFHelper* iface, REFIID riid, 
    void** ppvObject)
{
    return UnixFolder_IShellFolder2_QueryInterface(
1675
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, ISFHelper, iface)), riid, ppvObject);
1676 1677 1678 1679 1680
}

static ULONG WINAPI UnixFolder_ISFHelper_AddRef(ISFHelper* iface)
{
    return UnixFolder_IShellFolder2_AddRef(
1681
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, ISFHelper, iface)));
1682 1683 1684 1685 1686
}

static ULONG WINAPI UnixFolder_ISFHelper_Release(ISFHelper* iface)
{
    return UnixFolder_IShellFolder2_Release(
1687
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, ISFHelper, iface)));
1688 1689
}

1690
static HRESULT WINAPI UnixFolder_ISFHelper_GetUniqueName(ISFHelper* iface, LPWSTR pwszName, UINT uLen)
1691
{
1692 1693 1694 1695 1696 1697
    UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
    IEnumIDList *pEnum;
    HRESULT hr;
    LPITEMIDLIST pidlElem;
    DWORD dwFetched;
    int i;
1698
    WCHAR wszNewFolder[25];
1699
    static const WCHAR wszFormat[] = { '%','s',' ','%','d',0 };
1700

1701
    TRACE("(iface=%p, pwszName=%p, uLen=%u)\n", iface, pwszName, uLen);
1702 1703 1704

    LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, sizeof(wszNewFolder)/sizeof(WCHAR));

1705
    if (uLen < sizeof(wszNewFolder)/sizeof(WCHAR)+3)
1706 1707 1708 1709 1710
        return E_INVALIDARG;

    hr = IShellFolder2_EnumObjects(STATIC_CAST(IShellFolder2, This), 0,
                                   SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN, &pEnum);
    if (SUCCEEDED(hr)) {
1711
        lstrcpynW(pwszName, wszNewFolder, uLen);
1712 1713 1714
        IEnumIDList_Reset(pEnum);
        i = 2;
        while ((IEnumIDList_Next(pEnum, 1, &pidlElem, &dwFetched) == S_OK) && (dwFetched == 1)) {
1715 1716 1717
            WCHAR wszTemp[MAX_PATH];
            _ILSimpleGetTextW(pidlElem, wszTemp, MAX_PATH);
            if (!lstrcmpiW(wszTemp, pwszName)) {
1718
                IEnumIDList_Reset(pEnum);
1719
                snprintfW(pwszName, uLen, wszFormat, wszNewFolder, i++);
1720 1721 1722 1723 1724 1725 1726 1727 1728
                if (i > 99) {
                    hr = E_FAIL;
                    break;
                }
            }
        }
        IEnumIDList_Release(pEnum);
    }
    return hr;
1729 1730
}

1731
static HRESULT WINAPI UnixFolder_ISFHelper_AddFolder(ISFHelper* iface, HWND hwnd, LPCWSTR pwszName, 
1732 1733
    LPITEMIDLIST* ppidlOut)
{
1734 1735
    UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
    char szNewDir[FILENAME_MAX];
1736
    int cBaseLen;
1737

1738 1739
    TRACE("(iface=%p, hwnd=%p, pwszName=%s, ppidlOut=%p)\n", 
            iface, hwnd, debugstr_w(pwszName), ppidlOut);
1740 1741 1742 1743

    if (ppidlOut)
        *ppidlOut = NULL;

1744 1745 1746 1747 1748 1749 1750
    if (!This->m_pszPath || !(This->m_dwAttributes & SFGAO_FILESYSTEM))
        return E_FAIL;
    
    lstrcpynA(szNewDir, This->m_pszPath, FILENAME_MAX);
    cBaseLen = lstrlenA(szNewDir);
    WideCharToMultiByte(CP_UNIXCP, 0, pwszName, -1, szNewDir+cBaseLen, FILENAME_MAX-cBaseLen, 0, 0);
   
1751
    if (mkdir(szNewDir, 0777)) {
1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764
        char szMessage[256 + FILENAME_MAX];
        char szCaption[256];

        LoadStringA(shell32_hInstance, IDS_CREATEFOLDER_DENIED, szCaption, sizeof(szCaption));
        sprintf(szMessage, szCaption, szNewDir);
        LoadStringA(shell32_hInstance, IDS_CREATEFOLDER_CAPTION, szCaption, sizeof(szCaption));
        MessageBoxA(hwnd, szMessage, szCaption, MB_OK | MB_ICONEXCLAMATION);

        return E_FAIL;
    } else {
        LPITEMIDLIST pidlRelative;

        /* Inform the shell */
1765
        if (SUCCEEDED(UNIXFS_path_to_pidl(This, pwszName, &pidlRelative))) {
1766 1767 1768 1769 1770 1771 1772
            LPITEMIDLIST pidlAbsolute = ILCombine(This->m_pidlLocation, pidlRelative);
            if (ppidlOut)
                *ppidlOut = pidlRelative;
            else
                ILFree(pidlRelative);
            SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, pidlAbsolute, NULL);
            ILFree(pidlAbsolute);
1773
        } else return E_FAIL;
1774 1775
        return S_OK;
    }
1776 1777
}

1778 1779 1780 1781 1782 1783
/*
 * Delete specified files by converting the path to DOS paths and calling
 * SHFileOperationW. If an error occurs it returns an error code. If the paths can't
 * be converted, S_FALSE is returned. In such situation DeleteItems will try to delete
 * the files using syscalls
 */
1784
static HRESULT UNIXFS_delete_with_shfileop(UnixFolder *This, UINT cidl, const LPCITEMIDLIST *apidl)
1785 1786 1787 1788 1789
{
    char szAbsolute[FILENAME_MAX], *pszRelative;
    LPWSTR wszPathsList, wszListPos;
    SHFILEOPSTRUCTW op;
    HRESULT ret;
1790
    UINT i;
1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824
    
    lstrcpyA(szAbsolute, This->m_pszPath);
    pszRelative = szAbsolute + lstrlenA(szAbsolute);
    
    wszListPos = wszPathsList = HeapAlloc(GetProcessHeap(), 0, cidl*MAX_PATH*sizeof(WCHAR)+1);
    if (wszPathsList == NULL)
        return E_OUTOFMEMORY;
    for (i=0; i<cidl; i++) {
        LPWSTR wszDosPath;
        
        if (!_ILIsFolder(apidl[i]) && !_ILIsValue(apidl[i]))
            continue;
        if (!UNIXFS_filename_from_shitemid(apidl[i], pszRelative))
        {
            HeapFree(GetProcessHeap(), 0, wszPathsList);
            return E_INVALIDARG;
        }
        wszDosPath = wine_get_dos_file_name(szAbsolute);
        if (wszDosPath == NULL || lstrlenW(wszDosPath) >= MAX_PATH)
        {
            HeapFree(GetProcessHeap(), 0, wszPathsList);
            HeapFree(GetProcessHeap(), 0, wszDosPath);
            return S_FALSE;
        }
        lstrcpyW(wszListPos, wszDosPath);
        wszListPos += lstrlenW(wszListPos)+1;
        HeapFree(GetProcessHeap(), 0, wszDosPath);
    }
    *wszListPos = 0;
    
    ZeroMemory(&op, sizeof(op));
    op.hwnd = GetActiveWindow();
    op.wFunc = FO_DELETE;
    op.pFrom = wszPathsList;
1825
    op.fFlags = FOF_ALLOWUNDO;
1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837
    if (!SHFileOperationW(&op))
    {
        WARN("SHFileOperationW failed\n");
        ret = E_FAIL;
    }
    else
        ret = S_OK;

    HeapFree(GetProcessHeap(), 0, wszPathsList);
    return ret;
}

1838
static HRESULT UNIXFS_delete_with_syscalls(UnixFolder *This, UINT cidl, const LPCITEMIDLIST *apidl)
1839 1840
{
    char szAbsolute[FILENAME_MAX], *pszRelative;
1841
    static const WCHAR empty[] = {0};
1842
    UINT i;
1843
    
1844
    if (!SHELL_ConfirmYesNoW(GetActiveWindow(), ASK_DELETE_SELECTED, empty))
1845 1846
        return S_OK;
    
1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863
    lstrcpyA(szAbsolute, This->m_pszPath);
    pszRelative = szAbsolute + lstrlenA(szAbsolute);
    
    for (i=0; i<cidl; i++) {
        if (!UNIXFS_filename_from_shitemid(apidl[i], pszRelative))
            return E_INVALIDARG;
        if (_ILIsFolder(apidl[i])) {
            if (rmdir(szAbsolute))
                return E_FAIL;
        } else if (_ILIsValue(apidl[i])) {
            if (unlink(szAbsolute))
                return E_FAIL;
        }
    }
    return S_OK;
}

1864 1865 1866
static HRESULT WINAPI UnixFolder_ISFHelper_DeleteItems(ISFHelper* iface, UINT cidl, 
    LPCITEMIDLIST* apidl)
{
1867 1868 1869 1870 1871
    UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
    char szAbsolute[FILENAME_MAX], *pszRelative;
    LPITEMIDLIST pidlAbsolute;
    HRESULT hr = S_OK;
    UINT i;
1872
    struct stat st;
1873 1874 1875
    
    TRACE("(iface=%p, cidl=%d, apidl=%p)\n", iface, cidl, apidl);

1876 1877 1878 1879
    hr = UNIXFS_delete_with_shfileop(This, cidl, apidl);
    if (hr == S_FALSE)
        hr = UNIXFS_delete_with_syscalls(This, cidl, apidl);

1880 1881 1882
    lstrcpyA(szAbsolute, This->m_pszPath);
    pszRelative = szAbsolute + lstrlenA(szAbsolute);
    
1883 1884
    /* we need to manually send the notifies if the files doesn't exist */
    for (i=0; i<cidl; i++) {
1885
        if (!UNIXFS_filename_from_shitemid(apidl[i], pszRelative))
1886
            continue;
1887
        pidlAbsolute = ILCombine(This->m_pidlLocation, apidl[i]);
1888 1889 1890
        if (stat(szAbsolute, &st))
        {
            if (_ILIsFolder(apidl[i])) {
1891
                SHChangeNotify(SHCNE_RMDIR, SHCNF_IDLIST, pidlAbsolute, NULL);
1892
            } else if (_ILIsValue(apidl[i])) {
1893 1894 1895 1896 1897 1898 1899
                SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlAbsolute, NULL);
            }
        }
        ILFree(pidlAbsolute);
    }
        
    return hr;
1900 1901 1902 1903 1904
}

static HRESULT WINAPI UnixFolder_ISFHelper_CopyItems(ISFHelper* iface, IShellFolder *psfFrom, 
    UINT cidl, LPCITEMIDLIST *apidl)
{
1905 1906 1907 1908 1909 1910
    UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
    DWORD dwAttributes;
    UINT i;
    HRESULT hr;
    char szAbsoluteDst[FILENAME_MAX], *pszRelativeDst;
    
1911
    TRACE("(iface=%p, psfFrom=%p, cidl=%d, apidl=%p)\n", iface, psfFrom, cidl, apidl);
1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928

    if (!psfFrom || !cidl || !apidl)
        return E_INVALIDARG;

    /* All source items have to be filesystem items. */
    dwAttributes = SFGAO_FILESYSTEM;
    hr = IShellFolder_GetAttributesOf(psfFrom, cidl, apidl, &dwAttributes);
    if (FAILED(hr) || !(dwAttributes & SFGAO_FILESYSTEM)) 
        return E_INVALIDARG;

    lstrcpyA(szAbsoluteDst, This->m_pszPath);
    pszRelativeDst = szAbsoluteDst + strlen(szAbsoluteDst);
    
    for (i=0; i<cidl; i++) {
        WCHAR wszSrc[MAX_PATH];
        char szSrc[FILENAME_MAX];
        STRRET strret;
1929 1930
        HRESULT res;
        WCHAR *pwszDosSrc, *pwszDosDst;
1931 1932 1933 1934 1935 1936 1937 1938 1939 1940

        /* Build the unix path of the current source item. */
        if (FAILED(IShellFolder_GetDisplayNameOf(psfFrom, apidl[i], SHGDN_FORPARSING, &strret)))
            return E_FAIL;
        if (FAILED(StrRetToBufW(&strret, apidl[i], wszSrc, MAX_PATH)))
            return E_FAIL;
        if (!UNIXFS_get_unix_path(wszSrc, szSrc)) 
            return E_FAIL;

        /* Build the unix path of the current destination item */
1941
        UNIXFS_filename_from_shitemid(apidl[i], pszRelativeDst);
1942

1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955
        pwszDosSrc = wine_get_dos_file_name(szSrc);
        pwszDosDst = wine_get_dos_file_name(szAbsoluteDst);

        if (pwszDosSrc && pwszDosDst)
            res = UNIXFS_copy(pwszDosSrc, pwszDosDst);
        else
            res = E_OUTOFMEMORY;

        HeapFree(GetProcessHeap(), 0, pwszDosSrc);
        HeapFree(GetProcessHeap(), 0, pwszDosDst);

        if (res != S_OK)
            return res;
1956 1957
    }
    return S_OK;
1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971
}

/* VTable for UnixFolder's ISFHelper interface
 */
static const ISFHelperVtbl UnixFolder_ISFHelper_Vtbl = {
    UnixFolder_ISFHelper_QueryInterface,
    UnixFolder_ISFHelper_AddRef,
    UnixFolder_ISFHelper_Release,
    UnixFolder_ISFHelper_GetUniqueName,
    UnixFolder_ISFHelper_AddFolder,
    UnixFolder_ISFHelper_DeleteItems,
    UnixFolder_ISFHelper_CopyItems
};

1972 1973 1974 1975
static HRESULT WINAPI UnixFolder_IDropTarget_QueryInterface(IDropTarget* iface, REFIID riid, 
    void** ppvObject)
{
    return UnixFolder_IShellFolder2_QueryInterface(
1976
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IDropTarget, iface)), riid, ppvObject);
1977 1978 1979 1980 1981
}

static ULONG WINAPI UnixFolder_IDropTarget_AddRef(IDropTarget* iface)
{
    return UnixFolder_IShellFolder2_AddRef(
1982
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IDropTarget, iface)));
1983 1984 1985 1986 1987
}

static ULONG WINAPI UnixFolder_IDropTarget_Release(IDropTarget* iface)
{
    return UnixFolder_IShellFolder2_Release(
1988
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IDropTarget, iface)));
1989 1990
}

1991 1992 1993
#define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
#define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])

1994 1995 1996
static HRESULT WINAPI UnixFolder_IDropTarget_DragEnter(IDropTarget *iface, IDataObject *pDataObject,
    DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
{
1997 1998 1999 2000
    UnixFolder *This = ADJUST_THIS(UnixFolder, IDropTarget, iface);
    FORMATETC format;
    STGMEDIUM medium; 
        
2001
    TRACE("(iface=%p, pDataObject=%p, dwKeyState=%08x, pt={.x=%d, .y=%d}, pdwEffect=%p)\n",
2002 2003
        iface, pDataObject, dwKeyState, pt.x, pt.y, pdwEffect);

2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029
    if (!pdwEffect || !pDataObject)
        return E_INVALIDARG;
  
    /* Compute a mask of supported drop-effects for this shellfolder object and the given data 
     * object. Dropping is only supported on folders, which represent filesystem locations. One
     * can't drop on file objects. And the 'move' drop effect is only supported, if the source
     * folder is not identical to the target folder. */
    This->m_dwDropEffectsMask = DROPEFFECT_NONE;
    InitFormatEtc(format, cfShellIDList, TYMED_HGLOBAL);
    if ((This->m_dwAttributes & SFGAO_FILESYSTEM) && /* Only drop to filesystem folders */
        _ILIsFolder(ILFindLastID(This->m_pidlLocation)) && /* Only drop to folders, not to files */
        SUCCEEDED(IDataObject_GetData(pDataObject, &format, &medium))) /* Only ShellIDList format */
    {
        LPIDA pidaShellIDList = GlobalLock(medium.u.hGlobal);
        This->m_dwDropEffectsMask |= DROPEFFECT_COPY|DROPEFFECT_LINK;

        if (pidaShellIDList) { /* Files can only be moved between two different folders */
            if (!ILIsEqual(HIDA_GetPIDLFolder(pidaShellIDList), This->m_pidlLocation))
                This->m_dwDropEffectsMask |= DROPEFFECT_MOVE;
            GlobalUnlock(medium.u.hGlobal);
        }
    }

    *pdwEffect = KeyStateToDropEffect(dwKeyState) & This->m_dwDropEffectsMask;
    
    return S_OK;
2030 2031 2032 2033 2034
}

static HRESULT WINAPI UnixFolder_IDropTarget_DragOver(IDropTarget *iface, DWORD dwKeyState, 
    POINTL pt, DWORD *pdwEffect)
{
2035 2036
    UnixFolder *This = ADJUST_THIS(UnixFolder, IDropTarget, iface);
    
2037
    TRACE("(iface=%p, dwKeyState=%08x, pt={.x=%d, .y=%d}, pdwEffect=%p)\n", iface, dwKeyState, 
2038
        pt.x, pt.y, pdwEffect);
2039

2040 2041 2042 2043 2044 2045
    if (!pdwEffect)
        return E_INVALIDARG;

    *pdwEffect = KeyStateToDropEffect(dwKeyState) & This->m_dwDropEffectsMask;
    
    return S_OK;
2046 2047 2048
}

static HRESULT WINAPI UnixFolder_IDropTarget_DragLeave(IDropTarget *iface) {
2049 2050 2051 2052 2053 2054 2055
    UnixFolder *This = ADJUST_THIS(UnixFolder, IDropTarget, iface);
    
    TRACE("(iface=%p)\n", iface);
 
    This->m_dwDropEffectsMask = DROPEFFECT_NONE;
    
    return S_OK;
2056 2057 2058 2059 2060
}

static HRESULT WINAPI UnixFolder_IDropTarget_Drop(IDropTarget *iface, IDataObject *pDataObject,
    DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
{
2061 2062 2063 2064 2065
    UnixFolder *This = ADJUST_THIS(UnixFolder, IDropTarget, iface);
    FORMATETC format;
    STGMEDIUM medium; 
    HRESULT hr;

2066
    TRACE("(iface=%p, pDataObject=%p, dwKeyState=%d, pt={.x=%d, .y=%d}, pdwEffect=%p) semi-stub\n",
2067
        iface, pDataObject, dwKeyState, pt.x, pt.y, pdwEffect);
2068 2069 2070

    InitFormatEtc(format, cfShellIDList, TYMED_HGLOBAL);
    hr = IDataObject_GetData(pDataObject, &format, &medium);
2071
    if (FAILED(hr))
2072 2073 2074 2075 2076 2077 2078 2079 2080 2081
        return hr;

    if (medium.tymed == TYMED_HGLOBAL) {
        IShellFolder *psfSourceFolder, *psfDesktopFolder;
        LPIDA pidaShellIDList = GlobalLock(medium.u.hGlobal);
        STRRET strret;
        UINT i;
    
        if (!pidaShellIDList) 
            return HRESULT_FROM_WIN32(GetLastError());
2082
        
2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126
        hr = SHGetDesktopFolder(&psfDesktopFolder);
        if (FAILED(hr)) {
            GlobalUnlock(medium.u.hGlobal);
            return hr;
        }

        hr = IShellFolder_BindToObject(psfDesktopFolder, HIDA_GetPIDLFolder(pidaShellIDList), NULL, 
                                       &IID_IShellFolder, (LPVOID*)&psfSourceFolder);
        IShellFolder_Release(psfDesktopFolder);
        if (FAILED(hr)) {
            GlobalUnlock(medium.u.hGlobal);
            return hr;
        }

        for (i = 0; i < pidaShellIDList->cidl; i++) {
            WCHAR wszSourcePath[MAX_PATH];

            hr = IShellFolder_GetDisplayNameOf(psfSourceFolder, HIDA_GetPIDLItem(pidaShellIDList, i),
                                               SHGDN_FORPARSING, &strret);
            if (FAILED(hr)) 
                break;

            hr = StrRetToBufW(&strret, NULL, wszSourcePath, MAX_PATH);
            if (FAILED(hr)) 
                break;

            switch (*pdwEffect) {
                case DROPEFFECT_MOVE:
                    FIXME("Move %s to %s!\n", debugstr_w(wszSourcePath), This->m_pszPath);
                    break;
                case DROPEFFECT_COPY:
                    FIXME("Copy %s to %s!\n", debugstr_w(wszSourcePath), This->m_pszPath);
                    break;
                case DROPEFFECT_LINK:
                    FIXME("Link %s from %s!\n", debugstr_w(wszSourcePath), This->m_pszPath);
                    break;
            }
        }
    
        IShellFolder_Release(psfSourceFolder);
        GlobalUnlock(medium.u.hGlobal);
        return hr;
    }
 
2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141
    return E_NOTIMPL;
}

/* VTable for UnixFolder's IDropTarget interface
 */
static const IDropTargetVtbl UnixFolder_IDropTarget_Vtbl = {
    UnixFolder_IDropTarget_QueryInterface,
    UnixFolder_IDropTarget_AddRef,
    UnixFolder_IDropTarget_Release,
    UnixFolder_IDropTarget_DragEnter,
    UnixFolder_IDropTarget_DragOver,
    UnixFolder_IDropTarget_DragLeave,
    UnixFolder_IDropTarget_Drop
};

2142
/******************************************************************************
2143
 * Unix[Dos]Folder_Constructor [Internal]
2144 2145 2146 2147 2148 2149 2150
 *
 * PARAMS
 *  pUnkOuter [I] Outer class for aggregation. Currently ignored.
 *  riid      [I] Interface asked for by the client.
 *  ppv       [O] Pointer to an riid interface to the UnixFolder object.
 *
 * NOTES
2151 2152
 *  Those are the only functions exported from shfldr_unixfs.c. They are called from
 *  shellole.c's default class factory and thus have to exhibit a LPFNCREATEINSTANCE
2153
 *  compatible signature.
2154 2155 2156
 *
 *  The UnixDosFolder_Constructor sets the dwPathMode member to PATHMODE_DOS. This
 *  means that paths are converted from dos to unix and back at the interfaces.
2157
 */
2158
static HRESULT CreateUnixFolder(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv, const CLSID *pCLSID) 
2159
{
2160 2161
    HRESULT hr = E_FAIL;
    UnixFolder *pUnixFolder = SHAlloc((ULONG)sizeof(UnixFolder));
2162 2163 2164 2165 2166
   
    if (pUnkOuter) {
        FIXME("Aggregation not yet implemented!\n");
        return CLASS_E_NOAGGREGATION;
    }
2167
    
2168 2169
    if(pUnixFolder) {
        pUnixFolder->lpIShellFolder2Vtbl = &UnixFolder_IShellFolder2_Vtbl;
2170
        pUnixFolder->lpIPersistFolder3Vtbl = &UnixFolder_IPersistFolder3_Vtbl;
2171
        pUnixFolder->lpIPersistPropertyBagVtbl = &UnixFolder_IPersistPropertyBag_Vtbl;
2172
        pUnixFolder->lpISFHelperVtbl = &UnixFolder_ISFHelper_Vtbl;
2173
        pUnixFolder->lpIDropTargetVtbl = &UnixFolder_IDropTarget_Vtbl;
2174 2175
        pUnixFolder->m_cRef = 0;
        pUnixFolder->m_pszPath = NULL;
2176
        pUnixFolder->m_pidlLocation = NULL;
2177
        pUnixFolder->m_dwPathMode = IsEqualCLSID(&CLSID_UnixFolder, pCLSID) ? PATHMODE_UNIX : PATHMODE_DOS;
2178
        pUnixFolder->m_dwAttributes = 0;
2179
        pUnixFolder->m_pCLSID = pCLSID;
2180
        pUnixFolder->m_dwDropEffectsMask = DROPEFFECT_NONE;
2181 2182 2183 2184 2185 2186 2187

        UnixFolder_IShellFolder2_AddRef(STATIC_CAST(IShellFolder2, pUnixFolder));
        hr = UnixFolder_IShellFolder2_QueryInterface(STATIC_CAST(IShellFolder2, pUnixFolder), riid, ppv);
        UnixFolder_IShellFolder2_Release(STATIC_CAST(IShellFolder2, pUnixFolder));
    }
    return hr;
}
2188

2189 2190
HRESULT WINAPI UnixFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
    TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2191
    return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_UnixFolder);
2192
}
2193

2194 2195
HRESULT WINAPI UnixDosFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
    TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2196
    return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_UnixDosFolder);
2197 2198
}

2199 2200
HRESULT WINAPI FolderShortcut_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
    TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2201
    return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_FolderShortcut);
2202 2203
}

2204 2205
HRESULT WINAPI MyDocuments_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
    TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2206
    return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_MyDocuments);
2207 2208
}

2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219
/******************************************************************************
 * UnixSubFolderIterator
 *
 * Class whose heap based objects represent iterators over the sub-directories
 * of a given UnixFolder object. 
 */

/* UnixSubFolderIterator object layout and typedef.
 */
typedef struct _UnixSubFolderIterator {
    const IEnumIDListVtbl *lpIEnumIDListVtbl;
Mike McCormack's avatar
Mike McCormack committed
2220
    LONG m_cRef;
2221
    SHCONTF m_fFilter;
2222 2223
    DIR *m_dirFolder;
    char m_szFolder[FILENAME_MAX];
2224 2225 2226 2227
} UnixSubFolderIterator;

static void UnixSubFolderIterator_Destroy(UnixSubFolderIterator *iterator) {
    TRACE("(iterator=%p)\n", iterator);
2228 2229 2230

    if (iterator->m_dirFolder)
        closedir(iterator->m_dirFolder);
2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279
    SHFree(iterator);
}

static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_QueryInterface(IEnumIDList* iface, 
    REFIID riid, void** ppv)
{
    TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
    
    if (!ppv) return E_INVALIDARG;
    
    if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IEnumIDList, riid)) {
        *ppv = iface;
    } else {
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    IEnumIDList_AddRef(iface);
    return S_OK;
}
                            
static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_AddRef(IEnumIDList* iface)
{
    UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);

    TRACE("(iface=%p)\n", iface);
   
    return InterlockedIncrement(&This->m_cRef);
}

static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_Release(IEnumIDList* iface)
{
    UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
    ULONG cRef;
    
    TRACE("(iface=%p)\n", iface);

    cRef = InterlockedDecrement(&This->m_cRef);
    
    if (!cRef) 
        UnixSubFolderIterator_Destroy(This);

    return cRef;
}

static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Next(IEnumIDList* iface, ULONG celt, 
    LPITEMIDLIST* rgelt, ULONG* pceltFetched)
{
    UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295
    ULONG i = 0;

    /* This->m_dirFolder will be NULL if the user doesn't have access rights for the dir. */
    if (This->m_dirFolder) {
        char *pszRelativePath = This->m_szFolder + lstrlenA(This->m_szFolder);
        struct dirent *pDirEntry;

        while (i < celt) {
            pDirEntry = readdir(This->m_dirFolder);
            if (!pDirEntry) break; /* No more entries */
            if (!strcmp(pDirEntry->d_name, ".") || !strcmp(pDirEntry->d_name, "..")) continue;

            /* Temporarily build absolute path in This->m_szFolder. Then construct a pidl
             * and see if it passes the filter. 
             */
            lstrcpyA(pszRelativePath, pDirEntry->d_name);
2296
            rgelt[i] = SHAlloc(
2297
                UNIXFS_shitemid_len_from_filename(pszRelativePath, NULL, NULL)+sizeof(USHORT));
2298 2299 2300 2301 2302 2303 2304
            if (!UNIXFS_build_shitemid(This->m_szFolder, rgelt[i]) ||
                !UNIXFS_is_pidl_of_type(rgelt[i], This->m_fFilter)) 
            {
                SHFree(rgelt[i]);
                continue;
            }
            memset(((PBYTE)rgelt[i])+rgelt[i]->mkid.cb, 0, sizeof(USHORT));
2305 2306
            i++;
        }
2307
        *pszRelativePath = '\0'; /* Restore the original path in This->m_szFolder. */
2308
    }
2309
    
2310 2311 2312
    if (pceltFetched)
        *pceltFetched = i;

2313
    return (i == 0) ? S_FALSE : S_OK;
2314
}
2315
    
2316 2317
static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Skip(IEnumIDList* iface, ULONG celt)
{
2318 2319 2320
    LPITEMIDLIST *apidl;
    ULONG cFetched;
    HRESULT hr;
2321
    
2322
    TRACE("(iface=%p, celt=%d)\n", iface, celt);
2323

2324
    /* Call IEnumIDList::Next and delete the resulting pidls. */
2325
    apidl = SHAlloc(celt * sizeof(LPITEMIDLIST));
2326 2327 2328 2329 2330 2331 2332
    hr = IEnumIDList_Next(iface, celt, apidl, &cFetched);
    if (SUCCEEDED(hr))
        while (cFetched--) 
            SHFree(apidl[cFetched]);
    SHFree(apidl);

    return hr;
2333 2334 2335 2336 2337 2338 2339 2340
}

static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Reset(IEnumIDList* iface)
{
    UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
        
    TRACE("(iface=%p)\n", iface);

2341 2342
    if (This->m_dirFolder)
        rewinddir(This->m_dirFolder);
2343 2344 2345 2346 2347 2348 2349
    
    return S_OK;
}

static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Clone(IEnumIDList* This, 
    IEnumIDList** ppenum)
{
2350
    FIXME("stub\n");
2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365
    return E_NOTIMPL;
}

/* VTable for UnixSubFolderIterator's IEnumIDList interface.
 */
static const IEnumIDListVtbl UnixSubFolderIterator_IEnumIDList_Vtbl = {
    UnixSubFolderIterator_IEnumIDList_QueryInterface,
    UnixSubFolderIterator_IEnumIDList_AddRef,
    UnixSubFolderIterator_IEnumIDList_Release,
    UnixSubFolderIterator_IEnumIDList_Next,
    UnixSubFolderIterator_IEnumIDList_Skip,
    UnixSubFolderIterator_IEnumIDList_Reset,
    UnixSubFolderIterator_IEnumIDList_Clone
};

2366
static IUnknown *UnixSubFolderIterator_Constructor(UnixFolder *pUnixFolder, SHCONTF fFilter) {
2367 2368 2369 2370 2371 2372 2373
    UnixSubFolderIterator *iterator;

    TRACE("(pUnixFolder=%p)\n", pUnixFolder);
    
    iterator = SHAlloc((ULONG)sizeof(UnixSubFolderIterator));
    iterator->lpIEnumIDListVtbl = &UnixSubFolderIterator_IEnumIDList_Vtbl;
    iterator->m_cRef = 0;
2374
    iterator->m_fFilter = fFilter;
2375 2376
    iterator->m_dirFolder = opendir(pUnixFolder->m_pszPath);
    lstrcpyA(iterator->m_szFolder, pUnixFolder->m_pszPath);
2377 2378 2379 2380 2381

    UnixSubFolderIterator_IEnumIDList_AddRef((IEnumIDList*)iterator);
    
    return (IUnknown*)iterator;
}
2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431

#else /* __MINGW32__ || _MSC_VER */

HRESULT WINAPI UnixFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv)
{
    return E_NOTIMPL;
}

HRESULT WINAPI UnixDosFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv)
{
    return E_NOTIMPL;
}

HRESULT WINAPI FolderShortcut_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv)
{
    return E_NOTIMPL;
}

HRESULT WINAPI MyDocuments_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv)
{
    return E_NOTIMPL;
}

#endif /* __MINGW32__ || _MSC_VER */

/******************************************************************************
 * UNIXFS_is_rooted_at_desktop [Internal]
 *
 * Checks if the unixfs namespace extension is rooted at desktop level.
 *
 * RETURNS
 *  TRUE, if unixfs is rooted at desktop level
 *  FALSE, if not.
 */
BOOL UNIXFS_is_rooted_at_desktop(void) {
    HKEY hKey;
    WCHAR wszRootedAtDesktop[69 + CHARS_IN_GUID] = {
        'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
        'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
        'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
        'N','a','m','e','S','p','a','c','e','\\',0 };

    if (StringFromGUID2(&CLSID_UnixDosFolder, wszRootedAtDesktop + 69, CHARS_IN_GUID) &&
        RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return TRUE;
    }
    return FALSE;
}