[LOCALSPL]
[reactos.git] / reactos / win32ss / printing / providers / localspl / printers.c
index 655cbb1..339c138 100644 (file)
@@ -2,7 +2,7 @@
  * PROJECT:     ReactOS Local Spooler
  * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
  * PURPOSE:     Functions related to Printers and printing
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
 // Global Variables
 SKIPLIST PrinterList;
 
+// Local Constants
+static DWORD dwPrinterInfo1Offsets[] = {
+    FIELD_OFFSET(PRINTER_INFO_1W, pName),
+    FIELD_OFFSET(PRINTER_INFO_1W, pComment),
+    FIELD_OFFSET(PRINTER_INFO_1W, pDescription),
+    MAXDWORD
+};
 
 /**
  * @name _PrinterListCompareRoutine
@@ -167,8 +174,14 @@ InitializePrinterList()
 
         pPrinter->pwszPrinterName = AllocSplStr(wszPrinterName);
         pPrinter->pPrintProcessor = pPrintProcessor;
+        pPrinter->pPort = pPort;
         InitializePrinterJobList(pPrinter);
 
+        // Get the location.
+        pPrinter->pwszLocation = AllocAndRegQueryWSZ(hSubKey, L"Location");
+        if (!pPrinter->pwszLocation)
+            continue;
+
         // Get the printer driver.
         pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver");
         if (!pPrinter->pwszPrinterDriver)
@@ -283,97 +296,176 @@ Cleanup:
     return (dwErrorCode == ERROR_SUCCESS);
 }
 
-
-DWORD
-_LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
+/**
+ * @name _LocalEnumPrintersCheckName
+ *
+ * Checks the Name parameter supplied to a call to EnumPrinters.
+ *
+ * @param Flags
+ * Flags parameter of EnumPrinters.
+ *
+ * @param Name
+ * Name parameter of EnumPrinters to check.
+ *
+ * @param pwszComputerName
+ * Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters.
+ * On return, it may contain a computer name to prepend in EnumPrinters depending on the case.
+ *
+ * @param pcchComputerName
+ * If a string to prepend is returned, this pointer receives its length in characters.
+ *
+ * @return
+ * ERROR_SUCCESS if processing in EnumPrinters can be continued.
+ * ERROR_INVALID_NAME if the Name parameter is invalid for the given flags and this Print Provider.
+ * Any other error code if GetComputerNameW fails. Error codes indicating failure should then be returned by EnumPrinters.
+ */
+static DWORD
+_LocalEnumPrintersCheckName(DWORD Flags, PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerName)
 {
-    const WCHAR wszComma[] = L",";
+    PCWSTR pName;
+    PCWSTR pComputerName;
 
-    DWORD cbName;
-    DWORD cbComment;
-    DWORD cbDescription;
-    DWORD cchComputerName = 0;
-    DWORD dwErrorCode;
-    DWORD i;
-    PBYTE pPrinterInfo;
-    PBYTE pPrinterString;
-    PSKIPLIST_NODE pNode;
-    PLOCAL_PRINTER pPrinter;
-    PRINTER_INFO_1W PrinterInfo1;
-    WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1];
-
-    DWORD dwOffsets[] = {
-        FIELD_OFFSET(PRINTER_INFO_1W, pName),
-        FIELD_OFFSET(PRINTER_INFO_1W, pDescription),
-        FIELD_OFFSET(PRINTER_INFO_1W, pComment),
-        MAXDWORD
-    };
+    // If there is no Name parameter to check, we can just continue in EnumPrinters.
+    if (!Name)
+        return ERROR_SUCCESS;
 
