Windows: GetInfoTip really set return to NULL
[openafs.git] / src / WINNT / client_exp / shell_ext.cpp
index 37e0f11..2f916f8 100644 (file)
@@ -1,19 +1,29 @@
 /*
  * Copyright 2000, International Business Machines Corporation and others.
  * All Rights Reserved.
- * 
+ *
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
  */
 
+#include <afxpriv.h>
+#include "stdafx.h"
+#include "PropFile.h"
+#include "PropACL.h"
+#include "PropVolume.h"
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <shtypes.h>
+#include <shlwapi.h>
+
 extern "C" {
 #include <afs/param.h>
 #include <afs/stds.h>
+#include <afs/afs_consts.h>
 }
 
-#include <afxpriv.h>
-#include "stdafx.h"
+#include <atlconv.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include "afs_shl_ext.h"
@@ -26,7 +36,8 @@ extern "C" {
 #include "server_status_dlg.h"
 #include "auth_dlg.h"
 #include "submounts_dlg.h"
-#include "gui2fs.h"
+#include "make_symbolic_link_dlg.h"
+#include <WINNT\afsreg.h>
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
@@ -34,90 +45,137 @@ extern "C" {
 static char THIS_FILE[] = __FILE__;
 #endif
 
+#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
+
 
 ULONG nCMRefCount = 0; // IContextMenu ref count
 ULONG nSERefCount = 0; // IShellExtInit ref count
 ULONG nICRefCount=0;
 ULONG nTPRefCount=0;
 ULONG nXPRefCount=0;
+UINT nPSRefCount=0;
 
-#define MAXSIZE 2048 /* most I'll get back from PIOCTL */
 #define PCCHAR(str)    ((char *)(const char *)str)
-static char space[MAXSIZE];
+static char space[AFS_PIOCTL_MAXSIZE];
 
 static BOOL IsADir(const CString& strName)
 {
-       struct _stat statbuf;
+    struct _stat statbuf;
 
-       if (_stat(strName, &statbuf) < 0)
-               return FALSE;
+    if (_tstat(strName, &statbuf) < 0)
+       return FALSE;
 
-       return statbuf.st_mode & _S_IFDIR;
+    return statbuf.st_mode & _S_IFDIR;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // CShellExt
 
 IMPLEMENT_DYNCREATE(CShellExt, CCmdTarget)
+IMPLEMENT_DYNCREATE(CShellExt2, CCmdTarget)
+#define REG_CLIENT_PARMS_KEY    "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters"
+#define OVERLAYENABLED 1
 
 CShellExt::CShellExt()
 {
-       EnableAutomation();
-       nCMRefCount++;
-       HRESULT hr;
-       hr = SHGetMalloc(&m_pAlloc);
-       if (FAILED(hr))
-               m_pAlloc = NULL;
+    HKEY NPKey;
+    EnableAutomation();
+    nCMRefCount++;
+    HRESULT hr;
+    UINT code;
+    DWORD ShellOption,LSPsize,LSPtype;
+    m_overlayObject = 0;
+    hr = SHGetMalloc(&m_pAlloc);
+    m_bIsOverlayEnabled=FALSE;
+    if (FAILED(hr))
+       m_pAlloc = NULL;
+    RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,0, (IsWow64()?KEY_WOW64_64KEY:0)|KEY_QUERY_VALUE, &NPKey);
+    LSPsize=sizeof(ShellOption);
+    code=RegQueryValueEx(NPKey, _T("ShellOption"), NULL,
+                         &LSPtype, (LPBYTE)&ShellOption, &LSPsize);
+    RegCloseKey (NPKey);
+    m_bIsOverlayEnabled=((code==0) && (LSPtype==REG_DWORD) && ((ShellOption & OVERLAYENABLED)!=0));
+
+    INITCOMMONCONTROLSEX used = {
+        sizeof(INITCOMMONCONTROLSEX),
+        ICC_DATE_CLASSES | ICC_WIN95_CLASSES | ICC_BAR_CLASSES | ICC_USEREX_CLASSES
+    };
+    InitCommonControlsEx(&used);
+
+    TRACE("Create CShellExt, Ref count %d/n",nCMRefCount);
 }
 
 CShellExt::~CShellExt()
 {
-       if(m_pAlloc) m_pAlloc->Release();
-       nCMRefCount--;
+    if(m_pAlloc)
+       m_pAlloc->Release();
+    nCMRefCount--;
+    TRACE("Destroy CShellExt, Ref count %d/n",nCMRefCount);
 }
 
 
 void CShellExt::OnFinalRelease()
 {
-       // When the last reference for an automation object is released
-       // OnFinalRelease is called.  The base class will automatically
-       // deletes the object.  Add additional cleanup required for your
-       // object before calling the base class.
+    // When the last reference for an automation object is released
+    // OnFinalRelease is called.  The base class will automatically
+    // deletes the object.  Add additional cleanup required for your
+    // object before calling the base class.
 
-       CCmdTarget::OnFinalRelease();
+    CCmdTarget::OnFinalRelease();
 }
 
 
 BEGIN_MESSAGE_MAP(CShellExt, CCmdTarget)
-       //{{AFX_MSG_MAP(CShellExt)
-               // NOTE - the ClassWizard will add and remove mapping macros here.
-       //}}AFX_MSG_MAP
+    //{{AFX_MSG_MAP(CShellExt)
+    // NOTE - the ClassWizard will add and remove mapping macros here.
+    //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
 BEGIN_DISPATCH_MAP(CShellExt, CCmdTarget)
-       //{{AFX_DISPATCH_MAP(CShellExt)
-               // NOTE - the ClassWizard will add and remove mapping macros here.
-       //}}AFX_DISPATCH_MAP
+    //{{AFX_DISPATCH_MAP(CShellExt)
+    // NOTE - the ClassWizard will add and remove mapping macros here.
+    //}}AFX_DISPATCH_MAP
 END_DISPATCH_MAP()
 
 // Note: we add support for IID_IShellExt to support typesafe binding
-//  from VBA.  This IID must match the GUID that is attached to the 
+//  from VBA.  This IID must match the GUID that is attached to the
 //  dispinterface in the .ODL file.
 
+#ifndef _WIN64
+// 32-bit
 // {DC515C27-6CAC-11D1-BAE7-00C04FD140D2}
 static const IID IID_IShellExt =
 { 0xdc515c27, 0x6cac, 0x11d1, { 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd2 } };
+static const IID IID_IShellExt2 =
+{ 0xdc515c27, 0x6cac, 0x11d1, { 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd3 } };
+#else
+// 64-bit
+// {5f820ca1-3dde-11db-b2ce-001558092db5}
+static const IID IID_IShellExt =
+{ 0x5f820ca1, 0x3dde, 0x11db, {0xb2, 0xce, 0x00, 0x15, 0x58, 0x09, 0x2d, 0xb5 } };
+static const IID IID_IShellExt2 =
+{ 0x5f820ca1, 0x3dde, 0x11db, {0xb2, 0xce, 0x00, 0x15, 0x58, 0x09, 0x2d, 0xb6 } };
+#endif
 
 BEGIN_INTERFACE_MAP(CShellExt, CCmdTarget)
-       INTERFACE_PART(CShellExt, IID_IShellExt, Dispatch)
+    INTERFACE_PART(CShellExt, IID_IShellExt, Dispatch)
     INTERFACE_PART(CShellExt, IID_IContextMenu, MenuExt)
     INTERFACE_PART(CShellExt, IID_IShellExtInit, ShellInit)
-       INTERFACE_PART(CShellExt, IID_IShellIconOverlayIdentifier, IconExt)
-       INTERFACE_PART(CShellExt, IID_IQueryInfo , ToolTipExt)
-       INTERFACE_PART(CShellExt, IID_IPersistFile , PersistFileExt)
+    INTERFACE_PART(CShellExt, IID_IShellIconOverlayIdentifier, IconExt)
+    INTERFACE_PART(CShellExt, IID_IQueryInfo , ToolTipExt)
+    INTERFACE_PART(CShellExt, IID_IPersistFile , PersistFileExt)
+    INTERFACE_PART(CShellExt, IID_IShellPropSheetExt , PropertySheetExt)
 END_INTERFACE_MAP()
 
-IMPLEMENT_OLECREATE(CShellExt, STR_EXT_TITLE, 0xdc515c27, 0x6cac, 0x11d1, 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd2)
+#ifndef _WIN64
+    // 32-bit
+IMPLEMENT_OLECREATE(CShellExt, _STR_EXT_TITLE, 0xdc515c27, 0x6cac, 0x11d1, 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd2)
+IMPLEMENT_OLECREATE(CShellExt2, _STR_EXT_TITLE, 0xdc515c27, 0x6cac, 0x11d1, 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd3)
+#else
+    // 64-bit
+IMPLEMENT_OLECREATE(CShellExt, _STR_EXT_TITLE, 0x5f820ca1, 0x3dde, 0x11db, 0xb2, 0xce, 0x0, 0x15, 0x58, 0x09, 0x2d, 0xb5)
+IMPLEMENT_OLECREATE(CShellExt2, _STR_EXT_TITLE, 0x5f820ca1, 0x3dde, 0x11db, 0xb2, 0xce, 0x0, 0x15, 0x58, 0x09, 0x2d, 0xb6)
+#endif
 
 
 /////////////////////////////////////////////////////////////////////////////
@@ -137,111 +195,127 @@ STDMETHODIMP CShellExt::XMenuExt::QueryInterface(REFIID riid, void** ppv)
 
 STDMETHODIMP_(ULONG) CShellExt::XMenuExt::AddRef(void)
 {
-       return ++nCMRefCount;
+    return ++nCMRefCount;
 }
 
 STDMETHODIMP_(ULONG) CShellExt::XMenuExt::Release(void)
 {
-       if (nCMRefCount > 0)
-               nCMRefCount--;
+    if (nCMRefCount > 0)
+       nCMRefCount--;
 
-       return nCMRefCount;
+    return nCMRefCount;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // IConextMenu Functions
 /////////////////////////////////////////////////////////////////////////////
 STDMETHODIMP CShellExt::XMenuExt::QueryContextMenu(HMENU hMenu,UINT indexMenu,
-    UINT idCmdFirst, UINT idCmdLast,UINT uFlags)
+                                                  UINT idCmdFirst, UINT idCmdLast,UINT uFlags)
 {
     METHOD_PROLOGUE(CShellExt, MenuExt);
 
-       // Don't add any menu items if we're being asked to deal with this file as a shortcut.
-       if (uFlags & CMF_VERBSONLY)
-               return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, (USHORT)0));
-
-       // Check to see if there's already an AFS menu here; if so, remove it
-       int nItemsNow = GetMenuItemCount (hMenu);
-       CString strAfsItemText = GetMessageString(IDS_AFS_ITEM);
-       LPCTSTR pszAfsItemText = (LPCTSTR)strAfsItemText;
-       for (int iItem = 0; iItem < nItemsNow; iItem++) {
-               TCHAR szItemText[256];
-               if (!GetMenuString (hMenu, iItem, szItemText, 256, MF_BYPOSITION))
-                       continue;
-               if (!lstrcmp (szItemText, pszAfsItemText)) {
-                       DeleteMenu (hMenu, iItem, MF_BYPOSITION);
-                       continue;
-               }
-               if ((!lstrcmp(szItemText,"&Delete"))&&(pThis->m_bIsSymlink)) {  /*this is a symlink - don't present a delete menu!*/
-                       DeleteMenu (hMenu, iItem, MF_BYPOSITION);
-                       continue;
-               }
-               if ((!lstrcmp(szItemText,"Cu&t"))&&(pThis->m_bIsSymlink)) {             /*same for cut*/
-                       DeleteMenu (hMenu, iItem, MF_BYPOSITION);
-                       continue;
-               }
+    // Don't add any menu items if we're being asked to deal with this file as a shortcut.
+    if (uFlags & CMF_VERBSONLY)
+       return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, (USHORT)0);
+
+    if (!pThis->m_bIsPathInAFS)
+       return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, (USHORT)0);
+
+    // Check to see if there's already an AFS menu here; if so, remove it
+    int nItemsNow = GetMenuItemCount (hMenu);
+    CString strAfsItemText = GetMessageString(IDS_AFS_ITEM);
+    CString strDeleteText = GetMessageString(IDS_MENU_DELETE);
+    CString strCutText = GetMessageString(IDS_MENU_CUT);
+    LPCTSTR pszAfsItemText = (LPCTSTR)strAfsItemText;
+    for (int iItem = 0; iItem < nItemsNow; iItem++) {
+       TCHAR szItemText[256];
+       if (!GetMenuString (hMenu, iItem, szItemText, 256, MF_BYPOSITION))
+           continue;
+       if (lstrcmp (szItemText, pszAfsItemText)==0) {
+           DeleteMenu (hMenu, iItem, MF_BYPOSITION);
+           continue;
+       }
+       if (((lstrcmp(szItemText,strDeleteText)==0)||(lstrcmp(szItemText,strCutText)==0))&&((pThis->m_bIsSymlink)||(pThis->m_bIsMountpoint))) {
+        DeleteMenu (hMenu, iItem, MF_BYPOSITION);
+           continue;
+       }
+    }
+    int indexShellMenu = 0;
+
+    // Create the AFS submenu using the allowed ID's.
+    HMENU hAfsMenu = CreatePopupMenu();
+    int indexAfsMenu = 0;
+
+    // Only enable the ACL menu item if a single directory is selected
+    int nSingleDirOnly = MF_GRAYED;
+    if (pThis->m_bDirSelected && (pThis->m_astrFileNames.GetSize() == 1))
+       nSingleDirOnly = MF_ENABLED;
+    ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_ACL_SET, GetMessageString(IDS_ACLS_ITEM));
+
+    // Volume/Partition submenu of the AFS submenu
+    HMENU hVolPartMenu = CreatePopupMenu();
+    int indexVolPartMenu = 0;
+    ::InsertMenu(hVolPartMenu, indexVolPartMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_VOLUME_PROPERTIES, GetMessageString(IDS_VOL_PART_PROPS_ITEM));
+    ::InsertMenu(hVolPartMenu, indexVolPartMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE, GetMessageString(IDS_VOL_PART_REFRESH_ITEM));
+    ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hVolPartMenu, GetMessageString(IDS_VOL_PART_ITEM));
+
+    // Mount Point submenu of the AFS submenu
+    HMENU hMountPointMenu = CreatePopupMenu();
+    int indexMountPointMenu = 0;
+    int nMountPointSelected = MF_GRAYED;
+    for (int n = pThis->m_astrFileNames.GetSize() - 1 ; n >= 0; n--) {
+       if ( IsMountPoint(pThis->m_astrFileNames[n]) ) {
+           nMountPointSelected = MF_ENABLED;
+           break;
+       }
+    }
+    ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_SHOW, GetMessageString(IDS_MP_SHOW_ITEM));
+    ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION | nMountPointSelected, idCmdFirst + IDM_MOUNTPOINT_REMOVE, GetMessageString(IDS_MP_REMOVE_ITEM));
+    ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_MAKE, GetMessageString(IDS_MP_MAKE_ITEM));
+    ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hMountPointMenu, GetMessageString(IDS_MOUNT_POINT_ITEM));
+
+    ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_FLUSH, GetMessageString(IDS_FLUSH_FILE_DIR_ITEM));
+    ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_FLUSH_VOLUME, GetMessageString(IDS_FLUSH_VOLUME_ITEM));
+    ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHOW_SERVER, GetMessageString(IDS_SHOW_FILE_SERVERS_ITEM));
+    ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHOWCELL, GetMessageString(IDS_SHOW_CELL_ITEM));
+    ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SERVER_STATUS, GetMessageString(IDS_SHOW_SERVER_STATUS_ITEM));
+
+    HMENU hSymbolicMenu = CreatePopupMenu();
+    int indexSymbolicMenu = 0;
+    int nSymlinkSelected = MF_GRAYED;
+    for (int n = pThis->m_astrFileNames.GetSize() - 1 ; n >= 0; n--) {
+       if ( IsSymlink(pThis->m_astrFileNames[n]) ) {
+           nSymlinkSelected = MF_ENABLED;
+           break;
        }
-       int indexShellMenu = 0;
-
-       // Create the AFS submenu using the allowed ID's.
-       HMENU hAfsMenu = CreatePopupMenu();
-       int indexAfsMenu = 0;
-
-       // The Authentication item has been removed from the AFS menu because
-       // there is now a tray icon to handle authentication.
-       //
-       //::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_AUTHENTICATION, GetMessageString(IDS_AUTHENTICATION_ITEM));
-       
-       // Only enable the ACL menu item if a single directory is selected
-       int nSingleDirOnly = MF_GRAYED;
-       if (pThis->m_bDirSelected && (pThis->m_astrFileNames.GetSize() == 1))
-               nSingleDirOnly = MF_ENABLED;
-       ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_ACL_SET, GetMessageString(IDS_ACLS_ITEM));
-       
-       // Volume/Partition submenu of the AFS submenu
-       HMENU hVolPartMenu = CreatePopupMenu();
-       int indexVolPartMenu = 0;
-       ::InsertMenu(hVolPartMenu, indexVolPartMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_VOLUME_PROPERTIES, GetMessageString(IDS_VOL_PART_PROPS_ITEM));
-       ::InsertMenu(hVolPartMenu, indexVolPartMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE, GetMessageString(IDS_VOL_PART_REFRESH_ITEM));
-       ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hVolPartMenu, GetMessageString(IDS_VOL_PART_ITEM));
-
-       // Mount Point submenu of the AFS submenu
-       HMENU hMountPointMenu = CreatePopupMenu();
-       int indexMountPointMenu = 0;
-       ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_SHOW, GetMessageString(IDS_MP_SHOW_ITEM));
-       ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_REMOVE, GetMessageString(IDS_MP_REMOVE_ITEM));
-       ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_MAKE, GetMessageString(IDS_MP_MAKE_ITEM));
-       ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hMountPointMenu, GetMessageString(IDS_MOUNT_POINT_ITEM));
-
-       ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_FLUSH, GetMessageString(IDS_FLUSH_FILE_DIR_ITEM));   
-       ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_FLUSH_VOLUME, GetMessageString(IDS_FLUSH_VOLUME_ITEM));
-       ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHOW_SERVER, GetMessageString(IDS_SHOW_FILE_SERVERS_ITEM));
-       ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHOWCELL, GetMessageString(IDS_SHOW_CELL_ITEM));
-       ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SERVER_STATUS, GetMessageString(IDS_SHOW_SERVER_STATUS_ITEM));
-       if (pThis->m_bIsSymlink)
-               ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_REMOVE_SYMLINK, GetMessageString(IDS_REMOVE_SYMLINK_ITEM));
-       
-       // The Submounts menu has been removed because the AFS tray icon
-       // and control panel now support mapping drives directly to an AFS
-       // path.
-       //
-       // HMENU hSubmountMenu = CreatePopupMenu();
-       // int indexSubmountMenu = 0;
-       // ::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_SUBMOUNTS_CREATE, GetMessageString(IDS_SUBMOUNTS_CREATE_ITEM));
-       // ::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SUBMOUNTS_EDIT, GetMessageString(IDS_SUBMOUNTS_EDIT_ITEM));
-       // ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hSubmountMenu, GetMessageString(IDS_SUBMOUNTS_ITEM));
-
-       // Add a separator
-       ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
-
-       // Add the AFS submenu to the shell's menu
-       ::InsertMenu(hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hAfsMenu, GetMessageString(IDS_AFS_ITEM));
-
-       // Add a separator after us
-       ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
-       
-    return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, 
-                       (USHORT)indexAfsMenu + indexVolPartMenu + indexMountPointMenu + indexShellMenu));
+    }
+
+    ::InsertMenu(hSymbolicMenu, indexSymbolicMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SYMBOLICLINK_ADD, GetMessageString(IDS_SYMBOLICLINK_ADD));
+    ::InsertMenu(hSymbolicMenu, indexSymbolicMenu++, MF_STRING | MF_BYPOSITION | nSymlinkSelected, idCmdFirst + IDM_SYMBOLICLINK_SHOW, GetMessageString(IDS_SYMBOLICLINK_SHOW));
+    ::InsertMenu(hSymbolicMenu, indexSymbolicMenu++, MF_STRING | MF_BYPOSITION | nSymlinkSelected, idCmdFirst + IDM_SYMBOLICLINK_REMOVE, GetMessageString(IDS_SYMBOLICLINK_REMOVE));
+    ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hSymbolicMenu, GetMessageString(IDS_SYMBOLIC_LINK_ITEM));
+
+    // The Submounts menu has been removed because the AFS tray icon
+    // and control panel now support mapping drives directly to an AFS
+    // path.
+    //
+    //HMENU hSubmountMenu = CreatePopupMenu();
+    //int indexSubmountMenu = 0;
+    //::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_SUBMOUNTS_CREATE, GetMessageString(IDS_SUBMOUNTS_CREATE_ITEM));
+    //::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SUBMOUNTS_EDIT, GetMessageString(IDS_SUBMOUNTS_EDIT_ITEM));
+    //::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hSubmountMenu, GetMessageString(IDS_SUBMOUNTS_ITEM));
+
+    // Add a separator
+    ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
+
+    // Add the AFS submenu to the shell's menu
+    ::InsertMenu(hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hAfsMenu, GetMessageString(IDS_AFS_ITEM));
+
+    // Add a separator after us
+    ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
+
+    return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL,
+                        (USHORT)indexAfsMenu + indexVolPartMenu + indexMountPointMenu + indexShellMenu + indexSymbolicMenu);
 }
 
 STDMETHODIMP CShellExt::XMenuExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
