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