-    if (Flags & PRINTER_ENUM_NAME)
+    // Check if Name does not begin with two backslashes (required for specifying Computer Names).
+    if (Name[0] != L'\\' || Name[1] != L'\\')
     {
-        if (Name)
+        if (Flags & PRINTER_ENUM_NAME)
         {
-            // The user supplied a Computer Name (with leading double backslashes) or Print Provider Name.
-            // Only process what's directed at us and dismiss every other request with ERROR_INVALID_NAME.
-            if (Name[0] == L'\\' && Name[1] == L'\\')
-            {
-                // Prepend slashes to the computer name.
-                wszComputerName[0] = L'\\';
-                wszComputerName[1] = L'\\';
+            // If PRINTER_ENUM_NAME is specified, any given Name parameter may only contain the
+            // Print Provider Name or the local Computer Name.
 
-                // Get the local computer name for comparison.
-                cchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
-                if (!GetComputerNameW(&wszComputerName[2], &cchComputerName))
-                {
-                    dwErrorCode = GetLastError();
-                    ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
-                    goto Cleanup;
-                }
+            // Compare with the Print Provider Name.
+            if (wcsicmp(Name, wszPrintProviderInfo[0]) == 0)
+                return ERROR_SUCCESS;
 
-                // Add the leading slashes to the total length.
-                cchComputerName += 2;
+            // Dismiss anything else.
+            return ERROR_INVALID_NAME;
+        }
+        else
+        {
+            // If PRINTER_ENUM_NAME is not specified, we just ignore anything that is not a Computer Name.
+            return ERROR_SUCCESS;
+        }
+    }
 
-                // Now compare this with the local computer name and reject if it doesn't match.
-                if (wcsicmp(&Name[2], &wszComputerName[2]) != 0)
-                {
-                    dwErrorCode = ERROR_INVALID_NAME;
-                    goto Cleanup;
-                }
+    // Prepend the backslashes to the output computer name.
+    pwszComputerName[0] = L'\\';
+    pwszComputerName[1] = L'\\';
 
-                // Add a trailing backslash to wszComputerName, which will later be prepended in front of the printer names.
-                wszComputerName[cchComputerName++] = L'\\';
-                wszComputerName[cchComputerName] = 0;
+    // Get the local computer name for comparison.
+    *pcchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
+    if (!GetComputerNameW(&pwszComputerName[2], pcchComputerName))
+    {
+        ERR("GetComputerNameW failed with error %lu!\n", GetLastError());
+        return GetLastError();
+    }
+
+    // Add the leading slashes to the total length.
+    *pcchComputerName += 2;
+
+    // Compare both names.
+    pComputerName = &pwszComputerName[2];
+    pName = &Name[2];
+    for (;;)
+    {
+        // Are we at the end of the local Computer Name string?
+        if (!*pComputerName)
+        {
+            // Are we also at the end of the supplied Name parameter?
+            // A terminating NUL character and a backslash are both treated as the end, but they are treated differently.
+            if (!*pName)
+            {
+                // If both names match and Name ends with a NUL character, the computer name will be prepended in EnumPrinters.
+                // Add a trailing backslash for that.
+                pwszComputerName[(*pcchComputerName)++] = L'\\';
+                pwszComputerName[*pcchComputerName] = 0;
+                return ERROR_SUCCESS;
             }
-            else if (wcsicmp(Name, wszPrintProviderInfo[0]) != 0)
+            else if (*pName == L'\\')
             {
-                // The user supplied a name that cannot be processed by the local print provider.
-                dwErrorCode = ERROR_INVALID_NAME;
-                goto Cleanup;
+                if (Flags & PRINTER_ENUM_NAME)
+                {
+                    // If PRINTER_ENUM_NAME is specified and a Name parameter is given, it must be exactly the local
+                    // Computer Name with two backslashes prepended. Anything else (like "\\COMPUTERNAME\") is dismissed.
+                    return ERROR_INVALID_NAME;
+                }
+                else
+                {
+                    // If PRINTER_ENUM_NAME is not specified and a Name parameter is given, it may also end with a backslash.
+                    // Only the Computer Name between the backslashes is checked then.
+                    // This is largely undocumented, but verified by tests (see winspool_apitest).
+                    // In this case, no computer name is prepended in EnumPrinters though.
+                    *pwszComputerName = 0;
+                    *pcchComputerName = 0;
+                    return ERROR_SUCCESS;
+                }
             }
         }
-        else
-        {
-            // The caller wants information about this Print Provider.
-            // spoolss packs this into an array of information about all Print Providers.
-            *pcbNeeded = sizeof(PRINTER_INFO_1W);
 
-            for (i = 0; i < 3; i++)
-                *pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR);
+        // Compare both Computer Names case-insensitively and reject with ERROR_INVALID_NAME if they don't match.
+        if (towlower(*pName) != towlower(*pComputerName))
+            return ERROR_INVALID_NAME;
 
-            // Check if the supplied buffer is large enough.
-            if (cbBuf < *pcbNeeded)
-            {
-                dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
-                goto Cleanup;
-            }
+        pName++;
+        pComputerName++;
+    }
+}
 
