1 /* Copyright (C) 2001-2006 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, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: gp_msprn.c 8250 2007-09-25 13:31:24Z giles $ */
15 /* %printer% IODevice */
16 
17 #include "windows_.h"
18 #include "errno_.h"
19 #include "stdio_.h"
20 #include "string_.h"
21 #include "ctype_.h"
22 #include "fcntl_.h"
23 #include <io.h>
24 #include "gp.h"
25 #include "gscdefs.h"
26 #include "gserrors.h"
27 #include "gserror.h"
28 #include "gstypes.h"
29 #include "gsmemory.h"		/* for gxiodev.h */
30 #include "gxiodev.h"
31 
32 /* The MS-Windows printer IODevice */
33 
34 /*
35  * This allows a MS-Windows printer to be specified as an
36  * output using
37  *  -sOutputFile="%printer%HP DeskJet 500"
38  *
39  * To write to a remote printer on another server
40  *  -sOutputFile="%printer%\\server\printer name"
41  *
42  * If you don't supply a printer name you will get
43  *  Error: /undefinedfilename in --.outputpage--
44  * If the printer name is invalid you will get
45  *  Error: /invalidfileaccess in --.outputpage--
46  *
47  * This is implemented by returning the file pointer
48  * for the write end of a pipe, and starting a thread
49  * which reads the pipe and writes to a Windows printer.
50  *
51  * The old method provided by gp_open_printer()
52  *  -sOutputFile="\\spool\HP DeskJet 500"
53  * should not be used.
54  * The "\\spool\" is not a UNC name and causes confusion.
55  */
56 
57 static iodev_proc_init(mswin_printer_init);
58 static iodev_proc_fopen(mswin_printer_fopen);
59 static iodev_proc_fclose(mswin_printer_fclose);
60 const gx_io_device gs_iodev_printer = {
61     "%printer%", "FileSystem",
62     {mswin_printer_init, iodev_no_open_device,
63      NULL /*iodev_os_open_file */ , mswin_printer_fopen, mswin_printer_fclose,
64      iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,
65      iodev_no_enumerate_files, NULL, NULL,
66      iodev_no_get_params, iodev_no_put_params
67     }
68 };
69 
70 typedef struct tid_s {
71     unsigned long tid;
72 } tid_t;
73 
74 
mswin_printer_thread(void * arg)75 void mswin_printer_thread(void *arg)
76 {
77     int fd = (int)arg;
78     char pname[gp_file_name_sizeof];
79     char data[4096];
80     HANDLE hprinter = INVALID_HANDLE_VALUE;
81     int count;
82     DWORD written;
83     DOC_INFO_1 di;
84 
85     /* Read from pipe and write to Windows printer.
86      * First gp_file_name_sizeof bytes are name of the printer.
87      */
88     if (read(fd, pname, sizeof(pname)) != sizeof(pname)) {
89 	/* Didn't get the printer name */
90 	close(fd);
91 	return;
92     }
93 
94     while ( (count = read(fd, data, sizeof(data))) > 0 ) {
95 	if (hprinter == INVALID_HANDLE_VALUE) {
96 	    if (!OpenPrinter(pname, &hprinter, NULL)) {
97 		close(fd);
98 		return;
99 	    }
100 	    di.pDocName = (LPTSTR)gs_product;
101 	    di.pOutputFile = NULL;
102 	    di.pDatatype = "RAW";
103 	    if (!StartDocPrinter(hprinter, 1, (LPBYTE) & di)) {
104 		AbortPrinter(hprinter);
105 		close(fd);
106 		return;
107 	    }
108 	}
109 	if (!StartPagePrinter(hprinter)) {
110 		AbortPrinter(hprinter);
111 		close(fd);
112 		return;
113 	}
114 	if (!WritePrinter(hprinter, (LPVOID) data, count, &written)) {
115 	    AbortPrinter(hprinter);
116 	    close(fd);
117 	    return;
118 	}
119     }
120     if (hprinter != INVALID_HANDLE_VALUE) {
121 	if (count == 0) {
122 	    /* EOF */
123 	    EndPagePrinter(hprinter);
124 	    EndDocPrinter(hprinter);
125 	    ClosePrinter(hprinter);
126 	}
127 	else {
128 	    /* Error */
129 	    AbortPrinter(hprinter);
130 	}
131     }
132     close(fd);
133 }
134 
135 /* The file device procedures */
136 static int
mswin_printer_init(gx_io_device * iodev,gs_memory_t * mem)137 mswin_printer_init(gx_io_device * iodev, gs_memory_t * mem)
138 {
139     /* state -> structure containing thread handle */
140     iodev->state = gs_alloc_bytes(mem, sizeof(tid_t), "mswin_printer_init");
141     if (iodev->state == NULL)
142         return_error(gs_error_VMerror);
143     ((tid_t *)iodev->state)->tid = -1;
144     return 0;
145 }
146 
147 
148 static int
mswin_printer_fopen(gx_io_device * iodev,const char * fname,const char * access,FILE ** pfile,char * rfname,uint rnamelen)149 mswin_printer_fopen(gx_io_device * iodev, const char *fname, const char *access,
150 	   FILE ** pfile, char *rfname, uint rnamelen)
151 {
152     DWORD version = GetVersion();
153     HANDLE hprinter;
154     int pipeh[2];
155     unsigned long tid;
156     HANDLE hthread;
157     char pname[gp_file_name_sizeof];
158     unsigned long *ptid = &((tid_t *)(iodev->state))->tid;
159 
160     /* Win32s supports neither pipes nor Win32 printers. */
161     if (((HIWORD(version) & 0x8000) != 0) &&
162 	((HIWORD(version) & 0x4000) == 0))
163 	return_error(gs_error_invalidfileaccess);
164 
165     /* Make sure that printer exists. */
166     if (!OpenPrinter((LPTSTR)fname, &hprinter, NULL))
167 	return_error(gs_error_invalidfileaccess);
168     ClosePrinter(hprinter);
169 
170     /* Create a pipe to connect a FILE pointer to a Windows printer. */
171     if (_pipe(pipeh, 4096, _O_BINARY) != 0)
172 	return_error(gs_fopen_errno_to_code(errno));
173 
174     *pfile = fdopen(pipeh[1], (char *)access);
175     if (*pfile == NULL) {
176 	close(pipeh[0]);
177 	close(pipeh[1]);
178 	return_error(gs_fopen_errno_to_code(errno));
179     }
180 
181     /* start a thread to read the pipe */
182     tid = _beginthread(&mswin_printer_thread, 32768, (void *)(pipeh[0]));
183     if (tid == -1) {
184 	fclose(*pfile);
185 	close(pipeh[0]);
186 	return_error(gs_error_invalidfileaccess);
187     }
188     /* Duplicate thread handle so we can wait on it
189      * even if original handle is closed by CRTL
190      * when the thread finishes.
191      */
192     if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)tid,
193 	GetCurrentProcess(), &hthread,
194 	0, FALSE, DUPLICATE_SAME_ACCESS)) {
195 	fclose(*pfile);
196 	return_error(gs_error_invalidfileaccess);
197     }
198     *ptid = (unsigned long)hthread;
199 
200     /* Give the name of the printer to the thread by writing
201      * it to the pipe.  This is avoids elaborate thread
202      * synchronisation code.
203      */
204     strncpy(pname, fname, sizeof(pname));
205     fwrite(pname, 1, sizeof(pname), *pfile);
206 
207     return 0;
208 }
209 
210 static int
mswin_printer_fclose(gx_io_device * iodev,FILE * file)211 mswin_printer_fclose(gx_io_device * iodev, FILE * file)
212 {
213     unsigned long *ptid = &((tid_t *)(iodev->state))->tid;
214     HANDLE hthread;
215     fclose(file);
216     if (*ptid != -1) {
217 	/* Wait until the print thread finishes before continuing */
218 	hthread = (HANDLE)*ptid;
219 	WaitForSingleObject(hthread, 60000);
220 	CloseHandle(hthread);
221 	*ptid = -1;
222     }
223     return 0;
224 }
225 
226 
227