1 /* 2 * Get/put file functions for CUPS. 3 * 4 * Copyright 2007-2014 by Apple Inc. 5 * Copyright 1997-2006 by Easy Software Products. 6 * 7 * These coded instructions, statements, and computer programs are the 8 * property of Apple Inc. and are protected by Federal copyright 9 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 10 * which should have been included with this file. If this file is 11 * missing or damaged, see the license at "http://www.cups.org/". 12 * 13 * This file is subject to the Apple OS-Developed Software exception. 14 */ 15 16 /* 17 * Include necessary headers... 18 */ 19 20 #include "cups-private.h" 21 #include <fcntl.h> 22 #include <sys/stat.h> 23 #if defined(WIN32) || defined(__EMX__) 24 # include <io.h> 25 #else 26 # include <unistd.h> 27 #endif /* WIN32 || __EMX__ */ 28 29 30 /* 31 * 'cupsGetFd()' - Get a file from the server. 32 * 33 * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved. 34 * 35 * @since CUPS 1.1.20/macOS 10.4@ 36 */ 37 38 http_status_t /* O - HTTP status */ 39 cupsGetFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 40 const char *resource, /* I - Resource name */ 41 int fd) /* I - File descriptor */ 42 { 43 ssize_t bytes; /* Number of bytes read */ 44 char buffer[8192]; /* Buffer for file */ 45 http_status_t status; /* HTTP status from server */ 46 char if_modified_since[HTTP_MAX_VALUE]; 47 /* If-Modified-Since header */ 48 49 50 /* 51 * Range check input... 52 */ 53 54 DEBUG_printf(("cupsGetFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd)); 55 56 if (!resource || fd < 0) 57 { 58 if (http) 59 http->error = EINVAL; 60 61 return (HTTP_STATUS_ERROR); 62 } 63 64 if (!http) 65 if ((http = _cupsConnect()) == NULL) 66 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 67 68 /* 69 * Then send GET requests to the HTTP server... 70 */ 71 72 strlcpy(if_modified_since, httpGetField(http, HTTP_FIELD_IF_MODIFIED_SINCE), 73 sizeof(if_modified_since)); 74 75 do 76 { 77 if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close")) 78 { 79 httpClearFields(http); 80 if (httpReconnect2(http, 30000, NULL)) 81 { 82 status = HTTP_STATUS_ERROR; 83 break; 84 } 85 } 86 87 httpClearFields(http); 88 httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); 89 httpSetField(http, HTTP_FIELD_IF_MODIFIED_SINCE, if_modified_since); 90 91 if (httpGet(http, resource)) 92 { 93 if (httpReconnect2(http, 30000, NULL)) 94 { 95 status = HTTP_STATUS_ERROR; 96 break; 97 } 98 else 99 { 100 status = HTTP_STATUS_UNAUTHORIZED; 101 continue; 102 } 103 } 104 105 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE); 106 107 if (status == HTTP_STATUS_UNAUTHORIZED) 108 { 109 /* 110 * Flush any error message... 111 */ 112 113 httpFlush(http); 114 115 /* 116 * See if we can do authentication... 117 */ 118 119 if (cupsDoAuthentication(http, "GET", resource)) 120 { 121 status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; 122 break; 123 } 124 125 if (httpReconnect2(http, 30000, NULL)) 126 { 127 status = HTTP_STATUS_ERROR; 128 break; 129 } 130 131 continue; 132 } 133 #ifdef HAVE_SSL 134 else if (status == HTTP_STATUS_UPGRADE_REQUIRED) 135 { 136 /* Flush any error message... */ 137 httpFlush(http); 138 139 /* Reconnect... */ 140 if (httpReconnect2(http, 30000, NULL)) 141 { 142 status = HTTP_STATUS_ERROR; 143 break; 144 } 145 146 /* Upgrade with encryption... */ 147 httpEncryption(http, HTTP_ENCRYPTION_REQUIRED); 148 149 /* Try again, this time with encryption enabled... */ 150 continue; 151 } 152 #endif /* HAVE_SSL */ 153 } 154 while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED); 155 156 /* 157 * See if we actually got the file or an error... 158 */ 159 160 if (status == HTTP_STATUS_OK) 161 { 162 /* 163 * Yes, copy the file... 164 */ 165 166 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) 167 write(fd, buffer, (size_t)bytes); 168 } 169 else 170 { 171 _cupsSetHTTPError(status); 172 httpFlush(http); 173 } 174 175 /* 176 * Return the request status... 177 */ 178 179 DEBUG_printf(("1cupsGetFd: Returning %d...", status)); 180 181 return (status); 182 } 183 184 185 /* 186 * 'cupsGetFile()' - Get a file from the server. 187 * 188 * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved. 189 * 190 * @since CUPS 1.1.20/macOS 10.4@ 191 */ 192 193 http_status_t /* O - HTTP status */ 194 cupsGetFile(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 195 const char *resource, /* I - Resource name */ 196 const char *filename) /* I - Filename */ 197 { 198 int fd; /* File descriptor */ 199 http_status_t status; /* Status */ 200 201 202 /* 203 * Range check input... 204 */ 205 206 if (!http || !resource || !filename) 207 { 208 if (http) 209 http->error = EINVAL; 210 211 return (HTTP_STATUS_ERROR); 212 } 213 214 /* 215 * Create the file... 216 */ 217 218 if ((fd = open(filename, O_WRONLY | O_EXCL | O_TRUNC)) < 0) 219 { 220 /* 221 * Couldn't open the file! 222 */ 223 224 http->error = errno; 225 226 return (HTTP_STATUS_ERROR); 227 } 228 229 /* 230 * Get the file... 231 */ 232 233 status = cupsGetFd(http, resource, fd); 234 235 /* 236 * If the file couldn't be gotten, then remove the file... 237 */ 238 239 close(fd); 240 241 if (status != HTTP_STATUS_OK) 242 unlink(filename); 243 244 /* 245 * Return the HTTP status code... 246 */ 247 248 return (status); 249 } 250 251 252 /* 253 * 'cupsPutFd()' - Put a file on the server. 254 * 255 * This function returns @code HTTP_STATUS_CREATED@ when the file is stored 256 * successfully. 257 * 258 * @since CUPS 1.1.20/macOS 10.4@ 259 */ 260 261 http_status_t /* O - HTTP status */ 262 cupsPutFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 263 const char *resource, /* I - Resource name */ 264 int fd) /* I - File descriptor */ 265 { 266 ssize_t bytes; /* Number of bytes read */ 267 int retries; /* Number of retries */ 268 char buffer[8192]; /* Buffer for file */ 269 http_status_t status; /* HTTP status from server */ 270 271 272 /* 273 * Range check input... 274 */ 275 276 DEBUG_printf(("cupsPutFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd)); 277 278 if (!resource || fd < 0) 279 { 280 if (http) 281 http->error = EINVAL; 282 283 return (HTTP_STATUS_ERROR); 284 } 285 286 if (!http) 287 if ((http = _cupsConnect()) == NULL) 288 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 289 290 /* 291 * Then send PUT requests to the HTTP server... 292 */ 293 294 retries = 0; 295 296 do 297 { 298 if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close")) 299 { 300 httpClearFields(http); 301 if (httpReconnect2(http, 30000, NULL)) 302 { 303 status = HTTP_STATUS_ERROR; 304 break; 305 } 306 } 307 308 DEBUG_printf(("2cupsPutFd: starting attempt, authstring=\"%s\"...", 309 http->authstring)); 310 311 httpClearFields(http); 312 httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); 313 httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked"); 314 httpSetExpect(http, HTTP_STATUS_CONTINUE); 315 316 if (httpPut(http, resource)) 317 { 318 if (httpReconnect2(http, 30000, NULL)) 319 { 320 status = HTTP_STATUS_ERROR; 321 break; 322 } 323 else 324 { 325 status = HTTP_STATUS_UNAUTHORIZED; 326 continue; 327 } 328 } 329 330 /* 331 * Wait up to 1 second for a 100-continue response... 332 */ 333 334 if (httpWait(http, 1000)) 335 status = httpUpdate(http); 336 else 337 status = HTTP_STATUS_CONTINUE; 338 339 if (status == HTTP_STATUS_CONTINUE) 340 { 341 /* 342 * Copy the file... 343 */ 344 345 lseek(fd, 0, SEEK_SET); 346 347 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) 348 if (httpCheck(http)) 349 { 350 if ((status = httpUpdate(http)) != HTTP_STATUS_CONTINUE) 351 break; 352 } 353 else 354 httpWrite2(http, buffer, (size_t)bytes); 355 } 356 357 if (status == HTTP_STATUS_CONTINUE) 358 { 359 httpWrite2(http, buffer, 0); 360 361 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE); 362 } 363 364 if (status == HTTP_STATUS_ERROR && !retries) 365 { 366 DEBUG_printf(("2cupsPutFd: retry on status %d", status)); 367 368 retries ++; 369 370 /* Flush any error message... */ 371 httpFlush(http); 372 373 /* Reconnect... */ 374 if (httpReconnect2(http, 30000, NULL)) 375 { 376 status = HTTP_STATUS_ERROR; 377 break; 378 } 379 380 /* Try again... */ 381 continue; 382 } 383 384 DEBUG_printf(("2cupsPutFd: status=%d", status)); 385 386 if (status == HTTP_STATUS_UNAUTHORIZED) 387 { 388 /* 389 * Flush any error message... 390 */ 391 392 httpFlush(http); 393 394 /* 395 * See if we can do authentication... 396 */ 397 398 if (cupsDoAuthentication(http, "PUT", resource)) 399 { 400 status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; 401 break; 402 } 403 404 if (httpReconnect2(http, 30000, NULL)) 405 { 406 status = HTTP_STATUS_ERROR; 407 break; 408 } 409 410 continue; 411 } 412 #ifdef HAVE_SSL 413 else if (status == HTTP_STATUS_UPGRADE_REQUIRED) 414 { 415 /* Flush any error message... */ 416 httpFlush(http); 417 418 /* Reconnect... */ 419 if (httpReconnect2(http, 30000, NULL)) 420 { 421 status = HTTP_STATUS_ERROR; 422 break; 423 } 424 425 /* Upgrade with encryption... */ 426 httpEncryption(http, HTTP_ENCRYPTION_REQUIRED); 427 428 /* Try again, this time with encryption enabled... */ 429 continue; 430 } 431 #endif /* HAVE_SSL */ 432 } 433 while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED || 434 (status == HTTP_STATUS_ERROR && retries < 2)); 435 436 /* 437 * See if we actually put the file or an error... 438 */ 439 440 if (status != HTTP_STATUS_CREATED) 441 { 442 _cupsSetHTTPError(status); 443 httpFlush(http); 444 } 445 446 DEBUG_printf(("1cupsPutFd: Returning %d...", status)); 447 448 return (status); 449 } 450 451 452 /* 453 * 'cupsPutFile()' - Put a file on the server. 454 * 455 * This function returns @code HTTP_CREATED@ when the file is stored 456 * successfully. 457 * 458 * @since CUPS 1.1.20/macOS 10.4@ 459 */ 460 461 http_status_t /* O - HTTP status */ 462 cupsPutFile(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 463 const char *resource, /* I - Resource name */ 464 const char *filename) /* I - Filename */ 465 { 466 int fd; /* File descriptor */ 467 http_status_t status; /* Status */ 468 469 470 /* 471 * Range check input... 472 */ 473 474 if (!http || !resource || !filename) 475 { 476 if (http) 477 http->error = EINVAL; 478 479 return (HTTP_STATUS_ERROR); 480 } 481 482 /* 483 * Open the local file... 484 */ 485 486 if ((fd = open(filename, O_RDONLY)) < 0) 487 { 488 /* 489 * Couldn't open the file! 490 */ 491 492 http->error = errno; 493 494 return (HTTP_STATUS_ERROR); 495 } 496 497 /* 498 * Put the file... 499 */ 500 501 status = cupsPutFd(http, resource, fd); 502 503 close(fd); 504 505 return (status); 506 } 507