shfldr_unixfs.c 84.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * 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
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
/*
 * 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
 * lot about this, since path-names can't be told appart from ordinary strings
 * 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,
 * don't have the SFGAO_FILESYSTEM flag set. So the file dialogs should'nt allow
 * 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. 
 *
 * To sum it all up, you can still savely run wine with you root account (Just
 * 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
#include <dirent.h>
132
#include <stdlib.h>
133 134 135
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
136 137 138 139 140 141
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
142
#include <grp.h>
143
#include <limits.h>
144 145 146 147 148 149 150 151 152

#define COBJMACROS
#define NONAMELESSUNION
#define NONAMELESSSTRUCT

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "objbase.h"
153
#include "winreg.h"
154
#include "shlwapi.h"
155
#include "winternl.h"
156 157 158
#include "wine/debug.h"

#include "shell32_main.h"
159
#include "shellfolder.h"
160 161
#include "shfldr.h"
#include "shresdef.h"
162
#include "pidl.h"
163 164 165 166

WINE_DEFAULT_DEBUG_CHANNEL(shell);

const GUID CLSID_UnixFolder = {0xcc702eb2, 0x7dc5, 0x11d9, {0xc6, 0x87, 0x00, 0x04, 0x23, 0x8a, 0x01, 0xcd}};
167
const GUID CLSID_UnixDosFolder = {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
168 169 170 171

#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)

172 173 174 175 176 177 178 179
#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. */
180

181 182 183 184 185 186
#define PATHMODE_UNIX 0
#define PATHMODE_DOS  1

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

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

204 205 206 207 208 209 210 211 212 213 214
/******************************************************************************
 * 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;
215
    WCHAR wszRootedAtDesktop[69 + CHARS_IN_GUID] = { 
216 217 218 219 220
        '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 }; 

221 222 223 224 225 226 227
    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;
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 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
 * 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]
351 352 353 354 355 356 357 358 359 360 361 362
 *
 * 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
 */
static inline BOOL UNIXFS_is_pidl_of_type(LPITEMIDLIST pIDL, SHCONTF fFilter) {
363 364 365 366 367 368
    LPPIDLDATA pIDLData = _ILGetDataPointer(pIDL);
    if (!(fFilter & SHCONTF_INCLUDEHIDDEN) && pIDLData && 
        (pIDLData->u.file.uFileAttribs & FILE_ATTRIBUTE_HIDDEN)) 
    {
        return FALSE;
    }
369 370 371 372
    if (_ILIsFolder(pIDL) && (fFilter & SHCONTF_FOLDERS)) return TRUE;
    if (_ILIsValue(pIDL) && (fFilter & SHCONTF_NONFOLDERS)) return TRUE;
    return FALSE;
}
373

374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
/******************************************************************************
 * UNIXFS_is_dos_device [Internal]
 *
 * Determines if a unix directory corresponds to any dos device.
 *
 * PARAMS
 *  statPath [I] The stat struct of the directory, as returned by stat(2).
 *
 * RETURNS
 *  TRUE, if statPath corresponds to any dos drive letter
 *  FALSE, otherwise
 */
static BOOL UNIXFS_is_dos_device(const struct stat *statPath) {
    struct stat statDrive;
    char *pszDrivePath;
    DWORD dwDriveMap;
    WCHAR wszDosDevice[4] = { 'A', ':', '\\', 0 };

    for (dwDriveMap = GetLogicalDrives(); dwDriveMap; dwDriveMap >>= 1, wszDosDevice[0]++) {
        if (!(dwDriveMap & 0x1)) continue;
        pszDrivePath = wine_get_unix_file_name(wszDosDevice);
        if (pszDrivePath && !stat(pszDrivePath, &statDrive)) {
            HeapFree(GetProcessHeap(), 0, pszDrivePath);
            if ((statPath->st_dev == statDrive.st_dev) && (statPath->st_ino == statDrive.st_ino))
                return TRUE;
        }
    }
    return FALSE;
}

404
/******************************************************************************
405
 * UNIXFS_get_unix_path [Internal]
406
 *
407 408
 * Convert an absolute dos path to an absolute unix path.
 * Evaluate "/.", "/.." and the symbolic links in $WINEPREFIX/dosdevices.
409 410
 *
 * PARAMS
411
 *  pszDosPath       [I] An absolute dos path
412 413 414 415 416 417
 *  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
 */
418
static BOOL UNIXFS_get_unix_path(LPCWSTR pszDosPath, char *pszCanonicalPath)
419
{
420
    char *pPathTail, *pElement, *pCanonicalTail, szPath[FILENAME_MAX], *pszUnixPath;
421 422
    WCHAR wszDrive[] = { '?', ':', '\\', 0 };
    int cDriveSymlinkLen;
423
    
424
    TRACE("(pszDosPath=%s, pszCanonicalPath=%p)\n", debugstr_w(pszDosPath), pszCanonicalPath);
425

426
    if (!pszDosPath || pszDosPath[1] != ':')
427 428
        return FALSE;

429 430 431 432 433
    /* 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);
434
    pElement = realpath(pszUnixPath, szPath);
435 436
    HeapFree(GetProcessHeap(), 0, pszUnixPath);
    if (!pElement) return FALSE;
437
    if (szPath[strlen(szPath)-1] != '/') strcat(szPath, "/");
438 439

    /* Append the part relative to the drive symbolic link target. */
440
    pszUnixPath = wine_get_unix_file_name(pszDosPath);
441 442
    if (!pszUnixPath) return FALSE;
    strcat(szPath, pszUnixPath + cDriveSymlinkLen);
443
    HeapFree(GetProcessHeap(), 0, pszUnixPath);
444
    
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
    /* 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;
467 468
        } else if (!strcmp("/..", pElement)) {
            /* Remove last element in canonical path for "/.." elements, then skip. */
469 470 471 472 473 474
            char *pTemp = strrchr(pszCanonicalPath, '/');
            if (pTemp)
                pCanonicalTail = pTemp;
            *pCanonicalTail = '\0';
            *pPathTail = cTemp;
        } else {
475
            /* Directory or file. Copy to canonical path */
476 477 478 479 480 481 482
            if (pCanonicalTail - pszCanonicalPath + pPathTail - pElement + 1 > FILENAME_MAX)
                return FALSE;
                
            memcpy(pCanonicalTail, pElement, pPathTail - pElement + 1);
            pCanonicalTail += pPathTail - pElement;
            *pPathTail = cTemp;
        }