@@ -251,172 +325,238 @@ STDMETHODIMP CShellExt::XMenuExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
     if (HIWORD(lpici->lpVerb ))
         return E_FAIL;
 
-       AddRef();
+    AddRef();
 
-       CStringArray &files = pThis->m_astrFileNames;
+    CStringArray &files = pThis->m_astrFileNames;
+
+    switch (LOWORD(lpici->lpVerb))
+    {
+    case IDM_AUTHENTICATION:   {
+       CAuthDlg dlg;
+       dlg.DoModal();
+       break;
+    }
+
+    case IDM_ACL_SET:                  {
+       CSetAfsAcl dlg;
+       ASSERT(files.GetSize() == 1);
+       dlg.SetDir(files[0]);
+       dlg.DoModal();
+       break;
+    }
+
+    case IDM_VOLUME_PROPERTIES:        {
+       CVolumeInfo dlg;
+       dlg.SetFiles(files);
+       dlg.DoModal();
+       break;
+    }
+
+    case IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE:
+       CheckVolumes();
+       break;
+
+    case IDM_MOUNTPOINT_SHOW:
+       ListMount(files);
+       break;
+
+    case IDM_MOUNTPOINT_REMOVE:        {
+       int nChoice = ShowMessageBox(IDS_REALLY_DEL_MOUNT_POINTS, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_DEL_MOUNT_POINTS);
+       if (nChoice == IDYES)
+           RemoveMount(files);
+       break;
+    }
 
-       switch (LOWORD(lpici->lpVerb))
+    case IDM_MOUNTPOINT_MAKE:  {
+       CMakeMountPointDlg dlg;
+       ASSERT(files.GetSize() == 1);
+       dlg.SetDir(files[0]);
+       dlg.DoModal();
+       break;
+    }
+
+    case IDM_FLUSH:
+       Flush(files);
+       break;
+
+    case IDM_FLUSH_VOLUME:
+       FlushVolume(files);
+       break;
+
+    case IDM_SHOW_SERVER:
+       WhereIs(files);
+       break;
+
+    case IDM_SHOWCELL:
+       WhichCell(files);
+       break;
+
+    case IDM_SERVER_STATUS: {
+       CServerStatusDlg dlg;
+       dlg.DoModal();
+       break;
+    }
+
+        /*
+       case IDM_SUBMOUNTS_EDIT:        {
+       CSubmountsDlg dlg;
+       dlg.DoModal();
+       break;
+       }
+       case IDM_SUBMOUNTS_CREATE:      {
+       ASSERT(files.GetSize() == 1);
+       CSubmountsDlg dlg;
+       dlg.SetAddOnlyMode(files[0]);
+       dlg.DoModal();
+       break;
+       }
+        */
+    case IDM_SYMBOLICLINK_REMOVE: {
+       if (files.GetSize()>1)
+           break;
+       CString msg=files.GetAt(0);
+       int i;
+       if ((i=msg.ReverseFind('\\'))>0)
+           msg=msg.Left(i+1);
+       else if ((i=msg.ReverseFind(':'))>0)
+           msg=msg.Left(i+1)+"\\";
+       if (!SetCurrentDirectory(msg))
        {
-               case IDM_AUTHENTICATION:        {
-                                                                               CAuthDlg dlg;
-                                                                               dlg.DoModal();
-                                                                               break;
-                                                                       }
-               
-               case IDM_ACL_SET:                       { 
-                                                                               CSetAfsAcl dlg;
-                                                                               ASSERT(files.GetSize() == 1);
-                                                                               dlg.SetDir(files[0]);
-                                                                               dlg.DoModal();
-                                                                       }
-                                                                       break;
-               
-               case IDM_VOLUME_PROPERTIES:     {
-                                                                               CVolumeInfo dlg;
-                                                                               dlg.SetFiles(files);
-                                                                               dlg.DoModal();
-                                                                       }
-                                                                       break;
-
-               case IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE:     CheckVolumes();
-                                                                                                       break;
-
-               case IDM_MOUNTPOINT_SHOW:       ListMount(files);
-                                                                       break;
-
-               case IDM_MOUNTPOINT_REMOVE:     {
-                                                                               int nChoice = ShowMessageBox(IDS_REALLY_DEL_MOUNT_POINTS, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_DEL_MOUNT_POINTS);
-                                                                               if (nChoice == IDYES)
-                                                                                       RemoveMount(files);
-                                                                       }
-                                                                       break;
-               
-               case IDM_MOUNTPOINT_MAKE:       {
-                                                                               CMakeMountPointDlg dlg;
-                                                                               ASSERT(files.GetSize() == 1);
-                                                                               dlg.SetDir(files[0]);
-                                                                               dlg.DoModal();
-                                                                       }
-                                                                       break;
-
-
-               case IDM_FLUSH:                         Flush(files);
-                                                                       break;
-               
-               case IDM_FLUSH_VOLUME:          FlushVolume(files);
-                                                                       break;
-
-               case IDM_SHOW_SERVER:           WhereIs(files);
-                                                                       break;
-               
-               case IDM_SHOWCELL:                      WhichCell(files);
-                                                                       break;
-
-               case IDM_SERVER_STATUS:         {
-                                                                               CServerStatusDlg dlg;
-                                                                               dlg.DoModal();
-                                                                       }
-                                                                       break;
-               
-               case IDM_SUBMOUNTS_EDIT:        {
-                                                                               CSubmountsDlg dlg;
-                                                                               dlg.DoModal();
-                                                                       }
-                                                                       break;
-
-               case IDM_SUBMOUNTS_CREATE:      {
-                                                                               ASSERT(files.GetSize() == 1);
-                                                                               CSubmountsDlg dlg;
-                                                                               dlg.SetAddOnlyMode(files[0]);
-                                                                               dlg.DoModal();
-                                                                       }
-                                                                       break;
-               case IDM_REMOVE_SYMLINK:        {
-                                                                               if (files.GetSize()>1)
-                                                                                       break;
-                                                                               int nChoice = ShowMessageBox(IDS_REALLY_REMOVE_SYMLINK, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_REMOVE_SYMLINK);
-                                                                               if (nChoice == IDYES)
-                                                                                       RemoveSymlink(files.GetAt(0));
-                                                                       }
-                                                                       break;
-               default:
-                       ASSERT(FALSE);
-                       Release();
-                   return E_INVALIDARG;
+           MessageBeep((UINT)-1);
+           ShowMessageBox(IDS_UNABLE_TO_SET_CURRENT_DIRECTORY,MB_OK,IDS_UNABLE_TO_SET_CURRENT_DIRECTORY);
+           break;
        }
+       msg=files.GetAt(0);
+       if ((i=msg.ReverseFind('\\'))>0||((i=msg.ReverseFind(':'))>0))
+           msg=msg.Right(msg.GetLength()-i-1);
+       int nChoice = ShowMessageBox(IDS_REALLY_REMOVE_SYMLINK, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_REMOVE_SYMLINK,msg);
+       if (nChoice == IDYES)
+           RemoveSymlink(files.GetAt(0));
+       break;
+    }
+
+    case IDM_SYMBOLICLINK_ADD: {
+       CStringA msg(files.GetAt(0));
+       int i;
+       if ((i=msg.ReverseFind('\\'))>0)
+           msg=msg.Left(i+1);
+       else if ((i=msg.ReverseFind(':'))>0)
+           msg=msg.Left(i+1)+"\\";
+       CMakeSymbolicLinkDlg dlg;
+       dlg.Setbase(msg);
+       dlg.DoModal();
+       break;
+    }
 
+    case IDM_SYMBOLICLINK_SHOW:
+       ListSymlink(files);
+       break;
+
+    case IDM_REMOVE_SYMLINK:   {
+       if (files.GetSize()>1)
+           break;
+       int nChoice = ShowMessageBox(IDS_REALLY_REMOVE_SYMLINK, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_REMOVE_SYMLINK);
+       if (nChoice == IDYES)
+           RemoveSymlink(files.GetAt(0));
+       }
+       break;
+    default:
+       ASSERT(FALSE);
        Release();
+       return E_INVALIDARG;
+    }
+
+    Release();
 
     return NOERROR;
 }
 
