1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy. 2141cc406Sopenharmony_ci 3141cc406Sopenharmony_ci Copyright (C) 2019 Touboul Nathane 4141cc406Sopenharmony_ci Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com> 5141cc406Sopenharmony_ci 6141cc406Sopenharmony_ci This file is part of the SANE package. 7141cc406Sopenharmony_ci 8141cc406Sopenharmony_ci SANE is free software; you can redistribute it and/or modify it under 9141cc406Sopenharmony_ci the terms of the GNU General Public License as published by the Free 10141cc406Sopenharmony_ci Software Foundation; either version 3 of the License, or (at your 11141cc406Sopenharmony_ci option) any later version. 12141cc406Sopenharmony_ci 13141cc406Sopenharmony_ci SANE is distributed in the hope that it will be useful, but WITHOUT 14141cc406Sopenharmony_ci ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15141cc406Sopenharmony_ci FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16141cc406Sopenharmony_ci for more details. 17141cc406Sopenharmony_ci 18141cc406Sopenharmony_ci You should have received a copy of the GNU General Public License 19141cc406Sopenharmony_ci along with sane; see the file COPYING. 20141cc406Sopenharmony_ci If not, see <https://www.gnu.org/licenses/>. 21141cc406Sopenharmony_ci 22141cc406Sopenharmony_ci This file implements a SANE backend for eSCL scanners. */ 23141cc406Sopenharmony_ci 24141cc406Sopenharmony_ci#define DEBUG_DECLARE_ONLY 25141cc406Sopenharmony_ci#include "../include/sane/config.h" 26141cc406Sopenharmony_ci 27141cc406Sopenharmony_ci#include "escl.h" 28141cc406Sopenharmony_ci 29141cc406Sopenharmony_ci#include <stdio.h> 30141cc406Sopenharmony_ci#include <stdlib.h> 31141cc406Sopenharmony_ci#include <string.h> 32141cc406Sopenharmony_ci 33141cc406Sopenharmony_ci#include <libxml/parser.h> 34141cc406Sopenharmony_ci 35141cc406Sopenharmony_cistruct idle 36141cc406Sopenharmony_ci{ 37141cc406Sopenharmony_ci char *memory; 38141cc406Sopenharmony_ci size_t size; 39141cc406Sopenharmony_ci}; 40141cc406Sopenharmony_ci 41141cc406Sopenharmony_ci/** 42141cc406Sopenharmony_ci * \fn static size_t memory_callback_s(void *contents, size_t size, size_t nmemb, void *userp) 43141cc406Sopenharmony_ci * \brief Callback function that stocks in memory the content of the scanner status. 44141cc406Sopenharmony_ci * 45141cc406Sopenharmony_ci * \return realsize (size of the content needed -> the scanner status) 46141cc406Sopenharmony_ci */ 47141cc406Sopenharmony_cistatic size_t 48141cc406Sopenharmony_cimemory_callback_s(void *contents, size_t size, size_t nmemb, void *userp) 49141cc406Sopenharmony_ci{ 50141cc406Sopenharmony_ci size_t realsize = size * nmemb; 51141cc406Sopenharmony_ci struct idle *mem = (struct idle *)userp; 52141cc406Sopenharmony_ci 53141cc406Sopenharmony_ci char *str = realloc(mem->memory, mem->size + realsize + 1); 54141cc406Sopenharmony_ci if (str == NULL) { 55141cc406Sopenharmony_ci DBG(1, "not enough memory (realloc returned NULL)\n"); 56141cc406Sopenharmony_ci return (0); 57141cc406Sopenharmony_ci } 58141cc406Sopenharmony_ci mem->memory = str; 59141cc406Sopenharmony_ci memcpy(&(mem->memory[mem->size]), contents, realsize); 60141cc406Sopenharmony_ci mem->size = mem->size + realsize; 61141cc406Sopenharmony_ci mem->memory[mem->size] = 0; 62141cc406Sopenharmony_ci return (realsize); 63141cc406Sopenharmony_ci} 64141cc406Sopenharmony_ci 65141cc406Sopenharmony_ci/** 66141cc406Sopenharmony_ci * \fn static int find_nodes_s(xmlNode *node) 67141cc406Sopenharmony_ci * \brief Function that browses the xml file and parses it, to find the xml children node. 68141cc406Sopenharmony_ci * --> to recover the scanner status. 69141cc406Sopenharmony_ci * 70141cc406Sopenharmony_ci * \return 0 if a xml child node is found, 1 otherwise 71141cc406Sopenharmony_ci */ 72141cc406Sopenharmony_cistatic int 73141cc406Sopenharmony_cifind_nodes_s(xmlNode *node) 74141cc406Sopenharmony_ci{ 75141cc406Sopenharmony_ci xmlNode *child = node->children; 76141cc406Sopenharmony_ci 77141cc406Sopenharmony_ci while (child) { 78141cc406Sopenharmony_ci if (child->type == XML_ELEMENT_NODE) 79141cc406Sopenharmony_ci return (0); 80141cc406Sopenharmony_ci child = child->next; 81141cc406Sopenharmony_ci } 82141cc406Sopenharmony_ci return (1); 83141cc406Sopenharmony_ci} 84141cc406Sopenharmony_ci 85141cc406Sopenharmony_cistatic void 86141cc406Sopenharmony_ciprint_xml_job_status(xmlNode *node, 87141cc406Sopenharmony_ci SANE_Status *job, 88141cc406Sopenharmony_ci int *image) 89141cc406Sopenharmony_ci{ 90141cc406Sopenharmony_ci while (node) { 91141cc406Sopenharmony_ci if (node->type == XML_ELEMENT_NODE) { 92141cc406Sopenharmony_ci if (find_nodes_s(node)) { 93141cc406Sopenharmony_ci if (strcmp((const char *)node->name, "JobState") == 0) { 94141cc406Sopenharmony_ci const char *state = (const char *)xmlNodeGetContent(node); 95141cc406Sopenharmony_ci if (!strcmp(state, "Processing")) { 96141cc406Sopenharmony_ci *job = SANE_STATUS_DEVICE_BUSY; 97141cc406Sopenharmony_ci DBG(10, "jobId Processing SANE_STATUS_DEVICE_BUSY\n"); 98141cc406Sopenharmony_ci } 99141cc406Sopenharmony_ci else if (!strcmp(state, "Completed")) { 100141cc406Sopenharmony_ci *job = SANE_STATUS_GOOD; 101141cc406Sopenharmony_ci DBG(10, "jobId Completed SANE_STATUS_GOOD\n"); 102141cc406Sopenharmony_ci } 103141cc406Sopenharmony_ci else if (strcmp((const char *)node->name, "ImagesToTransfer") == 0) { 104141cc406Sopenharmony_ci const char *state = (const char *)xmlNodeGetContent(node); 105141cc406Sopenharmony_ci *image = atoi(state); 106141cc406Sopenharmony_ci } 107141cc406Sopenharmony_ci } 108141cc406Sopenharmony_ci } 109141cc406Sopenharmony_ci } 110141cc406Sopenharmony_ci print_xml_job_status(node->children, job, image); 111141cc406Sopenharmony_ci node = node->next; 112141cc406Sopenharmony_ci } 113141cc406Sopenharmony_ci} 114141cc406Sopenharmony_ci 115141cc406Sopenharmony_cistatic void 116141cc406Sopenharmony_ciprint_xml_platen_and_adf_status(xmlNode *node, 117141cc406Sopenharmony_ci SANE_Status *platen, 118141cc406Sopenharmony_ci SANE_Status *adf, 119141cc406Sopenharmony_ci const char* jobId, 120141cc406Sopenharmony_ci SANE_Status *job, 121141cc406Sopenharmony_ci int *image) 122141cc406Sopenharmony_ci{ 123141cc406Sopenharmony_ci while (node) { 124141cc406Sopenharmony_ci if (node->type == XML_ELEMENT_NODE) { 125141cc406Sopenharmony_ci if (find_nodes_s(node)) { 126141cc406Sopenharmony_ci if (strcmp((const char *)node->name, "State") == 0) { 127141cc406Sopenharmony_ci DBG(10, "State\t"); 128141cc406Sopenharmony_ci const char *state = (const char *)xmlNodeGetContent(node); 129141cc406Sopenharmony_ci if (!strcmp(state, "Idle")) { 130141cc406Sopenharmony_ci DBG(10, "Idle SANE_STATUS_GOOD\n"); 131141cc406Sopenharmony_ci *platen = SANE_STATUS_GOOD; 132141cc406Sopenharmony_ci } else if (!strcmp(state, "Processing")) { 133141cc406Sopenharmony_ci DBG(10, "Processing SANE_STATUS_DEVICE_BUSY\n"); 134141cc406Sopenharmony_ci *platen = SANE_STATUS_DEVICE_BUSY; 135141cc406Sopenharmony_ci } else { 136141cc406Sopenharmony_ci DBG(10, "%s SANE_STATUS_UNSUPPORTED\n", state); 137141cc406Sopenharmony_ci *platen = SANE_STATUS_UNSUPPORTED; 138141cc406Sopenharmony_ci } 139141cc406Sopenharmony_ci } 140141cc406Sopenharmony_ci // Thank's Alexander Pevzner (pzz@apevzner.com) 141141cc406Sopenharmony_ci else if (adf && strcmp((const char *)node->name, "AdfState") == 0) { 142141cc406Sopenharmony_ci const char *state = (const char *)xmlNodeGetContent(node); 143141cc406Sopenharmony_ci if (!strcmp(state, "ScannerAdfLoaded")){ 144141cc406Sopenharmony_ci DBG(10, "ScannerAdfLoaded SANE_STATUS_GOOD\n"); 145141cc406Sopenharmony_ci *adf = SANE_STATUS_GOOD; 146141cc406Sopenharmony_ci } else if (!strcmp(state, "ScannerAdfJam")) { 147141cc406Sopenharmony_ci DBG(10, "ScannerAdfJam SANE_STATUS_JAMMED\n"); 148141cc406Sopenharmony_ci *adf = SANE_STATUS_JAMMED; 149141cc406Sopenharmony_ci } else if (!strcmp(state, "ScannerAdfDoorOpen")) { 150141cc406Sopenharmony_ci DBG(10, "ScannerAdfDoorOpen SANE_STATUS_COVER_OPEN\n"); 151141cc406Sopenharmony_ci *adf = SANE_STATUS_COVER_OPEN; 152141cc406Sopenharmony_ci } else if (!strcmp(state, "ScannerAdfProcessing")) { 153141cc406Sopenharmony_ci /* Kyocera version */ 154141cc406Sopenharmony_ci DBG(10, "ScannerAdfProcessing SANE_STATUS_NO_DOC\n"); 155141cc406Sopenharmony_ci *adf = SANE_STATUS_NO_DOCS; 156141cc406Sopenharmony_ci } else if (!strcmp(state, "ScannerAdfEmpty")) { 157141cc406Sopenharmony_ci DBG(10, "ScannerAdfEmpty SANE_STATUS_NO_DOCS\n"); 158141cc406Sopenharmony_ci /* Cannon TR4500, EPSON XP-7100 */ 159141cc406Sopenharmony_ci *adf = SANE_STATUS_NO_DOCS; 160141cc406Sopenharmony_ci } else { 161141cc406Sopenharmony_ci DBG(10, "%s SANE_STATUS_NO_DOCS\n", state); 162141cc406Sopenharmony_ci *adf = SANE_STATUS_UNSUPPORTED; 163141cc406Sopenharmony_ci } 164141cc406Sopenharmony_ci } 165141cc406Sopenharmony_ci else if (jobId && job && strcmp((const char *)node->name, "JobUri") == 0) { 166141cc406Sopenharmony_ci if (strstr((const char *)xmlNodeGetContent(node), jobId)) { 167141cc406Sopenharmony_ci print_xml_job_status(node, job, image); 168141cc406Sopenharmony_ci } 169141cc406Sopenharmony_ci } 170141cc406Sopenharmony_ci } 171141cc406Sopenharmony_ci } 172141cc406Sopenharmony_ci print_xml_platen_and_adf_status(node->children, 173141cc406Sopenharmony_ci platen, 174141cc406Sopenharmony_ci adf, 175141cc406Sopenharmony_ci jobId, 176141cc406Sopenharmony_ci job, 177141cc406Sopenharmony_ci image); 178141cc406Sopenharmony_ci node = node->next; 179141cc406Sopenharmony_ci } 180141cc406Sopenharmony_ci} 181141cc406Sopenharmony_ci 182141cc406Sopenharmony_ci/** 183141cc406Sopenharmony_ci * \fn SANE_Status escl_status(const ESCL_Device *device) 184141cc406Sopenharmony_ci * \brief Function that finally recovers the scanner status ('Idle', or not), using curl. 185141cc406Sopenharmony_ci * This function is called in the 'sane_open' function and it's the equivalent of 186141cc406Sopenharmony_ci * the following curl command : "curl http(s)://'ip':'port'/eSCL/ScannerStatus". 187141cc406Sopenharmony_ci * 188141cc406Sopenharmony_ci * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) 189141cc406Sopenharmony_ci */ 190141cc406Sopenharmony_ciSANE_Status 191141cc406Sopenharmony_ciescl_status(const ESCL_Device *device, 192141cc406Sopenharmony_ci int source, 193141cc406Sopenharmony_ci const char* jobId, 194141cc406Sopenharmony_ci SANE_Status *job) 195141cc406Sopenharmony_ci{ 196141cc406Sopenharmony_ci SANE_Status status = SANE_STATUS_DEVICE_BUSY; 197141cc406Sopenharmony_ci SANE_Status platen= SANE_STATUS_DEVICE_BUSY; 198141cc406Sopenharmony_ci SANE_Status adf= SANE_STATUS_DEVICE_BUSY; 199141cc406Sopenharmony_ci CURL *curl_handle = NULL; 200141cc406Sopenharmony_ci struct idle *var = NULL; 201141cc406Sopenharmony_ci xmlDoc *data = NULL; 202141cc406Sopenharmony_ci xmlNode *node = NULL; 203141cc406Sopenharmony_ci const char *scanner_status = "/eSCL/ScannerStatus"; 204141cc406Sopenharmony_ci int image = -1; 205141cc406Sopenharmony_ci int pass = 0; 206141cc406Sopenharmony_cireload: 207141cc406Sopenharmony_ci 208141cc406Sopenharmony_ci if (device == NULL) 209141cc406Sopenharmony_ci return (SANE_STATUS_NO_MEM); 210141cc406Sopenharmony_ci status = SANE_STATUS_DEVICE_BUSY; 211141cc406Sopenharmony_ci platen= SANE_STATUS_DEVICE_BUSY; 212141cc406Sopenharmony_ci adf= SANE_STATUS_DEVICE_BUSY; 213141cc406Sopenharmony_ci var = (struct idle*)calloc(1, sizeof(struct idle)); 214141cc406Sopenharmony_ci if (var == NULL) 215141cc406Sopenharmony_ci return (SANE_STATUS_NO_MEM); 216141cc406Sopenharmony_ci var->memory = malloc(1); 217141cc406Sopenharmony_ci var->size = 0; 218141cc406Sopenharmony_ci curl_handle = curl_easy_init(); 219141cc406Sopenharmony_ci 220141cc406Sopenharmony_ci escl_curl_url(curl_handle, device, scanner_status); 221141cc406Sopenharmony_ci curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memory_callback_s); 222141cc406Sopenharmony_ci curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)var); 223141cc406Sopenharmony_ci curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); 224141cc406Sopenharmony_ci curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 3L); 225141cc406Sopenharmony_ci CURLcode res = curl_easy_perform(curl_handle); 226141cc406Sopenharmony_ci if (res != CURLE_OK) { 227141cc406Sopenharmony_ci DBG( 1, "The scanner didn't respond: %s\n", curl_easy_strerror(res)); 228141cc406Sopenharmony_ci status = SANE_STATUS_INVAL; 229141cc406Sopenharmony_ci goto clean_data; 230141cc406Sopenharmony_ci } 231141cc406Sopenharmony_ci DBG( 10, "eSCL : Status : %s.\n", var->memory); 232141cc406Sopenharmony_ci data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0); 233141cc406Sopenharmony_ci if (data == NULL) { 234141cc406Sopenharmony_ci status = SANE_STATUS_NO_MEM; 235141cc406Sopenharmony_ci goto clean_data; 236141cc406Sopenharmony_ci } 237141cc406Sopenharmony_ci node = xmlDocGetRootElement(data); 238141cc406Sopenharmony_ci if (node == NULL) { 239141cc406Sopenharmony_ci status = SANE_STATUS_NO_MEM; 240141cc406Sopenharmony_ci goto clean; 241141cc406Sopenharmony_ci } 242141cc406Sopenharmony_ci /* Decode Job status */ 243141cc406Sopenharmony_ci // Thank's Alexander Pevzner (pzz@apevzner.com) 244141cc406Sopenharmony_ci print_xml_platen_and_adf_status(node, &platen, &adf, jobId, job, &image); 245141cc406Sopenharmony_ci if (platen != SANE_STATUS_GOOD && 246141cc406Sopenharmony_ci platen != SANE_STATUS_UNSUPPORTED) { 247141cc406Sopenharmony_ci status = platen; 248141cc406Sopenharmony_ci } else if (source == PLATEN) { 249141cc406Sopenharmony_ci status = platen; 250141cc406Sopenharmony_ci } else { 251141cc406Sopenharmony_ci status = adf; 252141cc406Sopenharmony_ci } 253141cc406Sopenharmony_ci DBG (10, "STATUS : %s\n", sane_strstatus(status)); 254141cc406Sopenharmony_ciclean: 255141cc406Sopenharmony_ci xmlFreeDoc(data); 256141cc406Sopenharmony_ciclean_data: 257141cc406Sopenharmony_ci xmlCleanupParser(); 258141cc406Sopenharmony_ci xmlMemoryDump(); 259141cc406Sopenharmony_ci curl_easy_cleanup(curl_handle); 260141cc406Sopenharmony_ci free(var->memory); 261141cc406Sopenharmony_ci free(var); 262141cc406Sopenharmony_ci if (pass == 0 && 263141cc406Sopenharmony_ci source != PLATEN && 264141cc406Sopenharmony_ci image == 0 && 265141cc406Sopenharmony_ci (status == SANE_STATUS_GOOD || 266141cc406Sopenharmony_ci status == SANE_STATUS_UNSUPPORTED || 267141cc406Sopenharmony_ci status == SANE_STATUS_DEVICE_BUSY)) { 268141cc406Sopenharmony_ci pass = 1; 269141cc406Sopenharmony_ci goto reload; 270141cc406Sopenharmony_ci } 271141cc406Sopenharmony_ci return (status); 272141cc406Sopenharmony_ci} 273