-            // Copy over the print processor information.
-            ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
-            PackStrings(wszPrintProviderInfo, pPrinterEnum, dwOffsets, &pPrinterEnum[*pcbNeeded]);
-            *pcReturned = 1;
-            dwErrorCode = ERROR_SUCCESS;
-            goto Cleanup;
-        }
+static DWORD
+_DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    int i;
+
+    // Count the needed bytes for Print Provider information.
+    *pcbNeeded = sizeof(PRINTER_INFO_1W);
+
+    for (i = 0; i < 3; i++)
+        *pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR);
+
+    // Check if the supplied buffer is large enough.
+    if (cbBuf < *pcbNeeded)
+        return ERROR_INSUFFICIENT_BUFFER;
+
+    // Copy over the Print Provider information.
+    ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
+    PackStrings(wszPrintProviderInfo, pPrinterEnum, dwPrinterInfo1Offsets, &pPrinterEnum[*pcbNeeded]);
+    *pcReturned = 1;
+
+    return ERROR_SUCCESS;
+}
+
+static DWORD
+_LocalEnumPrintersLevel0(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    return ERROR_INVALID_LEVEL;
+}
+
+static DWORD
+_LocalEnumPrintersLevel1(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    const WCHAR wszComma[] = L",";
+
+    size_t cbName;
+    size_t cbComment;
+    size_t cbDescription;
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE pPrinterInfo;
+    PBYTE pPrinterStrings;
+    PSKIPLIST_NODE pNode;
+    PLOCAL_PRINTER pPrinter;
+    PWSTR p;
+    PWSTR pwszStrings[3];
+
+    if (Flags & PRINTER_ENUM_NAME && !Name)
+    {
+        // The caller wants information about this Print Provider.
+        // spoolss packs this into an array of information about all Print Providers.
+        dwErrorCode = _DumpLevel1PrintProviderInformation(pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
+        goto Cleanup;
     }
 
     // Count the required buffer size and the number of printers.
@@ -383,14 +475,17 @@ _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cb
     {
         pPrinter = (PLOCAL_PRINTER)pNode->Element;
 
-        // This looks wrong, but is totally right. PRINTER_INFO_1W has three members pName, pComment and pDescription.
-        // But pComment equals the "Description" registry value while pDescription is concatenated out of pName and pComment.
+        // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
+        if (Flags & PRINTER_ENUM_SHARED)
+            continue;
+
+        // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
         // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
-        cbName = (wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+        cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
         cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
-        cbDescription = cchComputerName * sizeof(WCHAR) + cbName + cbComment + sizeof(WCHAR);
+        cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
 
-        *pcbNeeded += sizeof(PRINTER_INFO_1W) + cchComputerName * sizeof(WCHAR) + cbName + cbComment + cbDescription;
+        *pcbNeeded += sizeof(PRINTER_INFO_1W) + cbName + cbComment + cbDescription;
         i++;
     }
 
@@ -401,49 +496,53 @@ _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cb
         goto Cleanup;
     }
 
-    // Put the strings right after the last PRINTER_INFO_1W structure.
-    // Due to all the required string processing, we can't just use PackStrings here :(
+    // Initialize the variables for filling the output buffer using PackStrings.
     pPrinterInfo = pPrinterEnum;
-    pPrinterString = pPrinterEnum + i * sizeof(PRINTER_INFO_1W);
+    pPrinterStrings = &pPrinterEnum[*pcbNeeded];
 
-    // Copy over the printer information.
+    // Copy over the Printer information.
     for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
     {
         pPrinter = (PLOCAL_PRINTER)pNode->Element;
 
-        // FIXME: As for now, the Flags member returns no information.
-        PrinterInfo1.Flags = 0;
+        // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
+        if (Flags & PRINTER_ENUM_SHARED)
+            continue;
 
-        // Copy the printer name.
-        PrinterInfo1.pName = (PWSTR)pPrinterString;
-        CopyMemory(pPrinterString, wszComputerName, cchComputerName * sizeof(WCHAR));
-        pPrinterString += cchComputerName * sizeof(WCHAR);
-        cbName = (wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
-        CopyMemory(pPrinterString, pPrinter->pwszPrinterName, cbName);
-        pPrinterString += cbName;
+        // Indicate that this is a Printer.
+        ((PPRINTER_INFO_1W)pPrinterInfo)->Flags = PRINTER_ENUM_ICON8;
 
-        // Copy the printer comment (equals the "Description" registry value).
-        PrinterInfo1.pComment = (PWSTR)pPrinterString;
+        // Calculate the string lengths.
+        cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
         cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
-        CopyMemory(pPrinterString, pPrinter->pwszDescription, cbComment);
-        pPrinterString += cbComment;
-
-        // Copy the description, which for PRINTER_INFO_1W has the form "Name,Comment,"
-        PrinterInfo1.pDescription = (PWSTR)pPrinterString;
-        CopyMemory(pPrinterString, wszComputerName, cchComputerName * sizeof(WCHAR));
-        pPrinterString += cchComputerName * sizeof(WCHAR);
-        CopyMemory(pPrinterString, pPrinter->pwszPrinterName, cbName - sizeof(WCHAR));
-        pPrinterString += cbName - sizeof(WCHAR);
-        CopyMemory(pPrinterString, wszComma, sizeof(WCHAR));
-        pPrinterString += sizeof(WCHAR);
-        CopyMemory(pPrinterString, pPrinter->pwszDescription, cbComment - sizeof(WCHAR));
-        pPrinterString += cbComment - sizeof(WCHAR);
-        CopyMemory(pPrinterString, wszComma, sizeof(wszComma));
-        pPrinterString += sizeof(wszComma);
-                
+        cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
+
+        // Copy the Printer Name.
+        pwszStrings[0] = DllAllocSplMem(cbName);
+        p = pwszStrings[0];
+        StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
+        StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
+
+        // Copy the Printer comment (equals the "Description" registry value).
+        pwszStrings[1] = pPrinter->pwszDescription;
+
+        // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
+        pwszStrings[2] = DllAllocSplMem(cbDescription);
+        p = pwszStrings[2];
+        StringCbCopyExW(p, cbDescription, wszComputerName, &p, &cbDescription, 0);
+        StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterName, &p, &cbDescription, 0);
+        StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
+        StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterDriver, &p, &cbDescription, 0);
+        StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
+        StringCbCopyExW(p, cbDescription, pPrinter->pwszLocation, &p, &cbDescription, 0);
+
         // Finally copy the structure and advance to the next one in the output buffer.
-        CopyMemory(pPrinterInfo, &PrinterInfo1, sizeof(PRINTER_INFO_1W));
+        pPrinterStrings = PackStrings(pwszStrings, pPrinterInfo, dwPrinterInfo1Offsets, pPrinterStrings);
         pPrinterInfo += sizeof(PRINTER_INFO_1W);
+
+        // Free the memory for temporary strings.
+        DllFreeSplMem(pwszStrings[0]);
+        DllFreeSplMem(pwszStrings[2]);
     }
 
     *pcReturned = i;
@@ -453,35 +552,85 @@ Cleanup:
     return dwErrorCode;
 }
 