483
    } while (pPathTail[0] == '/');
484 485 486 487 488 489
   
    TRACE("--> %s\n", debugstr_a(pszCanonicalPath));
    
    return TRUE;
}

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
/******************************************************************************
 * 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);
}

512 513 514 515
/******************************************************************************
 * UNIXFS_build_shitemid [Internal]
 *
 * Constructs a new SHITEMID for the last component of path 'pszUnixPath' into 
516
 * buffer 'pIDL'.
517 518 519 520 521 522 523 524 525 526 527 528 529 530
 *
 * PARAMS
 *  pszUnixPath [I] An absolute path. The SHITEMID will be build for the last component.
 *  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.
 */
531
static char* UNIXFS_build_shitemid(char *pszUnixPath, void *pIDL) {
532 533
    LPPIDLDATA pIDLData;
    struct stat fileStat;
534 535 536 537 538 539
    char *pszComponentU, *pszComponentA;
    WCHAR *pwszComponentW;
    int cComponentULen, cComponentALen;
    USHORT cbLen;
    FileStructW *pFileStructW;
    WORD uOffsetW, *pOffsetW;
540 541 542 543 544 545 546

    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;
    
547 548 549 550 551 552 553 554
    /* 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;
    
555 556 557 558
    /* Set shell32's standard SHITEMID data fields. */
    pIDLData = _ILGetDataPointer((LPCITEMIDLIST)pIDL);
    pIDLData->type = S_ISDIR(fileStat.st_mode) ? PT_FOLDER : PT_VALUE;
    pIDLData->u.file.dwFileSize = (DWORD)fileStat.st_size;
559 560
    UNIXFS_seconds_since_1970_to_dos_date_time(fileStat.st_mtime, &pIDLData->u.file.uFileDate, 
        &pIDLData->u.file.uFileTime);
561 562
    pIDLData->u.file.uFileAttribs = 0;
    if (S_ISDIR(fileStat.st_mode)) pIDLData->u.file.uFileAttribs |= FILE_ATTRIBUTE_DIRECTORY;
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
    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;
583 584
}

585 586 587 588
/******************************************************************************
 * UNIXFS_path_to_pidl [Internal]
 *
 * PARAMS
589 590 591
 *  pUnixFolder [I] If path is relative, pUnixFolder represents the base path
 *  path        [I] An absolute unix or dos path or a path relativ to pUnixFolder
 *  ppidl       [O] The corresponding ITEMIDLIST. Release with SHFree/ILFree
592 593 594
 *  
 * RETURNS
 *  Success: TRUE
595
 *  Failure: FALSE, invalid params or out of memory
596 597 598
 *
 * NOTES
 *  pUnixFolder also carries the information if the path is expected to be unix or dos.
599
 */
600
static BOOL UNIXFS_path_to_pidl(UnixFolder *pUnixFolder, const WCHAR *path, LPITEMIDLIST *ppidl) {
601
    LPITEMIDLIST pidl;
602 603 604
    int cPidlLen, cPathLen;
    char *pSlash, *pNextSlash, szCompletePath[FILENAME_MAX], *pNextPathElement, *pszAPath;
    WCHAR *pwszPath;
605 606 607 608 609

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

611 612 613 614 615 616 617
    /* 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 */
618
        if (!UNIXFS_get_unix_path(path, szCompletePath))
619
            return FALSE;
620
        pNextPathElement = szCompletePath;
621 622 623 624
    } 
    else if ((pUnixFolder->m_dwPathMode == PATHMODE_UNIX) && (path[0] == '/')) 
    {
        /* Absolute unix path. Just convert to ANSI. */
625
        WideCharToMultiByte(CP_UNIXCP, 0, path, -1, szCompletePath, FILENAME_MAX, NULL, NULL); 
626
        pNextPathElement = szCompletePath;
627 628 629 630 631 632
    } 
    else 
    {
        /* Relative dos or unix path. Concat with this folder's path */
        int cBasePathLen = strlen(pUnixFolder->m_pszPath);
        memcpy(szCompletePath, pUnixFolder->m_pszPath, cBasePathLen);
633
        WideCharToMultiByte(CP_UNIXCP, 0, path, -1, szCompletePath + cBasePathLen, 
634
                            FILENAME_MAX - cBasePathLen, NULL, NULL);
635
        pNextPathElement = szCompletePath + cBasePathLen - 1;
636 637 638 639 640 641 642 643 644
        
        /* If in dos mode, replace '\' with '/' */
        if (pUnixFolder->m_dwPathMode == PATHMODE_DOS) {
            char *pBackslash = strchr(pNextPathElement, '\\');
            while (pBackslash) {
                *pBackslash = '/';
                pBackslash = strchr(pBackslash, '\\');
            }
        }
645
    }
646

647 648 649 650 651 652 653 654
    /* Special case for the root folder. */
    if (!strcmp(szCompletePath, "/")) {
        *ppidl = pidl = (LPITEMIDLIST)SHAlloc(sizeof(USHORT));
        if (!pidl) return FALSE;
        pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
        return TRUE;
    }
    
655 656 657 658 659 660 661 662 663 664 665 666 667 668
    /* 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);
        return FALSE;
    }
    
    /* 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);
669
    
670 671 672 673 674 675 676
    /* Convert to CP_ACP and WCHAR */
    if (!UNIXFS_shitemid_len_from_filename(pNextPathElement, &pszAPath, &pwszPath))
        return 0;

    /* Compute the length of the complete ITEMIDLIST */
    cPidlLen = 0;
    pSlash = pszAPath;
677
    while (pSlash) {
678 679 680 681
        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;
682
    }
