• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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