+static DWORD
+_LocalEnumPrintersLevel2(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    return ERROR_INVALID_LEVEL;
+}
+
+static DWORD
+_LocalEnumPrintersLevel4(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    return ERROR_INVALID_LEVEL;
+}
+
+static DWORD
+_LocalEnumPrintersLevel5(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    return ERROR_INVALID_LEVEL;
+}
+
 BOOL WINAPI
 LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
 {
+    DWORD cchComputerName = 0;
     DWORD dwErrorCode;
+    WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 };
 
-    // Do no sanity checks here. This is verified by localspl_apitest!
+    ASSERT(pcbNeeded);
+    ASSERT(pcReturned);
 
     // Begin counting.
     *pcbNeeded = 0;
     *pcReturned = 0;
 
-    // Think positive :)
-    // Treat it as success if the caller queried no information and we don't need to return any.
-    dwErrorCode = ERROR_SUCCESS;
+    if (Flags & PRINTER_ENUM_CONNECTIONS || Flags & PRINTER_ENUM_REMOTE || Flags & PRINTER_ENUM_NETWORK)
+    {
+        // If the flags for the Network Print Provider are given, bail out with ERROR_INVALID_NAME.
+        // This is the internal way for a Print Provider to signal that it doesn't handle this request.
+        dwErrorCode = ERROR_INVALID_NAME;
+        goto Cleanup;
+    }
 
