1 /* Copyright (C) 2001-2019 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
13    CA 94945, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 
17 /* %printer% IODevice */
18 
19 #include "windows_.h"
20 #include "errno_.h"
21 #include "stdio_.h"
22 #include "string_.h"
23 #include "ctype_.h"
24 #include "fcntl_.h"
25 #include <io.h>
26 #include "gp.h"
27 #include "gscdefs.h"
28 #include "gserrors.h"
29 #include "gstypes.h"
30 #include "gsmemory.h"		/* for gxiodev.h */
31 #include "gxiodev.h"
32 
33 /* The MS-Windows printer IODevice */
34 
35 /*
36  * This allows a MS-Windows printer to be specified as an
37  * output using
38  *  -sOutputFile="%printer%HP DeskJet 500"
39  *
40  * To write to a remote printer on another server
41  *  -sOutputFile="%printer%\\server\printer name"
42  *
43  * If you don't supply a printer name you will get
44  *  Error: /undefinedfilename in --.outputpage--
45  * If the printer name is invalid you will get
46  *  Error: /invalidfileaccess in --.outputpage--
47  *
48  * This is implemented by returning the file pointer
49  * for the write end of a pipe, and starting a thread
50  * which reads the pipe and writes to a Windows printer.
51  *
52  * The old method provided by gp_open_printer()
53  *  -sOutputFile="\\spool\HP DeskJet 500"
54  * should not be used.
55  * The "\\spool\" is not a UNC name and causes confusion.
56  */
57 
58 static iodev_proc_init(mswin_printer_init);
59 static iodev_proc_finit(mswin_printer_finit);
60 static iodev_proc_fopen(mswin_printer_fopen);
61 static iodev_proc_fclose(mswin_printer_fclose);
62 const gx_io_device gs_iodev_printer = {
63     "%printer%", "FileSystem",
64     {mswin_printer_init, mswin_printer_finit, iodev_no_open_device,
65      NULL /*iodev_os_open_file */ , mswin_printer_fopen, mswin_printer_fclose,
66      iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,
67      iodev_no_enumerate_files, NULL, NULL,
68      iodev_no_get_params, iodev_no_put_params
69     },
70     NULL,
71     NULL
72 };
73 
74 typedef struct tid_s {
75     unsigned long tid;
76 } tid_t;
77 
mswin_printer_thread(void * arg)78 void mswin_printer_thread(void *arg)
79 {
80     int fd = (int)arg;
81     char pname[gp_file_name_sizeof];
82     char data[4096];
83     HANDLE hprinter = INVALID_HANDLE_VALUE;
84     int count;
85     DWORD written;
86     DOC_INFO_1 di;
87 
88     /* Read from pipe and write to Windows printer.
89      * First gp_file_name_sizeof bytes are name of the printer.
90      */
91     if (read(fd, pname, sizeof(pname)) != sizeof(pname)) {
92         /* Didn't get the printer name */
93         close(fd);
94         return;
95     }
96 
97     while ( (count = read(fd, data, sizeof(data))) > 0 ) {
98         if (hprinter == INVALID_HANDLE_VALUE) {
99             if (!gp_OpenPrinter(pname, &hprinter)) {
100                 close(fd);
101                 return;
102             }
103             di.pDocName = (LPTSTR)gs_product;
104             di.pOutputFile = NULL;
105             di.pDatatype = "RAW";
106             if (!StartDocPrinter(hprinter, 1, (LPBYTE) & di)) {
107                 AbortPrinter(hprinter);
108                 close(fd);
109                 return;
110             }
111         }
112         if (!StartPagePrinter(hprinter)) {
113                 AbortPrinter(hprinter);
114                 close(fd);
115                 return;
116         }
117         if (!WritePrinter(hprinter, (LPVOID) data, count, &written)) {
118             AbortPrinter(hprinter);
119             close(fd);
120             return;
121         }
122     }
123     if (hprinter != INVALID_HANDLE_VALUE) {
124         if (count == 0) {
125             /* EOF */
126             EndPagePrinter(hprinter);
127             EndDocPrinter(hprinter);
128             ClosePrinter(hprinter);
129         }
130         else {
131             /* Error */
132             AbortPrinter(hprinter);
133         }
134     }
135     close(fd);
136 }
137 
138 /* The file device procedures */
139 static int
mswin_printer_init(gx_io_device * iodev,gs_memory_t * mem)140 mswin_printer_init(gx_io_device * iodev, gs_memory_t * mem)
141 {
142     /* state -> structure containing thread handle */
143     iodev->state = gs_alloc_bytes(mem, sizeof(tid_t), "mswin_printer_init");
144     if (iodev->state == NULL)
145         return_error(gs_error_VMerror);
146     ((tid_t *)iodev->state)->tid = -1;
147     return 0;
148 }
149 
150 static void
mswin_printer_finit(gx_io_device * iodev,gs_memory_t * mem)151 mswin_printer_finit(gx_io_device * iodev, gs_memory_t * mem)
152 {
153     gs_free_object(mem, iodev->state, "mswin_printer_finit");
154     iodev->state = NULL;
155     return;
156 }
157 
158 static int
mswin_printer_fopen(gx_io_device * iodev,const char * fname,const char * access,gp_file ** pfile,char * rfname,uint rnamelen,gs_memory_t * mem)159 mswin_printer_fopen(gx_io_device * iodev, const char *fname, const char *access,
160                     gp_file ** pfile, char *rfname, uint rnamelen, gs_memory_t *mem)
161 {
162     DWORD version = GetVersion();
163     HANDLE hprinter;
164     int pipeh[2];
165     unsigned long tid;
166     HANDLE hthread;
167     char pname[gp_file_name_sizeof];
168     unsigned long *ptid = &((tid_t *)(iodev->state))->tid;
169     gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
170     gs_fs_list_t *fs = ctx->core->fs;
171 
172     if (gp_validate_path(mem, fname, access) != 0)
173         return gs_error_invalidfileaccess;
174 
175     /* First we try the open_printer method. */
176     /* Note that the loop condition here ensures we don't
177      * trigger on the last registered fs entry (our standard
178      * 'file' one). */
179     if (access[0] == 'w' || access[0] == 'a')
180     {
181         *pfile = NULL;
182         for (fs = ctx->core->fs; fs != NULL && fs->next != NULL; fs = fs->next)
183         {
184             int code = 0;
185             if (fs->fs.open_printer)
186                 code = fs->fs.open_printer(mem, fs->secret, fname, 1, pfile);
187             if (code < 0)
188                 return code;
189             if (*pfile != NULL)
190                 return code;
191         }
192     } else
193         return gs_error_invalidfileaccess;
194 
195     /* If nothing claimed that, then continue with the
196      * standard MS way of working. */
197 
198     /* Win32s supports neither pipes nor Win32 printers. */
199     if (((HIWORD(version) & 0x8000) != 0) &&
200         ((HIWORD(version) & 0x4000) == 0))
201         return_error(gs_error_invalidfileaccess);
202 
203     /* Make sure that printer exists. */
204     if (!gp_OpenPrinter((LPTSTR)fname, &hprinter))
205         return_error(gs_error_invalidfileaccess);
206     ClosePrinter(hprinter);
207 
208     *pfile = gp_file_FILE_alloc(mem);
209     if (*pfile == NULL)
210         return_error(gs_error_VMerror);
211 
212     /* Create a pipe to connect a FILE pointer to a Windows printer. */
213     if (_pipe(pipeh, 4096, _O_BINARY) != 0) {
214         gp_file_dealloc(*pfile);
215         *pfile = NULL;
216         return_error(gs_fopen_errno_to_code(errno));
217     }
218 
219     if (gp_file_FILE_set(*pfile, fdopen(pipeh[1], (char *)access), NULL)) {
220         *pfile = NULL;
221         close(pipeh[0]);
222         close(pipeh[1]);
223         return_error(gs_fopen_errno_to_code(errno));
224     }
225 
226     /* start a thread to read the pipe */
227     tid = _beginthread(&mswin_printer_thread, 32768, (void *)(pipeh[0]));
228     if (tid == -1) {
229         gp_fclose(*pfile);
230         *pfile = NULL;
231         close(pipeh[0]);
232         return_error(gs_error_invalidfileaccess);
233     }
234     /* Duplicate thread handle so we can wait on it
235      * even if original handle is closed by CRTL
236      * when the thread finishes.
237      */
238     if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)tid,
239         GetCurrentProcess(), &hthread,
240         0, FALSE, DUPLICATE_SAME_ACCESS)) {
241         gp_fclose(*pfile);
242         *pfile = NULL;
243         return_error(gs_error_invalidfileaccess);
244     }
245     *ptid = (unsigned long)hthread;
246 
247     /* Give the name of the printer to the thread by writing
248      * it to the pipe.  This is avoids elaborate thread
249      * synchronisation code.
250      */
251     strncpy(pname, fname, sizeof(pname));
252     gp_fwrite(pname, 1, sizeof(pname), *pfile);
253 
254     return 0;
255 }
256 
257 static int
mswin_printer_fclose(gx_io_device * iodev,gp_file * file)258 mswin_printer_fclose(gx_io_device * iodev, gp_file * file)
259 {
260     unsigned long *ptid = &((tid_t *)(iodev->state))->tid;
261     HANDLE hthread;
262     gp_fclose(file);
263     if (*ptid != -1) {
264         /* Wait until the print thread finishes before continuing */
265         hthread = (HANDLE)*ptid;
266         WaitForSingleObject(hthread, 60000);
267         CloseHandle(hthread);
268         *ptid = -1;
269     }
270     return 0;
271 }
272