Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / channels / rdpdr / printer / printer_main.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol client.
3  * Print Virtual Channel
4  *
5  * Copyright 2010-2011 Vic Lee
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include "config.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <freerdp/utils/stream.h>
25 #include <freerdp/utils/unicode.h>
26 #include <freerdp/utils/memory.h>
27 #include <freerdp/utils/thread.h>
28 #include <freerdp/utils/svc_plugin.h>
29
30 #include "rdpdr_constants.h"
31 #include "rdpdr_types.h"
32
33 #ifdef WITH_CUPS
34 #include "printer_cups.h"
35 #endif
36
37 #include "printer_main.h"
38
39 typedef struct _PRINTER_DEVICE PRINTER_DEVICE;
40 struct _PRINTER_DEVICE
41 {
42         DEVICE device;
43
44         rdpPrinter* printer;
45
46         LIST* irp_list;
47         freerdp_thread* thread;
48 };
49
50 static void printer_process_irp_create(PRINTER_DEVICE* printer_dev, IRP* irp)
51 {
52         rdpPrintJob* printjob = NULL;
53
54         if (printer_dev->printer != NULL)
55                 printjob = printer_dev->printer->CreatePrintJob(printer_dev->printer, irp->devman->id_sequence++);
56
57         if (printjob != NULL)
58         {
59                 stream_write_uint32(irp->output, printjob->id); /* FileId */
60
61                 DEBUG_SVC("printjob id: %d", printjob->id);
62         }
63         else
64         {
65                 stream_write_uint32(irp->output, 0); /* FileId */
66                 irp->IoStatus = STATUS_PRINT_QUEUE_FULL;
67
68                 DEBUG_WARN("error creating print job.");
69         }
70
71         irp->Complete(irp);
72 }
73
74 static void printer_process_irp_close(PRINTER_DEVICE* printer_dev, IRP* irp)
75 {
76         rdpPrintJob* printjob = NULL;
77
78         if (printer_dev->printer != NULL)
79                 printjob = printer_dev->printer->FindPrintJob(printer_dev->printer, irp->FileId);
80
81         if (printjob == NULL)
82         {
83                 irp->IoStatus = STATUS_UNSUCCESSFUL;
84
85                 DEBUG_WARN("printjob id %d not found.", irp->FileId);
86         }
87         else
88         {
89                 printjob->Close(printjob);
90
91                 DEBUG_SVC("printjob id %d closed.", irp->FileId);
92         }
93
94         stream_write_zero(irp->output, 4); /* Padding(4) */
95
96         irp->Complete(irp);
97 }
98
99 static void printer_process_irp_write(PRINTER_DEVICE* printer_dev, IRP* irp)
100 {
101         rdpPrintJob* printjob = NULL;
102         uint32 Length;
103         uint64 Offset;
104
105         stream_read_uint32(irp->input, Length);
106         stream_read_uint64(irp->input, Offset);
107         stream_seek(irp->input, 20); /* Padding */
108
109         if (printer_dev->printer != NULL)
110                 printjob = printer_dev->printer->FindPrintJob(printer_dev->printer, irp->FileId);
111
112         if (printjob == NULL)
113         {
114                 irp->IoStatus = STATUS_UNSUCCESSFUL;
115                 Length = 0;
116
117                 DEBUG_WARN("printjob id %d not found.", irp->FileId);
118         }
119         else
120         {
121                 printjob->Write(printjob, stream_get_tail(irp->input), Length);
122
123                 DEBUG_SVC("printjob id %d written %d bytes.", irp->FileId, Length);
124         }
125
126         stream_write_uint32(irp->output, Length);
127         stream_write_uint8(irp->output, 0); /* Padding */
128
129         irp->Complete(irp);
130 }
131
132 static void printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
133 {
134         switch (irp->MajorFunction)
135         {
136                 case IRP_MJ_CREATE:
137                         printer_process_irp_create(printer_dev, irp);
138                         break;
139
140                 case IRP_MJ_CLOSE:
141                         printer_process_irp_close(printer_dev, irp);
142                         break;
143
144                 case IRP_MJ_WRITE:
145                         printer_process_irp_write(printer_dev, irp);
146                         break;
147
148                 default:
149                         DEBUG_WARN("MajorFunction 0x%X not supported", irp->MajorFunction);
150                         irp->IoStatus = STATUS_NOT_SUPPORTED;
151                         irp->Complete(irp);
152                         break;
153         }
154 }
155
156 static void printer_process_irp_list(PRINTER_DEVICE* printer_dev)
157 {
158         IRP* irp;
159
160         while (1)
161         {
162                 if (freerdp_thread_is_stopped(printer_dev->thread))
163                         break;
164
165                 freerdp_thread_lock(printer_dev->thread);
166                 irp = (IRP*)list_dequeue(printer_dev->irp_list);
167                 freerdp_thread_unlock(printer_dev->thread);
168
169                 if (irp == NULL)
170                         break;
171
172                 printer_process_irp(printer_dev, irp);
173         }
174 }
175
176 static void* printer_thread_func(void* arg)
177 {
178         PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)arg;
179
180         while (1)
181         {
182                 freerdp_thread_wait(printer_dev->thread);
183
184                 if (freerdp_thread_is_stopped(printer_dev->thread))
185                         break;
186
187                 freerdp_thread_reset(printer_dev->thread);
188                 printer_process_irp_list(printer_dev);
189         }
190
191         freerdp_thread_quit(printer_dev->thread);
192
193         return NULL;
194 }
195
196 static void printer_irp_request(DEVICE* device, IRP* irp)
197 {
198         PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)device;
199
200         freerdp_thread_lock(printer_dev->thread);
201         list_enqueue(printer_dev->irp_list, irp);
202         freerdp_thread_unlock(printer_dev->thread);
203
204         freerdp_thread_signal(printer_dev->thread);
205 }
206
207 static void printer_free(DEVICE* device)
208 {
209         PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)device;
210         IRP* irp;
211
212         freerdp_thread_stop(printer_dev->thread);
213         freerdp_thread_free(printer_dev->thread);
214         
215         while ((irp = (IRP*)list_dequeue(printer_dev->irp_list)) != NULL)
216                 irp->Discard(irp);
217         list_free(printer_dev->irp_list);
218
219         if (printer_dev->printer)
220                 printer_dev->printer->Free(printer_dev->printer);
221
222         xfree(printer_dev->device.name);
223
224         xfree(printer_dev);
225 }
226
227 void printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, rdpPrinter* printer)
228 {
229         PRINTER_DEVICE* printer_dev;
230         char* port;
231         UNICONV* uniconv;
232         uint32 Flags;
233         size_t DriverNameLen;
234         char* DriverName;
235         size_t PrintNameLen;
236         char* PrintName;
237         uint32 CachedFieldsLen;
238         uint8* CachedPrinterConfigData;
239
240         port = xmalloc(10);
241         snprintf(port, 10, "PRN%d", printer->id);
242
243         printer_dev = xnew(PRINTER_DEVICE);
244
245         printer_dev->device.type = RDPDR_DTYP_PRINT;
246         printer_dev->device.name = port;
247         printer_dev->device.IRPRequest = printer_irp_request;
248         printer_dev->device.Free = printer_free;
249
250         printer_dev->printer = printer;
251
252         CachedFieldsLen = 0;
253         CachedPrinterConfigData = NULL;
254
255         DEBUG_SVC("Printer %s registered", printer->name);
256
257         Flags = 0;
258         if (printer->is_default)
259                 Flags |= RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER;
260
261         uniconv = freerdp_uniconv_new();
262         DriverName = freerdp_uniconv_out(uniconv, printer->driver, &DriverNameLen);
263         PrintName = freerdp_uniconv_out(uniconv, printer->name, &PrintNameLen);
264         freerdp_uniconv_free(uniconv);
265
266         printer_dev->device.data = stream_new(28 + DriverNameLen + PrintNameLen + CachedFieldsLen);
267
268         stream_write_uint32(printer_dev->device.data, Flags);
269         stream_write_uint32(printer_dev->device.data, 0); /* CodePage, reserved */
270         stream_write_uint32(printer_dev->device.data, 0); /* PnPNameLen */
271         stream_write_uint32(printer_dev->device.data, DriverNameLen + 2);
272         stream_write_uint32(printer_dev->device.data, PrintNameLen + 2);
273         stream_write_uint32(printer_dev->device.data, CachedFieldsLen);
274         stream_write(printer_dev->device.data, DriverName, DriverNameLen);
275         stream_write_uint16(printer_dev->device.data, 0);
276         stream_write(printer_dev->device.data, PrintName, PrintNameLen);
277         stream_write_uint16(printer_dev->device.data, 0);
278         if (CachedFieldsLen > 0)
279         {
280                 stream_write(printer_dev->device.data, CachedPrinterConfigData, CachedFieldsLen);
281         }
282
283         xfree(DriverName);
284         xfree(PrintName);
285
286         printer_dev->irp_list = list_new();
287         printer_dev->thread = freerdp_thread_new();
288
289         pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)printer_dev);
290
291         freerdp_thread_start(printer_dev->thread, printer_thread_func, printer_dev);
292 }
293
294 int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
295 {
296         rdpPrinterDriver* driver = NULL;
297         rdpPrinter** printers;
298         rdpPrinter* printer;
299         int i;
300         char* name;
301         char* driver_name;
302
303 #ifdef WITH_CUPS
304         driver = printer_cups_get_driver();
305 #endif
306         if (driver == NULL)
307         {
308                 DEBUG_WARN("no driver.");
309                 return 1;
310         }
311
312         name = (char*)pEntryPoints->plugin_data->data[1];
313         driver_name = (char*)pEntryPoints->plugin_data->data[2];
314
315         if (name && name[0])
316         {
317                 printer = driver->GetPrinter(driver, name);
318                 if (printer == NULL)
319                 {
320                         DEBUG_WARN("printer %s not found.", name);
321                         return 1;
322                 }
323                 if (driver_name && driver_name[0])
324                         printer->driver = driver_name;
325
326                 printer_register(pEntryPoints, printer);
327         }
328         else
329         {
330                 printers = driver->EnumPrinters(driver);
331                 for (i = 0; printers[i]; i++)
332                 {
333                         printer = printers[i];
334                         printer_register(pEntryPoints, printer);
335                 }
336                 xfree(printers);
337         }
338
339         return 0;
340 }