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>
16 #include <sys/types.h>
18 #include "afs_shl_ext.h"
19 #include "shell_ext.h"
20 #include "volume_info.h"
21 #include "set_afs_acl.h"
23 #include "make_mount_point_dlg.h"
25 #include "server_status_dlg.h"
27 #include "submounts_dlg.h"
33 static char THIS_FILE[] = __FILE__;
37 ULONG nCMRefCount = 0; // IContextMenu ref count
38 ULONG nSERefCount = 0; // IShellExtInit ref count
43 #define MAXSIZE 2048 /* most I'll get back from PIOCTL */
44 #define PCCHAR(str) ((char *)(const char *)str)
45 static char space[MAXSIZE];
47 static BOOL IsADir(const CString& strName)
51 if (_stat(strName, &statbuf) < 0)
54 return statbuf.st_mode & _S_IFDIR;
57 /////////////////////////////////////////////////////////////////////////////
60 IMPLEMENT_DYNCREATE(CShellExt, CCmdTarget)
62 CShellExt::CShellExt()
67 hr = SHGetMalloc(&m_pAlloc);
72 CShellExt::~CShellExt()
74 if(m_pAlloc) m_pAlloc->Release();
79 void CShellExt::OnFinalRelease()
81 // When the last reference for an automation object is released
82 // OnFinalRelease is called. The base class will automatically
83 // deletes the object. Add additional cleanup required for your
84 // object before calling the base class.
86 CCmdTarget::OnFinalRelease();
90 BEGIN_MESSAGE_MAP(CShellExt, CCmdTarget)
91 //{{AFX_MSG_MAP(CShellExt)
92 // NOTE - the ClassWizard will add and remove mapping macros here.
96 BEGIN_DISPATCH_MAP(CShellExt, CCmdTarget)
97 //{{AFX_DISPATCH_MAP(CShellExt)
98 // NOTE - the ClassWizard will add and remove mapping macros here.
102 // Note: we add support for IID_IShellExt to support typesafe binding
103 // from VBA. This IID must match the GUID that is attached to the
104 // dispinterface in the .ODL file.
106 // {DC515C27-6CAC-11D1-BAE7-00C04FD140D2}
107 static const IID IID_IShellExt =
108 { 0xdc515c27, 0x6cac, 0x11d1, { 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd2 } };
110 BEGIN_INTERFACE_MAP(CShellExt, CCmdTarget)
111 INTERFACE_PART(CShellExt, IID_IShellExt, Dispatch)
112 INTERFACE_PART(CShellExt, IID_IContextMenu, MenuExt)
113 INTERFACE_PART(CShellExt, IID_IShellExtInit, ShellInit)
114 INTERFACE_PART(CShellExt, IID_IShellIconOverlayIdentifier, IconExt)
115 INTERFACE_PART(CShellExt, IID_IQueryInfo , ToolTipExt)
116 INTERFACE_PART(CShellExt, IID_IPersistFile , PersistFileExt)
119 IMPLEMENT_OLECREATE(CShellExt, STR_EXT_TITLE, 0xdc515c27, 0x6cac, 0x11d1, 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd2)
122 /////////////////////////////////////////////////////////////////////////////
123 // CShellExt message handlers
124 /////////////////////////////////////////////////////////////////////////////
127 /////////////////////////////////////////////////////////////////////////////
128 // IUnknown for IContextMenu
129 /////////////////////////////////////////////////////////////////////////////
130 STDMETHODIMP CShellExt::XMenuExt::QueryInterface(REFIID riid, void** ppv)
132 METHOD_PROLOGUE(CShellExt, MenuExt);
134 return pThis->ExternalQueryInterface(&riid, ppv);
137 STDMETHODIMP_(ULONG) CShellExt::XMenuExt::AddRef(void)
139 return ++nCMRefCount;
142 STDMETHODIMP_(ULONG) CShellExt::XMenuExt::Release(void)
150 /////////////////////////////////////////////////////////////////////////////
151 // IConextMenu Functions
152 /////////////////////////////////////////////////////////////////////////////
153 STDMETHODIMP CShellExt::XMenuExt::QueryContextMenu(HMENU hMenu,UINT indexMenu,
154 UINT idCmdFirst, UINT idCmdLast,UINT uFlags)
156 METHOD_PROLOGUE(CShellExt, MenuExt);
158 // Don't add any menu items if we're being asked to deal with this file as a shortcut.
159 if (uFlags & CMF_VERBSONLY)
160 return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, (USHORT)0));
162 // Check to see if there's already an AFS menu here; if so, remove it
163 int nItemsNow = GetMenuItemCount (hMenu);
164 CString strAfsItemText = GetMessageString(IDS_AFS_ITEM);
165 LPCTSTR pszAfsItemText = (LPCTSTR)strAfsItemText;
166 for (int iItem = 0; iItem < nItemsNow; iItem++) {
167 TCHAR szItemText[256];
168 if (!GetMenuString (hMenu, iItem, szItemText, 256, MF_BYPOSITION))
170 if (!lstrcmp (szItemText, pszAfsItemText)) {
171 DeleteMenu (hMenu, iItem, MF_BYPOSITION);
174 if ((!lstrcmp(szItemText,"&Delete"))&&(pThis->m_bIsSymlink)) { /*this is a symlink - don't present a delete menu!*/
175 DeleteMenu (hMenu, iItem, MF_BYPOSITION);
178 if ((!lstrcmp(szItemText,"Cu&t"))&&(pThis->m_bIsSymlink)) { /*same for cut*/
179 DeleteMenu (hMenu, iItem, MF_BYPOSITION);
183 int indexShellMenu = 0;
185 // Create the AFS submenu using the allowed ID's.
186 HMENU hAfsMenu = CreatePopupMenu();
187 int indexAfsMenu = 0;
189 // The Authentication item has been removed from the AFS menu because
190 // there is now a tray icon to handle authentication.
192 //::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_AUTHENTICATION, GetMessageString(IDS_AUTHENTICATION_ITEM));
194 // Only enable the ACL menu item if a single directory is selected
195 int nSingleDirOnly = MF_GRAYED;
196 if (pThis->m_bDirSelected && (pThis->m_astrFileNames.GetSize() == 1))
197 nSingleDirOnly = MF_ENABLED;
198 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_ACL_SET, GetMessageString(IDS_ACLS_ITEM));
200 // Volume/Partition submenu of the AFS submenu
201 HMENU hVolPartMenu = CreatePopupMenu();
202 int indexVolPartMenu = 0;
203 ::InsertMenu(hVolPartMenu, indexVolPartMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_VOLUME_PROPERTIES, GetMessageString(IDS_VOL_PART_PROPS_ITEM));
204 ::InsertMenu(hVolPartMenu, indexVolPartMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE, GetMessageString(IDS_VOL_PART_REFRESH_ITEM));
205 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hVolPartMenu, GetMessageString(IDS_VOL_PART_ITEM));
207 // Mount Point submenu of the AFS submenu
208 HMENU hMountPointMenu = CreatePopupMenu();
209 int indexMountPointMenu = 0;
210 ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_SHOW, GetMessageString(IDS_MP_SHOW_ITEM));
211 ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_REMOVE, GetMessageString(IDS_MP_REMOVE_ITEM));
212 ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_MAKE, GetMessageString(IDS_MP_MAKE_ITEM));
213 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hMountPointMenu, GetMessageString(IDS_MOUNT_POINT_ITEM));
215 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_FLUSH, GetMessageString(IDS_FLUSH_FILE_DIR_ITEM));
216 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_FLUSH_VOLUME, GetMessageString(IDS_FLUSH_VOLUME_ITEM));
217 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHOW_SERVER, GetMessageString(IDS_SHOW_FILE_SERVERS_ITEM));
218 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHOWCELL, GetMessageString(IDS_SHOW_CELL_ITEM));
219 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SERVER_STATUS, GetMessageString(IDS_SHOW_SERVER_STATUS_ITEM));
220 if (pThis->m_bIsSymlink)
221 ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_REMOVE_SYMLINK, GetMessageString(IDS_REMOVE_SYMLINK_ITEM));
223 // The Submounts menu has been removed because the AFS tray icon
224 // and control panel now support mapping drives directly to an AFS
227 // HMENU hSubmountMenu = CreatePopupMenu();
228 // int indexSubmountMenu = 0;
229 // ::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_SUBMOUNTS_CREATE, GetMessageString(IDS_SUBMOUNTS_CREATE_ITEM));
230 // ::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SUBMOUNTS_EDIT, GetMessageString(IDS_SUBMOUNTS_EDIT_ITEM));
231 // ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hSubmountMenu, GetMessageString(IDS_SUBMOUNTS_ITEM));
234 ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
236 // Add the AFS submenu to the shell's menu
237 ::InsertMenu(hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hAfsMenu, GetMessageString(IDS_AFS_ITEM));
239 // Add a separator after us
240 ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
242 return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL,
243 (USHORT)indexAfsMenu + indexVolPartMenu + indexMountPointMenu + indexShellMenu));
246 STDMETHODIMP CShellExt::XMenuExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
248 METHOD_PROLOGUE(CShellExt, MenuExt);
250 if (HIWORD(lpici->lpVerb ))
255 CStringArray &files = pThis->m_astrFileNames;
257 switch (LOWORD(lpici->lpVerb))
259 case IDM_AUTHENTICATION: {
267 ASSERT(files.GetSize() == 1);
268 dlg.SetDir(files[0]);
273 case IDM_VOLUME_PROPERTIES: {
280 case IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE: CheckVolumes();
283 case IDM_MOUNTPOINT_SHOW: ListMount(files);
286 case IDM_MOUNTPOINT_REMOVE: {
287 int nChoice = ShowMessageBox(IDS_REALLY_DEL_MOUNT_POINTS, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_DEL_MOUNT_POINTS);
288 if (nChoice == IDYES)
293 case IDM_MOUNTPOINT_MAKE: {
294 CMakeMountPointDlg dlg;
295 ASSERT(files.GetSize() == 1);
296 dlg.SetDir(files[0]);
302 case IDM_FLUSH: Flush(files);
305 case IDM_FLUSH_VOLUME: FlushVolume(files);
308 case IDM_SHOW_SERVER: WhereIs(files);
311 case IDM_SHOWCELL: WhichCell(files);
314 case IDM_SERVER_STATUS: {
315 CServerStatusDlg dlg;
320 case IDM_SUBMOUNTS_EDIT: {
326 case IDM_SUBMOUNTS_CREATE: {
327 ASSERT(files.GetSize() == 1);
329 dlg.SetAddOnlyMode(files[0]);
333 case IDM_REMOVE_SYMLINK: {
334 if (files.GetCount()>1)
336 int nChoice = ShowMessageBox(IDS_REALLY_REMOVE_SYMLINK, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_REMOVE_SYMLINK);
337 if (nChoice == IDYES)
338 RemoveSymlink(files.GetAt(0));
352 STDMETHODIMP CShellExt::XMenuExt::GetCommandString(UINT idCmd, UINT uType,
353 UINT* pwReserved, LPSTR pszName, UINT cchMax)
355 if (uType != GCS_HELPTEXT)
356 return NOERROR; // ?????????????????????????????????????????????????
360 AfxSetResourceHandle(theApp.m_hInstance);
364 case IDM_AUTHENTICATION: nCmdStrID = ID_AUTHENTICATE;
367 case IDM_ACL_SET: nCmdStrID = ID_ACL_SET;
370 case IDM_VOLUME_PROPERTIES: nCmdStrID = ID_VOLUME_PROPERTIES;
373 case IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE: nCmdStrID = ID_VOLUMEPARTITION_UPDATENAMEIDTABLE;
376 case IDM_MOUNTPOINT_SHOW: nCmdStrID = ID_MOUNTPOINT_SHOW;
379 case IDM_MOUNTPOINT_REMOVE: nCmdStrID = ID_MOUNTPOINT_REMOVE;
382 case IDM_MOUNTPOINT_MAKE: nCmdStrID = ID_MOUNTPOINT_MAKE;
385 case IDM_FLUSH: nCmdStrID = ID_FLUSH;
388 case IDM_FLUSH_VOLUME: nCmdStrID = ID_VOLUME_FLUSH;
391 case IDM_SHOW_SERVER: nCmdStrID = ID_WHEREIS;
394 case IDM_SHOWCELL: nCmdStrID = ID_SHOWCELL;
397 case IDM_SERVER_STATUS: nCmdStrID = ID_SERVER_STATUS;
400 case IDM_SUBMOUNTS_CREATE: nCmdStrID = ID_SUBMOUNTS_CREATE;
403 case IDM_SUBMOUNTS_EDIT: nCmdStrID = ID_SUBMOUNTS_EDIT;
406 case IDM_REMOVE_SYMLINK: nCmdStrID= ID_REMOVE_SYMLINK;
416 LoadString (strMsg, nCmdStrID);
418 strncpy(pszName, strMsg, cchMax);
423 /////////////////////////////////////////////////////////////////////////////
424 // IUnknown for IShellExtInit
425 /////////////////////////////////////////////////////////////////////////////
426 STDMETHODIMP CShellExt::XShellInit::QueryInterface(REFIID riid, void** ppv)
428 METHOD_PROLOGUE(CShellExt, ShellInit);
430 return pThis->ExternalQueryInterface(&riid, ppv);
433 STDMETHODIMP_(ULONG) CShellExt::XShellInit::AddRef(void)
435 return ++nSERefCount;
438 STDMETHODIMP_(ULONG) CShellExt::XShellInit::Release(void)
446 /////////////////////////////////////////////////////////////////////////////
447 // IShellInit Functions
448 /////////////////////////////////////////////////////////////////////////////
449 STDMETHODIMP CShellExt::XShellInit::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
451 METHOD_PROLOGUE(CShellExt, ShellInit);
453 HRESULT hres = E_FAIL;
454 FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
457 // We must have a data object
461 // Use the given IDataObject to get a list of filenames (CF_HDROP)
462 hres = pdobj->GetData(&fmte, &medium);
466 int nNumFiles = DragQueryFile((HDROP)medium.hGlobal, 0xFFFFFFFF, NULL, 0);
470 pThis->m_bDirSelected = FALSE;
472 for (int ii = 0; ii < nNumFiles; ii++) {
475 // Get the size of the file name string
476 int nNameLen = DragQueryFile((HDROP)medium.hGlobal, ii, 0, 0);
478 // Make room for it in our string object
479 LPTSTR pszFileNameBuf = strFileName.GetBuffer(nNameLen + 1); // +1 for the terminating NULL
480 ASSERT(pszFileNameBuf);
483 DragQueryFile((HDROP)medium.hGlobal, ii, pszFileNameBuf, nNameLen + 1);
485 strFileName.ReleaseBuffer();
487 if (!IsPathInAfs(strFileName)) {
488 pThis->m_astrFileNames.RemoveAll();
491 pThis->m_bIsSymlink=IsSymlink(strFileName);
494 if (IsADir(strFileName))
495 pThis->m_bDirSelected = TRUE;
497 pThis->m_astrFileNames.Add(strFileName);
500 if (pThis->m_astrFileNames.GetSize() > 0)
507 ReleaseStgMedium(&medium);
512 STDMETHODIMP CShellExt::XIconExt::QueryInterface(REFIID riid, void** ppv)
514 METHOD_PROLOGUE(CShellExt, IconExt);
515 return pThis->ExternalQueryInterface(&riid, ppv);
518 STDMETHODIMP_(ULONG) CShellExt::XIconExt::AddRef(void)
520 return ++nICRefCount;
523 STDMETHODIMP_(ULONG) CShellExt::XIconExt::Release(void)
532 /////////////////////////////////////////////////////////////////////////////
533 // IIconHandler Functions
534 /////////////////////////////////////////////////////////////////////////////
536 STDMETHODIMP CShellExt::XIconExt::GetOverlayInfo(LPWSTR pwszIconFile
537 ,int cchMax,int* pIndex,DWORD* pdwFlags)
539 if(IsBadWritePtr(pIndex, sizeof(int)))
541 if(IsBadWritePtr(pdwFlags, sizeof(DWORD)))
544 HMODULE hModule=GetModuleHandle("shell32.dll");
545 TCHAR szModule[MAX_PATH];
546 DWORD z=GetModuleFileName(hModule,szModule,sizeof(szModule));
547 MultiByteToWideChar( CP_ACP,0,szModule,-1,pwszIconFile,cchMax);
549 *pdwFlags = ISIOI_ICONFILE | ISIOI_ICONINDEX;
553 STDMETHODIMP CShellExt::XIconExt::GetPriority(int* pPriority)
555 if(IsBadWritePtr(pPriority, sizeof(int)))
561 STDMETHODIMP CShellExt::XIconExt::IsMemberOf(LPCWSTR pwszPath,DWORD dwAttrib)
563 TCHAR szPath[MAX_PATH];
564 WideCharToMultiByte( CP_ACP,0,pwszPath,-1,szPath,MAX_PATH,NULL,NULL);
565 if (IsSymlink(szPath))
570 /* TOOL TIP INFO IMPLIMENTION */
572 STDMETHODIMP CShellExt::XToolTipExt::QueryInterface(REFIID riid, void** ppv)
574 METHOD_PROLOGUE(CShellExt, ToolTipExt);
575 return pThis->ExternalQueryInterface(&riid, ppv);
578 STDMETHODIMP_(ULONG) CShellExt::XToolTipExt::AddRef(void)
580 return ++nTPRefCount;
583 STDMETHODIMP_(ULONG) CShellExt::XToolTipExt::Release(void)
591 STDMETHODIMP CShellExt::XToolTipExt::GetInfoTip(DWORD dwFlags, LPWSTR *ppwszTip)
593 METHOD_PROLOGUE(CShellExt, ToolTipExt);
595 if (!IsSymlink(pThis->m_szFile))
601 // dwFlags is currently unused.
602 *ppwszTip = (WCHAR*) (pThis->m_pAlloc)->Alloc((1+lstrlen(pThis->m_szFile))*sizeof(WCHAR));
605 wcscpy(*ppwszTip, (WCHAR*)T2OLE(pThis->m_szFile));
610 STDMETHODIMP CShellExt::XToolTipExt::GetInfoFlags(LPDWORD pdwFlags)
615 ////////// IPersistFile
616 /////// PersistFileExt
618 STDMETHODIMP CShellExt::XPersistFileExt::QueryInterface(REFIID riid, void** ppv)
620 METHOD_PROLOGUE(CShellExt, PersistFileExt);
621 return pThis->ExternalQueryInterface(&riid, ppv);
624 STDMETHODIMP_(ULONG) CShellExt::XPersistFileExt::AddRef(void)
626 return ++nXPRefCount;
629 STDMETHODIMP_(ULONG) CShellExt::XPersistFileExt::Release(void)
637 STDMETHODIMP CShellExt::XPersistFileExt::Load(LPCOLESTR wszFile, DWORD dwMode)
639 METHOD_PROLOGUE(CShellExt, PersistFileExt);
641 _tcscpy(pThis->m_szFile, OLE2T((WCHAR*)wszFile));
645 STDMETHODIMP CShellExt::XPersistFileExt::GetClassID(LPCLSID)
650 STDMETHODIMP CShellExt::XPersistFileExt::IsDirty(VOID)
655 STDMETHODIMP CShellExt::XPersistFileExt::Save(LPCOLESTR, BOOL)
660 STDMETHODIMP CShellExt::XPersistFileExt::SaveCompleted(LPCOLESTR)
665 STDMETHODIMP CShellExt::XPersistFileExt::GetCurFile(LPOLESTR FAR*)