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 = 0;
106     SIZE_T cchServerName = 0;
107 
108     if (pi->pPrinterName)
109         cchPrinterName = wcslen(pi->pPrinterName);
110     if (pi->pServerName)
111         cchServerName = wcslen(pi->pServerName);
112     if ((cchPrinterName + cchServerName) > (MAXUSHORT - 2))
113     {
114         return NULL;
115     }
116 
117     tmp.type = 0x00;
118     tmp.u.cprinter.dummy = 0xFF;
119     if (pi->pPrinterName)
120         tmp.u.cprinter.offsServer = cchPrinterName + 1;
121     else
122         tmp.u.cprinter.offsServer = 1;
123 
124     size += tmp.u.cprinter.offsServer * sizeof(WCHAR);
125     if (pi->pServerName)
126         size += (cchServerName + 1) * sizeof(WCHAR);
127     else
128         size += sizeof(WCHAR);
129 
130     pidl = (LPITEMIDLIST)SHAlloc(size + 4);
131     if (!pidl)
132         return pidl;
133 
134     pidl->mkid.cb = size + 2;
135     memcpy(pidl->mkid.abID, &tmp, 2 + size0);
136 
137     p = &((PIDLDATA*)pidl->mkid.abID)->u.cprinter;
138 
139     p->Attributes = pi->Attributes;
140     if (pi->pPrinterName)
141         wcscpy(p->szName, pi->pPrinterName);
142     else
143         p->szName[0] = L'\0';
144 
145     if (pi->pServerName)
146         wcscpy(p->szName + p->offsServer, pi->pServerName);
147     else
148         p->szName[p->offsServer] = L'\0';
149 
150     *(WORD*)((char*)pidl + (size + 2)) = 0;
151     return pidl;
152 }
153 
154 /**************************************************************************
155  *  CPrintersEnum::CreatePrintersEnumList()
156  */
157 BOOL CPrintersEnum::CreatePrintersEnumList(DWORD dwFlags)
158 {
159     BOOL ret = TRUE;
160 
161     TRACE("(%p)->(flags=0x%08lx) \n", this, dwFlags);
162 
163     /* enumerate the folders */
164     if (dwFlags & SHCONTF_NONFOLDERS)
165     {
166         DWORD needed = 0, num = 0, i;
167         PRINTER_INFO_4W *pi;
168 
169         EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &needed, &num);
170         if (!needed)
171             return ret;
172 
173         pi = (PRINTER_INFO_4W *)HeapAlloc(GetProcessHeap(), 0, needed);
174         if(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, 4, (LPBYTE)pi, needed, &needed, &num)) {
175             HeapFree(GetProcessHeap(), 0, pi);
176             return FALSE;
177         }
178 
179         for(i = 0; i < num; i++) {
180             LPITEMIDLIST pidl = _ILCreatePrinterItem(&pi[i]);
181             if (pidl)
182             {
183                 if (!AddToEnumList(pidl))
184                     SHFree(pidl);
185             }
186         }
187         HeapFree(GetProcessHeap(), 0, pi);
188     }
189     return ret;
190 }
191 
192 CPrinterFolder::CPrinterFolder()
193 {
194     pidlRoot = NULL;
195     dwAttributes = 0;
196     pclsid = NULL;
197 }
198 
199 CPrinterFolder::~CPrinterFolder()
200 {
201     TRACE("-- destroying IShellFolder(%p)\n", this);
202     if (pidlRoot)
203         SHFree(pidlRoot);
204 }
205 
206 /**************************************************************************
207  *    CPrinterFolder::ParseDisplayName
208  *
209  * This is E_NOTIMPL in Windows too.
210  */
211 HRESULT WINAPI CPrinterFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
212         DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes)
213 {
214     TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
215           this, hwndOwner, pbc, lpszDisplayName, debugstr_w(lpszDisplayName),
216           pchEaten, ppidl, pdwAttributes);
217 
218     *ppidl = 0;
219     if (pchEaten)
220         *pchEaten = 0;
221 
222     return E_NOTIMPL;
223 }
224 
225 static PIDLPrinterStruct * _ILGetPrinterStruct(LPCITEMIDLIST pidl)
226 {
227     LPPIDLDATA pdata = _ILGetDataPointer(pidl);
228 
229     if (pdata && pdata->type == 0x00)
230         return (PIDLPrinterStruct*) & (pdata->u.cfont);
231 
232     return NULL;
233 }
234 
235 /**************************************************************************
236  *        CPrinterFolder::EnumObjects
237  */
238 HRESULT WINAPI CPrinterFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST * ppEnumIDList)
239 {
240     return ShellObjectCreatorInit<CPrintersEnum>(hwndOwner, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
241 }
242 
243 /**************************************************************************
244  *        CPrinterFolder::BindToObject
245  */
246 HRESULT WINAPI CPrinterFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut)
247 {
248     return E_NOTIMPL;
249 }
250 
251 /**************************************************************************
252  *    ISF_Printers_fnBindToStorage
253  */
254 HRESULT WINAPI CPrinterFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut)
255 {
256     FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n",
257            this, pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
258 
259     *ppvOut = NULL;
260     return E_NOTIMPL;
261 }
262 
263 /**************************************************************************
264  *     CPrinterFolder::CompareIDs
265  */
266 HRESULT WINAPI CPrinterFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
267 {
268     return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
269 }
270 
271 /**************************************************************************
272  *    CPrinterFolder::CreateViewObject
273  */
274 HRESULT WINAPI CPrinterFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
275 {
276     CComPtr<IShellView> pShellView;
277     HRESULT hr = E_INVALIDARG;
278 
279     TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
280           hwndOwner, shdebugstr_guid (&riid), ppvOut);
281 
282     if (!ppvOut)
283         return hr;
284 
285     *ppvOut = NULL;
286 
287     if (IsEqualIID(riid, IID_IDropTarget))
288     {
289         WARN("IDropTarget not implemented\n");
290         hr = E_NOTIMPL;
291     }
292     else if(IsEqualIID(riid, IID_IContextMenu))
293     {
294         WARN("IContextMenu not implemented\n");
295         hr = E_NOTIMPL;
296     }
297     else if(IsEqualIID(riid, IID_IShellView))
298     {
299         SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
300         hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
301     }
302     TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
303     return hr;
304 }
305 
306 /**************************************************************************
307  *  CPrinterFolder::GetAttributesOf
308  */
309 HRESULT WINAPI CPrinterFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut)
310 {
311     static const DWORD dwPrintersAttributes =
312         SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_CANRENAME | SFGAO_CANDELETE;
313     HRESULT hr = S_OK;
314 
315     FIXME ("(%p)->(cidl=%d apidl=%p mask=0x%08lx): stub\n",
316            this, cidl, apidl, *rgfInOut);
317 
318     *rgfInOut &= dwPrintersAttributes;
319 
320     *rgfInOut &= ~SFGAO_VALIDATE;
321 
322     TRACE ("-- result=0x%08x\n", *rgfInOut);
323     return hr;
324 }
325 
326 /**************************************************************************
327  *    CPrinterFolder::GetUIObjectOf
328  *
329  * PARAMETERS
330  *  HWND           hwndOwner, //[in ] Parent window for any output
331  *  UINT           cidl,      //[in ] array size
332  *  LPCITEMIDLIST* apidl,     //[in ] simple pidl array
333  *  REFIID         riid,      //[in ] Requested Interface
334  *  UINT*          prgfInOut, //[   ] reserved
335  *  LPVOID*        ppvObject) //[out] Resulting Interface
336  *
337  */
338 HRESULT WINAPI CPrinterFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
339         REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
340 {
341     LPVOID pObj = NULL;
342     HRESULT hr = E_INVALIDARG;
343 
344     TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
345            this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
346 
347     if (!ppvOut)
348         return hr;
349 
350     *ppvOut = NULL;
351 
352     if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && cidl == 1)
353         hr = CPrintersExtractIconW_CreateInstane(apidl[0], riid, &pObj);
354     else
355         hr = E_NOINTERFACE;
356 
357     if (SUCCEEDED(hr) && !pObj)
358         hr = E_OUTOFMEMORY;
359 
360     *ppvOut = pObj;
361     TRACE ("(%p)->hr=0x%08lx\n", this, hr);
362     return hr;
363 }
364 
365 /**************************************************************************
366  *    CPrinterFolder::GetDisplayNameOf
367  *
368  */
369 HRESULT WINAPI CPrinterFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
370 {
371     PIDLPrinterStruct * p;
372 
373     TRACE ("(%p)->(pidl=%p,0x%08lx,%p)\n", this, pidl, dwFlags, strRet);
374     pdump (pidl);
375 
376     if (!strRet)
377     {
378         WARN("no strRet\n");
379         return E_INVALIDARG;
380     }
381 
382     p = _ILGetPrinterStruct(pidl);
383     if (!p)
384     {
385         ERR("no printer struct\n");
386         return E_INVALIDARG;
387     }
388 
389     return SHSetStrRet(strRet, p->szName);
390 }
391 
392 /**************************************************************************
393  *  CPrinterFolder::SetNameOf
394  *  Changes the name of a file object or subfolder, possibly changing its item
395  *  identifier in the process.
396  *
397  * PARAMETERS
398  *  HWND          hwndOwner,  //[in ] Owner window for output
399  *  LPCITEMIDLIST pidl,       //[in ] simple pidl of item to change
400  *  LPCOLESTR     lpszName,   //[in ] the items new display name
401  *  DWORD         dwFlags,    //[in ] SHGNO formatting flags
402  *  LPITEMIDLIST* ppidlOut)   //[out] simple pidl returned
403  */
404 HRESULT WINAPI CPrinterFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,    /* simple pidl */
405         LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
406 {
407     FIXME("(%p)->(%p,pidl=%p,%s,%lu,%p)\n", this, hwndOwner, pidl,
408           debugstr_w (lpName), dwFlags, pPidlOut);
409 
410     return E_FAIL;
411 }
412 
413 HRESULT WINAPI CPrinterFolder::GetDefaultSearchGUID(GUID *pguid)
414 {
415     FIXME("(%p)\n", this);
416     return E_NOTIMPL;
417 }
418 
419 HRESULT WINAPI CPrinterFolder::EnumSearches(IEnumExtraSearch **ppenum)
420 {
421     FIXME("(%p)\n", this);
422     return E_NOTIMPL;
423 }
424 
425 HRESULT WINAPI CPrinterFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
426 {
427     if (pSort)
428         *pSort = 0;
429     if (pDisplay)
430         *pDisplay = 0;
431 
432     return S_OK;
433 }
434 
435 HRESULT WINAPI CPrinterFolder::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
436 {
437     if (!pcsFlags || iColumn >= PrinterSHELLVIEWCOLUMNS)
438         return E_INVALIDARG;
439     *pcsFlags = PrinterSFHeader[iColumn].colstate;
440     return S_OK;
441 
442 }
443 
444 HRESULT WINAPI CPrinterFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
445 {
446     FIXME("(%p): stub\n", this);
447 
448     return E_NOTIMPL;
449 }
450 
451 HRESULT WINAPI CPrinterFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
452 {
453     TRACE("(%p)->(%p %i %p): stub\n", this, pidl, iColumn, psd);
454 
455     if (iColumn >= PrinterSHELLVIEWCOLUMNS)
456         return E_FAIL;
457 
458     psd->fmt = PrinterSFHeader[iColumn].fmt;
459     psd->cxChar = PrinterSFHeader[iColumn].cxChar;
460     if (pidl == NULL)
461         return SHSetStrRet(&psd->str, PrinterSFHeader[iColumn].colnameid);
462 
463     if (iColumn == COLUMN_NAME)
464         return GetDisplayNameOf(pidl, SHGDN_NORMAL, &psd->str);
465 
466     psd->str.uType = STRRET_CSTR;
467     psd->str.cStr[0] = '\0';
468 
469     return E_NOTIMPL;
470 }
471 
472 HRESULT WINAPI CPrinterFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
473 {
474     FIXME ("(%p): stub\n", this);
475     return E_NOTIMPL;
476 }
477 
478 /************************************************************************
479  *    CPrinterFolder::GetClassID
480  */
481 HRESULT WINAPI CPrinterFolder::GetClassID(CLSID *lpClassId)
482 {
483     TRACE ("(%p)\n", this);
484 
485     *lpClassId = CLSID_Printers;
486 
487     return S_OK;
488 }
489 
490 /************************************************************************
491  *    CPrinterFolder::Initialize
492  */
493 HRESULT WINAPI CPrinterFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
494 {
495     if (pidlRoot)
496         SHFree((LPVOID)pidlRoot);
497 
498     pidlRoot = ILClone(pidl);
499     return S_OK;
500 }
501 
502 /**************************************************************************
503  *    CPrinterFolder::GetCurFolder
504  */
505 HRESULT WINAPI CPrinterFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
506 {
507     TRACE ("(%p)->(%p)\n", this, pidl);
508 
509     *pidl = ILClone (pidlRoot);
510     return S_OK;
511 }
512