/* * SNMP supplies functions for CUPS. * * Copyright © 2008-2015 by Apple Inc. * * Licensed under Apache License v2.0. See the file "LICENSE" for more * information. */ /* * Include necessary headers. */ #include "backend-private.h" #include #include /* * Local constants... */ #define CUPS_MAX_SUPPLIES 32 /* Maximum number of supplies for a printer */ #define CUPS_SUPPLY_TIMEOUT 2.0 /* Timeout for SNMP lookups */ #define CUPS_DEVELOPER_LOW 0x0001 #define CUPS_DEVELOPER_EMPTY 0x0002 #define CUPS_MARKER_SUPPLY_LOW 0x0004 #define CUPS_MARKER_SUPPLY_EMPTY 0x0008 #define CUPS_OPC_NEAR_EOL 0x0010 #define CUPS_OPC_LIFE_OVER 0x0020 #define CUPS_TONER_LOW 0x0040 #define CUPS_TONER_EMPTY 0x0080 #define CUPS_WASTE_ALMOST_FULL 0x0100 #define CUPS_WASTE_FULL 0x0200 #define CUPS_CLEANER_NEAR_EOL 0x0400 /* Proposed JPS3 */ #define CUPS_CLEANER_LIFE_OVER 0x0800 /* Proposed JPS3 */ #define CUPS_SNMP_NONE 0x0000 #define CUPS_SNMP_CAPACITY 0x0001 /* Supply levels reported as percentages */ /* * Local structures... */ typedef struct /**** Printer supply data ****/ { char name[CUPS_SNMP_MAX_STRING], /* Name of supply */ color[8]; /* Color: "#RRGGBB" or "none" */ int colorant, /* Colorant index */ sclass, /* Supply class */ type, /* Supply type */ max_capacity, /* Maximum capacity */ level; /* Current level value */ } backend_supplies_t; typedef struct /**** Printer state table ****/ { int bit; /* State bit */ const char *keyword; /* IPP printer-state-reasons keyword */ } backend_state_t; /* * Local globals... */ static http_addr_t current_addr; /* Current address */ static int current_state = -1; /* Current device state bits */ static int charset = -1; /* Character set for supply names */ static unsigned quirks = CUPS_SNMP_NONE; /* Quirks we have to work around */ static int num_supplies = 0; /* Number of supplies found */ static backend_supplies_t supplies[CUPS_MAX_SUPPLIES]; /* Supply information */ static int supply_state = -1; /* Supply state info */ static const int hrDeviceDescr[] = { CUPS_OID_hrDeviceDescr, 1, -1 }; /* Device description OID */ static const int hrPrinterStatus[] = { CUPS_OID_hrPrinterStatus, 1, -1 }; /* Current state OID */ static const int hrPrinterDetectedErrorState[] = { CUPS_OID_hrPrinterDetectedErrorState, 1, -1 }; /* Current printer state bits OID */ static const int prtGeneralCurrentLocalization[] = { CUPS_OID_prtGeneralCurrentLocalization, 1, -1 }; static const int prtLocalizationCharacterSet[] = { CUPS_OID_prtLocalizationCharacterSet, 1, 1, -1 }, prtLocalizationCharacterSetOffset = (sizeof(prtLocalizationCharacterSet) / sizeof(prtLocalizationCharacterSet[0])); static const int prtMarkerColorantValue[] = { CUPS_OID_prtMarkerColorantValue, -1 }, /* Colorant OID */ prtMarkerColorantValueOffset = (sizeof(prtMarkerColorantValue) / sizeof(prtMarkerColorantValue[0])); /* Offset to colorant index */ static const int prtMarkerLifeCount[] = { CUPS_OID_prtMarkerLifeCount, 1, 1, -1 }; /* Page counter OID */ static const int prtMarkerSuppliesEntry[] = { CUPS_OID_prtMarkerSuppliesEntry, -1 }; /* Supplies OID */ static const int prtMarkerSuppliesColorantIndex[] = { CUPS_OID_prtMarkerSuppliesColorantIndex, -1 }, /* Colorant index OID */ prtMarkerSuppliesColorantIndexOffset = (sizeof(prtMarkerSuppliesColorantIndex) / sizeof(prtMarkerSuppliesColorantIndex[0])); /* Offset to supply index */ static const int prtMarkerSuppliesDescription[] = { CUPS_OID_prtMarkerSuppliesDescription, -1 }, /* Description OID */ prtMarkerSuppliesDescriptionOffset = (sizeof(prtMarkerSuppliesDescription) / sizeof(prtMarkerSuppliesDescription[0])); /* Offset to supply index */ static const int prtMarkerSuppliesLevel[] = { CUPS_OID_prtMarkerSuppliesLevel, -1 }, /* Level OID */ prtMarkerSuppliesLevelOffset = (sizeof(prtMarkerSuppliesLevel) / sizeof(prtMarkerSuppliesLevel[0])); /* Offset to supply index */ static const int prtMarkerSuppliesMaxCapacity[] = { CUPS_OID_prtMarkerSuppliesMaxCapacity, -1 }, /* Max capacity OID */ prtMarkerSuppliesMaxCapacityOffset = (sizeof(prtMarkerSuppliesMaxCapacity) / sizeof(prtMarkerSuppliesMaxCapacity[0])); /* Offset to supply index */ static const int prtMarkerSuppliesClass[] = { CUPS_OID_prtMarkerSuppliesClass, -1 }, /* Class OID */ prtMarkerSuppliesClassOffset = (sizeof(prtMarkerSuppliesClass) / sizeof(prtMarkerSuppliesClass[0])); /* Offset to supply index */ static const int prtMarkerSuppliesType[] = { CUPS_OID_prtMarkerSuppliesType, -1 }, /* Type OID */ prtMarkerSuppliesTypeOffset = (sizeof(prtMarkerSuppliesType) / sizeof(prtMarkerSuppliesType[0])); /* Offset to supply index */ static const int prtMarkerSuppliesSupplyUnit[] = { CUPS_OID_prtMarkerSuppliesSupplyUnit, -1 }, /* Units OID */ prtMarkerSuppliesSupplyUnitOffset = (sizeof(prtMarkerSuppliesSupplyUnit) / sizeof(prtMarkerSuppliesSupplyUnit[0])); /* Offset to supply index */ static const backend_state_t printer_states[] = { /* { CUPS_TC_lowPaper, "media-low-report" }, */ { CUPS_TC_noPaper | CUPS_TC_inputTrayEmpty, "media-empty-warning" }, /* { CUPS_TC_lowToner, "toner-low-report" }, */ /* now use prtMarkerSupplies */ /* { CUPS_TC_noToner, "toner-empty-warning" }, */ /* now use prtMarkerSupplies */ { CUPS_TC_doorOpen, "door-open-report" }, { CUPS_TC_jammed, "media-jam-warning" }, /* { CUPS_TC_offline, "offline-report" }, */ /* unreliable */ /* { CUPS_TC_serviceRequested | CUPS_TC_overduePreventMaint, "service-needed-warning" }, */ /* unreliable */ { CUPS_TC_inputTrayMissing, "input-tray-missing-warning" }, { CUPS_TC_outputTrayMissing, "output-tray-missing-warning" }, { CUPS_TC_markerSupplyMissing, "marker-supply-missing-warning" }, { CUPS_TC_outputNearFull, "output-area-almost-full-report" }, { CUPS_TC_outputFull, "output-area-full-warning" } }; static const backend_state_t supply_states[] = { { CUPS_DEVELOPER_LOW, "developer-low-report" }, { CUPS_DEVELOPER_EMPTY, "developer-empty-warning" }, { CUPS_MARKER_SUPPLY_LOW, "marker-supply-low-report" }, { CUPS_MARKER_SUPPLY_EMPTY, "marker-supply-empty-warning" }, { CUPS_OPC_NEAR_EOL, "opc-near-eol-report" }, { CUPS_OPC_LIFE_OVER, "opc-life-over-warning" }, { CUPS_TONER_LOW, "toner-low-report" }, { CUPS_TONER_EMPTY, "toner-empty-warning" }, { CUPS_WASTE_ALMOST_FULL, "waste-receptacle-almost-full-report" }, { CUPS_WASTE_FULL, "waste-receptacle-full-warning" }, { CUPS_CLEANER_NEAR_EOL, "cleaner-life-almost-over-report" }, { CUPS_CLEANER_LIFE_OVER, "cleaner-life-over-warning" }, }; /* * Local functions... */ static void backend_init_supplies(int snmp_fd, http_addr_t *addr); static void backend_walk_cb(cups_snmp_t *packet, void *data); static void utf16_to_utf8(cups_utf8_t *dst, const unsigned char *src, size_t srcsize, size_t dstsize, int le); /* * 'backendSNMPSupplies()' - Get the current supplies for a device. */ int /* O - 0 on success, -1 on error */ backendSNMPSupplies( int snmp_fd, /* I - SNMP socket */ http_addr_t *addr, /* I - Printer address */ int *page_count, /* O - Page count */ int *printer_state) /* O - Printer state */ { if (!httpAddrEqual(addr, ¤t_addr)) backend_init_supplies(snmp_fd, addr); else if (num_supplies > 0) _cupsSNMPWalk(snmp_fd, ¤t_addr, CUPS_SNMP_VERSION_1, _cupsSNMPDefaultCommunity(), prtMarkerSuppliesLevel, CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL); if (page_count) *page_count = -1; if (printer_state) *printer_state = -1; if (num_supplies > 0) { int i, /* Looping var */ percent, /* Percent full */ new_state, /* New state value */ change_state, /* State change */ new_supply_state = 0; /* Supply state */ char value[CUPS_MAX_SUPPLIES * 4], /* marker-levels value string */ *ptr; /* Pointer into value string */ cups_snmp_t packet; /* SNMP response packet */ /* * Generate the marker-levels value string... */ for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr)) { if (supplies[i].max_capacity > 0 && supplies[i].level >= 0) percent = 100 * supplies[i].level / supplies[i].max_capacity; else if (supplies[i].level >= 0 && supplies[i].level <= 100 && (quirks & CUPS_SNMP_CAPACITY)) percent = supplies[i].level; else percent = 50; if (supplies[i].sclass == CUPS_TC_receptacleThatIsFilled) percent = 100 - percent; if (percent <= 5) { switch (supplies[i].type) { case CUPS_TC_toner : case CUPS_TC_tonerCartridge : if (percent <= 1) new_supply_state |= CUPS_TONER_EMPTY; else new_supply_state |= CUPS_TONER_LOW; break; case CUPS_TC_ink : case CUPS_TC_inkCartridge : case CUPS_TC_inkRibbon : case CUPS_TC_solidWax : case CUPS_TC_ribbonWax : if (percent <= 1) new_supply_state |= CUPS_MARKER_SUPPLY_EMPTY; else new_supply_state |= CUPS_MARKER_SUPPLY_LOW; break; case CUPS_TC_developer : if (percent <= 1) new_supply_state |= CUPS_DEVELOPER_EMPTY; else new_supply_state |= CUPS_DEVELOPER_LOW; break; case CUPS_TC_coronaWire : case CUPS_TC_fuser : case CUPS_TC_opc : case CUPS_TC_transferUnit : if (percent <= 1) new_supply_state |= CUPS_OPC_LIFE_OVER; else new_supply_state |= CUPS_OPC_NEAR_EOL; break; #if 0 /* Because no two vendors report waste containers the same, disable SNMP reporting of same */ case CUPS_TC_wasteInk : case CUPS_TC_wastePaper : case CUPS_TC_wasteToner : case CUPS_TC_wasteWater : case CUPS_TC_wasteWax : if (percent <= 1) new_supply_state |= CUPS_WASTE_FULL; else new_supply_state |= CUPS_WASTE_ALMOST_FULL; break; #endif /* 0 */ case CUPS_TC_cleanerUnit : case CUPS_TC_fuserCleaningPad : if (percent <= 1) new_supply_state |= CUPS_CLEANER_LIFE_OVER; else new_supply_state |= CUPS_CLEANER_NEAR_EOL; break; } } if (i) *ptr++ = ','; if ((supplies[i].max_capacity > 0 || (quirks & CUPS_SNMP_CAPACITY)) && supplies[i].level >= 0) snprintf(ptr, sizeof(value) - (size_t)(ptr - value), "%d", percent); else strlcpy(ptr, "-1", sizeof(value) - (size_t)(ptr - value)); } fprintf(stderr, "ATTR: marker-levels=%s\n", value); if (supply_state < 0) change_state = 0xffff; else change_state = supply_state ^ new_supply_state; fprintf(stderr, "DEBUG: new_supply_state=%x, change_state=%x\n", new_supply_state, change_state); for (i = 0; i < (int)(sizeof(supply_states) / sizeof(supply_states[0])); i ++) if (change_state & supply_states[i].bit) { fprintf(stderr, "STATE: %c%s\n", (new_supply_state & supply_states[i].bit) ? '+' : '-', supply_states[i].keyword); } supply_state = new_supply_state; /* * Get the current printer status bits... */ if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1, _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1, hrPrinterDetectedErrorState)) return (-1); if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) || packet.object_type != CUPS_ASN1_OCTET_STRING) return (-1); if (packet.object_value.string.num_bytes == 2) new_state = (packet.object_value.string.bytes[0] << 8) | packet.object_value.string.bytes[1]; else if (packet.object_value.string.num_bytes == 1) new_state = (packet.object_value.string.bytes[0] << 8); else new_state = 0; if (current_state < 0) change_state = 0xffff; else change_state = current_state ^ new_state; fprintf(stderr, "DEBUG: new_state=%x, change_state=%x\n", new_state, change_state); for (i = 0; i < (int)(sizeof(printer_states) / sizeof(printer_states[0])); i ++) if (change_state & printer_states[i].bit) { fprintf(stderr, "STATE: %c%s\n", (new_state & printer_states[i].bit) ? '+' : '-', printer_states[i].keyword); } current_state = new_state; /* * Get the current printer state... */ if (printer_state) { if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1, _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1, hrPrinterStatus)) return (-1); if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) || packet.object_type != CUPS_ASN1_INTEGER) return (-1); *printer_state = packet.object_value.integer; } /* * Get the current page count... */ if (page_count) { if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1, _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1, prtMarkerLifeCount)) return (-1); if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) || packet.object_type != CUPS_ASN1_COUNTER) return (-1); *page_count = packet.object_value.counter; } return (0); } else return (-1); } /* * 'backend_init_supplies()' - Initialize the supplies list. */ static void backend_init_supplies( int snmp_fd, /* I - SNMP socket */ http_addr_t *addr) /* I - Printer address */ { int i, /* Looping var */ type; /* Current marker type */ const char *community; /* SNMP community name */ cups_file_t *cachefile; /* Cache file */ const char *cachedir; /* CUPS_CACHEDIR value */ char addrstr[1024], /* Address string */ cachefilename[1024], /* Cache filename */ description[CUPS_SNMP_MAX_STRING], /* Device description string */ value[CUPS_MAX_SUPPLIES * (CUPS_SNMP_MAX_STRING * 4 + 3)], /* Value string */ *ptr, /* Pointer into value string */ *name_ptr; /* Pointer into name string */ cups_snmp_t packet; /* SNMP response packet */ ppd_file_t *ppd; /* PPD file for this queue */ ppd_attr_t *ppdattr; /* cupsSNMPSupplies attribute */ static const char * const types[] = /* Supply types */ { "other", "unknown", "toner", "waste-toner", "ink", "ink-cartridge", "ink-ribbon", "waste-ink", "opc", "developer", "fuser-oil", "solid-wax", "ribbon-wax", "waste-wax", "fuser", "corona-wire", "fuser-oil-wick", "cleaner-unit", "fuser-cleaning-pad", "transfer-unit", "toner-cartridge", "fuser-oiler", "water", "waste-water", "glue-water-additive", "waste-paper", "binding-supply", "banding-supply", "stitching-wire", "shrink-wrap", "paper-wrap", "staples", "inserts", "covers" }; /* * Reset state information... */ current_addr = *addr; current_state = -1; num_supplies = -1; charset = -1; memset(supplies, 0, sizeof(supplies)); /* * See if we should be getting supply levels via SNMP... */ community = _cupsSNMPDefaultCommunity(); if (!*community) return; if ((ppd = ppdOpenFile(getenv("PPD"))) == NULL || ((ppdattr = ppdFindAttr(ppd, "cupsSNMPSupplies", NULL)) != NULL && ppdattr->value && _cups_strcasecmp(ppdattr->value, "true"))) { ppdClose(ppd); return; } if ((ppdattr = ppdFindAttr(ppd, "cupsSNMPQuirks", NULL)) != NULL) { if (!_cups_strcasecmp(ppdattr->value, "capacity")) quirks |= CUPS_SNMP_CAPACITY; } ppdClose(ppd); /* * Get the device description... */ if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1, community, CUPS_ASN1_GET_REQUEST, 1, hrDeviceDescr)) return; if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) || packet.object_type != CUPS_ASN1_OCTET_STRING) { strlcpy(description, "Unknown", sizeof(description)); num_supplies = 0; } else strlcpy(description, (char *)packet.object_value.string.bytes, sizeof(description)); fprintf(stderr, "DEBUG2: hrDeviceDesc=\"%s\"\n", description); /* * See if we have already queried this device... */ httpAddrString(addr, addrstr, sizeof(addrstr)); if ((cachedir = getenv("CUPS_CACHEDIR")) == NULL) cachedir = CUPS_CACHEDIR; snprintf(cachefilename, sizeof(cachefilename), "%s/%s.snmp", cachedir, addrstr); if ((cachefile = cupsFileOpen(cachefilename, "r")) != NULL) { /* * Yes, read the cache file: * * 3 num_supplies charset * device description * supply structures... */ if (cupsFileGets(cachefile, value, sizeof(value))) { if (sscanf(value, "3 %d%d", &num_supplies, &charset) == 2 && num_supplies <= CUPS_MAX_SUPPLIES && cupsFileGets(cachefile, value, sizeof(value))) { if (!strcmp(description, value)) cupsFileRead(cachefile, (char *)supplies, (size_t)num_supplies * sizeof(backend_supplies_t)); else { num_supplies = -1; charset = -1; } } else { num_supplies = -1; charset = -1; } } cupsFileClose(cachefile); } /* * If the cache information isn't correct, scan for supplies... */ if (charset < 0) { /* * Get the configured character set... */ int oid[CUPS_SNMP_MAX_OID]; /* OID for character set */ if (!_cupsSNMPWrite(snmp_fd, ¤t_addr, CUPS_SNMP_VERSION_1, community, CUPS_ASN1_GET_REQUEST, 1, prtGeneralCurrentLocalization)) return; if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) || packet.object_type != CUPS_ASN1_INTEGER) { fprintf(stderr, "DEBUG: prtGeneralCurrentLocalization type is %x, expected %x!\n", packet.object_type, CUPS_ASN1_INTEGER); return; } fprintf(stderr, "DEBUG2: prtGeneralCurrentLocalization=%d\n", packet.object_value.integer); _cupsSNMPCopyOID(oid, prtLocalizationCharacterSet, CUPS_SNMP_MAX_OID); oid[prtLocalizationCharacterSetOffset - 2] = packet.object_value.integer; if (!_cupsSNMPWrite(snmp_fd, ¤t_addr, CUPS_SNMP_VERSION_1, community, CUPS_ASN1_GET_REQUEST, 1, oid)) return; if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) || packet.object_type != CUPS_ASN1_INTEGER) { fprintf(stderr, "DEBUG: prtLocalizationCharacterSet type is %x, expected %x!\n", packet.object_type, CUPS_ASN1_INTEGER); return; } fprintf(stderr, "DEBUG2: prtLocalizationCharacterSet=%d\n", packet.object_value.integer); charset = packet.object_value.integer; } if (num_supplies < 0) { /* * Walk the printer configuration information... */ _cupsSNMPWalk(snmp_fd, ¤t_addr, CUPS_SNMP_VERSION_1, community, prtMarkerSuppliesEntry, CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL); } /* * Save the cached information... */ if (num_supplies < 0) num_supplies = 0; if ((cachefile = cupsFileOpen(cachefilename, "w")) != NULL) { cupsFilePrintf(cachefile, "3 %d %d\n", num_supplies, charset); cupsFilePrintf(cachefile, "%s\n", description); if (num_supplies > 0) cupsFileWrite(cachefile, (char *)supplies, (size_t)num_supplies * sizeof(backend_supplies_t)); cupsFileClose(cachefile); } if (num_supplies <= 0) return; /* * Get the colors... */ for (i = 0; i < num_supplies; i ++) strlcpy(supplies[i].color, "none", sizeof(supplies[i].color)); _cupsSNMPWalk(snmp_fd, ¤t_addr, CUPS_SNMP_VERSION_1, community, prtMarkerColorantValue, CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL); /* * Output the marker-colors attribute... */ for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr)) { if (i) *ptr++ = ','; strlcpy(ptr, supplies[i].color, sizeof(value) - (size_t)(ptr - value)); } fprintf(stderr, "ATTR: marker-colors=%s\n", value); /* * Output the marker-names attribute (the double quoting is necessary to deal * with embedded quotes and commas in the marker names...) */ for (i = 0, ptr = value; i < num_supplies; i ++) { if (i) *ptr++ = ','; *ptr++ = '\''; *ptr++ = '\"'; for (name_ptr = supplies[i].name; *name_ptr;) { if (*name_ptr == '\\' || *name_ptr == '\"' || *name_ptr == '\'') { *ptr++ = '\\'; *ptr++ = '\\'; *ptr++ = '\\'; } *ptr++ = *name_ptr++; } *ptr++ = '\"'; *ptr++ = '\''; } *ptr = '\0'; fprintf(stderr, "ATTR: marker-names=%s\n", value); /* * Output the marker-types attribute... */ for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr)) { if (i) *ptr++ = ','; type = supplies[i].type; if (type < CUPS_TC_other || type > CUPS_TC_covers) strlcpy(ptr, "unknown", sizeof(value) - (size_t)(ptr - value)); else strlcpy(ptr, types[type - CUPS_TC_other], sizeof(value) - (size_t)(ptr - value)); } fprintf(stderr, "ATTR: marker-types=%s\n", value); } /* * 'backend_walk_cb()' - Interpret the supply value responses. */ static void backend_walk_cb(cups_snmp_t *packet, /* I - SNMP packet */ void *data) /* I - User data (unused) */ { int i, j, k; /* Looping vars */ static const char * const colors[][2] = { /* Standard color names */ { "black", "#000000" }, { "blue", "#0000FF" }, { "brown", "#A52A2A" }, { "cyan", "#00FFFF" }, { "dark-gray", "#404040" }, { "dark gray", "#404040" }, { "dark-yellow", "#FFCC00" }, { "dark yellow", "#FFCC00" }, { "gold", "#FFD700" }, { "gray", "#808080" }, { "green", "#00FF00" }, { "light-black", "#606060" }, { "light black", "#606060" }, { "light-cyan", "#E0FFFF" }, { "light cyan", "#E0FFFF" }, { "light-gray", "#D3D3D3" }, { "light gray", "#D3D3D3" }, { "light-magenta", "#FF77FF" }, { "light magenta", "#FF77FF" }, { "magenta", "#FF00FF" }, { "orange", "#FFA500" }, { "red", "#FF0000" }, { "silver", "#C0C0C0" }, { "white", "#FFFFFF" }, { "yellow", "#FFFF00" } }; (void)data; if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerColorantValue) && packet->object_type == CUPS_ASN1_OCTET_STRING) { /* * Get colorant... */ i = packet->object_name[prtMarkerColorantValueOffset]; fprintf(stderr, "DEBUG2: prtMarkerColorantValue.1.%d = \"%s\"\n", i, (char *)packet->object_value.string.bytes); for (j = 0; j < num_supplies; j ++) if (supplies[j].colorant == i) { for (k = 0; k < (int)(sizeof(colors) / sizeof(colors[0])); k ++) if (!_cups_strcasecmp(colors[k][0], (char *)packet->object_value.string.bytes)) { strlcpy(supplies[j].color, colors[k][1], sizeof(supplies[j].color)); break; } } } else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesColorantIndex)) { /* * Get colorant index... */ i = packet->object_name[prtMarkerSuppliesColorantIndexOffset]; if (i < 1 || i > CUPS_MAX_SUPPLIES || packet->object_type != CUPS_ASN1_INTEGER) return; fprintf(stderr, "DEBUG2: prtMarkerSuppliesColorantIndex.1.%d = %d\n", i, packet->object_value.integer); if (i > num_supplies) num_supplies = i; supplies[i - 1].colorant = packet->object_value.integer; } else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesDescription)) { /* * Get supply name/description... */ i = packet->object_name[prtMarkerSuppliesDescriptionOffset]; if (i < 1 || i > CUPS_MAX_SUPPLIES || packet->object_type != CUPS_ASN1_OCTET_STRING) return; if (i > num_supplies) num_supplies = i; switch (charset) { case CUPS_TC_csASCII : case CUPS_TC_csUTF8 : case CUPS_TC_csUnicodeASCII : strlcpy(supplies[i - 1].name, (char *)packet->object_value.string.bytes, sizeof(supplies[0].name)); break; case CUPS_TC_csISOLatin1 : case CUPS_TC_csUnicodeLatin1 : cupsCharsetToUTF8((cups_utf8_t *)supplies[i - 1].name, (char *)packet->object_value.string.bytes, sizeof(supplies[0].name), CUPS_ISO8859_1); break; case CUPS_TC_csShiftJIS : case CUPS_TC_csWindows31J : /* Close enough for our purposes */ cupsCharsetToUTF8((cups_utf8_t *)supplies[i - 1].name, (char *)packet->object_value.string.bytes, sizeof(supplies[0].name), CUPS_JIS_X0213); break; case CUPS_TC_csUCS4 : case CUPS_TC_csUTF32 : case CUPS_TC_csUTF32BE : case CUPS_TC_csUTF32LE : cupsUTF32ToUTF8((cups_utf8_t *)supplies[i - 1].name, (cups_utf32_t *)packet->object_value.string.bytes, sizeof(supplies[0].name)); break; case CUPS_TC_csUnicode : case CUPS_TC_csUTF16BE : case CUPS_TC_csUTF16LE : utf16_to_utf8((cups_utf8_t *)supplies[i - 1].name, packet->object_value.string.bytes, packet->object_value.string.num_bytes, sizeof(supplies[0].name), charset == CUPS_TC_csUTF16LE); break; default : /* * If we get here, the printer is using an unknown character set and * we just want to copy characters that look like ASCII... */ { char *src, *dst; /* Pointers into strings */ /* * Loop safe because both the object_value and supplies char arrays * are CUPS_SNMP_MAX_STRING elements long. */ for (src = (char *)packet->object_value.string.bytes, dst = supplies[i - 1].name; *src; src ++) { if ((*src & 0x80) || *src < ' ' || *src == 0x7f) *dst++ = '?'; else *dst++ = *src; } *dst = '\0'; } break; } fprintf(stderr, "DEBUG2: prtMarkerSuppliesDescription.1.%d = \"%s\"\n", i, supplies[i - 1].name); } else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesLevel)) { /* * Get level... */ i = packet->object_name[prtMarkerSuppliesLevelOffset]; if (i < 1 || i > CUPS_MAX_SUPPLIES || packet->object_type != CUPS_ASN1_INTEGER) return; fprintf(stderr, "DEBUG2: prtMarkerSuppliesLevel.1.%d = %d\n", i, packet->object_value.integer); if (i > num_supplies) num_supplies = i; supplies[i - 1].level = packet->object_value.integer; } else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesMaxCapacity) && !(quirks & CUPS_SNMP_CAPACITY)) { /* * Get max capacity... */ i = packet->object_name[prtMarkerSuppliesMaxCapacityOffset]; if (i < 1 || i > CUPS_MAX_SUPPLIES || packet->object_type != CUPS_ASN1_INTEGER) return; fprintf(stderr, "DEBUG2: prtMarkerSuppliesMaxCapacity.1.%d = %d\n", i, packet->object_value.integer); if (i > num_supplies) num_supplies = i; if (supplies[i - 1].max_capacity == 0 && packet->object_value.integer > 0) supplies[i - 1].max_capacity = packet->object_value.integer; } else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesClass)) { /* * Get marker class... */ i = packet->object_name[prtMarkerSuppliesClassOffset]; if (i < 1 || i > CUPS_MAX_SUPPLIES || packet->object_type != CUPS_ASN1_INTEGER) return; fprintf(stderr, "DEBUG2: prtMarkerSuppliesClass.1.%d = %d\n", i, packet->object_value.integer); if (i > num_supplies) num_supplies = i; supplies[i - 1].sclass = packet->object_value.integer; } else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesType)) { /* * Get marker type... */ i = packet->object_name[prtMarkerSuppliesTypeOffset]; if (i < 1 || i > CUPS_MAX_SUPPLIES || packet->object_type != CUPS_ASN1_INTEGER) return; fprintf(stderr, "DEBUG2: prtMarkerSuppliesType.1.%d = %d\n", i, packet->object_value.integer); if (i > num_supplies) num_supplies = i; supplies[i - 1].type = packet->object_value.integer; } else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesSupplyUnit)) { /* * Get units for capacity... */ i = packet->object_name[prtMarkerSuppliesSupplyUnitOffset]; if (i < 1 || i > CUPS_MAX_SUPPLIES || packet->object_type != CUPS_ASN1_INTEGER) return; fprintf(stderr, "DEBUG2: prtMarkerSuppliesSupplyUnit.1.%d = %d\n", i, packet->object_value.integer); if (i > num_supplies) num_supplies = i; if (packet->object_value.integer == CUPS_TC_percent) supplies[i - 1].max_capacity = 100; } } /* * 'utf16_to_utf8()' - Convert UTF-16 text to UTF-8. */ static void utf16_to_utf8( cups_utf8_t *dst, /* I - Destination buffer */ const unsigned char *src, /* I - Source string */ size_t srcsize, /* I - Size of source string */ size_t dstsize, /* I - Size of destination buffer */ int le) /* I - Source is little-endian? */ { cups_utf32_t ch, /* Current character */ temp[CUPS_SNMP_MAX_STRING], /* UTF-32 string */ *ptr; /* Pointer into UTF-32 string */ for (ptr = temp; srcsize >= 2;) { if (le) ch = (cups_utf32_t)(src[0] | (src[1] << 8)); else ch = (cups_utf32_t)((src[0] << 8) | src[1]); src += 2; srcsize -= 2; if (ch >= 0xd800 && ch <= 0xdbff && srcsize >= 2) { /* * Multi-word UTF-16 char... */ cups_utf32_t lch; /* Lower word */ if (le) lch = (cups_utf32_t)(src[0] | (src[1] << 8)); else lch = (cups_utf32_t)((src[0] << 8) | src[1]); if (lch >= 0xdc00 && lch <= 0xdfff) { src += 2; srcsize -= 2; ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; } } if (ptr < (temp + CUPS_SNMP_MAX_STRING - 1)) *ptr++ = ch; } *ptr = '\0'; cupsUTF32ToUTF8(dst, temp, (int)dstsize); }