-    if (Flags & PRINTER_ENUM_LOCAL)
+    if (!(Flags & PRINTER_ENUM_LOCAL || Flags & PRINTER_ENUM_NAME))
     {
-        // The function behaves quite differently for each level.
-        if (Level == 1)
-        {
-            dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
-        }
-        else
-        {
-            // TODO: Handle other levels.
-            // The caller supplied an invalid level.
-            dwErrorCode = ERROR_INVALID_LEVEL;
-            goto Cleanup;
-        }
+        // The Local Print Provider is the right destination for the request, but without any of these flags,
+        // there is no information that can be returned.
+        // So just signal a successful request.
+        dwErrorCode = ERROR_SUCCESS;
+        goto Cleanup;
+    }
+
+    // Check the supplied Name parameter (if any).
+    // This may return a Computer Name string we later prepend to the output.
+    dwErrorCode = _LocalEnumPrintersCheckName(Flags, Name, wszComputerName, &cchComputerName);
+    if (dwErrorCode != ERROR_SUCCESS)
+        goto Cleanup;
+
+    if (Level == 0)
+    {
+        dwErrorCode = _LocalEnumPrintersLevel0(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
+    }
+    else if (Level == 1)
+    {
+        dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
+    }
+    else if (Level == 2)
+    {
+        dwErrorCode = _LocalEnumPrintersLevel2(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
+    }
+    else if (Level == 4)
+    {
+        dwErrorCode = _LocalEnumPrintersLevel4(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
+    }
+    else if (Level == 5)
+    {
+        dwErrorCode = _LocalEnumPrintersLevel5(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
+    }
+    else
+    {
+        // The caller supplied an invalid level.
+        dwErrorCode = ERROR_INVALID_LEVEL;
     }
 
 Cleanup:
@@ -504,9 +653,11 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
     PLOCAL_JOB pJob;
     PLOCAL_HANDLE pHandle = NULL;
     PLOCAL_PORT pPort;
+    PLOCAL_PORT_HANDLE pPortHandle = NULL;
     PLOCAL_PRINT_MONITOR pPrintMonitor;
     PLOCAL_PRINTER pPrinter;
     PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
+    PLOCAL_XCV_HANDLE pXcvHandle = NULL;
     WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
     WCHAR wszFullPath[MAX_PATH];
 
@@ -578,9 +729,9 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
     if (cchFirstParameter)
     {
         // Yes, extract it.
+        // No null-termination is necessary here, because DllAllocSplMem returns a zero-initialized buffer.
         pwszFirstParameter = DllAllocSplMem((cchFirstParameter + 1) * sizeof(WCHAR));
         CopyMemory(pwszFirstParameter, lpPrinterName, cchFirstParameter * sizeof(WCHAR));
-        pwszFirstParameter[cchFirstParameter] = 0;
     }
 
     // Do we have a second parameter?
@@ -634,9 +785,21 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
             goto Cleanup;
         }
 
+        // Create a new LOCAL_PORT_HANDLE.
+        pPortHandle = DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE));
+        if (!pPortHandle)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        pPortHandle->hPort = hExternalHandle;
+        pPortHandle->pPort = pPort;
+
         // Return the Port handle through our general handle.
         pHandle->HandleType = HandleType_Port;
-        pHandle->pSpecificHandle = hExternalHandle;
+        pHandle->pSpecificHandle = pPortHandle;
     }
     else if (!pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Xcv", 3) == 0)
     {
@@ -699,9 +862,21 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
             goto Cleanup;
         }
 
+        // Create a new LOCAL_XCV_HANDLE.
+        pXcvHandle = DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE));
+        if (!pXcvHandle)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        pXcvHandle->hXcv = hExternalHandle;
+        pXcvHandle->pPrintMonitor = pPrintMonitor;
+
         // Return the Xcv handle through our general handle.
         pHandle->HandleType = HandleType_Xcv;
