Windows: GetInfoTip really set return to NULL
[openafs.git] / src / WINNT / client_exp / shell_ext.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
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
8  */
9
10 #include <afxpriv.h>
11 #include "stdafx.h"
12 #include "PropFile.h"
13 #include "PropACL.h"
14 #include "PropVolume.h"
15 #include <winsock2.h>
16 #include <ws2tcpip.h>
17 #include <shtypes.h>
18 #include <shlwapi.h>
19
20 extern "C" {
21 #include <afs/param.h>
22 #include <afs/stds.h>
23 #include <afs/afs_consts.h>
24 }
25
26 #include <atlconv.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include "afs_shl_ext.h"
30 #include "shell_ext.h"
31 #include "volume_info.h"
32 #include "set_afs_acl.h"
33 #include "gui2fs.h"
34 #include "make_mount_point_dlg.h"
35 #include "msgs.h"
36 #include "server_status_dlg.h"
37 #include "auth_dlg.h"
38 #include "submounts_dlg.h"
39 #include "make_symbolic_link_dlg.h"
40 #include <WINNT\afsreg.h>
41
42 #ifdef _DEBUG
43 #define new DEBUG_NEW
44 #undef THIS_FILE
45 static char THIS_FILE[] = __FILE__;
46 #endif
47
48 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
49
50
51 ULONG nCMRefCount = 0;  // IContextMenu ref count
52 ULONG nSERefCount = 0;  // IShellExtInit ref count
53 ULONG nICRefCount=0;
54 ULONG nTPRefCount=0;
55 ULONG nXPRefCount=0;
56 UINT nPSRefCount=0;
57
58 #define PCCHAR(str)     ((char *)(const char *)str)
59 static char space[AFS_PIOCTL_MAXSIZE];
60
61 static BOOL IsADir(const CString& strName)
62 {
63     struct _stat statbuf;
64
65     if (_tstat(strName, &statbuf) < 0)
66         return FALSE;
67
68     return statbuf.st_mode & _S_IFDIR;
69 }
70
71 /////////////////////////////////////////////////////////////////////////////
72 // CShellExt
73
74 IMPLEMENT_DYNCREATE(CShellExt, CCmdTarget)
75 IMPLEMENT_DYNCREATE(CShellExt2, CCmdTarget)
76 #define REG_CLIENT_PARMS_KEY    "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters"
77 #define OVERLAYENABLED 1
78
79 CShellExt::CShellExt()
80 {
81     HKEY NPKey;
82     EnableAutomation();
83     nCMRefCount++;
84     HRESULT hr;
85     UINT code;
86     DWORD ShellOption,LSPsize,LSPtype;
87     m_overlayObject = 0;
88     hr = SHGetMalloc(&m_pAlloc);
89     m_bIsOverlayEnabled=FALSE;
90     if (FAILED(hr))
91         m_pAlloc = NULL;
92     RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,0, (IsWow64()?KEY_WOW64_64KEY:0)|KEY_QUERY_VALUE, &NPKey);
93     LSPsize=sizeof(ShellOption);
94     code=RegQueryValueEx(NPKey, _T("ShellOption"), NULL,
95                           &LSPtype, (LPBYTE)&ShellOption, &LSPsize);
96     RegCloseKey (NPKey);
97     m_bIsOverlayEnabled=((code==0) && (LSPtype==REG_DWORD) && ((ShellOption & OVERLAYENABLED)!=0));
98
99     INITCOMMONCONTROLSEX used = {
100         sizeof(INITCOMMONCONTROLSEX),
101         ICC_DATE_CLASSES | ICC_WIN95_CLASSES | ICC_BAR_CLASSES | ICC_USEREX_CLASSES
102     };
103     InitCommonControlsEx(&used);
104
105     TRACE("Create CShellExt, Ref count %d/n",nCMRefCount);
106 }
107
108 CShellExt::~CShellExt()
109 {
110     if(m_pAlloc)
111         m_pAlloc->Release();
112     nCMRefCount--;
113     TRACE("Destroy CShellExt, Ref count %d/n",nCMRefCount);
114 }
115
116
117 void CShellExt::OnFinalRelease()
118 {
119     // When the last reference for an automation object is released
120     // OnFinalRelease is called.  The base class will automatically
121     // deletes the object.  Add additional cleanup required for your
122     // object before calling the base class.
123
124     CCmdTarget::OnFinalRelease();
125 }
126
127
128 BEGIN_MESSAGE_MAP(CShellExt, CCmdTarget)
129     //{{AFX_MSG_MAP(CShellExt)
130     // NOTE - the ClassWizard will add and remove mapping macros here.
131     //}}AFX_MSG_MAP
132 END_MESSAGE_MAP()
133
134 BEGIN_DISPATCH_MAP(CShellExt, CCmdTarget)
135     //{{AFX_DISPATCH_MAP(CShellExt)
136     // NOTE - the ClassWizard will add and remove mapping macros here.
137     //}}AFX_DISPATCH_MAP
138 END_DISPATCH_MAP()
139
140 // Note: we add support for IID_IShellExt to support typesafe binding
141 //  from VBA.  This IID must match the GUID that is attached to the
142 //  dispinterface in the .ODL file.
143
144 #ifndef _WIN64
145 // 32-bit
146 // {DC515C27-6CAC-11D1-BAE7-00C04FD140D2}
147 static const IID IID_IShellExt =
148 { 0xdc515c27, 0x6cac, 0x11d1, { 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd2 } };
149 static const IID IID_IShellExt2 =
150 { 0xdc515c27, 0x6cac, 0x11d1, { 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd3 } };
151 #else
152 // 64-bit
153 // {5f820ca1-3dde-11db-b2ce-001558092db5}
154 static const IID IID_IShellExt =
155 { 0x5f820ca1, 0x3dde, 0x11db, {0xb2, 0xce, 0x00, 0x15, 0x58, 0x09, 0x2d, 0xb5 } };
156 static const IID IID_IShellExt2 =
157 { 0x5f820ca1, 0x3dde, 0x11db, {0xb2, 0xce, 0x00, 0x15, 0x58, 0x09, 0x2d, 0xb6 } };
158 #endif
159
160 BEGIN_INTERFACE_MAP(CShellExt, CCmdTarget)
161     INTERFACE_PART(CShellExt, IID_IShellExt, Dispatch)
162     INTERFACE_PART(CShellExt, IID_IContextMenu, MenuExt)
163     INTERFACE_PART(CShellExt, IID_IShellExtInit, ShellInit)
164     INTERFACE_PART(CShellExt, IID_IShellIconOverlayIdentifier, IconExt)
165     INTERFACE_PART(CShellExt, IID_IQueryInfo , ToolTipExt)
166     INTERFACE_PART(CShellExt, IID_IPersistFile , PersistFileExt)
167     INTERFACE_PART(CShellExt, IID_IShellPropSheetExt , PropertySheetExt)
168 END_INTERFACE_MAP()
169
170 #ifndef _WIN64
171     // 32-bit
172 IMPLEMENT_OLECREATE(CShellExt, _STR_EXT_TITLE, 0xdc515c27, 0x6cac, 0x11d1, 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd2)
173 IMPLEMENT_OLECREATE(CShellExt2, _STR_EXT_TITLE, 0xdc515c27, 0x6cac, 0x11d1, 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd3)
174 #else
175     // 64-bit
176 IMPLEMENT_OLECREATE(CShellExt, _STR_EXT_TITLE, 0x5f820ca1, 0x3dde, 0x11db, 0xb2, 0xce, 0x0, 0x15, 0x58, 0x09, 0x2d, 0xb5)
177 IMPLEMENT_OLECREATE(CShellExt2, _STR_EXT_TITLE, 0x5f820ca1, 0x3dde, 0x11db, 0xb2, 0xce, 0x0, 0x15, 0x58, 0x09, 0x2d, 0xb6)
178 #endif
179
180
181 /////////////////////////////////////////////////////////////////////////////
182 // CShellExt message handlers
183 /////////////////////////////////////////////////////////////////////////////
184
185
186 /////////////////////////////////////////////////////////////////////////////
187 // IUnknown for IContextMenu
188 /////////////////////////////////////////////////////////////////////////////
189 STDMETHODIMP CShellExt::XMenuExt::QueryInterface(REFIID riid, void** ppv)
190 {
191     METHOD_PROLOGUE(CShellExt, MenuExt);
192
193     return pThis->ExternalQueryInterface(&riid, ppv);
194 }
195
196 STDMETHODIMP_(ULONG) CShellExt::XMenuExt::AddRef(void)
197 {
198     return ++nCMRefCount;
199 }
200
201 STDMETHODIMP_(ULONG) CShellExt::XMenuExt::Release(void)
202 {
203     if (nCMRefCount > 0)
204         nCMRefCount--;
205
206     return nCMRefCount;
207 }
208
209 /////////////////////////////////////////////////////////////////////////////
210 // IConextMenu Functions
211 /////////////////////////////////////////////////////////////////////////////
212 STDMETHODIMP CShellExt::XMenuExt::QueryContextMenu(HMENU hMenu,UINT indexMenu,
213                                                    UINT idCmdFirst, UINT idCmdLast,UINT uFlags)
214 {
215     METHOD_PROLOGUE(CShellExt, MenuExt);
216
217     // Don't add any menu items if we're being asked to deal with this file as a shortcut.
218     if (uFlags & CMF_VERBSONLY)
219         return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, (USHORT)0);
220
221     if (!pThis->m_bIsPathInAFS)
222         return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, (USHORT)0);
223
224     // Check to see if there's already an AFS menu here; if so, remove it
225     int nItemsNow = GetMenuItemCount (hMenu);
226     CString strAfsItemText = GetMessageString(IDS_AFS_ITEM);
227     CString strDeleteText = GetMessageString(IDS_MENU_DELETE);
228     CString strCutText = GetMessageString(IDS_MENU_CUT);
229     LPCTSTR pszAfsItemText = (LPCTSTR)strAfsItemText;
230     for (int iItem = 0; iItem < nItemsNow; iItem++) {
231         TCHAR szItemText[256];
232         if (!GetMenuString (hMenu, iItem, szItemText, 256, MF_BYPOSITION))
233             continue;
234         if (lstrcmp (szItemText, pszAfsItemText)==0) {
235             DeleteMenu (hMenu, iItem, MF_BYPOSITION);
236             continue;
237         }
238         if (((lstrcmp(szItemText,strDeleteText)==0)||(lstrcmp(szItemText,strCutText)==0))&&((pThis->m_bIsSymlink)||(pThis->m_bIsMountpoint))) {
239         DeleteMenu (hMenu, iItem, MF_BYPOSITION);
240             continue;
241         }
242     }
243     int indexShellMenu = 0;
244
245     // Create the AFS submenu using the allowed ID's.
246     HMENU hAfsMenu = CreatePopupMenu();
247     int indexAfsMenu = 0;
248
249     // Only enable the ACL menu item if a single directory is selected
250     int nSingleDirOnly = MF_GRAYED;
251     if (pThis->m_bDirSelected && (pThis->m_astrFileNames.GetSize() == 1))
252         nSingleDirOnly = MF_ENABLED;
253     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_ACL_SET, GetMessageString(IDS_ACLS_ITEM));
254
255     // Volume/Partition submenu of the AFS submenu
256     HMENU hVolPartMenu = CreatePopupMenu();
257     int indexVolPartMenu = 0;
258     ::InsertMenu(hVolPartMenu, indexVolPartMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_VOLUME_PROPERTIES, GetMessageString(IDS_VOL_PART_PROPS_ITEM));
259     ::InsertMenu(hVolPartMenu, indexVolPartMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE, GetMessageString(IDS_VOL_PART_REFRESH_ITEM));
260     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hVolPartMenu, GetMessageString(IDS_VOL_PART_ITEM));
261
262     // Mount Point submenu of the AFS submenu
263     HMENU hMountPointMenu = CreatePopupMenu();
264     int indexMountPointMenu = 0;
265     int nMountPointSelected = MF_GRAYED;
266     for (int n = pThis->m_astrFileNames.GetSize() - 1 ; n >= 0; n--) {
267         if ( IsMountPoint(pThis->m_astrFileNames[n]) ) {
268             nMountPointSelected = MF_ENABLED;
269             break;
270         }
271     }
272     ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_SHOW, GetMessageString(IDS_MP_SHOW_ITEM));
273     ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION | nMountPointSelected, idCmdFirst + IDM_MOUNTPOINT_REMOVE, GetMessageString(IDS_MP_REMOVE_ITEM));
274     ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_MAKE, GetMessageString(IDS_MP_MAKE_ITEM));
275     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hMountPointMenu, GetMessageString(IDS_MOUNT_POINT_ITEM));
276
277     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_FLUSH, GetMessageString(IDS_FLUSH_FILE_DIR_ITEM));
278     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_FLUSH_VOLUME, GetMessageString(IDS_FLUSH_VOLUME_ITEM));
279     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHOW_SERVER, GetMessageString(IDS_SHOW_FILE_SERVERS_ITEM));
280     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHOWCELL, GetMessageString(IDS_SHOW_CELL_ITEM));
281     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SERVER_STATUS, GetMessageString(IDS_SHOW_SERVER_STATUS_ITEM));
282
283     HMENU hSymbolicMenu = CreatePopupMenu();
284     int indexSymbolicMenu = 0;
285     int nSymlinkSelected = MF_GRAYED;
286     for (int n = pThis->m_astrFileNames.GetSize() - 1 ; n >= 0; n--) {
287         if ( IsSymlink(pThis->m_astrFileNames[n]) ) {
288             nSymlinkSelected = MF_ENABLED;
289             break;
290         }
291     }
292
293     ::InsertMenu(hSymbolicMenu, indexSymbolicMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SYMBOLICLINK_ADD, GetMessageString(IDS_SYMBOLICLINK_ADD));
294     ::InsertMenu(hSymbolicMenu, indexSymbolicMenu++, MF_STRING | MF_BYPOSITION | nSymlinkSelected, idCmdFirst + IDM_SYMBOLICLINK_SHOW, GetMessageString(IDS_SYMBOLICLINK_SHOW));
295     ::InsertMenu(hSymbolicMenu, indexSymbolicMenu++, MF_STRING | MF_BYPOSITION | nSymlinkSelected, idCmdFirst + IDM_SYMBOLICLINK_REMOVE, GetMessageString(IDS_SYMBOLICLINK_REMOVE));
296     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hSymbolicMenu, GetMessageString(IDS_SYMBOLIC_LINK_ITEM));
297
298     // The Submounts menu has been removed because the AFS tray icon
299     // and control panel now support mapping drives directly to an AFS
300     // path.
301     //
302     //HMENU hSubmountMenu = CreatePopupMenu();
303     //int indexSubmountMenu = 0;
304     //::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_SUBMOUNTS_CREATE, GetMessageString(IDS_SUBMOUNTS_CREATE_ITEM));
305     //::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SUBMOUNTS_EDIT, GetMessageString(IDS_SUBMOUNTS_EDIT_ITEM));
306     //::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hSubmountMenu, GetMessageString(IDS_SUBMOUNTS_ITEM));
307
308     // Add a separator
309     ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
310
311     // Add the AFS submenu to the shell's menu
312     ::InsertMenu(hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hAfsMenu, GetMessageString(IDS_AFS_ITEM));
313
314     // Add a separator after us
315     ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
316
317     return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL,
318                          (USHORT)indexAfsMenu + indexVolPartMenu + indexMountPointMenu + indexShellMenu + indexSymbolicMenu);
319 }
320
321 STDMETHODIMP CShellExt::XMenuExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
322 {
323     METHOD_PROLOGUE(CShellExt, MenuExt);
324
325     if (HIWORD(lpici->lpVerb ))
326         return E_FAIL;
327
328     AddRef();
329
330     CStringArray &files = pThis->m_astrFileNames;
331
332     switch (LOWORD(lpici->lpVerb))
333     {
334     case IDM_AUTHENTICATION:    {
335         CAuthDlg dlg;
336         dlg.DoModal();
337         break;
338     }
339
340     case IDM_ACL_SET:                   {
341         CSetAfsAcl dlg;
342         ASSERT(files.GetSize() == 1);
343         dlg.SetDir(files[0]);
344         dlg.DoModal();
345         break;
346     }
347
348     case IDM_VOLUME_PROPERTIES: {
349         CVolumeInfo dlg;
350         dlg.SetFiles(files);
351         dlg.DoModal();
352         break;
353     }
354
355     case IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE:
356         CheckVolumes();
357         break;
358
359     case IDM_MOUNTPOINT_SHOW:
360         ListMount(files);
361         break;
362
363     case IDM_MOUNTPOINT_REMOVE: {
364         int nChoice = ShowMessageBox(IDS_REALLY_DEL_MOUNT_POINTS, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_DEL_MOUNT_POINTS);
365         if (nChoice == IDYES)
366             RemoveMount(files);
367         break;
368     }
369
370     case IDM_MOUNTPOINT_MAKE:   {
371         CMakeMountPointDlg dlg;
372         ASSERT(files.GetSize() == 1);
373         dlg.SetDir(files[0]);
374         dlg.DoModal();
375         break;
376     }
377
378     case IDM_FLUSH:
379         Flush(files);
380         break;
381
382     case IDM_FLUSH_VOLUME:
383         FlushVolume(files);
384         break;
385
386     case IDM_SHOW_SERVER:
387         WhereIs(files);
388         break;
389
390     case IDM_SHOWCELL:
391         WhichCell(files);
392         break;
393
394     case IDM_SERVER_STATUS: {
395         CServerStatusDlg dlg;
396         dlg.DoModal();
397         break;
398     }
399
400         /*
401         case IDM_SUBMOUNTS_EDIT:        {
402         CSubmountsDlg dlg;
403         dlg.DoModal();
404         break;
405         }
406         case IDM_SUBMOUNTS_CREATE:      {
407         ASSERT(files.GetSize() == 1);
408         CSubmountsDlg dlg;
409         dlg.SetAddOnlyMode(files[0]);
410         dlg.DoModal();
411         break;
412         }
413         */
414     case IDM_SYMBOLICLINK_REMOVE: {
415         if (files.GetSize()>1)
416             break;
417         CString msg=files.GetAt(0);
418         int i;
419         if ((i=msg.ReverseFind('\\'))>0)
420             msg=msg.Left(i+1);
421         else if ((i=msg.ReverseFind(':'))>0)
422             msg=msg.Left(i+1)+"\\";
423         if (!SetCurrentDirectory(msg))
424         {
425             MessageBeep((UINT)-1);
426             ShowMessageBox(IDS_UNABLE_TO_SET_CURRENT_DIRECTORY,MB_OK,IDS_UNABLE_TO_SET_CURRENT_DIRECTORY);
427             break;
428         }
429         msg=files.GetAt(0);
430         if ((i=msg.ReverseFind('\\'))>0||((i=msg.ReverseFind(':'))>0))
431             msg=msg.Right(msg.GetLength()-i-1);
432         int nChoice = ShowMessageBox(IDS_REALLY_REMOVE_SYMLINK, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_REMOVE_SYMLINK,msg);
433         if (nChoice == IDYES)
434             RemoveSymlink(files.GetAt(0));
435         break;
436     }
437
438     case IDM_SYMBOLICLINK_ADD: {
439         CStringA msg(files.GetAt(0));
440         int i;
441         if ((i=msg.ReverseFind('\\'))>0)
442             msg=msg.Left(i+1);
443         else if ((i=msg.ReverseFind(':'))>0)
444             msg=msg.Left(i+1)+"\\";
445         CMakeSymbolicLinkDlg dlg;
446         dlg.Setbase(msg);
447         dlg.DoModal();
448         break;
449     }
450
451     case IDM_SYMBOLICLINK_SHOW:
452         ListSymlink(files);
453         break;
454
455     case IDM_REMOVE_SYMLINK:    {
456         if (files.GetSize()>1)
457             break;
458         int nChoice = ShowMessageBox(IDS_REALLY_REMOVE_SYMLINK, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_REMOVE_SYMLINK);
459         if (nChoice == IDYES)
460             RemoveSymlink(files.GetAt(0));
461         }
462         break;
463     default:
464         ASSERT(FALSE);
465         Release();
466         return E_INVALIDARG;
467     }
468
469     Release();
470
471     return NOERROR;
472 }
473
474 STDMETHODIMP CShellExt::XMenuExt::GetCommandString(UINT_PTR idCmd, UINT uType,
475     UINT* pwReserved, LPSTR pszName, UINT cchMax)
476 {
477     if (uType != GCS_HELPTEXT)
478         return NOERROR;                 // ?????????????????????????????????????????????????
479
480     UINT nCmdStrID;
481
482     AfxSetResourceHandle(theApp.m_hInstance);
483
484     switch (idCmd)
485     {
486     case IDM_AUTHENTICATION:
487         nCmdStrID = ID_AUTHENTICATE;
488         break;
489
490     case IDM_ACL_SET:
491         nCmdStrID = ID_ACL_SET;
492         break;
493
494     case IDM_VOLUME_PROPERTIES:
495         nCmdStrID = ID_VOLUME_PROPERTIES;
496         break;
497
498     case IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE:
499         nCmdStrID = ID_VOLUMEPARTITION_UPDATENAMEIDTABLE;
500         break;
501
502     case IDM_MOUNTPOINT_SHOW:
503         nCmdStrID = ID_MOUNTPOINT_SHOW;
504         break;
505
506     case IDM_MOUNTPOINT_REMOVE:
507         nCmdStrID = ID_MOUNTPOINT_REMOVE;
508         break;
509
510     case IDM_MOUNTPOINT_MAKE:
511         nCmdStrID = ID_MOUNTPOINT_MAKE;
512         break;
513
514     case IDM_FLUSH:
515         nCmdStrID = ID_FLUSH;
516         break;
517
518     case IDM_FLUSH_VOLUME:
519         nCmdStrID = ID_VOLUME_FLUSH;
520         break;
521
522     case IDM_SHOW_SERVER:
523         nCmdStrID = ID_WHEREIS;
524         break;
525
526     case IDM_SHOWCELL:
527         nCmdStrID = ID_SHOWCELL;
528         break;
529
530     case IDM_SERVER_STATUS:
531         nCmdStrID = ID_SERVER_STATUS;
532         break;
533
534     case IDM_SYMBOLICLINK_ADD:
535         nCmdStrID = ID_SYMBOLICLINK_ADD;
536         break;
537
538     case IDM_SYMBOLICLINK_SHOW:
539         nCmdStrID = ID_SYMBOLICLINK_SHOW;
540         break;
541
542     case IDM_SYMBOLICLINK_REMOVE:
543         nCmdStrID = ID_SYMBOLICLINK_REMOVE;
544         break;
545
546     case IDM_REMOVE_SYMLINK:
547         nCmdStrID= ID_REMOVE_SYMLINK;
548         break;
549
550     default:
551         ASSERT(FALSE);
552         Release();
553         return E_INVALIDARG;
554     }
555
556     CString strMsg;
557     LoadString (strMsg, nCmdStrID);
558
559     _tcsncpy((LPTSTR) pszName, strMsg, cchMax);
560
561     return NOERROR;
562 }
563
564 /////////////////////////////////////////////////////////////////////////////
565 // IUnknown for IShellExtInit
566 /////////////////////////////////////////////////////////////////////////////
567 STDMETHODIMP CShellExt::XShellInit::QueryInterface(REFIID riid, void** ppv)
568 {
569     METHOD_PROLOGUE(CShellExt, ShellInit);
570
571     return pThis->ExternalQueryInterface(&riid, ppv);
572 }
573
574 STDMETHODIMP_(ULONG) CShellExt::XShellInit::AddRef(void)
575 {
576     return ++nSERefCount;
577 }
578
579 STDMETHODIMP_(ULONG) CShellExt::XShellInit::Release(void)
580 {
581     if (nSERefCount > 0)
582         nSERefCount--;
583
584     return nSERefCount;
585 }
586
587 /////////////////////////////////////////////////////////////////////////////
588 // IShellInit Functions
589 /////////////////////////////////////////////////////////////////////////////
590 STDMETHODIMP CShellExt::XShellInit::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
591 {
592     METHOD_PROLOGUE(CShellExt, ShellInit);
593
594     HRESULT hres = E_FAIL;
595     FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
596     STGMEDIUM medium;
597
598     // We must have a data object
599     if ((pdobj == NULL) && (pidlFolder == NULL))
600         return E_FAIL;
601
602     pThis->m_bIsSymlink=false;
603     pThis->m_bIsMountpoint=false;
604     pThis->m_bIsPathInAFS=false;
605     pThis->m_bDirSelected=false;
606
607     if (pdobj) {
608         //  Use the given IDataObject to get a list of filenames (CF_HDROP)
609         hres = pdobj->GetData(&fmte, &medium);
610         if (FAILED(hres)) {
611         return E_FAIL;
612         }
613
614         int nNumFiles = DragQueryFile((HDROP)medium.hGlobal, 0xFFFFFFFF, NULL, 0);
615         if (nNumFiles == 0)
616             hres = E_FAIL;
617         else {
618             pThis->m_bDirSelected = FALSE;
619
620             for (int ii = 0; ii < nNumFiles; ii++) {
621                 CString strFileName;
622
623                 // Get the size of the file name string
624                 int nNameLen = DragQueryFile((HDROP)medium.hGlobal, ii, 0, 0);
625
626                 // Make room for it in our string object
627                 LPTSTR pszFileNameBuf = strFileName.GetBuffer(nNameLen + 1);    // +1 for the terminating NULL
628                 ASSERT(pszFileNameBuf);
629
630                 // Get the file name
631                 DragQueryFile((HDROP)medium.hGlobal, ii, pszFileNameBuf, nNameLen + 1);
632
633                 strFileName.ReleaseBuffer();
634                 if (!IsPathInAfs(strFileName)) {
635                 pThis->m_astrFileNames.RemoveAll();
636                 pThis->m_bIsPathInAFS=false;
637                 break;
638                 } else {
639                 pThis->m_bIsSymlink=pThis->m_bIsSymlink||IsSymlink(strFileName);
640                 pThis->m_bIsMountpoint=pThis->m_bIsMountpoint||IsMountPoint(strFileName);
641                 pThis->m_bIsPathInAFS=true;
642                 }
643
644                 if (IsADir(strFileName))
645                 pThis->m_bDirSelected = TRUE;
646
647                 pThis->m_astrFileNames.Add(strFileName);
648             }
649             //  Release the data
650             ReleaseStgMedium(&medium);
651         }
652     }
653     if ((pThis->m_astrFileNames.GetSize() == 0)&&(pidlFolder)) {
654         // if there are no valid files selected, try the folder background
655         IShellFolder *parentFolder = NULL;
656         STRRET name;
657         TCHAR * szDisplayName = NULL;
658
659         hres = ::SHGetDesktopFolder(&parentFolder);
660         if (FAILED(hres))
661             return hres;
662
663         hres = parentFolder->GetDisplayNameOf(pidlFolder, SHGDN_NORMAL | SHGDN_FORPARSING, &name);
664         if (FAILED(hres)) {
665             parentFolder->Release();
666             return hres;
667         }
668
669         hres = StrRetToStr (&name, pidlFolder, &szDisplayName);
670         if (FAILED(hres))
671             return hres;
672         parentFolder->Release();
673         if (szDisplayName) {
674             pThis->m_bDirSelected = TRUE;
675             CString strFileName = CString(szDisplayName);
676             if (IsPathInAfs(strFileName)) {
677                 pThis->m_bIsSymlink=IsSymlink(strFileName);
678                 pThis->m_bIsMountpoint=IsMountPoint(strFileName);
679                 pThis->m_bIsPathInAFS=true;
680                 pThis->m_astrFileNames.Add(strFileName);
681             }
682             CoTaskMemFree(szDisplayName);
683         }
684     }
685         if (pThis->m_astrFileNames.GetSize() > 0)
686             hres = NOERROR;
687         else
688             hres = E_FAIL;
689
690     return hres;
691 }
692
693 STDMETHODIMP CShellExt::XIconExt::QueryInterface(REFIID riid, void** ppv)
694 {
695     METHOD_PROLOGUE(CShellExt, IconExt);
696     return pThis->ExternalQueryInterface(&riid, ppv);
697 }
698
699 STDMETHODIMP_(ULONG) CShellExt::XIconExt::AddRef(void)
700 {
701     return ++nICRefCount;
702 }
703
704 STDMETHODIMP_(ULONG) CShellExt::XIconExt::Release(void)
705 {
706     if (nICRefCount > 0)
707         nICRefCount--;
708
709     return nICRefCount;
710 }
711
712
713 /////////////////////////////////////////////////////////////////////////////
714 // IIconHandler Functions
715 /////////////////////////////////////////////////////////////////////////////
716
717 STDMETHODIMP CShellExt::XIconExt::GetOverlayInfo(LPWSTR pwszIconFile
718         ,int cchMax,int* pIndex,DWORD* pdwFlags)
719 {
720     METHOD_PROLOGUE(CShellExt, IconExt);
721     if(IsBadWritePtr(pIndex, sizeof(int)))
722         return E_INVALIDARG;
723     if(IsBadWritePtr(pdwFlags, sizeof(DWORD)))
724         return E_INVALIDARG;
725
726     // The icons must reside in the same path as this dll
727     TCHAR szModule[MAX_PATH];
728     GetModuleFileName(theApp.m_hInstance, szModule, MAX_PATH);
729     TCHAR * slash = _tcsrchr(szModule, '\\');
730     if (slash) {
731         *slash = 0;
732         switch (pThis->GetOverlayObject())
733         {
734             case 0:
735                 _tcscat(szModule, _T("\\link.ico"));
736             break;
737             case 1:
738                 _tcscat(szModule, _T("\\mount.ico"));
739             break;
740         }
741     }
742 #ifndef UNICODE
743     MultiByteToWideChar( CP_ACP,0,szModule,-1,pwszIconFile,cchMax);
744 #else
745     _tcsncpy(pwszIconFile, szModule, cchMax);
746 #endif
747     *pIndex = 0;
748     *pdwFlags = ISIOI_ICONFILE;
749     return S_OK;
750 }
751
752 STDMETHODIMP CShellExt::XIconExt::GetPriority(int* pPriority)
753 {
754     if(IsBadWritePtr(pPriority, sizeof(int)))
755         return E_INVALIDARG;
756     *pPriority = 0;
757     return S_OK;
758 }
759
760 STDMETHODIMP CShellExt::XIconExt::IsMemberOf(LPCWSTR pwszPath,DWORD dwAttrib)
761 {
762     METHOD_PROLOGUE(CShellExt, IconExt);
763     TCHAR szPath[MAX_PATH];
764 #ifdef UNICODE
765     _tcscpy(szPath, pwszPath);
766 #else
767     WideCharToMultiByte( CP_ACP,0,pwszPath,-1,szPath,MAX_PATH,NULL,NULL);
768 #endif
769         if (!IsPathInAfs(szPath))
770                 return S_FALSE;
771
772     if ((pThis->GetOverlayObject() == 0)&&(IsSymlink(szPath))) {
773         return S_OK;
774     }
775     if ((pThis->GetOverlayObject() == 1)&&(IsMountPoint(szPath))) {
776         return S_OK;
777     }
778     return S_FALSE;
779 }
780
781 /*  TOOL TIP INFO IMPLIMENTION   */
782
783 STDMETHODIMP CShellExt::XToolTipExt::QueryInterface(REFIID riid, void** ppv)
784 {
785     METHOD_PROLOGUE(CShellExt, ToolTipExt);
786     return pThis->ExternalQueryInterface(&riid, ppv);
787 }
788
789 STDMETHODIMP_(ULONG) CShellExt::XToolTipExt::AddRef(void)
790 {
791     return ++nTPRefCount;
792 }
793
794 STDMETHODIMP_(ULONG) CShellExt::XToolTipExt::Release(void)
795 {
796     if (nTPRefCount> 0)
797         nTPRefCount--;
798
799     return nTPRefCount;
800 }
801
802 STDMETHODIMP CShellExt::XToolTipExt::GetInfoTip(DWORD dwFlags, LPWSTR *ppwszTip)
803 {
804     METHOD_PROLOGUE(CShellExt, ToolTipExt);
805
806     if ((_tcslen(pThis->m_szFile) == 0)||(!IsPathInAfs(pThis->m_szFile)))
807     {
808         *ppwszTip=NULL;
809         return S_OK;
810     }
811     bool bIsSymlink = !!IsSymlink(pThis->m_szFile);
812     bool bIsMountpoint = !!IsMountPoint(pThis->m_szFile);
813     if ((!bIsSymlink) && (!bIsMountpoint))
814     {
815         *ppwszTip=NULL;
816         return S_OK;
817     }
818     USES_CONVERSION;
819     // dwFlags is currently unused.
820     CString sInfo;
821     if (bIsSymlink)
822         sInfo = GetSymlink(pThis->m_szFile);
823     else if (bIsMountpoint)
824         sInfo = GetMountpoint(pThis->m_szFile);
825     *ppwszTip = (WCHAR*) (pThis->m_pAlloc)->Alloc((1+sInfo.GetLength())*sizeof(WCHAR));
826     if (*ppwszTip)
827         wcscpy(*ppwszTip, (WCHAR*)T2COLE((LPCTSTR)sInfo));
828
829     return S_OK;
830 }
831 STDMETHODIMP CShellExt::XToolTipExt::GetInfoFlags(LPDWORD pdwFlags)
832 {
833     *pdwFlags = 0;
834     return S_OK;
835 }
836
837 //////////                          IPersistFile
838 ///////                            PersistFileExt
839
840 STDMETHODIMP CShellExt::XPersistFileExt::QueryInterface(REFIID riid, void** ppv)
841 {
842     METHOD_PROLOGUE(CShellExt, PersistFileExt);
843     return pThis->ExternalQueryInterface(&riid, ppv);
844 }
845
846 STDMETHODIMP_(ULONG) CShellExt::XPersistFileExt::AddRef(void)
847 {
848     return ++nXPRefCount;
849 }
850
851 STDMETHODIMP_(ULONG) CShellExt::XPersistFileExt::Release(void)
852 {
853     if (nXPRefCount> 0)
854         nXPRefCount--;
855
856     return nXPRefCount;
857 }
858
859 STDMETHODIMP    CShellExt::XPersistFileExt::Load(LPCOLESTR wszFile, DWORD dwMode)
860 {
861     METHOD_PROLOGUE(CShellExt, PersistFileExt);
862     USES_CONVERSION;
863     _tcscpy(pThis->m_szFile, OLE2T((WCHAR*)wszFile));
864     return S_OK;
865 }
866
867 STDMETHODIMP CShellExt::XPersistFileExt::GetClassID(LPCLSID)
868 {
869     return E_NOTIMPL;
870 }
871
872 STDMETHODIMP CShellExt::XPersistFileExt::IsDirty(VOID)
873 {
874     return E_NOTIMPL;
875 }
876
877 STDMETHODIMP CShellExt::XPersistFileExt::Save(LPCOLESTR, BOOL)
878 {
879     return E_NOTIMPL;
880 }
881
882 STDMETHODIMP CShellExt::XPersistFileExt::SaveCompleted(LPCOLESTR)
883 {
884     return E_NOTIMPL;
885 }
886
887 STDMETHODIMP CShellExt::XPersistFileExt::GetCurFile(LPOLESTR FAR*)
888 {
889     return E_NOTIMPL;
890 }
891
892 // IShellPropSheetExt
893 STDMETHODIMP CShellExt::XPropertySheetExt::QueryInterface(REFIID riid, void** ppv)
894 {
895     METHOD_PROLOGUE(CShellExt, PropertySheetExt);
896     return pThis->ExternalQueryInterface(&riid, ppv);
897 }
898
899 STDMETHODIMP_(ULONG) CShellExt::XPropertySheetExt::AddRef(void)
900 {
901     return ++nPSRefCount;
902 }
903
904 STDMETHODIMP_(ULONG) CShellExt::XPropertySheetExt::Release(void)
905 {
906     if (nPSRefCount> 0)
907         nPSRefCount--;
908
909     return nPSRefCount;
910 }
911
912 BOOL CALLBACK PageProc (HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
913 {
914     PropertyPage * sheetpage;
915
916     if (uMessage == WM_INITDIALOG)
917     {
918         sheetpage = (PropertyPage*) ((LPPROPSHEETPAGE) lParam)->lParam;
919         SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) sheetpage);
920         sheetpage->SetHwnd(hwnd);
921     }
922     else
923     {
924         sheetpage = (PropertyPage*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
925     }
926
927     if (sheetpage != 0L)
928         return sheetpage->PropPageProc(hwnd, uMessage, wParam, lParam);
929     else
930         return FALSE;
931 }
932
933 UINT CALLBACK PropPageCallbackProc ( HWND /*hwnd*/, UINT uMsg, LPPROPSHEETPAGE ppsp )
934 {
935     // Delete the page before closing.
936     if (PSPCB_CREATE == uMsg)
937         return TRUE;
938     if (PSPCB_RELEASE == uMsg)
939     {
940         PropertyPage* sheetpage = (PropertyPage*) ppsp->lParam;
941         delete sheetpage;
942     }
943     return TRUE;
944 }
945
946 STDMETHODIMP CShellExt::XPropertySheetExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
947 {
948     METHOD_PROLOGUE(CShellExt, PropertySheetExt);
949
950     if(pThis->m_bIsPathInAFS) {
951         // add the property page for files/folder/mount points/symlinks
952         PROPSHEETPAGE psp;
953         SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
954         HPROPSHEETPAGE hPage;
955         CPropFile *sheetpage = NULL;
956
957         sheetpage = new CPropFile(pThis->m_astrFileNames);
958
959         if (sheetpage == NULL)
960             return E_OUTOFMEMORY;
961
962         HINSTANCE hInst = 0;
963         TaLocale_GetResource(RT_DIALOG, MAKEINTRESOURCE(IDD_PROPPAGE_FILE), LANG_USER_DEFAULT, &hInst);
964         sheetpage->m_hInst = hInst;
965         sheetpage->m_bIsMountpoint = pThis->m_bIsMountpoint;
966         sheetpage->m_bIsSymlink = pThis->m_bIsSymlink;
967         sheetpage->m_bIsDir=pThis->m_bDirSelected;
968         psp.dwSize = sizeof (psp);
969         psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USECALLBACK | PSP_USETITLE;
970         psp.hInstance = hInst;
971         psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE_FILE);
972         psp.pszIcon = NULL;
973         psp.pszTitle = _T("AFS");
974         psp.pfnDlgProc = (DLGPROC) PageProc;
975         psp.lParam = (LPARAM) sheetpage;
976         psp.pfnCallback = PropPageCallbackProc;
977         psp.pcRefParent = (UINT*) &nPSRefCount;
978
979         hPage = CreatePropertySheetPage (&psp);
980
981         if (hPage != NULL) {
982             if (!lpfnAddPage (hPage, lParam)) {
983                 delete sheetpage;
984                 DestroyPropertySheetPage (hPage);
985             }
986         }
987     }
988
989     // add the property page for Volume Data
990     PROPSHEETPAGE psp;
991     SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
992     HPROPSHEETPAGE hPage;
993     CPropVolume *sheetpage = NULL;
994
995     sheetpage = new CPropVolume(pThis->m_astrFileNames);
996
997     if (sheetpage == NULL)
998         return E_OUTOFMEMORY;
999
1000     HINSTANCE hInst = 0;
1001     TaLocale_GetResource(RT_DIALOG, MAKEINTRESOURCE(IDD_PROPPAGE_VOLUME), LANG_USER_DEFAULT, &hInst);
1002     sheetpage->m_hInst = hInst;
1003     sheetpage->m_bIsMountpoint = pThis->m_bIsMountpoint;
1004     sheetpage->m_bIsSymlink = pThis->m_bIsSymlink;
1005     sheetpage->m_bIsDir=pThis->m_bDirSelected;
1006     psp.dwSize = sizeof (psp);
1007     psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USECALLBACK | PSP_USETITLE;
1008     psp.hInstance = hInst;
1009     psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE_VOLUME);
1010     psp.pszIcon = NULL;
1011     psp.pszTitle = _T("AFS Volume");
1012     psp.pfnDlgProc = (DLGPROC) PageProc;
1013     psp.lParam = (LPARAM) sheetpage;
1014     psp.pfnCallback = PropPageCallbackProc;
1015     psp.pcRefParent = (UINT*) &nPSRefCount;
1016
1017     hPage = CreatePropertySheetPage (&psp);
1018
1019     if (hPage != NULL) {
1020         if (!lpfnAddPage (hPage, lParam)) {
1021             delete sheetpage;
1022             DestroyPropertySheetPage (hPage);
1023         }
1024     }
1025
1026     if(pThis->m_bDirSelected) {
1027         // add the property page for ACLs
1028         PROPSHEETPAGE psp;
1029         SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
1030         HPROPSHEETPAGE hPage;
1031         CPropACL *sheetpage = NULL;
1032
1033         sheetpage = new CPropACL(pThis->m_astrFileNames);
1034
1035         if (sheetpage == NULL)
1036             return E_OUTOFMEMORY;
1037
1038         HINSTANCE hInst = 0;
1039         TaLocale_GetResource(RT_DIALOG, MAKEINTRESOURCE(IDD_PROPPAGE_ACL), LANG_USER_DEFAULT, &hInst);
1040         sheetpage->m_hInst = hInst;
1041         sheetpage->m_bIsMountpoint = pThis->m_bIsMountpoint;
1042         sheetpage->m_bIsSymlink = pThis->m_bIsSymlink;
1043         sheetpage->m_bIsDir=pThis->m_bDirSelected;
1044         psp.dwSize = sizeof (psp);
1045         psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USECALLBACK | PSP_USETITLE;
1046         psp.hInstance = hInst;
1047         psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE_ACL);
1048         psp.pszIcon = NULL;
1049         psp.pszTitle = _T("AFS ACL");
1050         psp.pfnDlgProc = (DLGPROC) PageProc;
1051         psp.lParam = (LPARAM) sheetpage;
1052         psp.pfnCallback = PropPageCallbackProc;
1053         psp.pcRefParent = (UINT*) &nPSRefCount;
1054
1055         hPage = CreatePropertySheetPage (&psp);
1056
1057         if (hPage != NULL) {
1058             if (!lpfnAddPage (hPage, lParam)) {
1059                 delete sheetpage;
1060                 DestroyPropertySheetPage (hPage);
1061             }
1062         }
1063     }
1064
1065     return S_OK;
1066 }
1067
1068 STDMETHODIMP CShellExt::XPropertySheetExt::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam)
1069 {
1070     return E_FAIL;
1071 }