1/* 2 * File functions for CUPS. 3 * 4 * Since stdio files max out at 256 files on many systems, we have to 5 * write similar functions without this limit. At the same time, using 6 * our own file functions allows us to provide transparent support of 7 * gzip'd print files, PPD files, etc. 8 * 9 * Copyright 2007-2015 by Apple Inc. 10 * Copyright 1997-2007 by Easy Software Products, all rights reserved. 11 * 12 * These coded instructions, statements, and computer programs are the 13 * property of Apple Inc. and are protected by Federal copyright 14 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 15 * which should have been included with this file. If this file is 16 * missing or damaged, see the license at "http://www.cups.org/". 17 * 18 * This file is subject to the Apple OS-Developed Software exception. 19 */ 20 21/* 22 * Include necessary headers... 23 */ 24 25#include "file-private.h" 26#include <sys/stat.h> 27#include <sys/types.h> 28 29 30/* 31 * Local functions... 32 */ 33 34#ifdef HAVE_LIBZ 35static ssize_t cups_compress(cups_file_t *fp, const char *buf, size_t bytes); 36#endif /* HAVE_LIBZ */ 37static ssize_t cups_fill(cups_file_t *fp); 38static int cups_open(const char *filename, int mode); 39static ssize_t cups_read(cups_file_t *fp, char *buf, size_t bytes); 40static ssize_t cups_write(cups_file_t *fp, const char *buf, size_t bytes); 41 42 43#ifndef WIN32 44/* 45 * '_cupsFileCheck()' - Check the permissions of the given filename. 46 */ 47 48_cups_fc_result_t /* O - Check result */ 49_cupsFileCheck( 50 const char *filename, /* I - Filename to check */ 51 _cups_fc_filetype_t filetype, /* I - Type of file checks? */ 52 int dorootchecks, /* I - Check for root permissions? */ 53 _cups_fc_func_t cb, /* I - Callback function */ 54 void *context) /* I - Context pointer for callback */ 55 56{ 57 struct stat fileinfo; /* File information */ 58 char message[1024], /* Message string */ 59 temp[1024], /* Parent directory filename */ 60 *ptr; /* Pointer into parent directory */ 61 _cups_fc_result_t result; /* Check result */ 62 63 64 /* 65 * Does the filename contain a relative path ("../")? 66 */ 67 68 if (strstr(filename, "../")) 69 { 70 /* 71 * Yes, fail it! 72 */ 73 74 result = _CUPS_FILE_CHECK_RELATIVE_PATH; 75 goto finishup; 76 } 77 78 /* 79 * Does the program even exist and is it accessible? 80 */ 81 82 if (stat(filename, &fileinfo)) 83 { 84 /* 85 * Nope... 86 */ 87 88 result = _CUPS_FILE_CHECK_MISSING; 89 goto finishup; 90 } 91 92 /* 93 * Check the execute bit... 94 */ 95 96 result = _CUPS_FILE_CHECK_OK; 97 98 switch (filetype) 99 { 100 case _CUPS_FILE_CHECK_DIRECTORY : 101 if (!S_ISDIR(fileinfo.st_mode)) 102 result = _CUPS_FILE_CHECK_WRONG_TYPE; 103 break; 104 105 default : 106 if (!S_ISREG(fileinfo.st_mode)) 107 result = _CUPS_FILE_CHECK_WRONG_TYPE; 108 break; 109 } 110 111 if (result) 112 goto finishup; 113 114 /* 115 * Are we doing root checks? 116 */ 117 118 if (!dorootchecks) 119 { 120 /* 121 * Nope, so anything (else) goes... 122 */ 123 124 goto finishup; 125 } 126 127 /* 128 * Verify permission of the file itself: 129 * 130 * 1. Must be owned by root 131 * 2. Must not be writable by group 132 * 3. Must not be setuid 133 * 4. Must not be writable by others 134 */ 135 136 if (fileinfo.st_uid || /* 1. Must be owned by root */ 137 (fileinfo.st_mode & S_IWGRP) || /* 2. Must not be writable by group */ 138 (fileinfo.st_mode & S_ISUID) || /* 3. Must not be setuid */ 139 (fileinfo.st_mode & S_IWOTH)) /* 4. Must not be writable by others */ 140 { 141 result = _CUPS_FILE_CHECK_PERMISSIONS; 142 goto finishup; 143 } 144 145 if (filetype == _CUPS_FILE_CHECK_DIRECTORY || 146 filetype == _CUPS_FILE_CHECK_FILE_ONLY) 147 goto finishup; 148 149 /* 150 * Now check the containing directory... 151 */ 152 153 strlcpy(temp, filename, sizeof(temp)); 154 if ((ptr = strrchr(temp, '/')) != NULL) 155 { 156 if (ptr == temp) 157 ptr[1] = '\0'; 158 else 159 *ptr = '\0'; 160 } 161 162 if (stat(temp, &fileinfo)) 163 { 164 /* 165 * Doesn't exist?!? 166 */ 167 168 result = _CUPS_FILE_CHECK_MISSING; 169 filetype = _CUPS_FILE_CHECK_DIRECTORY; 170 filename = temp; 171 172 goto finishup; 173 } 174 175 if (fileinfo.st_uid || /* 1. Must be owned by root */ 176 (fileinfo.st_mode & S_IWGRP) || /* 2. Must not be writable by group */ 177 (fileinfo.st_mode & S_ISUID) || /* 3. Must not be setuid */ 178 (fileinfo.st_mode & S_IWOTH)) /* 4. Must not be writable by others */ 179 { 180 result = _CUPS_FILE_CHECK_PERMISSIONS; 181 filetype = _CUPS_FILE_CHECK_DIRECTORY; 182 filename = temp; 183 } 184 185 /* 186 * Common return point... 187 */ 188 189 finishup: 190 191 if (cb) 192 { 193 cups_lang_t *lang = cupsLangDefault(); 194 /* Localization information */ 195 196 switch (result) 197 { 198 case _CUPS_FILE_CHECK_OK : 199 if (filetype == _CUPS_FILE_CHECK_DIRECTORY) 200 snprintf(message, sizeof(message), 201 _cupsLangString(lang, _("Directory \"%s\" permissions OK " 202 "(0%o/uid=%d/gid=%d).")), 203 filename, fileinfo.st_mode, (int)fileinfo.st_uid, 204 (int)fileinfo.st_gid); 205 else 206 snprintf(message, sizeof(message), 207 _cupsLangString(lang, _("File \"%s\" permissions OK " 208 "(0%o/uid=%d/gid=%d).")), 209 filename, fileinfo.st_mode, (int)fileinfo.st_uid, 210 (int)fileinfo.st_gid); 211 break; 212 213 case _CUPS_FILE_CHECK_MISSING : 214 if (filetype == _CUPS_FILE_CHECK_DIRECTORY) 215 snprintf(message, sizeof(message), 216 _cupsLangString(lang, _("Directory \"%s\" not available: " 217 "%s")), 218 filename, strerror(errno)); 219 else 220 snprintf(message, sizeof(message), 221 _cupsLangString(lang, _("File \"%s\" not available: %s")), 222 filename, strerror(errno)); 223 break; 224 225 case _CUPS_FILE_CHECK_PERMISSIONS : 226 if (filetype == _CUPS_FILE_CHECK_DIRECTORY) 227 snprintf(message, sizeof(message), 228 _cupsLangString(lang, _("Directory \"%s\" has insecure " 229 "permissions " 230 "(0%o/uid=%d/gid=%d).")), 231 filename, fileinfo.st_mode, (int)fileinfo.st_uid, 232 (int)fileinfo.st_gid); 233 else 234 snprintf(message, sizeof(message), 235 _cupsLangString(lang, _("File \"%s\" has insecure " 236 "permissions " 237 "(0%o/uid=%d/gid=%d).")), 238 filename, fileinfo.st_mode, (int)fileinfo.st_uid, 239 (int)fileinfo.st_gid); 240 break; 241 242 case _CUPS_FILE_CHECK_WRONG_TYPE : 243 if (filetype == _CUPS_FILE_CHECK_DIRECTORY) 244 snprintf(message, sizeof(message), 245 _cupsLangString(lang, _("Directory \"%s\" is a file.")), 246 filename); 247 else 248 snprintf(message, sizeof(message), 249 _cupsLangString(lang, _("File \"%s\" is a directory.")), 250 filename); 251 break; 252 253 case _CUPS_FILE_CHECK_RELATIVE_PATH : 254 if (filetype == _CUPS_FILE_CHECK_DIRECTORY) 255 snprintf(message, sizeof(message), 256 _cupsLangString(lang, _("Directory \"%s\" contains a " 257 "relative path.")), filename); 258 else 259 snprintf(message, sizeof(message), 260 _cupsLangString(lang, _("File \"%s\" contains a relative " 261 "path.")), filename); 262 break; 263 } 264 265 (*cb)(context, result, message); 266 } 267 268 return (result); 269} 270 271 272/* 273 * '_cupsFileCheckFilter()' - Report file check results as CUPS filter messages. 274 */ 275 276void 277_cupsFileCheckFilter( 278 void *context, /* I - Context pointer (unused) */ 279 _cups_fc_result_t result, /* I - Result code */ 280 const char *message) /* I - Message text */ 281{ 282 const char *prefix; /* Messaging prefix */ 283 284 285 (void)context; 286 287 switch (result) 288 { 289 default : 290 case _CUPS_FILE_CHECK_OK : 291 prefix = "DEBUG2"; 292 break; 293 294 case _CUPS_FILE_CHECK_MISSING : 295 case _CUPS_FILE_CHECK_WRONG_TYPE : 296 prefix = "ERROR"; 297 fputs("STATE: +cups-missing-filter-warning\n", stderr); 298 break; 299 300 case _CUPS_FILE_CHECK_PERMISSIONS : 301 case _CUPS_FILE_CHECK_RELATIVE_PATH : 302 prefix = "ERROR"; 303 fputs("STATE: +cups-insecure-filter-warning\n", stderr); 304 break; 305 } 306 307 fprintf(stderr, "%s: %s\n", prefix, message); 308} 309#endif /* !WIN32 */ 310 311 312/* 313 * 'cupsFileClose()' - Close a CUPS file. 314 * 315 * @since CUPS 1.2/macOS 10.5@ 316 */ 317 318int /* O - 0 on success, -1 on error */ 319cupsFileClose(cups_file_t *fp) /* I - CUPS file */ 320{ 321 int fd; /* File descriptor */ 322 char mode; /* Open mode */ 323 int status; /* Return status */ 324 int is_stdio; /* Is a stdio file? */ 325 326 327 DEBUG_printf(("cupsFileClose(fp=%p)", (void *)fp)); 328 329 /* 330 * Range check... 331 */ 332 333 if (!fp) 334 return (-1); 335 336 /* 337 * Flush pending write data... 338 */ 339 340 if (fp->mode == 'w') 341 status = cupsFileFlush(fp); 342 else 343 status = 0; 344 345#ifdef HAVE_LIBZ 346 if (fp->compressed && status >= 0) 347 { 348 if (fp->mode == 'r') 349 { 350 /* 351 * Free decompression data... 352 */ 353 354 inflateEnd(&fp->stream); 355 } 356 else 357 { 358 /* 359 * Flush any remaining compressed data... 360 */ 361 362 unsigned char trailer[8]; /* Trailer CRC and length */ 363 int done; /* Done writing... */ 364 365 366 fp->stream.avail_in = 0; 367 368 for (done = 0;;) 369 { 370 if (fp->stream.next_out > fp->cbuf) 371 { 372 if (cups_write(fp, (char *)fp->cbuf, 373 (size_t)(fp->stream.next_out - fp->cbuf)) < 0) 374 status = -1; 375 376 fp->stream.next_out = fp->cbuf; 377 fp->stream.avail_out = sizeof(fp->cbuf); 378 } 379 380 if (done || status < 0) 381 break; 382 383 done = deflate(&fp->stream, Z_FINISH) == Z_STREAM_END && 384 fp->stream.next_out == fp->cbuf; 385 } 386 387 /* 388 * Write the CRC and length... 389 */ 390 391 trailer[0] = (unsigned char)fp->crc; 392 trailer[1] = (unsigned char)(fp->crc >> 8); 393 trailer[2] = (unsigned char)(fp->crc >> 16); 394 trailer[3] = (unsigned char)(fp->crc >> 24); 395 trailer[4] = (unsigned char)fp->pos; 396 trailer[5] = (unsigned char)(fp->pos >> 8); 397 trailer[6] = (unsigned char)(fp->pos >> 16); 398 trailer[7] = (unsigned char)(fp->pos >> 24); 399 400 if (cups_write(fp, (char *)trailer, 8) < 0) 401 status = -1; 402 403 /* 404 * Free all memory used by the compression stream... 405 */ 406 407 deflateEnd(&(fp->stream)); 408 } 409 } 410#endif /* HAVE_LIBZ */ 411 412 /* 413 * Save the file descriptor we used and free memory... 414 */ 415 416 fd = fp->fd; 417 mode = fp->mode; 418 is_stdio = fp->is_stdio; 419 420 if (fp->printf_buffer) 421 free(fp->printf_buffer); 422 423 free(fp); 424 425 /* 426 * Close the file, returning the close status... 427 */ 428 429 if (mode == 's') 430 { 431 if (httpAddrClose(NULL, fd) < 0) 432 status = -1; 433 } 434 else if (!is_stdio) 435 { 436 if (close(fd) < 0) 437 status = -1; 438 } 439 440 return (status); 441} 442 443 444/* 445 * 'cupsFileCompression()' - Return whether a file is compressed. 446 * 447 * @since CUPS 1.2/macOS 10.5@ 448 */ 449 450int /* O - @code CUPS_FILE_NONE@ or @code CUPS_FILE_GZIP@ */ 451cupsFileCompression(cups_file_t *fp) /* I - CUPS file */ 452{ 453 return (fp ? fp->compressed : CUPS_FILE_NONE); 454} 455 456 457/* 458 * 'cupsFileEOF()' - Return the end-of-file status. 459 * 460 * @since CUPS 1.2/macOS 10.5@ 461 */ 462 463int /* O - 1 on end of file, 0 otherwise */ 464cupsFileEOF(cups_file_t *fp) /* I - CUPS file */ 465{ 466 return (fp ? fp->eof : 1); 467} 468 469 470/* 471 * 'cupsFileFind()' - Find a file using the specified path. 472 * 473 * This function allows the paths in the path string to be separated by 474 * colons (UNIX standard) or semicolons (Windows standard) and stores the 475 * result in the buffer supplied. If the file cannot be found in any of 476 * the supplied paths, @code NULL@ is returned. A @code NULL@ path only 477 * matches the current directory. 478 * 479 * @since CUPS 1.2/macOS 10.5@ 480 */ 481 482const char * /* O - Full path to file or @code NULL@ if not found */ 483cupsFileFind(const char *filename, /* I - File to find */ 484 const char *path, /* I - Colon/semicolon-separated path */ 485 int executable, /* I - 1 = executable files, 0 = any file/dir */ 486 char *buffer, /* I - Filename buffer */ 487 int bufsize) /* I - Size of filename buffer */ 488{ 489 char *bufptr, /* Current position in buffer */ 490 *bufend; /* End of buffer */ 491 492 493 /* 494 * Range check input... 495 */ 496 497 DEBUG_printf(("cupsFileFind(filename=\"%s\", path=\"%s\", executable=%d, buffer=%p, bufsize=%d)", filename, path, executable, (void *)buffer, bufsize)); 498 499 if (!filename || !buffer || bufsize < 2) 500 return (NULL); 501 502 if (!path) 503 { 504 /* 505 * No path, so check current directory... 506 */ 507 508 if (!access(filename, 0)) 509 { 510 strlcpy(buffer, filename, (size_t)bufsize); 511 return (buffer); 512 } 513 else 514 return (NULL); 515 } 516 517 /* 518 * Now check each path and return the first match... 519 */ 520 521 bufend = buffer + bufsize - 1; 522 bufptr = buffer; 523 524 while (*path) 525 { 526#ifdef WIN32 527 if (*path == ';' || (*path == ':' && ((bufptr - buffer) > 1 || !isalpha(buffer[0] & 255)))) 528#else 529 if (*path == ';' || *path == ':') 530#endif /* WIN32 */ 531 { 532 if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend) 533 *bufptr++ = '/'; 534 535 strlcpy(bufptr, filename, (size_t)(bufend - bufptr)); 536 537#ifdef WIN32 538 if (!access(buffer, 0)) 539#else 540 if (!access(buffer, executable ? X_OK : 0)) 541#endif /* WIN32 */ 542 { 543 DEBUG_printf(("1cupsFileFind: Returning \"%s\"", buffer)); 544 return (buffer); 545 } 546 547 bufptr = buffer; 548 } 549 else if (bufptr < bufend) 550 *bufptr++ = *path; 551 552 path ++; 553 } 554 555 /* 556 * Check the last path... 557 */ 558 559 if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend) 560 *bufptr++ = '/'; 561 562 strlcpy(bufptr, filename, (size_t)(bufend - bufptr)); 563 564 if (!access(buffer, 0)) 565 { 566 DEBUG_printf(("1cupsFileFind: Returning \"%s\"", buffer)); 567 return (buffer); 568 } 569 else 570 { 571 DEBUG_puts("1cupsFileFind: Returning NULL"); 572 return (NULL); 573 } 574} 575 576 577/* 578 * 'cupsFileFlush()' - Flush pending output. 579 * 580 * @since CUPS 1.2/macOS 10.5@ 581 */ 582 583int /* O - 0 on success, -1 on error */ 584cupsFileFlush(cups_file_t *fp) /* I - CUPS file */ 585{ 586 ssize_t bytes; /* Bytes to write */ 587 588 589 DEBUG_printf(("cupsFileFlush(fp=%p)", (void *)fp)); 590 591 /* 592 * Range check input... 593 */ 594 595 if (!fp || fp->mode != 'w') 596 { 597 DEBUG_puts("1cupsFileFlush: Attempt to flush a read-only file..."); 598 return (-1); 599 } 600 601 bytes = (ssize_t)(fp->ptr - fp->buf); 602 603 DEBUG_printf(("2cupsFileFlush: Flushing " CUPS_LLFMT " bytes...", 604 CUPS_LLCAST bytes)); 605 606 if (bytes > 0) 607 { 608#ifdef HAVE_LIBZ 609 if (fp->compressed) 610 bytes = cups_compress(fp, fp->buf, (size_t)bytes); 611 else 612#endif /* HAVE_LIBZ */ 613 bytes = cups_write(fp, fp->buf, (size_t)bytes); 614 615 if (bytes < 0) 616 return (-1); 617 618 fp->ptr = fp->buf; 619 } 620 621 return (0); 622} 623 624 625/* 626 * 'cupsFileGetChar()' - Get a single character from a file. 627 * 628 * @since CUPS 1.2/macOS 10.5@ 629 */ 630 631int /* O - Character or -1 on end of file */ 632cupsFileGetChar(cups_file_t *fp) /* I - CUPS file */ 633{ 634 /* 635 * Range check input... 636 */ 637 638 DEBUG_printf(("4cupsFileGetChar(fp=%p)", (void *)fp)); 639 640 if (!fp || (fp->mode != 'r' && fp->mode != 's')) 641 { 642 DEBUG_puts("5cupsFileGetChar: Bad arguments!"); 643 return (-1); 644 } 645 646 /* 647 * If the input buffer is empty, try to read more data... 648 */ 649 650 DEBUG_printf(("5cupsFileGetChar: fp->eof=%d, fp->ptr=%p, fp->end=%p", fp->eof, (void *)fp->ptr, (void *)fp->end)); 651 652 if (fp->ptr >= fp->end) 653 if (cups_fill(fp) <= 0) 654 { 655 DEBUG_puts("5cupsFileGetChar: Unable to fill buffer!"); 656 return (-1); 657 } 658 659 /* 660 * Return the next character in the buffer... 661 */ 662 663 DEBUG_printf(("5cupsFileGetChar: Returning %d...", *(fp->ptr) & 255)); 664 665 fp->pos ++; 666 667 DEBUG_printf(("6cupsFileGetChar: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 668 669 return (*(fp->ptr)++ & 255); 670} 671 672 673/* 674 * 'cupsFileGetConf()' - Get a line from a configuration file. 675 * 676 * @since CUPS 1.2/macOS 10.5@ 677 */ 678 679char * /* O - Line read or @code NULL@ on end of file or error */ 680cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ 681 char *buf, /* O - String buffer */ 682 size_t buflen, /* I - Size of string buffer */ 683 char **value, /* O - Pointer to value */ 684 int *linenum) /* IO - Current line number */ 685{ 686 char *ptr; /* Pointer into line */ 687 688 689 /* 690 * Range check input... 691 */ 692 693 DEBUG_printf(("2cupsFileGetConf(fp=%p, buf=%p, buflen=" CUPS_LLFMT 694 ", value=%p, linenum=%p)", (void *)fp, (void *)buf, CUPS_LLCAST buflen, (void *)value, (void *)linenum)); 695 696 if (!fp || (fp->mode != 'r' && fp->mode != 's') || 697 !buf || buflen < 2 || !value) 698 { 699 if (value) 700 *value = NULL; 701 702 return (NULL); 703 } 704 705 /* 706 * Read the next non-comment line... 707 */ 708 709 *value = NULL; 710 711 while (cupsFileGets(fp, buf, buflen)) 712 { 713 (*linenum) ++; 714 715 /* 716 * Strip any comments... 717 */ 718 719 if ((ptr = strchr(buf, '#')) != NULL) 720 { 721 if (ptr > buf && ptr[-1] == '\\') 722 { 723 // Unquote the #... 724 _cups_strcpy(ptr - 1, ptr); 725 } 726 else 727 { 728 // Strip the comment and any trailing whitespace... 729 while (ptr > buf) 730 { 731 if (!_cups_isspace(ptr[-1])) 732 break; 733 734 ptr --; 735 } 736 737 *ptr = '\0'; 738 } 739 } 740 741 /* 742 * Strip leading whitespace... 743 */ 744 745 for (ptr = buf; _cups_isspace(*ptr); ptr ++); 746 747 if (ptr > buf) 748 _cups_strcpy(buf, ptr); 749 750 /* 751 * See if there is anything left... 752 */ 753 754 if (buf[0]) 755 { 756 /* 757 * Yes, grab any value and return... 758 */ 759 760 for (ptr = buf; *ptr; ptr ++) 761 if (_cups_isspace(*ptr)) 762 break; 763 764 if (*ptr) 765 { 766 /* 767 * Have a value, skip any other spaces... 768 */ 769 770 while (_cups_isspace(*ptr)) 771 *ptr++ = '\0'; 772 773 if (*ptr) 774 *value = ptr; 775 776 /* 777 * Strip trailing whitespace and > for lines that begin with <... 778 */ 779 780 ptr += strlen(ptr) - 1; 781 782 if (buf[0] == '<' && *ptr == '>') 783 *ptr-- = '\0'; 784 else if (buf[0] == '<' && *ptr != '>') 785 { 786 /* 787 * Syntax error... 788 */ 789 790 *value = NULL; 791 return (buf); 792 } 793 794 while (ptr > *value && _cups_isspace(*ptr)) 795 *ptr-- = '\0'; 796 } 797 798 /* 799 * Return the line... 800 */ 801 802 return (buf); 803 } 804 } 805 806 return (NULL); 807} 808 809 810/* 811 * 'cupsFileGetLine()' - Get a CR and/or LF-terminated line that may 812 * contain binary data. 813 * 814 * This function differs from @link cupsFileGets@ in that the trailing CR 815 * and LF are preserved, as is any binary data on the line. The buffer is 816 * nul-terminated, however you should use the returned length to determine 817 * the number of bytes on the line. 818 * 819 * @since CUPS 1.2/macOS 10.5@ 820 */ 821 822size_t /* O - Number of bytes on line or 0 on end of file */ 823cupsFileGetLine(cups_file_t *fp, /* I - File to read from */ 824 char *buf, /* I - Buffer */ 825 size_t buflen) /* I - Size of buffer */ 826{ 827 int ch; /* Character from file */ 828 char *ptr, /* Current position in line buffer */ 829 *end; /* End of line buffer */ 830 831 832 /* 833 * Range check input... 834 */ 835 836 DEBUG_printf(("2cupsFileGetLine(fp=%p, buf=%p, buflen=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST buflen)); 837 838 if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 3) 839 return (0); 840 841 /* 842 * Now loop until we have a valid line... 843 */ 844 845 for (ptr = buf, end = buf + buflen - 2; ptr < end ;) 846 { 847 if (fp->ptr >= fp->end) 848 if (cups_fill(fp) <= 0) 849 break; 850 851 *ptr++ = ch = *(fp->ptr)++; 852 fp->pos ++; 853 854 if (ch == '\r') 855 { 856 /* 857 * Check for CR LF... 858 */ 859 860 if (fp->ptr >= fp->end) 861 if (cups_fill(fp) <= 0) 862 break; 863 864 if (*(fp->ptr) == '\n') 865 { 866 *ptr++ = *(fp->ptr)++; 867 fp->pos ++; 868 } 869 870 break; 871 } 872 else if (ch == '\n') 873 { 874 /* 875 * Line feed ends a line... 876 */ 877 878 break; 879 } 880 } 881 882 *ptr = '\0'; 883 884 DEBUG_printf(("4cupsFileGetLine: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 885 886 return ((size_t)(ptr - buf)); 887} 888 889 890/* 891 * 'cupsFileGets()' - Get a CR and/or LF-terminated line. 892 * 893 * @since CUPS 1.2/macOS 10.5@ 894 */ 895 896char * /* O - Line read or @code NULL@ on end of file or error */ 897cupsFileGets(cups_file_t *fp, /* I - CUPS file */ 898 char *buf, /* O - String buffer */ 899 size_t buflen) /* I - Size of string buffer */ 900{ 901 int ch; /* Character from file */ 902 char *ptr, /* Current position in line buffer */ 903 *end; /* End of line buffer */ 904 905 906 /* 907 * Range check input... 908 */ 909 910 DEBUG_printf(("2cupsFileGets(fp=%p, buf=%p, buflen=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST buflen)); 911 912 if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 2) 913 return (NULL); 914 915 /* 916 * Now loop until we have a valid line... 917 */ 918 919 for (ptr = buf, end = buf + buflen - 1; ptr < end ;) 920 { 921 if (fp->ptr >= fp->end) 922 if (cups_fill(fp) <= 0) 923 { 924 if (ptr == buf) 925 return (NULL); 926 else 927 break; 928 } 929 930 ch = *(fp->ptr)++; 931 fp->pos ++; 932 933 if (ch == '\r') 934 { 935 /* 936 * Check for CR LF... 937 */ 938 939 if (fp->ptr >= fp->end) 940 if (cups_fill(fp) <= 0) 941 break; 942 943 if (*(fp->ptr) == '\n') 944 { 945 fp->ptr ++; 946 fp->pos ++; 947 } 948 949 break; 950 } 951 else if (ch == '\n') 952 { 953 /* 954 * Line feed ends a line... 955 */ 956 957 break; 958 } 959 else 960 *ptr++ = (char)ch; 961 } 962 963 *ptr = '\0'; 964 965 DEBUG_printf(("4cupsFileGets: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 966 967 return (buf); 968} 969 970 971/* 972 * 'cupsFileLock()' - Temporarily lock access to a file. 973 * 974 * @since CUPS 1.2/macOS 10.5@ 975 */ 976 977int /* O - 0 on success, -1 on error */ 978cupsFileLock(cups_file_t *fp, /* I - CUPS file */ 979 int block) /* I - 1 to wait for the lock, 0 to fail right away */ 980{ 981 /* 982 * Range check... 983 */ 984 985 if (!fp || fp->mode == 's') 986 return (-1); 987 988 /* 989 * Try the lock... 990 */ 991 992#ifdef WIN32 993 return (_locking(fp->fd, block ? _LK_LOCK : _LK_NBLCK, 0)); 994#else 995 return (lockf(fp->fd, block ? F_LOCK : F_TLOCK, 0)); 996#endif /* WIN32 */ 997} 998 999 1000/* 1001 * 'cupsFileNumber()' - Return the file descriptor associated with a CUPS file. 1002 * 1003 * @since CUPS 1.2/macOS 10.5@ 1004 */ 1005 1006int /* O - File descriptor */ 1007cupsFileNumber(cups_file_t *fp) /* I - CUPS file */ 1008{ 1009 if (fp) 1010 return (fp->fd); 1011 else 1012 return (-1); 1013} 1014 1015 1016/* 1017 * 'cupsFileOpen()' - Open a CUPS file. 1018 * 1019 * The "mode" parameter can be "r" to read, "w" to write, overwriting any 1020 * existing file, "a" to append to an existing file or create a new file, 1021 * or "s" to open a socket connection. 1022 * 1023 * When opening for writing ("w"), an optional number from 1 to 9 can be 1024 * supplied which enables Flate compression of the file. Compression is 1025 * not supported for the "a" (append) mode. 1026 * 1027 * When opening a socket connection, the filename is a string of the form 1028 * "address:port" or "hostname:port". The socket will make an IPv4 or IPv6 1029 * connection as needed, generally preferring IPv6 connections when there is 1030 * a choice. 1031 * 1032 * @since CUPS 1.2/macOS 10.5@ 1033 */ 1034 1035cups_file_t * /* O - CUPS file or @code NULL@ if the file or socket cannot be opened */ 1036cupsFileOpen(const char *filename, /* I - Name of file */ 1037 const char *mode) /* I - Open mode */ 1038{ 1039 cups_file_t *fp; /* New CUPS file */ 1040 int fd; /* File descriptor */ 1041 char hostname[1024], /* Hostname */ 1042 *portname; /* Port "name" (number or service) */ 1043 http_addrlist_t *addrlist; /* Host address list */ 1044 1045 1046 DEBUG_printf(("cupsFileOpen(filename=\"%s\", mode=\"%s\")", filename, 1047 mode)); 1048 1049 /* 1050 * Range check input... 1051 */ 1052 1053 if (!filename || !mode || 1054 (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') || 1055 (*mode == 'a' && isdigit(mode[1] & 255))) 1056 return (NULL); 1057 1058 /* 1059 * Open the file... 1060 */ 1061 1062 switch (*mode) 1063 { 1064 case 'a' : /* Append file */ 1065 fd = cups_open(filename, 1066 O_RDWR | O_CREAT | O_APPEND | O_LARGEFILE | O_BINARY); 1067 break; 1068 1069 case 'r' : /* Read file */ 1070 fd = open(filename, O_RDONLY | O_LARGEFILE | O_BINARY, 0); 1071 break; 1072 1073 case 'w' : /* Write file */ 1074 fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY); 1075 if (fd < 0 && errno == ENOENT) 1076 { 1077 fd = cups_open(filename, 1078 O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE | O_BINARY); 1079 if (fd < 0 && errno == EEXIST) 1080 fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY); 1081 } 1082 1083 if (fd >= 0) 1084#ifdef WIN32 1085 _chsize(fd, 0); 1086#else 1087 ftruncate(fd, 0); 1088#endif /* WIN32 */ 1089 break; 1090 1091 case 's' : /* Read/write socket */ 1092 strlcpy(hostname, filename, sizeof(hostname)); 1093 if ((portname = strrchr(hostname, ':')) != NULL) 1094 *portname++ = '\0'; 1095 else 1096 return (NULL); 1097 1098 /* 1099 * Lookup the hostname and service... 1100 */ 1101 1102 if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL) 1103 return (NULL); 1104 1105 /* 1106 * Connect to the server... 1107 */ 1108 1109 if (!httpAddrConnect(addrlist, &fd)) 1110 { 1111 httpAddrFreeList(addrlist); 1112 return (NULL); 1113 } 1114 1115 httpAddrFreeList(addrlist); 1116 break; 1117 1118 default : /* Remove bogus compiler warning... */ 1119 return (NULL); 1120 } 1121 1122 if (fd < 0) 1123 return (NULL); 1124 1125 /* 1126 * Create the CUPS file structure... 1127 */ 1128 1129 if ((fp = cupsFileOpenFd(fd, mode)) == NULL) 1130 { 1131 if (*mode == 's') 1132 httpAddrClose(NULL, fd); 1133 else 1134 close(fd); 1135 } 1136 1137 /* 1138 * Return it... 1139 */ 1140 1141 return (fp); 1142} 1143 1144/* 1145 * 'cupsFileOpenFd()' - Open a CUPS file using a file descriptor. 1146 * 1147 * The "mode" parameter can be "r" to read, "w" to write, "a" to append, 1148 * or "s" to treat the file descriptor as a bidirectional socket connection. 1149 * 1150 * When opening for writing ("w"), an optional number from 1 to 9 can be 1151 * supplied which enables Flate compression of the file. Compression is 1152 * not supported for the "a" (append) mode. 1153 * 1154 * @since CUPS 1.2/macOS 10.5@ 1155 */ 1156 1157cups_file_t * /* O - CUPS file or @code NULL@ if the file could not be opened */ 1158cupsFileOpenFd(int fd, /* I - File descriptor */ 1159 const char *mode) /* I - Open mode */ 1160{ 1161 cups_file_t *fp; /* New CUPS file */ 1162 1163 1164 DEBUG_printf(("cupsFileOpenFd(fd=%d, mode=\"%s\")", fd, mode)); 1165 1166 /* 1167 * Range check input... 1168 */ 1169 1170 if (fd < 0 || !mode || 1171 (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') || 1172 (*mode == 'a' && isdigit(mode[1] & 255))) 1173 return (NULL); 1174 1175 /* 1176 * Allocate memory... 1177 */ 1178 1179 if ((fp = calloc(1, sizeof(cups_file_t))) == NULL) 1180 return (NULL); 1181 1182 /* 1183 * Open the file... 1184 */ 1185 1186 fp->fd = fd; 1187 1188 switch (*mode) 1189 { 1190 case 'a' : 1191 fp->pos = lseek(fd, 0, SEEK_END); 1192 1193 case 'w' : 1194 fp->mode = 'w'; 1195 fp->ptr = fp->buf; 1196 fp->end = fp->buf + sizeof(fp->buf); 1197 1198#ifdef HAVE_LIBZ 1199 if (mode[1] >= '1' && mode[1] <= '9') 1200 { 1201 /* 1202 * Open a compressed stream, so write the standard gzip file 1203 * header... 1204 */ 1205 1206 unsigned char header[10]; /* gzip file header */ 1207 time_t curtime; /* Current time */ 1208 1209 1210 curtime = time(NULL); 1211 header[0] = 0x1f; 1212 header[1] = 0x8b; 1213 header[2] = Z_DEFLATED; 1214 header[3] = 0; 1215 header[4] = (unsigned char)curtime; 1216 header[5] = (unsigned char)(curtime >> 8); 1217 header[6] = (unsigned char)(curtime >> 16); 1218 header[7] = (unsigned char)(curtime >> 24); 1219 header[8] = 0; 1220 header[9] = 0x03; 1221 1222 cups_write(fp, (char *)header, 10); 1223 1224 /* 1225 * Initialize the compressor... 1226 */ 1227 1228 deflateInit2(&(fp->stream), mode[1] - '0', Z_DEFLATED, -15, 8, 1229 Z_DEFAULT_STRATEGY); 1230 1231 fp->stream.next_out = fp->cbuf; 1232 fp->stream.avail_out = sizeof(fp->cbuf); 1233 fp->compressed = 1; 1234 fp->crc = crc32(0L, Z_NULL, 0); 1235 } 1236#endif /* HAVE_LIBZ */ 1237 break; 1238 1239 case 'r' : 1240 fp->mode = 'r'; 1241 break; 1242 1243 case 's' : 1244 fp->mode = 's'; 1245 break; 1246 1247 default : /* Remove bogus compiler warning... */ 1248 return (NULL); 1249 } 1250 1251 /* 1252 * Don't pass this file to child processes... 1253 */ 1254 1255#ifndef WIN32 1256 fcntl(fp->fd, F_SETFD, fcntl(fp->fd, F_GETFD) | FD_CLOEXEC); 1257#endif /* !WIN32 */ 1258 1259 return (fp); 1260} 1261 1262 1263/* 1264 * 'cupsFilePeekChar()' - Peek at the next character from a file. 1265 * 1266 * @since CUPS 1.2/macOS 10.5@ 1267 */ 1268 1269int /* O - Character or -1 on end of file */ 1270cupsFilePeekChar(cups_file_t *fp) /* I - CUPS file */ 1271{ 1272 /* 1273 * Range check input... 1274 */ 1275 1276 if (!fp || (fp->mode != 'r' && fp->mode != 's')) 1277 return (-1); 1278 1279 /* 1280 * If the input buffer is empty, try to read more data... 1281 */ 1282 1283 if (fp->ptr >= fp->end) 1284 if (cups_fill(fp) <= 0) 1285 return (-1); 1286 1287 /* 1288 * Return the next character in the buffer... 1289 */ 1290 1291 return (*(fp->ptr) & 255); 1292} 1293 1294 1295/* 1296 * 'cupsFilePrintf()' - Write a formatted string. 1297 * 1298 * @since CUPS 1.2/macOS 10.5@ 1299 */ 1300 1301int /* O - Number of bytes written or -1 on error */ 1302cupsFilePrintf(cups_file_t *fp, /* I - CUPS file */ 1303 const char *format, /* I - Printf-style format string */ 1304 ...) /* I - Additional args as necessary */ 1305{ 1306 va_list ap; /* Argument list */ 1307 ssize_t bytes; /* Formatted size */ 1308 1309 1310 DEBUG_printf(("2cupsFilePrintf(fp=%p, format=\"%s\", ...)", (void *)fp, format)); 1311 1312 if (!fp || !format || (fp->mode != 'w' && fp->mode != 's')) 1313 return (-1); 1314 1315 if (!fp->printf_buffer) 1316 { 1317 /* 1318 * Start with an 1k printf buffer... 1319 */ 1320 1321 if ((fp->printf_buffer = malloc(1024)) == NULL) 1322 return (-1); 1323 1324 fp->printf_size = 1024; 1325 } 1326 1327 va_start(ap, format); 1328 bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap); 1329 va_end(ap); 1330 1331 if (bytes >= (ssize_t)fp->printf_size) 1332 { 1333 /* 1334 * Expand the printf buffer... 1335 */ 1336 1337 char *temp; /* Temporary buffer pointer */ 1338 1339 1340 if (bytes > 65535) 1341 return (-1); 1342 1343 if ((temp = realloc(fp->printf_buffer, (size_t)(bytes + 1))) == NULL) 1344 return (-1); 1345 1346 fp->printf_buffer = temp; 1347 fp->printf_size = (size_t)(bytes + 1); 1348 1349 va_start(ap, format); 1350 bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap); 1351 va_end(ap); 1352 } 1353 1354 if (fp->mode == 's') 1355 { 1356 if (cups_write(fp, fp->printf_buffer, (size_t)bytes) < 0) 1357 return (-1); 1358 1359 fp->pos += bytes; 1360 1361 DEBUG_printf(("4cupsFilePrintf: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 1362 1363 return ((int)bytes); 1364 } 1365 1366 if ((fp->ptr + bytes) > fp->end) 1367 if (cupsFileFlush(fp)) 1368 return (-1); 1369 1370 fp->pos += bytes; 1371 1372 DEBUG_printf(("4cupsFilePrintf: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 1373 1374 if ((size_t)bytes > sizeof(fp->buf)) 1375 { 1376#ifdef HAVE_LIBZ 1377 if (fp->compressed) 1378 return ((int)cups_compress(fp, fp->printf_buffer, (size_t)bytes)); 1379 else 1380#endif /* HAVE_LIBZ */ 1381 return ((int)cups_write(fp, fp->printf_buffer, (size_t)bytes)); 1382 } 1383 else 1384 { 1385 memcpy(fp->ptr, fp->printf_buffer, (size_t)bytes); 1386 fp->ptr += bytes; 1387 return ((int)bytes); 1388 } 1389} 1390 1391 1392/* 1393 * 'cupsFilePutChar()' - Write a character. 1394 * 1395 * @since CUPS 1.2/macOS 10.5@ 1396 */ 1397 1398int /* O - 0 on success, -1 on error */ 1399cupsFilePutChar(cups_file_t *fp, /* I - CUPS file */ 1400 int c) /* I - Character to write */ 1401{ 1402 /* 1403 * Range check input... 1404 */ 1405 1406 if (!fp || (fp->mode != 'w' && fp->mode != 's')) 1407 return (-1); 1408 1409 if (fp->mode == 's') 1410 { 1411 /* 1412 * Send character immediately over socket... 1413 */ 1414 1415 char ch; /* Output character */ 1416 1417 1418 ch = (char)c; 1419 1420 if (send(fp->fd, &ch, 1, 0) < 1) 1421 return (-1); 1422 } 1423 else 1424 { 1425 /* 1426 * Buffer it up... 1427 */ 1428 1429 if (fp->ptr >= fp->end) 1430 if (cupsFileFlush(fp)) 1431 return (-1); 1432 1433 *(fp->ptr) ++ = (char)c; 1434 } 1435 1436 fp->pos ++; 1437 1438 DEBUG_printf(("4cupsFilePutChar: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 1439 1440 return (0); 1441} 1442 1443 1444/* 1445 * 'cupsFilePutConf()' - Write a configuration line. 1446 * 1447 * This function handles any comment escaping of the value. 1448 * 1449 * @since CUPS 1.4/macOS 10.6@ 1450 */ 1451 1452ssize_t /* O - Number of bytes written or -1 on error */ 1453cupsFilePutConf(cups_file_t *fp, /* I - CUPS file */ 1454 const char *directive, /* I - Directive */ 1455 const char *value) /* I - Value */ 1456{ 1457 ssize_t bytes, /* Number of bytes written */ 1458 temp; /* Temporary byte count */ 1459 const char *ptr; /* Pointer into value */ 1460 1461 1462 if (!fp || !directive || !*directive) 1463 return (-1); 1464 1465 if ((bytes = cupsFilePuts(fp, directive)) < 0) 1466 return (-1); 1467 1468 if (cupsFilePutChar(fp, ' ') < 0) 1469 return (-1); 1470 bytes ++; 1471 1472 if (value && *value) 1473 { 1474 if ((ptr = strchr(value, '#')) != NULL) 1475 { 1476 /* 1477 * Need to quote the first # in the info string... 1478 */ 1479 1480 if ((temp = cupsFileWrite(fp, value, (size_t)(ptr - value))) < 0) 1481 return (-1); 1482 bytes += temp; 1483 1484 if (cupsFilePutChar(fp, '\\') < 0) 1485 return (-1); 1486 bytes ++; 1487 1488 if ((temp = cupsFilePuts(fp, ptr)) < 0) 1489 return (-1); 1490 bytes += temp; 1491 } 1492 else if ((temp = cupsFilePuts(fp, value)) < 0) 1493 return (-1); 1494 else 1495 bytes += temp; 1496 } 1497 1498 if (cupsFilePutChar(fp, '\n') < 0) 1499 return (-1); 1500 else 1501 return (bytes + 1); 1502} 1503 1504 1505/* 1506 * 'cupsFilePuts()' - Write a string. 1507 * 1508 * Like the @code fputs@ function, no newline is appended to the string. 1509 * 1510 * @since CUPS 1.2/macOS 10.5@ 1511 */ 1512 1513int /* O - Number of bytes written or -1 on error */ 1514cupsFilePuts(cups_file_t *fp, /* I - CUPS file */ 1515 const char *s) /* I - String to write */ 1516{ 1517 ssize_t bytes; /* Bytes to write */ 1518 1519 1520 /* 1521 * Range check input... 1522 */ 1523 1524 if (!fp || !s || (fp->mode != 'w' && fp->mode != 's')) 1525 return (-1); 1526 1527 /* 1528 * Write the string... 1529 */ 1530 1531 bytes = (ssize_t)strlen(s); 1532 1533 if (fp->mode == 's') 1534 { 1535 if (cups_write(fp, s, (size_t)bytes) < 0) 1536 return (-1); 1537 1538 fp->pos += bytes; 1539 1540 DEBUG_printf(("4cupsFilePuts: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 1541 1542 return ((int)bytes); 1543 } 1544 1545 if ((fp->ptr + bytes) > fp->end) 1546 if (cupsFileFlush(fp)) 1547 return (-1); 1548 1549 fp->pos += bytes; 1550 1551 DEBUG_printf(("4cupsFilePuts: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 1552 1553 if ((size_t)bytes > sizeof(fp->buf)) 1554 { 1555#ifdef HAVE_LIBZ 1556 if (fp->compressed) 1557 return ((int)cups_compress(fp, s, (size_t)bytes)); 1558 else 1559#endif /* HAVE_LIBZ */ 1560 return ((int)cups_write(fp, s, (size_t)bytes)); 1561 } 1562 else 1563 { 1564 memcpy(fp->ptr, s, (size_t)bytes); 1565 fp->ptr += bytes; 1566 return ((int)bytes); 1567 } 1568} 1569 1570 1571/* 1572 * 'cupsFileRead()' - Read from a file. 1573 * 1574 * @since CUPS 1.2/macOS 10.5@ 1575 */ 1576 1577ssize_t /* O - Number of bytes read or -1 on error */ 1578cupsFileRead(cups_file_t *fp, /* I - CUPS file */ 1579 char *buf, /* O - Buffer */ 1580 size_t bytes) /* I - Number of bytes to read */ 1581{ 1582 size_t total; /* Total bytes read */ 1583 ssize_t count; /* Bytes read */ 1584 1585 1586 DEBUG_printf(("2cupsFileRead(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes)); 1587 1588 /* 1589 * Range check input... 1590 */ 1591 1592 if (!fp || !buf || (fp->mode != 'r' && fp->mode != 's')) 1593 return (-1); 1594 1595 if (bytes == 0) 1596 return (0); 1597 1598 /* 1599 * Loop until all bytes are read... 1600 */ 1601 1602 total = 0; 1603 while (bytes > 0) 1604 { 1605 if (fp->ptr >= fp->end) 1606 if (cups_fill(fp) <= 0) 1607 { 1608 DEBUG_printf(("4cupsFileRead: cups_fill() returned -1, total=" 1609 CUPS_LLFMT, CUPS_LLCAST total)); 1610 1611 if (total > 0) 1612 return ((ssize_t)total); 1613 else 1614 return (-1); 1615 } 1616 1617 count = (ssize_t)(fp->end - fp->ptr); 1618 if (count > (ssize_t)bytes) 1619 count = (ssize_t)bytes; 1620 1621 memcpy(buf, fp->ptr,(size_t) count); 1622 fp->ptr += count; 1623 fp->pos += count; 1624 1625 DEBUG_printf(("4cupsFileRead: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 1626 1627 /* 1628 * Update the counts for the last read... 1629 */ 1630 1631 bytes -= (size_t)count; 1632 total += (size_t)count; 1633 buf += count; 1634 } 1635 1636 /* 1637 * Return the total number of bytes read... 1638 */ 1639 1640 DEBUG_printf(("3cupsFileRead: total=" CUPS_LLFMT, CUPS_LLCAST total)); 1641 1642 return ((ssize_t)total); 1643} 1644 1645 1646/* 1647 * 'cupsFileRewind()' - Set the current file position to the beginning of the 1648 * file. 1649 * 1650 * @since CUPS 1.2/macOS 10.5@ 1651 */ 1652 1653off_t /* O - New file position or -1 on error */ 1654cupsFileRewind(cups_file_t *fp) /* I - CUPS file */ 1655{ 1656 /* 1657 * Range check input... 1658 */ 1659 1660 DEBUG_printf(("cupsFileRewind(fp=%p)", (void *)fp)); 1661 DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 1662 1663 if (!fp || fp->mode != 'r') 1664 return (-1); 1665 1666 /* 1667 * Handle special cases... 1668 */ 1669 1670 if (fp->bufpos == 0) 1671 { 1672 /* 1673 * No seeking necessary... 1674 */ 1675 1676 fp->pos = 0; 1677 1678 if (fp->ptr) 1679 { 1680 fp->ptr = fp->buf; 1681 fp->eof = 0; 1682 } 1683 1684 DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 1685 1686 return (0); 1687 } 1688 1689 /* 1690 * Otherwise, seek in the file and cleanup any compression buffers... 1691 */ 1692 1693#ifdef HAVE_LIBZ 1694 if (fp->compressed) 1695 { 1696 inflateEnd(&fp->stream); 1697 fp->compressed = 0; 1698 } 1699#endif /* HAVE_LIBZ */ 1700 1701 if (lseek(fp->fd, 0, SEEK_SET)) 1702 { 1703 DEBUG_printf(("1cupsFileRewind: lseek failed: %s", strerror(errno))); 1704 return (-1); 1705 } 1706 1707 fp->bufpos = 0; 1708 fp->pos = 0; 1709 fp->ptr = NULL; 1710 fp->end = NULL; 1711 fp->eof = 0; 1712 1713 DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 1714 1715 return (0); 1716} 1717 1718 1719/* 1720 * 'cupsFileSeek()' - Seek in a file. 1721 * 1722 * @since CUPS 1.2/macOS 10.5@ 1723 */ 1724 1725off_t /* O - New file position or -1 on error */ 1726cupsFileSeek(cups_file_t *fp, /* I - CUPS file */ 1727 off_t pos) /* I - Position in file */ 1728{ 1729 ssize_t bytes; /* Number bytes in buffer */ 1730 1731 1732 DEBUG_printf(("cupsFileSeek(fp=%p, pos=" CUPS_LLFMT ")", (void *)fp, CUPS_LLCAST pos)); 1733 DEBUG_printf(("2cupsFileSeek: fp->pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 1734 DEBUG_printf(("2cupsFileSeek: fp->ptr=%p, fp->end=%p", (void *)fp->ptr, (void *)fp->end)); 1735 1736 /* 1737 * Range check input... 1738 */ 1739 1740 if (!fp || pos < 0 || fp->mode != 'r') 1741 return (-1); 1742 1743 /* 1744 * Handle special cases... 1745 */ 1746 1747 if (pos == 0) 1748 return (cupsFileRewind(fp)); 1749 1750 if (fp->ptr) 1751 { 1752 bytes = (ssize_t)(fp->end - fp->buf); 1753 1754 DEBUG_printf(("2cupsFileSeek: bytes=" CUPS_LLFMT, CUPS_LLCAST bytes)); 1755 1756 if (pos >= fp->bufpos && pos < (fp->bufpos + bytes)) 1757 { 1758 /* 1759 * No seeking necessary... 1760 */ 1761 1762 fp->pos = pos; 1763 fp->ptr = fp->buf + pos - fp->bufpos; 1764 fp->eof = 0; 1765 1766 return (pos); 1767 } 1768 } 1769 1770#ifdef HAVE_LIBZ 1771 if (!fp->compressed && !fp->ptr) 1772 { 1773 /* 1774 * Preload a buffer to determine whether the file is compressed... 1775 */ 1776 1777 if (cups_fill(fp) <= 0) 1778 return (-1); 1779 } 1780#endif /* HAVE_LIBZ */ 1781 1782 /* 1783 * Seek forwards or backwards... 1784 */ 1785 1786 fp->eof = 0; 1787 1788 if (pos < fp->bufpos) 1789 { 1790 /* 1791 * Need to seek backwards... 1792 */ 1793 1794 DEBUG_puts("2cupsFileSeek: SEEK BACKWARDS"); 1795 1796#ifdef HAVE_LIBZ 1797 if (fp->compressed) 1798 { 1799 inflateEnd(&fp->stream); 1800 1801 lseek(fp->fd, 0, SEEK_SET); 1802 fp->bufpos = 0; 1803 fp->pos = 0; 1804 fp->ptr = NULL; 1805 fp->end = NULL; 1806 1807 while ((bytes = cups_fill(fp)) > 0) 1808 if (pos >= fp->bufpos && pos < (fp->bufpos + bytes)) 1809 break; 1810 1811 if (bytes <= 0) 1812 return (-1); 1813 1814 fp->ptr = fp->buf + pos - fp->bufpos; 1815 fp->pos = pos; 1816 } 1817 else 1818#endif /* HAVE_LIBZ */ 1819 { 1820 fp->bufpos = lseek(fp->fd, pos, SEEK_SET); 1821 fp->pos = fp->bufpos; 1822 fp->ptr = NULL; 1823 fp->end = NULL; 1824 1825 DEBUG_printf(("2cupsFileSeek: lseek() returned " CUPS_LLFMT, 1826 CUPS_LLCAST fp->pos)); 1827 } 1828 } 1829 else 1830 { 1831 /* 1832 * Need to seek forwards... 1833 */ 1834 1835 DEBUG_puts("2cupsFileSeek: SEEK FORWARDS"); 1836 1837#ifdef HAVE_LIBZ 1838 if (fp->compressed) 1839 { 1840 while ((bytes = cups_fill(fp)) > 0) 1841 { 1842 if (pos >= fp->bufpos && pos < (fp->bufpos + bytes)) 1843 break; 1844 } 1845 1846 if (bytes <= 0) 1847 return (-1); 1848 1849 fp->ptr = fp->buf + pos - fp->bufpos; 1850 fp->pos = pos; 1851 } 1852 else 1853#endif /* HAVE_LIBZ */ 1854 { 1855 fp->bufpos = lseek(fp->fd, pos, SEEK_SET); 1856 fp->pos = fp->bufpos; 1857 fp->ptr = NULL; 1858 fp->end = NULL; 1859 1860 DEBUG_printf(("2cupsFileSeek: lseek() returned " CUPS_LLFMT, 1861 CUPS_LLCAST fp->pos)); 1862 } 1863 } 1864 1865 DEBUG_printf(("2cupsFileSeek: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 1866 1867 return (fp->pos); 1868} 1869 1870 1871/* 1872 * 'cupsFileStderr()' - Return a CUPS file associated with stderr. 1873 * 1874 * @since CUPS 1.2/macOS 10.5@ 1875 */ 1876 1877cups_file_t * /* O - CUPS file */ 1878cupsFileStderr(void) 1879{ 1880 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */ 1881 1882 1883 /* 1884 * Open file descriptor 2 as needed... 1885 */ 1886 1887 if (!cg->stdio_files[2]) 1888 { 1889 /* 1890 * Flush any pending output on the stdio file... 1891 */ 1892 1893 fflush(stderr); 1894 1895 /* 1896 * Open file descriptor 2... 1897 */ 1898 1899 if ((cg->stdio_files[2] = cupsFileOpenFd(2, "w")) != NULL) 1900 cg->stdio_files[2]->is_stdio = 1; 1901 } 1902 1903 return (cg->stdio_files[2]); 1904} 1905 1906 1907/* 1908 * 'cupsFileStdin()' - Return a CUPS file associated with stdin. 1909 * 1910 * @since CUPS 1.2/macOS 10.5@ 1911 */ 1912 1913cups_file_t * /* O - CUPS file */ 1914cupsFileStdin(void) 1915{ 1916 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */ 1917 1918 1919 /* 1920 * Open file descriptor 0 as needed... 1921 */ 1922 1923 if (!cg->stdio_files[0]) 1924 { 1925 /* 1926 * Open file descriptor 0... 1927 */ 1928 1929 if ((cg->stdio_files[0] = cupsFileOpenFd(0, "r")) != NULL) 1930 cg->stdio_files[0]->is_stdio = 1; 1931 } 1932 1933 return (cg->stdio_files[0]); 1934} 1935 1936 1937/* 1938 * 'cupsFileStdout()' - Return a CUPS file associated with stdout. 1939 * 1940 * @since CUPS 1.2/macOS 10.5@ 1941 */ 1942 1943cups_file_t * /* O - CUPS file */ 1944cupsFileStdout(void) 1945{ 1946 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */ 1947 1948 1949 /* 1950 * Open file descriptor 1 as needed... 1951 */ 1952 1953 if (!cg->stdio_files[1]) 1954 { 1955 /* 1956 * Flush any pending output on the stdio file... 1957 */ 1958 1959 fflush(stdout); 1960 1961 /* 1962 * Open file descriptor 1... 1963 */ 1964 1965 if ((cg->stdio_files[1] = cupsFileOpenFd(1, "w")) != NULL) 1966 cg->stdio_files[1]->is_stdio = 1; 1967 } 1968 1969 return (cg->stdio_files[1]); 1970} 1971 1972 1973/* 1974 * 'cupsFileTell()' - Return the current file position. 1975 * 1976 * @since CUPS 1.2/macOS 10.5@ 1977 */ 1978 1979off_t /* O - File position */ 1980cupsFileTell(cups_file_t *fp) /* I - CUPS file */ 1981{ 1982 DEBUG_printf(("2cupsFileTell(fp=%p)", (void *)fp)); 1983 DEBUG_printf(("3cupsFileTell: pos=" CUPS_LLFMT, CUPS_LLCAST (fp ? fp->pos : -1))); 1984 1985 return (fp ? fp->pos : 0); 1986} 1987 1988 1989/* 1990 * 'cupsFileUnlock()' - Unlock access to a file. 1991 * 1992 * @since CUPS 1.2/macOS 10.5@ 1993 */ 1994 1995int /* O - 0 on success, -1 on error */ 1996cupsFileUnlock(cups_file_t *fp) /* I - CUPS file */ 1997{ 1998 /* 1999 * Range check... 2000 */ 2001 2002 DEBUG_printf(("cupsFileUnlock(fp=%p)", (void *)fp)); 2003 2004 if (!fp || fp->mode == 's') 2005 return (-1); 2006 2007 /* 2008 * Unlock... 2009 */ 2010 2011#ifdef WIN32 2012 return (_locking(fp->fd, _LK_UNLCK, 0)); 2013#else 2014 return (lockf(fp->fd, F_ULOCK, 0)); 2015#endif /* WIN32 */ 2016} 2017 2018 2019/* 2020 * 'cupsFileWrite()' - Write to a file. 2021 * 2022 * @since CUPS 1.2/macOS 10.5@ 2023 */ 2024 2025ssize_t /* O - Number of bytes written or -1 on error */ 2026cupsFileWrite(cups_file_t *fp, /* I - CUPS file */ 2027 const char *buf, /* I - Buffer */ 2028 size_t bytes) /* I - Number of bytes to write */ 2029{ 2030 /* 2031 * Range check input... 2032 */ 2033 2034 DEBUG_printf(("2cupsFileWrite(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes)); 2035 2036 if (!fp || !buf || (fp->mode != 'w' && fp->mode != 's')) 2037 return (-1); 2038 2039 if (bytes == 0) 2040 return (0); 2041 2042 /* 2043 * Write the buffer... 2044 */ 2045 2046 if (fp->mode == 's') 2047 { 2048 if (cups_write(fp, buf, bytes) < 0) 2049 return (-1); 2050 2051 fp->pos += (off_t)bytes; 2052 2053 DEBUG_printf(("4cupsFileWrite: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 2054 2055 return ((ssize_t)bytes); 2056 } 2057 2058 if ((fp->ptr + bytes) > fp->end) 2059 if (cupsFileFlush(fp)) 2060 return (-1); 2061 2062 fp->pos += (off_t)bytes; 2063 2064 DEBUG_printf(("4cupsFileWrite: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); 2065 2066 if (bytes > sizeof(fp->buf)) 2067 { 2068#ifdef HAVE_LIBZ 2069 if (fp->compressed) 2070 return (cups_compress(fp, buf, bytes)); 2071 else 2072#endif /* HAVE_LIBZ */ 2073 return (cups_write(fp, buf, bytes)); 2074 } 2075 else 2076 { 2077 memcpy(fp->ptr, buf, bytes); 2078 fp->ptr += bytes; 2079 return ((ssize_t)bytes); 2080 } 2081} 2082 2083 2084#ifdef HAVE_LIBZ 2085/* 2086 * 'cups_compress()' - Compress a buffer of data. 2087 */ 2088 2089static ssize_t /* O - Number of bytes written or -1 */ 2090cups_compress(cups_file_t *fp, /* I - CUPS file */ 2091 const char *buf, /* I - Buffer */ 2092 size_t bytes) /* I - Number bytes */ 2093{ 2094 DEBUG_printf(("7cups_compress(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes)); 2095 2096 /* 2097 * Update the CRC... 2098 */ 2099 2100 fp->crc = crc32(fp->crc, (const Bytef *)buf, (uInt)bytes); 2101 2102 /* 2103 * Deflate the bytes... 2104 */ 2105 2106 fp->stream.next_in = (Bytef *)buf; 2107 fp->stream.avail_in = (uInt)bytes; 2108 2109 while (fp->stream.avail_in > 0) 2110 { 2111 /* 2112 * Flush the current buffer... 2113 */ 2114 2115 DEBUG_printf(("9cups_compress: avail_in=%d, avail_out=%d", 2116 fp->stream.avail_in, fp->stream.avail_out)); 2117 2118 if (fp->stream.avail_out < (uInt)(sizeof(fp->cbuf) / 8)) 2119 { 2120 if (cups_write(fp, (char *)fp->cbuf, (size_t)(fp->stream.next_out - fp->cbuf)) < 0) 2121 return (-1); 2122 2123 fp->stream.next_out = fp->cbuf; 2124 fp->stream.avail_out = sizeof(fp->cbuf); 2125 } 2126 2127 deflate(&(fp->stream), Z_NO_FLUSH); 2128 } 2129 2130 return ((ssize_t)bytes); 2131} 2132#endif /* HAVE_LIBZ */ 2133 2134 2135/* 2136 * 'cups_fill()' - Fill the input buffer. 2137 */ 2138 2139static ssize_t /* O - Number of bytes or -1 */ 2140cups_fill(cups_file_t *fp) /* I - CUPS file */ 2141{ 2142 ssize_t bytes; /* Number of bytes read */ 2143#ifdef HAVE_LIBZ 2144 int status; /* Decompression status */ 2145 const unsigned char *ptr, /* Pointer into buffer */ 2146 *end; /* End of buffer */ 2147#endif /* HAVE_LIBZ */ 2148 2149 2150 DEBUG_printf(("7cups_fill(fp=%p)", (void *)fp)); 2151 DEBUG_printf(("9cups_fill: fp->ptr=%p, fp->end=%p, fp->buf=%p, fp->bufpos=" CUPS_LLFMT ", fp->eof=%d", (void *)fp->ptr, (void *)fp->end, (void *)fp->buf, CUPS_LLCAST fp->bufpos, fp->eof)); 2152 2153 if (fp->ptr && fp->end) 2154 fp->bufpos += fp->end - fp->buf; 2155 2156#ifdef HAVE_LIBZ 2157 DEBUG_printf(("9cups_fill: fp->compressed=%d", fp->compressed)); 2158 2159 while (!fp->ptr || fp->compressed) 2160 { 2161 /* 2162 * Check to see if we have read any data yet; if not, see if we have a 2163 * compressed file... 2164 */ 2165 2166 if (!fp->ptr) 2167 { 2168 /* 2169 * Reset the file position in case we are seeking... 2170 */ 2171 2172 fp->compressed = 0; 2173 2174 /* 2175 * Read the first bytes in the file to determine if we have a gzip'd 2176 * file... 2177 */ 2178 2179 if ((bytes = cups_read(fp, (char *)fp->buf, sizeof(fp->buf))) < 0) 2180 { 2181 /* 2182 * Can't read from file! 2183 */ 2184 2185 DEBUG_printf(("9cups_fill: cups_read() returned " CUPS_LLFMT, 2186 CUPS_LLCAST bytes)); 2187 2188 fp->eof = 1; 2189 2190 return (-1); 2191 } 2192 2193 if (bytes < 10 || fp->buf[0] != 0x1f || 2194 (fp->buf[1] & 255) != 0x8b || 2195 fp->buf[2] != 8 || (fp->buf[3] & 0xe0) != 0) 2196 { 2197 /* 2198 * Not a gzip'd file! 2199 */ 2200 2201 fp->ptr = fp->buf; 2202 fp->end = fp->buf + bytes; 2203 2204 DEBUG_printf(("9cups_fill: Returning " CUPS_LLFMT, 2205 CUPS_LLCAST bytes)); 2206 2207 return (bytes); 2208 } 2209 2210 /* 2211 * Parse header junk: extra data, original name, and comment... 2212 */ 2213 2214 ptr = (unsigned char *)fp->buf + 10; 2215 end = (unsigned char *)fp->buf + bytes; 2216 2217 if (fp->buf[3] & 0x04) 2218 { 2219 /* 2220 * Skip extra data... 2221 */ 2222 2223 if ((ptr + 2) > end) 2224 { 2225 /* 2226 * Can't read from file! 2227 */ 2228 2229 DEBUG_puts("9cups_fill: Extra gzip header data missing, returning -1."); 2230 2231 fp->eof = 1; 2232 errno = EIO; 2233 2234 return (-1); 2235 } 2236 2237 bytes = ((unsigned char)ptr[1] << 8) | (unsigned char)ptr[0]; 2238 ptr += 2 + bytes; 2239 2240 if (ptr > end) 2241 { 2242 /* 2243 * Can't read from file! 2244 */ 2245 2246 DEBUG_puts("9cups_fill: Extra gzip header data does not fit in initial buffer, returning -1."); 2247 2248 fp->eof = 1; 2249 errno = EIO; 2250 2251 return (-1); 2252 } 2253 } 2254 2255 if (fp->buf[3] & 0x08) 2256 { 2257 /* 2258 * Skip original name data... 2259 */ 2260 2261 while (ptr < end && *ptr) 2262 ptr ++; 2263 2264 if (ptr < end) 2265 ptr ++; 2266 else 2267 { 2268 /* 2269 * Can't read from file! 2270 */ 2271 2272 DEBUG_puts("9cups_fill: Original filename in gzip header data does not fit in initial buffer, returning -1."); 2273 2274 fp->eof = 1; 2275 errno = EIO; 2276 2277 return (-1); 2278 } 2279 } 2280 2281 if (fp->buf[3] & 0x10) 2282 { 2283 /* 2284 * Skip comment data... 2285 */ 2286 2287 while (ptr < end && *ptr) 2288 ptr ++; 2289 2290 if (ptr < end) 2291 ptr ++; 2292 else 2293 { 2294 /* 2295 * Can't read from file! 2296 */ 2297 2298 DEBUG_puts("9cups_fill: Comment in gzip header data does not fit in initial buffer, returning -1."); 2299 2300 fp->eof = 1; 2301 errno = EIO; 2302 2303 return (-1); 2304 } 2305 } 2306 2307 if (fp->buf[3] & 0x02) 2308 { 2309 /* 2310 * Skip header CRC data... 2311 */ 2312 2313 ptr += 2; 2314 2315 if (ptr > end) 2316 { 2317 /* 2318 * Can't read from file! 2319 */ 2320 2321 DEBUG_puts("9cups_fill: Header CRC in gzip header data does not fit in initial buffer, returning -1."); 2322 2323 fp->eof = 1; 2324 errno = EIO; 2325 2326 return (-1); 2327 } 2328 } 2329 2330 /* 2331 * Copy the flate-compressed data to the compression buffer... 2332 */ 2333 2334 if ((bytes = end - ptr) > 0) 2335 memcpy(fp->cbuf, ptr, (size_t)bytes); 2336 2337 /* 2338 * Setup the decompressor data... 2339 */ 2340 2341 fp->stream.zalloc = (alloc_func)0; 2342 fp->stream.zfree = (free_func)0; 2343 fp->stream.opaque = (voidpf)0; 2344 fp->stream.next_in = (Bytef *)fp->cbuf; 2345 fp->stream.next_out = NULL; 2346 fp->stream.avail_in = (uInt)bytes; 2347 fp->stream.avail_out = 0; 2348 fp->crc = crc32(0L, Z_NULL, 0); 2349 2350 if ((status = inflateInit2(&(fp->stream), -15)) != Z_OK) 2351 { 2352 DEBUG_printf(("9cups_fill: inflateInit2 returned %d, returning -1.", status)); 2353 2354 fp->eof = 1; 2355 errno = EIO; 2356 2357 return (-1); 2358 } 2359 2360 fp->compressed = 1; 2361 } 2362 2363 if (fp->compressed) 2364 { 2365 /* 2366 * If we have reached end-of-file, return immediately... 2367 */ 2368 2369 if (fp->eof) 2370 { 2371 DEBUG_puts("9cups_fill: EOF, returning 0."); 2372 2373 return (0); 2374 } 2375 2376 /* 2377 * Fill the decompression buffer as needed... 2378 */ 2379 2380 if (fp->stream.avail_in == 0) 2381 { 2382 if ((bytes = cups_read(fp, (char *)fp->cbuf, sizeof(fp->cbuf))) <= 0) 2383 { 2384 DEBUG_printf(("9cups_fill: cups_read error, returning %d.", (int)bytes)); 2385 2386 fp->eof = 1; 2387 2388 return (bytes); 2389 } 2390 2391 fp->stream.next_in = fp->cbuf; 2392 fp->stream.avail_in = (uInt)bytes; 2393 } 2394 2395 /* 2396 * Decompress data from the buffer... 2397 */ 2398 2399 fp->stream.next_out = (Bytef *)fp->buf; 2400 fp->stream.avail_out = sizeof(fp->buf); 2401 2402 status = inflate(&(fp->stream), Z_NO_FLUSH); 2403 2404 if (fp->stream.next_out > (Bytef *)fp->buf) 2405 fp->crc = crc32(fp->crc, (Bytef *)fp->buf, 2406 (uInt)(fp->stream.next_out - (Bytef *)fp->buf)); 2407 2408 if (status == Z_STREAM_END) 2409 { 2410 /* 2411 * Read the CRC and length... 2412 */ 2413 2414 unsigned char trailer[8]; /* Trailer bytes */ 2415 uLong tcrc; /* Trailer CRC */ 2416 ssize_t tbytes = 0; /* Number of bytes */ 2417 2418 if (fp->stream.avail_in > 0) 2419 { 2420 if (fp->stream.avail_in > sizeof(trailer)) 2421 tbytes = (ssize_t)sizeof(trailer); 2422 else 2423 tbytes = (ssize_t)fp->stream.avail_in; 2424 2425 memcpy(trailer, fp->stream.next_in, (size_t)tbytes); 2426 fp->stream.next_in += tbytes; 2427 fp->stream.avail_in -= (size_t)tbytes; 2428 } 2429 2430 if (tbytes < (ssize_t)sizeof(trailer)) 2431 { 2432 if (read(fp->fd, trailer + tbytes, sizeof(trailer) - (size_t)tbytes) < ((ssize_t)sizeof(trailer) - tbytes)) 2433 { 2434 /* 2435 * Can't get it, so mark end-of-file... 2436 */ 2437 2438 DEBUG_puts("9cups_fill: Unable to read gzip CRC trailer, returning -1."); 2439 2440 fp->eof = 1; 2441 errno = EIO; 2442 2443 return (-1); 2444 } 2445 } 2446 2447 tcrc = ((((((uLong)trailer[3] << 8) | (uLong)trailer[2]) << 8) | 2448 (uLong)trailer[1]) << 8) | (uLong)trailer[0]; 2449 2450 if (tcrc != fp->crc) 2451 { 2452 /* 2453 * Bad CRC, mark end-of-file... 2454 */ 2455 2456 DEBUG_printf(("9cups_fill: tcrc=%08x != fp->crc=%08x, returning -1.", (unsigned int)tcrc, (unsigned int)fp->crc)); 2457 2458 fp->eof = 1; 2459 errno = EIO; 2460 2461 return (-1); 2462 } 2463 2464 /* 2465 * Otherwise, reset the compressed flag so that we re-read the 2466 * file header... 2467 */ 2468 2469 fp->compressed = 0; 2470 } 2471 else if (status < Z_OK) 2472 { 2473 DEBUG_printf(("9cups_fill: inflate returned %d, returning -1.", status)); 2474 2475 fp->eof = 1; 2476 errno = EIO; 2477 2478 return (-1); 2479 } 2480 2481 bytes = (ssize_t)sizeof(fp->buf) - (ssize_t)fp->stream.avail_out; 2482 2483 /* 2484 * Return the decompressed data... 2485 */ 2486 2487 fp->ptr = fp->buf; 2488 fp->end = fp->buf + bytes; 2489 2490 if (bytes) 2491 { 2492 DEBUG_printf(("9cups_fill: Returning %d.", (int)bytes)); 2493 return (bytes); 2494 } 2495 } 2496 } 2497#endif /* HAVE_LIBZ */ 2498 2499 /* 2500 * Read a buffer's full of data... 2501 */ 2502 2503 if ((bytes = cups_read(fp, fp->buf, sizeof(fp->buf))) <= 0) 2504 { 2505 /* 2506 * Can't read from file! 2507 */ 2508 2509 fp->eof = 1; 2510 fp->ptr = fp->buf; 2511 fp->end = fp->buf; 2512 } 2513 else 2514 { 2515 /* 2516 * Return the bytes we read... 2517 */ 2518 2519 fp->eof = 0; 2520 fp->ptr = fp->buf; 2521 fp->end = fp->buf + bytes; 2522 } 2523 2524 DEBUG_printf(("9cups_fill: Not gzip, returning %d.", (int)bytes)); 2525 2526 return (bytes); 2527} 2528 2529 2530/* 2531 * 'cups_open()' - Safely open a file for writing. 2532 * 2533 * We don't allow appending to directories or files that are hard-linked or 2534 * symlinked. 2535 */ 2536 2537static int /* O - File descriptor or -1 otherwise */ 2538cups_open(const char *filename, /* I - Filename */ 2539 int mode) /* I - Open mode */ 2540{ 2541 int fd; /* File descriptor */ 2542 struct stat fileinfo; /* File information */ 2543#ifndef WIN32 2544 struct stat linkinfo; /* Link information */ 2545#endif /* !WIN32 */ 2546 2547 2548 /* 2549 * Open the file... 2550 */ 2551 2552 if ((fd = open(filename, mode, 0666)) < 0) 2553 return (-1); 2554 2555 /* 2556 * Then verify that the file descriptor doesn't point to a directory or hard- 2557 * linked file. 2558 */ 2559 2560 if (fstat(fd, &fileinfo)) 2561 { 2562 close(fd); 2563 return (-1); 2564 } 2565 2566 if (fileinfo.st_nlink != 1) 2567 { 2568 close(fd); 2569 errno = EPERM; 2570 return (-1); 2571 } 2572 2573#ifdef WIN32 2574 if (fileinfo.st_mode & _S_IFDIR) 2575#else 2576 if (S_ISDIR(fileinfo.st_mode)) 2577#endif /* WIN32 */ 2578 { 2579 close(fd); 2580 errno = EISDIR; 2581 return (-1); 2582 } 2583 2584#ifndef WIN32 2585 /* 2586 * Then use lstat to determine whether the filename is a symlink... 2587 */ 2588 2589 if (lstat(filename, &linkinfo)) 2590 { 2591 close(fd); 2592 return (-1); 2593 } 2594 2595 if (S_ISLNK(linkinfo.st_mode) || 2596 fileinfo.st_dev != linkinfo.st_dev || 2597 fileinfo.st_ino != linkinfo.st_ino || 2598#ifdef HAVE_ST_GEN 2599 fileinfo.st_gen != linkinfo.st_gen || 2600#endif /* HAVE_ST_GEN */ 2601 fileinfo.st_nlink != linkinfo.st_nlink || 2602 fileinfo.st_mode != linkinfo.st_mode) 2603 { 2604 /* 2605 * Yes, don't allow! 2606 */ 2607 2608 close(fd); 2609 errno = EPERM; 2610 return (-1); 2611 } 2612#endif /* !WIN32 */ 2613 2614 return (fd); 2615} 2616 2617 2618/* 2619 * 'cups_read()' - Read from a file descriptor. 2620 */ 2621 2622static ssize_t /* O - Number of bytes read or -1 */ 2623cups_read(cups_file_t *fp, /* I - CUPS file */ 2624 char *buf, /* I - Buffer */ 2625 size_t bytes) /* I - Number bytes */ 2626{ 2627 ssize_t total; /* Total bytes read */ 2628 2629 2630 DEBUG_printf(("7cups_read(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes)); 2631 2632 /* 2633 * Loop until we read at least 0 bytes... 2634 */ 2635 2636 for (;;) 2637 { 2638#ifdef WIN32 2639 if (fp->mode == 's') 2640 total = (ssize_t)recv(fp->fd, buf, (unsigned)bytes, 0); 2641 else 2642 total = (ssize_t)read(fp->fd, buf, (unsigned)bytes); 2643#else 2644 if (fp->mode == 's') 2645 total = recv(fp->fd, buf, bytes, 0); 2646 else 2647 total = read(fp->fd, buf, bytes); 2648#endif /* WIN32 */ 2649 2650 DEBUG_printf(("9cups_read: total=" CUPS_LLFMT, CUPS_LLCAST total)); 2651 2652 if (total >= 0) 2653 break; 2654 2655 /* 2656 * Reads can be interrupted by signals and unavailable resources... 2657 */ 2658 2659 if (errno == EAGAIN || errno == EINTR) 2660 continue; 2661 else 2662 return (-1); 2663 } 2664 2665 /* 2666 * Return the total number of bytes read... 2667 */ 2668 2669 return (total); 2670} 2671 2672 2673/* 2674 * 'cups_write()' - Write to a file descriptor. 2675 */ 2676 2677static ssize_t /* O - Number of bytes written or -1 */ 2678cups_write(cups_file_t *fp, /* I - CUPS file */ 2679 const char *buf, /* I - Buffer */ 2680 size_t bytes) /* I - Number bytes */ 2681{ 2682 size_t total; /* Total bytes written */ 2683 ssize_t count; /* Count this time */ 2684 2685 2686 DEBUG_printf(("7cups_write(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes)); 2687 2688 /* 2689 * Loop until all bytes are written... 2690 */ 2691 2692 total = 0; 2693 while (bytes > 0) 2694 { 2695#ifdef WIN32 2696 if (fp->mode == 's') 2697 count = (ssize_t)send(fp->fd, buf, (unsigned)bytes, 0); 2698 else 2699 count = (ssize_t)write(fp->fd, buf, (unsigned)bytes); 2700#else 2701 if (fp->mode == 's') 2702 count = send(fp->fd, buf, bytes, 0); 2703 else 2704 count = write(fp->fd, buf, bytes); 2705#endif /* WIN32 */ 2706 2707 DEBUG_printf(("9cups_write: count=" CUPS_LLFMT, CUPS_LLCAST count)); 2708 2709 if (count < 0) 2710 { 2711 /* 2712 * Writes can be interrupted by signals and unavailable resources... 2713 */ 2714 2715 if (errno == EAGAIN || errno == EINTR) 2716 continue; 2717 else 2718 return (-1); 2719 } 2720 2721 /* 2722 * Update the counts for the last write call... 2723 */ 2724 2725 bytes -= (size_t)count; 2726 total += (size_t)count; 2727 buf += count; 2728 } 2729 2730 /* 2731 * Return the total number of bytes written... 2732 */ 2733 2734 return ((ssize_t)total); 2735} 2736