683 684 685 686 687 688 689 690 691

    /* 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);

692
    *ppidl = pidl = (LPITEMIDLIST)SHAlloc(cPidlLen);
693
    if (!pidl) return FALSE;
694

695
    /* Concatenate the SHITEMIDs of the sub-directories. */
696
    while (*pNextPathElement) {
697
        pSlash = strchr(pNextPathElement+1, '/');
698
        if (pSlash) *pSlash = '\0';
699
        pNextPathElement = UNIXFS_build_shitemid(szCompletePath, pidl);
700 701 702
        if (pSlash) *pSlash = '/';
            
        if (!pNextPathElement) {
703
            SHFree(*ppidl);
704
            *ppidl = NULL;
705
            return FALSE;
706
        }
707 708 709
        pidl = ILGetNext(pidl);
    }
    pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
710

711
    if ((char *)pidl-(char *)*ppidl+sizeof(USHORT) != cPidlLen) /* We've corrupted the heap :( */ 
712
        ERR("Computed length of pidl incorrect. Please report.\n");
713
    
714 715 716
    return TRUE;
}

717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
/******************************************************************************
 * 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;
    struct stat statPrefix;
    char *pNextDir;
        
    /* Determine the path's length bytes */
    while (current && current->mkid.cb) {
744
        dwPathLen += UNIXFS_filename_from_shitemid(current, NULL) + 1; /* For the '/' */
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
        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;
    if (!(This->m_dwAttributes & SFGAO_FILESYSTEM)) {
        *pNextDir = '\0';
        if (!stat(This->m_pszPath, &statPrefix) && UNIXFS_is_dos_device(&statPrefix))
            This->m_dwAttributes |= SFGAO_FILESYSTEM;
    }
    while (current && current->mkid.cb) {
767
        pNextDir += UNIXFS_filename_from_shitemid(current, pNextDir);
768 769 770 771 772 773 774 775 776 777 778 779 780
        if (!(This->m_dwAttributes & SFGAO_FILESYSTEM)) {
            *pNextDir = '\0';
            if (!stat(This->m_pszPath, &statPrefix) && UNIXFS_is_dos_device(&statPrefix))
                This->m_dwAttributes |= SFGAO_FILESYSTEM;
        }
        *pNextDir++ = '/';
        current = ILGetNext(current);
    }
    *pNextDir='\0';
 
    return S_OK;
}

781 782 783 784 785 786 787 788 789 790 791 792 793 794
/******************************************************************************
 * 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);
}

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

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

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

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

    return InterlockedIncrement(&This->m_cRef);
}

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

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

    return cRef;
}

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

    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);

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

881
    if (!result) TRACE("FAILED!\n");
882 883 884
    return result ? S_OK : E_FAIL;
}

885
static IUnknown *UnixSubFolderIterator_Constructor(UnixFolder *pUnixFolder, SHCONTF fFilter);
886

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

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

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

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

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

922 923
    if (!pidl || !pidl->mkid.cb)
        return E_INVALIDARG;
924 925 926 927 928 929 930 931 932 933
   
    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;
    }
    
    hr = CreateUnixFolder(NULL, &IID_IPersistFolder3, (void**)&persistFolder, clsidChild);
934 935
    if (!SUCCEEDED(hr)) return hr;
    hr = IPersistFolder_QueryInterface(persistFolder, riid, (void**)ppvOut);
936 937
   
    if (SUCCEEDED(hr)) {
938 939 940 941 942 943
        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);
    } 

944
    IPersistFolder3_Release(persistFolder);
945
    
946 947 948
    return hr;
}

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

956
static HRESULT WINAPI UnixFolder_IShellFolder2_CompareIDs(IShellFolder2* iface, LPARAM lParam, 
957 958
    LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
    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);

977 978 979 980 981
    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);

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

    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;
1006 1007
}

1008 1009
static HRESULT WINAPI UnixFolder_IShellFolder2_CreateViewObject(IShellFolder2* iface, HWND hwndOwner,
    REFIID riid, void** ppv)
1010
{
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
    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);
        }
1026 1027 1028
    } else if (IsEqualIID(&IID_IDropTarget, riid)) {
        hr = IShellFolder2_QueryInterface(iface, &IID_IDropTarget, ppv);
    }
1029 1030
    
    return hr;
1031 1032
}

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

1050 1051
        *rgfInOut = SFGAO_CANCOPY|SFGAO_CANMOVE|SFGAO_CANLINK|SFGAO_CANRENAME|SFGAO_CANDELETE|
                    SFGAO_HASPROPSHEET|SFGAO_DROPTARGET|SFGAO_FILESYSTEM;
1052 1053
        lstrcpyA(szAbsolutePath, This->m_pszPath);
        pszRelativePath = szAbsolutePath + lstrlenA(szAbsolutePath);
1054
        for (i=0; i<cidl; i++) {
1055
            if (!(This->m_dwAttributes & SFGAO_FILESYSTEM)) {
1056
                struct stat fileStat;
1057 1058
                if (!UNIXFS_filename_from_shitemid(apidl[i], pszRelativePath)) 
                    return E_INVALIDARG;
1059 1060 1061
                if (stat(szAbsolutePath, &fileStat) || !UNIXFS_is_dos_device(&fileStat))
                    *rgfInOut &= ~SFGAO_FILESYSTEM;
            }
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 1096 1097 1098
        pidl = ILCombine(This->m_pidlLocation, apidl[0]);
        *ppvOut = (LPVOID)IExtractIconA_Constructor(pidl);
        SHFree(pidl);
        return S_OK;
1099
    } else if (IsEqualIID(&IID_IExtractIconW, riid)) {
1100
        LPITEMIDLIST pidl;
1101
        if (cidl != 1) return E_INVALIDARG;
1102 1103 1104
        pidl = ILCombine(This->m_pidlLocation, apidl[0]);
        *ppvOut = (LPVOID)IExtractIconW_Constructor(pidl);
        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=%lx, 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 1153 1154 1155 1156

            hr = IShellFolder_BindToObject(iface, pidl, NULL, &IID_IShellFolder, (void**)&pSubFolder);
            if (!SUCCEEDED(hr)) return hr;
       
            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 1185
    WCHAR wszSrcRelative[MAX_PATH];
    int cBasePathLen = lstrlenA(This->m_pszPath), i;
1186
    struct stat statDest;
Michael Jung's avatar
Michael Jung committed
1187 1188 1189
    LPITEMIDLIST pidlSrc, pidlDest, pidlRelativeDest;
    LPOLESTR lpwszName;
    HRESULT hr;
1190
   
Michael Jung's avatar
Michael Jung committed
1191 1192
    TRACE("(iface=%p, hwnd=%p, pidl=%p, lpcwszName=%s, uFlags=0x%08lx, ppidlOut=%p)\n",
          iface, hwnd, pidl, debugstr_w(lpcwszName), uFlags, ppidlOut); 
1193

Michael Jung's avatar
Michael Jung committed
1194
    /* prepare to fail */
