1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * Copyright (C) 2016 Mopria Alliance, Inc. 4 * Copyright (C) 2013 Hewlett-Packard Development Company, L.P. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19#include <stdio.h> 20#include <stdlib.h> 21#include <sys/stat.h> 22#include <unistd.h> 23#include <errno.h> 24#include <sys/socket.h> 25#include <arpa/inet.h> 26#include <fcntl.h> 27#include <netdb.h> 28 29#include "ifc_print_job.h" 30#include "wprint_debug.h" 31 32#define TAG "printer" 33 34#define DEFAULT_TIMEOUT (5000) 35 36typedef struct { 37 ifc_print_job_t ifc; 38 int port_num; 39 int psock; 40 wJob_t job_id; 41 status_t job_status; 42 int timeout_enabled; 43} _print_job_t; 44 45static long int _wprint_timeout_msec = DEFAULT_TIMEOUT; 46 47static status_t _init(const ifc_print_job_t *this_p, const char *printer_addr, int port, 48 const char *printer_uri, bool use_secure_uri) { 49 _print_job_t *print_job = IMPL(_print_job_t, ifc, this_p); 50 51 if (!print_job) return ERROR; 52 53 // if a print-to-file is requested, open a file 54 55 if (print_job->port_num == PORT_FILE) { 56 print_job->psock = open(printer_addr, O_CREAT | O_WRONLY | O_TRUNC, 57 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 58 59 if (print_job->psock == ERROR) { 60 LOGE("cannot create output file : %s, %s", printer_addr, strerror(errno)); 61 } else { 62 LOGI("opened %s for writing", printer_addr); 63 } 64 } else { 65 // open a socket to the printer:port 66 print_job->psock = wConnect(printer_addr, print_job->port_num, _wprint_timeout_msec); 67 } 68 69 print_job->job_status = ((print_job->psock != -1) ? OK : ERROR); 70 return print_job->job_status; 71} 72 73static void _destroy(const ifc_print_job_t *this_p) { 74 _print_job_t *print_job = IMPL(_print_job_t, ifc, this_p); 75 if (print_job) { 76 free(print_job); 77 } 78} 79 80static int _start_job(const ifc_print_job_t *this_p, const wprint_job_params_t *job_params) { 81 _print_job_t *print_job = IMPL(_print_job_t, ifc, this_p); 82 83 if (print_job) { 84 return OK; 85 } else { 86 return ERROR; 87 } 88} 89 90static int _send_data(const ifc_print_job_t *this_p, const char *buffer, size_t length) { 91 status_t retval = OK; 92 size_t length_in = length; 93 ssize_t bytes_written; 94 _print_job_t *print_job = IMPL(_print_job_t, ifc, this_p); 95 96 if (this_p && buffer && (print_job->job_status == OK)) { 97 if (print_job->port_num == PORT_FILE) { 98 while ((length > 0) && (retval != -1)) { 99 bytes_written = write(print_job->psock, buffer, length); 100 if (bytes_written < 0) { 101 retval = ERROR; 102 } else { 103 length -= bytes_written; 104 buffer += bytes_written; 105 } 106 } 107 } else { 108 fd_set w_fds; 109 int selreturn; 110 struct timeval timeout; 111 112 while ((length > 0) && (retval == OK)) { 113 FD_ZERO(&w_fds); 114 FD_SET(print_job->psock, &w_fds); 115 timeout.tv_sec = 20; 116 timeout.tv_usec = 0; 117 selreturn = select(print_job->psock + 1, NULL, &w_fds, NULL, &timeout); 118 if (selreturn < 0) { 119 LOGE("select returned an errnor (%d)", errno); 120 retval = ERROR; 121 } else if (selreturn > 0) { 122 if (FD_ISSET(print_job->psock, &w_fds)) { 123 bytes_written = write(print_job->psock, buffer, length); 124 if (bytes_written < 0) { 125 LOGE("unable to transmit %d bytes of data (errno %d)", length, errno); 126 retval = ERROR; 127 } else { 128 length -= bytes_written; 129 buffer += bytes_written; 130 } 131 } else { 132 LOGE("select returned OK, but fd is not set"); 133 retval = ERROR; 134 } 135 } else { 136 retval = (print_job->timeout_enabled ? ERROR : OK); 137 if (retval == ERROR) { 138 LOGE("select timed out"); 139 } 140 } 141 } 142 } 143 144 print_job->job_status = retval; 145 } else { 146 retval = ERROR; 147 } 148 return ((retval == OK) ? length_in : (int)ERROR); 149} 150 151static int _end_job(const ifc_print_job_t *this_p) { 152 _print_job_t *print_job = IMPL(_print_job_t, ifc, this_p); 153 if (print_job) { 154 close(print_job->psock); 155 print_job->psock = -1; 156 return print_job->job_status; 157 } 158 return ERROR; 159} 160 161static void _enable_timeout(const ifc_print_job_t *this_p, int enable) { 162 _print_job_t *print_job = IMPL(_print_job_t, ifc, this_p); 163 if (print_job) { 164 print_job->timeout_enabled = enable; 165 } 166} 167 168static int _check_status(const ifc_print_job_t *this_p) { 169 _print_job_t *print_job = IMPL(_print_job_t, ifc, this_p); 170 171 if (print_job) return print_job->job_status; 172 173 return ERROR; 174} 175 176int wConnect(const char *printer_addr, int port_num, long int timeout_msec) { 177 struct sockaddr_in sin; 178 struct hostent *h_info; 179 fd_set fdset; 180 struct timeval tv; 181 int psock; 182 183 psock = socket(PF_INET, SOCK_STREAM, 0); 184 if (psock == ERROR) return ERROR; 185 186 memset((char *) &sin, 0, sizeof(sin)); 187 sin.sin_family = AF_INET; 188 sin.sin_port = htons(port_num); 189 190 if ((sin.sin_addr.s_addr = inet_addr(printer_addr)) == -1) { 191 /* 192 * The IP address is not in dotted decimal notation. Try to get the 193 * network peripheral IP address by host name. 194 */ 195 196 if ((h_info = gethostbyname(printer_addr)) != NULL) { 197 (void) memcpy(&(sin.sin_addr.s_addr), h_info->h_addr, h_info->h_length); 198 } else { 199 LOGE("ERROR: unknown host %s", printer_addr); 200 close(psock); 201 return ERROR; 202 } 203 } 204 205 // temporarily set the socket to NONBLOCK'ing mode to catch timeout 206 fcntl(psock, F_SETFL, O_NONBLOCK); 207 208 // open a TCP connection to the printer:port 209 int socketConnect = connect(psock, (const struct sockaddr *) &sin, sizeof(sin)); 210 if (socketConnect == 0) { 211 FD_ZERO(&fdset); 212 FD_SET(psock, &fdset); 213 214 tv.tv_sec = (timeout_msec / 1000); 215 tv.tv_usec = (timeout_msec % 1000) * 1000; 216 217 /* check if the socket is connected and available for write within 218 * the specified timeout period 219 */ 220 if (select(psock + 1, NULL, &fdset, NULL, &tv) == 1) { 221 int so_error, flags; 222 socklen_t len = sizeof so_error; 223 224 getsockopt(psock, SOL_SOCKET, SO_ERROR, &so_error, &len); 225 if (so_error == 0) { 226 // restore the socket back to normal blocking mode 227 228 flags = fcntl(psock, F_GETFL); 229 fcntl(psock, F_SETFL, flags & ~O_NONBLOCK); 230 231 LOGI("connected to %s:%d", printer_addr, port_num); 232 } else { 233 close(psock); 234 psock = ERROR; 235 LOGE("cannot connect on %s:%d, %s", printer_addr, port_num, strerror(errno)); 236 } 237 } else { 238 LOGE("connecting to %s:%d .. timed out after %ld milliseconds", printer_addr, 239 port_num, timeout_msec); 240 close(psock); 241 psock = ERROR; 242 } 243 } 244 return psock; 245} 246 247static const ifc_print_job_t _print_job_ifc = {.init = _init, .validate_job = NULL, 248 .start_job = _start_job, .send_data = _send_data, .end_job = _end_job, .destroy = _destroy, 249 .enable_timeout = _enable_timeout, .check_status = _check_status,}; 250 251const ifc_print_job_t *printer_connect(int port_num) { 252 _print_job_t *print_job; 253 print_job = (_print_job_t *) malloc(sizeof(_print_job_t)); 254 255 if (print_job) { 256 print_job->port_num = port_num; 257 print_job->psock = -1; 258 print_job->job_id = WPRINT_BAD_JOB_HANDLE; 259 print_job->job_status = ERROR; 260 print_job->timeout_enabled = 0; 261 memcpy(&print_job->ifc, &_print_job_ifc, sizeof(ifc_print_job_t)); 262 263 return &print_job->ifc; 264 } else { 265 return NULL; 266 } 267}