1 /* sane - Scanner Access Now Easy.
2 
3    Copyright (C) 2019 Touboul Nathane
4    Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
5 
6    This file is part of the SANE package.
7 
8    SANE is free software; you can redistribute it and/or modify it under
9    the terms of the GNU General Public License as published by the Free
10    Software Foundation; either version 3 of the License, or (at your
11    option) any later version.
12 
13    SANE is distributed in the hope that it will be useful, but WITHOUT
14    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16    for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with sane; see the file COPYING.
20    If not, see <https://www.gnu.org/licenses/>.
21 
22    This file implements a SANE backend for eSCL scanners.  */
23 
24 #define DEBUG_DECLARE_ONLY
25 #include "../include/sane/config.h"
26 
27 #include "escl.h"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "../include/sane/sanei.h"
34 
35 /**
36  * \fn static size_t write_callback(void *str, size_t size, size_t nmemb, void *userp)
37  * \brief Callback function that writes the image scanned into the temporary file.
38  *
39  * \return to_write (the result of the fwrite function)
40  */
41 static size_t
write_callback(void *str, size_t size, size_t nmemb, void *userp)42 write_callback(void *str, size_t size, size_t nmemb, void *userp)
43 {
44     capabilities_t *scanner = (capabilities_t *)userp;
45     size_t to_write = fwrite(str, size, nmemb, scanner->tmp);
46     scanner->real_read += to_write;
47     return (to_write);
48 }
49 
50 /**
51  * \fn SANE_Status escl_scan(capabilities_t *scanner, const ESCL_Device *device, char *result)
52  * \brief Function that, after recovering the 'new job', scans the image writed in the
53  *        temporary file, using curl.
54  *        This function is called in the 'sane_start' function and it's the equivalent of
55  *        the following curl command : "curl -s http(s)://'ip:'port'/eSCL/ScanJobs/'new job'/NextDocument > image.jpg".
56  *
57  * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
58  */
59 SANE_Status
escl_scan(capabilities_t *scanner, const ESCL_Device *device, char *scanJob, char *result)60 escl_scan(capabilities_t *scanner, const ESCL_Device *device, char *scanJob, char *result)
61 {
62     CURL *curl_handle = NULL;
63     const char *scan_jobs = "/eSCL/";
64     const char *scanner_start = "/NextDocument";
65     char scan_cmd[PATH_MAX] = { 0 };
66     SANE_Status status = SANE_STATUS_GOOD;
67 
68     if (device == NULL)
69         return (SANE_STATUS_NO_MEM);
70     scanner->real_read = 0;
71     curl_handle = curl_easy_init();
72     if (curl_handle != NULL) {
73         snprintf(scan_cmd, sizeof(scan_cmd), "%s%s%s%s",
74                  scan_jobs, scanJob, result, scanner_start);
75         escl_curl_url(curl_handle, device, scan_cmd);
76         curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback);
77         curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
78         curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 3L);
79         if (scanner->tmp)
80             fclose(scanner->tmp);
81         scanner->tmp = tmpfile();
82         if (scanner->tmp != NULL) {
83             curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, scanner);
84             CURLcode res = curl_easy_perform(curl_handle);
85             if (res != CURLE_OK) {
86                 DBG( 1, "Unable to scan: %s\n", curl_easy_strerror(res));
87                 scanner->real_read = 0;
88                 fclose(scanner->tmp);
89                 scanner->tmp = NULL;
90                 status = SANE_STATUS_INVAL;
91 		goto cleanup;
92             }
93             fseek(scanner->tmp, 0, SEEK_SET);
94         }
95         else
96             status = SANE_STATUS_NO_MEM;
97 cleanup:
98         curl_easy_cleanup(curl_handle);
99     }
100     DBG(10, "eSCL scan : [%s]\treal read (%ld)\n", sane_strstatus(status), scanner->real_read);
101     if (scanner->real_read == 0)
102     {
103        fclose(scanner->tmp);
104        scanner->tmp = NULL;
105        return SANE_STATUS_NO_DOCS;
106     }
107     return (status);
108 }
109