1195 1196 1197
    if (ppidlOut)
        *ppidlOut = NULL;
    
Michael Jung's avatar
Michael Jung committed
1198 1199 1200 1201 1202 1203 1204 1205 1206
    /* 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);

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

    /* build destination path */
Michael Jung's avatar
Michael Jung committed
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225
    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);
    }
1226 1227 1228 1229 1230 1231

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

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

    /* 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
1238 1239 1240 1241 1242
    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)) {
1243 1244
        rename(szDest, szSrc); /* Undo the renaming */
        return E_FAIL;
1245
    }
Michael Jung's avatar
Michael Jung committed
1246 1247 1248
    pidlDest = ILCombine(This->m_pidlLocation, pidlRelativeDest);
    ILFree(pidlRelativeDest);
    pidlSrc = ILCombine(This->m_pidlLocation, pidl);
1249 1250 1251 1252 1253 1254 1255 1256
    
    /* 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
1257 1258 1259 1260
        *ppidlOut = ILClone(ILFindLastID(pidlDest));
        
    ILFree(pidlSrc);
    ILFree(pidlDest);
1261 1262
    
    return S_OK;
1263 1264
}

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

static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumn(IShellFolder2* iface, 
    DWORD dwReserved, ULONG *pSort, ULONG *pDisplay) 
{
1275
    FIXME("stub\n");
1276 1277 1278 1279 1280 1281
    return E_NOTIMPL;
}

static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumnState(IShellFolder2* iface, 
    UINT iColumn, SHCOLSTATEF *pcsFlags)
{
1282
    FIXME("stub\n");
1283 1284 1285 1286 1287 1288
    return E_NOTIMPL;
}

static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultSearchGUID(IShellFolder2* iface, 
    GUID *pguid)
{
1289
    FIXME("stub\n");
1290 1291 1292 1293 1294 1295
    return E_NOTIMPL;
}

static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsEx(IShellFolder2* iface, 
    LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
{
1296
    FIXME("stub\n");
1297 1298 1299
    return E_NOTIMPL;
}

1300
#define SHELLVIEWCOLUMNS 7 
1301

1302 1303 1304
static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsOf(IShellFolder2* iface, 
    LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *psd)
{
1305
    UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
1306
    HRESULT hr = E_FAIL;
1307 1308 1309 1310
    struct passwd *pPasswd;
    struct group *pGroup;
    static const shvheader SFHeader[SHELLVIEWCOLUMNS] = {
        {IDS_SHV_COLUMN1,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
1311 1312 1313 1314
        {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},
1315
        {IDS_SHV_COLUMN10, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7},
1316
        {IDS_SHV_COLUMN11, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7}
1317 1318
    };

1319 1320
    TRACE("(iface=%p, pidl=%p, iColumn=%d, psd=%p) stub\n", iface, pidl, iColumn, psd);
    
1321
    if (!psd || iColumn >= SHELLVIEWCOLUMNS)
1322 1323 1324
        return E_INVALIDARG;

    if (!pidl) {
1325 1326
        psd->fmt = SFHeader[iColumn].fmt;
        psd->cxChar = SFHeader[iColumn].cxChar;
1327
        psd->str.uType = STRRET_CSTR;
1328
        LoadStringA(shell32_hInstance, SFHeader[iColumn].colnameid, psd->str.u.cStr, MAX_PATH);
1329 1330
        return S_OK;
    } else {
1331
        struct stat statItem;
1332
        if (iColumn == 4 || iColumn == 5 || iColumn == 6) {
1333 1334 1335
            char szPath[FILENAME_MAX];
            strcpy(szPath, This->m_pszPath);
            if (!UNIXFS_filename_from_shitemid(pidl, szPath + strlen(szPath)))
1336 1337 1338 1339
                return E_INVALIDARG;
            if (stat(szPath, &statItem)) 
                return E_INVALIDARG;
        }
1340 1341
        psd->str.u.cStr[0] = '\0';
        psd->str.uType = STRRET_CSTR;
1342 1343 1344 1345
        switch (iColumn) {
            case 0:
                hr = IShellFolder2_GetDisplayNameOf(iface, pidl, SHGDN_NORMAL|SHGDN_INFOLDER, &psd->str);
                break;
1346
            case 1:
1347 1348 1349 1350 1351 1352 1353 1354 1355
                _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:
1356 1357 1358 1359 1360 1361 1362 1363 1364 1365
                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' : '-';
1366 1367
                psd->str.u.cStr[10] = '\0';
                break;
1368
            case 5:
1369
                pPasswd = getpwuid(statItem.st_uid);
1370 1371
                if (pPasswd) strcpy(psd->str.u.cStr, pPasswd->pw_name);
                break;
1372
            case 6:
1373
                pGroup = getgrgid(statItem.st_gid);
1374 1375
                if (pGroup) strcpy(psd->str.u.cStr, pGroup->gr_name);
                break;
1376 1377 1378 1379 1380 1381 1382 1383 1384
        }
    }
    
    return hr;
}

static HRESULT WINAPI UnixFolder_IShellFolder2_MapColumnToSCID(IShellFolder2* iface, UINT iColumn,
    SHCOLUMNID *pscid) 
{
1385
    FIXME("stub\n");
1386 1387 1388 1389
    return E_NOTIMPL;
}

/* VTable for UnixFolder's IShellFolder2 interface.
1390
 */
