1 /*
2  *    Virtual Printers Folder
3  *
4  *    Copyright 1997                Marcus Meissner
5  *    Copyright 1998, 1999, 2002    Juergen Schmied
6  *    Copyright 2005                Huw Davies
7  *    Copyright 2009                Andrew Hill
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23 
24 #include <precomp.h>
25 
26 #include <winspool.h>
27 
28 WINE_DEFAULT_DEBUG_CHANNEL (shell);
29 
30 static shvheader PrinterSFHeader[] = {
31     {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
32     {IDS_SHV_COLUMN_DOCUMENTS , SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
33     {IDS_SHV_COLUMN_STATUS, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
34     {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
35     {IDS_SHV_COLUMN_LOCATION, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
36     {IDS_SHV_COLUMN_MODEL, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15}
37 };
38 
39 #define COLUMN_NAME          0
40 #define COLUMN_DOCUMENTS     1
41 #define COLUMN_STATUS        2
42 #define COLUMN_COMMENTS      3
43 #define COLUMN_LOCATION      4
44 #define COLUMN_MODEL         5
45 
46 #define PrinterSHELLVIEWCOLUMNS (6)
47 
48 /**************************************************************************
49  *  CPrintersExtractIconW_CreateInstane
50  *
51  *  There is no CPrintersExtractIconW. We just initialize CExtractIcon properly to do our job.
52  */
53 HRESULT WINAPI CPrintersExtractIconW_CreateInstane(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppv)
54 {
55     CComPtr<IDefaultExtractIconInit> initIcon;
56     HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit,&initIcon));
57     if (FAILED_UNEXPECTEDLY(hr))
58         return hr;
59 
60     /* FIXME: other icons for default, network, print to file */
61     initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_PRINTER);
62 
63     return initIcon->QueryInterface(riid,ppv);
64 }
65 
66 /***********************************************************************
67  *     Printers folder implementation
68  */
69 
70 class CPrintersEnum: public CEnumIDListBase
71 {
72     public:
73         CPrintersEnum();
74         ~CPrintersEnum();
75         HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags);
76         BOOL CreatePrintersEnumList(DWORD dwFlags);
77 
78         BEGIN_COM_MAP(CPrintersEnum)
79         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
80         END_COM_MAP()
81 };
82 
83 CPrintersEnum::CPrintersEnum()
84 {
85 }
86 
87 CPrintersEnum::~CPrintersEnum()
88 {
89 }
90 
91 HRESULT WINAPI CPrintersEnum::Initialize(HWND hwndOwner, DWORD dwFlags)
92 {
93     if (CreatePrintersEnumList(dwFlags) == FALSE)
94         return E_FAIL;
95     return S_OK;
96 }
97 
98 static LPITEMIDLIST _ILCreatePrinterItem(PRINTER_INFO_4W *pi)
99 {
100     PIDLDATA tmp;
101     LPITEMIDLIST pidl;
102     PIDLPrinterStruct * p;
103     int size0 = (char*)&tmp.u.cprinter.szName - (char*)&tmp.u.cprinter;
104     int size = size0;
105     SIZE_T cchPrinterName, cchServerName;
106 
107     cchPrinterName = wcslen(pi->pPrinterName);
108     cchServerName = wcslen(pi->pServerName);
109     if ((cchPrinterName + cchServerName) > (MAXUSHORT - 2))
110     {
111         return NULL;
112     }
113 
114     tmp.type = 0x00;
115     tmp.u.cprinter.dummy = 0xFF;
116     if (pi->pPrinterName)
117         tmp.u.cprinter.offsServer = cchPrinterName + 1;
118     else
119         tmp.u.cprinter.offsServer = 1;
120 
121     size += tmp.u.cprinter.offsServer * sizeof(WCHAR);
122     if (pi->pServerName)
123         size += (cchServerName + 1) * sizeof(WCHAR);
124     else
125         size += sizeof(WCHAR);
126 
127     pidl = (LPITEMIDLIST)SHAlloc(size + 4);
128     if (!pidl)
129         return pidl;
130 
131     pidl->mkid.cb = size + 2;
132     memcpy(pidl->mkid.abID, &tmp, 2 + size0);
133 
134     p = &((PIDLDATA*)pidl->mkid.abID)->u.cprinter;
135 
136     p->Attributes = pi->Attributes;
137     if (pi->pPrinterName)
138         wcscpy(p->szName, pi->pPrinterName);
139     else
140         p->szName[0] = L'\0';
141 
142     if (pi->pServerName)
143         wcscpy(p->szName + p->offsServer, pi->pServerName);
144     else
145         p->szName[p->offsServer] = L'\0';
146 
147     *(WORD*)((char*)pidl + (size + 2)) = 0;
148     return pidl;
149 }
150 
151 /**************************************************************************
152  *  CPrintersEnum::CreatePrintersEnumList()
153  */
154 BOOL CPrintersEnum::CreatePrintersEnumList(DWORD dwFlags)
155 {
156     BOOL ret = TRUE;
157 
158     TRACE("(%p)->(flags=0x%08lx) \n", this, dwFlags);
159 
160     /* enumerate the folders */
161     if (dwFlags & SHCONTF_NONFOLDERS)
162     {
163         DWORD needed = 0, num = 0, i;
164         PRINTER_INFO_4W *pi;
165 
166         EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &needed, &num);
167         if (!needed)
168             return ret;
169 
170         pi = (PRINTER_INFO_4W *)HeapAlloc(GetProcessHeap(), 0, needed);
171         if(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, 4, (LPBYTE)pi, needed, &needed, &num)) {
172             HeapFree(GetProcessHeap(), 0, pi);
173             return FALSE;
174         }
175 
176         for(i = 0; i < num; i++) {
177             LPITEMIDLIST pidl = _ILCreatePrinterItem(&pi[i]);
178             if (pidl)
179             {
180                 if (!AddToEnumList(pidl))
181                     SHFree(pidl);
182             }
183         }
184         HeapFree(GetProcessHeap(), 0, pi);
185     }
186     return ret;
187 }
188 
189 CPrinterFolder::CPrinterFolder()
190 {
191     pidlRoot = NULL;
192     dwAttributes = 0;
193     pclsid = NULL;
194 }
195 
196 CPrinterFolder::~CPrinterFolder()
197 {
198     TRACE("-- destroying IShellFolder(%p)\n", this);
199     if (pidlRoot)
200         SHFree(pidlRoot);
201 }
202 
203 /**************************************************************************
204  *    CPrinterFolder::ParseDisplayName
205  *
206  * This is E_NOTIMPL in Windows too.
207  */
208 HRESULT WINAPI CPrinterFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
209         DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes)
210 {
211     TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
212           this, hwndOwner, pbc, lpszDisplayName, debugstr_w(lpszDisplayName),
213           pchEaten, ppidl, pdwAttributes);
214 
215     *ppidl = 0;
216     if (pchEaten)
217         *pchEaten = 0;
218 
219     return E_NOTIMPL;
220 }
221 
222 static PIDLPrinterStruct * _ILGetPrinterStruct(LPCITEMIDLIST pidl)
223 {
224     LPPIDLDATA pdata = _ILGetDataPointer(pidl);
225 
226     if (pdata && pdata->type == 0x00)
227         return (PIDLPrinterStruct*) & (pdata->u.cfont);
228 
229     return NULL;
230 }
231 
232 /**************************************************************************
233  *        CPrinterFolder::EnumObjects
234  */
235 HRESULT WINAPI CPrinterFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST * ppEnumIDList)
236 {
237     return ShellObjectCreatorInit<CPrintersEnum>(hwndOwner, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
238 }
239 
240 /**************************************************************************
241  *        CPrinterFolder::BindToObject
242  */
243 HRESULT WINAPI CPrinterFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut)
244 {
245     return E_NOTIMPL;
246 }
247 
248 /**************************************************************************
249  *    ISF_Printers_fnBindToStorage
250  */
251 HRESULT WINAPI CPrinterFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut)
252 {
253     FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n",
254            this, pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
255 
256     *ppvOut = NULL;
257     return E_NOTIMPL;
258 }
259 
260 /**************************************************************************
261  *     CPrinterFolder::CompareIDs
262  */
263 HRESULT WINAPI CPrinterFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
264 {
265     return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
266 }
267 
268 /**************************************************************************
269  *    CPrinterFolder::CreateViewObject
270  */
271 HRESULT WINAPI CPrinterFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
272 {
273     CComPtr<IShellView> pShellView;
274     HRESULT hr = E_INVALIDARG;
275 
276     TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
277           hwndOwner, shdebugstr_guid (&riid), ppvOut);
278 
279     if (!ppvOut)
280         return hr;
281 
282     *ppvOut = NULL;
283 
284     if (IsEqualIID(riid, IID_IDropTarget))
285     {
286         WARN("IDropTarget not implemented\n");
287         hr = E_NOTIMPL;
288     }
289     else if(IsEqualIID(riid, IID_IContextMenu))
290     {
291         WARN("IContextMenu not implemented\n");
292         hr = E_NOTIMPL;
293     }
294     else if(IsEqualIID(riid, IID_IShellView))
295     {
296         SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
297         hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
298     }
299     TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
300     return hr;
301 }
302 
303 /**************************************************************************
304  *  CPrinterFolder::GetAttributesOf
305  */
306 HRESULT WINAPI CPrinterFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut)
307 {
308     static const DWORD dwPrintersAttributes =
309         SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_CANRENAME | SFGAO_CANDELETE;
310     HRESULT hr = S_OK;
311 
312     FIXME ("(%p)->(cidl=%d apidl=%p mask=0x%08lx): stub\n",
313            this, cidl, apidl, *rgfInOut);
314 
315     *rgfInOut &= dwPrintersAttributes;
316 
317     *rgfInOut &= ~SFGAO_VALIDATE;
318 
319     TRACE ("-- result=0x%08x\n", *rgfInOut);
320     return hr;
321 }
322 
323 /**************************************************************************
324  *    CPrinterFolder::GetUIObjectOf
325  *
326  * PARAMETERS
327  *  HWND           hwndOwner, //[in ] Parent window for any output
328  *  UINT           cidl,      //[in ] array size
329  *  LPCITEMIDLIST* apidl,     //[in ] simple pidl array
330  *  REFIID         riid,      //[in ] Requested Interface
331  *  UINT*          prgfInOut, //[   ] reserved
332  *  LPVOID*        ppvObject) //[out] Resulting Interface
333  *
334  */
335 HRESULT WINAPI CPrinterFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
336         REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
337 {
338     LPVOID pObj = NULL;
339     HRESULT hr = E_INVALIDARG;
340 
341     TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
342            this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
343 
344     if (!ppvOut)
345         return hr;
346 
347     *ppvOut = NULL;
348 
349     if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && cidl == 1)
350         hr = CPrintersExtractIconW_CreateInstane(apidl[0], riid, &pObj);
351     else
352         hr = E_NOINTERFACE;
353 
354     if (SUCCEEDED(hr) && !pObj)
355         hr = E_OUTOFMEMORY;
356 
357     *ppvOut = pObj;
358     TRACE ("(%p)->hr=0x%08lx\n", this, hr);
359     return hr;
360 }
361 
362 /**************************************************************************
363  *    CPrinterFolder::GetDisplayNameOf
364  *
365  */
366 HRESULT WINAPI CPrinterFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
367 {
368     PIDLPrinterStruct * p;
369 
370     TRACE ("(%p)->(pidl=%p,0x%08lx,%p)\n", this, pidl, dwFlags, strRet);
371     pdump (pidl);
372 
373     if (!strRet)
374     {
375         WARN("no strRet\n");
376         return E_INVALIDARG;
377     }
378 
379     p = _ILGetPrinterStruct(pidl);
380     if (!p)
381     {
382         ERR("no printer struct\n");
383         return E_INVALIDARG;
384     }
385 
386     return SHSetStrRet(strRet, p->szName);
387 }
388 
389 /**************************************************************************
390  *  CPrinterFolder::SetNameOf
391  *  Changes the name of a file object or subfolder, possibly changing its item
392  *  identifier in the process.
393  *
394  * PARAMETERS
395  *  HWND          hwndOwner,  //[in ] Owner window for output
396  *  LPCITEMIDLIST pidl,       //[in ] simple pidl of item to change
397  *  LPCOLESTR     lpszName,   //[in ] the items new display name
398  *  DWORD         dwFlags,    //[in ] SHGNO formatting flags
399  *  LPITEMIDLIST* ppidlOut)   //[out] simple pidl returned
400  */
401 HRESULT WINAPI CPrinterFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,    /* simple pidl */
402         LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
403 {
404     FIXME("(%p)->(%p,pidl=%p,%s,%lu,%p)\n", this, hwndOwner, pidl,
405           debugstr_w (lpName), dwFlags, pPidlOut);
406 
407     return E_FAIL;
408 }
409 
410 HRESULT WINAPI CPrinterFolder::GetDefaultSearchGUID(GUID *pguid)
411 {
412     FIXME("(%p)\n", this);
413     return E_NOTIMPL;
414 }
415 
416 HRESULT WINAPI CPrinterFolder::EnumSearches(IEnumExtraSearch **ppenum)
417 {
418     FIXME("(%p)\n", this);
419     return E_NOTIMPL;
420 }
421 
422 HRESULT WINAPI CPrinterFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
423 {
424     if (pSort)
425         *pSort = 0;
426     if (pDisplay)
427         *pDisplay = 0;
428 
429     return S_OK;
430 }
431 
432 HRESULT WINAPI CPrinterFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
433 {
434     if (!pcsFlags || iColumn >= PrinterSHELLVIEWCOLUMNS)
435         return E_INVALIDARG;
436     *pcsFlags = PrinterSFHeader[iColumn].pcsFlags;
437     return S_OK;
438 
439 }
440 
441 HRESULT WINAPI CPrinterFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
442 {
443     FIXME("(%p): stub\n", this);
444 
445     return E_NOTIMPL;
446 }
447 
448 HRESULT WINAPI CPrinterFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
449 {
450     TRACE("(%p)->(%p %i %p): stub\n", this, pidl, iColumn, psd);
451 
452     if (iColumn >= PrinterSHELLVIEWCOLUMNS)
453         return E_FAIL;
454 
455     psd->fmt = PrinterSFHeader[iColumn].fmt;
456     psd->cxChar = PrinterSFHeader[iColumn].cxChar;
457     if (pidl == NULL)
458         return SHSetStrRet(&psd->str, PrinterSFHeader[iColumn].colnameid);
459 
460     if (iColumn == COLUMN_NAME)
461         return GetDisplayNameOf(pidl, SHGDN_NORMAL, &psd->str);
462 
463     psd->str.uType = STRRET_CSTR;
464     psd->str.cStr[0] = '\0';
465 
466     return E_NOTIMPL;
467 }
468 
469 HRESULT WINAPI CPrinterFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
470 {
471     FIXME ("(%p): stub\n", this);
472     return E_NOTIMPL;
473 }
474 
475 /************************************************************************
476  *    CPrinterFolder::GetClassID
477  */
478 HRESULT WINAPI CPrinterFolder::GetClassID(CLSID *lpClassId)
479 {
480     TRACE ("(%p)\n", this);
481 
482     *lpClassId = CLSID_Printers;
483 
484     return S_OK;
485 }
486 
487 /************************************************************************
488  *    CPrinterFolder::Initialize
489  */
490 HRESULT WINAPI CPrinterFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
491 {
492     if (pidlRoot)
493         SHFree((LPVOID)pidlRoot);
494 
495     pidlRoot = ILClone(pidl);
496     return S_OK;
497 }
498 
499 /**************************************************************************
500  *    CPrinterFolder::GetCurFolder
501  */
502 HRESULT WINAPI CPrinterFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
503 {
504     TRACE ("(%p)->(%p)\n", this, pidl);
505 
506     *pidl = ILClone (pidlRoot);
507     return S_OK;
508 }
509