1 /*
2  * PROJECT:     ReactOS Local Spooler
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Functions related to Ports of the Print Monitors
5  * COPYRIGHT:   Copyright 2015-2017 Colin Finck (colin@reactos.org)
6  */
7 
8 #include "precomp.h"
9 
10 // Local Variables
11 static LIST_ENTRY _PortList;
12 
13 
14 PLOCAL_PORT
FindPort(PCWSTR pwszName)15 FindPort(PCWSTR pwszName)
16 {
17     PLIST_ENTRY pEntry;
18     PLOCAL_PORT pPort;
19 
20     TRACE("FindPort(%S)\n", pwszName);
21 
22     if (!pwszName)
23         return NULL;
24 
25     for (pEntry = _PortList.Flink; pEntry != &_PortList; pEntry = pEntry->Flink)
26     {
27         pPort = CONTAINING_RECORD(pEntry, LOCAL_PORT, Entry);
28 
29         if (_wcsicmp(pPort->pwszName, pwszName) == 0)
30             return pPort;
31     }
32 
33     return NULL;
34 }
35 
36 BOOL
CreatePortEntry(PCWSTR pwszName,PLOCAL_PRINT_MONITOR pPrintMonitor)37 CreatePortEntry( PCWSTR pwszName, PLOCAL_PRINT_MONITOR pPrintMonitor )
38 {
39     PLOCAL_PORT pPort;
40     DWORD cbPortName = (wcslen( pwszName ) + 1) * sizeof(WCHAR);
41 
42     // Create a new LOCAL_PORT structure for it.
43     pPort = DllAllocSplMem(sizeof(LOCAL_PORT) + cbPortName);
44     if (!pPort)
45     {
46         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
47         return FALSE;
48     }
49 
50     pPort->pPrintMonitor = pPrintMonitor;
51     pPort->pwszName = wcscpy( (PWSTR)(pPort+1), pwszName );
52 
53     // Insert it into the list and advance to the next port.
54     InsertTailList(&_PortList, &pPort->Entry);
55 
56     return TRUE;
57 }
58 
59 BOOL
InitializePortList(void)60 InitializePortList(void)
61 {
62     BOOL bReturnValue;
63     DWORD cbNeeded;
64     DWORD cbPortName;
65     DWORD dwErrorCode;
66     DWORD dwReturned;
67     DWORD i;
68     PLOCAL_PORT pPort;
69     PLOCAL_PRINT_MONITOR pPrintMonitor;
70     PLIST_ENTRY pEntry;
71     PPORT_INFO_1W p;
72     PPORT_INFO_1W pPortInfo1 = NULL;
73 
74     TRACE("InitializePortList()\n");
75 
76     // Initialize an empty list for our Ports.
77     InitializeListHead(&_PortList);
78 
79     // Loop through all Print Monitors.
80     for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
81     {
82         // Cleanup from the previous run.
83         if (pPortInfo1)
84         {
85             DllFreeSplMem(pPortInfo1);
86             pPortInfo1 = NULL;
87         }
88 
89         pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
90 
91         // Determine the required buffer size for EnumPorts.
92         if (pPrintMonitor->bIsLevel2)
93             bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnEnumPorts(pPrintMonitor->hMonitor, NULL, 1, NULL, 0, &cbNeeded, &dwReturned);
94         else
95             bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnEnumPorts(NULL, 1, NULL, 0, &cbNeeded, &dwReturned);
96 
97         // Check the returned error code.
98         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
99         {
100             ERR("Print Monitor \"%S\" failed with error %lu on EnumPorts!\n", pPrintMonitor->pwszName, GetLastError());
101             continue;
102         }
103 
104         // Allocate a buffer large enough.
105         pPortInfo1 = DllAllocSplMem(cbNeeded);
106         if (!pPortInfo1)
107         {
108             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
109             ERR("DllAllocSplMem failed!\n");
110             goto Cleanup;
111         }
112 
113         // Get the ports handled by this monitor.
114         if (pPrintMonitor->bIsLevel2)
115             bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnEnumPorts(pPrintMonitor->hMonitor, NULL, 1, (PBYTE)pPortInfo1, cbNeeded, &cbNeeded, &dwReturned);
116         else
117             bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnEnumPorts(NULL, 1, (PBYTE)pPortInfo1, cbNeeded, &cbNeeded, &dwReturned);
118 
119         // Check the return value.
120         if (!bReturnValue)
121         {
122             ERR("Print Monitor \"%S\" failed with error %lu on EnumPorts!\n", pPrintMonitor->pwszName, GetLastError());
123             continue;
124         }
125 
126         // Loop through all returned ports.
127         p = pPortInfo1;
128 
129         for (i = 0; i < dwReturned; i++)
130         {
131             cbPortName = (wcslen(p->pName) + 1) * sizeof(WCHAR);
132 
133             // Create a new LOCAL_PORT structure for it.
134             pPort = DllAllocSplMem(sizeof(LOCAL_PORT) + cbPortName);
135             if (!pPort)
136             {
137                 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
138                 ERR("DllAllocSplMem failed!\n");
139                 goto Cleanup;
140             }
141 
142             pPort->pPrintMonitor = pPrintMonitor;
143             pPort->pwszName = (PWSTR)((PBYTE)pPort + sizeof(LOCAL_PORT));
144             CopyMemory(pPort->pwszName, p->pName, cbPortName);
145 
146             // Insert it into the list and advance to the next port.
147             InsertTailList(&_PortList, &pPort->Entry);
148             p++;
149         }
150     }
151 
152     dwErrorCode = ERROR_SUCCESS;
153 
154 Cleanup:
155     // Inside the loop
156     if (pPortInfo1)
157         DllFreeSplMem(pPortInfo1);
158 
159     SetLastError(dwErrorCode);
160     return (dwErrorCode == ERROR_SUCCESS);
161 }
162 
163 BOOL WINAPI
LocalEnumPorts(PWSTR pName,DWORD Level,PBYTE pPorts,DWORD cbBuf,PDWORD pcbNeeded,PDWORD pcReturned)164 LocalEnumPorts(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
165 {
166     BOOL bReturnValue = TRUE;
167     DWORD cbCallBuffer;
168     DWORD cbNeeded;
169     DWORD dwReturned;
170     PBYTE pCallBuffer;
171     PLOCAL_PRINT_MONITOR pPrintMonitor;
172     PLIST_ENTRY pEntry;
173 
174     TRACE("LocalEnumPorts(%S, %lu, %p, %lu, %p, %p)\n", pName, Level, pPorts, cbBuf, pcbNeeded, pcReturned);
175 
176     // Begin counting.
177     *pcbNeeded = 0;
178     *pcReturned = 0;
179 
180     // At the beginning, we have the full buffer available.
181     cbCallBuffer = cbBuf;
182     pCallBuffer = pPorts;
183 
184     // Loop through all Print Monitors.
185     for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
186     {
187         pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
188 
189         // Call the EnumPorts function of this Print Monitor.
190         cbNeeded = 0;
191         dwReturned = 0;
192 
193         if (pPrintMonitor->bIsLevel2)
194             bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnEnumPorts(pPrintMonitor->hMonitor, pName, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned);
195         else
196             bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnEnumPorts(pName, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned);
197 
198         // Add the returned counts to the total values.
199         *pcbNeeded += cbNeeded;
200         *pcReturned += dwReturned;
201 
202         // Reduce the available buffer size for the next call without risking an underflow.
203         if (cbNeeded < cbCallBuffer)
204             cbCallBuffer -= cbNeeded;
205         else
206             cbCallBuffer = 0;
207 
208         // Advance the buffer if the caller provided it.
209         if (pCallBuffer)
210             pCallBuffer += cbNeeded;
211     }
212 
213     return bReturnValue;
214 }
215 
216 BOOL WINAPI
LocalAddPortEx(PWSTR pName,DWORD Level,PBYTE lpBuffer,PWSTR lpMonitorName)217 LocalAddPortEx(PWSTR pName, DWORD Level, PBYTE lpBuffer, PWSTR lpMonitorName)
218 {
219     DWORD lres;
220     BOOL res = FALSE;
221     PLOCAL_PORT pPort;
222     PLOCAL_PRINT_MONITOR pPrintMonitor;
223     PORT_INFO_1W * pi = (PORT_INFO_1W *) lpBuffer;
224 
225     FIXME("LocalAddPortEx(%S, %lu, %p, %S)\n", pName, Level, lpBuffer, lpMonitorName);
226 
227     lres = copy_servername_from_name(pName, NULL);
228     if ( lres )
229     {
230         FIXME("server %s not supported\n", debugstr_w(pName));
231         SetLastError(ERROR_INVALID_PARAMETER);
232         return FALSE;
233     }
234 
235     if ( Level != 1 )
236     {
237         SetLastError(ERROR_INVALID_LEVEL);
238         return FALSE;
239     }
240 
241     if ((!pi) || (!lpMonitorName) || (!lpMonitorName[0]))
242     {
243         SetLastError(ERROR_INVALID_PARAMETER);
244         return FALSE;
245     }
246 
247     pPrintMonitor = FindPrintMonitor( lpMonitorName );
248     if (!pPrintMonitor )
249     {
250         SetLastError(ERROR_INVALID_PARAMETER);
251         return FALSE;
252     }
253 
254     pPort = FindPort( pi->pName );
255     if ( pPort )
256     {
257         SetLastError(ERROR_INVALID_PARAMETER);
258         return FALSE;
259     }
260 
261     if ( pPrintMonitor->bIsLevel2 && ((PMONITOR2)pPrintMonitor->pMonitor)->pfnAddPortEx )
262     {
263         res = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnAddPortEx(pPrintMonitor->hMonitor, pName, Level, lpBuffer, lpMonitorName);
264     }
265     else if ( !pPrintMonitor->bIsLevel2 && ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnAddPortEx )
266     {
267         res = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnAddPortEx(pName, Level, lpBuffer, lpMonitorName);
268     }
269     else
270     {
271         SetLastError(ERROR_INVALID_PARAMETER);
272     }
273 
274     if ( res )
275     {
276         res = CreatePortEntry( pi->pName, pPrintMonitor );
277     }
278 
279     return res;
280 }
281 
282 //
283 // Local (AP, CP & DP) is still around, seems to be a backup if a failure was encountered.. New way, WinSpool->LocalUI->XcvDataW.
284 //
285 BOOL WINAPI
LocalAddPort(LPWSTR pName,HWND hWnd,LPWSTR pMonitorName)286 LocalAddPort(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
287 {
288     DWORD lres;
289     BOOL res = FALSE;
290     PLOCAL_PRINT_MONITOR pPrintMonitor;
291 
292     FIXME("LocalAddPort(%S, %p, %s)\n", pName, hWnd, debugstr_w(pMonitorName));
293 
294     lres = copy_servername_from_name(pName, NULL);
295     if (lres)
296     {
297         FIXME("server %s not supported\n", debugstr_w(pName));
298         SetLastError(ERROR_INVALID_PARAMETER);
299         return FALSE;
300     }
301 
302     /* an empty Monitorname is Invalid */
303     if (!pMonitorName[0])
304     {
305         SetLastError(ERROR_NOT_SUPPORTED);
306         return FALSE;
307     }
308 
309     pPrintMonitor = FindPrintMonitor( pMonitorName );
310     if (!pPrintMonitor )
311     {
312         SetLastError(ERROR_INVALID_PARAMETER);
313         return FALSE;
314     }
315 
316     if ( pPrintMonitor->bIsLevel2 && ((PMONITOR2)pPrintMonitor->pMonitor)->pfnAddPort )
317     {
318         res = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnAddPort(pPrintMonitor->hMonitor, pName, hWnd, pMonitorName);
319     }
320     else if ( !pPrintMonitor->bIsLevel2 && ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnAddPort )
321     {
322         res = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnAddPort(pName, hWnd, pMonitorName);
323     }
324     else
325     {
326         SetLastError(ERROR_INVALID_PARAMETER);
327     }
328 
329     if ( res )
330     {
331         DWORD cbNeeded, cReturned, i;
332         PPORT_INFO_1 pPorts;
333 
334         //
335         // Play it safe,,, we know its Monitor2.... This is ReactOS.
336         //
337         if ( LocalEnumPorts( pName, 1, NULL, 0, &cbNeeded, &cReturned ) )
338         {
339             pPorts = DllAllocSplMem( cbNeeded );
340             if (pPorts)
341             {
342                 if ( LocalEnumPorts( pName, 1, (PBYTE)pPorts, cbNeeded, &cbNeeded, &cReturned ) )
343                 {
344                     for ( i = 0; i < cReturned; i++ )
345                     {
346                         if ( !FindPort( pPorts[i].pName ) )
347                         {
348                             CreatePortEntry( pPorts[i].pName, pPrintMonitor );
349                         }
350                     }
351                 }
352                 DllFreeSplMem( pPorts );
353             }
354         }
355     }
356 
357     return res;
358 }
359 
360 BOOL WINAPI
LocalConfigurePort(PWSTR pName,HWND hWnd,PWSTR pPortName)361 LocalConfigurePort(PWSTR pName, HWND hWnd, PWSTR pPortName)
362 {
363     LONG lres;
364     DWORD res;
365     PLOCAL_PORT pPrintPort;
366     PLOCAL_PRINT_MONITOR pPrintMonitor;
367 
368     FIXME("LocalConfigurePort(%S, %p, %S)\n", pName, hWnd, pPortName);
369 
370     lres = copy_servername_from_name(pName, NULL);
371     if (lres)
372     {
373         FIXME("server %s not supported\n", debugstr_w(pName));
374         SetLastError(ERROR_INVALID_NAME);
375         return FALSE;
376     }
377 
378     /* an empty Portname is Invalid, but can popup a Dialog */
379     if (!pPortName[0])
380     {
381         SetLastError(ERROR_NOT_SUPPORTED);
382         return FALSE;
383     }
384 
385     pPrintPort = FindPort(pPortName);
386     if (!pPrintPort )
387     {
388         SetLastError(ERROR_INVALID_PARAMETER);
389         return FALSE;
390     }
391 
392     pPrintMonitor = pPrintPort->pPrintMonitor;
393 
394     if ( pPrintMonitor->bIsLevel2 && ((PMONITOR2)pPrintMonitor->pMonitor)->pfnConfigurePort )
395     {
396         res = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnConfigurePort(pPrintMonitor->hMonitor, pName, hWnd, pPortName);
397     }
398     else if ( !pPrintMonitor->bIsLevel2 && ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnConfigurePort )
399     {
400         res = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnConfigurePort(pName, hWnd, pPortName);
401     }
402     else
403     {
404         SetLastError(ERROR_INVALID_PARAMETER);
405     }
406 
407     return res;
408 }
409 
410 BOOL WINAPI
LocalDeletePort(PWSTR pName,HWND hWnd,PWSTR pPortName)411 LocalDeletePort(PWSTR pName, HWND hWnd, PWSTR pPortName)
412 {
413     LONG lres;
414     DWORD res = FALSE;
415     PLOCAL_PORT pPrintPort;
416     PLOCAL_PRINT_MONITOR pPrintMonitor;
417 
418     FIXME("LocalDeletePort(%S, %p, %S)\n", pName, hWnd, pPortName);
419 
420     lres = copy_servername_from_name(pName, NULL);
421     if (lres)
422     {
423         FIXME("server %s not supported\n", debugstr_w(pName));
424         SetLastError(ERROR_INVALID_NAME);
425         return FALSE;
426     }
427 
428     if (!pPortName[0])
429     {
430         SetLastError(ERROR_NOT_SUPPORTED);
431         return FALSE;
432     }
433 
434     pPrintPort = FindPort(pPortName);
435     if (!pPrintPort )
436     {
437         SetLastError(ERROR_INVALID_PARAMETER);
438         return FALSE;
439     }
440 
441     pPrintMonitor = pPrintPort->pPrintMonitor;
442 
443     if ( pPrintMonitor->bIsLevel2 && ((PMONITOR2)pPrintMonitor->pMonitor)->pfnDeletePort )
444     {
445         res = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnDeletePort(pPrintMonitor->hMonitor, pName, hWnd, pPortName);
446     }
447     else if ( !pPrintMonitor->bIsLevel2 && ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnDeletePort )
448     {
449         res = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnDeletePort(pName, hWnd, pPortName);
450     }
451 
452     RemoveEntryList(&pPrintPort->Entry);
453 
454     DllFreeSplMem(pPrintPort);
455 
456     return res;
457 }
458 
459 BOOL WINAPI
LocalSetPort(PWSTR pName,PWSTR pPortName,DWORD dwLevel,PBYTE pPortInfo)460 LocalSetPort(PWSTR pName, PWSTR pPortName, DWORD dwLevel, PBYTE pPortInfo)
461 {
462     LONG lres;
463     DWORD res = 0;
464     PPORT_INFO_3W ppi3w = (PPORT_INFO_3W)pPortInfo;
465     PLOCAL_PORT pPrintPort;
466 
467     TRACE("LocalSetPort(%S, %S, %lu, %p)\n", pName, pPortName, dwLevel, pPortInfo);
468 
469     lres = copy_servername_from_name(pName, NULL);
470     if (lres)
471     {
472         FIXME("server %s not supported\n", debugstr_w(pName));
473         SetLastError(ERROR_INVALID_NAME);
474         return FALSE;
475     }
476 
477     if ((dwLevel < 1) || (dwLevel > 2))
478     {
479         SetLastError(ERROR_INVALID_LEVEL);
480         return FALSE;
481     }
482 
483     if ( !ppi3w )
484     {
485         SetLastError(ERROR_INVALID_PARAMETER);
486         return FALSE;
487     }
488 
489     pPrintPort = FindPort(pPortName);
490     if ( !pPrintPort )
491     {
492         SetLastError(ERROR_UNKNOWN_PORT);
493         return FALSE;
494     }
495 
496     FIXME("Add Status Support to Local Ports!\n");
497 
498     return res;
499 }
500