1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411
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
1412 1413
};

1414
static HRESULT WINAPI UnixFolder_IPersistFolder3_QueryInterface(IPersistFolder3* iface, REFIID riid, 
1415 1416
    void** ppvObject)
{
1417
    return UnixFolder_IShellFolder2_QueryInterface(
1418
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistFolder3, iface)), riid, ppvObject);
1419 1420
}

1421
static ULONG WINAPI UnixFolder_IPersistFolder3_AddRef(IPersistFolder3* iface)
1422
{
1423
    return UnixFolder_IShellFolder2_AddRef(
1424
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistFolder3, iface)));
1425 1426
}

1427
static ULONG WINAPI UnixFolder_IPersistFolder3_Release(IPersistFolder3* iface)
1428
{
1429
    return UnixFolder_IShellFolder2_Release(
1430
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistFolder3, iface)));
1431 1432
}

1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443
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;

    memcpy(pClassID, This->m_pCLSID, sizeof(CLSID));
    return S_OK;
1444 1445
}

1446
static HRESULT WINAPI UnixFolder_IPersistFolder3_Initialize(IPersistFolder3* iface, LPCITEMIDLIST pidl)
1447
{
1448
    UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder3, iface);
1449 1450
    LPCITEMIDLIST current = pidl;
    char szBasePath[FILENAME_MAX] = "/";
1451 1452 1453
    
    TRACE("(iface=%p, pidl=%p)\n", iface, pidl);

1454 1455
    /* Find the UnixFolderClass root */
    while (current->mkid.cb) {
1456 1457 1458
        if ((_ILIsDrive(current) && IsEqualCLSID(This->m_pCLSID, &CLSID_ShellFSFolder)) ||
            (_ILIsSpecialFolder(current) && IsEqualCLSID(This->m_pCLSID, _ILGetGUIDPointer(current))))
        {
1459
            break;
1460
        }
1461 1462
        current = ILGetNext(current);
    }
1463

1464
    if (current && current->mkid.cb) {
1465 1466 1467 1468 1469 1470
        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))) {
1471 1472 1473 1474 1475 1476
            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;
1477 1478
        } 
        current = ILGetNext(current);
1479 1480 1481
    } else if (_ILIsDesktop(pidl) || _ILIsValue(pidl) || _ILIsFolder(pidl)) {
        /* Path rooted at Desktop */
        WCHAR wszDesktopPath[MAX_PATH];
1482 1483
        if (!SHGetSpecialFolderPathW(0, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE)) 
            return E_FAIL;
1484 1485 1486
        PathAddBackslashW(wszDesktopPath);
        if (!UNIXFS_get_unix_path(wszDesktopPath, szBasePath))
            return E_FAIL;
1487 1488 1489 1490 1491 1492 1493
        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;
1494 1495 1496 1497
    } else {
        ERR("Unknown pidl type!\n");
        pdump(pidl);
        return E_INVALIDARG;
1498
    }
1499
  
1500
    This->m_pidlLocation = ILClone(pidl);
1501
    return UNIXFS_initialize_target_folder(This, szBasePath, current, 0); 
1502 1503
}

1504
static HRESULT WINAPI UnixFolder_IPersistFolder3_GetCurFolder(IPersistFolder3* iface, LPITEMIDLIST* ppidl)
1505
{
1506
    UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder3, iface);
1507 1508
    
    TRACE ("(iface=%p, ppidl=%p)\n", iface, ppidl);
1509 1510 1511 1512 1513 1514

    if (!ppidl)
        return E_POINTER;
    *ppidl = ILClone (This->m_pidlLocation);
    return S_OK;
}
1515 1516 1517 1518 1519 1520 1521

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] = "";
1522
    
1523 1524 1525 1526 1527 1528 1529
    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) {
1530 1531 1532 1533
        if (FAILED(SHGetFolderPathW(0, ppfti->csidl, NULL, 0, wszTargetDosPath)) ||
            !UNIXFS_get_unix_path(wszTargetDosPath, szTargetPath))
        {
            return E_FAIL;
1534 1535
        }
    } else if (*ppfti->szTargetParsingName) {
1536 1537 1538
        lstrcpyW(wszTargetDosPath, ppfti->szTargetParsingName);
        PathAddBackslashW(wszTargetDosPath);
        if (!UNIXFS_get_unix_path(wszTargetDosPath, szTargetPath)) {
1539 1540
            return E_FAIL;
        }
1541
    } else if (ppfti->pidlTargetFolder) {
1542 1543 1544 1545
        if (!SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTargetDosPath) ||
            !UNIXFS_get_unix_path(wszTargetDosPath, szTargetPath))
        {
            return E_FAIL;
1546
        }
1547 1548
    } else {
        return E_FAIL;
1549 1550 1551 1552 1553 1554 1555
    }

    This->m_pszPath = SHAlloc(lstrlenA(szTargetPath)+1);
    if (!This->m_pszPath) 
        return E_FAIL;
    lstrcpyA(This->m_pszPath, szTargetPath);
    This->m_pidlLocation = ILClone(pidlRoot);
1556 1557
    This->m_dwAttributes = (ppfti->dwAttributes != -1) ? ppfti->dwAttributes :
        (SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|SFGAO_CANRENAME|SFGAO_FILESYSTEM);
1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568

    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;
}

1569 1570
/* VTable for UnixFolder's IPersistFolder interface.
 */
1571 1572 1573 1574 1575 1576 1577 1578 1579
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
1580 1581
};

1582 1583
static HRESULT WINAPI UnixFolder_IPersistPropertyBag_QueryInterface(IPersistPropertyBag* iface,
    REFIID riid, void** ppv)
1584 1585
{
    return UnixFolder_IShellFolder2_QueryInterface(
1586
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface)), riid, ppv);
1587 1588
}