-STDMETHODIMP CShellExt::XMenuExt::GetCommandString(UINT idCmd, UINT uType,
+STDMETHODIMP CShellExt::XMenuExt::GetCommandString(UINT_PTR idCmd, UINT uType,
     UINT* pwReserved, LPSTR pszName, UINT cchMax)
 {
-       if (uType != GCS_HELPTEXT)
-               return NOERROR;                 // ?????????????????????????????????????????????????
+    if (uType != GCS_HELPTEXT)
+       return NOERROR;                 // ?????????????????????????????????????????????????
 
-       UINT nCmdStrID;
+    UINT nCmdStrID;
 
     AfxSetResourceHandle(theApp.m_hInstance);
 
     switch (idCmd)
-       {
-               case IDM_AUTHENTICATION:        nCmdStrID = ID_AUTHENTICATE;
-                                                                       break;
-               
-               case IDM_ACL_SET:                       nCmdStrID = ID_ACL_SET;
-                                                                       break;
-               
-               case IDM_VOLUME_PROPERTIES:     nCmdStrID = ID_VOLUME_PROPERTIES;
-                                                                       break;
-
-               case IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE:     nCmdStrID = ID_VOLUMEPARTITION_UPDATENAMEIDTABLE;
-                                                                                                       break;
-
-               case IDM_MOUNTPOINT_SHOW:       nCmdStrID = ID_MOUNTPOINT_SHOW;
-                                                                       break;
-
-               case IDM_MOUNTPOINT_REMOVE:     nCmdStrID = ID_MOUNTPOINT_REMOVE;
-                                                                       break;
-               
-               case IDM_MOUNTPOINT_MAKE:       nCmdStrID = ID_MOUNTPOINT_MAKE;
-                                                                       break;
-
-               case IDM_FLUSH:                         nCmdStrID = ID_FLUSH;
-                                                                       break;
-               
-               case IDM_FLUSH_VOLUME:          nCmdStrID = ID_VOLUME_FLUSH;
-                                                                       break;
-
-               case IDM_SHOW_SERVER:           nCmdStrID = ID_WHEREIS;
-                                                                       break;
-               
-               case IDM_SHOWCELL:                      nCmdStrID = ID_SHOWCELL;
-                                                                       break;
-
-               case IDM_SERVER_STATUS:         nCmdStrID = ID_SERVER_STATUS;
-                                                                       break;
-
-               case IDM_SUBMOUNTS_CREATE:      nCmdStrID = ID_SUBMOUNTS_CREATE;
-                                                                       break;
-               
-               case IDM_SUBMOUNTS_EDIT:        nCmdStrID = ID_SUBMOUNTS_EDIT;
-                                                                       break;
-
-               case IDM_REMOVE_SYMLINK:        nCmdStrID= ID_REMOVE_SYMLINK;
-                                                                       break;
-               
-               default:
-                       ASSERT(FALSE);
-                       Release();
-                   return E_INVALIDARG;
-       }
+    {
+    case IDM_AUTHENTICATION:
+       nCmdStrID = ID_AUTHENTICATE;
+       break;
+
+    case IDM_ACL_SET:
+       nCmdStrID = ID_ACL_SET;
+       break;
+
+    case IDM_VOLUME_PROPERTIES:
+       nCmdStrID = ID_VOLUME_PROPERTIES;
+       break;
+
+    case IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE:
+       nCmdStrID = ID_VOLUMEPARTITION_UPDATENAMEIDTABLE;
+       break;
+
+    case IDM_MOUNTPOINT_SHOW:
+       nCmdStrID = ID_MOUNTPOINT_SHOW;
+       break;
+
+    case IDM_MOUNTPOINT_REMOVE:
+       nCmdStrID = ID_MOUNTPOINT_REMOVE;
+       break;
+
+    case IDM_MOUNTPOINT_MAKE:
+       nCmdStrID = ID_MOUNTPOINT_MAKE;
+       break;
+
+    case IDM_FLUSH:
+       nCmdStrID = ID_FLUSH;
+       break;
+
+    case IDM_FLUSH_VOLUME:
+       nCmdStrID = ID_VOLUME_FLUSH;
+       break;
+
+    case IDM_SHOW_SERVER:
+       nCmdStrID = ID_WHEREIS;
+       break;
+
+    case IDM_SHOWCELL:
+       nCmdStrID = ID_SHOWCELL;
+       break;
+
+    case IDM_SERVER_STATUS:
+       nCmdStrID = ID_SERVER_STATUS;
+       break;
+
+    case IDM_SYMBOLICLINK_ADD:
+       nCmdStrID = ID_SYMBOLICLINK_ADD;
+       break;
+
+    case IDM_SYMBOLICLINK_SHOW:
+       nCmdStrID = ID_SYMBOLICLINK_SHOW;
+       break;
+
+    case IDM_SYMBOLICLINK_REMOVE:
+       nCmdStrID = ID_SYMBOLICLINK_REMOVE;
+       break;
+
+    case IDM_REMOVE_SYMLINK:
+       nCmdStrID= ID_REMOVE_SYMLINK;
+       break;
+
+    default:
+       ASSERT(FALSE);
+       Release();
+       return E_INVALIDARG;
+    }
 
     CString strMsg;
     LoadString (strMsg, nCmdStrID);
 
-    strncpy(pszName, strMsg, cchMax);
+    _tcsncpy((LPTSTR) pszName, strMsg, cchMax);
 
     return NOERROR;
 }
@@ -433,15 +573,15 @@ STDMETHODIMP CShellExt::XShellInit::QueryInterface(REFIID riid, void** ppv)
 
 STDMETHODIMP_(ULONG) CShellExt::XShellInit::AddRef(void)
 {
-       return ++nSERefCount;
+    return ++nSERefCount;
 }
 
 STDMETHODIMP_(ULONG) CShellExt::XShellInit::Release(void)
 {
-       if (nSERefCount > 0)
-               nSERefCount--;
-       
-       return nSERefCount;
+    if (nSERefCount > 0)
+       nSERefCount--;
+
+    return nSERefCount;
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -455,57 +595,97 @@ STDMETHODIMP CShellExt::XShellInit::Initialize(LPCITEMIDLIST pidlFolder, IDataOb
     FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
     STGMEDIUM medium;
 
-       // We must have a data object
-    if (pdobj == NULL)
-           return E_FAIL;
-
-    //  Use the given IDataObject to get a list of filenames (CF_HDROP)
-    hres = pdobj->GetData(&fmte, &medium);
-    if (FAILED(hres))
-           return E_FAIL;
-
-    int nNumFiles = DragQueryFile((HDROP)medium.hGlobal, 0xFFFFFFFF, NULL, 0);
-       if (nNumFiles == 0)
-               hres = E_FAIL;
-       else {
-               pThis->m_bDirSelected = FALSE;
-
-               for (int ii = 0; ii < nNumFiles; ii++) {
-                       CString strFileName;
-
-                       // Get the size of the file name string
-                       int nNameLen = DragQueryFile((HDROP)medium.hGlobal, ii, 0, 0);
-
-                       // Make room for it in our string object
-                       LPTSTR pszFileNameBuf = strFileName.GetBuffer(nNameLen + 1);    // +1 for the terminating NULL
-                       ASSERT(pszFileNameBuf);
-                       
-                       // Get the file name
-                       DragQueryFile((HDROP)medium.hGlobal, ii, pszFileNameBuf, nNameLen + 1);
-                       
-                       strFileName.ReleaseBuffer();
-
-                       if (!IsPathInAfs(strFileName)) {
-                               pThis->m_astrFileNames.RemoveAll();
-                               break;
-                       } else {
-                               pThis->m_bIsSymlink=IsSymlink(strFileName);
-                       }
-
-                       if (IsADir(strFileName))
-                               pThis->m_bDirSelected = TRUE;
-
-                       pThis->m_astrFileNames.Add(strFileName);
-               }
-
-               if (pThis->m_astrFileNames.GetSize() > 0)
-                       hres = S_OK;
-               else
-                       hres = E_FAIL;
+    // We must have a data object
+    if ((pdobj == NULL) && (pidlFolder == NULL))
+        return E_FAIL;
+
+    pThis->m_bIsSymlink=false;
+    pThis->m_bIsMountpoint=false;
+    pThis->m_bIsPathInAFS=false;
+    pThis->m_bDirSelected=false;
+
+    if (pdobj) {
+        //  Use the given IDataObject to get a list of filenames (CF_HDROP)
+        hres = pdobj->GetData(&fmte, &medium);
+        if (FAILED(hres)) {
+        return E_FAIL;
+        }
+
+        int nNumFiles = DragQueryFile((HDROP)medium.hGlobal, 0xFFFFFFFF, NULL, 0);
+        if (nNumFiles == 0)
+            hres = E_FAIL;
+        else {
+            pThis->m_bDirSelected = FALSE;
+
+            for (int ii = 0; ii < nNumFiles; ii++) {
+                CString strFileName;
+
+                // Get the size of the file name string
+                int nNameLen = DragQueryFile((HDROP)medium.hGlobal, ii, 0, 0);
+
+                // Make room for it in our string object
+                LPTSTR pszFileNameBuf = strFileName.GetBuffer(nNameLen + 1);   // +1 for the terminating NULL
+                ASSERT(pszFileNameBuf);
+
+                // Get the file name
+                DragQueryFile((HDROP)medium.hGlobal, ii, pszFileNameBuf, nNameLen + 1);
+
+                strFileName.ReleaseBuffer();
+                if (!IsPathInAfs(strFileName)) {
+                pThis->m_astrFileNames.RemoveAll();
+                pThis->m_bIsPathInAFS=false;
+                break;
+                } else {
+                pThis->m_bIsSymlink=pThis->m_bIsSymlink||IsSymlink(strFileName);
+                pThis->m_bIsMountpoint=pThis->m_bIsMountpoint||IsMountPoint(strFileName);
+                pThis->m_bIsPathInAFS=true;
+                }
+
+                if (IsADir(strFileName))
+                pThis->m_bDirSelected = TRUE;
+
+                pThis->m_astrFileNames.Add(strFileName);
+            }
+            // Release the data
+            ReleaseStgMedium(&medium);
+        }
     }
-    // Release the data
-    ReleaseStgMedium(&medium);
+    if ((pThis->m_astrFileNames.GetSize() == 0)&&(pidlFolder)) {
+        // if there are no valid files selected, try the folder background
+        IShellFolder *parentFolder = NULL;
+        STRRET name;
+        TCHAR * szDisplayName = NULL;
+
+        hres = ::SHGetDesktopFolder(&parentFolder);
+        if (FAILED(hres))
+            return hres;
+
+        hres = parentFolder->GetDisplayNameOf(pidlFolder, SHGDN_NORMAL | SHGDN_FORPARSING, &name);
+        if (FAILED(hres)) {
+            parentFolder->Release();
+            return hres;
+        }
+
+        hres = StrRetToStr (&name, pidlFolder, &szDisplayName);
+        if (FAILED(hres))
+            return hres;
+        parentFolder->Release();
+        if (szDisplayName) {
+            pThis->m_bDirSelected = TRUE;
+            CString strFileName = CString(szDisplayName);
+            if (IsPathInAfs(strFileName)) {
+                pThis->m_bIsSymlink=IsSymlink(strFileName);
+                pThis->m_bIsMountpoint=IsMountPoint(strFileName);
+                pThis->m_bIsPathInAFS=true;
+                pThis->m_astrFileNames.Add(strFileName);
+            }
+            CoTaskMemFree(szDisplayName);
+        }
+    }
+       if (pThis->m_astrFileNames.GetSize() > 0)
+           hres = NOERROR;
+       else
+           hres = E_FAIL;
 
     return hres;
 }
@@ -518,15 +698,15 @@ STDMETHODIMP CShellExt::XIconExt::QueryInterface(REFIID riid, void** ppv)
 
 STDMETHODIMP_(ULONG) CShellExt::XIconExt::AddRef(void)
 {
-       return ++nICRefCount;
+    return ++nICRefCount;
 }
 
 STDMETHODIMP_(ULONG) CShellExt::XIconExt::Release(void)
 {
-       if (nICRefCount > 0)
-               nICRefCount--;
+    if (nICRefCount > 0)
+       nICRefCount--;
 
-       return nICRefCount;
+    return nICRefCount;
 }
 
 
@@ -537,38 +717,68 @@ STDMETHODIMP_(ULONG) CShellExt::XIconExt::Release(void)
 STDMETHODIMP CShellExt::XIconExt::GetOverlayInfo(LPWSTR pwszIconFile
        ,int cchMax,int* pIndex,DWORD* pdwFlags)
 {
-       if(IsBadWritePtr(pIndex, sizeof(int)))
-               return E_INVALIDARG;
-       if(IsBadWritePtr(pdwFlags, sizeof(DWORD)))
-               return E_INVALIDARG;
-
-       HMODULE hModule=GetModuleHandle("shell32.dll");
-       TCHAR szModule[MAX_PATH];
-       DWORD z=GetModuleFileName(hModule,szModule,sizeof(szModule));
-       MultiByteToWideChar( CP_ACP,0,szModule,-1,pwszIconFile,cchMax); 
-       *pIndex = 30;
-       *pdwFlags = ISIOI_ICONFILE | ISIOI_ICONINDEX;
-       return S_OK;
+    METHOD_PROLOGUE(CShellExt, IconExt);
+    if(IsBadWritePtr(pIndex, sizeof(int)))
+       return E_INVALIDARG;
+    if(IsBadWritePtr(pdwFlags, sizeof(DWORD)))
+       return E_INVALIDARG;
+
+    // The icons must reside in the same path as this dll
+    TCHAR szModule[MAX_PATH];
+    GetModuleFileName(theApp.m_hInstance, szModule, MAX_PATH);
+    TCHAR * slash = _tcsrchr(szModule, '\\');
+    if (slash) {
+        *slash = 0;
+        switch (pThis->GetOverlayObject())
+        {
+            case 0:
+                _tcscat(szModule, _T("\\link.ico"));
+            break;
+            case 1:
+                _tcscat(szModule, _T("\\mount.ico"));
+            break;
+        }
+    }
+#ifndef UNICODE
+    MultiByteToWideChar( CP_ACP,0,szModule,-1,pwszIconFile,cchMax);
+#else
+    _tcsncpy(pwszIconFile, szModule, cchMax);
+#endif
+    *pIndex = 0;
+    *pdwFlags = ISIOI_ICONFILE;
+    return S_OK;
 }
 
 STDMETHODIMP CShellExt::XIconExt::GetPriority(int* pPriority)
 {
-       if(IsBadWritePtr(pPriority, sizeof(int)))
-          return E_INVALIDARG;
-       *pPriority = 0;
-       return S_OK;
+    if(IsBadWritePtr(pPriority, sizeof(int)))
+       return E_INVALIDARG;
+    *pPriority = 0;
+    return S_OK;
 }
 
 STDMETHODIMP CShellExt::XIconExt::IsMemberOf(LPCWSTR pwszPath,DWORD dwAttrib)
 {
-       TCHAR szPath[MAX_PATH];
-       WideCharToMultiByte( CP_ACP,0,pwszPath,-1,szPath,MAX_PATH,NULL,NULL);
-       if (IsSymlink(szPath))
-               return S_OK;
-       return S_FALSE;
+    METHOD_PROLOGUE(CShellExt, IconExt);
+    TCHAR szPath[MAX_PATH];
+#ifdef UNICODE
+    _tcscpy(szPath, pwszPath);
+#else
+    WideCharToMultiByte( CP_ACP,0,pwszPath,-1,szPath,MAX_PATH,NULL,NULL);
+#endif
+       if (!IsPathInAfs(szPath))
+               return S_FALSE;
+
+    if ((pThis->GetOverlayObject() == 0)&&(IsSymlink(szPath))) {
+        return S_OK;
+    }
+    if ((pThis->GetOverlayObject() == 1)&&(IsMountPoint(szPath))) {
+        return S_OK;
+    }
+    return S_FALSE;
 }
 
-/*          TOOL TIP INFO IMPLIMENTION   */
+/*  TOOL TIP INFO IMPLIMENTION   */
 
 STDMETHODIMP CShellExt::XToolTipExt::QueryInterface(REFIID riid, void** ppv)
 {
@@ -578,39 +788,50 @@ STDMETHODIMP CShellExt::XToolTipExt::QueryInterface(REFIID riid, void** ppv)
 
 STDMETHODIMP_(ULONG) CShellExt::XToolTipExt::AddRef(void)
 {
-       return ++nTPRefCount;
+    return ++nTPRefCount;
 }
 
 STDMETHODIMP_(ULONG) CShellExt::XToolTipExt::Release(void)
 {
-       if (nTPRefCount> 0)
-               nTPRefCount--;
+    if (nTPRefCount> 0)
+       nTPRefCount--;
 
-       return nTPRefCount;
+    return nTPRefCount;
 }
 
 STDMETHODIMP CShellExt::XToolTipExt::GetInfoTip(DWORD dwFlags, LPWSTR *ppwszTip)
 {
     METHOD_PROLOGUE(CShellExt, ToolTipExt);
 
-       if (!IsSymlink(pThis->m_szFile))
-       {
-               ppwszTip=NULL;
-               return S_OK;
-       }
-       USES_CONVERSION;
-       // dwFlags is currently unused.
-       *ppwszTip = (WCHAR*) (pThis->m_pAlloc)->Alloc((1+lstrlen(pThis->m_szFile))*sizeof(WCHAR));
-       if (*ppwszTip)
-       {
-               wcscpy(*ppwszTip, (WCHAR*)T2OLE(pThis->m_szFile));
-       }
-
+    if ((_tcslen(pThis->m_szFile) == 0)||(!IsPathInAfs(pThis->m_szFile)))
+    {
+        *ppwszTip=NULL;
+        return S_OK;
+    }
+    bool bIsSymlink = !!IsSymlink(pThis->m_szFile);
+    bool bIsMountpoint = !!IsMountPoint(pThis->m_szFile);
+    if ((!bIsSymlink) && (!bIsMountpoint))
+    {
+       *ppwszTip=NULL;
        return S_OK;
+    }
+    USES_CONVERSION;
+    // dwFlags is currently unused.
+    CString sInfo;
+    if (bIsSymlink)
+        sInfo = GetSymlink(pThis->m_szFile);
+    else if (bIsMountpoint)
+        sInfo = GetMountpoint(pThis->m_szFile);
+    *ppwszTip = (WCHAR*) (pThis->m_pAlloc)->Alloc((1+sInfo.GetLength())*sizeof(WCHAR));
+    if (*ppwszTip)
+        wcscpy(*ppwszTip, (WCHAR*)T2COLE((LPCTSTR)sInfo));
+
+    return S_OK;
 }
 STDMETHODIMP CShellExt::XToolTipExt::GetInfoFlags(LPDWORD pdwFlags)
 {
-       return S_OK;
+    *pdwFlags = 0;
+    return S_OK;
 }
 
 //////////                          IPersistFile
@@ -624,46 +845,227 @@ STDMETHODIMP CShellExt::XPersistFileExt::QueryInterface(REFIID riid, void** ppv)
 
 STDMETHODIMP_(ULONG) CShellExt::XPersistFileExt::AddRef(void)
 {
-       return ++nXPRefCount;
+    return ++nXPRefCount;
 }
 
 STDMETHODIMP_(ULONG) CShellExt::XPersistFileExt::Release(void)
 {
-       if (nXPRefCount> 0)
-               nXPRefCount--;
+    if (nXPRefCount> 0)
+       nXPRefCount--;
 
-       return nXPRefCount;
+    return nXPRefCount;
 }
 
 STDMETHODIMP   CShellExt::XPersistFileExt::Load(LPCOLESTR wszFile, DWORD dwMode)
 {
     METHOD_PROLOGUE(CShellExt, PersistFileExt);
-       USES_CONVERSION;
-       _tcscpy(pThis->m_szFile, OLE2T((WCHAR*)wszFile)); 
-       return S_OK;    
+    USES_CONVERSION;
+    _tcscpy(pThis->m_szFile, OLE2T((WCHAR*)wszFile));
+    return S_OK;
 }
 
 STDMETHODIMP CShellExt::XPersistFileExt::GetClassID(LPCLSID)
-{ 
-       return E_NOTIMPL;       
+{
+    return E_NOTIMPL;
 }
 
 STDMETHODIMP CShellExt::XPersistFileExt::IsDirty(VOID)
-{ 
-       return E_NOTIMPL; 
+{
+    return E_NOTIMPL;
 }
 
 STDMETHODIMP CShellExt::XPersistFileExt::Save(LPCOLESTR, BOOL)
-{ 
-       return E_NOTIMPL; 
+{
+    return E_NOTIMPL;
 }
 
 STDMETHODIMP CShellExt::XPersistFileExt::SaveCompleted(LPCOLESTR)
-{ 
-       return E_NOTIMPL; 
+{
+    return E_NOTIMPL;
 }
 
 STDMETHODIMP CShellExt::XPersistFileExt::GetCurFile(LPOLESTR FAR*)
-{ 
-       return E_NOTIMPL; 
+{
+    return E_NOTIMPL;
+}
+
+// IShellPropSheetExt
+STDMETHODIMP CShellExt::XPropertySheetExt::QueryInterface(REFIID riid, void** ppv)
+{
+    METHOD_PROLOGUE(CShellExt, PropertySheetExt);
+    return pThis->ExternalQueryInterface(&riid, ppv);
+}
+
+STDMETHODIMP_(ULONG) CShellExt::XPropertySheetExt::AddRef(void)
+{
+    return ++nPSRefCount;
+}
+
+STDMETHODIMP_(ULONG) CShellExt::XPropertySheetExt::Release(void)
+{
+    if (nPSRefCount> 0)
+       nPSRefCount--;
+
+    return nPSRefCount;
+}
+
+BOOL CALLBACK PageProc (HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
+{
+    PropertyPage * sheetpage;
+
+    if (uMessage == WM_INITDIALOG)
+    {
+        sheetpage = (PropertyPage*) ((LPPROPSHEETPAGE) lParam)->lParam;
+        SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) sheetpage);
+        sheetpage->SetHwnd(hwnd);
+    }
+    else
+    {
+        sheetpage = (PropertyPage*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
+    }
+
+    if (sheetpage != 0L)
+        return sheetpage->PropPageProc(hwnd, uMessage, wParam, lParam);
+    else
+        return FALSE;
+}
+
+UINT CALLBACK PropPageCallbackProc ( HWND /*hwnd*/, UINT uMsg, LPPROPSHEETPAGE ppsp )
+{
+    // Delete the page before closing.
+    if (PSPCB_CREATE == uMsg)
+        return TRUE;
+    if (PSPCB_RELEASE == uMsg)
+    {
+        PropertyPage* sheetpage = (PropertyPage*) ppsp->lParam;
+        delete sheetpage;
+    }
+    return TRUE;
+}
+
+STDMETHODIMP CShellExt::XPropertySheetExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
+{
+    METHOD_PROLOGUE(CShellExt, PropertySheetExt);
+
+    if(pThis->m_bIsPathInAFS) {
+        // add the property page for files/folder/mount points/symlinks
+        PROPSHEETPAGE psp;
+        SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
+        HPROPSHEETPAGE hPage;
+        CPropFile *sheetpage = NULL;
+
+        sheetpage = new CPropFile(pThis->m_astrFileNames);
+
+        if (sheetpage == NULL)
+            return E_OUTOFMEMORY;
+
+        HINSTANCE hInst = 0;
+        TaLocale_GetResource(RT_DIALOG, MAKEINTRESOURCE(IDD_PROPPAGE_FILE), LANG_USER_DEFAULT, &hInst);
+        sheetpage->m_hInst = hInst;
+        sheetpage->m_bIsMountpoint = pThis->m_bIsMountpoint;
+        sheetpage->m_bIsSymlink = pThis->m_bIsSymlink;
+        sheetpage->m_bIsDir=pThis->m_bDirSelected;
+        psp.dwSize = sizeof (psp);
+        psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USECALLBACK | PSP_USETITLE;
+        psp.hInstance = hInst;
+        psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE_FILE);
+        psp.pszIcon = NULL;
+        psp.pszTitle = _T("AFS");
+        psp.pfnDlgProc = (DLGPROC) PageProc;
+        psp.lParam = (LPARAM) sheetpage;
+        psp.pfnCallback = PropPageCallbackProc;
+        psp.pcRefParent = (UINT*) &nPSRefCount;
+
+        hPage = CreatePropertySheetPage (&psp);
+
+        if (hPage != NULL) {
+            if (!lpfnAddPage (hPage, lParam)) {
+                delete sheetpage;
+                DestroyPropertySheetPage (hPage);
+            }
+        }
+    }
+
+    // add the property page for Volume Data
+    PROPSHEETPAGE psp;
+    SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
+    HPROPSHEETPAGE hPage;
+    CPropVolume *sheetpage = NULL;
+
+    sheetpage = new CPropVolume(pThis->m_astrFileNames);
+
+    if (sheetpage == NULL)
+        return E_OUTOFMEMORY;
+
+    HINSTANCE hInst = 0;
+    TaLocale_GetResource(RT_DIALOG, MAKEINTRESOURCE(IDD_PROPPAGE_VOLUME), LANG_USER_DEFAULT, &hInst);
+    sheetpage->m_hInst = hInst;
+    sheetpage->m_bIsMountpoint = pThis->m_bIsMountpoint;
+    sheetpage->m_bIsSymlink = pThis->m_bIsSymlink;
+    sheetpage->m_bIsDir=pThis->m_bDirSelected;
+    psp.dwSize = sizeof (psp);
+    psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USECALLBACK | PSP_USETITLE;
+    psp.hInstance = hInst;
+    psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE_VOLUME);
+    psp.pszIcon = NULL;
+    psp.pszTitle = _T("AFS Volume");
+    psp.pfnDlgProc = (DLGPROC) PageProc;
+    psp.lParam = (LPARAM) sheetpage;
+    psp.pfnCallback = PropPageCallbackProc;
+    psp.pcRefParent = (UINT*) &nPSRefCount;
+
+    hPage = CreatePropertySheetPage (&psp);
+
+    if (hPage != NULL) {
+        if (!lpfnAddPage (hPage, lParam)) {
+            delete sheetpage;
+            DestroyPropertySheetPage (hPage);
+        }
+    }
+
+    if(pThis->m_bDirSelected) {
+        // add the property page for ACLs
+        PROPSHEETPAGE psp;
+        SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
+        HPROPSHEETPAGE hPage;
+        CPropACL *sheetpage = NULL;
+
+        sheetpage = new CPropACL(pThis->m_astrFileNames);
+
+        if (sheetpage == NULL)
+            return E_OUTOFMEMORY;
+
+        HINSTANCE hInst = 0;
+        TaLocale_GetResource(RT_DIALOG, MAKEINTRESOURCE(IDD_PROPPAGE_ACL), LANG_USER_DEFAULT, &hInst);
+        sheetpage->m_hInst = hInst;
+        sheetpage->m_bIsMountpoint = pThis->m_bIsMountpoint;
+        sheetpage->m_bIsSymlink = pThis->m_bIsSymlink;
+        sheetpage->m_bIsDir=pThis->m_bDirSelected;
+        psp.dwSize = sizeof (psp);
+        psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USECALLBACK | PSP_USETITLE;
+        psp.hInstance = hInst;
+        psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE_ACL);
+        psp.pszIcon = NULL;
+        psp.pszTitle = _T("AFS ACL");
+        psp.pfnDlgProc = (DLGPROC) PageProc;
+        psp.lParam = (LPARAM) sheetpage;
+        psp.pfnCallback = PropPageCallbackProc;
+        psp.pcRefParent = (UINT*) &nPSRefCount;
+
+        hPage = CreatePropertySheetPage (&psp);
+
+        if (hPage != NULL) {
+            if (!lpfnAddPage (hPage, lParam)) {
+                delete sheetpage;
+                DestroyPropertySheetPage (hPage);
+            }
+        }
+    }
+
+    return S_OK;
+}
+
+STDMETHODIMP CShellExt::XPropertySheetExt::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam)
+{
+    return E_FAIL;
 }