1 /* sane - Scanner Access Now Easy.
2
3 Copyright (C) 2019 Touboul Nathane
4 Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
5
6 This file is part of the SANE package.
7
8 SANE is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12
13 SANE is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with sane; see the file COPYING.
20 If not, see <https://www.gnu.org/licenses/>.
21
22 This file implements a SANE backend for eSCL scanners. */
23
24 #define DEBUG_DECLARE_ONLY
25 #include "../include/sane/config.h"
26
27 #include "escl.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <libxml/parser.h>
34
35 #include "../include/sane/saneopts.h"
36
37 struct cap
38 {
39 char *memory;
40 size_t size;
41 };
42
43 static size_t
header_callback(void * str,size_t size,size_t nmemb,void * userp)44 header_callback(void *str, size_t size, size_t nmemb, void *userp)
45 {
46 struct cap *header = (struct cap *)userp;
47 size_t realsize = size * nmemb;
48 char *content = realloc(header->memory, header->size + realsize + 1);
49
50 if (content == NULL) {
51 DBG( 1, "Not enough memory (realloc returned NULL)\n");
52 return (0);
53 }
54 header->memory = content;
55 memcpy(&(header->memory[header->size]), str, realsize);
56 header->size = header->size + realsize;
57 header->memory[header->size] = 0;
58 return (realsize);
59 }
60
61
62 /**
63 * \fn static SANE_String_Const convert_elements(SANE_String_Const str)
64 * \brief Function that converts the 'color modes' of the scanner (color/gray) to be understood by SANE.
65 *
66 * \return SANE_VALUE_SCAN_MODE_GRAY / SANE_VALUE_SCAN_MODE_COLOR / SANE_VALUE_SCAN_MODE_LINEART; NULL otherwise
67 */
68 static SANE_String_Const
convert_elements(SANE_String_Const str)69 convert_elements(SANE_String_Const str)
70 {
71 if (strcmp(str, "Grayscale8") == 0)
72 return (SANE_VALUE_SCAN_MODE_GRAY);
73 else if (strcmp(str, "RGB24") == 0)
74 return (SANE_VALUE_SCAN_MODE_COLOR);
75 #if HAVE_POPPLER_GLIB
76 else if (strcmp(str, "BlackAndWhite1") == 0)
77 return (SANE_VALUE_SCAN_MODE_LINEART);
78 #endif
79 return (NULL);
80 }
81
82 /**
83 * \fn static SANE_String_Const *char_to_array(SANE_String_Const *tab, int *tabsize, SANE_String_Const mode, int good_array)
84 * \brief Function that creates the character arrays to put inside :
85 * the 'color modes', the 'content types', the 'document formats' and the 'supported intents'.
86 *
87 * \return board (the allocated array)
88 */
89 static SANE_String_Const *
char_to_array(SANE_String_Const * tab,int * tabsize,SANE_String_Const mode,int good_array)90 char_to_array(SANE_String_Const *tab, int *tabsize, SANE_String_Const mode, int good_array)
91 {
92 SANE_String_Const *board = NULL;
93 int i = 0;
94 SANE_String_Const convert = NULL;
95
96 if (mode == NULL)
97 return (tab);
98 if (good_array != 0) {
99 convert = convert_elements(mode);
100 if (convert == NULL)
101 return (tab);
102 }
103 else
104 convert = mode;
105 for (i = 0; i < (*tabsize); i++) {
106 if (strcmp(tab[i], convert) == 0)
107 return (tab);
108 }
109 (*tabsize)++;
110 if (*tabsize == 1)
111 board = (SANE_String_Const *)malloc(sizeof(SANE_String_Const) * ((*tabsize) + 1));
112 else
113 board = (SANE_String_Const *)realloc(tab, sizeof(SANE_String_Const) * ((*tabsize) + 1));
114 board[*tabsize - 1] = (SANE_String_Const)strdup(convert);
115 board[*tabsize] = NULL;
116 return (board);
117 }
118
119 /**
120 * \fn static SANE_Int *int_to_array(SANE_Int *tab, int *tabsize, int cont)
121 * \brief Function that creates the integer array to put inside the 'supported resolutions'.
122 *
123 * \return board (the allocated array)
124 */
125 static SANE_Int *
int_to_array(SANE_Int * tab,int * tabsize,int cont)126 int_to_array(SANE_Int *tab, int *tabsize, int cont)
127 {
128 SANE_Int *board = NULL;
129 int i = 0;
130
131 for (i = 0; i < (*tabsize); i++) {
132 if (tab[i] == cont)
133 return (tab);
134 }
135 (*tabsize)++;
136 if (*tabsize == 1) {
137 (*tabsize)++;
138 board = malloc(sizeof(SANE_Int *) * (*tabsize) + 1);
139 }
140 else
141 board = realloc(tab, sizeof(SANE_Int *) * (*tabsize) + 1);
142 board[0] = *tabsize - 1;
143 board[*tabsize - 1] = cont;
144 board[*tabsize] = -1;
145 return (board);
146 }
147
148 /**
149 * \fn static size_t memory_callback_c(void *contents, size_t size, size_t nmemb, void *userp)
150 * \brief Callback function that stocks in memory the content of the scanner capabilities.
151 *
152 * \return realsize (size of the content needed -> the scanner capabilities)
153 */
154 static size_t
memory_callback_c(void * contents,size_t size,size_t nmemb,void * userp)155 memory_callback_c(void *contents, size_t size, size_t nmemb, void *userp)
156 {
157 size_t realsize = size * nmemb;
158 struct cap *mem = (struct cap *)userp;
159
160 char *str = realloc(mem->memory, mem->size + realsize + 1);
161 if (str == NULL) {
162 DBG(10, "not enough memory (realloc returned NULL)\n");
163 return (0);
164 }
165 mem->memory = str;
166 memcpy(&(mem->memory[mem->size]), contents, realsize);
167 mem->size = mem->size + realsize;
168 mem->memory[mem->size] = 0;
169 return (realsize);
170 }
171
172 /**
173 * \fn static int find_nodes_c(xmlNode *node)
174 * \brief Function that browses the xml file and parses it, to find the xml children node.
175 * --> to recover the scanner capabilities.
176 *
177 * \return 0 if a xml child node is found, 1 otherwise
178 */
179 static int
find_nodes_c(xmlNode * node)180 find_nodes_c(xmlNode *node)
181 {
182 xmlNode *child = node->children;
183
184 while (child) {
185 if (child->type == XML_ELEMENT_NODE)
186 return (0);
187 child = child->next;
188 }
189 return (1);
190 }
191
192 /**
193 * \fn static int find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner)
194 * \brief Function that searches in the xml file if a scanner capabilitie stocked
195 * in one of the created array (character/integer array) is found.
196 *
197 * \return 0
198 */
199 static int
find_valor_of_array_variables(xmlNode * node,capabilities_t * scanner,int type)200 find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner, int type)
201 {
202 const char *name = (const char *)node->name;
203 if (strcmp(name, "ColorMode") == 0) {
204 const char *color = (SANE_String_Const)xmlNodeGetContent(node);
205 #if HAVE_POPPLER_GLIB
206 if (type == PLATEN || strcmp(color, "BlackAndWhite1"))
207 #else
208 if (strcmp(color, "BlackAndWhite1"))
209 #endif
210 scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes, &scanner->caps[type].ColorModesSize, (SANE_String_Const)xmlNodeGetContent(node), 1);
211 }
212 else if (strcmp(name, "ContentType") == 0)
213 scanner->caps[type].ContentTypes = char_to_array(scanner->caps[type].ContentTypes, &scanner->caps[type].ContentTypesSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
214 else if (strcmp(name, "DocumentFormat") == 0)
215 {
216 int i = 0;
217 SANE_Bool have_jpeg = SANE_FALSE, have_png = SANE_FALSE, have_tiff = SANE_FALSE, have_pdf = SANE_FALSE;
218 scanner->caps[type].DocumentFormats = char_to_array(scanner->caps[type].DocumentFormats, &scanner->caps[type].DocumentFormatsSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
219 scanner->caps[type].have_jpeg = -1;
220 scanner->caps[type].have_png = -1;
221 scanner->caps[type].have_tiff = -1;
222 scanner->caps[type].have_pdf = -1;
223 for(; i < scanner->caps[type].DocumentFormatsSize; i++)
224 {
225 if (!strcmp(scanner->caps[type].DocumentFormats[i], "image/jpeg"))
226 {
227 have_jpeg = SANE_TRUE;
228 scanner->caps[type].have_jpeg = i;
229 }
230 #if(defined HAVE_LIBPNG)
231 else if(!strcmp(scanner->caps[type].DocumentFormats[i], "image/png"))
232 {
233 have_png = SANE_TRUE;
234 scanner->caps[type].have_png = i;
235 }
236 #endif
237 #if(defined HAVE_TIFFIO_H)
238 else if(type == PLATEN && !strcmp(scanner->caps[type].DocumentFormats[i], "image/tiff"))
239 {
240 have_tiff = SANE_TRUE;
241 scanner->caps[type].have_tiff = i;
242 }
243 #endif
244 #if HAVE_POPPLER_GLIB
245 else if(type == PLATEN && !strcmp(scanner->caps[type].DocumentFormats[i], "application/pdf"))
246 {
247 have_pdf = SANE_TRUE;
248 scanner->caps[type].have_pdf = i;
249 }
250 #endif
251 }
252 if (have_pdf)
253 scanner->caps[type].default_format = strdup("application/pdf");
254 else if (have_tiff)
255 scanner->caps[type].default_format = strdup("image/tiff");
256 else if (have_png)
257 scanner->caps[type].default_format = strdup("image/png");
258 else if (have_jpeg)
259 scanner->caps[type].default_format = strdup("image/jpeg");
260 }
261 else if (strcmp(name, "DocumentFormatExt") == 0)
262 scanner->caps[type].format_ext = 1;
263 else if (strcmp(name, "Intent") == 0)
264 scanner->caps[type].SupportedIntents = char_to_array(scanner->caps[type].SupportedIntents, &scanner->caps[type].SupportedIntentsSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
265 else if (strcmp(name, "XResolution") == 0)
266 scanner->caps[type].SupportedResolutions = int_to_array(scanner->caps[type].SupportedResolutions, &scanner->caps[type].SupportedResolutionsSize, atoi((const char *)xmlNodeGetContent(node)));
267 return (0);
268 }
269
270 /**
271 * \fn static int find_value_of_int_variables(xmlNode *node, capabilities_t *scanner)
272 * \brief Function that searches in the xml file if a integer scanner capabilitie is found.
273 * The integer scanner capabilities that are interesting are :
274 * MinWidth, MaxWidth, MaxHeight, MinHeight, MaxScanRegions, MaxOpticalXResolution,
275 * RiskyLeftMargin, RiskyRightMargin, RiskyTopMargin, RiskyBottomMargin.
276 *
277 * \return 0
278 */
279 static int
find_value_of_int_variables(xmlNode * node,capabilities_t * scanner,int type)280 find_value_of_int_variables(xmlNode *node, capabilities_t *scanner, int type)
281 {
282 int MaxWidth = 0;
283 int MaxHeight = 0;
284 const char *name = (const char *)node->name;
285
286 if (strcmp(name, "MinWidth") == 0)
287 scanner->caps[type].MinWidth = atoi((const char*)xmlNodeGetContent(node));
288 else if (strcmp(name, "MaxWidth") == 0) {
289 MaxWidth = atoi((const char*)xmlNodeGetContent(node));
290 if (scanner->caps[type].MaxWidth == 0 || MaxWidth < scanner->caps[type].MaxWidth)
291 scanner->caps[type].MaxWidth = atoi((const char *)xmlNodeGetContent(node));
292 }
293 else if (strcmp(name, "MinHeight") == 0)
294 scanner->caps[type].MinHeight = atoi((const char*)xmlNodeGetContent(node));
295 else if (strcmp(name, "MaxHeight") == 0) {
296 MaxHeight = atoi((const char*)xmlNodeGetContent(node));
297 if (scanner->caps[type].MaxHeight == 0 || MaxHeight < scanner->caps[type].MaxHeight)
298 scanner->caps[type].MaxHeight = atoi((const char *)xmlNodeGetContent(node));
299 }
300 else if (strcmp(name, "MaxScanRegions") == 0)
301 scanner->caps[type].MaxScanRegions = atoi((const char *)xmlNodeGetContent(node));
302 else if (strcmp(name, "MaxOpticalXResolution") == 0)
303 scanner->caps[type].MaxOpticalXResolution = atoi((const char *)xmlNodeGetContent(node));
304 else if (strcmp(name, "RiskyLeftMargin") == 0)
305 scanner->caps[type].RiskyLeftMargin = atoi((const char *)xmlNodeGetContent(node));
306 else if (strcmp(name, "RiskyRightMargin") == 0)
307 scanner->caps[type].RiskyRightMargin = atoi((const char *)xmlNodeGetContent(node));
308 else if (strcmp(name, "RiskyTopMargin") == 0)
309 scanner->caps[type].RiskyTopMargin = atoi((const char *)xmlNodeGetContent(node));
310 else if (strcmp(name, "RiskyBottomMargin") == 0)
311 scanner->caps[type].RiskyBottomMargin = atoi((const char *)xmlNodeGetContent(node));
312 find_valor_of_array_variables(node, scanner, type);
313 return (0);
314 }
315
316 static support_t*
print_support(xmlNode * node)317 print_support(xmlNode *node)
318 {
319 support_t *sup = (support_t*)calloc(1, sizeof(support_t));
320 int cpt = 0;
321 int have_norm = 0;
322 while (node) {
323 if (!strcmp((const char *)node->name, "Min")){
324 sup->min = atoi((const char *)xmlNodeGetContent(node));
325 cpt++;
326 }
327 else if (!strcmp((const char *)node->name, "Max")) {
328 sup->max = atoi((const char *)xmlNodeGetContent(node));
329 cpt++;
330 }
331 else if (!strcmp((const char *)node->name, "Normal")) {
332 sup->value = atoi((const char *)xmlNodeGetContent(node));
333 sup->normal = sup->value;
334 cpt++;
335 have_norm = 1;
336 }
337 else if (!strcmp((const char *)node->name, "Step")) {
338 sup->step = atoi((const char *)xmlNodeGetContent(node));
339 cpt++;
340 }
341 node = node->next;
342 }
343 if (cpt == 4)
344 return sup;
345 if (cpt == 3 && have_norm == 0) {
346 sup->value = (sup->max / 2 );
347 sup->normal = sup->value;
348 return sup;
349 }
350 free(sup);
351 return NULL;
352 }
353
354 static int
find_struct_variables(xmlNode * node,capabilities_t * scanner)355 find_struct_variables(xmlNode *node, capabilities_t *scanner)
356 {
357 const char *name = (const char *)node->name;
358 if (strcmp(name, "BrightnessSupport") == 0) {
359 scanner->brightness =
360 print_support(node->children);
361 return 1;
362 }
363 else if (strcmp(name, "ContrastSupport") == 0) {
364 scanner->contrast =
365 print_support(node->children);
366 return 1;
367 }
368 else if (strcmp(name, "SharpenSupport") == 0) {
369 scanner->sharpen =
370 print_support(node->children);
371 return 1;
372 }
373 else if (strcmp(name, "ThresholdSupport") == 0) {
374 scanner->threshold =
375 print_support(node->children);
376 return 1;
377 }
378 return (0);
379 }
380
381 /**
382 * \fn static int find_true_variables(xmlNode *node, capabilities_t *scanner)
383 * \brief Function that searches in the xml file if we find a scanner capability stored
384 * in one of the created array (character/integer array),
385 * or, if we find a integer scanner capability.
386 *
387 * \return 0
388 */
389 static int
find_true_variables(xmlNode * node,capabilities_t * scanner,int type)390 find_true_variables(xmlNode *node, capabilities_t *scanner, int type)
391 {
392 const char *name = (const char *)node->name;
393 if (strcmp(name, "MinWidth") == 0 ||
394 strcmp(name, "MaxWidth") == 0 ||
395 strcmp(name, "MinHeight") == 0 ||
396 strcmp(name, "MaxHeight") == 0 ||
397 strcmp(name, "MaxScanRegions") == 0 ||
398 strcmp(name, "ColorMode") == 0 ||
399 strcmp(name, "ContentType") == 0 ||
400 strcmp(name, "DocumentFormat") == 0 ||
401 strcmp(name, "XResolution") == 0 ||
402 strcmp(name, "Intent") == 0 ||
403 strcmp(name, "MaxOpticalXResolution") == 0 ||
404 strcmp(name, "RiskyLeftMargin") == 0 ||
405 strcmp(name, "RiskyRightMargin") == 0 ||
406 strcmp(name, "RiskyTopMargin") == 0 ||
407 strcmp(name, "RiskyBottomMargin") == 0 ||
408 strcmp(name, "DocumentFormatExt") == 0)
409 find_value_of_int_variables(node, scanner, type);
410 return (0);
411 }
412
413 static char*
replace_char(char * str,char find,char replace)414 replace_char(char* str, char find, char replace){
415 char *current_pos = strchr(str,find);
416 while (current_pos) {
417 *current_pos = replace;
418 current_pos = strchr(current_pos,find);
419 }
420 return str;
421 }
422
423 /**
424 * \fn static int print_xml_c(xmlNode *node, capabilities_t *scanner)
425 * \brief Function that browses the xml file, node by node.
426 *
427 * \return 0
428 */
429 static int
print_xml_c(xmlNode * node,ESCL_Device * device,capabilities_t * scanner,int type)430 print_xml_c(xmlNode *node, ESCL_Device *device, capabilities_t *scanner, int type)
431 {
432 while (node) {
433 if (node->type == XML_ELEMENT_NODE) {
434 if (find_nodes_c(node) && type != -1)
435 find_true_variables(node, scanner, type);
436 }
437 if (!strcmp((const char *)node->name, "Version")&& node->ns && node->ns->prefix){
438 if (!strcmp((const char*)node->ns->prefix, "pwg"))
439 device->version = strdup((const char *)xmlNodeGetContent(node));
440 }
441 if (!strcmp((const char *)node->name, "MakeAndModel")){
442 device->model_name = strdup((const char *)xmlNodeGetContent(node));
443 }
444 else if (!strcmp((const char *)node->name, "PlatenInputCaps")) {
445 scanner->Sources[PLATEN] = (SANE_String_Const)strdup(SANE_I18N ("Flatbed"));
446 scanner->SourcesSize++;
447 scanner->source = PLATEN;
448 print_xml_c(node->children, device, scanner, PLATEN);
449 scanner->caps[PLATEN].duplex = 0;
450 }
451 else if (!strcmp((const char *)node->name, "AdfSimplexInputCaps")) {
452 scanner->Sources[ADFSIMPLEX] = (SANE_String_Const)strdup(SANE_I18N("ADF"));
453 scanner->SourcesSize++;
454 if (scanner->source == -1) scanner->source = ADFSIMPLEX;
455 print_xml_c(node->children, device, scanner, ADFSIMPLEX);
456 scanner->caps[ADFSIMPLEX].duplex = 0;
457 }
458 else if (!strcmp((const char *)node->name, "AdfDuplexInputCaps")) {
459 scanner->Sources[ADFDUPLEX] = (SANE_String_Const)strdup(SANE_I18N ("ADF Duplex"));
460 scanner->SourcesSize++;
461 if (scanner->source == -1) scanner->source = ADFDUPLEX;
462 print_xml_c(node->children, device, scanner, ADFDUPLEX);
463 scanner->caps[ADFDUPLEX].duplex = 1;
464 }
465 else if (find_struct_variables(node, scanner) == 0)
466 print_xml_c(node->children, device, scanner, type);
467 node = node->next;
468 }
469 return (0);
470 }
471
472 static void
_reduce_color_modes(capabilities_t * scanner)473 _reduce_color_modes(capabilities_t *scanner)
474 {
475 int type = 0;
476 for (type = 0; type < 3; type++) {
477 if (scanner->caps[type].ColorModesSize) {
478 if (scanner->caps[type].default_format &&
479 strcmp(scanner->caps[type].default_format, "application/pdf")) {
480 if (scanner->caps[type].ColorModesSize == 3) {
481 free(scanner->caps[type].ColorModes);
482 scanner->caps[type].ColorModes = NULL;
483 scanner->caps[type].ColorModesSize = 0;
484 scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes,
485 &scanner->caps[type].ColorModesSize,
486 (SANE_String_Const)SANE_VALUE_SCAN_MODE_GRAY, 0);
487 scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes,
488 &scanner->caps[type].ColorModesSize,
489 (SANE_String_Const)SANE_VALUE_SCAN_MODE_COLOR, 0);
490 }
491 }
492 }
493 }
494 }
495
496 static void
_delete_pdf(capabilities_t * scanner)497 _delete_pdf(capabilities_t *scanner)
498 {
499 int type = 0;
500 for (type = 0; type < 3; type++) {
501 if (scanner->caps[type].ColorModesSize) {
502 if (scanner->caps[type].default_format) {
503 scanner->caps[type].have_pdf = -1;
504 if (!strcmp(scanner->caps[type].default_format, "application/pdf")) {
505 free(scanner->caps[type].default_format);
506 if (scanner->caps[type].have_tiff > -1)
507 scanner->caps[type].default_format = strdup("image/tiff");
508 else if (scanner->caps[type].have_png > -1)
509 scanner->caps[type].default_format = strdup("image/png");
510 else if (scanner->caps[type].have_jpeg > -1)
511 scanner->caps[type].default_format = strdup("image/jpeg");
512 }
513 free(scanner->caps[type].ColorModes);
514 scanner->caps[type].ColorModes = NULL;
515 scanner->caps[type].ColorModesSize = 0;
516 scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes,
517 &scanner->caps[type].ColorModesSize,
518 (SANE_String_Const)SANE_VALUE_SCAN_MODE_GRAY, 0);
519 scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes,
520 &scanner->caps[type].ColorModesSize,
521 (SANE_String_Const)SANE_VALUE_SCAN_MODE_COLOR, 0);
522 }
523 }
524 }
525 }
526
527 /**
528 * \fn capabilities_t *escl_capabilities(const ESCL_Device *device, SANE_Status *status)
529 * \brief Function that finally recovers all the capabilities of the scanner, using curl.
530 * This function is called in the 'sane_open' function and it's the equivalent of
531 * the following curl command : "curl http(s)://'ip':'port'/eSCL/ScannerCapabilities".
532 *
533 * \return scanner (the structure that stocks all the capabilities elements)
534 */
535 capabilities_t *
escl_capabilities(ESCL_Device * device,char * blacklist,SANE_Status * status)536 escl_capabilities(ESCL_Device *device, char *blacklist, SANE_Status *status)
537 {
538 capabilities_t *scanner = (capabilities_t*)calloc(1, sizeof(capabilities_t));
539 CURL *curl_handle = NULL;
540 struct cap *var = NULL;
541 struct cap *header = NULL;
542 xmlDoc *data = NULL;
543 xmlNode *node = NULL;
544 int i = 0;
545 const char *scanner_capabilities = "/eSCL/ScannerCapabilities";
546 SANE_Bool use_pdf = SANE_TRUE;
547
548 *status = SANE_STATUS_GOOD;
549 if (device == NULL)
550 *status = SANE_STATUS_NO_MEM;
551 var = (struct cap *)calloc(1, sizeof(struct cap));
552 if (var == NULL)
553 *status = SANE_STATUS_NO_MEM;
554 var->memory = malloc(1);
555 var->size = 0;
556 header = (struct cap *)calloc(1, sizeof(struct cap));
557 if (header == NULL)
558 *status = SANE_STATUS_NO_MEM;
559 header->memory = malloc(1);
560 header->size = 0;
561 curl_handle = curl_easy_init();
562 escl_curl_url(curl_handle, device, scanner_capabilities);
563 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memory_callback_c);
564 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)var);
565 curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_callback);
566 curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *)header);
567 curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
568 curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 3L);
569 CURLcode res = curl_easy_perform(curl_handle);
570 if (res == CURLE_OK)
571 DBG( 1, "Create NewJob : the scanner header responded : [%s]\n", header->memory);
572 if (res != CURLE_OK) {
573 DBG( 1, "The scanner didn't respond: %s\n", curl_easy_strerror(res));
574 *status = SANE_STATUS_INVAL;
575 goto clean_data;
576 }
577 DBG( 10, "XML Capabilities[\n%s\n]\n", var->memory);
578 data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0);
579 if (data == NULL) {
580 *status = SANE_STATUS_NO_MEM;
581 goto clean_data;
582 }
583 node = xmlDocGetRootElement(data);
584 if (node == NULL) {
585 *status = SANE_STATUS_NO_MEM;
586 goto clean;
587 }
588
589 if (device->hack &&
590 header &&
591 header->memory &&
592 strstr(header->memory, "Server: HP_Compact_Server"))
593 device->hack = curl_slist_append(NULL, "Host: localhost");
594
595 scanner->source = 0;
596 scanner->Sources = (SANE_String_Const *)malloc(sizeof(SANE_String_Const) * 4);
597 for (i = 0; i < 4; i++)
598 scanner->Sources[i] = NULL;
599 print_xml_c(node, device, scanner, -1);
600 DBG (3, "1-blacklist_pdf: %s\n", (use_pdf ? "TRUE" : "FALSE") );
601 if (device->model_name != NULL) {
602 if (strcasestr(device->model_name, "MFC-J985DW")) {
603 DBG (3, "blacklist_pdf: device not support PDF\n");
604 use_pdf = SANE_FALSE;
605 }
606 else if (blacklist) {
607 char *model = strdup(device->model_name);
608 replace_char(model, ' ', '_');
609 if (strcasestr(blacklist, model)) {
610 use_pdf = SANE_FALSE;
611 }
612 free(model);
613 }
614 }
615 DBG (3, "1-blacklist_pdf: %s\n", (use_pdf ? "TRUE" : "FALSE") );
616 if (use_pdf)
617 _reduce_color_modes(scanner);
618 else
619 _delete_pdf(scanner);
620 clean:
621 xmlFreeDoc(data);
622 clean_data:
623 xmlCleanupParser();
624 xmlMemoryDump();
625 curl_easy_cleanup(curl_handle);
626 if (header)
627 free(header->memory);
628 free(header);
629 if (var)
630 free(var->memory);
631 free(var);
632 return (scanner);
633 }
634