1589
static ULONG WINAPI UnixFolder_IPersistPropertyBag_AddRef(IPersistPropertyBag* iface)
1590 1591
{
    return UnixFolder_IShellFolder2_AddRef(
1592
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface)));
1593 1594
}

1595
static ULONG WINAPI UnixFolder_IPersistPropertyBag_Release(IPersistPropertyBag* iface)
1596 1597
{
    return UnixFolder_IShellFolder2_Release(
1598
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface)));
1599 1600 1601 1602 1603 1604
}

static HRESULT WINAPI UnixFolder_IPersistPropertyBag_GetClassID(IPersistPropertyBag* iface, 
    CLSID* pClassID)
{
    return UnixFolder_IPersistFolder3_GetClassID(
1605
        STATIC_CAST(IPersistFolder3, ADJUST_THIS(UnixFolder, IPersistPropertyBag, iface)), pClassID);
1606 1607 1608 1609
}

static HRESULT WINAPI UnixFolder_IPersistPropertyBag_InitNew(IPersistPropertyBag* iface)
{
1610
    FIXME("() stub\n");
1611 1612 1613 1614 1615 1616
    return E_NOTIMPL;
}

static HRESULT WINAPI UnixFolder_IPersistPropertyBag_Load(IPersistPropertyBag *iface, 
    IPropertyBag *pPropertyBag, IErrorLog *pErrorLog)
{
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642
     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);
1643 1644 1645 1646 1647
}

static HRESULT WINAPI UnixFolder_IPersistPropertyBag_Save(IPersistPropertyBag *iface,
    IPropertyBag *pPropertyBag, BOOL fClearDirty, BOOL fSaveAllProperties)
{
1648
    FIXME("() stub\n");
1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663
    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
};

1664 1665 1666 1667
static HRESULT WINAPI UnixFolder_ISFHelper_QueryInterface(ISFHelper* iface, REFIID riid, 
    void** ppvObject)
{
    return UnixFolder_IShellFolder2_QueryInterface(
1668
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, ISFHelper, iface)), riid, ppvObject);
1669 1670 1671 1672 1673
}

static ULONG WINAPI UnixFolder_ISFHelper_AddRef(ISFHelper* iface)
{
    return UnixFolder_IShellFolder2_AddRef(
1674
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, ISFHelper, iface)));
1675 1676 1677 1678 1679
}

static ULONG WINAPI UnixFolder_ISFHelper_Release(ISFHelper* iface)
{
    return UnixFolder_IShellFolder2_Release(
1680
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, ISFHelper, iface)));
1681 1682
}

1683
static HRESULT WINAPI UnixFolder_ISFHelper_GetUniqueName(ISFHelper* iface, LPWSTR pwszName, UINT uLen)
1684
{
1685 1686 1687 1688 1689 1690
    UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
    IEnumIDList *pEnum;
    HRESULT hr;
    LPITEMIDLIST pidlElem;
    DWORD dwFetched;
    int i;
1691 1692
    static const WCHAR wszNewFolder[] = { 'N','e','w',' ','F','o','l','d','e','r', 0 };
    static const WCHAR wszFormat[] = { '%','s',' ','%','d',0 };
1693

1694
    TRACE("(iface=%p, pwszName=%p, uLen=%u)\n", iface, pwszName, uLen);
1695
    
1696
    if (uLen < sizeof(wszNewFolder)/sizeof(WCHAR)+3)
1697 1698 1699 1700 1701
        return E_INVALIDARG;

    hr = IShellFolder2_EnumObjects(STATIC_CAST(IShellFolder2, This), 0,
                                   SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN, &pEnum);
    if (SUCCEEDED(hr)) {
1702
        lstrcpynW(pwszName, wszNewFolder, uLen);
1703 1704 1705
        IEnumIDList_Reset(pEnum);
        i = 2;
        while ((IEnumIDList_Next(pEnum, 1, &pidlElem, &dwFetched) == S_OK) && (dwFetched == 1)) {
1706 1707 1708
            WCHAR wszTemp[MAX_PATH];
            _ILSimpleGetTextW(pidlElem, wszTemp, MAX_PATH);
            if (!lstrcmpiW(wszTemp, pwszName)) {
1709
                IEnumIDList_Reset(pEnum);
1710
                snprintfW(pwszName, uLen, wszFormat, wszNewFolder, i++);
1711 1712 1713 1714 1715 1716 1717 1718 1719
                if (i > 99) {
                    hr = E_FAIL;
                    break;
                }
            }
        }
        IEnumIDList_Release(pEnum);
    }
    return hr;
1720 1721
}

1722
static HRESULT WINAPI UnixFolder_ISFHelper_AddFolder(ISFHelper* iface, HWND hwnd, LPCWSTR pwszName, 
1723 1724
    LPITEMIDLIST* ppidlOut)
{
1725 1726
    UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
    char szNewDir[FILENAME_MAX];
1727
    int cBaseLen;
1728

1729 1730
    TRACE("(iface=%p, hwnd=%p, pwszName=%s, ppidlOut=%p)\n", 
            iface, hwnd, debugstr_w(pwszName), ppidlOut);
1731 1732 1733 1734

    if (ppidlOut)
        *ppidlOut = NULL;

1735 1736 1737 1738 1739 1740 1741
    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);
   
1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755
    if (mkdir(szNewDir, 0755)) {
        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 */
1756
        if (UNIXFS_path_to_pidl(This, pwszName, &pidlRelative)) {
1757 1758 1759 1760 1761 1762 1763
            LPITEMIDLIST pidlAbsolute = ILCombine(This->m_pidlLocation, pidlRelative);
            if (ppidlOut)
                *ppidlOut = pidlRelative;
            else
                ILFree(pidlRelative);
            SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, pidlAbsolute, NULL);
            ILFree(pidlAbsolute);
1764
        } else return E_FAIL;
1765 1766
        return S_OK;
    }
1767 1768 1769 1770 1771
}

