2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
11 #include <afs/param.h>
17 #include <sys/types.h>
19 #include "afs_shl_ext.h"
20 #include "shell_ext.h"
21 #include "volume_info.h"
22 #include "set_afs_acl.h"
24 #include "make_mount_point_dlg.h"
26 #include "server_status_dlg.h"
28 #include "submounts_dlg.h"
34 static char THIS_FILE[] = __FILE__;
38 ULONG nCMRefCount = 0; // IContextMenu ref count
39 ULONG nSERefCount = 0; // IShellExtInit ref count
44 #define MAXSIZE 2048 /* most I'll get back from PIOCTL */
45 #define PCCHAR(str) ((char *)(const char *)str)
46 static char space[MAXSIZE];
48 static BOOL IsADir(const CString& strName)
52 if (_stat(strName, &statbuf) < 0)
55 return statbuf.st_mode & _S_IFDIR;
58 /////////////////////////////////////////////////////////////////////////////
61 IMPLEMENT_DYNCREATE(CShellExt, CCmdTarget)
63 CShellExt::CShellExt()
68 hr = SHGetMalloc(&m_pAlloc);
73 CShellExt::~CShellExt()
75 if(m_pAlloc) m_pAlloc->Release();
80 void CShellExt::OnFinalRelease()
82 // When the last reference for an automation object is released
83 // OnFinalRelease is called. The base class will automatically
84 // deletes the object. Add additional cleanup required for your
85 // object before calling the base class.
87 CCmdTarget::OnFinalRelease();
91 BEGIN_MESSAGE_MAP(CShellExt, CCmdTarget)
92 //{{AFX_MSG_MAP(CShellExt)
93 // NOTE - the ClassWizard will add and remove mapping macros here.
97 BEGIN_DISPATCH_MAP(CShellExt, CCmdTarget)
98 //{{AFX_DISPATCH_MAP(CShellExt)
99 // NOTE - the ClassWizard will add and remove mapping macros here.
103 // Note: we add support for IID_IShellExt to support typesafe binding
104 // from VBA. This IID must match the GUID that is attached to the
105 // dispinterface in the .ODL file.
107 // {DC515C27-6CAC-11D1-BAE7-00C04FD140D2}
108 static const IID IID_IShellExt =
109 { 0xdc515c27, 0x6cac, 0x11d1, { 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd2 } };
111 BEGIN_INTERFACE_MAP(CShellExt, CCmdTarget)
112 INTERFACE_PART(CShellExt, IID_IShellExt, Dispatch)
113 INTERFACE_PART(CShellExt, IID_IContextMenu, MenuExt)
114 INTERFACE_PART(CShellExt, IID_IShellExtInit, ShellInit)
115 INTERFACE_PART(CShellExt, IID_IShellIconOverlayIdentifier, IconExt)
116 INTERFACE_PART(CShellExt, IID_IQueryInfo , ToolTipExt)
117 INTERFACE_PART(CShellExt, IID_IPersistFile , PersistFileExt)
120 IMPLEMENT_OLECREATE(CShellExt, STR_EXT_TITLE, 0xdc515c27, 0x6cac, 0x11d1, 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd2)
123 /////////////////////////////////////////////////////////////////////////////
124 // CShellExt message handlers
125 /////////////////////////////////////////////////////////////////////////////
128 /////////////////////////////////////////////////////////////////////////////
129 // IUnknown for IContextMenu
130 /////////////////////////////////////////////////////////////////////////////
131 STDMETHODIMP CShellExt::XMenuExt::QueryInterface(REFIID riid, void** ppv)
133 METHOD_PROLOGUE(CShellExt, MenuExt);
135 return pThis->ExternalQueryInterface(&riid, ppv);
138 STDMETHODIMP_(ULONG) CShellExt::XMenuExt::AddRef(void)
140 return ++nCMRefCount;
143 STDMETHODIMP_(ULONG) CShellExt::XMenuExt::Release(void)
151 /////////////////////////////////////////////////////////////////////////////
152 // IConextMenu Functions
153 /////////////////////////////////////////////////////////////////////////////
154 STDMETHODIMP CShellExt::XMenuExt::QueryContextMenu(HMENU hMenu,UINT indexMenu,
155 UINT idCmdFirst, UINT idCmdLast,UINT uFlags)
157 METHOD_PROLOGUE(CShellExt, MenuExt);
159 // Don't add any menu items if we're being asked to deal with this file as a shortcut.
160 if (uFlags & CMF_VERBSONLY)
161 return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, (USHORT)0));
163 // Check to see if there's already an AFS menu here; if so, remove it
164 int nItemsNow = GetMenuItemCount (hMenu);
165 CString strAfsItemText = GetMessageString(IDS_AFS_ITEM);
166 LPCTSTR pszAfsItemText = (LPCTSTR)strAfsItemText;
167 for (int iItem = 0; iItem < nItemsNow; iItem++) {
168 TCHAR szItemText[256];
169 if (!GetMenuString (hMenu, iItem, szItemText, 256, MF_BYPOSITION))
171 if (!lstrcmp (szItemText, pszAfsItemText)) {
172 DeleteMenu (hMenu, iItem, MF_BYPOSITION);
175 if ((!lstrcmp(szItemText,"&Delete"))&&(pThis->m_bIsSymlink)) { /*this is a symlink - don't present a delete menu!*/
176 DeleteMenu (hMenu, iItem, MF_BYPOSITION);
179 if ((!lstrcmp(szItemText,"Cu&t"))&&(pThis->m_bIsSymlink)) { /*same for cut*/
180 DeleteMenu (hMenu, iItem, MF_BYPOSITION);
184 int indexShellMenu = 0;
186 // Create the AFS submenu using the allowed ID's.
187 HMENU hAfsMenu = CreatePopupMenu();
188 int indexAfsMenu = 0;
190 // The Authentication item has been removed from the AFS menu because
191 // there is now a tray icon to handle authentication.
193 //::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_AUTHENTICATION, GetMessageString(IDS_AUTHENTICATION_ITEM));
195 // Only enable the ACL menu item if a single directory is selected
196 int nSingleDirOnly = MF_GRAYED;
197 if (pThis->m_bDirSelected && (pThis->m_astrFileNames.GetSize() == 1))
198 nSingleDirOnly = MF_ENABLED;
199 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_ACL_SET, GetMessageString(IDS_ACLS_ITEM));
201 // Volume/Partition submenu of the AFS submenu
202 HMENU hVolPartMenu = CreatePopupMenu();
203 int indexVolPartMenu = 0;
204 ::InsertMenu(hVolPartMenu, indexVolPartMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_VOLUME_PROPERTIES, GetMessageString(IDS_VOL_PART_PROPS_ITEM));
205 ::InsertMenu(hVolPartMenu, indexVolPartMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE, GetMessageString(IDS_VOL_PART_REFRESH_ITEM));
206 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hVolPartMenu, GetMessageString(IDS_VOL_PART_ITEM));
208 // Mount Point submenu of the AFS submenu
209 HMENU hMountPointMenu = CreatePopupMenu();
210 int indexMountPointMenu = 0;
211 ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_SHOW, GetMessageString(IDS_MP_SHOW_ITEM));
212 ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_REMOVE, GetMessageString(IDS_MP_REMOVE_ITEM));
213 ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_MAKE, GetMessageString(IDS_MP_MAKE_ITEM));
214 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hMountPointMenu, GetMessageString(IDS_MOUNT_POINT_ITEM));
216 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_FLUSH, GetMessageString(IDS_FLUSH_FILE_DIR_ITEM));
217 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_FLUSH_VOLUME, GetMessageString(IDS_FLUSH_VOLUME_ITEM));
218 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHOW_SERVER, GetMessageString(IDS_SHOW_FILE_SERVERS_ITEM));
219 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHOWCELL, GetMessageString(IDS_SHOW_CELL_ITEM));
220 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SERVER_STATUS, GetMessageString(IDS_SHOW_SERVER_STATUS_ITEM));
221 if (pThis->m_bIsSymlink)
222 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_REMOVE_SYMLINK, GetMessageString(IDS_REMOVE_SYMLINK_ITEM));
224 // The Submounts menu has been removed because the AFS tray icon
225 // and control panel now support mapping drives directly to an AFS
228 // HMENU hSubmountMenu = CreatePopupMenu();
229 // int indexSubmountMenu = 0;
230 // ::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_SUBMOUNTS_CREATE, GetMessageString(IDS_SUBMOUNTS_CREATE_ITEM));
231 // ::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SUBMOUNTS_EDIT, GetMessageString(IDS_SUBMOUNTS_EDIT_ITEM));
232 // ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hSubmountMenu, GetMessageString(IDS_SUBMOUNTS_ITEM));
235 ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
237 // Add the AFS submenu to the shell's menu
238 ::InsertMenu(hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hAfsMenu, GetMessageString(IDS_AFS_ITEM));
240 // Add a separator after us
241 ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
243 return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL,
244 (USHORT)indexAfsMenu + indexVolPartMenu + indexMountPointMenu + indexShellMenu));
247 STDMETHODIMP CShellExt::XMenuExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
249 METHOD_PROLOGUE(CShellExt, MenuExt);
251 if (HIWORD(lpici->lpVerb ))
256 CStringArray &files = pThis->m_astrFileNames;
258 switch (LOWORD(lpici->lpVerb))
260 case IDM_AUTHENTICATION: {
268 ASSERT(files.GetSize() == 1);
269 dlg.SetDir(files[0]);
274 case IDM_VOLUME_PROPERTIES: {
281 case IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE: CheckVolumes();
284 case IDM_MOUNTPOINT_SHOW: ListMount(files);
287 case IDM_MOUNTPOINT_REMOVE: {
288 int nChoice = ShowMessageBox(IDS_REALLY_DEL_MOUNT_POINTS, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_DEL_MOUNT_POINTS);
289 if (nChoice == IDYES)
294 case IDM_MOUNTPOINT_MAKE: {
295 CMakeMountPointDlg dlg;
296 ASSERT(files.GetSize() == 1);
297 dlg.SetDir(files[0]);
303 case IDM_FLUSH: Flush(files);
306 case IDM_FLUSH_VOLUME: FlushVolume(files);
309 case IDM_SHOW_SERVER: WhereIs(files);
312 case IDM_SHOWCELL: WhichCell(files);
315 case IDM_SERVER_STATUS: {
316 CServerStatusDlg dlg;
321 case IDM_SUBMOUNTS_EDIT: {
327 case IDM_SUBMOUNTS_CREATE: {
328 ASSERT(files.GetSize() == 1);
330 dlg.SetAddOnlyMode(files[0]);
334 case IDM_REMOVE_SYMLINK: {
335 if (files.GetSize()>1)
337 int nChoice = ShowMessageBox(IDS_REALLY_REMOVE_SYMLINK, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_REMOVE_SYMLINK);
338 if (nChoice == IDYES)
339 RemoveSymlink(files.GetAt(0));
353 STDMETHODIMP CShellExt::XMenuExt::GetCommandString(UINT idCmd, UINT uType,
354 UINT* pwReserved, LPSTR pszName, UINT cchMax)
356 if (uType != GCS_HELPTEXT)
357 return NOERROR; // ?????????????????????????????????????????????????
361 AfxSetResourceHandle(theApp.m_hInstance);
365 case IDM_AUTHENTICATION: nCmdStrID = ID_AUTHENTICATE;
368 case IDM_ACL_SET: nCmdStrID = ID_ACL_SET;
371 case IDM_VOLUME_PROPERTIES: nCmdStrID = ID_VOLUME_PROPERTIES;
374 case IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE: nCmdStrID = ID_VOLUMEPARTITION_UPDATENAMEIDTABLE;
377 case IDM_MOUNTPOINT_SHOW: nCmdStrID = ID_MOUNTPOINT_SHOW;
380 case IDM_MOUNTPOINT_REMOVE: nCmdStrID = ID_MOUNTPOINT_REMOVE;
383 case IDM_MOUNTPOINT_MAKE: nCmdStrID = ID_MOUNTPOINT_MAKE;
386 case IDM_FLUSH: nCmdStrID = ID_FLUSH;
389 case IDM_FLUSH_VOLUME: nCmdStrID = ID_VOLUME_FLUSH;
392 case IDM_SHOW_SERVER: nCmdStrID = ID_WHEREIS;
395 case IDM_SHOWCELL: nCmdStrID = ID_SHOWCELL;
398 case IDM_SERVER_STATUS: nCmdStrID = ID_SERVER_STATUS;
401 case IDM_SUBMOUNTS_CREATE: nCmdStrID = ID_SUBMOUNTS_CREATE;
404 case IDM_SUBMOUNTS_EDIT: nCmdStrID = ID_SUBMOUNTS_EDIT;
407 case IDM_REMOVE_SYMLINK: nCmdStrID= ID_REMOVE_SYMLINK;
417 LoadString (strMsg, nCmdStrID);
419 strncpy(pszName, strMsg, cchMax);
424 /////////////////////////////////////////////////////////////////////////////
425 // IUnknown for IShellExtInit
426 /////////////////////////////////////////////////////////////////////////////
427 STDMETHODIMP CShellExt::XShellInit::QueryInterface(REFIID riid, void** ppv)
429 METHOD_PROLOGUE(CShellExt, ShellInit);
431 return pThis->ExternalQueryInterface(&riid, ppv);
434 STDMETHODIMP_(ULONG) CShellExt::XShellInit::AddRef(void)
436 return ++nSERefCount;
439 STDMETHODIMP_(ULONG) CShellExt::XShellInit::Release(void)
447 /////////////////////////////////////////////////////////////////////////////
448 // IShellInit Functions
449 /////////////////////////////////////////////////////////////////////////////
450 STDMETHODIMP CShellExt::XShellInit::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
452 METHOD_PROLOGUE(CShellExt, ShellInit);
454 HRESULT hres = E_FAIL;
455 FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
458 // We must have a data object
462 // Use the given IDataObject to get a list of filenames (CF_HDROP)
463 hres = pdobj->GetData(&fmte, &medium);
467 int nNumFiles = DragQueryFile((HDROP)medium.hGlobal, 0xFFFFFFFF, NULL, 0);
471 pThis->m_bDirSelected = FALSE;
473 for (int ii = 0; ii < nNumFiles; ii++) {
476 // Get the size of the file name string
477 int nNameLen = DragQueryFile((HDROP)medium.hGlobal, ii, 0, 0);
479 // Make room for it in our string object
480 LPTSTR pszFileNameBuf = strFileName.GetBuffer(nNameLen + 1); // +1 for the terminating NULL
481 ASSERT(pszFileNameBuf);
484 DragQueryFile((HDROP)medium.hGlobal, ii, pszFileNameBuf, nNameLen + 1);
486 strFileName.ReleaseBuffer();
488 if (!IsPathInAfs(strFileName)) {
489 pThis->m_astrFileNames.RemoveAll();
492 pThis->m_bIsSymlink=IsSymlink(strFileName);
495 if (IsADir(strFileName))
496 pThis->m_bDirSelected = TRUE;
498 pThis->m_astrFileNames.Add(strFileName);
501 if (pThis->m_astrFileNames.GetSize() > 0)
508 ReleaseStgMedium(&medium);
513 STDMETHODIMP CShellExt::XIconExt::QueryInterface(REFIID riid, void** ppv)
515 METHOD_PROLOGUE(CShellExt, IconExt);
516 return pThis->ExternalQueryInterface(&riid, ppv);
519 STDMETHODIMP_(ULONG) CShellExt::XIconExt::AddRef(void)
521 return ++nICRefCount;
524 STDMETHODIMP_(ULONG) CShellExt::XIconExt::Release(void)
533 /////////////////////////////////////////////////////////////////////////////
534 // IIconHandler Functions
535 /////////////////////////////////////////////////////////////////////////////
537 STDMETHODIMP CShellExt::XIconExt::GetOverlayInfo(LPWSTR pwszIconFile
538 ,int cchMax,int* pIndex,DWORD* pdwFlags)
540 if(IsBadWritePtr(pIndex, sizeof(int)))
542 if(IsBadWritePtr(pdwFlags, sizeof(DWORD)))
545 HMODULE hModule=GetModuleHandle("shell32.dll");
546 TCHAR szModule[MAX_PATH];
547 DWORD z=GetModuleFileName(hModule,szModule,sizeof(szModule));
548 MultiByteToWideChar( CP_ACP,0,szModule,-1,pwszIconFile,cchMax);
550 *pdwFlags = ISIOI_ICONFILE | ISIOI_ICONINDEX;
554 STDMETHODIMP CShellExt::XIconExt::GetPriority(int* pPriority)
556 if(IsBadWritePtr(pPriority, sizeof(int)))
562 STDMETHODIMP CShellExt::XIconExt::IsMemberOf(LPCWSTR pwszPath,DWORD dwAttrib)
564 TCHAR szPath[MAX_PATH];
565 WideCharToMultiByte( CP_ACP,0,pwszPath,-1,szPath,MAX_PATH,NULL,NULL);
566 if (IsSymlink(szPath))
571 /* TOOL TIP INFO IMPLIMENTION */
573 STDMETHODIMP CShellExt::XToolTipExt::QueryInterface(REFIID riid, void** ppv)
575 METHOD_PROLOGUE(CShellExt, ToolTipExt);
576 return pThis->ExternalQueryInterface(&riid, ppv);
579 STDMETHODIMP_(ULONG) CShellExt::XToolTipExt::AddRef(void)
581 return ++nTPRefCount;
584 STDMETHODIMP_(ULONG) CShellExt::XToolTipExt::Release(void)
592 STDMETHODIMP CShellExt::XToolTipExt::GetInfoTip(DWORD dwFlags, LPWSTR *ppwszTip)
594 METHOD_PROLOGUE(CShellExt, ToolTipExt);
596 if (!IsSymlink(pThis->m_szFile))
602 // dwFlags is currently unused.
603 *ppwszTip = (WCHAR*) (pThis->m_pAlloc)->Alloc((1+lstrlen(pThis->m_szFile))*sizeof(WCHAR));
606 wcscpy(*ppwszTip, (WCHAR*)T2OLE(pThis->m_szFile));
611 STDMETHODIMP CShellExt::XToolTipExt::GetInfoFlags(LPDWORD pdwFlags)
616 ////////// IPersistFile
617 /////// PersistFileExt
619 STDMETHODIMP CShellExt::XPersistFileExt::QueryInterface(REFIID riid, void** ppv)
621 METHOD_PROLOGUE(CShellExt, PersistFileExt);
622 return pThis->ExternalQueryInterface(&riid, ppv);
625 STDMETHODIMP_(ULONG) CShellExt::XPersistFileExt::AddRef(void)
627 return ++nXPRefCount;
630 STDMETHODIMP_(ULONG) CShellExt::XPersistFileExt::Release(void)
638 STDMETHODIMP CShellExt::XPersistFileExt::Load(LPCOLESTR wszFile, DWORD dwMode)
640 METHOD_PROLOGUE(CShellExt, PersistFileExt);
642 _tcscpy(pThis->m_szFile, OLE2T((WCHAR*)wszFile));
646 STDMETHODIMP CShellExt::XPersistFileExt::GetClassID(LPCLSID)
651 STDMETHODIMP CShellExt::XPersistFileExt::IsDirty(VOID)
656 STDMETHODIMP CShellExt::XPersistFileExt::Save(LPCOLESTR, BOOL)
661 STDMETHODIMP CShellExt::XPersistFileExt::SaveCompleted(LPCOLESTR)
666 STDMETHODIMP CShellExt::XPersistFileExt::GetCurFile(LPOLESTR FAR*)