-        pHandle->pSpecificHandle = hExternalHandle;
+        pHandle->pSpecificHandle = pXcvHandle;
     }
     else
     {
@@ -720,7 +895,7 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
             goto Cleanup;
         }
 
-        // Create a new printer handle.
+        // Create a new LOCAL_PRINTER_HANDLE.
         pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
         if (!pPrinterHandle)
         {
@@ -844,12 +1019,44 @@ Cleanup:
 BOOL WINAPI
 LocalReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
 {
+    BOOL bReturnValue;
     DWORD dwErrorCode;
     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PORT_HANDLE pPortHandle;
     PLOCAL_PRINTER_HANDLE pPrinterHandle;
 
     // Sanity checks.
-    if (!pHandle || pHandle->HandleType != HandleType_Printer)
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Port handles are an entirely different thing.
+    if (pHandle->HandleType == HandleType_Port)
+    {
+        pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
+
+        // Call the monitor's ReadPort function.
+        if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
+        else
+            bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
+
+        if (!bReturnValue)
+        {
+            // The ReadPort function failed. Return its last error.
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+
+        // We were successful!
+        dwErrorCode = ERROR_SUCCESS;
+        goto Cleanup;
+    }
+
+    // The remaining function deals with Printer handles only.
+    if (pHandle->HandleType != HandleType_Printer)
     {
         dwErrorCode = ERROR_INVALID_HANDLE;
         goto Cleanup;
@@ -883,25 +1090,65 @@ Cleanup:
 DWORD WINAPI
 LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
 {
+    BOOL bReturnValue;
     DWORD dwErrorCode;
     DWORD dwReturnValue = 0;
     PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
+    PLOCAL_JOB pJob;
     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PORT_HANDLE pPortHandle;
     PLOCAL_PRINTER_HANDLE pPrinterHandle;
 
     // Sanity checks.
-    if (!pDocInfo1)
+    if (!pHandle)
     {
-        dwErrorCode = ERROR_INVALID_PARAMETER;
+        dwErrorCode = ERROR_INVALID_HANDLE;
         goto Cleanup;
     }
 
-    if (!pHandle || pHandle->HandleType != HandleType_Printer)
+    // Port handles are an entirely different thing.
+    if (pHandle->HandleType == HandleType_Port)
+    {
+        pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
+
+        // This call should come from a Print Processor and the job this port is going to print was assigned to us before opening the Print Processor.
+        // Claim it exclusively for this port handle.
+        pJob = pPortHandle->pPort->pNextJobToProcess;
+        pPortHandle->pPort->pNextJobToProcess = NULL;
+        ASSERT(pJob);
+
+        // Call the monitor's StartDocPort function.
+        if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
+        else
+            bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
+
+        if (!bReturnValue)
+        {
+            // The StartDocPort function failed. Return its last error.
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+
+        // We were successful!
+        dwErrorCode = ERROR_SUCCESS;
+        dwReturnValue = pJob->dwJobID;
+        goto Cleanup;
+    }
+
+    // The remaining function deals with Printer handles only.
+    if (pHandle->HandleType != HandleType_Printer)
     {
         dwErrorCode = ERROR_INVALID_HANDLE;
         goto Cleanup;
     }
 
+    if (!pDocInfo1)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
 
     // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
@@ -988,14 +1235,46 @@ Cleanup:
 }
 
 BOOL WINAPI
-LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
+LocalWritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
 {
+    BOOL bReturnValue;
     DWORD dwErrorCode;
     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PORT_HANDLE pPortHandle;
     PLOCAL_PRINTER_HANDLE pPrinterHandle;
 
     // Sanity checks.
-    if (!pHandle || pHandle->HandleType != HandleType_Printer)
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Port handles are an entirely different thing.
+    if (pHandle->HandleType == HandleType_Port)
+    {
+        pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
+
+        // Call the monitor's WritePort function.
+        if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
+        else
+            bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
+
+        if (!bReturnValue)
+        {
+            // The WritePort function failed. Return its last error.
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+
+        // We were successful!
+        dwErrorCode = ERROR_SUCCESS;
+        goto Cleanup;
+    }
+
+    // The remaining function deals with Printer handles only.
+    if (pHandle->HandleType != HandleType_Printer)
     {
         dwErrorCode = ERROR_INVALID_HANDLE;
         goto Cleanup;
@@ -1053,12 +1332,44 @@ Cleanup:
 BOOL WINAPI
 LocalEndDocPrinter(HANDLE hPrinter)
 {
+    BOOL bReturnValue;
     DWORD dwErrorCode;
     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PORT_HANDLE pPortHandle;
     PLOCAL_PRINTER_HANDLE pPrinterHandle;
 
     // Sanity checks.
-    if (!pHandle || pHandle->HandleType != HandleType_Printer)
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Port handles are an entirely different thing.
+    if (pHandle->HandleType == HandleType_Port)
+    {
+        pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
+
+        // Call the monitor's EndDocPort function.
+        if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnEndDocPort(pPortHandle->hPort);
+        else
+            bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnEndDocPort(pPortHandle->hPort);
+
+        if (!bReturnValue)
+        {
+            // The EndDocPort function failed. Return its last error.
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+
+        // We were successful!
+        dwErrorCode = ERROR_SUCCESS;
+        goto Cleanup;
+    }
+
+    // The remaining function deals with Printer handles only.
+    if (pHandle->HandleType != HandleType_Printer)
     {
         dwErrorCode = ERROR_INVALID_HANDLE;
         goto Cleanup;
@@ -1089,7 +1400,9 @@ BOOL WINAPI
 LocalClosePrinter(HANDLE hPrinter)
 {
     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PORT_HANDLE pPortHandle;
     PLOCAL_PRINTER_HANDLE pPrinterHandle;
+    PLOCAL_XCV_HANDLE pXcvHandle;
 
     if (!pHandle)
     {
@@ -1099,7 +1412,13 @@ LocalClosePrinter(HANDLE hPrinter)
 
     if (pHandle->HandleType == HandleType_Port)
     {
-        // TODO
+        pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
+
+        // Call the monitor's ClosePort function.
+        if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
+            ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnClosePort(pPortHandle->hPort);
+        else
+            ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnClosePort(pPortHandle->hPort);
     }
     else if (pHandle->HandleType == HandleType_Printer)
     {
@@ -1112,16 +1431,20 @@ LocalClosePrinter(HANDLE hPrinter)
         // Free memory for the fields.
         DllFreeSplMem(pPrinterHandle->pDevMode);
         DllFreeSplStr(pPrinterHandle->pwszDatatype);
-
-        // Free memory for the printer handle itself.
-        DllFreeSplMem(pPrinterHandle);
     }
     else if (pHandle->HandleType == HandleType_Xcv)
     {
-        // TODO
+        pXcvHandle = (PLOCAL_XCV_HANDLE)pHandle->pSpecificHandle;
+
+        // Call the monitor's XcvClosePort function.
+        if (pXcvHandle->pPrintMonitor->bIsLevel2)
+            ((PMONITOR2)pXcvHandle->pPrintMonitor->pMonitor)->pfnXcvClosePort(pXcvHandle->hXcv);
+        else
+            ((LPMONITOREX)pXcvHandle->pPrintMonitor->pMonitor)->Monitor.pfnXcvClosePort(pXcvHandle->hXcv);
     }
 
-    // Free memory for the handle itself.
+    // Free memory for the handle and the specific handle.
+    DllFreeSplMem(pHandle->pSpecificHandle);
     DllFreeSplMem(pHandle);
 
     return TRUE;