static HRESULT WINAPI UnixFolder_ISFHelper_DeleteItems(ISFHelper* iface, UINT cidl, 
    LPCITEMIDLIST* apidl)
{
1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783
    UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
    char szAbsolute[FILENAME_MAX], *pszRelative;
    LPITEMIDLIST pidlAbsolute;
    HRESULT hr = S_OK;
    UINT i;
    
    TRACE("(iface=%p, cidl=%d, apidl=%p)\n", iface, cidl, apidl);

    lstrcpyA(szAbsolute, This->m_pszPath);
    pszRelative = szAbsolute + lstrlenA(szAbsolute);
    
    for (i=0; i<cidl && SUCCEEDED(hr); i++) {
1784 1785
        if (!UNIXFS_filename_from_shitemid(apidl[i], pszRelative))
            return E_INVALIDARG;
1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
        pidlAbsolute = ILCombine(This->m_pidlLocation, apidl[i]);
        if (_ILIsFolder(apidl[i])) {
            if (rmdir(szAbsolute)) {
                hr = E_FAIL;
            } else {
                SHChangeNotify(SHCNE_RMDIR, SHCNF_IDLIST, pidlAbsolute, NULL);
            }
        } else if (_ILIsValue(apidl[i])) {
            if (unlink(szAbsolute)) {
                hr = E_FAIL;
            } else {
                SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlAbsolute, NULL);
            }
        }
        ILFree(pidlAbsolute);
    }
        
    return hr;
1804 1805 1806 1807 1808
}

static HRESULT WINAPI UnixFolder_ISFHelper_CopyItems(ISFHelper* iface, IShellFolder *psfFrom, 
    UINT cidl, LPCITEMIDLIST *apidl)
{
1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842
    UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
    DWORD dwAttributes;
    UINT i;
    HRESULT hr;
    char szAbsoluteDst[FILENAME_MAX], *pszRelativeDst;
    
    TRACE("(iface=%p, psfFrom=%p, cidl=%d, apidl=%p): semi-stub\n", iface, psfFrom, cidl, apidl);

    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;

        /* 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 */
1843
        UNIXFS_filename_from_shitemid(apidl[i], pszRelativeDst);
1844 1845 1846 1847

        FIXME("Would copy %s to %s. Not yet implemented.\n", szSrc, szAbsoluteDst);
    }
    return S_OK;
1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861
}

/* 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
};

1862 1863 1864 1865
static HRESULT WINAPI UnixFolder_IDropTarget_QueryInterface(IDropTarget* iface, REFIID riid, 
    void** ppvObject)
{
    return UnixFolder_IShellFolder2_QueryInterface(
1866
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IDropTarget, iface)), riid, ppvObject);
1867 1868 1869 1870 1871
}

static ULONG WINAPI UnixFolder_IDropTarget_AddRef(IDropTarget* iface)
{
    return UnixFolder_IShellFolder2_AddRef(
1872
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IDropTarget, iface)));
1873 1874 1875 1876 1877
}

static ULONG WINAPI UnixFolder_IDropTarget_Release(IDropTarget* iface)
{
    return UnixFolder_IShellFolder2_Release(
1878
        STATIC_CAST(IShellFolder2, ADJUST_THIS(UnixFolder, IDropTarget, iface)));
1879 1880
}

1881 1882 1883
#define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
#define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])

1884 1885 1886
static HRESULT WINAPI UnixFolder_IDropTarget_DragEnter(IDropTarget *iface, IDataObject *pDataObject,
    DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
{
1887 1888 1889 1890 1891
    UnixFolder *This = ADJUST_THIS(UnixFolder, IDropTarget, iface);
    FORMATETC format;
    STGMEDIUM medium; 
        
    TRACE("(iface=%p, pDataObject=%p, dwKeyState=%08lx, pt={.x=%ld, .y=%ld}, pdwEffect=%p)\n",
1892 1893
        iface, pDataObject, dwKeyState, pt.x, pt.y, pdwEffect);

1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919
    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;
1920 1921 1922 1923 1924
}

static HRESULT WINAPI UnixFolder_IDropTarget_DragOver(IDropTarget *iface, DWORD dwKeyState, 
    POINTL pt, DWORD *pdwEffect)
{
1925 1926 1927 1928
    UnixFolder *This = ADJUST_THIS(UnixFolder, IDropTarget, iface);
    
    TRACE("(iface=%p, dwKeyState=%08lx, pt={.x=%ld, .y=%ld}, pdwEffect=%p)\n", iface, dwKeyState, 
        pt.x, pt.y, pdwEffect);
1929

1930 1931 1932 1933 1934 1935
    if (!pdwEffect)
        return E_INVALIDARG;

    *pdwEffect = KeyStateToDropEffect(dwKeyState) & This->m_dwDropEffectsMask;
    
    return S_OK;
1936 1937 1938
}

static HRESULT WINAPI UnixFolder_IDropTarget_DragLeave(IDropTarget *iface) {
1939 1940 1941 1942 1943 1944 1945
    UnixFolder *This = ADJUST_THIS(UnixFolder, IDropTarget, iface);
    
    TRACE("(iface=%p)\n", iface);
 
    This->m_dwDropEffectsMask = DROPEFFECT_NONE;
    
    return S_OK;
1946 1947 1948 1949 1950
}

static HRESULT WINAPI UnixFolder_IDropTarget_Drop(IDropTarget *iface, IDataObject *pDataObject,
    DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
{
1951 1952 1953 1954 1955 1956
    UnixFolder *This = ADJUST_THIS(UnixFolder, IDropTarget, iface);
    FORMATETC format;
    STGMEDIUM medium; 
    HRESULT hr;

    TRACE("(iface=%p, pDataObject=%p, dwKeyState=%ld, pt={.x=%ld, .y=%ld}, pdwEffect=%p) semi-stub\n",
1957
        iface, pDataObject, dwKeyState, pt.x, pt.y, pdwEffect);
1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971

    InitFormatEtc(format, cfShellIDList, TYMED_HGLOBAL);
    hr = IDataObject_GetData(pDataObject, &format, &medium);
    if (!SUCCEEDED(hr)) 
        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());
1972
        
1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016
        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;
    }
 
2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031
    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
};

2032
/******************************************************************************
2033
 * Unix[Dos]Folder_Constructor [Internal]
2034 2035 2036 2037 2038 2039 2040
 *
 * 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
2041 2042
 *  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
2043
 *  compatible signature.
2044 2045 2046
 *
 *  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.
2047
 */
