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