1/* 2 * "$Id: snmp-supplies.c 11560 2014-02-06 20:10:19Z msweet $" 3 * 4 * SNMP supplies functions for CUPS. 5 * 6 * Copyright 2008-2014 by Apple Inc. 7 * 8 * These coded instructions, statements, and computer programs are the 9 * property of Apple Inc. and are protected by Federal copyright 10 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 11 * "LICENSE" which should have been included with this file. If this 12 * file is missing or damaged, see the license at "http://www.cups.org/". 13 * 14 * This file is subject to the Apple OS-Developed Software exception. 15 */ 16 17/* 18 * Include necessary headers. 19 */ 20 21#include "backend-private.h" 22#include <cups/array.h> 23 24 25/* 26 * Local constants... 27 */ 28 29#define CUPS_MAX_SUPPLIES 32 /* Maximum number of supplies for a printer */ 30#define CUPS_SUPPLY_TIMEOUT 2.0 /* Timeout for SNMP lookups */ 31 32#define CUPS_DEVELOPER_LOW 0x0001 33#define CUPS_DEVELOPER_EMPTY 0x0002 34#define CUPS_MARKER_SUPPLY_LOW 0x0004 35#define CUPS_MARKER_SUPPLY_EMPTY 0x0008 36#define CUPS_OPC_NEAR_EOL 0x0010 37#define CUPS_OPC_LIFE_OVER 0x0020 38#define CUPS_TONER_LOW 0x0040 39#define CUPS_TONER_EMPTY 0x0080 40#define CUPS_WASTE_ALMOST_FULL 0x0100 41#define CUPS_WASTE_FULL 0x0200 42#define CUPS_CLEANER_NEAR_EOL 0x0400 /* Proposed JPS3 */ 43#define CUPS_CLEANER_LIFE_OVER 0x0800 /* Proposed JPS3 */ 44 45#define CUPS_SNMP_NONE 0x0000 46#define CUPS_SNMP_CAPACITY 0x0001 /* Supply levels reported as percentages */ 47 48 49/* 50 * Local structures... 51 */ 52 53typedef struct /**** Printer supply data ****/ 54{ 55 char name[CUPS_SNMP_MAX_STRING], /* Name of supply */ 56 color[8]; /* Color: "#RRGGBB" or "none" */ 57 int colorant, /* Colorant index */ 58 sclass, /* Supply class */ 59 type, /* Supply type */ 60 max_capacity, /* Maximum capacity */ 61 level; /* Current level value */ 62} backend_supplies_t; 63 64typedef struct /**** Printer state table ****/ 65{ 66 int bit; /* State bit */ 67 const char *keyword; /* IPP printer-state-reasons keyword */ 68} backend_state_t; 69 70 71/* 72 * Local globals... 73 */ 74 75static http_addr_t current_addr; /* Current address */ 76static int current_state = -1; 77 /* Current device state bits */ 78static int charset = -1; /* Character set for supply names */ 79static unsigned quirks = CUPS_SNMP_NONE; 80 /* Quirks we have to work around */ 81static int num_supplies = 0; 82 /* Number of supplies found */ 83static backend_supplies_t supplies[CUPS_MAX_SUPPLIES]; 84 /* Supply information */ 85static int supply_state = -1; 86 /* Supply state info */ 87 88static const int hrDeviceDescr[] = 89 { CUPS_OID_hrDeviceDescr, 1, -1 }; 90 /* Device description OID */ 91static const int hrPrinterStatus[] = 92 { CUPS_OID_hrPrinterStatus, 1, -1 }; 93 /* Current state OID */ 94static const int hrPrinterDetectedErrorState[] = 95 { CUPS_OID_hrPrinterDetectedErrorState, 1, -1 }; 96 /* Current printer state bits OID */ 97static const int prtGeneralCurrentLocalization[] = 98 { CUPS_OID_prtGeneralCurrentLocalization, 1, -1 }; 99static const int prtLocalizationCharacterSet[] = 100 { CUPS_OID_prtLocalizationCharacterSet, 1, 1, -1 }, 101 prtLocalizationCharacterSetOffset = 102 (sizeof(prtLocalizationCharacterSet) / 103 sizeof(prtLocalizationCharacterSet[0])); 104static const int prtMarkerColorantValue[] = 105 { CUPS_OID_prtMarkerColorantValue, -1 }, 106 /* Colorant OID */ 107 prtMarkerColorantValueOffset = 108 (sizeof(prtMarkerColorantValue) / 109 sizeof(prtMarkerColorantValue[0])); 110 /* Offset to colorant index */ 111static const int prtMarkerLifeCount[] = 112 { CUPS_OID_prtMarkerLifeCount, 1, 1, -1 }; 113 /* Page counter OID */ 114static const int prtMarkerSuppliesEntry[] = 115 { CUPS_OID_prtMarkerSuppliesEntry, -1 }; 116 /* Supplies OID */ 117static const int prtMarkerSuppliesColorantIndex[] = 118 { CUPS_OID_prtMarkerSuppliesColorantIndex, -1 }, 119 /* Colorant index OID */ 120 prtMarkerSuppliesColorantIndexOffset = 121 (sizeof(prtMarkerSuppliesColorantIndex) / 122 sizeof(prtMarkerSuppliesColorantIndex[0])); 123 /* Offset to supply index */ 124static const int prtMarkerSuppliesDescription[] = 125 { CUPS_OID_prtMarkerSuppliesDescription, -1 }, 126 /* Description OID */ 127 prtMarkerSuppliesDescriptionOffset = 128 (sizeof(prtMarkerSuppliesDescription) / 129 sizeof(prtMarkerSuppliesDescription[0])); 130 /* Offset to supply index */ 131static const int prtMarkerSuppliesLevel[] = 132 { CUPS_OID_prtMarkerSuppliesLevel, -1 }, 133 /* Level OID */ 134 prtMarkerSuppliesLevelOffset = 135 (sizeof(prtMarkerSuppliesLevel) / 136 sizeof(prtMarkerSuppliesLevel[0])); 137 /* Offset to supply index */ 138static const int prtMarkerSuppliesMaxCapacity[] = 139 { CUPS_OID_prtMarkerSuppliesMaxCapacity, -1 }, 140 /* Max capacity OID */ 141 prtMarkerSuppliesMaxCapacityOffset = 142 (sizeof(prtMarkerSuppliesMaxCapacity) / 143 sizeof(prtMarkerSuppliesMaxCapacity[0])); 144 /* Offset to supply index */ 145static const int prtMarkerSuppliesClass[] = 146 { CUPS_OID_prtMarkerSuppliesClass, -1 }, 147 /* Class OID */ 148 prtMarkerSuppliesClassOffset = 149 (sizeof(prtMarkerSuppliesClass) / 150 sizeof(prtMarkerSuppliesClass[0])); 151 /* Offset to supply index */ 152static const int prtMarkerSuppliesType[] = 153 { CUPS_OID_prtMarkerSuppliesType, -1 }, 154 /* Type OID */ 155 prtMarkerSuppliesTypeOffset = 156 (sizeof(prtMarkerSuppliesType) / 157 sizeof(prtMarkerSuppliesType[0])); 158 /* Offset to supply index */ 159static const int prtMarkerSuppliesSupplyUnit[] = 160 { CUPS_OID_prtMarkerSuppliesSupplyUnit, -1 }, 161 /* Units OID */ 162 prtMarkerSuppliesSupplyUnitOffset = 163 (sizeof(prtMarkerSuppliesSupplyUnit) / 164 sizeof(prtMarkerSuppliesSupplyUnit[0])); 165 /* Offset to supply index */ 166 167static const backend_state_t printer_states[] = 168 { 169 /* { CUPS_TC_lowPaper, "media-low-report" }, */ 170 { CUPS_TC_noPaper | CUPS_TC_inputTrayEmpty, "media-empty-warning" }, 171 /* { CUPS_TC_lowToner, "toner-low-report" }, */ /* now use prtMarkerSupplies */ 172 /* { CUPS_TC_noToner, "toner-empty-warning" }, */ /* now use prtMarkerSupplies */ 173 { CUPS_TC_doorOpen, "door-open-report" }, 174 { CUPS_TC_jammed, "media-jam-warning" }, 175 /* { CUPS_TC_offline, "offline-report" }, */ /* unreliable */ 176 /* { CUPS_TC_serviceRequested | CUPS_TC_overduePreventMaint, "service-needed-warning" }, */ /* unreliable */ 177 { CUPS_TC_inputTrayMissing, "input-tray-missing-warning" }, 178 { CUPS_TC_outputTrayMissing, "output-tray-missing-warning" }, 179 { CUPS_TC_markerSupplyMissing, "marker-supply-missing-warning" }, 180 { CUPS_TC_outputNearFull, "output-area-almost-full-report" }, 181 { CUPS_TC_outputFull, "output-area-full-warning" } 182 }; 183 184static const backend_state_t supply_states[] = 185 { 186 { CUPS_DEVELOPER_LOW, "developer-low-report" }, 187 { CUPS_DEVELOPER_EMPTY, "developer-empty-warning" }, 188 { CUPS_MARKER_SUPPLY_LOW, "marker-supply-low-report" }, 189 { CUPS_MARKER_SUPPLY_EMPTY, "marker-supply-empty-warning" }, 190 { CUPS_OPC_NEAR_EOL, "opc-near-eol-report" }, 191 { CUPS_OPC_LIFE_OVER, "opc-life-over-warning" }, 192 { CUPS_TONER_LOW, "toner-low-report" }, 193 { CUPS_TONER_EMPTY, "toner-empty-warning" }, 194 { CUPS_WASTE_ALMOST_FULL, "waste-receptacle-almost-full-report" }, 195 { CUPS_WASTE_FULL, "waste-receptacle-full-warning" }, 196 { CUPS_CLEANER_NEAR_EOL, "cleaner-life-almost-over-report" }, 197 { CUPS_CLEANER_LIFE_OVER, "cleaner-life-over-warning" }, 198 }; 199 200 201/* 202 * Local functions... 203 */ 204 205static void backend_init_supplies(int snmp_fd, http_addr_t *addr); 206static void backend_walk_cb(cups_snmp_t *packet, void *data); 207static void utf16_to_utf8(cups_utf8_t *dst, const unsigned char *src, 208 size_t srcsize, size_t dstsize, int le); 209 210 211/* 212 * 'backendSNMPSupplies()' - Get the current supplies for a device. 213 */ 214 215int /* O - 0 on success, -1 on error */ 216backendSNMPSupplies( 217 int snmp_fd, /* I - SNMP socket */ 218 http_addr_t *addr, /* I - Printer address */ 219 int *page_count, /* O - Page count */ 220 int *printer_state) /* O - Printer state */ 221{ 222 if (!httpAddrEqual(addr, ¤t_addr)) 223 backend_init_supplies(snmp_fd, addr); 224 else if (num_supplies > 0) 225 _cupsSNMPWalk(snmp_fd, ¤t_addr, CUPS_SNMP_VERSION_1, 226 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesLevel, 227 CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL); 228 229 if (page_count) 230 *page_count = -1; 231 232 if (printer_state) 233 *printer_state = -1; 234 235 if (num_supplies > 0) 236 { 237 int i, /* Looping var */ 238 percent, /* Percent full */ 239 new_state, /* New state value */ 240 change_state, /* State change */ 241 new_supply_state = 0; /* Supply state */ 242 char value[CUPS_MAX_SUPPLIES * 4], 243 /* marker-levels value string */ 244 *ptr; /* Pointer into value string */ 245 cups_snmp_t packet; /* SNMP response packet */ 246 247 /* 248 * Generate the marker-levels value string... 249 */ 250 251 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr)) 252 { 253 if (supplies[i].max_capacity > 0 && supplies[i].level >= 0) 254 percent = 100 * supplies[i].level / supplies[i].max_capacity; 255 else if (supplies[i].level >= 0 && supplies[i].level <= 100 && 256 (quirks & CUPS_SNMP_CAPACITY)) 257 percent = supplies[i].level; 258 else 259 percent = 50; 260 261 if (supplies[i].sclass == CUPS_TC_receptacleThatIsFilled) 262 percent = 100 - percent; 263 264 if (percent <= 5) 265 { 266 switch (supplies[i].type) 267 { 268 case CUPS_TC_toner : 269 case CUPS_TC_tonerCartridge : 270 if (percent <= 1) 271 new_supply_state |= CUPS_TONER_EMPTY; 272 else 273 new_supply_state |= CUPS_TONER_LOW; 274 break; 275 case CUPS_TC_ink : 276 case CUPS_TC_inkCartridge : 277 case CUPS_TC_inkRibbon : 278 case CUPS_TC_solidWax : 279 case CUPS_TC_ribbonWax : 280 if (percent <= 1) 281 new_supply_state |= CUPS_MARKER_SUPPLY_EMPTY; 282 else 283 new_supply_state |= CUPS_MARKER_SUPPLY_LOW; 284 break; 285 case CUPS_TC_developer : 286 if (percent <= 1) 287 new_supply_state |= CUPS_DEVELOPER_EMPTY; 288 else 289 new_supply_state |= CUPS_DEVELOPER_LOW; 290 break; 291 case CUPS_TC_coronaWire : 292 case CUPS_TC_fuser : 293 case CUPS_TC_opc : 294 case CUPS_TC_transferUnit : 295 if (percent <= 1) 296 new_supply_state |= CUPS_OPC_LIFE_OVER; 297 else 298 new_supply_state |= CUPS_OPC_NEAR_EOL; 299 break; 300 case CUPS_TC_wasteInk : 301 case CUPS_TC_wastePaper : 302 case CUPS_TC_wasteToner : 303 case CUPS_TC_wasteWater : 304 case CUPS_TC_wasteWax : 305 if (percent <= 1) 306 new_supply_state |= CUPS_WASTE_FULL; 307 else 308 new_supply_state |= CUPS_WASTE_ALMOST_FULL; 309 break; 310 case CUPS_TC_cleanerUnit : 311 case CUPS_TC_fuserCleaningPad : 312 if (percent <= 1) 313 new_supply_state |= CUPS_CLEANER_LIFE_OVER; 314 else 315 new_supply_state |= CUPS_CLEANER_NEAR_EOL; 316 break; 317 } 318 } 319 320 if (i) 321 *ptr++ = ','; 322 323 if ((supplies[i].max_capacity > 0 || (quirks & CUPS_SNMP_CAPACITY)) && 324 supplies[i].level >= 0) 325 snprintf(ptr, sizeof(value) - (size_t)(ptr - value), "%d", percent); 326 else 327 strlcpy(ptr, "-1", sizeof(value) - (size_t)(ptr - value)); 328 } 329 330 fprintf(stderr, "ATTR: marker-levels=%s\n", value); 331 332 if (supply_state < 0) 333 change_state = 0xffff; 334 else 335 change_state = supply_state ^ new_supply_state; 336 337 fprintf(stderr, "DEBUG: new_supply_state=%x, change_state=%x\n", 338 new_supply_state, change_state); 339 340 for (i = 0; 341 i < (int)(sizeof(supply_states) / sizeof(supply_states[0])); 342 i ++) 343 if (change_state & supply_states[i].bit) 344 { 345 fprintf(stderr, "STATE: %c%s\n", 346 (new_supply_state & supply_states[i].bit) ? '+' : '-', 347 supply_states[i].keyword); 348 } 349 350 supply_state = new_supply_state; 351 352 /* 353 * Get the current printer status bits... 354 */ 355 356 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1, 357 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1, 358 hrPrinterDetectedErrorState)) 359 return (-1); 360 361 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) || 362 packet.object_type != CUPS_ASN1_OCTET_STRING) 363 return (-1); 364 365 if (packet.object_value.string.num_bytes == 2) 366 new_state = (packet.object_value.string.bytes[0] << 8) | 367 packet.object_value.string.bytes[1]; 368 else if (packet.object_value.string.num_bytes == 1) 369 new_state = (packet.object_value.string.bytes[0] << 8); 370 else 371 new_state = 0; 372 373 if (current_state < 0) 374 change_state = 0xffff; 375 else 376 change_state = current_state ^ new_state; 377 378 fprintf(stderr, "DEBUG: new_state=%x, change_state=%x\n", new_state, 379 change_state); 380 381 for (i = 0; 382 i < (int)(sizeof(printer_states) / sizeof(printer_states[0])); 383 i ++) 384 if (change_state & printer_states[i].bit) 385 { 386 fprintf(stderr, "STATE: %c%s\n", 387 (new_state & printer_states[i].bit) ? '+' : '-', 388 printer_states[i].keyword); 389 } 390 391 current_state = new_state; 392 393 /* 394 * Get the current printer state... 395 */ 396 397 if (printer_state) 398 { 399 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1, 400 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1, 401 hrPrinterStatus)) 402 return (-1); 403 404 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) || 405 packet.object_type != CUPS_ASN1_INTEGER) 406 return (-1); 407 408 *printer_state = packet.object_value.integer; 409 } 410 411 /* 412 * Get the current page count... 413 */ 414 415 if (page_count) 416 { 417 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1, 418 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1, 419 prtMarkerLifeCount)) 420 return (-1); 421 422 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) || 423 packet.object_type != CUPS_ASN1_COUNTER) 424 return (-1); 425 426 *page_count = packet.object_value.counter; 427 } 428 429 return (0); 430 } 431 else 432 return (-1); 433} 434 435 436/* 437 * 'backend_init_supplies()' - Initialize the supplies list. 438 */ 439 440static void 441backend_init_supplies( 442 int snmp_fd, /* I - SNMP socket */ 443 http_addr_t *addr) /* I - Printer address */ 444{ 445 int i, /* Looping var */ 446 type; /* Current marker type */ 447 cups_file_t *cachefile; /* Cache file */ 448 const char *cachedir; /* CUPS_CACHEDIR value */ 449 char addrstr[1024], /* Address string */ 450 cachefilename[1024], /* Cache filename */ 451 description[CUPS_SNMP_MAX_STRING], 452 /* Device description string */ 453 value[CUPS_MAX_SUPPLIES * (CUPS_SNMP_MAX_STRING * 4 + 3)], 454 /* Value string */ 455 *ptr, /* Pointer into value string */ 456 *name_ptr; /* Pointer into name string */ 457 cups_snmp_t packet; /* SNMP response packet */ 458 ppd_file_t *ppd; /* PPD file for this queue */ 459 ppd_attr_t *ppdattr; /* cupsSNMPSupplies attribute */ 460 static const char * const types[] = /* Supply types */ 461 { 462 "other", 463 "unknown", 464 "toner", 465 "waste-toner", 466 "ink", 467 "ink-cartridge", 468 "ink-ribbon", 469 "waste-ink", 470 "opc", 471 "developer", 472 "fuser-oil", 473 "solid-wax", 474 "ribbon-wax", 475 "waste-wax", 476 "fuser", 477 "corona-wire", 478 "fuser-oil-wick", 479 "cleaner-unit", 480 "fuser-cleaning-pad", 481 "transfer-unit", 482 "toner-cartridge", 483 "fuser-oiler", 484 "water", 485 "waste-water", 486 "glue-water-additive", 487 "waste-paper", 488 "binding-supply", 489 "banding-supply", 490 "stitching-wire", 491 "shrink-wrap", 492 "paper-wrap", 493 "staples", 494 "inserts", 495 "covers" 496 }; 497 498 499 /* 500 * Reset state information... 501 */ 502 503 current_addr = *addr; 504 current_state = -1; 505 num_supplies = -1; 506 charset = -1; 507 508 memset(supplies, 0, sizeof(supplies)); 509 510 /* 511 * See if we should be getting supply levels via SNMP... 512 */ 513 514 if ((ppd = ppdOpenFile(getenv("PPD"))) == NULL || 515 ((ppdattr = ppdFindAttr(ppd, "cupsSNMPSupplies", NULL)) != NULL && 516 ppdattr->value && _cups_strcasecmp(ppdattr->value, "true"))) 517 { 518 ppdClose(ppd); 519 return; 520 } 521 522 if ((ppdattr = ppdFindAttr(ppd, "cupsSNMPQuirks", NULL)) != NULL) 523 { 524 if (!_cups_strcasecmp(ppdattr->value, "capacity")) 525 quirks |= CUPS_SNMP_CAPACITY; 526 } 527 528 ppdClose(ppd); 529 530 /* 531 * Get the device description... 532 */ 533 534 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1, 535 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1, 536 hrDeviceDescr)) 537 return; 538 539 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) || 540 packet.object_type != CUPS_ASN1_OCTET_STRING) 541 { 542 strlcpy(description, "Unknown", sizeof(description)); 543 num_supplies = 0; 544 } 545 else 546 strlcpy(description, (char *)packet.object_value.string.bytes, 547 sizeof(description)); 548 549 fprintf(stderr, "DEBUG2: hrDeviceDesc=\"%s\"\n", description); 550 551 /* 552 * See if we have already queried this device... 553 */ 554 555 httpAddrString(addr, addrstr, sizeof(addrstr)); 556 557 if ((cachedir = getenv("CUPS_CACHEDIR")) == NULL) 558 cachedir = CUPS_CACHEDIR; 559 560 snprintf(cachefilename, sizeof(cachefilename), "%s/%s.snmp", cachedir, 561 addrstr); 562 563 if ((cachefile = cupsFileOpen(cachefilename, "r")) != NULL) 564 { 565 /* 566 * Yes, read the cache file: 567 * 568 * 3 num_supplies charset 569 * device description 570 * supply structures... 571 */ 572 573 if (cupsFileGets(cachefile, value, sizeof(value))) 574 { 575 if (sscanf(value, "3 %d%d", &num_supplies, &charset) == 2 && 576 num_supplies <= CUPS_MAX_SUPPLIES && 577 cupsFileGets(cachefile, value, sizeof(value))) 578 { 579 if (!strcmp(description, value)) 580 cupsFileRead(cachefile, (char *)supplies, 581 (size_t)num_supplies * sizeof(backend_supplies_t)); 582 else 583 { 584 num_supplies = -1; 585 charset = -1; 586 } 587 } 588 else 589 { 590 num_supplies = -1; 591 charset = -1; 592 } 593 } 594 595 cupsFileClose(cachefile); 596 } 597 598 /* 599 * If the cache information isn't correct, scan for supplies... 600 */ 601 602 if (charset < 0) 603 { 604 /* 605 * Get the configured character set... 606 */ 607 608 int oid[CUPS_SNMP_MAX_OID]; /* OID for character set */ 609 610 611 if (!_cupsSNMPWrite(snmp_fd, ¤t_addr, CUPS_SNMP_VERSION_1, 612 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1, 613 prtGeneralCurrentLocalization)) 614 return; 615 616 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) || 617 packet.object_type != CUPS_ASN1_INTEGER) 618 { 619 fprintf(stderr, 620 "DEBUG: prtGeneralCurrentLocalization type is %x, expected %x!\n", 621 packet.object_type, CUPS_ASN1_INTEGER); 622 return; 623 } 624 625 fprintf(stderr, "DEBUG2: prtGeneralCurrentLocalization=%d\n", 626 packet.object_value.integer); 627 628 _cupsSNMPCopyOID(oid, prtLocalizationCharacterSet, CUPS_SNMP_MAX_OID); 629 oid[prtLocalizationCharacterSetOffset - 2] = packet.object_value.integer; 630 631 632 if (!_cupsSNMPWrite(snmp_fd, ¤t_addr, CUPS_SNMP_VERSION_1, 633 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1, 634 oid)) 635 return; 636 637 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) || 638 packet.object_type != CUPS_ASN1_INTEGER) 639 { 640 fprintf(stderr, 641 "DEBUG: prtLocalizationCharacterSet type is %x, expected %x!\n", 642 packet.object_type, CUPS_ASN1_INTEGER); 643 return; 644 } 645 646 fprintf(stderr, "DEBUG2: prtLocalizationCharacterSet=%d\n", 647 packet.object_value.integer); 648 charset = packet.object_value.integer; 649 } 650 651 if (num_supplies < 0) 652 { 653 /* 654 * Walk the printer configuration information... 655 */ 656 657 _cupsSNMPWalk(snmp_fd, ¤t_addr, CUPS_SNMP_VERSION_1, 658 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesEntry, 659 CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL); 660 } 661 662 /* 663 * Save the cached information... 664 */ 665 666 if (num_supplies < 0) 667 num_supplies = 0; 668 669 if ((cachefile = cupsFileOpen(cachefilename, "w")) != NULL) 670 { 671 cupsFilePrintf(cachefile, "3 %d %d\n", num_supplies, charset); 672 cupsFilePrintf(cachefile, "%s\n", description); 673 674 if (num_supplies > 0) 675 cupsFileWrite(cachefile, (char *)supplies, 676 (size_t)num_supplies * sizeof(backend_supplies_t)); 677 678 cupsFileClose(cachefile); 679 } 680 681 if (num_supplies <= 0) 682 return; 683 684 /* 685 * Get the colors... 686 */ 687 688 for (i = 0; i < num_supplies; i ++) 689 strlcpy(supplies[i].color, "none", sizeof(supplies[i].color)); 690 691 _cupsSNMPWalk(snmp_fd, ¤t_addr, CUPS_SNMP_VERSION_1, 692 _cupsSNMPDefaultCommunity(), prtMarkerColorantValue, 693 CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL); 694 695 /* 696 * Output the marker-colors attribute... 697 */ 698 699 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr)) 700 { 701 if (i) 702 *ptr++ = ','; 703 704 strlcpy(ptr, supplies[i].color, sizeof(value) - (size_t)(ptr - value)); 705 } 706 707 fprintf(stderr, "ATTR: marker-colors=%s\n", value); 708 709 /* 710 * Output the marker-names attribute (the double quoting is necessary to deal 711 * with embedded quotes and commas in the marker names...) 712 */ 713 714 for (i = 0, ptr = value; i < num_supplies; i ++) 715 { 716 if (i) 717 *ptr++ = ','; 718 719 *ptr++ = '\''; 720 *ptr++ = '\"'; 721 for (name_ptr = supplies[i].name; *name_ptr;) 722 { 723 if (*name_ptr == '\\' || *name_ptr == '\"' || *name_ptr == '\'') 724 { 725 *ptr++ = '\\'; 726 *ptr++ = '\\'; 727 *ptr++ = '\\'; 728 } 729 730 *ptr++ = *name_ptr++; 731 } 732 *ptr++ = '\"'; 733 *ptr++ = '\''; 734 } 735 736 *ptr = '\0'; 737 738 fprintf(stderr, "ATTR: marker-names=%s\n", value); 739 740 /* 741 * Output the marker-types attribute... 742 */ 743 744 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr)) 745 { 746 if (i) 747 *ptr++ = ','; 748 749 type = supplies[i].type; 750 751 if (type < CUPS_TC_other || type > CUPS_TC_covers) 752 strlcpy(ptr, "unknown", sizeof(value) - (size_t)(ptr - value)); 753 else 754 strlcpy(ptr, types[type - CUPS_TC_other], sizeof(value) - (size_t)(ptr - value)); 755 } 756 757 fprintf(stderr, "ATTR: marker-types=%s\n", value); 758} 759 760 761/* 762 * 'backend_walk_cb()' - Interpret the supply value responses. 763 */ 764 765static void 766backend_walk_cb(cups_snmp_t *packet, /* I - SNMP packet */ 767 void *data) /* I - User data (unused) */ 768{ 769 int i, j, k; /* Looping vars */ 770 static const char * const colors[][2] = 771 { /* Standard color names */ 772 { "black", "#000000" }, 773 { "blue", "#0000FF" }, 774 { "brown", "#A52A2A" }, 775 { "cyan", "#00FFFF" }, 776 { "dark-gray", "#404040" }, 777 { "dark gray", "#404040" }, 778 { "dark-yellow", "#FFCC00" }, 779 { "dark yellow", "#FFCC00" }, 780 { "gold", "#FFD700" }, 781 { "gray", "#808080" }, 782 { "green", "#00FF00" }, 783 { "light-black", "#606060" }, 784 { "light black", "#606060" }, 785 { "light-cyan", "#E0FFFF" }, 786 { "light cyan", "#E0FFFF" }, 787 { "light-gray", "#D3D3D3" }, 788 { "light gray", "#D3D3D3" }, 789 { "light-magenta", "#FF77FF" }, 790 { "light magenta", "#FF77FF" }, 791 { "magenta", "#FF00FF" }, 792 { "orange", "#FFA500" }, 793 { "red", "#FF0000" }, 794 { "silver", "#C0C0C0" }, 795 { "white", "#FFFFFF" }, 796 { "yellow", "#FFFF00" } 797 }; 798 799 800 (void)data; 801 802 if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerColorantValue) && 803 packet->object_type == CUPS_ASN1_OCTET_STRING) 804 { 805 /* 806 * Get colorant... 807 */ 808 809 i = packet->object_name[prtMarkerColorantValueOffset]; 810 811 fprintf(stderr, "DEBUG2: prtMarkerColorantValue.1.%d = \"%s\"\n", i, 812 (char *)packet->object_value.string.bytes); 813 814 for (j = 0; j < num_supplies; j ++) 815 if (supplies[j].colorant == i) 816 { 817 for (k = 0; k < (int)(sizeof(colors) / sizeof(colors[0])); k ++) 818 if (!_cups_strcasecmp(colors[k][0], 819 (char *)packet->object_value.string.bytes)) 820 { 821 strlcpy(supplies[j].color, colors[k][1], sizeof(supplies[j].color)); 822 break; 823 } 824 } 825 } 826 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesColorantIndex)) 827 { 828 /* 829 * Get colorant index... 830 */ 831 832 i = packet->object_name[prtMarkerSuppliesColorantIndexOffset]; 833 if (i < 1 || i > CUPS_MAX_SUPPLIES || 834 packet->object_type != CUPS_ASN1_INTEGER) 835 return; 836 837 fprintf(stderr, "DEBUG2: prtMarkerSuppliesColorantIndex.1.%d = %d\n", i, 838 packet->object_value.integer); 839 840 if (i > num_supplies) 841 num_supplies = i; 842 843 supplies[i - 1].colorant = packet->object_value.integer; 844 } 845 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesDescription)) 846 { 847 /* 848 * Get supply name/description... 849 */ 850 851 i = packet->object_name[prtMarkerSuppliesDescriptionOffset]; 852 if (i < 1 || i > CUPS_MAX_SUPPLIES || 853 packet->object_type != CUPS_ASN1_OCTET_STRING) 854 return; 855 856 if (i > num_supplies) 857 num_supplies = i; 858 859 switch (charset) 860 { 861 case CUPS_TC_csASCII : 862 case CUPS_TC_csUTF8 : 863 case CUPS_TC_csUnicodeASCII : 864 strlcpy(supplies[i - 1].name, 865 (char *)packet->object_value.string.bytes, 866 sizeof(supplies[0].name)); 867 break; 868 869 case CUPS_TC_csISOLatin1 : 870 case CUPS_TC_csUnicodeLatin1 : 871 cupsCharsetToUTF8((cups_utf8_t *)supplies[i - 1].name, 872 (char *)packet->object_value.string.bytes, 873 sizeof(supplies[0].name), CUPS_ISO8859_1); 874 break; 875 876 case CUPS_TC_csShiftJIS : 877 case CUPS_TC_csWindows31J : /* Close enough for our purposes */ 878 cupsCharsetToUTF8((cups_utf8_t *)supplies[i - 1].name, 879 (char *)packet->object_value.string.bytes, 880 sizeof(supplies[0].name), CUPS_JIS_X0213); 881 break; 882 883 case CUPS_TC_csUCS4 : 884 case CUPS_TC_csUTF32 : 885 case CUPS_TC_csUTF32BE : 886 case CUPS_TC_csUTF32LE : 887 cupsUTF32ToUTF8((cups_utf8_t *)supplies[i - 1].name, 888 (cups_utf32_t *)packet->object_value.string.bytes, 889 sizeof(supplies[0].name)); 890 break; 891 892 case CUPS_TC_csUnicode : 893 case CUPS_TC_csUTF16BE : 894 case CUPS_TC_csUTF16LE : 895 utf16_to_utf8((cups_utf8_t *)supplies[i - 1].name, 896 packet->object_value.string.bytes, 897 packet->object_value.string.num_bytes, 898 sizeof(supplies[0].name), charset == CUPS_TC_csUTF16LE); 899 break; 900 901 default : 902 /* 903 * If we get here, the printer is using an unknown character set and 904 * we just want to copy characters that look like ASCII... 905 */ 906 907 { 908 char *src, *dst; /* Pointers into strings */ 909 910 /* 911 * Loop safe because both the object_value and supplies char arrays 912 * are CUPS_SNMP_MAX_STRING elements long. 913 */ 914 915 for (src = (char *)packet->object_value.string.bytes, 916 dst = supplies[i - 1].name; 917 *src; 918 src ++) 919 { 920 if ((*src & 0x80) || *src < ' ' || *src == 0x7f) 921 *dst++ = '?'; 922 else 923 *dst++ = *src; 924 } 925 926 *dst = '\0'; 927 } 928 break; 929 } 930 931 fprintf(stderr, "DEBUG2: prtMarkerSuppliesDescription.1.%d = \"%s\"\n", i, 932 supplies[i - 1].name); 933 934 } 935 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesLevel)) 936 { 937 /* 938 * Get level... 939 */ 940 941 i = packet->object_name[prtMarkerSuppliesLevelOffset]; 942 if (i < 1 || i > CUPS_MAX_SUPPLIES || 943 packet->object_type != CUPS_ASN1_INTEGER) 944 return; 945 946 fprintf(stderr, "DEBUG2: prtMarkerSuppliesLevel.1.%d = %d\n", i, 947 packet->object_value.integer); 948 949 if (i > num_supplies) 950 num_supplies = i; 951 952 supplies[i - 1].level = packet->object_value.integer; 953 } 954 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesMaxCapacity) && 955 !(quirks & CUPS_SNMP_CAPACITY)) 956 { 957 /* 958 * Get max capacity... 959 */ 960 961 i = packet->object_name[prtMarkerSuppliesMaxCapacityOffset]; 962 if (i < 1 || i > CUPS_MAX_SUPPLIES || 963 packet->object_type != CUPS_ASN1_INTEGER) 964 return; 965 966 fprintf(stderr, "DEBUG2: prtMarkerSuppliesMaxCapacity.1.%d = %d\n", i, 967 packet->object_value.integer); 968 969 if (i > num_supplies) 970 num_supplies = i; 971 972 if (supplies[i - 1].max_capacity == 0 && 973 packet->object_value.integer > 0) 974 supplies[i - 1].max_capacity = packet->object_value.integer; 975 } 976 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesClass)) 977 { 978 /* 979 * Get marker class... 980 */ 981 982 i = packet->object_name[prtMarkerSuppliesClassOffset]; 983 if (i < 1 || i > CUPS_MAX_SUPPLIES || 984 packet->object_type != CUPS_ASN1_INTEGER) 985 return; 986 987 fprintf(stderr, "DEBUG2: prtMarkerSuppliesClass.1.%d = %d\n", i, 988 packet->object_value.integer); 989 990 if (i > num_supplies) 991 num_supplies = i; 992 993 supplies[i - 1].sclass = packet->object_value.integer; 994 } 995 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesType)) 996 { 997 /* 998 * Get marker type... 999 */ 1000 1001 i = packet->object_name[prtMarkerSuppliesTypeOffset]; 1002 if (i < 1 || i > CUPS_MAX_SUPPLIES || 1003 packet->object_type != CUPS_ASN1_INTEGER) 1004 return; 1005 1006 fprintf(stderr, "DEBUG2: prtMarkerSuppliesType.1.%d = %d\n", i, 1007 packet->object_value.integer); 1008 1009 if (i > num_supplies) 1010 num_supplies = i; 1011 1012 supplies[i - 1].type = packet->object_value.integer; 1013 } 1014 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesSupplyUnit)) 1015 { 1016 /* 1017 * Get units for capacity... 1018 */ 1019 1020 i = packet->object_name[prtMarkerSuppliesSupplyUnitOffset]; 1021 if (i < 1 || i > CUPS_MAX_SUPPLIES || 1022 packet->object_type != CUPS_ASN1_INTEGER) 1023 return; 1024 1025 fprintf(stderr, "DEBUG2: prtMarkerSuppliesSupplyUnit.1.%d = %d\n", i, 1026 packet->object_value.integer); 1027 1028 if (i > num_supplies) 1029 num_supplies = i; 1030 1031 if (packet->object_value.integer == CUPS_TC_percent) 1032 supplies[i - 1].max_capacity = 100; 1033 } 1034} 1035 1036 1037/* 1038 * 'utf16_to_utf8()' - Convert UTF-16 text to UTF-8. 1039 */ 1040 1041static void 1042utf16_to_utf8( 1043 cups_utf8_t *dst, /* I - Destination buffer */ 1044 const unsigned char *src, /* I - Source string */ 1045 size_t srcsize, /* I - Size of source string */ 1046 size_t dstsize, /* I - Size of destination buffer */ 1047 int le) /* I - Source is little-endian? */ 1048{ 1049 cups_utf32_t ch, /* Current character */ 1050 temp[CUPS_SNMP_MAX_STRING], 1051 /* UTF-32 string */ 1052 *ptr; /* Pointer into UTF-32 string */ 1053 1054 1055 for (ptr = temp; srcsize >= 2;) 1056 { 1057 if (le) 1058 ch = (cups_utf32_t)(src[0] | (src[1] << 8)); 1059 else 1060 ch = (cups_utf32_t)((src[0] << 8) | src[1]); 1061 1062 src += 2; 1063 srcsize -= 2; 1064 1065 if (ch >= 0xd800 && ch <= 0xdbff && srcsize >= 2) 1066 { 1067 /* 1068 * Multi-word UTF-16 char... 1069 */ 1070 1071 cups_utf32_t lch; /* Lower word */ 1072 1073 1074 if (le) 1075 lch = (cups_utf32_t)(src[0] | (src[1] << 8)); 1076 else 1077 lch = (cups_utf32_t)((src[0] << 8) | src[1]); 1078 1079 if (lch >= 0xdc00 && lch <= 0xdfff) 1080 { 1081 src += 2; 1082 srcsize -= 2; 1083 1084 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; 1085 } 1086 } 1087 1088 if (ptr < (temp + CUPS_SNMP_MAX_STRING - 1)) 1089 *ptr++ = ch; 1090 } 1091 1092 *ptr = '\0'; 1093 1094 cupsUTF32ToUTF8(dst, temp, (int)dstsize); 1095} 1096 1097 1098/* 1099 * End of "$Id: snmp-supplies.c 11560 2014-02-06 20:10:19Z msweet $". 1100 */ 1101