2048
static HRESULT CreateUnixFolder(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv, const CLSID *pCLSID) 
2049
{
2050 2051
    HRESULT hr = E_FAIL;
    UnixFolder *pUnixFolder = SHAlloc((ULONG)sizeof(UnixFolder));
2052 2053 2054 2055 2056
   
    if (pUnkOuter) {
        FIXME("Aggregation not yet implemented!\n");
        return CLASS_E_NOAGGREGATION;
    }
2057
    
2058 2059
    if(pUnixFolder) {
        pUnixFolder->lpIShellFolder2Vtbl = &UnixFolder_IShellFolder2_Vtbl;
2060
        pUnixFolder->lpIPersistFolder3Vtbl = &UnixFolder_IPersistFolder3_Vtbl;
2061
        pUnixFolder->lpIPersistPropertyBagVtbl = &UnixFolder_IPersistPropertyBag_Vtbl;
2062
        pUnixFolder->lpISFHelperVtbl = &UnixFolder_ISFHelper_Vtbl;
2063
        pUnixFolder->lpIDropTargetVtbl = &UnixFolder_IDropTarget_Vtbl;
2064 2065
        pUnixFolder->m_cRef = 0;
        pUnixFolder->m_pszPath = NULL;
2066
        pUnixFolder->m_pidlLocation = NULL;
2067
        pUnixFolder->m_dwPathMode = IsEqualCLSID(&CLSID_UnixFolder, pCLSID) ? PATHMODE_UNIX : PATHMODE_DOS;
2068
        pUnixFolder->m_dwAttributes = 0;
2069
        pUnixFolder->m_pCLSID = pCLSID;
2070
        pUnixFolder->m_dwDropEffectsMask = DROPEFFECT_NONE;
2071 2072 2073 2074 2075 2076 2077

        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;
}
2078

2079 2080
HRESULT WINAPI UnixFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
    TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2081
    return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_UnixFolder);
2082
}
2083

2084 2085
HRESULT WINAPI UnixDosFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
    TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2086
    return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_UnixDosFolder);
2087 2088
}

2089 2090
HRESULT WINAPI FolderShortcut_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
    TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2091
    return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_FolderShortcut);
2092 2093
}

2094 2095
HRESULT WINAPI MyDocuments_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
    TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
2096
    return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_MyDocuments);
2097 2098
}

2099 2100 2101 2102 2103
HRESULT WINAPI ShellFSFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
    TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
    return CreateUnixFolder(pUnkOuter, riid, ppv, &CLSID_ShellFSFolder);
}

2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114
/******************************************************************************
 * 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
2115
    LONG m_cRef;
2116
    SHCONTF m_fFilter;
2117 2118
    DIR *m_dirFolder;
    char m_szFolder[FILENAME_MAX];
2119 2120 2121 2122
} UnixSubFolderIterator;

static void UnixSubFolderIterator_Destroy(UnixSubFolderIterator *iterator) {
    TRACE("(iterator=%p)\n", iterator);
2123 2124 2125

    if (iterator->m_dirFolder)
        closedir(iterator->m_dirFolder);
2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174
    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);
2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190
    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);
2191 2192
            rgelt[i] = (LPITEMIDLIST)SHAlloc(
                UNIXFS_shitemid_len_from_filename(pszRelativePath, NULL, NULL)+sizeof(USHORT));
2193 2194 2195 2196 2197 2198 2199
            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));
2200 2201
            i++;
        }
2202
        *pszRelativePath = '\0'; /* Restore the original path in This->m_szFolder. */
2203
    }
2204
    
2205 2206 2207
    if (pceltFetched)
        *pceltFetched = i;

2208
    return (i == 0) ? S_FALSE : S_OK;
2209
}
2210
    
2211 2212
static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Skip(IEnumIDList* iface, ULONG celt)
{
2213 2214 2215
    LPITEMIDLIST *apidl;
    ULONG cFetched;
    HRESULT hr;
2216 2217 2218
    
    TRACE("(iface=%p, celt=%ld)\n", iface, celt);

2219 2220 2221 2222 2223 2224 2225 2226 2227
    /* Call IEnumIDList::Next and delete the resulting pidls. */
    apidl = (LPITEMIDLIST*)SHAlloc(celt * sizeof(LPITEMIDLIST));
    hr = IEnumIDList_Next(iface, celt, apidl, &cFetched);
    if (SUCCEEDED(hr))
        while (cFetched--) 
            SHFree(apidl[cFetched]);
    SHFree(apidl);

    return hr;
2228 2229 2230 2231 2232 2233 2234 2235
}

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

2236 2237
    if (This->m_dirFolder)
        rewinddir(This->m_dirFolder);
2238 2239 2240 2241 2242 2243 2244
    
    return S_OK;
}

static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Clone(IEnumIDList* This, 
    IEnumIDList** ppenum)
{
2245
    FIXME("stub\n");
2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260
    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
};

2261
static IUnknown *UnixSubFolderIterator_Constructor(UnixFolder *pUnixFolder, SHCONTF fFilter) {
2262 2263 2264 2265 2266 2267 2268
    UnixSubFolderIterator *iterator;

    TRACE("(pUnixFolder=%p)\n", pUnixFolder);
    
    iterator = SHAlloc((ULONG)sizeof(UnixSubFolderIterator));
    iterator->lpIEnumIDListVtbl = &UnixSubFolderIterator_IEnumIDList_Vtbl;
    iterator->m_cRef = 0;
2269
    iterator->m_fFilter = fFilter;
2270 2271
    iterator->m_dirFolder = opendir(pUnixFolder->m_pszPath);
    lstrcpyA(iterator->m_szFolder, pUnixFolder->m_pszPath);
2272 2273 2274 2275 2276

    UnixSubFolderIterator_IEnumIDList_AddRef((IEnumIDList*)iterator);
    
    return (IUnknown*)iterator;
}