diff --git a/backend/escl/escl.c b/backend/escl/escl.c index 62d8a4365..1afcc43a1 100644 --- a/backend/escl/escl.c +++ b/backend/escl/escl.c @@ -46,7 +46,7 @@ static int num_devices = 0; typedef struct Handled { struct Handled *next; - SANE_String_Const name; + ESCL_Device *device; char *result; ESCL_ScanParam param; SANE_Option_Descriptor opt[NUM_OPTIONS]; @@ -72,6 +72,16 @@ escl_free_device(ESCL_Device *current) return NULL; } +void +escl_free_handler(escl_sane_t *handler) +{ + if (handler == NULL) + return; + + escl_free_device(handler->device); + free(handler); +} + static SANE_Status escl_check_and_add_device(ESCL_Device *current) { @@ -155,6 +165,9 @@ escl_device_add(int port_nb, const char *model_name, char *ip_address, char *typ if (strcmp(type, "_uscan._tcp") != 0 && strcmp(type, "http") != 0) { snprintf(tmp, sizeof(tmp), "%s SSL", model_name); + current->https = SANE_TRUE; + } else { + current->https = SANE_FALSE; } model = (char*)(tmp[0] != 0 ? tmp : model_name); current->model_name = strdup(model); @@ -205,7 +218,7 @@ convertFromESCLDev(ESCL_Device *cdev) return NULL; } - if (strcmp(cdev->type, "_uscan._tcp") == 0 || strcmp(cdev->type, "http") == 0) + if (!cdev->https) 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); @@ -402,13 +415,13 @@ sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) * \return status (if everything is OK, status = SANE_STATUS_GOOD) */ static SANE_Status -init_options(SANE_String_Const name, escl_sane_t *s) +init_options(const ESCL_Device *device, escl_sane_t *s) { DBG (10, "escl init_options\n"); SANE_Status status = SANE_STATUS_GOOD; int i = 0; - if (name == NULL) + if (device == NULL) return (SANE_STATUS_INVAL); memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); @@ -528,6 +541,43 @@ init_options(SANE_String_Const name, escl_sane_t *s) return (status); } +SANE_Status +escl_parse_name(SANE_String_Const name, ESCL_Device *device) +{ + SANE_String_Const host = NULL; + SANE_String_Const port_str = NULL; + DBG(10, "escl_parse_name\n"); + if (name == NULL || device == NULL) { + return SANE_STATUS_INVAL; + } + + if (strncmp(name, "https://", 8) == 0) { + device->https = SANE_TRUE; + host = name + 8; + } else if (strncmp(name, "http://", 7) == 0) { + device->https = SANE_FALSE; + host = name + 7; + } else { + DBG(1, "Unknown URL scheme in %s", name); + return SANE_STATUS_INVAL; + } + + port_str = strchr(host, ':'); + if (port_str == NULL) { + DBG(1, "Port missing from URL: %s", name); + return SANE_STATUS_INVAL; + } + port_str++; + device->port_nb = atoi(port_str); + if (device->port_nb < 1 || device->port_nb > 65535) { + DBG(1, "Invalid port number in URL: %s", name); + return SANE_STATUS_INVAL; + } + + device->ip_address = strndup(host, port_str - host - 1); + return SANE_STATUS_GOOD; +} + /** * \fn SANE_Status sane_open(SANE_String_Const name, SANE_Handle *h) * \brief Function that establishes a connection with the device named by 'name', @@ -546,23 +596,39 @@ sane_open(SANE_String_Const name, SANE_Handle *h) 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); - if (!handler->name) { - DBG (10, "Handle Name allocation failure.\n"); - return (SANE_STATUS_NO_MEM); + + ESCL_Device *device = calloc(1, sizeof(ESCL_Device)); + if (device == NULL) { + DBG (10, "Handle device allocation failure.\n"); + return SANE_STATUS_NO_MEM; } - handler->scanner = escl_capabilities(name, &status); - if (status != SANE_STATUS_GOOD) + status = escl_parse_name(name, device); + if (status != SANE_STATUS_GOOD) { + escl_free_device(device); + return status; + } + + status = escl_status(device); + if (status != SANE_STATUS_GOOD) { + escl_free_device(device); return (status); - status = init_options(name, handler); - if (status != SANE_STATUS_GOOD) + } + handler = (escl_sane_t *)calloc(1, sizeof(escl_sane_t)); + if (handler == NULL) { + escl_free_device(device); + return (SANE_STATUS_NO_MEM); + } + handler->device = device; // Handler owns device now. + handler->scanner = escl_capabilities(device, &status); + if (status != SANE_STATUS_GOOD) { + escl_free_handler(handler); return (status); + } + status = init_options(device, handler); + if (status != SANE_STATUS_GOOD) { + escl_free_handler(handler); + return (status); + } handler->ps.depth = 8; handler->ps.last_frame = SANE_TRUE; handler->ps.format = SANE_FRAME_RGB; @@ -570,8 +636,10 @@ sane_open(SANE_String_Const name, SANE_Handle *h) handler->ps.lines = MM_TO_PIXEL(handler->val[OPT_BR_Y].w, 300.0); handler->ps.bytes_per_line = handler->ps.pixels_per_line * 3; status = sane_get_parameters(handler, 0); - if (status != SANE_STATUS_GOOD) + if (status != SANE_STATUS_GOOD) { + escl_free_handler(handler); return (status); + } handler->cancel = SANE_FALSE; handler->write_scan_data = SANE_FALSE; handler->decompress_scan_data = SANE_FALSE; @@ -597,7 +665,7 @@ sane_cancel(SANE_Handle h) handler->scanner->tmp = NULL; } handler->cancel = SANE_TRUE; - escl_scanner(handler->name, handler->result); + escl_scanner(handler->device, handler->result); } /** @@ -610,7 +678,7 @@ sane_close(SANE_Handle h) { DBG (10, "escl sane_close\n"); if (h != NULL) { - free(h); + escl_free_handler(h); h = NULL; } } @@ -727,8 +795,10 @@ sane_start(SANE_Handle h) int he = 0; int bps = 0; - if (handler->name == NULL) + if (handler->device == NULL) { + DBG(1, "Missing handler device.\n"); return (SANE_STATUS_INVAL); + } handler->cancel = SANE_FALSE; handler->write_scan_data = SANE_FALSE; handler->decompress_scan_data = SANE_FALSE; @@ -775,10 +845,10 @@ sane_start(SANE_Handle h) DBG (10, "Default Color allocation failure.\n"); return (SANE_STATUS_NO_MEM); } - handler->result = escl_newjob(handler->scanner, handler->name, &status); + handler->result = escl_newjob(handler->scanner, handler->device, &status); if (status != SANE_STATUS_GOOD) return (status); - status = escl_scan(handler->scanner, handler->name, handler->result); + status = escl_scan(handler->scanner, handler->device, handler->result); if (status != SANE_STATUS_GOOD) return (status); if (!strcmp(handler->scanner->default_format, "image/jpeg")) @@ -905,3 +975,34 @@ sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ n { return (SANE_STATUS_UNSUPPORTED); } + +/** + * \fn void escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path) + * \brief Uses the device info in 'device' and the path from 'path' to construct + * a full URL. Sets this URL and any necessary connection options into + * 'handle'. + */ +void +escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path) +{ + int url_len; + char *url; + + url_len = snprintf(NULL, 0, "%s://%s:%d%s", + (device->https ? "https" : "http"), device->ip_address, + device->port_nb, path); + url_len++; + url = (char *)malloc(url_len); + snprintf(url, url_len, "%s://%s:%d%s", + (device->https ? "https" : "http"), device->ip_address, + device->port_nb, path); + + DBG( 1, "escl_curl_url: URL: %s\n", url ); + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + if (device->https) { + DBG( 1, "Ignoring safety certificates, use https\n"); + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L); + } +} diff --git a/backend/escl/escl.h b/backend/escl/escl.h index e333f944c..f6c658575 100644 --- a/backend/escl/escl.h +++ b/backend/escl/escl.h @@ -79,10 +79,11 @@ typedef struct { typedef struct ESCL_Device { struct ESCL_Device *next; - char *model_name; - int port_nb; + char *model_name; + int port_nb; char *ip_address; - char *type; + char *type; + SANE_Bool https; } ESCL_Device; typedef struct capabilities @@ -158,13 +159,16 @@ enum 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 escl_status(const ESCL_Device *device); +capabilities_t *escl_capabilities(const ESCL_Device *device, SANE_Status *status); +char *escl_newjob(capabilities_t *scanner, const ESCL_Device *device, SANE_Status *status); -SANE_Status escl_scan(capabilities_t *scanner, SANE_String_Const name, +SANE_Status escl_scan(capabilities_t *scanner, const ESCL_Device *device, char *result); -void escl_scanner(SANE_String_Const name, char *result); +void escl_scanner(const ESCL_Device *device, char *result); + +typedef void CURL; +void escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path); unsigned char *escl_crop_surface(capabilities_t *scanner, unsigned char *surface, int w, int h, int bps, int *width, int *height); diff --git a/backend/escl/escl_capabilities.c b/backend/escl/escl_capabilities.c index 690ff1eff..81ce6ee0b 100644 --- a/backend/escl/escl_capabilities.c +++ b/backend/escl/escl_capabilities.c @@ -319,7 +319,7 @@ print_xml_c(xmlNode *node, capabilities_t *scanner) } /** - * \fn capabilities_t *escl_capabilities(SANE_String_Const name, SANE_Status *status) + * \fn capabilities_t *escl_capabilities(const ESCL_Device *device, 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". @@ -327,7 +327,7 @@ print_xml_c(xmlNode *node, capabilities_t *scanner) * \return scanner (the structure that stocks all the capabilities elements) */ capabilities_t * -escl_capabilities(SANE_String_Const name, SANE_Status *status) +escl_capabilities(const ESCL_Device *device, SANE_Status *status) { capabilities_t *scanner = (capabilities_t*)calloc(1, sizeof(capabilities_t)); CURL *curl_handle = NULL; @@ -335,10 +335,9 @@ escl_capabilities(SANE_String_Const name, SANE_Status *status) xmlDoc *data = NULL; xmlNode *node = NULL; const char *scanner_capabilities = "/eSCL/ScannerCapabilities"; - char tmp[PATH_MAX] = { 0 }; *status = SANE_STATUS_GOOD; - if (name == NULL) + if (device == NULL) *status = SANE_STATUS_NO_MEM; var = (struct cap *)calloc(1, sizeof(struct cap)); if (var == NULL) @@ -346,15 +345,7 @@ escl_capabilities(SANE_String_Const name, SANE_Status *status) var->memory = malloc(1); var->size = 0; curl_handle = curl_easy_init(); - strcpy(tmp, name); - strcat(tmp, scanner_capabilities); - DBG( 1, "Get Capabilities : %s\n", tmp); - curl_easy_setopt(curl_handle, CURLOPT_URL, tmp); - if (strncmp(name, "https", 5) == 0) { - DBG( 1, "Ignoring safety certificates, use https\n"); - curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); - } + escl_curl_url(curl_handle, device, scanner_capabilities); 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) { diff --git a/backend/escl/escl_newjob.c b/backend/escl/escl_newjob.c index 084431cc3..bde54234e 100644 --- a/backend/escl/escl_newjob.c +++ b/backend/escl/escl_newjob.c @@ -122,7 +122,7 @@ download_callback(void *str, size_t size, size_t nmemb, void *userp) } /** - * \fn char *escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *status) + * \fn char *escl_newjob (capabilities_t *scanner, const ESCL_Device *device, 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 @@ -131,14 +131,13 @@ download_callback(void *str, size_t size, size_t nmemb, void *userp) * \return result (the 'new job', situated in LOCATION) */ char * -escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *status) +escl_newjob (capabilities_t *scanner, const ESCL_Device *device, 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; @@ -146,7 +145,7 @@ escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *statu char *format_ext = NULL; *status = SANE_STATUS_GOOD; - if (name == NULL || scanner == NULL) { + if (device == NULL || scanner == NULL) { *status = SANE_STATUS_NO_MEM; DBG( 1, "Create NewJob : the name or the scan are invalid.\n"); return (NULL); @@ -188,13 +187,7 @@ escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *statu 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); - } + escl_curl_url(curl_handle, device, scan_jobs); 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); diff --git a/backend/escl/escl_reset.c b/backend/escl/escl_reset.c index 7722d898d..f5737bc08 100644 --- a/backend/escl/escl_reset.c +++ b/backend/escl/escl_reset.c @@ -32,12 +32,12 @@ #include /** - * \fn void escl_scanner(SANE_String_Const name, char *result) + * \fn void escl_scanner(const ESCL_Device *device, 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) +escl_scanner(const ESCL_Device *device, char *result) { CURL *curl_handle = NULL; const char *scan_jobs = "/eSCL/ScanJobs"; @@ -46,22 +46,14 @@ escl_scanner(SANE_String_Const name, char *result) int i = 0; long answer = 0; - if (name == NULL || result == NULL) + if (device == NULL || result == NULL) return; 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); - DBG( 1, "Reset Job : %s.\n", scan_cmd); - if (strncmp(name, "https", 5) == 0) { - DBG( 1, "Ignoring safety certificates, use https\n"); - curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); - } + snprintf(scan_cmd, sizeof(scan_cmd), "%s%s%s", + scan_jobs, result, scanner_start); + escl_curl_url(curl_handle, device, scan_cmd); if (curl_easy_perform(curl_handle) == CURLE_OK) { curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &answer); if (i < 3 && answer == 503) { diff --git a/backend/escl/escl_scan.c b/backend/escl/escl_scan.c index 8f077a132..f5721b034 100644 --- a/backend/escl/escl_scan.c +++ b/backend/escl/escl_scan.c @@ -49,7 +49,7 @@ write_callback(void *str, size_t size, size_t nmemb, void *userp) } /** - * \fn SANE_Status escl_scan(capabilities_t *scanner, SANE_String_Const name, char *result) + * \fn SANE_Status escl_scan(capabilities_t *scanner, const ESCL_Device *device, 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 @@ -58,7 +58,7 @@ write_callback(void *str, size_t size, size_t nmemb, void *userp) * \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) +escl_scan(capabilities_t *scanner, const ESCL_Device *device, char *result) { CURL *curl_handle = NULL; const char *scan_jobs = "/eSCL/ScanJobs"; @@ -66,21 +66,13 @@ escl_scan(capabilities_t __sane_unused__ *scanner, SANE_String_Const name, char char scan_cmd[PATH_MAX] = { 0 }; SANE_Status status = SANE_STATUS_GOOD; - if (name == NULL) + if (device == NULL) return (SANE_STATUS_NO_MEM); 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); - DBG( 1, "Scan : %s.\n", scan_cmd); - if (strncmp(name, "https", 5) == 0) { - DBG( 1, "Ignoring safety certificates, use https\n"); - curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); - } + snprintf(scan_cmd, sizeof(scan_cmd), "%s%s%s", + scan_jobs, result, scanner_start); + escl_curl_url(curl_handle, device, scan_cmd); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback); scanner->tmp = tmpfile(); if (scanner->tmp != NULL) { diff --git a/backend/escl/escl_status.c b/backend/escl/escl_status.c index 68b51dcfa..8feb1a941 100644 --- a/backend/escl/escl_status.c +++ b/backend/escl/escl_status.c @@ -110,7 +110,7 @@ print_xml_s(xmlNode *node, SANE_Status *status) } /** - * \fn SANE_Status escl_status(SANE_String_Const name) + * \fn SANE_Status escl_status(const ESCL_Device *device) * \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". @@ -118,7 +118,7 @@ print_xml_s(xmlNode *node, SANE_Status *status) * \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) +escl_status(const ESCL_Device *device) { SANE_Status status; CURL *curl_handle = NULL; @@ -126,9 +126,8 @@ escl_status(SANE_String_Const name) xmlDoc *data = NULL; xmlNode *node = NULL; const char *scanner_status = "/eSCL/ScannerStatus"; - char tmp[PATH_MAX] = { 0 }; - if (name == NULL) + if (device == NULL) return (SANE_STATUS_NO_MEM); var = (struct idle*)calloc(1, sizeof(struct idle)); if (var == NULL) @@ -136,15 +135,8 @@ escl_status(SANE_String_Const name) var->memory = malloc(1); var->size = 0; curl_handle = curl_easy_init(); - strcpy(tmp, name); - strcat(tmp, scanner_status); - curl_easy_setopt(curl_handle, CURLOPT_URL, tmp); - DBG( 1, "Get Status : %s.\n", tmp); - if (strncmp(name, "https", 5) == 0) { - DBG( 1, "Ignoring safety certificates, use https\n"); - curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); - } + + escl_curl_url(curl_handle, device, scanner_status); 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) {