/*** This file is part of cups-filters. This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This file 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with avahi; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5) #define HAVE_CUPS_1_6 1 #endif enum resolve_uri_converter_type /**** Resolving DNS-SD based URI ****/ { CUPS_BACKEND_URI_CONVERTER = -1, IPPFIND_BASED_CONVERTER_FOR_PRINT_URI = 0, IPPFIND_BASED_CONVERTER_FOR_FAX_URI = 1 }; char get_printer_attributes_log[LOGSIZE]; static int convert_to_port(char *a) { int port = 0; for (int i = 0; i 0; i --) if (ippFindAttribute(response, req_attrs[i - 1], IPP_TAG_ZERO) == NULL) break; if (ipp_status == IPP_STATUS_ERROR_BAD_REQUEST || ipp_status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED || (req_attrs && i > 0) || (cap && total_attrs < 20)) { log_printf(get_printer_attributes_log, "get-printer-attributes IPP request failed:\n"); if (ipp_status == IPP_STATUS_ERROR_BAD_REQUEST) log_printf(get_printer_attributes_log, " - ipp_status == IPP_STATUS_ERROR_BAD_REQUEST\n"); else if (ipp_status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED) log_printf(get_printer_attributes_log, " - ipp_status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED\n"); if (req_attrs && i > 0) log_printf(get_printer_attributes_log, " - Required IPP attribute %s not found\n", req_attrs[i - 1]); if (cap && total_attrs < 20) log_printf(get_printer_attributes_log, " - Too few IPP attributes: %d (30 or more expected)\n", total_attrs); ippDelete(response); } else { /* Suitable response, we are done */ // if we did not succeed to obtain the "media-col-database" attribute // try to get it separately if (cap && ippFindAttribute(response, "media-col-database", IPP_TAG_ZERO) == NULL) { ipp_t *response2 = NULL; log_printf(get_printer_attributes_log, "Polling \"media-col-database\" attribute separately.\n"); request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); ippSetVersion(request, 2, 0); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "media-col-database"); response2 = cupsDoRequest(http_printer, request, resource); ipp_status = cupsLastError(); if (response2) { if ((attr = ippFindAttribute(response2, "media-col-database", IPP_TAG_ZERO)) != NULL) { // Copy "media-col-database" attribute into the original // IPP response log_printf(get_printer_attributes_log, "\"media-col-database\" attribute found.\n"); ippCopyAttribute(response, attr, 0); } ippDelete(response2); } } if (have_http == 0) httpClose(http_printer); if (uri) free(uri); return response; } } else { log_printf(get_printer_attributes_log, "Request for IPP attributes (get-printer-attributes) for printer with URI %s failed: %s\n", uri, cupsLastErrorString()); log_printf(get_printer_attributes_log, "get-printer-attributes IPP request failed:\n"); log_printf(get_printer_attributes_log, " - No response\n"); } if (fallback == 1 + cap) { log_printf(get_printer_attributes_log, "No further fallback available, giving up\n"); if (driverless_info != NULL) *driverless_info = DRVLESS_CHECKERR; } else if (cap && fallback == 1) { log_printf(get_printer_attributes_log, "The server doesn't support the standard IPP request, trying request without media-col\n"); if (driverless_info != NULL) *driverless_info = DRVLESS_INCOMPLETEIPP; } else if (fallback == 0) { log_printf(get_printer_attributes_log, "The server doesn't support IPP2.0 request, trying IPP1.1 request\n"); if (driverless_info != NULL) *driverless_info = DRVLESS_IPP11; } } if (have_http == 0) httpClose(http_printer); if (uri) free(uri); return NULL; } char* ippfind_based_uri_converter (const char *uri, int is_fax) { int ippfind_pid = 0, /* Process ID of ippfind for IPP */ post_proc_pipe[2], /* Pipe to post-processing for IPP */ wait_children, /* Number of child processes left */ wait_pid, /* Process ID from wait() */ wait_status, /* Status from child */ exit_status = 0, /* Exit status */ bytes, port, i, output_of_fax_uri = 0, is_local; char *ippfind_argv[100], /* Arguments for ippfind */ *ptr_to_port = NULL, *reg_type, *resolved_uri = NULL, /* Buffer for resolved URI */ *resource_field = NULL, *service_hostname = NULL, /* URI components... */ scheme[32], userpass[256], hostname[1024], resource[1024], *buffer = NULL, /* Copy buffer */ *ptr; /* Pointer into string */; cups_file_t *fp; /* Post-processing input file */ int status; /* Status of GET request */ status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)); if (status < HTTP_URI_OK) { /* Invalid URI */ fprintf(stderr, "ERROR: Could not parse URI: %s\n", uri); goto error; } /* URI is not DNS-SD-based, so do not resolve */ if ((reg_type = strstr(hostname, "._tcp")) == NULL) { return strdup(uri); } resolved_uri = (char *)malloc(MAX_URI_LEN * (sizeof(char))); if (resolved_uri == NULL) { fprintf(stderr, "resolved_uri malloc: Out of memory\n"); goto error; } memset(resolved_uri, 0, MAX_URI_LEN); reg_type --; while (reg_type >= hostname && *reg_type != '.') reg_type --; if (reg_type < hostname) { fprintf(stderr, "ERROR: Invalid DNS-SD service name: %s\n", hostname); goto error; } *reg_type++ = '\0'; i = 0; ippfind_argv[i++] = "ippfind"; ippfind_argv[i++] = reg_type; /* list IPP(S) entries */ ippfind_argv[i++] = "-T"; /* DNS-SD poll timeout */ ippfind_argv[i++] = "0"; /* Minimum time required */ if (is_fax) { ippfind_argv[i++] = "--txt"; ippfind_argv[i++] = "rfo"; } ippfind_argv[i++] = "-N"; ippfind_argv[i++] = hostname; ippfind_argv[i++] = "-x"; ippfind_argv[i++] = "echo"; /* Output the needed data fields */ ippfind_argv[i++] = "-en"; /* separated by tab characters */ if(is_fax) ippfind_argv[i++] = "\n{service_hostname}\t{txt_rfo}\t{service_port}\t"; else ippfind_argv[i++] = "\n{service_hostname}\t{txt_rp}\t{service_port}\t"; ippfind_argv[i++] = ";"; ippfind_argv[i++] = "--local"; /* Rest only if local service */ ippfind_argv[i++] = "-x"; ippfind_argv[i++] = "echo"; /* Output an 'L' at the end of the */ ippfind_argv[i++] = "-en"; /* line */ ippfind_argv[i++] = "L"; ippfind_argv[i++] = ";"; ippfind_argv[i++] = NULL; /* * Create a pipe for passing the ippfind output to post-processing */ if (pipe(post_proc_pipe)) { perror("ERROR: Unable to create pipe to post-processing"); goto error; } if ((ippfind_pid = fork()) == 0) { /* * Child comes here... */ dup2(post_proc_pipe[1], 1); close(post_proc_pipe[0]); close(post_proc_pipe[1]); execvp(CUPS_IPPFIND, ippfind_argv); perror("ERROR: Unable to execute ippfind utility"); exit(1); } else if (ippfind_pid < 0) { /* * Unable to fork! */ perror("ERROR: Unable to execute ippfind utility"); goto error; } close(post_proc_pipe[1]); fp = cupsFileOpenFd(post_proc_pipe[0], "r"); buffer = (char*)malloc(MAX_OUTPUT_LEN * sizeof(char)); if (buffer == NULL) { fprintf(stderr, "buffer malloc: Out of memory.\n"); goto error; } memset(buffer, 0, MAX_OUTPUT_LEN); while ((bytes = cupsFileGetLine(fp, buffer, MAX_OUTPUT_LEN)) > 0) { /* Mark all the fields of the output of ippfind */ ptr = buffer; /* ignore new lines */ if (bytes < 3) goto read_error; /* First, build the DNS-SD-service-name-based URI ... */ while (ptr && !isalnum(*ptr & 255)) ptr ++; service_hostname = ptr; ptr = memchr(ptr, '\t', MAX_OUTPUT_LEN - (ptr - buffer)); if (!ptr) goto read_error; *ptr = '\0'; ptr ++; resource_field = ptr; ptr = memchr(ptr, '\t', MAX_OUTPUT_LEN - (ptr - buffer)); if (!ptr) goto read_error; *ptr = '\0'; ptr ++; ptr_to_port = ptr; ptr = memchr(ptr, '\t', MAX_OUTPUT_LEN - (ptr - buffer)); if (!ptr) goto read_error; *ptr = '\0'; ptr ++; /* Do we have a local service so that we have to set the host name to "localhost"? */ is_local = (*ptr == 'L'); ptr = strchr(reg_type, '.'); if (!ptr) goto read_error; *ptr = '\0'; port = convert_to_port(ptr_to_port); httpAssembleURIf(HTTP_URI_CODING_ALL, resolved_uri, 2047, reg_type + 1, NULL, (is_local ? "localhost" : service_hostname), port, "/%s", resource_field); if (is_fax) output_of_fax_uri = 1; /* fax-uri requested from fax-capable device */ read_error: memset(buffer, 0, MAX_OUTPUT_LEN); } cupsFileClose(fp); if (buffer != NULL) free(buffer); /* * Wait for the child processes to exit... */ wait_children = 1; while (wait_children > 0) { /* * Wait until we get a valid process ID or the job is canceled... */ while ((wait_pid = wait(&wait_status)) < 0 && errno == EINTR) { } if (wait_pid < 0) break; wait_children --; /* * Report child status... */ if (wait_status) { if (WIFEXITED(wait_status)) { exit_status = WEXITSTATUS(wait_status); if (wait_pid == ippfind_pid && exit_status <= 2) exit_status = 0; } else if (WTERMSIG(wait_status) == SIGTERM) { } else { exit_status = WTERMSIG(wait_status); } } } if (is_fax && !output_of_fax_uri) { fprintf(stderr, "fax URI requested from not fax-capable device\n"); goto error; } return (resolved_uri); /* * Exit... */ error: if (resolved_uri != NULL) free(resolved_uri); return (NULL); } #endif /* HAVE_CUPS_1_6 */