Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / channels / rdpdr / printer / printer_cups.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol client.
3  * Print Virtual Channel - CUPS driver
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 <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <pthread.h>
24 #include <time.h>
25 #include <cups/cups.h>
26 #include <freerdp/utils/memory.h>
27 #include <freerdp/utils/svc_plugin.h>
28
29 #include "rdpdr_constants.h"
30 #include "rdpdr_types.h"
31 #include "printer_main.h"
32
33 #include "printer_cups.h"
34
35 typedef struct rdp_cups_printer_driver rdpCupsPrinterDriver;
36 typedef struct rdp_cups_printer rdpCupsPrinter;
37 typedef struct rdp_cups_print_job rdpCupsPrintJob;
38
39 struct rdp_cups_printer_driver
40 {
41         rdpPrinterDriver driver;
42
43         int id_sequence;
44 };
45
46 struct rdp_cups_printer
47 {
48         rdpPrinter printer;
49
50         rdpCupsPrintJob* printjob;
51 };
52
53 struct rdp_cups_print_job
54 {
55         rdpPrintJob printjob;
56
57         void* printjob_object;
58         int printjob_id;
59 };
60
61 static void printer_cups_get_printjob_name(char* buf, int size)
62 {
63         time_t tt;
64         struct tm* t;
65
66         tt = time(NULL);
67         t = localtime(&tt);
68         snprintf(buf, size - 1, "FreeRDP Print Job %d%02d%02d%02d%02d%02d",
69                 t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
70                 t->tm_hour, t->tm_min, t->tm_sec);
71 }
72
73 static void printer_cups_write_printjob(rdpPrintJob* printjob, uint8* data, int size)
74 {
75         rdpCupsPrintJob* cups_printjob = (rdpCupsPrintJob*)printjob;
76
77 #ifndef _CUPS_API_1_4
78
79         {
80                 FILE* fp;
81
82                 fp = fopen((const char*)cups_printjob->printjob_object, "a+b");
83                 if (fp == NULL)
84                 {
85                         DEBUG_WARN("failed to open file %s", (char*)cups_printjob->printjob_object);
86                         return;
87                 }
88                 if (fwrite(data, 1, size, fp) < size)
89                 {
90                         DEBUG_WARN("failed to write file %s", (char*)cups_printjob->printjob_object);
91                 }
92                 fclose(fp);
93         }
94
95 #else
96
97         cupsWriteRequestData((http_t*)cups_printjob->printjob_object, (const char*)data, size);
98
99 #endif
100 }
101
102 static void printer_cups_close_printjob(rdpPrintJob* printjob)
103 {
104         rdpCupsPrintJob* cups_printjob = (rdpCupsPrintJob*)printjob;
105
106 #ifndef _CUPS_API_1_4
107
108         {
109                 char buf[100];
110
111                 printer_cups_get_printjob_name(buf, sizeof(buf));
112                 if (cupsPrintFile(printjob->printer->name, (const char *)cups_printjob->printjob_object, buf, 0, NULL) == 0)
113                 {
114                         DEBUG_WARN("cupsPrintFile: %s", cupsLastErrorString());
115                 }
116                 unlink(cups_printjob->printjob_object);
117                 xfree(cups_printjob->printjob_object);
118         }
119
120 #else
121
122         cupsFinishDocument((http_t*)cups_printjob->printjob_object, printjob->printer->name);
123         cups_printjob->printjob_id = 0;
124         httpClose((http_t*)cups_printjob->printjob_object);
125
126 #endif
127
128         xfree(cups_printjob);
129
130         ((rdpCupsPrinter*)printjob->printer)->printjob = NULL;
131 }
132
133 static rdpPrintJob* printer_cups_create_printjob(rdpPrinter* printer, uint32 id)
134 {
135         rdpCupsPrinter* cups_printer = (rdpCupsPrinter*)printer;
136         rdpCupsPrintJob* cups_printjob;
137
138         if (cups_printer->printjob != NULL)
139                 return NULL;
140
141         cups_printjob = xnew(rdpCupsPrintJob);
142
143         cups_printjob->printjob.id = id;
144         cups_printjob->printjob.printer = printer;
145
146         cups_printjob->printjob.Write = printer_cups_write_printjob;
147         cups_printjob->printjob.Close = printer_cups_close_printjob;
148
149 #ifndef _CUPS_API_1_4
150
151         cups_printjob->printjob_object = xstrdup(tmpnam(NULL));
152
153 #else
154         {
155                 char buf[100];
156
157                 cups_printjob->printjob_object = httpConnectEncrypt(cupsServer(), ippPort(), HTTP_ENCRYPT_IF_REQUESTED);
158                 if (cups_printjob->printjob_object == NULL)
159                 {
160                         DEBUG_WARN("httpConnectEncrypt: %s", cupsLastErrorString());
161                         xfree(cups_printjob);
162                         return NULL;
163                 }
164
165                 printer_cups_get_printjob_name(buf, sizeof(buf));
166                 cups_printjob->printjob_id = cupsCreateJob((http_t*)cups_printjob->printjob_object,
167                         printer->name, buf, 0, NULL);
168
169                 if (cups_printjob->printjob_id == 0)
170                 {
171                         DEBUG_WARN("cupsCreateJob: %s", cupsLastErrorString());
172                         httpClose((http_t*)cups_printjob->printjob_object);
173                         xfree(cups_printjob);
174                         return NULL;
175                 }
176                 cupsStartDocument((http_t*)cups_printjob->printjob_object,
177                         printer->name, cups_printjob->printjob_id, buf,
178                         CUPS_FORMAT_AUTO, 1);
179         }
180
181 #endif
182
183         cups_printer->printjob = cups_printjob;
184         
185         return (rdpPrintJob*)cups_printjob;
186 }
187
188 static rdpPrintJob* printer_cups_find_printjob(rdpPrinter* printer, uint32 id)
189 {
190         rdpCupsPrinter* cups_printer = (rdpCupsPrinter*)printer;
191
192         if (cups_printer->printjob == NULL)
193                 return NULL;
194         if (cups_printer->printjob->printjob.id != id)
195                 return NULL;
196
197         return (rdpPrintJob*)cups_printer->printjob;
198 }
199
200 static void printer_cups_free_printer(rdpPrinter* printer)
201 {
202         rdpCupsPrinter* cups_printer = (rdpCupsPrinter*)printer;
203
204         if (cups_printer->printjob)
205                 cups_printer->printjob->printjob.Close((rdpPrintJob*)cups_printer->printjob);
206         xfree(printer->name);
207         xfree(printer);
208 }
209
210 static rdpPrinter* printer_cups_new_printer(rdpCupsPrinterDriver* cups_driver, const char* name, boolean is_default)
211 {
212         rdpCupsPrinter* cups_printer;
213
214         cups_printer = xnew(rdpCupsPrinter);
215
216         cups_printer->printer.id = cups_driver->id_sequence++;
217         cups_printer->printer.name = xstrdup(name);
218         /* This is a generic PostScript printer driver developed by MS, so it should be good in most cases */
219         cups_printer->printer.driver = "MS Publisher Imagesetter";
220         cups_printer->printer.is_default = is_default;
221
222         cups_printer->printer.CreatePrintJob = printer_cups_create_printjob;
223         cups_printer->printer.FindPrintJob = printer_cups_find_printjob;
224         cups_printer->printer.Free = printer_cups_free_printer;
225
226         return (rdpPrinter*)cups_printer;
227 }
228
229 static rdpPrinter** printer_cups_enum_printers(rdpPrinterDriver* driver)
230 {
231         rdpPrinter** printers;
232         int num_printers;
233         cups_dest_t *dests;
234         cups_dest_t *dest;
235         int num_dests;
236         int i;
237
238         num_dests = cupsGetDests(&dests);
239         printers = (rdpPrinter**)xzalloc(sizeof(rdpPrinter*) * (num_dests + 1));
240         num_printers = 0;
241         for (i = 0, dest = dests; i < num_dests; i++, dest++)
242         {
243                 if (dest->instance == NULL)
244                 {
245                         printers[num_printers++] = printer_cups_new_printer((rdpCupsPrinterDriver*)driver,
246                                 dest->name, dest->is_default);
247                 }
248         }
249         cupsFreeDests(num_dests, dests);
250
251         return printers;
252 }
253
254 static rdpPrinter* printer_cups_get_printer(rdpPrinterDriver* driver, const char* name)
255 {
256         rdpCupsPrinterDriver* cups_driver = (rdpCupsPrinterDriver*)driver;
257
258         return printer_cups_new_printer(cups_driver, name, cups_driver->id_sequence == 1 ? true : false);
259 }
260
261 static rdpCupsPrinterDriver* cups_driver = NULL;
262
263 rdpPrinterDriver* printer_cups_get_driver(void)
264 {
265         if (cups_driver == NULL)
266         {
267                 cups_driver = xnew(rdpCupsPrinterDriver);
268
269                 cups_driver->driver.EnumPrinters = printer_cups_enum_printers;
270                 cups_driver->driver.GetPrinter = printer_cups_get_printer;
271
272                 cups_driver->id_sequence = 1;
273
274 #ifdef _CUPS_API_1_4
275                 DEBUG_SVC("using CUPS API 1.4");
276 #else
277                 DEBUG_SVC("using CUPS API 1.2");
278 #endif
279         }
280
281         return (rdpPrinterDriver*)cups_driver;
282 }
283