kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			Add escl backend
							rodzic
							
								
									fdcb2fa5e8
								
							
						
					
					
						commit
						7c34046a39
					
				| 
						 | 
				
			
			@ -12,3 +12,7 @@ root = true                     ; look no further
 | 
			
		|||
charset = utf-8
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
 | 
			
		||||
[backend/escl/*]
 | 
			
		||||
indent_size = 4
 | 
			
		||||
indent_style = space
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,7 @@
 | 
			
		|||
variables:
 | 
			
		||||
  REGISTRY_HUB: "registry.gitlab.com/sane-project/ci-envs"
 | 
			
		||||
  CONFIGURE_MINI: "--enable-silent-rules"
 | 
			
		||||
  CONFIGURE_FULL: "--with-usb --enable-avahi --enable-pnm-backend"
 | 
			
		||||
  CONFIGURE_FULL: "--with-usb --enable-avahi --enable-pnm-backend --with-libcurl"
 | 
			
		||||
 | 
			
		||||
stages:
 | 
			
		||||
  - tarball
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								AUTHORS
								
								
								
								
							
							
						
						
									
										3
									
								
								AUTHORS
								
								
								
								
							| 
						 | 
				
			
			@ -32,6 +32,7 @@ Backends:
 | 
			
		|||
 epson:         Karl Heinz Kremer
 | 
			
		||||
 epson2:        Alessandro Zummo
 | 
			
		||||
 epsonds:       Alessandro Zummo
 | 
			
		||||
 escl:          Touboul Nathane, Thierry HUCHARD (*)
 | 
			
		||||
 fujitsu:       Randolph Bentson, Frederik Ramm, Oliver Schirrmeister,
 | 
			
		||||
                m. allan noah (*)
 | 
			
		||||
 genesys:       Henning Geinitz, Gerhard Jaeger (*), Stéphane Voltz,
 | 
			
		||||
| 
						 | 
				
			
			@ -248,9 +249,11 @@ Sergey Vlasov <vsu@altlinux.ru>
 | 
			
		|||
Simon Krix <kinsei@users.sourceforge.net>
 | 
			
		||||
Simon Munton <simon@munton.demon.co.uk>
 | 
			
		||||
Stéphane Voltz <stef.dev@free.fr>
 | 
			
		||||
Thierry HUCHARD <thierry@ordissimo.com>
 | 
			
		||||
Thomas Soumarmon <soumarmt@nerim.net>
 | 
			
		||||
Tom Martone <tom@martoneconsulting.com>
 | 
			
		||||
Tom Wang <tom.wang@mustek.com.tw>
 | 
			
		||||
Touboul Nathane <nathane.touboul@gmail.com>
 | 
			
		||||
Tristan Tarrant <ttarrant@etnoteam.it>
 | 
			
		||||
Troy Rollo <sane@troy.rollo.name>
 | 
			
		||||
Ullrich Sigwanz <usigwanz@freesurf.ch>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										20
									
								
								acinclude.m4
								
								
								
								
							
							
						
						
									
										20
									
								
								acinclude.m4
								
								
								
								
							| 
						 | 
				
			
			@ -611,6 +611,26 @@ for be in ${BACKENDS}; do
 | 
			
		|||
    fi
 | 
			
		||||
    ;;
 | 
			
		||||
 | 
			
		||||
    escl)
 | 
			
		||||
    if test "x${enable_avahi}" != "xyes"; then
 | 
			
		||||
      echo "*** $be backend requires AVAHI library - $DISABLE_MSG"
 | 
			
		||||
      backend_supported="no"
 | 
			
		||||
    fi
 | 
			
		||||
    if test "x${with_libcurl}" != "xyes"; then
 | 
			
		||||
      echo "*** $be backend requires cURL library - $DISABLE_MSG"
 | 
			
		||||
      backend_supported="no"
 | 
			
		||||
    fi
 | 
			
		||||
    if test "x${have_libxml}" != "xyes"; then
 | 
			
		||||
      echo "*** $be backend requires XML library - $DISABLE_MSG"
 | 
			
		||||
      backend_supported="no"
 | 
			
		||||
    fi
 | 
			
		||||
    # FIXME: Remove when PNG and/or PDF support have been added.
 | 
			
		||||
    if test "x${sane_cv_use_libjpeg}" != "xyes"; then
 | 
			
		||||
      echo "*** $be backend currently requires JPEG library - $DISABLE_MSG"
 | 
			
		||||
      backend_supported="no"
 | 
			
		||||
    fi
 | 
			
		||||
    ;;
 | 
			
		||||
 | 
			
		||||
    gphoto2)
 | 
			
		||||
    if test "${HAVE_GPHOTO2}" != "true" \
 | 
			
		||||
      || test "${sane_cv_use_libjpeg}" != "yes"; then
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,8 +68,8 @@ BACKEND_CONFS= abaton.conf agfafocus.conf apple.conf artec.conf \
 | 
			
		|||
	       canon_pp.conf cardscan.conf coolscan2.conf coolscan3.conf \
 | 
			
		||||
	       coolscan.conf dc210.conf dc240.conf dc25.conf \
 | 
			
		||||
	       dell1600n_net.conf dmc.conf epjitsu.conf epson2.conf \
 | 
			
		||||
           epson.conf epsonds.conf fujitsu.conf genesys.conf gphoto2.conf \
 | 
			
		||||
	       gt68xx.conf hp3900.conf hp4200.conf hp5400.conf \
 | 
			
		||||
	       epson.conf epsonds.conf escl.conf fujitsu.conf genesys.conf \
 | 
			
		||||
	       gphoto2.conf gt68xx.conf hp3900.conf hp4200.conf hp5400.conf \
 | 
			
		||||
	       hp.conf hpsj5s.conf hs2p.conf ibm.conf kodak.conf kodakaio.conf\
 | 
			
		||||
	       kvs1025.conf \
 | 
			
		||||
	       leo.conf lexmark.conf ma1509.conf magicolor.conf \
 | 
			
		||||
| 
						 | 
				
			
			@ -161,7 +161,7 @@ be_convenience_libs = libabaton.la libagfafocus.la \
 | 
			
		|||
    libcoolscan2.la libcoolscan3.la libdc25.la \
 | 
			
		||||
    libdc210.la libdc240.la libdell1600n_net.la \
 | 
			
		||||
    libdmc.la libdll.la libdll_preload.la libepjitsu.la libepson.la \
 | 
			
		||||
    libepson2.la libepsonds.la libfujitsu.la libgenesys.la \
 | 
			
		||||
    libepson2.la libepsonds.la libescl.la libfujitsu.la libgenesys.la \
 | 
			
		||||
    libgphoto2_i.la libgt68xx.la libhp.la \
 | 
			
		||||
    libhp3500.la libhp3900.la libhp4200.la \
 | 
			
		||||
    libhp5400.la libhp5590.la libhpljm1005.la \
 | 
			
		||||
| 
						 | 
				
			
			@ -194,8 +194,8 @@ be_dlopen_libs = libsane-abaton.la libsane-agfafocus.la \
 | 
			
		|||
    libsane-coolscan2.la libsane-coolscan3.la libsane-dc25.la \
 | 
			
		||||
    libsane-dc210.la libsane-dc240.la libsane-dell1600n_net.la \
 | 
			
		||||
    libsane-dmc.la libsane-epjitsu.la libsane-epson.la \
 | 
			
		||||
    libsane-epson2.la libsane-epsonds.la libsane-fujitsu.la libsane-genesys.la \
 | 
			
		||||
    libsane-gphoto2.la libsane-gt68xx.la libsane-hp.la \
 | 
			
		||||
    libsane-epson2.la libsane-epsonds.la libsane-escl.la libsane-fujitsu.la \
 | 
			
		||||
    libsane-genesys.la libsane-gphoto2.la libsane-gt68xx.la libsane-hp.la \
 | 
			
		||||
    libsane-hp3500.la libsane-hp3900.la libsane-hp4200.la \
 | 
			
		||||
    libsane-hp5400.la libsane-hp5590.la libsane-hpljm1005.la \
 | 
			
		||||
    libsane-hpsj5s.la libsane-hs2p.la libsane-ibm.la libsane-kodak.la libsane-kodakaio.la\
 | 
			
		||||
| 
						 | 
				
			
			@ -434,6 +434,21 @@ libsane_dmc_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
 | 
			
		|||
libsane_dmc_la_LIBADD = $(COMMON_LIBS) libdmc.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
 | 
			
		||||
EXTRA_DIST += dmc.conf.in
 | 
			
		||||
 | 
			
		||||
if have_libavahi
 | 
			
		||||
if have_libcurl
 | 
			
		||||
if have_libxml2
 | 
			
		||||
libescl_la_SOURCES = escl/escl.c  escl/escl_capabilities.c  escl/escl_devices.c  escl/escl.h  escl/escl_newjob.c  escl/escl_reset.c  escl/escl_scan.c  escl/escl_status.c
 | 
			
		||||
libescl_la_CPPFLAGS = $(AM_CPPFLAGS) $(JPEG_CFLAGS) $(XML_CFLAGS) $(libcurl_CFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=escl
 | 
			
		||||
 | 
			
		||||
nodist_libsane_escl_la_SOURCES = escl-s.c
 | 
			
		||||
libsane_escl_la_CPPFLAGS = $(AM_CPPFLAGS) $(JPEG_CFLAGS) $(XML_CFLAGS) $(libcurl_CFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=escl
 | 
			
		||||
libsane_escl_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
 | 
			
		||||
libsane_escl_la_LIBADD = $(COMMON_LIBS) libescl.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(JPEG_LIBS) $(XML_LIBS) $(libcurl_LIBS) $(AVAHI_LIBS)
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
EXTRA_DIST += escl.conf.in
 | 
			
		||||
 | 
			
		||||
libepjitsu_la_SOURCES = epjitsu.c epjitsu.h epjitsu-cmd.h
 | 
			
		||||
libepjitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epjitsu
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
# escl.conf -- ESCL configuration
 | 
			
		||||
# Lines starting with a # or a ; are comments. Comments must be on a
 | 
			
		||||
# line of their own. End-of-line comments are not supported.
 | 
			
		||||
# Explanation : if you can't detect your device but it's an eSCL device, modify this escl conf' file to use your device.
 | 
			
		||||
#               -> uncomment the lines below, from '[device]' to 'port'.
 | 
			
		||||
#               -> put your device name instead of 'EPSON X'.
 | 
			
		||||
#               -> put your type of protocol instead of 'https' : http or https.
 | 
			
		||||
#               -> put your device ip instead of '123.456.789.10'.
 | 
			
		||||
#               -> put the port that you use instead of '88'.
 | 
			
		||||
# For example, the lines below are for one device, but if you have several devices to use, you can duplicate the lines below as many times as you have devices.
 | 
			
		||||
 | 
			
		||||
#[device]
 | 
			
		||||
 | 
			
		||||
#model EPSON X
 | 
			
		||||
#type https
 | 
			
		||||
#ip 123.456.789.10
 | 
			
		||||
#port 88
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,960 @@
 | 
			
		|||
/* sane - Scanner Access Now Easy.
 | 
			
		||||
 | 
			
		||||
   Copyright (C) 2019 Touboul Nathane
 | 
			
		||||
   Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
 | 
			
		||||
 | 
			
		||||
   This file is part of the SANE package.
 | 
			
		||||
 | 
			
		||||
   SANE is free software; you can redistribute it and/or modify it under
 | 
			
		||||
   the terms of the GNU General Public License as published by the Free
 | 
			
		||||
   Software Foundation; either version 3 of the License, or (at your
 | 
			
		||||
   option) any later version.
 | 
			
		||||
 | 
			
		||||
   SANE is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 | 
			
		||||
   for more details.
 | 
			
		||||
 | 
			
		||||
   You should have received a copy of the GNU General Public License
 | 
			
		||||
   along with sane; see the file COPYING.  If not, write to the Free
 | 
			
		||||
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
			
		||||
 | 
			
		||||
   This file implements a SANE backend for eSCL scanners.  */
 | 
			
		||||
 | 
			
		||||
#include "escl.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <jpeglib.h>
 | 
			
		||||
#include <setjmp.h>
 | 
			
		||||
 | 
			
		||||
#include "../include/sane/saneopts.h"
 | 
			
		||||
#include "../include/sane/sanei.h"
 | 
			
		||||
#include "../include/sane/sanei_backend.h"
 | 
			
		||||
#include "../include/sane/sanei_config.h"
 | 
			
		||||
#include "../include/sane/sanei_debug.h"
 | 
			
		||||
 | 
			
		||||
#define min(A,B) (((A)<(B)) ? (A) : (B))
 | 
			
		||||
#define max(A,B) (((A)>(B)) ? (A) : (B))
 | 
			
		||||
#define INPUT_BUFFER_SIZE 4096
 | 
			
		||||
 | 
			
		||||
static const SANE_Device **devlist = NULL;
 | 
			
		||||
static ESCL_Device *list_devices_primary = NULL;
 | 
			
		||||
static int num_devices = 0;
 | 
			
		||||
 | 
			
		||||
typedef struct Handled {
 | 
			
		||||
    struct Handled *next;
 | 
			
		||||
    SANE_String_Const name;
 | 
			
		||||
    char *result;
 | 
			
		||||
    ESCL_ScanParam param;
 | 
			
		||||
    SANE_Option_Descriptor opt[NUM_OPTIONS];
 | 
			
		||||
    Option_Value val[NUM_OPTIONS];
 | 
			
		||||
    capabilities_t *scanner;
 | 
			
		||||
    SANE_Range x_range;
 | 
			
		||||
    SANE_Range y_range;
 | 
			
		||||
    unsigned char *img_data;
 | 
			
		||||
    long img_size;
 | 
			
		||||
    long img_read;
 | 
			
		||||
    SANE_Bool cancel;
 | 
			
		||||
    SANE_Bool write_scan_data;
 | 
			
		||||
    SANE_Bool decompress_scan_data;
 | 
			
		||||
    SANE_Bool end_read;
 | 
			
		||||
    SANE_Parameters ps;
 | 
			
		||||
} escl_sane_t;
 | 
			
		||||
 | 
			
		||||
struct my_error_mgr
 | 
			
		||||
{
 | 
			
		||||
    struct jpeg_error_mgr errmgr;
 | 
			
		||||
    jmp_buf escape;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
    struct jpeg_source_mgr pub;
 | 
			
		||||
    FILE *ctx;
 | 
			
		||||
    unsigned char buffer[INPUT_BUFFER_SIZE];
 | 
			
		||||
} my_source_mgr;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static SANE_Status escl_add_in_list(ESCL_Device *current)
 | 
			
		||||
 * \brief Function that adds all the element needed to my list :
 | 
			
		||||
 *        the port number, the model name, the ip address, and the type of url (http/https).
 | 
			
		||||
 *        Moreover, this function counts the number of devices found.
 | 
			
		||||
 *
 | 
			
		||||
 * \return SANE_STATUS_GOOD if everything is OK.
 | 
			
		||||
 */
 | 
			
		||||
static SANE_Status
 | 
			
		||||
escl_add_in_list(ESCL_Device *current)
 | 
			
		||||
{
 | 
			
		||||
    ++num_devices;
 | 
			
		||||
    current->next = list_devices_primary;
 | 
			
		||||
    list_devices_primary = current;
 | 
			
		||||
    return (SANE_STATUS_GOOD);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn SANE_Status escl_device_add(int port_nb, const char *model_name, char *ip_address, char *type)
 | 
			
		||||
 * \brief Function that browses my list ('for' loop) and returns the "escl_add_in_list" function to
 | 
			
		||||
 *        adds all the element needed to my list :
 | 
			
		||||
 *        the port number, the model name, the ip address and the type of the url (http / https).
 | 
			
		||||
 *
 | 
			
		||||
 * \return escl_add_in_list(current)
 | 
			
		||||
 */
 | 
			
		||||
SANE_Status
 | 
			
		||||
escl_device_add(int port_nb, const char *model_name, char *ip_address, char *type)
 | 
			
		||||
{
 | 
			
		||||
    ESCL_Device *current = NULL;
 | 
			
		||||
    DBG (10, "escl_device_add\n");
 | 
			
		||||
    for (current = list_devices_primary; current; current = current->next) {
 | 
			
		||||
        if (strcmp(current->ip_address, ip_address) == 0 && current->port_nb == port_nb
 | 
			
		||||
            && strcmp(current->type, type) == 0)
 | 
			
		||||
            return (SANE_STATUS_GOOD);
 | 
			
		||||
    }
 | 
			
		||||
    current = malloc(sizeof(*current));
 | 
			
		||||
    if (current == NULL)
 | 
			
		||||
        return (SANE_STATUS_NO_MEM);
 | 
			
		||||
    memset(current, 0, sizeof(*current));
 | 
			
		||||
    current->port_nb = port_nb;
 | 
			
		||||
    current->model_name = strdup(model_name);
 | 
			
		||||
    current->ip_address = strdup(ip_address);
 | 
			
		||||
    current->type = strdup(type);
 | 
			
		||||
    return escl_add_in_list(current);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static inline size_t max_string_size(const SANE_String_Const strings[])
 | 
			
		||||
 * \brief Function that browses the string ('for' loop) and counts the number of character in the string.
 | 
			
		||||
 *        --> this allows to know the maximum size of the string.
 | 
			
		||||
 *
 | 
			
		||||
 * \return max_size + 1 (the size max)
 | 
			
		||||
 */
 | 
			
		||||
static inline size_t
 | 
			
		||||
max_string_size(const SANE_String_Const strings[])
 | 
			
		||||
{
 | 
			
		||||
    size_t max_size = 0;
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; strings[i]; ++i) {
 | 
			
		||||
        size_t size = strlen (strings[i]);
 | 
			
		||||
        if (size > max_size)
 | 
			
		||||
            max_size = size;
 | 
			
		||||
    }
 | 
			
		||||
    return (max_size + 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static SANE_Device *convertFromESCLDev(ESCL_Device *cdev)
 | 
			
		||||
 * \brief Function that checks if the url of the received scanner is secured or not (http / https).
 | 
			
		||||
 *        --> if the url is not secured, our own url will be composed like "http://'ip':'port'".
 | 
			
		||||
 *        --> else, our own url will be composed like "https://'ip':'port'".
 | 
			
		||||
 *        AND, it's in this function that we gather all the informations of the url (that were in our list) :
 | 
			
		||||
 *        the model_name, the port, the ip, and the type of url.
 | 
			
		||||
 *        SO, leaving this function, we have in memory the complete url.
 | 
			
		||||
 *
 | 
			
		||||
 * \return sdev (structure that contains the elements of the url)
 | 
			
		||||
 */
 | 
			
		||||
static SANE_Device *
 | 
			
		||||
convertFromESCLDev(ESCL_Device *cdev)
 | 
			
		||||
{
 | 
			
		||||
    SANE_Device *sdev = (SANE_Device*) calloc(1, sizeof(SANE_Device));
 | 
			
		||||
    char tmp[PATH_MAX] = { 0 };
 | 
			
		||||
 | 
			
		||||
    if (strcmp(cdev->type, "_uscan._tcp") == 0 || strcmp(cdev->type, "http") == 0)
 | 
			
		||||
        snprintf(tmp, sizeof(tmp), "http://%s:%d", cdev->ip_address, cdev->port_nb);
 | 
			
		||||
    else
 | 
			
		||||
        snprintf(tmp, sizeof(tmp), "https://%s:%d", cdev->ip_address, cdev->port_nb);
 | 
			
		||||
    sdev->name = strdup(tmp);
 | 
			
		||||
    sdev->model = strdup(cdev->model_name);
 | 
			
		||||
    sdev->vendor = strdup("ESCL");
 | 
			
		||||
    sdev->type = strdup("flatbed scanner");
 | 
			
		||||
    return (sdev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize)
 | 
			
		||||
 * \brief Function that's called before any other SANE function ; it's the first SANE function called.
 | 
			
		||||
 *        --> this function checks the SANE config. and can check the authentication of the user if
 | 
			
		||||
 *        'authorize' value is more than SANE_TRUE.
 | 
			
		||||
 *        In this case, it will be necessary to define an authentication method.
 | 
			
		||||
 *
 | 
			
		||||
 * \return SANE_STATUS_GOOD (everything is OK)
 | 
			
		||||
 */
 | 
			
		||||
SANE_Status
 | 
			
		||||
sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize)
 | 
			
		||||
{
 | 
			
		||||
    DBG_INIT();
 | 
			
		||||
    DBG (10, "escl sane_init\n");
 | 
			
		||||
    SANE_Status status = SANE_STATUS_GOOD;
 | 
			
		||||
 | 
			
		||||
    if (version_code != NULL)
 | 
			
		||||
        *version_code = SANE_VERSION_CODE(1, 0, 0);
 | 
			
		||||
    if (status != SANE_STATUS_GOOD)
 | 
			
		||||
        return (status);
 | 
			
		||||
    return (SANE_STATUS_GOOD);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn void sane_exit(void)
 | 
			
		||||
 * \brief Function that must be called to terminate use of a backend.
 | 
			
		||||
 *        This function will first close all device handles that still might be open.
 | 
			
		||||
 *        --> by freeing all the elements of my list.
 | 
			
		||||
 *        After this function, no function other than 'sane_init' may be called.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
sane_exit(void)
 | 
			
		||||
{
 | 
			
		||||
    DBG (10, "escl sane_exit\n");
 | 
			
		||||
    ESCL_Device *next = NULL;
 | 
			
		||||
 | 
			
		||||
    while (list_devices_primary != NULL) {
 | 
			
		||||
        next = list_devices_primary->next;
 | 
			
		||||
        free(list_devices_primary);
 | 
			
		||||
        list_devices_primary = next;
 | 
			
		||||
    }
 | 
			
		||||
    if (devlist)
 | 
			
		||||
        free (devlist);
 | 
			
		||||
    list_devices_primary = NULL;
 | 
			
		||||
    devlist = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static SANE_Status attach_one_config(SANEI_Config *config, const char *line)
 | 
			
		||||
 * \brief Function that implements a configuration file to the user :
 | 
			
		||||
 *        if the user can't detect some devices, he will be able to force their detection with this config' file to use them.
 | 
			
		||||
 *        Thus, this function parses the config' file to use the device of the user with the information below :
 | 
			
		||||
 *        the type of protocol (http/https), the ip, the port number, and the model name.
 | 
			
		||||
 *
 | 
			
		||||
 * \return escl_add_in_list(escl_device) if the parsing worked, SANE_STATUS_GOOD otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static SANE_Status
 | 
			
		||||
attach_one_config(SANEI_Config __sane_unused__ *config, const char *line)
 | 
			
		||||
{
 | 
			
		||||
    int port = 0;
 | 
			
		||||
    static int count = 0;
 | 
			
		||||
    static ESCL_Device *escl_device = NULL;
 | 
			
		||||
 | 
			
		||||
    if (strncmp(line, "[device]", 8) == 0) {
 | 
			
		||||
        count = 0;
 | 
			
		||||
        escl_device = (ESCL_Device*)calloc(1, sizeof(ESCL_Device));
 | 
			
		||||
    }
 | 
			
		||||
    if (strncmp(line, "ip", 2) == 0) {
 | 
			
		||||
        const char *ip_space = sanei_config_skip_whitespace(line + 2);
 | 
			
		||||
        if (escl_device != NULL && ip_space != NULL) {
 | 
			
		||||
            count++;
 | 
			
		||||
            escl_device->ip_address = strdup(ip_space);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (sscanf(line, "port %i", &port) == 1 && port != 0) {
 | 
			
		||||
        const char *port_space = sanei_config_skip_whitespace(line + 4);
 | 
			
		||||
        if (escl_device != NULL && port_space != NULL) {
 | 
			
		||||
            count++;
 | 
			
		||||
            escl_device->port_nb = port;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (strncmp(line, "model", 5) == 0) {
 | 
			
		||||
        const char *model_space = sanei_config_skip_whitespace(line + 5);
 | 
			
		||||
        if (escl_device != NULL && model_space != NULL) {
 | 
			
		||||
            count++;
 | 
			
		||||
            escl_device->model_name = strdup(model_space);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (strncmp(line, "type", 4) == 0) {
 | 
			
		||||
        const char *type_space = sanei_config_skip_whitespace(line + 4);
 | 
			
		||||
        if (escl_device != NULL && type_space != NULL) {
 | 
			
		||||
            count++;
 | 
			
		||||
            escl_device->type = strdup(type_space);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (count == 4)
 | 
			
		||||
        return (escl_add_in_list(escl_device));
 | 
			
		||||
    return (SANE_STATUS_GOOD);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
 | 
			
		||||
 * \brief Function that searches for connected devices and places them in our 'device_list'. ('for' loop)
 | 
			
		||||
 *        If the attribute 'local_only' is worth SANE_FALSE, we only returns the connected devices locally.
 | 
			
		||||
 *
 | 
			
		||||
 * \return SANE_STATUS_GOOD if devlist != NULL ; SANE_STATUS_NO_MEM otherwise.
 | 
			
		||||
 */
 | 
			
		||||
SANE_Status
 | 
			
		||||
sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
 | 
			
		||||
{
 | 
			
		||||
    if (local_only)             /* eSCL is a network-only protocol */
 | 
			
		||||
        return (device_list ? SANE_STATUS_GOOD : SANE_STATUS_INVAL);
 | 
			
		||||
 | 
			
		||||
    DBG (10, "escl sane_get_devices\n");
 | 
			
		||||
    ESCL_Device *dev = NULL;
 | 
			
		||||
    static const SANE_Device **devlist = 0;
 | 
			
		||||
    SANE_Status status;
 | 
			
		||||
 | 
			
		||||
    if (device_list == NULL)
 | 
			
		||||
        return (SANE_STATUS_INVAL);
 | 
			
		||||
    status = sanei_configure_attach(ESCL_CONFIG_FILE, NULL, attach_one_config);
 | 
			
		||||
    if (status != SANE_STATUS_GOOD)
 | 
			
		||||
        return (status);
 | 
			
		||||
    escl_devices(&status);
 | 
			
		||||
    if (status != SANE_STATUS_GOOD)
 | 
			
		||||
        return (status);
 | 
			
		||||
    if (devlist)
 | 
			
		||||
        free(devlist);
 | 
			
		||||
    devlist = (const SANE_Device **) calloc (num_devices + 1, sizeof (devlist[0]));
 | 
			
		||||
    if (devlist == NULL)
 | 
			
		||||
        return (SANE_STATUS_NO_MEM);
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    for (dev = list_devices_primary; i < num_devices; dev = dev->next) {
 | 
			
		||||
        SANE_Device *s_dev = convertFromESCLDev(dev);
 | 
			
		||||
        devlist[i] = s_dev;
 | 
			
		||||
        i++;
 | 
			
		||||
    }
 | 
			
		||||
    devlist[i] = 0;
 | 
			
		||||
    *device_list = devlist;
 | 
			
		||||
    return (devlist) ? SANE_STATUS_GOOD : SANE_STATUS_NO_MEM;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static SANE_Status init_options(SANE_String_Const name, escl_sane_t *s)
 | 
			
		||||
 * \brief Function thzt initializes all the needed options of the received scanner
 | 
			
		||||
 *        (the resolution / the color / the margins) thanks to the informations received with
 | 
			
		||||
 *        the 'escl_capabilities' function, called just before.
 | 
			
		||||
 *
 | 
			
		||||
 * \return status (if everything is OK, status = SANE_STATUS_GOOD)
 | 
			
		||||
 */
 | 
			
		||||
static SANE_Status
 | 
			
		||||
init_options(SANE_String_Const name, escl_sane_t *s)
 | 
			
		||||
{
 | 
			
		||||
    DBG (10, "escl init_options\n");
 | 
			
		||||
    SANE_Status status = SANE_STATUS_GOOD;
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    if (name == NULL)
 | 
			
		||||
        return (SANE_STATUS_INVAL);
 | 
			
		||||
    memset (s->opt, 0, sizeof (s->opt));
 | 
			
		||||
    memset (s->val, 0, sizeof (s->val));
 | 
			
		||||
    for (i = 0; i < NUM_OPTIONS; ++i) {
 | 
			
		||||
        s->opt[i].size = sizeof (SANE_Word);
 | 
			
		||||
        s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
			
		||||
    }
 | 
			
		||||
    s->x_range.min = 0;
 | 
			
		||||
    s->x_range.max = s->scanner->MaxWidth - s->scanner->MinWidth;
 | 
			
		||||
    s->x_range.quant = 1;
 | 
			
		||||
    s->y_range.min = 0;
 | 
			
		||||
    s->y_range.max = s->scanner->MaxHeight - s->scanner->MinHeight;
 | 
			
		||||
    s->y_range.quant = 1;
 | 
			
		||||
    s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
 | 
			
		||||
    s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
 | 
			
		||||
    s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
 | 
			
		||||
    s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
 | 
			
		||||
    s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
 | 
			
		||||
 | 
			
		||||
    s->opt[OPT_MODE_GROUP].title = "Scan Mode";
 | 
			
		||||
    s->opt[OPT_MODE_GROUP].desc = "";
 | 
			
		||||
    s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
 | 
			
		||||
    s->opt[OPT_MODE_GROUP].cap = 0;
 | 
			
		||||
    s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
			
		||||
 | 
			
		||||
    s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
 | 
			
		||||
    s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
 | 
			
		||||
    s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
 | 
			
		||||
    s->opt[OPT_MODE].type = SANE_TYPE_STRING;
 | 
			
		||||
    s->opt[OPT_MODE].unit = SANE_UNIT_NONE;
 | 
			
		||||
    s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
			
		||||
    s->opt[OPT_MODE].constraint.string_list = s->scanner->ColorModes;
 | 
			
		||||
    s->val[OPT_MODE].s = (char *)strdup(s->scanner->ColorModes[0]);
 | 
			
		||||
    s->opt[OPT_MODE].size = max_string_size(s->scanner->ColorModes);
 | 
			
		||||
    s->scanner->default_color = (char *)strdup(s->scanner->ColorModes[0]);
 | 
			
		||||
 | 
			
		||||
    s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
 | 
			
		||||
    s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
 | 
			
		||||
    s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
 | 
			
		||||
    s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
 | 
			
		||||
    s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
 | 
			
		||||
    s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
 | 
			
		||||
    s->opt[OPT_RESOLUTION].constraint.word_list = s->scanner->SupportedResolutions;
 | 
			
		||||
    s->val[OPT_RESOLUTION].w = s->scanner->SupportedResolutions[1];
 | 
			
		||||
    s->scanner->default_resolution = s->scanner->SupportedResolutions[1];
 | 
			
		||||
 | 
			
		||||
    s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
 | 
			
		||||
    s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
 | 
			
		||||
    s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
 | 
			
		||||
    s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
 | 
			
		||||
    s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
 | 
			
		||||
    s->val[OPT_PREVIEW].w = SANE_FALSE;
 | 
			
		||||
 | 
			
		||||
    s->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW;
 | 
			
		||||
    s->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW;
 | 
			
		||||
    s->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW;
 | 
			
		||||
    s->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL;
 | 
			
		||||
    s->val[OPT_GRAY_PREVIEW].w = SANE_FALSE;
 | 
			
		||||
 | 
			
		||||
    s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
 | 
			
		||||
    s->opt[OPT_GEOMETRY_GROUP].desc = "";
 | 
			
		||||
    s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
 | 
			
		||||
    s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
 | 
			
		||||
    s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
			
		||||
 | 
			
		||||
    s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
 | 
			
		||||
    s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
 | 
			
		||||
    s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
 | 
			
		||||
    s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
 | 
			
		||||
    s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
 | 
			
		||||
    s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
			
		||||
    s->opt[OPT_TL_X].constraint.range = &s->x_range;
 | 
			
		||||
    s->val[OPT_TL_X].w = s->scanner->RiskyLeftMargin;
 | 
			
		||||
 | 
			
		||||
    s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
 | 
			
		||||
    s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
 | 
			
		||||
    s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
 | 
			
		||||
    s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
 | 
			
		||||
    s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
 | 
			
		||||
    s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
			
		||||
    s->opt[OPT_TL_Y].constraint.range = &s->y_range;
 | 
			
		||||
    s->val[OPT_TL_Y].w = s->scanner->RiskyTopMargin;
 | 
			
		||||
 | 
			
		||||
    s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
 | 
			
		||||
    s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
 | 
			
		||||
    s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
 | 
			
		||||
    s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
 | 
			
		||||
    s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
 | 
			
		||||
    s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
			
		||||
    s->opt[OPT_BR_X].constraint.range = &s->x_range;
 | 
			
		||||
    s->val[OPT_BR_X].w = s->scanner->MaxWidth;
 | 
			
		||||
 | 
			
		||||
    s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
 | 
			
		||||
    s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
 | 
			
		||||
    s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
 | 
			
		||||
    s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
 | 
			
		||||
    s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
 | 
			
		||||
    s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
			
		||||
    s->opt[OPT_BR_Y].constraint.range = &s->y_range;
 | 
			
		||||
    s->val[OPT_BR_Y].w = s->scanner->MaxHeight;
 | 
			
		||||
    return (status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn SANE_Status sane_open(SANE_String_Const name, SANE_Handle *h)
 | 
			
		||||
 * \brief Function that establishes a connection with the device named by 'name',
 | 
			
		||||
 *        and returns a 'handler' using 'SANE_Handle *h', representing it.
 | 
			
		||||
 *        Thus, it's this function that calls the 'escl_status' function firstly,
 | 
			
		||||
 *        then the 'escl_capabilities' function, and, after, the 'init_options' function.
 | 
			
		||||
 *
 | 
			
		||||
 * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
 | 
			
		||||
 */
 | 
			
		||||
SANE_Status
 | 
			
		||||
sane_open(SANE_String_Const name, SANE_Handle *h)
 | 
			
		||||
{
 | 
			
		||||
    DBG (10, "escl sane_open\n");
 | 
			
		||||
    SANE_Status status;
 | 
			
		||||
    escl_sane_t *handler = NULL;
 | 
			
		||||
 | 
			
		||||
    if (name == NULL)
 | 
			
		||||
        return (SANE_STATUS_INVAL);
 | 
			
		||||
    status = escl_status(name);
 | 
			
		||||
    if (status != SANE_STATUS_GOOD)
 | 
			
		||||
        return (status);
 | 
			
		||||
    handler = (escl_sane_t *)calloc(1, sizeof(escl_sane_t));
 | 
			
		||||
    if (handler == NULL)
 | 
			
		||||
        return (SANE_STATUS_NO_MEM);
 | 
			
		||||
    handler->name = strdup(name);
 | 
			
		||||
    handler->scanner = escl_capabilities(name, &status);
 | 
			
		||||
    if (status != SANE_STATUS_GOOD)
 | 
			
		||||
        return (status);
 | 
			
		||||
    status = init_options(name, handler);
 | 
			
		||||
    if (status != SANE_STATUS_GOOD)
 | 
			
		||||
        return (status);
 | 
			
		||||
    handler->ps.depth = 8;
 | 
			
		||||
    handler->ps.last_frame = SANE_TRUE;
 | 
			
		||||
    handler->ps.format = SANE_FRAME_RGB;
 | 
			
		||||
    handler->ps.pixels_per_line = handler->val[OPT_BR_X].w;
 | 
			
		||||
    handler->ps.lines = handler->val[OPT_BR_Y].w;
 | 
			
		||||
    handler->ps.bytes_per_line = handler->ps.pixels_per_line * 3;
 | 
			
		||||
    status = sane_get_parameters(handler, 0);
 | 
			
		||||
    if (status != SANE_STATUS_GOOD)
 | 
			
		||||
        return (status);
 | 
			
		||||
    handler->cancel = SANE_FALSE;
 | 
			
		||||
    handler->write_scan_data = SANE_FALSE;
 | 
			
		||||
    handler->decompress_scan_data = SANE_FALSE;
 | 
			
		||||
    handler->end_read = SANE_FALSE;
 | 
			
		||||
    *h = handler;
 | 
			
		||||
    return (status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn void sane_cancel(SANE_Handle h)
 | 
			
		||||
 * \brief Function that's used to, immediately or as quickly as possible, cancel the currently
 | 
			
		||||
 *        pending operation of the device represented by 'SANE_Handle h'.
 | 
			
		||||
 *        This functions calls the 'escl_scanner' functions, that resets the scan operations.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
sane_cancel(SANE_Handle h)
 | 
			
		||||
{
 | 
			
		||||
    DBG (10, "escl sane_cancel\n");
 | 
			
		||||
    escl_sane_t *handler = h;
 | 
			
		||||
 | 
			
		||||
    handler->cancel = SANE_TRUE;
 | 
			
		||||
    escl_scanner(handler->name, handler->result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn void sane_close(SANE_Handle h)
 | 
			
		||||
 * \brief Function that closes the communication with the device represented by 'SANE_Handle h'.
 | 
			
		||||
 *        This function must release the resources that were allocated to the opening of 'h'.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
sane_close(SANE_Handle h)
 | 
			
		||||
{
 | 
			
		||||
    DBG (10, "escl sane_close\n");
 | 
			
		||||
    if (h != NULL) {
 | 
			
		||||
        free(h);
 | 
			
		||||
        h = NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn const SANE_Option_Descriptor *sane_get_option_descriptor(SANE_Handle h, SANE_Int n)
 | 
			
		||||
 * \brief Function that retrieves a descriptor from the n number option of the scanner
 | 
			
		||||
 *        represented by 'h'.
 | 
			
		||||
 *        The descriptor remains valid until the machine is closed.
 | 
			
		||||
 *
 | 
			
		||||
 * \return s->opt + n
 | 
			
		||||
 */
 | 
			
		||||
const SANE_Option_Descriptor *
 | 
			
		||||
sane_get_option_descriptor(SANE_Handle h, SANE_Int n)
 | 
			
		||||
{
 | 
			
		||||
    DBG (10, "escl sane_get_option_descriptor\n");
 | 
			
		||||
    escl_sane_t *s = h;
 | 
			
		||||
 | 
			
		||||
    if ((unsigned) n >= NUM_OPTIONS || n < 0)
 | 
			
		||||
        return (0);
 | 
			
		||||
    return (s->opt + n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn SANE_Status sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int *i)
 | 
			
		||||
 * \brief Function that defines the actions to perform for the 'n' option of the machine,
 | 
			
		||||
 *        represented by 'h', if the action is 'a'.
 | 
			
		||||
 *        There are 3 types of possible actions :
 | 
			
		||||
 *        --> SANE_ACTION_GET_VALUE: 'v' must be used to provide the value of the option.
 | 
			
		||||
 *        --> SANE_ACTION_SET_VALUE: The option must take the 'v' value.
 | 
			
		||||
 *        --> SANE_ACTION_SET_AUTO: The backend or machine must affect the option with an appropriate value.
 | 
			
		||||
 *        Moreover, the parameter 'i' is used to provide additional information about the state of
 | 
			
		||||
 *        'n' option if SANE_ACTION_SET_VALUE has been performed.
 | 
			
		||||
 *
 | 
			
		||||
 * \return SANE_STATUS_GOOD if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL
 | 
			
		||||
 */
 | 
			
		||||
SANE_Status
 | 
			
		||||
sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int *i)
 | 
			
		||||
{
 | 
			
		||||
    DBG (10, "escl sane_control_option\n");
 | 
			
		||||
    escl_sane_t *handler = h;
 | 
			
		||||
 | 
			
		||||
    if (i)
 | 
			
		||||
        *i = 0;
 | 
			
		||||
    if (n >= NUM_OPTIONS || n < 0)
 | 
			
		||||
        return (SANE_STATUS_INVAL);
 | 
			
		||||
    if (a == SANE_ACTION_GET_VALUE) {
 | 
			
		||||
        switch (n) {
 | 
			
		||||
        case OPT_NUM_OPTS:
 | 
			
		||||
        case OPT_RESOLUTION:
 | 
			
		||||
        case OPT_TL_X:
 | 
			
		||||
        case OPT_TL_Y:
 | 
			
		||||
        case OPT_BR_X:
 | 
			
		||||
        case OPT_BR_Y:
 | 
			
		||||
        case OPT_PREVIEW:
 | 
			
		||||
        case OPT_GRAY_PREVIEW:
 | 
			
		||||
            *(SANE_Word *) v = handler->val[n].w;
 | 
			
		||||
            break;
 | 
			
		||||
        case OPT_MODE:
 | 
			
		||||
            strcpy (v, handler->val[n].s);
 | 
			
		||||
            break;
 | 
			
		||||
        case OPT_MODE_GROUP:
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        return (SANE_STATUS_GOOD);
 | 
			
		||||
    }
 | 
			
		||||
    if (a == SANE_ACTION_SET_VALUE) {
 | 
			
		||||
        switch (n) {
 | 
			
		||||
        case OPT_TL_X:
 | 
			
		||||
        case OPT_TL_Y:
 | 
			
		||||
        case OPT_BR_X:
 | 
			
		||||
        case OPT_BR_Y:
 | 
			
		||||
        case OPT_PREVIEW:
 | 
			
		||||
        case OPT_GRAY_PREVIEW:
 | 
			
		||||
            handler->val[n].w = *(SANE_Word *) v;
 | 
			
		||||
            if (i && handler->val[n].w != *(SANE_Word *) v)
 | 
			
		||||
                *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
 | 
			
		||||
            handler->val[n].w = *(SANE_Word *) v;
 | 
			
		||||
            break;
 | 
			
		||||
        case OPT_RESOLUTION:
 | 
			
		||||
            handler->val[n].w = *(SANE_Word *) v;
 | 
			
		||||
            if (i)
 | 
			
		||||
                *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
 | 
			
		||||
            break;
 | 
			
		||||
        case OPT_MODE:
 | 
			
		||||
            if (handler->val[n].s)
 | 
			
		||||
                free (handler->val[n].s);
 | 
			
		||||
            handler->val[n].s = strdup (v);
 | 
			
		||||
            if (i)
 | 
			
		||||
                *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return (SANE_STATUS_GOOD);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if(defined HAVE_LIBJPEG)
 | 
			
		||||
static void
 | 
			
		||||
error_exit(j_common_ptr cinfo)
 | 
			
		||||
{
 | 
			
		||||
    longjmp(cinfo->client_data, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static void get_JPEG_dimension(FILE *fp, int *w, int *h)
 | 
			
		||||
 * \brief Function that aims to get the dimensions of the jpeg image wich will be scanned.
 | 
			
		||||
 *        This function is called in the "sane_start" function.
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
get_JPEG_dimension(FILE *fp, int *w, int *h)
 | 
			
		||||
{
 | 
			
		||||
    struct jpeg_decompress_struct cinfo;
 | 
			
		||||
    struct jpeg_error_mgr jerr;
 | 
			
		||||
    jmp_buf env;
 | 
			
		||||
 | 
			
		||||
    cinfo.err = jpeg_std_error(&jerr);
 | 
			
		||||
    jerr.error_exit = error_exit;
 | 
			
		||||
    cinfo.client_data = env;
 | 
			
		||||
    if (setjmp(env))
 | 
			
		||||
        return;
 | 
			
		||||
    jpeg_create_decompress(&cinfo);
 | 
			
		||||
    jpeg_stdio_src(&cinfo, fp);
 | 
			
		||||
    jpeg_read_header(&cinfo, TRUE);
 | 
			
		||||
    cinfo.out_color_space = JCS_RGB;
 | 
			
		||||
    jpeg_start_decompress(&cinfo);
 | 
			
		||||
    *w = cinfo.output_width;
 | 
			
		||||
    *h = cinfo.output_height;
 | 
			
		||||
    jpeg_finish_decompress(&cinfo);
 | 
			
		||||
    jpeg_destroy_decompress(&cinfo);
 | 
			
		||||
    fseek(fp, SEEK_SET, 0);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn SANE_Status sane_start(SANE_Handle h)
 | 
			
		||||
 * \brief Function that initiates aquisition of an image from the device represented by handle 'h'.
 | 
			
		||||
 *        This function calls the "escl_newjob" function and the "escl_scan" function.
 | 
			
		||||
 *
 | 
			
		||||
 * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
 | 
			
		||||
 */
 | 
			
		||||
SANE_Status
 | 
			
		||||
sane_start(SANE_Handle h)
 | 
			
		||||
{
 | 
			
		||||
    DBG (10, "escl sane_start\n");
 | 
			
		||||
    SANE_Status status = SANE_STATUS_GOOD;
 | 
			
		||||
    escl_sane_t *handler = h;
 | 
			
		||||
    int w = 0;
 | 
			
		||||
    int he = 0;
 | 
			
		||||
 | 
			
		||||
    if (handler->name == NULL)
 | 
			
		||||
        return (SANE_STATUS_INVAL);
 | 
			
		||||
    handler->cancel = SANE_FALSE;
 | 
			
		||||
    handler->write_scan_data = SANE_FALSE;
 | 
			
		||||
    handler->decompress_scan_data = SANE_FALSE;
 | 
			
		||||
    handler->end_read = SANE_FALSE;
 | 
			
		||||
    handler->scanner->height = handler->val[OPT_BR_Y].w;
 | 
			
		||||
    handler->scanner->width = handler->val[OPT_BR_X].w;
 | 
			
		||||
    handler->scanner->pos_x = handler->val[OPT_TL_X].w;
 | 
			
		||||
    handler->scanner->pos_y = handler->val[OPT_TL_Y].w;
 | 
			
		||||
    if(handler->scanner->default_color)
 | 
			
		||||
       free(handler->scanner->default_color);
 | 
			
		||||
    if (handler->val[OPT_PREVIEW].w == SANE_TRUE)
 | 
			
		||||
    {
 | 
			
		||||
       int i = 0, val = 9999;;
 | 
			
		||||
       if (handler->val[OPT_GRAY_PREVIEW].w == SANE_TRUE ||
 | 
			
		||||
           !strncasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY, 3))
 | 
			
		||||
          handler->scanner->default_color = strdup("Grayscale8");
 | 
			
		||||
       else
 | 
			
		||||
          handler->scanner->default_color = strdup("RGB24");
 | 
			
		||||
       for (i = 1; i < handler->scanner->SupportedResolutionsSize; i++)
 | 
			
		||||
       {
 | 
			
		||||
          if (val > handler->scanner->SupportedResolutions[i])
 | 
			
		||||
              val = handler->scanner->SupportedResolutions[i];
 | 
			
		||||
       }
 | 
			
		||||
       handler->scanner->default_resolution = val;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
    handler->scanner->default_resolution = handler->val[OPT_RESOLUTION].w;
 | 
			
		||||
    if (!strncasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY, 3))
 | 
			
		||||
       handler->scanner->default_color = strdup("Grayscale8");
 | 
			
		||||
    else
 | 
			
		||||
       handler->scanner->default_color = strdup("RGB24");
 | 
			
		||||
    }
 | 
			
		||||
    handler->result = escl_newjob(handler->scanner, handler->name, &status);
 | 
			
		||||
    if (status != SANE_STATUS_GOOD)
 | 
			
		||||
        return (status);
 | 
			
		||||
    status = escl_scan(handler->scanner, handler->name, handler->result);
 | 
			
		||||
    get_JPEG_dimension(handler->scanner->tmp, &w, &he);
 | 
			
		||||
    fseek(handler->scanner->tmp, SEEK_SET, 0);
 | 
			
		||||
    handler->ps.depth = 8;
 | 
			
		||||
    handler->ps.pixels_per_line = w;
 | 
			
		||||
    handler->ps.lines = he;
 | 
			
		||||
    handler->ps.bytes_per_line = w * 3;
 | 
			
		||||
    handler->ps.last_frame = SANE_TRUE;
 | 
			
		||||
    handler->ps.format = SANE_FRAME_RGB;
 | 
			
		||||
    return (status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn SANE_Status sane_get_parameters(SANE_Handle h, SANE_Parameters *p)
 | 
			
		||||
 * \brief Function that retrieves the device parameters represented by 'h' and stores them in 'p'.
 | 
			
		||||
 *        This function is normally used after "sane_start".
 | 
			
		||||
 *        It's in this function that we choose to assign the default color. (Color or Monochrome)
 | 
			
		||||
 *
 | 
			
		||||
 * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
 | 
			
		||||
 */
 | 
			
		||||
SANE_Status
 | 
			
		||||
sane_get_parameters(SANE_Handle h, SANE_Parameters *p)
 | 
			
		||||
{
 | 
			
		||||
    DBG (10, "escl sane_get_parameters\n");
 | 
			
		||||
    SANE_Status status = SANE_STATUS_GOOD;
 | 
			
		||||
    escl_sane_t *handler = h;
 | 
			
		||||
 | 
			
		||||
    if (status != SANE_STATUS_GOOD)
 | 
			
		||||
        return (status);
 | 
			
		||||
    if (p != NULL) {
 | 
			
		||||
        p->depth = 8;
 | 
			
		||||
        p->last_frame = SANE_TRUE;
 | 
			
		||||
        p->format = SANE_FRAME_RGB;
 | 
			
		||||
        p->pixels_per_line = handler->ps.pixels_per_line;
 | 
			
		||||
        p->lines = handler->ps.lines;
 | 
			
		||||
        p->bytes_per_line = handler->ps.pixels_per_line * 3;
 | 
			
		||||
    }
 | 
			
		||||
    return (status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if(defined HAVE_LIBJPEG)
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static boolean fill_input_buffer(j_decompress_ptr cinfo)
 | 
			
		||||
 * \brief Called in the "skip_input_data" function.
 | 
			
		||||
 *
 | 
			
		||||
 * \return TRUE (everything is OK)
 | 
			
		||||
 */
 | 
			
		||||
static boolean
 | 
			
		||||
fill_input_buffer(j_decompress_ptr cinfo)
 | 
			
		||||
{
 | 
			
		||||
    my_source_mgr *src = (my_source_mgr *) cinfo->src;
 | 
			
		||||
    int nbytes = 0;
 | 
			
		||||
 | 
			
		||||
    nbytes = fread(src->buffer, 1, INPUT_BUFFER_SIZE, src->ctx);
 | 
			
		||||
    if (nbytes <= 0) {
 | 
			
		||||
        src->buffer[0] = (unsigned char) 0xFF;
 | 
			
		||||
        src->buffer[1] = (unsigned char) JPEG_EOI;
 | 
			
		||||
        nbytes = 2;
 | 
			
		||||
    }
 | 
			
		||||
    src->pub.next_input_byte = src->buffer;
 | 
			
		||||
    src->pub.bytes_in_buffer = nbytes;
 | 
			
		||||
    return (TRUE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
 | 
			
		||||
 * \brief Called in the "jpeg_RW_src" function.
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
skip_input_data(j_decompress_ptr cinfo, long num_bytes)
 | 
			
		||||
{
 | 
			
		||||
    my_source_mgr *src = (my_source_mgr *) cinfo->src;
 | 
			
		||||
 | 
			
		||||
    if (num_bytes > 0) {
 | 
			
		||||
        while (num_bytes > (long) src->pub.bytes_in_buffer) {
 | 
			
		||||
            num_bytes -= (long) src->pub.bytes_in_buffer;
 | 
			
		||||
            (void) src->pub.fill_input_buffer(cinfo);
 | 
			
		||||
        }
 | 
			
		||||
        src->pub.next_input_byte += (size_t) num_bytes;
 | 
			
		||||
        src->pub.bytes_in_buffer -= (size_t) num_bytes;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
term_source(j_decompress_ptr __sane_unused__ cinfo)
 | 
			
		||||
{
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
init_source(j_decompress_ptr __sane_unused__ cinfo)
 | 
			
		||||
{
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static void jpeg_RW_src(j_decompress_ptr cinfo, FILE *ctx)
 | 
			
		||||
 * \brief Called in the "escl_sane_decompressor" function.
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
jpeg_RW_src(j_decompress_ptr cinfo, FILE *ctx)
 | 
			
		||||
{
 | 
			
		||||
    my_source_mgr *src;
 | 
			
		||||
 | 
			
		||||
    if (cinfo->src == NULL) {
 | 
			
		||||
        cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small)
 | 
			
		||||
            ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(my_source_mgr));
 | 
			
		||||
        src = (my_source_mgr *) cinfo->src;
 | 
			
		||||
    }
 | 
			
		||||
    src = (my_source_mgr *) cinfo->src;
 | 
			
		||||
    src->pub.init_source = init_source;
 | 
			
		||||
    src->pub.fill_input_buffer = fill_input_buffer;
 | 
			
		||||
    src->pub.skip_input_data = skip_input_data;
 | 
			
		||||
    src->pub.resync_to_restart = jpeg_resync_to_restart;
 | 
			
		||||
    src->pub.term_source = term_source;
 | 
			
		||||
    src->ctx = ctx;
 | 
			
		||||
    src->pub.bytes_in_buffer = 0;
 | 
			
		||||
    src->pub.next_input_byte = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
my_error_exit(j_common_ptr cinfo)
 | 
			
		||||
{
 | 
			
		||||
    struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err;
 | 
			
		||||
 | 
			
		||||
    longjmp(err->escape, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
output_no_message(j_common_ptr __sane_unused__ cinfo)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn SANE_Status escl_sane_decompressor(escl_sane_t *handler)
 | 
			
		||||
 * \brief Function that aims to decompress the jpeg image to SANE be able to read the image.
 | 
			
		||||
 *        This function is called in the "sane_read" function.
 | 
			
		||||
 *
 | 
			
		||||
 * \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
 | 
			
		||||
 */
 | 
			
		||||
SANE_Status
 | 
			
		||||
escl_sane_decompressor(escl_sane_t *handler)
 | 
			
		||||
{
 | 
			
		||||
    int start = 0;
 | 
			
		||||
    struct jpeg_decompress_struct cinfo;
 | 
			
		||||
    JSAMPROW rowptr[1];
 | 
			
		||||
    unsigned char *surface = NULL;
 | 
			
		||||
    struct my_error_mgr jerr;
 | 
			
		||||
    int lineSize = 0;
 | 
			
		||||
 | 
			
		||||
    if (handler->scanner->tmp == NULL)
 | 
			
		||||
        return (SANE_STATUS_INVAL);
 | 
			
		||||
    fseek(handler->scanner->tmp, SEEK_SET, 0);
 | 
			
		||||
    start = ftell(handler->scanner->tmp);
 | 
			
		||||
    cinfo.err = jpeg_std_error(&jerr.errmgr);
 | 
			
		||||
    jerr.errmgr.error_exit = my_error_exit;
 | 
			
		||||
    jerr.errmgr.output_message = output_no_message;
 | 
			
		||||
    if (setjmp(jerr.escape)) {
 | 
			
		||||
        jpeg_destroy_decompress(&cinfo);
 | 
			
		||||
        if (surface != NULL)
 | 
			
		||||
            free(surface);
 | 
			
		||||
        return (SANE_STATUS_INVAL);
 | 
			
		||||
    }
 | 
			
		||||
    jpeg_create_decompress(&cinfo);
 | 
			
		||||
    jpeg_RW_src(&cinfo, handler->scanner->tmp);
 | 
			
		||||
    jpeg_read_header(&cinfo, TRUE);
 | 
			
		||||
    cinfo.out_color_space = JCS_RGB;
 | 
			
		||||
    cinfo.quantize_colors = FALSE;
 | 
			
		||||
    jpeg_calc_output_dimensions(&cinfo);
 | 
			
		||||
    surface = malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components);
 | 
			
		||||
    if (surface == NULL) {
 | 
			
		||||
        jpeg_destroy_decompress(&cinfo);
 | 
			
		||||
        fseek(handler->scanner->tmp, start, SEEK_SET);
 | 
			
		||||
        return (SANE_STATUS_NO_MEM);
 | 
			
		||||
    }
 | 
			
		||||
    lineSize = cinfo.output_width * cinfo.output_components;
 | 
			
		||||
    jpeg_start_decompress(&cinfo);
 | 
			
		||||
    while (cinfo.output_scanline < cinfo.output_height) {
 | 
			
		||||
        rowptr[0] = (JSAMPROW)surface + (lineSize * cinfo.output_scanline);
 | 
			
		||||
        jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1);
 | 
			
		||||
    }
 | 
			
		||||
    handler->img_data = surface;
 | 
			
		||||
    handler->img_size = lineSize * cinfo.output_height;
 | 
			
		||||
    handler->img_read = 0;
 | 
			
		||||
    jpeg_finish_decompress(&cinfo);
 | 
			
		||||
    jpeg_destroy_decompress(&cinfo);
 | 
			
		||||
    fclose(handler->scanner->tmp);
 | 
			
		||||
    handler->scanner->tmp = NULL;
 | 
			
		||||
    return (SANE_STATUS_GOOD);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn SANE_Status sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len)
 | 
			
		||||
 * \brief Function that's used to read image data from the device represented by handle 'h'.
 | 
			
		||||
 *        The argument 'buf' is a pointer to a memory area that is at least 'maxlen' bytes long.
 | 
			
		||||
 *        The number of bytes returned is stored in '*len'.
 | 
			
		||||
 *        --> When the call succeeds, the number of bytes returned can be anywhere in the range from 0 to 'maxlen' bytes.
 | 
			
		||||
 *
 | 
			
		||||
 * \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
 | 
			
		||||
 */
 | 
			
		||||
SANE_Status
 | 
			
		||||
sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len)
 | 
			
		||||
{
 | 
			
		||||
    DBG (10, "escl sane_read\n");
 | 
			
		||||
    escl_sane_t *handler = h;
 | 
			
		||||
    SANE_Status status = SANE_STATUS_GOOD;
 | 
			
		||||
    long readbyte;
 | 
			
		||||
 | 
			
		||||
    if (!handler | !buf | !len)
 | 
			
		||||
        return (SANE_STATUS_INVAL);
 | 
			
		||||
    if (handler->cancel)
 | 
			
		||||
        return (SANE_STATUS_CANCELLED);
 | 
			
		||||
    if (!handler->write_scan_data)
 | 
			
		||||
        handler->write_scan_data = SANE_TRUE;
 | 
			
		||||
    if (!handler->decompress_scan_data) {
 | 
			
		||||
        if (handler->scanner->tmp == NULL)
 | 
			
		||||
            return (SANE_STATUS_INVAL);
 | 
			
		||||
        status = escl_sane_decompressor(handler);
 | 
			
		||||
        if (status != SANE_STATUS_GOOD)
 | 
			
		||||
            return (status);
 | 
			
		||||
        handler->decompress_scan_data = SANE_TRUE;
 | 
			
		||||
    }
 | 
			
		||||
    if (handler->img_data == NULL)
 | 
			
		||||
        return (SANE_STATUS_INVAL);
 | 
			
		||||
    if (!handler->end_read) {
 | 
			
		||||
        readbyte = min((handler->img_size - handler->img_read), maxlen);
 | 
			
		||||
        memcpy(buf, handler->img_data + handler->img_read, readbyte);
 | 
			
		||||
        handler->img_read = handler->img_read + readbyte;
 | 
			
		||||
        *len = readbyte;
 | 
			
		||||
        if (handler->img_read == handler->img_size)
 | 
			
		||||
            handler->end_read = SANE_TRUE;
 | 
			
		||||
        else if (handler->img_read > handler->img_size) {
 | 
			
		||||
            *len = 0;
 | 
			
		||||
            handler->end_read = SANE_TRUE;
 | 
			
		||||
            free(handler->img_data);
 | 
			
		||||
            handler->img_data = NULL;
 | 
			
		||||
            return (SANE_STATUS_INVAL);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        *len = 0;
 | 
			
		||||
        free(handler->img_data);
 | 
			
		||||
        handler->img_data = NULL;
 | 
			
		||||
        return (SANE_STATUS_EOF);
 | 
			
		||||
    }
 | 
			
		||||
    return (SANE_STATUS_GOOD);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SANE_Status
 | 
			
		||||
sane_get_select_fd(SANE_Handle __sane_unused__ h, SANE_Int __sane_unused__ *fd)
 | 
			
		||||
{
 | 
			
		||||
    return (SANE_STATUS_UNSUPPORTED);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SANE_Status
 | 
			
		||||
sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking)
 | 
			
		||||
{
 | 
			
		||||
    return (SANE_STATUS_UNSUPPORTED);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,146 @@
 | 
			
		|||
/* sane - Scanner Access Now Easy.
 | 
			
		||||
 | 
			
		||||
   Copyright (C) 2019 Touboul Nathane
 | 
			
		||||
   Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
 | 
			
		||||
 | 
			
		||||
   This file is part of the SANE package.
 | 
			
		||||
 | 
			
		||||
   SANE is free software; you can redistribute it and/or modify it under
 | 
			
		||||
   the terms of the GNU General Public License as published by the Free
 | 
			
		||||
   Software Foundation; either version 3 of the License, or (at your
 | 
			
		||||
   option) any later version.
 | 
			
		||||
 | 
			
		||||
   SANE is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 | 
			
		||||
   for more details.
 | 
			
		||||
 | 
			
		||||
   You should have received a copy of the GNU General Public License
 | 
			
		||||
   along with sane; see the file COPYING.  If not, write to the Free
 | 
			
		||||
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
			
		||||
 | 
			
		||||
   This file implements a SANE backend for eSCL scanners.  */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifndef __ESCL_H__
 | 
			
		||||
#define __ESCL_H__
 | 
			
		||||
 | 
			
		||||
#include "../include/sane/config.h"
 | 
			
		||||
 | 
			
		||||
#if !(HAVE_LIBCURL && defined(WITH_AVAHI) && defined(HAVE_LIBXML2))
 | 
			
		||||
#error "The escl backend requires libcurl, libavahi and libxml2"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef HAVE_LIBJPEG
 | 
			
		||||
/* FIXME: Make JPEG support optional.
 | 
			
		||||
   Support for PNG and PDF is to be added later but currently only
 | 
			
		||||
   JPEG is supported.  Absence of JPEG support makes the backend a
 | 
			
		||||
   no-op at present.
 | 
			
		||||
 */
 | 
			
		||||
#error "The escl backend currently requires libjpeg"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "../include/sane/sane.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#ifndef BACKEND_NAME
 | 
			
		||||
#define BACKEND_NAME escl
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define ESCL_CONFIG_FILE "escl.conf"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int             p1_0;
 | 
			
		||||
    int             p2_0;
 | 
			
		||||
    int             p3_3;
 | 
			
		||||
    int             DocumentType;
 | 
			
		||||
    int             p4_0;
 | 
			
		||||
    int             p5_0;
 | 
			
		||||
    int             p6_1;
 | 
			
		||||
    int             reserve[11];
 | 
			
		||||
} ESCL_SCANOPTS;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef struct ESCL_Device {
 | 
			
		||||
    struct ESCL_Device *next;
 | 
			
		||||
 | 
			
		||||
    char    *model_name;
 | 
			
		||||
    int             port_nb;
 | 
			
		||||
    char      *ip_address;
 | 
			
		||||
    char *type;
 | 
			
		||||
} ESCL_Device;
 | 
			
		||||
 | 
			
		||||
typedef struct capabilities
 | 
			
		||||
{
 | 
			
		||||
    int height;
 | 
			
		||||
    int width;
 | 
			
		||||
    int pos_x;
 | 
			
		||||
    int pos_y;
 | 
			
		||||
    SANE_String default_color;
 | 
			
		||||
    SANE_String_Const default_format;
 | 
			
		||||
    SANE_Int default_resolution;
 | 
			
		||||
    int MinWidth;
 | 
			
		||||
    int MaxWidth;
 | 
			
		||||
    int MinHeight;
 | 
			
		||||
    int MaxHeight;
 | 
			
		||||
    int MaxScanRegions;
 | 
			
		||||
    SANE_String_Const *ColorModes;
 | 
			
		||||
    int ColorModesSize;
 | 
			
		||||
    SANE_String_Const *ContentTypes;
 | 
			
		||||
    int ContentTypesSize;
 | 
			
		||||
    SANE_String_Const *DocumentFormats;
 | 
			
		||||
    int DocumentFormatsSize;
 | 
			
		||||
    SANE_Int *SupportedResolutions;
 | 
			
		||||
    int SupportedResolutionsSize;
 | 
			
		||||
    SANE_String_Const *SupportedIntents;
 | 
			
		||||
    int SupportedIntentsSize;
 | 
			
		||||
    SANE_String_Const SupportedIntentDefault;
 | 
			
		||||
    int MaxOpticalXResolution;
 | 
			
		||||
    int RiskyLeftMargin;
 | 
			
		||||
    int RiskyRightMargin;
 | 
			
		||||
    int RiskyTopMargin;
 | 
			
		||||
    int RiskyBottomMargin;
 | 
			
		||||
    FILE *tmp;
 | 
			
		||||
    int format_ext;
 | 
			
		||||
} capabilities_t;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int                             XRes;
 | 
			
		||||
    int                             YRes;
 | 
			
		||||
    int                             Left;
 | 
			
		||||
    int                             Top;
 | 
			
		||||
    int                             Right;
 | 
			
		||||
    int                             Bottom;
 | 
			
		||||
    int                             ScanMode;
 | 
			
		||||
    int                             ScanMethod;
 | 
			
		||||
    ESCL_SCANOPTS  opts;
 | 
			
		||||
} ESCL_ScanParam;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
enum
 | 
			
		||||
{
 | 
			
		||||
    OPT_NUM_OPTS = 0,
 | 
			
		||||
    OPT_MODE_GROUP,
 | 
			
		||||
    OPT_MODE,
 | 
			
		||||
    OPT_RESOLUTION,
 | 
			
		||||
    OPT_PREVIEW,
 | 
			
		||||
    OPT_GRAY_PREVIEW,
 | 
			
		||||
 | 
			
		||||
    OPT_GEOMETRY_GROUP,
 | 
			
		||||
    OPT_TL_X,
 | 
			
		||||
    OPT_TL_Y,
 | 
			
		||||
    OPT_BR_X,
 | 
			
		||||
    OPT_BR_Y,
 | 
			
		||||
    NUM_OPTIONS
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ESCL_Device *escl_devices(SANE_Status *status);
 | 
			
		||||
SANE_Status escl_device_add(int port_nb, const char *model_name, char *ip_address, char *type);
 | 
			
		||||
SANE_Status escl_status(SANE_String_Const name);
 | 
			
		||||
capabilities_t *escl_capabilities(SANE_String_Const name, SANE_Status *status);
 | 
			
		||||
char *escl_newjob(capabilities_t *scanner, SANE_String_Const name, SANE_Status *status);
 | 
			
		||||
SANE_Status escl_scan(capabilities_t *scanner, SANE_String_Const name, char *result);
 | 
			
		||||
void escl_scanner(SANE_String_Const name, char *result);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,337 @@
 | 
			
		|||
/* sane - Scanner Access Now Easy.
 | 
			
		||||
 | 
			
		||||
   Copyright (C) 2019 Touboul Nathane
 | 
			
		||||
   Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
 | 
			
		||||
 | 
			
		||||
   This file is part of the SANE package.
 | 
			
		||||
 | 
			
		||||
   SANE is free software; you can redistribute it and/or modify it under
 | 
			
		||||
   the terms of the GNU General Public License as published by the Free
 | 
			
		||||
   Software Foundation; either version 3 of the License, or (at your
 | 
			
		||||
   option) any later version.
 | 
			
		||||
 | 
			
		||||
   SANE is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 | 
			
		||||
   for more details.
 | 
			
		||||
 | 
			
		||||
   You should have received a copy of the GNU General Public License
 | 
			
		||||
   along with sane; see the file COPYING.  If not, write to the Free
 | 
			
		||||
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
			
		||||
 | 
			
		||||
   This file implements a SANE backend for eSCL scanners.  */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "escl.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
#include <libxml/parser.h>
 | 
			
		||||
 | 
			
		||||
#include "../include/sane/saneopts.h"
 | 
			
		||||
 | 
			
		||||
struct cap
 | 
			
		||||
{
 | 
			
		||||
    char *memory;
 | 
			
		||||
    size_t size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static SANE_String_Const convert_elements(SANE_String_Const str)
 | 
			
		||||
 * \brief Function that converts the 'color modes' of the scanner (color/gray) to be understood by SANE.
 | 
			
		||||
 *
 | 
			
		||||
 * \return SANE_VALUE_SCAN_MODE_GRAY / SANE_VALUE_SCAN_MODE_COLOR ; NULL otherwise
 | 
			
		||||
 */
 | 
			
		||||
static SANE_String_Const
 | 
			
		||||
convert_elements(SANE_String_Const str)
 | 
			
		||||
{
 | 
			
		||||
    if (strcmp(str, "Grayscale8") == 0)
 | 
			
		||||
        return (SANE_VALUE_SCAN_MODE_GRAY);
 | 
			
		||||
    else if (strcmp(str, "RGB24") == 0)
 | 
			
		||||
        return (SANE_VALUE_SCAN_MODE_COLOR);
 | 
			
		||||
    return (NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static SANE_String_Const *char_to_array(SANE_String_Const *tab, int *tabsize, SANE_String_Const mode, int good_array)
 | 
			
		||||
 * \brief Function that creates the character arrays to put inside :
 | 
			
		||||
 *        the 'color modes', the 'content types', the 'document formats' and the 'supported intents'.
 | 
			
		||||
 *
 | 
			
		||||
 * \return board (the allocated array)
 | 
			
		||||
 */
 | 
			
		||||
static SANE_String_Const *
 | 
			
		||||
char_to_array(SANE_String_Const *tab, int *tabsize, SANE_String_Const mode, int good_array)
 | 
			
		||||
{
 | 
			
		||||
    SANE_String_Const *board = NULL;
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    SANE_String_Const convert = NULL;
 | 
			
		||||
 | 
			
		||||
    if (mode == NULL)
 | 
			
		||||
        return (tab);
 | 
			
		||||
    if (good_array != 0) {
 | 
			
		||||
        convert = convert_elements(mode);
 | 
			
		||||
        if (convert == NULL)
 | 
			
		||||
            return (tab);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        convert = mode;
 | 
			
		||||
    for (i = 0; i < (*tabsize); i++) {
 | 
			
		||||
        if (strcmp(tab[i], convert) == 0)
 | 
			
		||||
            return (tab);
 | 
			
		||||
    }
 | 
			
		||||
    (*tabsize)++;
 | 
			
		||||
    if (*tabsize == 1)
 | 
			
		||||
        board = (SANE_String_Const *)malloc(sizeof(SANE_String_Const) * (*tabsize) + 1);
 | 
			
		||||
    else
 | 
			
		||||
        board = (SANE_String_Const *)realloc(tab, sizeof(SANE_String_Const) * (*tabsize) + 1);
 | 
			
		||||
    board[*tabsize - 1] = (SANE_String_Const)strdup(convert);
 | 
			
		||||
    board[*tabsize] = NULL;
 | 
			
		||||
    return (board);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static SANE_Int *int_to_array(SANE_Int *tab, int *tabsize, int cont)
 | 
			
		||||
 * \brief Function that creates the integer array to put inside the 'supported resolutions'.
 | 
			
		||||
 *
 | 
			
		||||
 * \return board (the allocated array)
 | 
			
		||||
 */
 | 
			
		||||
static SANE_Int *
 | 
			
		||||
int_to_array(SANE_Int *tab, int *tabsize, int cont)
 | 
			
		||||
{
 | 
			
		||||
    SANE_Int *board = NULL;
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < (*tabsize); i++) {
 | 
			
		||||
        if (tab[i] == cont)
 | 
			
		||||
            return (tab);
 | 
			
		||||
    }
 | 
			
		||||
    (*tabsize)++;
 | 
			
		||||
    if (*tabsize == 1) {
 | 
			
		||||
        (*tabsize)++;
 | 
			
		||||
        board = malloc(sizeof(SANE_Int *) * (*tabsize) + 1);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        board = realloc(tab, sizeof(SANE_Int *) * (*tabsize) + 1);
 | 
			
		||||
    board[0] = *tabsize - 1;
 | 
			
		||||
    board[*tabsize - 1] = cont;
 | 
			
		||||
    board[*tabsize] = -1;
 | 
			
		||||
    return (board);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static size_t memory_callback_c(void *contents, size_t size, size_t nmemb, void *userp)
 | 
			
		||||
 * \brief Callback function that stocks in memory the content of the scanner capabilities.
 | 
			
		||||
 *
 | 
			
		||||
 * \return realsize (size of the content needed -> the scanner capabilities)
 | 
			
		||||
 */
 | 
			
		||||
static size_t
 | 
			
		||||
memory_callback_c(void *contents, size_t size, size_t nmemb, void *userp)
 | 
			
		||||
{
 | 
			
		||||
    size_t realsize = size * nmemb;
 | 
			
		||||
    struct cap *mem = (struct cap *)userp;
 | 
			
		||||
 | 
			
		||||
    char *str = realloc(mem->memory, mem->size + realsize + 1);
 | 
			
		||||
    if (str == NULL) {
 | 
			
		||||
        fprintf(stderr, "not enough memory (realloc returned NULL)\n");
 | 
			
		||||
        return (0);
 | 
			
		||||
    }
 | 
			
		||||
    mem->memory = str;
 | 
			
		||||
    memcpy(&(mem->memory[mem->size]), contents, realsize);
 | 
			
		||||
    mem->size = mem->size + realsize;
 | 
			
		||||
    mem->memory[mem->size] = 0;
 | 
			
		||||
    return (realsize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static int find_nodes_c(xmlNode *node)
 | 
			
		||||
 * \brief Function that browses the xml file and parses it, to find the xml children node.
 | 
			
		||||
 *        --> to recover the scanner capabilities.
 | 
			
		||||
 *
 | 
			
		||||
 * \return 0 if a xml child node is found, 1 otherwise
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
find_nodes_c(xmlNode *node)
 | 
			
		||||
{
 | 
			
		||||
    xmlNode *child = node->children;
 | 
			
		||||
 | 
			
		||||
    while (child) {
 | 
			
		||||
        if (child->type == XML_ELEMENT_NODE)
 | 
			
		||||
            return (0);
 | 
			
		||||
        child = child->next;
 | 
			
		||||
    }
 | 
			
		||||
    return (1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static int find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner)
 | 
			
		||||
 * \brief Function that searchs in the xml file if a scanner capabilitie stocked
 | 
			
		||||
 *        in one of the created array (character/integer array) is found.
 | 
			
		||||
 *
 | 
			
		||||
 * \return 0
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner)
 | 
			
		||||
{
 | 
			
		||||
    const char *name = (const char *)node->name;
 | 
			
		||||
    if (strcmp(name, "ColorMode") == 0)
 | 
			
		||||
        scanner->ColorModes = char_to_array(scanner->ColorModes, &scanner->ColorModesSize, (SANE_String_Const)xmlNodeGetContent(node), 1);
 | 
			
		||||
    else if (strcmp(name, "ContentType") == 0)
 | 
			
		||||
        scanner->ContentTypes = char_to_array(scanner->ContentTypes, &scanner->ContentTypesSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
 | 
			
		||||
    else if (strcmp(name, "DocumentFormat") == 0)
 | 
			
		||||
        scanner->DocumentFormats = char_to_array(scanner->DocumentFormats, &scanner->DocumentFormatsSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
 | 
			
		||||
    else if (strcmp(name, "DocumentFormatExt") == 0)
 | 
			
		||||
        scanner->format_ext = 1;
 | 
			
		||||
    else if (strcmp(name, "Intent") == 0)
 | 
			
		||||
        scanner->SupportedIntents = char_to_array(scanner->SupportedIntents, &scanner->SupportedIntentsSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
 | 
			
		||||
    else if (strcmp(name, "XResolution") == 0)
 | 
			
		||||
        scanner->SupportedResolutions = int_to_array(scanner->SupportedResolutions, &scanner->SupportedResolutionsSize, atoi((const char *)xmlNodeGetContent(node)));
 | 
			
		||||
    return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static int find_valor_of_int_variables(xmlNode *node, capabilities_t *scanner)
 | 
			
		||||
 * \brief Function that searchs in the xml file if a integer scanner capabilitie is found.
 | 
			
		||||
 *        The integer scanner capabilities that are interesting are :
 | 
			
		||||
 *        MinWidth, MaxWidth, MaxHeight, MinHeight, MaxScanRegions, MaxOpticalXResolution,
 | 
			
		||||
 *        RiskyLeftMargin, RiskyRightMargin, RiskyTopMargin, RiskyBottomMargin.
 | 
			
		||||
 *
 | 
			
		||||
 * \return 0
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
find_valor_of_int_variables(xmlNode *node, capabilities_t *scanner)
 | 
			
		||||
{
 | 
			
		||||
    int MaxWidth = 0;
 | 
			
		||||
    int MaxHeight = 0;
 | 
			
		||||
    const char *name = (const char *)node->name;
 | 
			
		||||
 | 
			
		||||
    if (strcmp(name, "MinWidth") == 0)
 | 
			
		||||
        scanner->MinWidth = atoi((const char*)xmlNodeGetContent(node));
 | 
			
		||||
    else if (strcmp(name, "MaxWidth") == 0) {
 | 
			
		||||
        MaxWidth = atoi((const char*)xmlNodeGetContent(node));
 | 
			
		||||
        if (scanner->MaxWidth == 0 || MaxWidth < scanner->MaxWidth)
 | 
			
		||||
            scanner->MaxWidth = atoi((const char *)xmlNodeGetContent(node));
 | 
			
		||||
    }
 | 
			
		||||
    else if (strcmp(name, "MinHeight") == 0)
 | 
			
		||||
        scanner->MinHeight = atoi((const char*)xmlNodeGetContent(node));
 | 
			
		||||
    else if (strcmp(name, "MaxHeight") == 0) {
 | 
			
		||||
        MaxHeight = atoi((const char*)xmlNodeGetContent(node));
 | 
			
		||||
        if (scanner->MaxHeight == 0 || MaxHeight < scanner->MaxHeight)
 | 
			
		||||
            scanner->MaxHeight = atoi((const char *)xmlNodeGetContent(node));
 | 
			
		||||
    }
 | 
			
		||||
    else if (strcmp(name, "MaxScanRegions") == 0)
 | 
			
		||||
        scanner->MaxScanRegions = atoi((const char *)xmlNodeGetContent(node));
 | 
			
		||||
    else if (strcmp(name, "MaxOpticalXResolution") == 0)
 | 
			
		||||
        scanner->MaxOpticalXResolution = atoi((const char *)xmlNodeGetContent(node));
 | 
			
		||||
    else if (strcmp(name, "RiskyLeftMargin") == 0)
 | 
			
		||||
        scanner->RiskyLeftMargin = atoi((const char *)xmlNodeGetContent(node));
 | 
			
		||||
    else if (strcmp(name, "RiskyRightMargin") == 0)
 | 
			
		||||
        scanner->RiskyRightMargin = atoi((const char *)xmlNodeGetContent(node));
 | 
			
		||||
    else if (strcmp(name, "RiskyTopMargin") == 0)
 | 
			
		||||
        scanner->RiskyTopMargin = atoi((const char *)xmlNodeGetContent(node));
 | 
			
		||||
    else if (strcmp(name, "RiskyBottomMargin") == 0)
 | 
			
		||||
        scanner->RiskyBottomMargin = atoi((const char *)xmlNodeGetContent(node));
 | 
			
		||||
    find_valor_of_array_variables(node, scanner);
 | 
			
		||||
    return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static int find_true_variables(xmlNode *node, capabilities_t *scanner)
 | 
			
		||||
 * \brief Function that searchs in the xml file if we find a scanner capabilitie stocked
 | 
			
		||||
 *        in one of the created array (character/integer array),
 | 
			
		||||
 *        or, if we find a integer scanner capabilitie.
 | 
			
		||||
 *
 | 
			
		||||
 * \return 0
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
find_true_variables(xmlNode *node, capabilities_t *scanner)
 | 
			
		||||
{
 | 
			
		||||
    const char *name = (const char *)node->name;
 | 
			
		||||
    if (strcmp(name, "MinWidth") == 0 || strcmp(name, "MaxWidth") == 0 || strcmp(name, "MinHeight") == 0 || strcmp(name, "MaxHeight") == 0
 | 
			
		||||
        || strcmp(name, "MaxScanRegions") == 0 || strcmp(name, "ColorMode") == 0 || strcmp(name, "ContentType") == 0
 | 
			
		||||
        || strcmp(name, "DocumentFormat") == 0 || strcmp(name, "XResolution") == 0 || strcmp(name, "Intent") == 0
 | 
			
		||||
        || strcmp(name, "MaxOpticalXResolution") == 0 || strcmp(name, "RiskyLeftMargin") == 0 || strcmp(name, "RiskyRightMargin") == 0
 | 
			
		||||
        || strcmp(name, "RiskyTopMargin") == 0 || strcmp(name, "RiskyBottomMargin") == 0)
 | 
			
		||||
        find_valor_of_int_variables(node, scanner);
 | 
			
		||||
    return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static int print_xml_c(xmlNode *node, capabilities_t *scanner)
 | 
			
		||||
 * \brief Function that browses the xml file, node by node.
 | 
			
		||||
 *
 | 
			
		||||
 * \return 0
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
print_xml_c(xmlNode *node, capabilities_t *scanner)
 | 
			
		||||
{
 | 
			
		||||
    while (node) {
 | 
			
		||||
        if (node->type == XML_ELEMENT_NODE) {
 | 
			
		||||
            if (find_nodes_c(node))
 | 
			
		||||
                find_true_variables(node, scanner);
 | 
			
		||||
        }
 | 
			
		||||
        print_xml_c(node->children, scanner);
 | 
			
		||||
        node = node->next;
 | 
			
		||||
    }
 | 
			
		||||
    return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn capabilities_t *escl_capabilities(SANE_String_Const name, SANE_Status *status)
 | 
			
		||||
 * \brief Function that finally recovers all the capabilities of the scanner, using curl.
 | 
			
		||||
 *        This function is called in the 'sane_open' function and it's the equivalent of
 | 
			
		||||
 *        the following curl command : "curl http(s)://'ip':'port'/eSCL/ScannerCapabilities".
 | 
			
		||||
 *
 | 
			
		||||
 * \return scanner (the structure that stocks all the capabilities elements)
 | 
			
		||||
 */
 | 
			
		||||
capabilities_t *
 | 
			
		||||
escl_capabilities(SANE_String_Const name, SANE_Status *status)
 | 
			
		||||
{
 | 
			
		||||
    capabilities_t *scanner = (capabilities_t*)calloc(1, sizeof(capabilities_t));
 | 
			
		||||
    CURL *curl_handle = NULL;
 | 
			
		||||
    struct cap *var = NULL;
 | 
			
		||||
    xmlDoc *data = NULL;
 | 
			
		||||
    xmlNode *node = NULL;
 | 
			
		||||
    const char *scanner_capabilities = "/eSCL/ScannerCapabilities";
 | 
			
		||||
    char tmp[PATH_MAX] = { 0 };
 | 
			
		||||
 | 
			
		||||
    *status = SANE_STATUS_GOOD;
 | 
			
		||||
    if (name == NULL)
 | 
			
		||||
        *status = SANE_STATUS_NO_MEM;
 | 
			
		||||
    var = (struct cap *)calloc(1, sizeof(struct cap));
 | 
			
		||||
    if (var == NULL)
 | 
			
		||||
        *status = SANE_STATUS_NO_MEM;
 | 
			
		||||
    var->memory = malloc(1);
 | 
			
		||||
    var->size = 0;
 | 
			
		||||
    curl_global_init(CURL_GLOBAL_ALL);
 | 
			
		||||
    curl_handle = curl_easy_init();
 | 
			
		||||
    strcpy(tmp, name);
 | 
			
		||||
    strcat(tmp, scanner_capabilities);
 | 
			
		||||
    curl_easy_setopt(curl_handle, CURLOPT_URL, tmp);
 | 
			
		||||
    if (strncmp(name, "https", 5) == 0) {
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
 | 
			
		||||
    }
 | 
			
		||||
    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memory_callback_c);
 | 
			
		||||
    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)var);
 | 
			
		||||
    if (curl_easy_perform(curl_handle) != CURLE_OK) {
 | 
			
		||||
        fprintf(stderr, "THERE IS NO SCANNER\n");
 | 
			
		||||
        *status = SANE_STATUS_INVAL;
 | 
			
		||||
    }
 | 
			
		||||
    data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0);
 | 
			
		||||
    if (data == NULL)
 | 
			
		||||
        *status = SANE_STATUS_NO_MEM;
 | 
			
		||||
    node = xmlDocGetRootElement(data);
 | 
			
		||||
    if (node == NULL)
 | 
			
		||||
        *status = SANE_STATUS_NO_MEM;
 | 
			
		||||
    print_xml_c(node, scanner);
 | 
			
		||||
    xmlFreeDoc(data);
 | 
			
		||||
    xmlCleanupParser();
 | 
			
		||||
    xmlMemoryDump();
 | 
			
		||||
    curl_easy_cleanup(curl_handle);
 | 
			
		||||
    free(var->memory);
 | 
			
		||||
    curl_global_cleanup();
 | 
			
		||||
    return (scanner);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,147 @@
 | 
			
		|||
/* sane - Scanner Access Now Easy.
 | 
			
		||||
 | 
			
		||||
   Copyright (C) 2019 Touboul Nathane
 | 
			
		||||
   Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
 | 
			
		||||
 | 
			
		||||
   This file is part of the SANE package.
 | 
			
		||||
 | 
			
		||||
   SANE is free software; you can redistribute it and/or modify it under
 | 
			
		||||
   the terms of the GNU General Public License as published by the Free
 | 
			
		||||
   Software Foundation; either version 3 of the License, or (at your
 | 
			
		||||
   option) any later version.
 | 
			
		||||
 | 
			
		||||
   SANE is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 | 
			
		||||
   for more details.
 | 
			
		||||
 | 
			
		||||
   You should have received a copy of the GNU General Public License
 | 
			
		||||
   along with sane; see the file COPYING.  If not, write to the Free
 | 
			
		||||
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
			
		||||
 | 
			
		||||
   This file implements a SANE backend for eSCL scanners.  */
 | 
			
		||||
 | 
			
		||||
#include "escl.h"
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <avahi-client/lookup.h>
 | 
			
		||||
#include <avahi-common/error.h>
 | 
			
		||||
#include <avahi-common/simple-watch.h>
 | 
			
		||||
 | 
			
		||||
#include "../include/sane/sanei.h"
 | 
			
		||||
 | 
			
		||||
static AvahiSimplePoll *simple_poll = NULL;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, AvahiResolverEvent event, const char *name,
 | 
			
		||||
 *                            const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata)
 | 
			
		||||
 * \brief Callback function that will check if the selected scanner follows the escl protocol or not.
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, AvahiResolverEvent event, const char *name,
 | 
			
		||||
                             const char __sane_unused__ *type, const char __sane_unused__ *domain, const char __sane_unused__ *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags __sane_unused__ flags, void __sane_unused__ *userdata)
 | 
			
		||||
{
 | 
			
		||||
    char a[AVAHI_ADDRESS_STR_MAX], *t;
 | 
			
		||||
    assert(r);
 | 
			
		||||
    switch (event) {
 | 
			
		||||
    case AVAHI_RESOLVER_FAILURE:
 | 
			
		||||
        break;
 | 
			
		||||
    case AVAHI_RESOLVER_FOUND:
 | 
			
		||||
        avahi_address_snprint(a, sizeof(a), address);
 | 
			
		||||
        t = avahi_string_list_to_string(txt);
 | 
			
		||||
        if (strstr(t, "\"rs=eSCL\""))
 | 
			
		||||
            escl_device_add(port, name, a, (char*)type);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain,
 | 
			
		||||
 *                           AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata)
 | 
			
		||||
 * \brief Callback function that will browse tanks to 'avahi' the scanners connected in network.
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain,
 | 
			
		||||
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata)
 | 
			
		||||
{
 | 
			
		||||
    AvahiClient *c = userdata;
 | 
			
		||||
    assert(b);
 | 
			
		||||
    switch (event) {
 | 
			
		||||
    case AVAHI_BROWSER_FAILURE:
 | 
			
		||||
        avahi_simple_poll_quit(simple_poll);
 | 
			
		||||
        return;
 | 
			
		||||
    case AVAHI_BROWSER_NEW:
 | 
			
		||||
        if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c)))
 | 
			
		||||
            break;
 | 
			
		||||
    case AVAHI_BROWSER_REMOVE:
 | 
			
		||||
        break;
 | 
			
		||||
    case AVAHI_BROWSER_ALL_FOR_NOW:
 | 
			
		||||
    case AVAHI_BROWSER_CACHE_EXHAUSTED:
 | 
			
		||||
        if (event != AVAHI_BROWSER_CACHE_EXHAUSTED)
 | 
			
		||||
            avahi_simple_poll_quit(simple_poll);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata)
 | 
			
		||||
 * \brief Callback Function that quit if it doesn't find a connected scanner, possible thanks the "Hello Protocol".
 | 
			
		||||
 *        --> Waiting for a answer by the scanner to continue the avahi process.
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata)
 | 
			
		||||
{
 | 
			
		||||
    assert(c);
 | 
			
		||||
    if (state == AVAHI_CLIENT_FAILURE)
 | 
			
		||||
        avahi_simple_poll_quit(simple_poll);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn ESCL_Device *escl_devices(SANE_Status *status)
 | 
			
		||||
 * \brief Function that calls all the avahi functions and then, recovers the connected eSCL devices.
 | 
			
		||||
 *        This function is called in the 'sane_get_devices' function.
 | 
			
		||||
 *
 | 
			
		||||
 * \return NULL (the eSCL devices found)
 | 
			
		||||
 */
 | 
			
		||||
ESCL_Device *
 | 
			
		||||
escl_devices(SANE_Status *status)
 | 
			
		||||
{
 | 
			
		||||
    AvahiClient *client = NULL;
 | 
			
		||||
    AvahiServiceBrowser *sb = NULL;
 | 
			
		||||
    int error;
 | 
			
		||||
 | 
			
		||||
    *status = SANE_STATUS_GOOD;
 | 
			
		||||
    if (!(simple_poll = avahi_simple_poll_new())) {
 | 
			
		||||
        fprintf(stderr, "Failed to create simple poll object.\n");
 | 
			
		||||
        *status = SANE_STATUS_INVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, NULL, &error);
 | 
			
		||||
    if (!client) {
 | 
			
		||||
        fprintf(stderr, "Failed to create client: %s\n", avahi_strerror(error));
 | 
			
		||||
        *status = SANE_STATUS_INVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_uscan._tcp", NULL, 0, browse_callback, client))) {
 | 
			
		||||
        fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client)));
 | 
			
		||||
        *status = SANE_STATUS_INVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_uscans._tcp", NULL, 0, browse_callback, client))) {
 | 
			
		||||
        fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client)));
 | 
			
		||||
        *status = SANE_STATUS_INVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    avahi_simple_poll_loop(simple_poll);
 | 
			
		||||
fail:
 | 
			
		||||
    if (sb)
 | 
			
		||||
        avahi_service_browser_free(sb);
 | 
			
		||||
    if (client)
 | 
			
		||||
        avahi_client_free(client);
 | 
			
		||||
    if (simple_poll)
 | 
			
		||||
        avahi_simple_poll_free(simple_poll);
 | 
			
		||||
    return (NULL);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,207 @@
 | 
			
		|||
/* sane - Scanner Access Now Easy.
 | 
			
		||||
 | 
			
		||||
   Copyright (C) 2019 Touboul Nathane
 | 
			
		||||
   Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
 | 
			
		||||
 | 
			
		||||
   This file is part of the SANE package.
 | 
			
		||||
 | 
			
		||||
   SANE is free software; you can redistribute it and/or modify it under
 | 
			
		||||
   the terms of the GNU General Public License as published by the Free
 | 
			
		||||
   Software Foundation; either version 3 of the License, or (at your
 | 
			
		||||
   option) any later version.
 | 
			
		||||
 | 
			
		||||
   SANE is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 | 
			
		||||
   for more details.
 | 
			
		||||
 | 
			
		||||
   You should have received a copy of the GNU General Public License
 | 
			
		||||
   along with sane; see the file COPYING.  If not, write to the Free
 | 
			
		||||
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
			
		||||
 | 
			
		||||
   This file implements a SANE backend for eSCL scanners.  */
 | 
			
		||||
 | 
			
		||||
#include "escl.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
 | 
			
		||||
struct uploading
 | 
			
		||||
{
 | 
			
		||||
    const char *read_data;
 | 
			
		||||
    size_t size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct downloading
 | 
			
		||||
{
 | 
			
		||||
    char *memory;
 | 
			
		||||
    size_t size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char settings[] =
 | 
			
		||||
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"                        \
 | 
			
		||||
    "<scan:ScanSettings xmlns:pwg=\"http://www.pwg.org/schemas/2010/12/sm\" xmlns:scan=\"http://schemas.hp.com/imaging/escl/2011/05/03\">" \
 | 
			
		||||
    "   <pwg:Version>2.0</pwg:Version>" \
 | 
			
		||||
    "   <pwg:ScanRegions>" \
 | 
			
		||||
    "      <pwg:ScanRegion>" \
 | 
			
		||||
    "          <pwg:ContentRegionUnits>escl:ThreeHundredthsOfInches</pwg:ContentRegionUnits>" \
 | 
			
		||||
    "          <pwg:Height>%d</pwg:Height>" \
 | 
			
		||||
    "          <pwg:Width>%d</pwg:Width>" \
 | 
			
		||||
    "          <pwg:XOffset>%d</pwg:XOffset>" \
 | 
			
		||||
    "          <pwg:YOffset>%d</pwg:YOffset>" \
 | 
			
		||||
    "      </pwg:ScanRegion>" \
 | 
			
		||||
    "   </pwg:ScanRegions>" \
 | 
			
		||||
    "   <pwg:DocumentFormat>image/jpeg</pwg:DocumentFormat>" \
 | 
			
		||||
    "%s" \
 | 
			
		||||
    "   <scan:ColorMode>%s</scan:ColorMode>" \
 | 
			
		||||
    "   <scan:XResolution>%d</scan:XResolution>" \
 | 
			
		||||
    "   <scan:YResolution>%d</scan:YResolution>" \
 | 
			
		||||
    "   <pwg:InputSource>Platen</pwg:InputSource>" \
 | 
			
		||||
    "</scan:ScanSettings>";
 | 
			
		||||
 | 
			
		||||
static const char formatExt[] =
 | 
			
		||||
    "   <scan:DocumentFormatExt>image/jpeg</scan:DocumentFormatExt>";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static size_t download_callback(void *str, size_t size, size_t nmemb, void *userp)
 | 
			
		||||
 * \brief Callback function that stocks in memory the content of the 'job'. Example below :
 | 
			
		||||
 *        "Trying 192.168.14.150...
 | 
			
		||||
 *         TCP_NODELAY set
 | 
			
		||||
 *         Connected to 192.168.14.150 (192.168.14.150) port 80
 | 
			
		||||
 *         POST /eSCL/ScanJobs HTTP/1.1
 | 
			
		||||
 *         Host: 192.168.14.150
 | 
			
		||||
 *         User-Agent: curl/7.55.1
 | 
			
		||||
 *         Accept: /
 | 
			
		||||
 *         Content-Length: 605
 | 
			
		||||
 *         Content-Type: application/x-www-form-urlencoded
 | 
			
		||||
 *         upload completely sent off: 605 out of 605 bytes
 | 
			
		||||
 *         < HTTP/1.1 201 Created
 | 
			
		||||
 *         < MIME-Version: 1.0
 | 
			
		||||
 *         < Location: http://192.168.14.150/eSCL/ScanJobs/22b54fd0-027b-1000-9bd0-f4a99726e2fa
 | 
			
		||||
 *         < Content-Length: 0
 | 
			
		||||
 *         < Connection: close
 | 
			
		||||
 *         <
 | 
			
		||||
 *         Closing connection 0"
 | 
			
		||||
 *
 | 
			
		||||
 * \return realsize (size of the content needed -> the 'job')
 | 
			
		||||
 */
 | 
			
		||||
static size_t
 | 
			
		||||
download_callback(void *str, size_t size, size_t nmemb, void *userp)
 | 
			
		||||
{
 | 
			
		||||
    struct downloading *download = (struct downloading *)userp;
 | 
			
		||||
    size_t realsize = size * nmemb;
 | 
			
		||||
    char *content = realloc(download->memory, download->size + realsize + 1);
 | 
			
		||||
 | 
			
		||||
    if (content == NULL) {
 | 
			
		||||
        fprintf(stderr, "not enough memory (realloc returned NULL)\n");
 | 
			
		||||
        return (0);
 | 
			
		||||
    }
 | 
			
		||||
    download->memory = content;
 | 
			
		||||
    memcpy(&(download->memory[download->size]), str, realsize);
 | 
			
		||||
    download->size = download->size + realsize;
 | 
			
		||||
    download->memory[download->size] = 0;
 | 
			
		||||
    return (realsize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn char *escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *status)
 | 
			
		||||
 * \brief Function that, using curl, uploads the data (composed by the scanner capabilities) to the
 | 
			
		||||
 *        server to download the 'job' and recover the 'new job' (char *result), in LOCATION.
 | 
			
		||||
 *        This function is called in the 'sane_start' function and it's the equivalent of the
 | 
			
		||||
 *        following curl command : "curl -v POST -d cap.xml http(s)://'ip':'port'/eSCL/ScanJobs".
 | 
			
		||||
 *
 | 
			
		||||
 * \return result (the 'new job', situated in LOCATION)
 | 
			
		||||
 */
 | 
			
		||||
char *
 | 
			
		||||
escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *status)
 | 
			
		||||
{
 | 
			
		||||
    CURL *curl_handle = NULL;
 | 
			
		||||
    struct uploading *upload = NULL;
 | 
			
		||||
    struct downloading *download = NULL;
 | 
			
		||||
    const char *scan_jobs = "/eSCL/ScanJobs";
 | 
			
		||||
    char cap_data[PATH_MAX] = { 0 };
 | 
			
		||||
    char job_cmd[PATH_MAX] = { 0 };
 | 
			
		||||
    char *location = NULL;
 | 
			
		||||
    char *result = NULL;
 | 
			
		||||
    char *temporary = NULL;
 | 
			
		||||
 | 
			
		||||
    *status = SANE_STATUS_GOOD;
 | 
			
		||||
    if (name == NULL || scanner == NULL) {
 | 
			
		||||
        *status = SANE_STATUS_NO_MEM;
 | 
			
		||||
        return (NULL);
 | 
			
		||||
    }
 | 
			
		||||
    upload = (struct uploading *)calloc(1, sizeof(struct uploading));
 | 
			
		||||
    if (upload == NULL) {
 | 
			
		||||
        *status = SANE_STATUS_NO_MEM;
 | 
			
		||||
        return (NULL);
 | 
			
		||||
    }
 | 
			
		||||
    download = (struct downloading *)calloc(1, sizeof(struct downloading));
 | 
			
		||||
    if (download == NULL) {
 | 
			
		||||
        free(upload);
 | 
			
		||||
        *status = SANE_STATUS_NO_MEM;
 | 
			
		||||
        return (NULL);
 | 
			
		||||
    }
 | 
			
		||||
    curl_global_init(CURL_GLOBAL_ALL);
 | 
			
		||||
    curl_handle = curl_easy_init();
 | 
			
		||||
    if (curl_handle != NULL) {
 | 
			
		||||
        snprintf(cap_data, sizeof(cap_data), settings, scanner->height, scanner->width, 0, 0,
 | 
			
		||||
                 (scanner->format_ext == 1 ? formatExt : ""),
 | 
			
		||||
                 scanner->default_color, scanner->default_resolution, scanner->default_resolution);
 | 
			
		||||
        //fprintf(stderr, "CAP_DATA = %s\n", cap_data);
 | 
			
		||||
        upload->read_data = strdup(cap_data);
 | 
			
		||||
        upload->size = strlen(cap_data);
 | 
			
		||||
        download->memory = malloc(1);
 | 
			
		||||
        download->size = 0;
 | 
			
		||||
        strcpy(job_cmd, name);
 | 
			
		||||
        strcat(job_cmd, scan_jobs);
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_URL, job_cmd);
 | 
			
		||||
        if (strncmp(name, "https", 5) == 0) {
 | 
			
		||||
            curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
 | 
			
		||||
            curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
 | 
			
		||||
        }
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, upload->read_data);
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, upload->size);
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, download_callback);
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *)download);
 | 
			
		||||
        if (curl_easy_perform(curl_handle) != CURLE_OK) {
 | 
			
		||||
            fprintf(stderr, "THERE IS NO SCANNER\n");
 | 
			
		||||
            *status = SANE_STATUS_INVAL;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            if (download->memory != NULL) {
 | 
			
		||||
                if (strstr(download->memory, "Location:")) {
 | 
			
		||||
                    temporary = strrchr(download->memory, '/');
 | 
			
		||||
                    if (temporary != NULL) {
 | 
			
		||||
                        location = strchr(temporary, '\r');
 | 
			
		||||
                        if (location == NULL)
 | 
			
		||||
                            location = strchr(temporary, '\n');
 | 
			
		||||
                        else {
 | 
			
		||||
                            *location = '\0';
 | 
			
		||||
                            result = strdup(temporary);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    free(download->memory);
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    fprintf(stderr, "THERE IS NO LOCATION\n");
 | 
			
		||||
                    *status = SANE_STATUS_INVAL;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                *status = SANE_STATUS_NO_MEM;
 | 
			
		||||
                return (NULL);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        curl_easy_cleanup(curl_handle);
 | 
			
		||||
    }
 | 
			
		||||
    curl_global_cleanup();
 | 
			
		||||
    if (upload != NULL)
 | 
			
		||||
        free(upload);
 | 
			
		||||
    if (download != NULL)
 | 
			
		||||
        free(download);
 | 
			
		||||
    return (result);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
/* sane - Scanner Access Now Easy.
 | 
			
		||||
 | 
			
		||||
   Copyright (C) 2019 Touboul Nathane
 | 
			
		||||
   Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
 | 
			
		||||
 | 
			
		||||
   This file is part of the SANE package.
 | 
			
		||||
 | 
			
		||||
   SANE is free software; you can redistribute it and/or modify it under
 | 
			
		||||
   the terms of the GNU General Public License as published by the Free
 | 
			
		||||
   Software Foundation; either version 3 of the License, or (at your
 | 
			
		||||
   option) any later version.
 | 
			
		||||
 | 
			
		||||
   SANE is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 | 
			
		||||
   for more details.
 | 
			
		||||
 | 
			
		||||
   You should have received a copy of the GNU General Public License
 | 
			
		||||
   along with sane; see the file COPYING.  If not, write to the Free
 | 
			
		||||
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
			
		||||
 | 
			
		||||
   This file implements a SANE backend for eSCL scanners.  */
 | 
			
		||||
 | 
			
		||||
#include "escl.h"
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn void escl_scanner(SANE_String_Const name, char *result)
 | 
			
		||||
 * \brief Function that resets the scanner after each scan, using curl.
 | 
			
		||||
 *        This function is called in the 'sane_cancel' function.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
escl_scanner(SANE_String_Const name, char *result)
 | 
			
		||||
{
 | 
			
		||||
    CURL *curl_handle = NULL;
 | 
			
		||||
    const char *scan_jobs = "/eSCL/ScanJobs";
 | 
			
		||||
    const char *scanner_start = "/NextDocument";
 | 
			
		||||
    char scan_cmd[PATH_MAX] = { 0 };
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    long answer = 0;
 | 
			
		||||
 | 
			
		||||
    if (name == NULL || result == NULL)
 | 
			
		||||
        return;
 | 
			
		||||
    curl_global_init(CURL_GLOBAL_ALL);
 | 
			
		||||
CURL_CALL:
 | 
			
		||||
    curl_handle = curl_easy_init();
 | 
			
		||||
    if (curl_handle != NULL) {
 | 
			
		||||
        strcpy(scan_cmd, name);
 | 
			
		||||
        strcat(scan_cmd, scan_jobs);
 | 
			
		||||
        strcat(scan_cmd, result);
 | 
			
		||||
        strcat(scan_cmd, scanner_start);
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_URL, scan_cmd);
 | 
			
		||||
        if (strncmp(name, "https", 5) == 0) {
 | 
			
		||||
            curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
 | 
			
		||||
            curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
 | 
			
		||||
        }
 | 
			
		||||
        if (curl_easy_perform(curl_handle) == CURLE_OK) {
 | 
			
		||||
            curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &answer);
 | 
			
		||||
            if (i < 3 && answer == 503) {
 | 
			
		||||
                curl_easy_cleanup(curl_handle);
 | 
			
		||||
                i++;
 | 
			
		||||
                goto CURL_CALL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        curl_easy_cleanup(curl_handle);
 | 
			
		||||
    }
 | 
			
		||||
    curl_global_cleanup();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,96 @@
 | 
			
		|||
/* sane - Scanner Access Now Easy.
 | 
			
		||||
 | 
			
		||||
   Copyright (C) 2019 Touboul Nathane
 | 
			
		||||
   Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
 | 
			
		||||
 | 
			
		||||
   This file is part of the SANE package.
 | 
			
		||||
 | 
			
		||||
   SANE is free software; you can redistribute it and/or modify it under
 | 
			
		||||
   the terms of the GNU General Public License as published by the Free
 | 
			
		||||
   Software Foundation; either version 3 of the License, or (at your
 | 
			
		||||
   option) any later version.
 | 
			
		||||
 | 
			
		||||
   SANE is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 | 
			
		||||
   for more details.
 | 
			
		||||
 | 
			
		||||
   You should have received a copy of the GNU General Public License
 | 
			
		||||
   along with sane; see the file COPYING.  If not, write to the Free
 | 
			
		||||
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
			
		||||
 | 
			
		||||
   This file implements a SANE backend for eSCL scanners.  */
 | 
			
		||||
 | 
			
		||||
#include "escl.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
 | 
			
		||||
#include "../include/sane/sanei.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static size_t write_callback(void *str, size_t size, size_t nmemb, void *userp)
 | 
			
		||||
 * \brief Callback function that writes the image scanned into the temporary file.
 | 
			
		||||
 *
 | 
			
		||||
 * \return to_write (the result of the fwrite fonction)
 | 
			
		||||
 */
 | 
			
		||||
static size_t
 | 
			
		||||
write_callback(void *str, size_t size, size_t nmemb, void *userp)
 | 
			
		||||
{
 | 
			
		||||
    size_t to_write = fwrite(str, size, nmemb, (FILE *)userp);
 | 
			
		||||
 | 
			
		||||
    return (to_write);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn SANE_Status escl_scan(capabilities_t *scanner, SANE_String_Const name, char *result)
 | 
			
		||||
 * \brief Function that, after recovering the 'new job', scans the image writed in the
 | 
			
		||||
 *        temporary file, using curl.
 | 
			
		||||
 *        This function is called in the 'sane_start' function and it's the equivalent of
 | 
			
		||||
 *        the following curl command : "curl -s http(s)://'ip:'port'/eSCL/ScanJobs/'new job'/NextDocument > image.jpg".
 | 
			
		||||
 *
 | 
			
		||||
 * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
 | 
			
		||||
 */
 | 
			
		||||
SANE_Status
 | 
			
		||||
escl_scan(capabilities_t __sane_unused__ *scanner, SANE_String_Const name, char *result)
 | 
			
		||||
{
 | 
			
		||||
    CURL *curl_handle = NULL;
 | 
			
		||||
    const char *scan_jobs = "/eSCL/ScanJobs";
 | 
			
		||||
    const char *scanner_start = "/NextDocument";
 | 
			
		||||
    char scan_cmd[PATH_MAX] = { 0 };
 | 
			
		||||
    SANE_Status status = SANE_STATUS_GOOD;
 | 
			
		||||
 | 
			
		||||
    if (name == NULL)
 | 
			
		||||
        return (SANE_STATUS_NO_MEM);
 | 
			
		||||
    curl_global_init(CURL_GLOBAL_ALL);
 | 
			
		||||
    curl_handle = curl_easy_init();
 | 
			
		||||
    if (curl_handle != NULL) {
 | 
			
		||||
        strcpy(scan_cmd, name);
 | 
			
		||||
        strcat(scan_cmd, scan_jobs);
 | 
			
		||||
        strcat(scan_cmd, result);
 | 
			
		||||
        strcat(scan_cmd, scanner_start);
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_URL, scan_cmd);
 | 
			
		||||
        if (strncmp(name, "https", 5) == 0) {
 | 
			
		||||
            curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
 | 
			
		||||
            curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
 | 
			
		||||
        }
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback);
 | 
			
		||||
        scanner->tmp = tmpfile();
 | 
			
		||||
        if (scanner->tmp != NULL) {
 | 
			
		||||
            curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, scanner->tmp);
 | 
			
		||||
            if (curl_easy_perform(curl_handle) != CURLE_OK) {
 | 
			
		||||
                status = SANE_STATUS_INVAL;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                curl_easy_cleanup(curl_handle);
 | 
			
		||||
            fseek(scanner->tmp, 0, SEEK_SET);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            status = SANE_STATUS_NO_MEM;
 | 
			
		||||
        curl_global_cleanup();
 | 
			
		||||
    }
 | 
			
		||||
    return (status);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,173 @@
 | 
			
		|||
/* sane - Scanner Access Now Easy.
 | 
			
		||||
 | 
			
		||||
   Copyright (C) 2019 Touboul Nathane
 | 
			
		||||
   Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
 | 
			
		||||
 | 
			
		||||
   This file is part of the SANE package.
 | 
			
		||||
 | 
			
		||||
   SANE is free software; you can redistribute it and/or modify it under
 | 
			
		||||
   the terms of the GNU General Public License as published by the Free
 | 
			
		||||
   Software Foundation; either version 3 of the License, or (at your
 | 
			
		||||
   option) any later version.
 | 
			
		||||
 | 
			
		||||
   SANE is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 | 
			
		||||
   for more details.
 | 
			
		||||
 | 
			
		||||
   You should have received a copy of the GNU General Public License
 | 
			
		||||
   along with sane; see the file COPYING.  If not, write to the Free
 | 
			
		||||
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
			
		||||
 | 
			
		||||
   This file implements a SANE backend for eSCL scanners.  */
 | 
			
		||||
 | 
			
		||||
#include "escl.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
#include <libxml/parser.h>
 | 
			
		||||
 | 
			
		||||
struct idle
 | 
			
		||||
{
 | 
			
		||||
    char *memory;
 | 
			
		||||
    size_t size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static size_t memory_callback_s(void *contents, size_t size, size_t nmemb, void *userp)
 | 
			
		||||
 * \brief Callback function that stocks in memory the content of the scanner status.
 | 
			
		||||
 *
 | 
			
		||||
 * \return realsize (size of the content needed -> the scanner status)
 | 
			
		||||
 */
 | 
			
		||||
static size_t
 | 
			
		||||
memory_callback_s(void *contents, size_t size, size_t nmemb, void *userp)
 | 
			
		||||
{
 | 
			
		||||
    size_t realsize = size * nmemb;
 | 
			
		||||
    struct idle *mem = (struct idle *)userp;
 | 
			
		||||
 | 
			
		||||
    char *str = realloc(mem->memory, mem->size + realsize + 1);
 | 
			
		||||
    if (str == NULL) {
 | 
			
		||||
        fprintf(stderr, "not enough memory (realloc returned NULL)\n");
 | 
			
		||||
        return (0);
 | 
			
		||||
    }
 | 
			
		||||
    mem->memory = str;
 | 
			
		||||
    memcpy(&(mem->memory[mem->size]), contents, realsize);
 | 
			
		||||
    mem->size = mem->size + realsize;
 | 
			
		||||
    mem->memory[mem->size] = 0;
 | 
			
		||||
    return (realsize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static int find_nodes_s(xmlNode *node)
 | 
			
		||||
 * \brief Function that browses the xml file and parses it, to find the xml children node.
 | 
			
		||||
 *        --> to recover the scanner status.
 | 
			
		||||
 *
 | 
			
		||||
 * \return 0 if a xml child node is found, 1 otherwise
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
find_nodes_s(xmlNode *node)
 | 
			
		||||
{
 | 
			
		||||
    xmlNode *child = node->children;
 | 
			
		||||
 | 
			
		||||
    while (child) {
 | 
			
		||||
        if (child->type == XML_ELEMENT_NODE)
 | 
			
		||||
            return (0);
 | 
			
		||||
        child = child->next;
 | 
			
		||||
    }
 | 
			
		||||
    return (1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn static void print_xml_s(xmlNode *node, SANE_Status *status)
 | 
			
		||||
 * \brief Function that browses the xml file, node by node.
 | 
			
		||||
 *        If the node 'State' is found, we are expecting to found in this node the 'Idle'
 | 
			
		||||
 *        content (if the scanner is ready to use) and then 'status' = SANE_STATUS_GOOD.
 | 
			
		||||
 *        Otherwise, this means that the scanner isn't ready to use.
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
print_xml_s(xmlNode *node, SANE_Status *status)
 | 
			
		||||
{
 | 
			
		||||
    int x = 0;
 | 
			
		||||
 | 
			
		||||
    while (node) {
 | 
			
		||||
        if (node->type == XML_ELEMENT_NODE) {
 | 
			
		||||
            if (find_nodes_s(node)) {
 | 
			
		||||
                if (strcmp((const char *)node->name, "State") == 0)
 | 
			
		||||
                    x = 1;
 | 
			
		||||
            }
 | 
			
		||||
            if (x == 1 && strcmp((const char *)xmlNodeGetContent(node), "Idle") == 0)
 | 
			
		||||
                *status = SANE_STATUS_GOOD;
 | 
			
		||||
        }
 | 
			
		||||
        print_xml_s(node->children, status);
 | 
			
		||||
        node = node->next;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \fn SANE_Status escl_status(SANE_String_Const name)
 | 
			
		||||
 * \brief Function that finally recovers the scanner status ('Idle', or not), using curl.
 | 
			
		||||
 *        This function is called in the 'sane_open' function and it's the equivalent of
 | 
			
		||||
 *        the following curl command : "curl http(s)://'ip':'port'/eSCL/ScannerStatus".
 | 
			
		||||
 *
 | 
			
		||||
 * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
 | 
			
		||||
 */
 | 
			
		||||
SANE_Status
 | 
			
		||||
escl_status(SANE_String_Const name)
 | 
			
		||||
{
 | 
			
		||||
    SANE_Status status;
 | 
			
		||||
    CURL *curl_handle = NULL;
 | 
			
		||||
    struct idle *var = NULL;
 | 
			
		||||
    xmlDoc *data = NULL;
 | 
			
		||||
    xmlNode *node = NULL;
 | 
			
		||||
    const char *scanner_status = "/eSCL/ScannerStatus";
 | 
			
		||||
    char tmp[PATH_MAX] = { 0 };
 | 
			
		||||
 | 
			
		||||
    if (name == NULL)
 | 
			
		||||
        return (SANE_STATUS_NO_MEM);
 | 
			
		||||
    var = (struct idle*)calloc(1, sizeof(struct idle));
 | 
			
		||||
    if (var == NULL)
 | 
			
		||||
        return (SANE_STATUS_NO_MEM);
 | 
			
		||||
    var->memory = malloc(1);
 | 
			
		||||
    var->size = 0;
 | 
			
		||||
    curl_global_init(CURL_GLOBAL_ALL);
 | 
			
		||||
    curl_handle = curl_easy_init();
 | 
			
		||||
    strcpy(tmp, name);
 | 
			
		||||
    strcat(tmp, scanner_status);
 | 
			
		||||
    curl_easy_setopt(curl_handle, CURLOPT_URL, tmp);
 | 
			
		||||
    if (strncmp(name, "https", 5) == 0) {
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
 | 
			
		||||
        curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
 | 
			
		||||
    }
 | 
			
		||||
    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memory_callback_s);
 | 
			
		||||
    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)var);
 | 
			
		||||
    if (curl_easy_perform(curl_handle) != CURLE_OK) {
 | 
			
		||||
        fprintf(stderr, "THERE IS NO SCANNER\n");
 | 
			
		||||
        status = SANE_STATUS_INVAL;
 | 
			
		||||
        goto clean_data;
 | 
			
		||||
    }
 | 
			
		||||
    data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0);
 | 
			
		||||
    if (data == NULL) {
 | 
			
		||||
        status = SANE_STATUS_NO_MEM;
 | 
			
		||||
        goto clean_data;
 | 
			
		||||
    }
 | 
			
		||||
    node = xmlDocGetRootElement(data);
 | 
			
		||||
    if (node == NULL) {
 | 
			
		||||
        status = SANE_STATUS_NO_MEM;
 | 
			
		||||
        goto clean;
 | 
			
		||||
    }
 | 
			
		||||
    status = SANE_STATUS_DEVICE_BUSY;
 | 
			
		||||
    print_xml_s(node, &status);
 | 
			
		||||
clean:
 | 
			
		||||
    xmlFreeDoc(data);
 | 
			
		||||
clean_data:
 | 
			
		||||
    xmlCleanupParser();
 | 
			
		||||
    xmlMemoryDump();
 | 
			
		||||
    curl_easy_cleanup(curl_handle);
 | 
			
		||||
    free(var->memory);
 | 
			
		||||
    free(var);
 | 
			
		||||
    curl_global_cleanup();
 | 
			
		||||
    return (status);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								configure.ac
								
								
								
								
							
							
						
						
									
										30
									
								
								configure.ac
								
								
								
								
							| 
						 | 
				
			
			@ -140,6 +140,7 @@ if test "$enable_avahi" = "yes"; then
 | 
			
		|||
  PKG_CHECK_MODULES(AVAHI, [ avahi-client >= 0.6.24 ],
 | 
			
		||||
                    [AC_DEFINE(WITH_AVAHI, 1, [define if Avahi support is enabled for saned and the net backend])], enable_avahi=no)
 | 
			
		||||
fi
 | 
			
		||||
AM_CONDITIONAL([have_libavahi], [test x != "x$AVAHI_LIBS"])
 | 
			
		||||
 | 
			
		||||
dnl check sane to make sure we don't have two installations
 | 
			
		||||
AC_CHECK_LIB(sane, sane_init, LIBSANE_EXISTS="yes")
 | 
			
		||||
| 
						 | 
				
			
			@ -430,6 +431,28 @@ AS_IF([test xyes = "x$with_usb" && test xyes != "x$have_usb"],
 | 
			
		|||
  ])
 | 
			
		||||
AM_CONDITIONAL([have_usblib], [test x != "x$USB_LIBS"])
 | 
			
		||||
 | 
			
		||||
dnl ******************************************************************
 | 
			
		||||
dnl Check for libcurl availability
 | 
			
		||||
dnl ******************************************************************
 | 
			
		||||
AC_ARG_WITH(libcurl,
 | 
			
		||||
  AS_HELP_STRING([--with-libcurl],
 | 
			
		||||
    [enable functionality that needs libcurl @<:@default=check@:>@]),
 | 
			
		||||
  [],
 | 
			
		||||
  [with_libcurl=check])
 | 
			
		||||
AC_DEFINE(HAVE_LIBCURL,
 | 
			
		||||
  [0], [Define to 1 if libcurl is available])
 | 
			
		||||
AS_IF([test xno != "x$with_libcurl"],
 | 
			
		||||
  [PKG_CHECK_MODULES(libcurl, [libcurl],
 | 
			
		||||
     [AC_DEFINE([HAVE_LIBCURL], [1])
 | 
			
		||||
      with_libcurl=yes
 | 
			
		||||
     ],
 | 
			
		||||
     [AS_IF([test xcheck != "x$with_libcurl"],
 | 
			
		||||
        [AC_MSG_ERROR([libcurl requested but not found])])
 | 
			
		||||
      with_libcurl=no
 | 
			
		||||
     ])
 | 
			
		||||
  ])
 | 
			
		||||
AM_CONDITIONAL([have_libcurl], [test x != "x$libcurl_LIBS"])
 | 
			
		||||
 | 
			
		||||
dnl ******************************************************************
 | 
			
		||||
dnl Check for USB record/replay support
 | 
			
		||||
dnl ******************************************************************
 | 
			
		||||
| 
						 | 
				
			
			@ -440,6 +463,7 @@ AC_ARG_WITH(usb_record_replay,
 | 
			
		|||
if test "x$with_usb_record_replay" != "xno"; then
 | 
			
		||||
  PKG_CHECK_MODULES([XML], [libxml-2.0], have_libxml=yes, have_libxml=no)
 | 
			
		||||
  if test "x$have_libxml" = xyes; then
 | 
			
		||||
    AC_DEFINE(HAVE_LIBXML2, 1, [Define to 1 if libxml2 is available])
 | 
			
		||||
    AC_DEFINE(WITH_USB_RECORD_REPLAY, 1, [define if USB record replay is enabled])
 | 
			
		||||
  else
 | 
			
		||||
    if test "x$with_usb_record_replay" = xyes; then
 | 
			
		||||
| 
						 | 
				
			
			@ -447,6 +471,7 @@ if test "x$with_usb_record_replay" != "xno"; then
 | 
			
		|||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
fi
 | 
			
		||||
AM_CONDITIONAL([have_libxml2], [test x != "x$XML_LIBS"])
 | 
			
		||||
 | 
			
		||||
dnl ************
 | 
			
		||||
dnl SCSI Support
 | 
			
		||||
| 
						 | 
				
			
			@ -611,8 +636,8 @@ AC_ARG_ENABLE(local-backends,
 | 
			
		|||
ALL_BACKENDS="abaton agfafocus apple artec artec_eplus48u as6e \
 | 
			
		||||
        avision bh canon canon630u canon_dr canon_pp cardscan \
 | 
			
		||||
        coolscan coolscan2 coolscan3 dc25 dc210 dc240 \
 | 
			
		||||
        dell1600n_net dmc epjitsu epson epson2 epsonds fujitsu genesys \
 | 
			
		||||
        gphoto2 gt68xx hp hp3500 hp3900 hp4200 hp5400 \
 | 
			
		||||
        dell1600n_net dmc epjitsu epson epson2 epsonds escl fujitsu \
 | 
			
		||||
        genesys gphoto2 gt68xx hp hp3500 hp3900 hp4200 hp5400 \
 | 
			
		||||
        hp5590 hpsj5s hpljm1005 hs2p ibm kodak kodakaio kvs1025 kvs20xx \
 | 
			
		||||
        kvs40xx leo lexmark ma1509 magicolor \
 | 
			
		||||
        matsushita microtek microtek2 mustek mustek_pp \
 | 
			
		||||
| 
						 | 
				
			
			@ -841,6 +866,7 @@ else
 | 
			
		|||
fi
 | 
			
		||||
echo "IPv6 support:  `eval eval echo ${ipv6}`"
 | 
			
		||||
echo "Avahi support: `eval eval echo ${enable_avahi}`"
 | 
			
		||||
echo "cURL support:  `eval eval echo ${with_libcurl}`"
 | 
			
		||||
echo "SNMP support:  `eval eval echo ${with_snmp}`"
 | 
			
		||||
echo "-> The following backends will be built:"
 | 
			
		||||
for backend in ${BACKENDS} ; do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ EXTRA_DIST = scanimage.man sane-config.man sane-find-scanner.man \
 | 
			
		|||
 | 
			
		||||
BACKEND_5MANS = sane-abaton.5 sane-agfafocus.5 sane-apple.5 sane-as6e.5 \
 | 
			
		||||
	sane-dll.5 sane-dc25.5 sane-dmc.5 sane-epson.5 sane-epson2.5 sane-epsonds.5 \
 | 
			
		||||
	sane-hp.5 sane-gphoto2.5 sane-leo.5 sane-lexmark.5 \
 | 
			
		||||
	sane-escl.5 sane-hp.5 sane-gphoto2.5 sane-leo.5 sane-lexmark.5 \
 | 
			
		||||
	sane-matsushita.5 sane-microtek.5 sane-microtek2.5 sane-mustek.5 \
 | 
			
		||||
	sane-nec.5 sane-net.5 sane-pie.5 sane-pieusb.5 sane-pint.5 sane-pnm.5 \
 | 
			
		||||
	sane-umax.5 sane-qcam.5 sane-scsi.5 sane-artec.5 sane-kodak.5 sane-kodakaio.5 \
 | 
			
		||||
| 
						 | 
				
			
			@ -41,8 +41,8 @@ BACKEND_5MANS = sane-abaton.5 sane-agfafocus.5 sane-apple.5 sane-as6e.5 \
 | 
			
		|||
 | 
			
		||||
EXTRA_DIST += sane-abaton.man sane-agfafocus.man sane-apple.man sane-as6e.man \
 | 
			
		||||
	sane-dll.man sane-dc25.man sane-dmc.man sane-epson.man \
 | 
			
		||||
	sane-epson2.man sane-epsonds.man sane-hp.man sane-gphoto2.man sane-leo.man \
 | 
			
		||||
	sane-lexmark.man sane-matsushita.man sane-microtek.man \
 | 
			
		||||
	sane-epson2.man sane-epsonds.man sane-escl.man sane-hp.man sane-gphoto2.man \
 | 
			
		||||
	sane-leo.man sane-lexmark.man sane-matsushita.man sane-microtek.man \
 | 
			
		||||
	sane-microtek2.man sane-mustek.man sane-nec.man sane-net.man \
 | 
			
		||||
	sane-pie.man sane-pieusb.man sane-pint.man sane-pnm.man sane-umax.man \
 | 
			
		||||
	sane-qcam.man sane-scsi.man sane-artec.man sane-fujitsu.man \
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +157,7 @@ DESC_FILES = descriptions/abaton.desc descriptions/agfafocus.desc \
 | 
			
		|||
  descriptions/dc210.desc descriptions/dc240.desc descriptions/dc25.desc \
 | 
			
		||||
  descriptions/dell1600n_net.desc descriptions/dll.desc descriptions/dmc.desc \
 | 
			
		||||
  descriptions/epjitsu.desc descriptions/epson2.desc descriptions/epson.desc \
 | 
			
		||||
  descriptions/epsonds.desc \
 | 
			
		||||
  descriptions/epsonds.desc descriptions/escl.desc \
 | 
			
		||||
  descriptions/fujitsu.desc descriptions/genesys.desc \
 | 
			
		||||
  descriptions/gphoto2.desc descriptions/gt68xx.desc descriptions/hp3500.desc \
 | 
			
		||||
  descriptions/hp3900.desc descriptions/hp4200.desc descriptions/hp5400.desc \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
:backend "escl"
 | 
			
		||||
:manpage "sane-escl"
 | 
			
		||||
:url "https://support.apple.com/en-us/HT201311"
 | 
			
		||||
:comment "The eSCL backend for sane supports AirScan/eSCL devices that announce themselves on mDNS as _uscan._utcp or _uscans._utcp"
 | 
			
		||||
 | 
			
		||||
:devicetype :scanner
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
.TH sane\-escl 5 "14 Dec 2019" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy"
 | 
			
		||||
.IX sane\-escl
 | 
			
		||||
.SH NAME
 | 
			
		||||
sane\-escl \- SANE backend for eSCL scanners
 | 
			
		||||
.SH DESCRIPTION
 | 
			
		||||
The
 | 
			
		||||
.B sane\-escl
 | 
			
		||||
library implements a SANE (Scanner Access Now Easy) backend that
 | 
			
		||||
provides access to eSCL protocol scanners.
 | 
			
		||||
 | 
			
		||||
.PP
 | 
			
		||||
The "escl" backend for SANE supports AirScan/eSCL devices that announce
 | 
			
		||||
themselves on mDNS as _uscan._utcp or _uscans._utcp.
 | 
			
		||||
If the device is available, the "escl" backend recovers these capacities.
 | 
			
		||||
The user configures and starts scanning.
 | 
			
		||||
A list of devices that use the eSCL protocol can be found at
 | 
			
		||||
.IR https://support.apple.com/en-us/HT201311 .
 | 
			
		||||
While these devices are expected to work, your mileage may vary.
 | 
			
		||||
 | 
			
		||||
.SH FILES
 | 
			
		||||
.TP
 | 
			
		||||
.I @CONFIGDIR@/escl.conf
 | 
			
		||||
The backend configuration file.
 | 
			
		||||
.TP
 | 
			
		||||
.I @LIBDIR@/libsane\-escl.a
 | 
			
		||||
The static library implementing this backend.
 | 
			
		||||
.TP
 | 
			
		||||
.I @LIBDIR@/libsane\-escl.so
 | 
			
		||||
The shared library implementing this backend (present on systems that
 | 
			
		||||
support dynamic loading).
 | 
			
		||||
.SH ENVIRONMENT
 | 
			
		||||
.TP
 | 
			
		||||
.B SANE_DEBUG_ESCL
 | 
			
		||||
If the library was compiled with debug support enabled, this
 | 
			
		||||
environment variable controls the debug level for this backend.  E.g.,
 | 
			
		||||
a value of 128 requests all debug output to be printed.  Smaller
 | 
			
		||||
levels reduce verbosity.
 | 
			
		||||
.SH "SEE ALSO"
 | 
			
		||||
sane(7), scanimage(1), xscanimage(1), xsane(1)
 | 
			
		||||
.SH AUTHORS
 | 
			
		||||
Touboul Nathane, Thierry HUCHARD
 | 
			
		||||
		Ładowanie…
	
		Reference in New Issue