1 /*
2 * PROJECT: ReactOS Local Port Monitor
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Main functions
5 * COPYRIGHT: Copyright 2015 Colin Finck (colin@reactos.org)
6 */
7
8 #include "precomp.h"
9
10 // Global Variables
11 DWORD cbLocalMonitor;
12 DWORD cbLocalPort;
13 PCWSTR pwszLocalMonitor;
14 PCWSTR pwszLocalPort;
15
16 // Local Constants
17 static MONITOR2 _MonitorFunctions = {
18 sizeof(MONITOR2), // cbSize
19 LocalmonEnumPorts, // pfnEnumPorts
20 LocalmonOpenPort, // pfnOpenPort
21 NULL, // pfnOpenPortEx
22 LocalmonStartDocPort, // pfnStartDocPort
23 LocalmonWritePort, // pfnWritePort
24 LocalmonReadPort, // pfnReadPort
25 LocalmonEndDocPort, // pfnEndDocPort
26 LocalmonClosePort, // pfnClosePort
27 LocalmonAddPort, // pfnAddPort moved to localui.dll since w2k, but~
28 LocalmonAddPortEx, // pfnAddPortEx
29 LocalmonConfigurePort, // pfnConfigurePort moved to localui.dll since w2k, but~
30 LocalmonDeletePort, // pfnDeletePort moved to localui.dll since w2k, but~
31 LocalmonGetPrinterDataFromPort, // pfnGetPrinterDataFromPort
32 LocalmonSetPortTimeOuts, // pfnSetPortTimeOuts
33 LocalmonXcvOpenPort, // pfnXcvOpenPort
34 LocalmonXcvDataPort, // pfnXcvDataPort
35 LocalmonXcvClosePort, // pfnXcvClosePort
36 LocalmonShutdown, // pfnShutdown
37 NULL, // pfnSendRecvBidiDataFromPort
38 };
39
40
41 /**
42 * @name _IsNEPort
43 *
44 * Checks if the given port name is a virtual Ne port.
45 * A virtual Ne port may appear in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports and can have the formats
46 * Ne00:, Ne01:, Ne-02:, Ne456:
47 * This check is extra picky to not cause false positives (like file name ports starting with "Ne").
48 *
49 * @param pwszPortName
50 * The port name to check.
51 *
52 * @return
53 * TRUE if this is definitely a virtual Ne port, FALSE if not.
54 */
55 static __inline BOOL
_IsNEPort(PCWSTR pwszPortName)56 _IsNEPort(PCWSTR pwszPortName)
57 {
58 PCWSTR p = pwszPortName;
59
60 // First character needs to be 'N' (uppercase or lowercase)
61 if (*p != L'N' && *p != L'n')
62 return FALSE;
63
64 // Next character needs to be 'E' (uppercase or lowercase)
65 p++;
66 if (*p != L'E' && *p != L'e')
67 return FALSE;
68
69 // An optional hyphen may follow now.
70 p++;
71 if (*p == L'-')
72 p++;
73
74 // Now an arbitrary number of digits may follow.
75 while (*p >= L'0' && *p <= L'9')
76 p++;
77
78 // Finally, the virtual Ne port must be terminated by a colon.
79 if (*p != ':')
80 return FALSE;
81
82 // If this is the end of the string, we have a virtual Ne port.
83 p++;
84 return (*p == L'\0');
85 }
86
87 static void
_LoadResources(HINSTANCE hinstDLL)88 _LoadResources(HINSTANCE hinstDLL)
89 {
90 LoadStringW(hinstDLL, IDS_LOCAL_MONITOR, (PWSTR)&pwszLocalMonitor, 0);
91 cbLocalMonitor = (wcslen(pwszLocalMonitor) + 1) * sizeof(WCHAR);
92
93 LoadStringW(hinstDLL, IDS_LOCAL_PORT, (PWSTR)&pwszLocalPort, 0);
94 cbLocalPort = (wcslen(pwszLocalPort) + 1) * sizeof(WCHAR);
95 }
96
97 BOOL WINAPI
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)98 DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
99 {
100 switch (fdwReason)
101 {
102 case DLL_PROCESS_ATTACH:
103 DisableThreadLibraryCalls(hinstDLL);
104 _LoadResources(hinstDLL);
105 break;
106 }
107
108 return TRUE;
109 }
110
111 void WINAPI
LocalmonShutdown(HANDLE hMonitor)112 LocalmonShutdown(HANDLE hMonitor)
113 {
114 PLOCALMON_HANDLE pLocalmon;
115 PLOCALMON_PORT pPort;
116 PLOCALMON_XCV pXcv;
117 PLIST_ENTRY pEntry;
118
119 TRACE("LocalmonShutdown(%p)\n", hMonitor);
120
121 pLocalmon = (PLOCALMON_HANDLE)hMonitor;
122
123 if ( pLocalmon->Sig != SIGLCMMON )
124 {
125 ERR("LocalmonShutdown : Invalid Monitor Handle\n",hMonitor);
126 return;
127 }
128
129 // Close all virtual file ports.
130 if (!IsListEmpty(&pLocalmon->FilePorts))
131 {
132 for (pEntry = pLocalmon->FilePorts.Flink; pEntry != &pLocalmon->FilePorts; pEntry = pEntry->Flink)
133 {
134 pPort = CONTAINING_RECORD(&pLocalmon->FilePorts.Flink, LOCALMON_PORT, Entry);
135 LocalmonClosePort((HANDLE)pPort);
136 }
137 }
138
139 // Do the same for the open Xcv ports.
140 if (!IsListEmpty(&pLocalmon->XcvHandles))
141 {
142 for (pEntry = pLocalmon->XcvHandles.Flink; pEntry != &pLocalmon->XcvHandles; pEntry = pEntry->Flink)
143 {
144 pXcv = CONTAINING_RECORD(pEntry, LOCALMON_XCV, Entry);
145 LocalmonXcvClosePort((HANDLE)pXcv);
146 }
147 }
148
149 // Now close all registry ports, remove them from the list and free their memory.
150 if (!IsListEmpty(&pLocalmon->RegistryPorts))
151 {
152 for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
153 {
154 pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
155 if ( LocalmonClosePort((HANDLE)pPort) ) continue;
156 RemoveEntryList(&pPort->Entry);
157 DllFreeSplMem(pPort);
158 }
159 }
160
161 // Finally clean the LOCALMON_HANDLE structure itself.
162 DeleteCriticalSection(&pLocalmon->Section);
163 DllFreeSplMem(pLocalmon);
164 }
165
166 PMONITOR2 WINAPI
InitializePrintMonitor2(PMONITORINIT pMonitorInit,PHANDLE phMonitor)167 InitializePrintMonitor2(PMONITORINIT pMonitorInit, PHANDLE phMonitor)
168 {
169 DWORD cchMaxPortName;
170 DWORD cchPortName;
171 DWORD dwErrorCode;
172 DWORD dwPortCount;
173 DWORD i;
174 HKEY hKey;
175 PMONITOR2 pReturnValue = NULL;
176 PLOCALMON_HANDLE pLocalmon;
177 PLOCALMON_PORT pPort = NULL;
178
179 TRACE("InitializePrintMonitor2(%p, %p)\n", pMonitorInit, phMonitor);
180
181 // Create a new LOCALMON_HANDLE structure.
182 pLocalmon = DllAllocSplMem(sizeof(LOCALMON_HANDLE));
183 pLocalmon->Sig = SIGLCMMON;
184 InitializeCriticalSection(&pLocalmon->Section);
185 InitializeListHead(&pLocalmon->FilePorts);
186 InitializeListHead(&pLocalmon->RegistryPorts);
187 InitializeListHead(&pLocalmon->XcvHandles);
188
189 // The Local Spooler Port Monitor doesn't need to care about the given registry key and functions.
190 // Instead it uses a well-known registry key for getting its information about local ports. Open this one.
191 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", 0, KEY_READ, &hKey);
192 if (dwErrorCode != ERROR_SUCCESS)
193 {
194 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
195 goto Cleanup;
196 }
197
198 // Get the number of ports and the length of the largest port name.
199 dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwPortCount, &cchMaxPortName, NULL, NULL, NULL);
200 if (dwErrorCode != ERROR_SUCCESS)
201 {
202 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
203 goto Cleanup;
204 }
205
206 // Loop through all ports.
207 for (i = 0; i < dwPortCount; i++)
208 {
209 // Allocate memory for a new LOCALMON_PORT structure and its name.
210 pPort = DllAllocSplMem(sizeof(LOCALMON_PORT) + (cchMaxPortName + 1) * sizeof(WCHAR));
211 if (!pPort)
212 {
213 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
214 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
215 goto Cleanup;
216 }
217
218 pPort->Sig = SIGLCMPORT;
219 pPort->pLocalmon = pLocalmon;
220 pPort->hFile = INVALID_HANDLE_VALUE;
221 pPort->pwszPortName = (PWSTR)(pPort+1);
222
223 // Get the port name.
224 cchPortName = cchMaxPortName + 1;
225 dwErrorCode = (DWORD)RegEnumValueW(hKey, i, pPort->pwszPortName, &cchPortName, NULL, NULL, NULL, NULL);
226 if (dwErrorCode != ERROR_SUCCESS)
227 {
228 ERR("RegEnumValueW failed with status %lu!\n", dwErrorCode);
229 goto Cleanup;
230 }
231
232 // pwszPortName can be one of the following to be valid for this Port Monitor:
233 // COMx: - Physical COM port
234 // LPTx: - Physical LPT port (or redirected one using "net use LPT1 ...")
235 // FILE: - Opens a prompt that asks for an output filename
236 // C:\bla.txt - Redirection into the file "C:\bla.txt"
237 // \\COMPUTERNAME\PrinterName - Redirection to a shared network printer installed as a local port
238 //
239 // We can't detect valid and invalid ones by the name, so we can only exclude empty ports and the virtual "Ne00:", "Ne01:", ... ports.
240 // Skip the invalid ones here.
241 if (!cchPortName || _IsNEPort(pPort->pwszPortName))
242 {
243 DllFreeSplMem(pPort);
244 pPort = NULL;
245 continue;
246 }
247
248 // Add it to the list.
249 InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry);
250 TRACE("InitializePrintMonitor2 Port : %s \n",debugstr_w(pPort->pwszPortName));
251
252 // Don't let the cleanup routine free this.
253 pPort = NULL;
254 }
255
256 // Return our handle and the Print Monitor functions.
257 *phMonitor = (HANDLE)pLocalmon;
258 pReturnValue = &_MonitorFunctions;
259 dwErrorCode = ERROR_SUCCESS;
260
261 Cleanup:
262 if (pPort)
263 DllFreeSplMem(pPort);
264
265 SetLastError(dwErrorCode);
266 return pReturnValue;
267 }
268