2 * SNMP supplies functions for CUPS.
4 * Copyright 2008-2015 by Apple Inc.
6 * These coded instructions, statements, and computer programs are the
7 * property of Apple Inc. and are protected by Federal copyright
8 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
9 * "LICENSE" which should have been included with this file. If this
10 * file is missing or damaged, see the license at "http://www.cups.org/".
12 * This file is subject to the Apple OS-Developed Software exception.
16 * Include necessary headers.
19 #include "backend-private.h"
20 #include <cups/ppd-private.h>
21 #include <cups/array.h>
28 #define CUPS_MAX_SUPPLIES 32 /* Maximum number of supplies for a printer */
29 #define CUPS_SUPPLY_TIMEOUT 2.0 /* Timeout for SNMP lookups */
31 #define CUPS_DEVELOPER_LOW 0x0001
32 #define CUPS_DEVELOPER_EMPTY 0x0002
33 #define CUPS_MARKER_SUPPLY_LOW 0x0004
34 #define CUPS_MARKER_SUPPLY_EMPTY 0x0008
35 #define CUPS_OPC_NEAR_EOL 0x0010
36 #define CUPS_OPC_LIFE_OVER 0x0020
37 #define CUPS_TONER_LOW 0x0040
38 #define CUPS_TONER_EMPTY 0x0080
39 #define CUPS_WASTE_ALMOST_FULL 0x0100
40 #define CUPS_WASTE_FULL 0x0200
41 #define CUPS_CLEANER_NEAR_EOL 0x0400 /* Proposed JPS3 */
42 #define CUPS_CLEANER_LIFE_OVER 0x0800 /* Proposed JPS3 */
44 #define CUPS_SNMP_NONE 0x0000
45 #define CUPS_SNMP_CAPACITY 0x0001 /* Supply levels reported as percentages */
52 typedef struct /**** Printer supply data ****/
54 char name
[CUPS_SNMP_MAX_STRING
], /* Name of supply */
55 color
[8]; /* Color: "#RRGGBB" or "none" */
56 int colorant
, /* Colorant index */
57 sclass
, /* Supply class */
58 type
, /* Supply type */
59 max_capacity
, /* Maximum capacity */
60 level
; /* Current level value */
63 typedef struct /**** Printer state table ****/
65 int bit
; /* State bit */
66 const char *keyword
; /* IPP printer-state-reasons keyword */
74 static http_addr_t current_addr
; /* Current address */
75 static int current_state
= -1;
76 /* Current device state bits */
77 static int charset
= -1; /* Character set for supply names */
78 static unsigned quirks
= CUPS_SNMP_NONE
;
79 /* Quirks we have to work around */
80 static int num_supplies
= 0;
81 /* Number of supplies found */
82 static backend_supplies_t supplies
[CUPS_MAX_SUPPLIES
];
83 /* Supply information */
84 static int supply_state
= -1;
85 /* Supply state info */
87 static const int hrDeviceDescr
[] =
88 { CUPS_OID_hrDeviceDescr
, 1, -1 };
89 /* Device description OID */
90 static const int hrPrinterStatus
[] =
91 { CUPS_OID_hrPrinterStatus
, 1, -1 };
92 /* Current state OID */
93 static const int hrPrinterDetectedErrorState
[] =
94 { CUPS_OID_hrPrinterDetectedErrorState
, 1, -1 };
95 /* Current printer state bits OID */
96 static const int prtGeneralCurrentLocalization
[] =
97 { CUPS_OID_prtGeneralCurrentLocalization
, 1, -1 };
98 static const int prtLocalizationCharacterSet
[] =
99 { CUPS_OID_prtLocalizationCharacterSet
, 1, 1, -1 },
100 prtLocalizationCharacterSetOffset
=
101 (sizeof(prtLocalizationCharacterSet
) /
102 sizeof(prtLocalizationCharacterSet
[0]));
103 static const int prtMarkerColorantValue
[] =
104 { CUPS_OID_prtMarkerColorantValue
, -1 },
106 prtMarkerColorantValueOffset
=
107 (sizeof(prtMarkerColorantValue
) /
108 sizeof(prtMarkerColorantValue
[0]));
109 /* Offset to colorant index */
110 static const int prtMarkerLifeCount
[] =
111 { CUPS_OID_prtMarkerLifeCount
, 1, 1, -1 };
112 /* Page counter OID */
113 static const int prtMarkerSuppliesEntry
[] =
114 { CUPS_OID_prtMarkerSuppliesEntry
, -1 };
116 static const int prtMarkerSuppliesColorantIndex
[] =
117 { CUPS_OID_prtMarkerSuppliesColorantIndex
, -1 },
118 /* Colorant index OID */
119 prtMarkerSuppliesColorantIndexOffset
=
120 (sizeof(prtMarkerSuppliesColorantIndex
) /
121 sizeof(prtMarkerSuppliesColorantIndex
[0]));
122 /* Offset to supply index */
123 static const int prtMarkerSuppliesDescription
[] =
124 { CUPS_OID_prtMarkerSuppliesDescription
, -1 },
125 /* Description OID */
126 prtMarkerSuppliesDescriptionOffset
=
127 (sizeof(prtMarkerSuppliesDescription
) /
128 sizeof(prtMarkerSuppliesDescription
[0]));
129 /* Offset to supply index */
130 static const int prtMarkerSuppliesLevel
[] =
131 { CUPS_OID_prtMarkerSuppliesLevel
, -1 },
133 prtMarkerSuppliesLevelOffset
=
134 (sizeof(prtMarkerSuppliesLevel
) /
135 sizeof(prtMarkerSuppliesLevel
[0]));
136 /* Offset to supply index */
137 static const int prtMarkerSuppliesMaxCapacity
[] =
138 { CUPS_OID_prtMarkerSuppliesMaxCapacity
, -1 },
139 /* Max capacity OID */
140 prtMarkerSuppliesMaxCapacityOffset
=
141 (sizeof(prtMarkerSuppliesMaxCapacity
) /
142 sizeof(prtMarkerSuppliesMaxCapacity
[0]));
143 /* Offset to supply index */
144 static const int prtMarkerSuppliesClass
[] =
145 { CUPS_OID_prtMarkerSuppliesClass
, -1 },
147 prtMarkerSuppliesClassOffset
=
148 (sizeof(prtMarkerSuppliesClass
) /
149 sizeof(prtMarkerSuppliesClass
[0]));
150 /* Offset to supply index */
151 static const int prtMarkerSuppliesType
[] =
152 { CUPS_OID_prtMarkerSuppliesType
, -1 },
154 prtMarkerSuppliesTypeOffset
=
155 (sizeof(prtMarkerSuppliesType
) /
156 sizeof(prtMarkerSuppliesType
[0]));
157 /* Offset to supply index */
158 static const int prtMarkerSuppliesSupplyUnit
[] =
159 { CUPS_OID_prtMarkerSuppliesSupplyUnit
, -1 },
161 prtMarkerSuppliesSupplyUnitOffset
=
162 (sizeof(prtMarkerSuppliesSupplyUnit
) /
163 sizeof(prtMarkerSuppliesSupplyUnit
[0]));
164 /* Offset to supply index */
166 static const backend_state_t printer_states
[] =
168 /* { CUPS_TC_lowPaper, "media-low-report" }, */
169 { CUPS_TC_noPaper
| CUPS_TC_inputTrayEmpty
, "media-empty-warning" },
170 /* { CUPS_TC_lowToner, "toner-low-report" }, */ /* now use prtMarkerSupplies */
171 /* { CUPS_TC_noToner, "toner-empty-warning" }, */ /* now use prtMarkerSupplies */
172 { CUPS_TC_doorOpen
, "door-open-report" },
173 { CUPS_TC_jammed
, "media-jam-warning" },
174 /* { CUPS_TC_offline, "offline-report" }, */ /* unreliable */
175 /* { CUPS_TC_serviceRequested | CUPS_TC_overduePreventMaint, "service-needed-warning" }, */ /* unreliable */
176 { CUPS_TC_inputTrayMissing
, "input-tray-missing-warning" },
177 { CUPS_TC_outputTrayMissing
, "output-tray-missing-warning" },
178 { CUPS_TC_markerSupplyMissing
, "marker-supply-missing-warning" },
179 { CUPS_TC_outputNearFull
, "output-area-almost-full-report" },
180 { CUPS_TC_outputFull
, "output-area-full-warning" }
183 static const backend_state_t supply_states
[] =
185 { CUPS_DEVELOPER_LOW
, "developer-low-report" },
186 { CUPS_DEVELOPER_EMPTY
, "developer-empty-warning" },
187 { CUPS_MARKER_SUPPLY_LOW
, "marker-supply-low-report" },
188 { CUPS_MARKER_SUPPLY_EMPTY
, "marker-supply-empty-warning" },
189 { CUPS_OPC_NEAR_EOL
, "opc-near-eol-report" },
190 { CUPS_OPC_LIFE_OVER
, "opc-life-over-warning" },
191 { CUPS_TONER_LOW
, "toner-low-report" },
192 { CUPS_TONER_EMPTY
, "toner-empty-warning" },
193 { CUPS_WASTE_ALMOST_FULL
, "waste-receptacle-almost-full-report" },
194 { CUPS_WASTE_FULL
, "waste-receptacle-full-warning" },
195 { CUPS_CLEANER_NEAR_EOL
, "cleaner-life-almost-over-report" },
196 { CUPS_CLEANER_LIFE_OVER
, "cleaner-life-over-warning" },
204 static void backend_init_supplies(int snmp_fd
, http_addr_t
*addr
);
205 static void backend_walk_cb(cups_snmp_t
*packet
, void *data
);
206 static void utf16_to_utf8(cups_utf8_t
*dst
, const unsigned char *src
,
207 size_t srcsize
, size_t dstsize
, int le
);
211 * 'backendSNMPSupplies()' - Get the current supplies for a device.
214 int /* O - 0 on success, -1 on error */
216 int snmp_fd
, /* I - SNMP socket */
217 http_addr_t
*addr
, /* I - Printer address */
218 int *page_count
, /* O - Page count */
219 int *printer_state
) /* O - Printer state */
221 if (!httpAddrEqual(addr
, ¤t_addr
))
222 backend_init_supplies(snmp_fd
, addr
);
223 else if (num_supplies
> 0)
224 _cupsSNMPWalk(snmp_fd
, ¤t_addr
, CUPS_SNMP_VERSION_1
,
225 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesLevel
,
226 CUPS_SUPPLY_TIMEOUT
, backend_walk_cb
, NULL
);
234 if (num_supplies
> 0)
236 int i
, /* Looping var */
237 percent
, /* Percent full */
238 new_state
, /* New state value */
239 change_state
, /* State change */
240 new_supply_state
= 0; /* Supply state */
241 char value
[CUPS_MAX_SUPPLIES
* 4],
242 /* marker-levels value string */
243 *ptr
; /* Pointer into value string */
244 cups_snmp_t packet
; /* SNMP response packet */
247 * Generate the marker-levels value string...
250 for (i
= 0, ptr
= value
; i
< num_supplies
; i
++, ptr
+= strlen(ptr
))
252 if (supplies
[i
].max_capacity
> 0 && supplies
[i
].level
>= 0)
253 percent
= 100 * supplies
[i
].level
/ supplies
[i
].max_capacity
;
254 else if (supplies
[i
].level
>= 0 && supplies
[i
].level
<= 100 &&
255 (quirks
& CUPS_SNMP_CAPACITY
))
256 percent
= supplies
[i
].level
;
260 if (supplies
[i
].sclass
== CUPS_TC_receptacleThatIsFilled
)
261 percent
= 100 - percent
;
265 switch (supplies
[i
].type
)
268 case CUPS_TC_tonerCartridge
:
270 new_supply_state
|= CUPS_TONER_EMPTY
;
272 new_supply_state
|= CUPS_TONER_LOW
;
275 case CUPS_TC_inkCartridge
:
276 case CUPS_TC_inkRibbon
:
277 case CUPS_TC_solidWax
:
278 case CUPS_TC_ribbonWax
:
280 new_supply_state
|= CUPS_MARKER_SUPPLY_EMPTY
;
282 new_supply_state
|= CUPS_MARKER_SUPPLY_LOW
;
284 case CUPS_TC_developer
:
286 new_supply_state
|= CUPS_DEVELOPER_EMPTY
;
288 new_supply_state
|= CUPS_DEVELOPER_LOW
;
290 case CUPS_TC_coronaWire
:
293 case CUPS_TC_transferUnit
:
295 new_supply_state
|= CUPS_OPC_LIFE_OVER
;
297 new_supply_state
|= CUPS_OPC_NEAR_EOL
;
299 #if 0 /* Because no two vendors report waste containers the same, disable SNMP reporting of same */
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
:
306 new_supply_state
|= CUPS_WASTE_FULL
;
308 new_supply_state
|= CUPS_WASTE_ALMOST_FULL
;
311 case CUPS_TC_cleanerUnit
:
312 case CUPS_TC_fuserCleaningPad
:
314 new_supply_state
|= CUPS_CLEANER_LIFE_OVER
;
316 new_supply_state
|= CUPS_CLEANER_NEAR_EOL
;
324 if ((supplies
[i
].max_capacity
> 0 || (quirks
& CUPS_SNMP_CAPACITY
)) &&
325 supplies
[i
].level
>= 0)
326 snprintf(ptr
, sizeof(value
) - (size_t)(ptr
- value
), "%d", percent
);
328 strlcpy(ptr
, "-1", sizeof(value
) - (size_t)(ptr
- value
));
331 fprintf(stderr
, "ATTR: marker-levels=%s\n", value
);
333 if (supply_state
< 0)
334 change_state
= 0xffff;
336 change_state
= supply_state
^ new_supply_state
;
338 fprintf(stderr
, "DEBUG: new_supply_state=%x, change_state=%x\n",
339 new_supply_state
, change_state
);
342 i
< (int)(sizeof(supply_states
) / sizeof(supply_states
[0]));
344 if (change_state
& supply_states
[i
].bit
)
346 fprintf(stderr
, "STATE: %c%s\n",
347 (new_supply_state
& supply_states
[i
].bit
) ? '+' : '-',
348 supply_states
[i
].keyword
);
351 supply_state
= new_supply_state
;
354 * Get the current printer status bits...
357 if (!_cupsSNMPWrite(snmp_fd
, addr
, CUPS_SNMP_VERSION_1
,
358 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
359 hrPrinterDetectedErrorState
))
362 if (!_cupsSNMPRead(snmp_fd
, &packet
, CUPS_SUPPLY_TIMEOUT
) ||
363 packet
.object_type
!= CUPS_ASN1_OCTET_STRING
)
366 if (packet
.object_value
.string
.num_bytes
== 2)
367 new_state
= (packet
.object_value
.string
.bytes
[0] << 8) |
368 packet
.object_value
.string
.bytes
[1];
369 else if (packet
.object_value
.string
.num_bytes
== 1)
370 new_state
= (packet
.object_value
.string
.bytes
[0] << 8);
374 if (current_state
< 0)
375 change_state
= 0xffff;
377 change_state
= current_state
^ new_state
;
379 fprintf(stderr
, "DEBUG: new_state=%x, change_state=%x\n", new_state
,
383 i
< (int)(sizeof(printer_states
) / sizeof(printer_states
[0]));
385 if (change_state
& printer_states
[i
].bit
)
387 fprintf(stderr
, "STATE: %c%s\n",
388 (new_state
& printer_states
[i
].bit
) ? '+' : '-',
389 printer_states
[i
].keyword
);
392 current_state
= new_state
;
395 * Get the current printer state...
400 if (!_cupsSNMPWrite(snmp_fd
, addr
, CUPS_SNMP_VERSION_1
,
401 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
405 if (!_cupsSNMPRead(snmp_fd
, &packet
, CUPS_SUPPLY_TIMEOUT
) ||
406 packet
.object_type
!= CUPS_ASN1_INTEGER
)
409 *printer_state
= packet
.object_value
.integer
;
413 * Get the current page count...
418 if (!_cupsSNMPWrite(snmp_fd
, addr
, CUPS_SNMP_VERSION_1
,
419 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
423 if (!_cupsSNMPRead(snmp_fd
, &packet
, CUPS_SUPPLY_TIMEOUT
) ||
424 packet
.object_type
!= CUPS_ASN1_COUNTER
)
427 *page_count
= packet
.object_value
.counter
;
438 * 'backend_init_supplies()' - Initialize the supplies list.
442 backend_init_supplies(
443 int snmp_fd
, /* I - SNMP socket */
444 http_addr_t
*addr
) /* I - Printer address */
446 int i
, /* Looping var */
447 type
; /* Current marker type */
448 cups_file_t
*cachefile
; /* Cache file */
449 const char *cachedir
; /* CUPS_CACHEDIR value */
450 char addrstr
[1024], /* Address string */
451 cachefilename
[1024], /* Cache filename */
452 description
[CUPS_SNMP_MAX_STRING
],
453 /* Device description string */
454 value
[CUPS_MAX_SUPPLIES
* (CUPS_SNMP_MAX_STRING
* 4 + 3)],
456 *ptr
, /* Pointer into value string */
457 *name_ptr
; /* Pointer into name string */
458 cups_snmp_t packet
; /* SNMP response packet */
459 ppd_file_t
*ppd
; /* PPD file for this queue */
460 ppd_attr_t
*ppdattr
; /* cupsSNMPSupplies attribute */
461 static const char * const types
[] = /* Supply types */
481 "fuser-cleaning-pad",
487 "glue-water-additive",
501 * Reset state information...
504 current_addr
= *addr
;
509 memset(supplies
, 0, sizeof(supplies
));
512 * See if we should be getting supply levels via SNMP...
515 if ((ppd
= ppdOpenFile(getenv("PPD"))) == NULL
||
516 ((ppdattr
= ppdFindAttr(ppd
, "cupsSNMPSupplies", NULL
)) != NULL
&&
517 ppdattr
->value
&& _cups_strcasecmp(ppdattr
->value
, "true")))
523 if ((ppdattr
= ppdFindAttr(ppd
, "cupsSNMPQuirks", NULL
)) != NULL
)
525 if (!_cups_strcasecmp(ppdattr
->value
, "capacity"))
526 quirks
|= CUPS_SNMP_CAPACITY
;
532 * Get the device description...
535 if (!_cupsSNMPWrite(snmp_fd
, addr
, CUPS_SNMP_VERSION_1
,
536 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
540 if (!_cupsSNMPRead(snmp_fd
, &packet
, CUPS_SUPPLY_TIMEOUT
) ||
541 packet
.object_type
!= CUPS_ASN1_OCTET_STRING
)
543 strlcpy(description
, "Unknown", sizeof(description
));
547 strlcpy(description
, (char *)packet
.object_value
.string
.bytes
,
548 sizeof(description
));
550 fprintf(stderr
, "DEBUG2: hrDeviceDesc=\"%s\"\n", description
);
553 * See if we have already queried this device...
556 httpAddrString(addr
, addrstr
, sizeof(addrstr
));
558 if ((cachedir
= getenv("CUPS_CACHEDIR")) == NULL
)
559 cachedir
= CUPS_CACHEDIR
;
561 snprintf(cachefilename
, sizeof(cachefilename
), "%s/%s.snmp", cachedir
,
564 if ((cachefile
= cupsFileOpen(cachefilename
, "r")) != NULL
)
567 * Yes, read the cache file:
569 * 3 num_supplies charset
571 * supply structures...
574 if (cupsFileGets(cachefile
, value
, sizeof(value
)))
576 if (sscanf(value
, "3 %d%d", &num_supplies
, &charset
) == 2 &&
577 num_supplies
<= CUPS_MAX_SUPPLIES
&&
578 cupsFileGets(cachefile
, value
, sizeof(value
)))
580 if (!strcmp(description
, value
))
581 cupsFileRead(cachefile
, (char *)supplies
,
582 (size_t)num_supplies
* sizeof(backend_supplies_t
));
596 cupsFileClose(cachefile
);
600 * If the cache information isn't correct, scan for supplies...
606 * Get the configured character set...
609 int oid
[CUPS_SNMP_MAX_OID
]; /* OID for character set */
612 if (!_cupsSNMPWrite(snmp_fd
, ¤t_addr
, CUPS_SNMP_VERSION_1
,
613 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
614 prtGeneralCurrentLocalization
))
617 if (!_cupsSNMPRead(snmp_fd
, &packet
, CUPS_SUPPLY_TIMEOUT
) ||
618 packet
.object_type
!= CUPS_ASN1_INTEGER
)
621 "DEBUG: prtGeneralCurrentLocalization type is %x, expected %x!\n",
622 packet
.object_type
, CUPS_ASN1_INTEGER
);
626 fprintf(stderr
, "DEBUG2: prtGeneralCurrentLocalization=%d\n",
627 packet
.object_value
.integer
);
629 _cupsSNMPCopyOID(oid
, prtLocalizationCharacterSet
, CUPS_SNMP_MAX_OID
);
630 oid
[prtLocalizationCharacterSetOffset
- 2] = packet
.object_value
.integer
;
633 if (!_cupsSNMPWrite(snmp_fd
, ¤t_addr
, CUPS_SNMP_VERSION_1
,
634 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
638 if (!_cupsSNMPRead(snmp_fd
, &packet
, CUPS_SUPPLY_TIMEOUT
) ||
639 packet
.object_type
!= CUPS_ASN1_INTEGER
)
642 "DEBUG: prtLocalizationCharacterSet type is %x, expected %x!\n",
643 packet
.object_type
, CUPS_ASN1_INTEGER
);
647 fprintf(stderr
, "DEBUG2: prtLocalizationCharacterSet=%d\n",
648 packet
.object_value
.integer
);
649 charset
= packet
.object_value
.integer
;
652 if (num_supplies
< 0)
655 * Walk the printer configuration information...
658 _cupsSNMPWalk(snmp_fd
, ¤t_addr
, CUPS_SNMP_VERSION_1
,
659 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesEntry
,
660 CUPS_SUPPLY_TIMEOUT
, backend_walk_cb
, NULL
);
664 * Save the cached information...
667 if (num_supplies
< 0)
670 if ((cachefile
= cupsFileOpen(cachefilename
, "w")) != NULL
)
672 cupsFilePrintf(cachefile
, "3 %d %d\n", num_supplies
, charset
);
673 cupsFilePrintf(cachefile
, "%s\n", description
);
675 if (num_supplies
> 0)
676 cupsFileWrite(cachefile
, (char *)supplies
,
677 (size_t)num_supplies
* sizeof(backend_supplies_t
));
679 cupsFileClose(cachefile
);
682 if (num_supplies
<= 0)
689 for (i
= 0; i
< num_supplies
; i
++)
690 strlcpy(supplies
[i
].color
, "none", sizeof(supplies
[i
].color
));
692 _cupsSNMPWalk(snmp_fd
, ¤t_addr
, CUPS_SNMP_VERSION_1
,
693 _cupsSNMPDefaultCommunity(), prtMarkerColorantValue
,
694 CUPS_SUPPLY_TIMEOUT
, backend_walk_cb
, NULL
);
697 * Output the marker-colors attribute...
700 for (i
= 0, ptr
= value
; i
< num_supplies
; i
++, ptr
+= strlen(ptr
))
705 strlcpy(ptr
, supplies
[i
].color
, sizeof(value
) - (size_t)(ptr
- value
));
708 fprintf(stderr
, "ATTR: marker-colors=%s\n", value
);
711 * Output the marker-names attribute (the double quoting is necessary to deal
712 * with embedded quotes and commas in the marker names...)
715 for (i
= 0, ptr
= value
; i
< num_supplies
; i
++)
722 for (name_ptr
= supplies
[i
].name
; *name_ptr
;)
724 if (*name_ptr
== '\\' || *name_ptr
== '\"' || *name_ptr
== '\'')
731 *ptr
++ = *name_ptr
++;
739 fprintf(stderr
, "ATTR: marker-names=%s\n", value
);
742 * Output the marker-types attribute...
745 for (i
= 0, ptr
= value
; i
< num_supplies
; i
++, ptr
+= strlen(ptr
))
750 type
= supplies
[i
].type
;
752 if (type
< CUPS_TC_other
|| type
> CUPS_TC_covers
)
753 strlcpy(ptr
, "unknown", sizeof(value
) - (size_t)(ptr
- value
));
755 strlcpy(ptr
, types
[type
- CUPS_TC_other
], sizeof(value
) - (size_t)(ptr
- value
));
758 fprintf(stderr
, "ATTR: marker-types=%s\n", value
);
763 * 'backend_walk_cb()' - Interpret the supply value responses.
767 backend_walk_cb(cups_snmp_t
*packet
, /* I - SNMP packet */
768 void *data
) /* I - User data (unused) */
770 int i
, j
, k
; /* Looping vars */
771 static const char * const colors
[][2] =
772 { /* Standard color names */
773 { "black", "#000000" },
774 { "blue", "#0000FF" },
775 { "brown", "#A52A2A" },
776 { "cyan", "#00FFFF" },
777 { "dark-gray", "#404040" },
778 { "dark gray", "#404040" },
779 { "dark-yellow", "#FFCC00" },
780 { "dark yellow", "#FFCC00" },
781 { "gold", "#FFD700" },
782 { "gray", "#808080" },
783 { "green", "#00FF00" },
784 { "light-black", "#606060" },
785 { "light black", "#606060" },
786 { "light-cyan", "#E0FFFF" },
787 { "light cyan", "#E0FFFF" },
788 { "light-gray", "#D3D3D3" },
789 { "light gray", "#D3D3D3" },
790 { "light-magenta", "#FF77FF" },
791 { "light magenta", "#FF77FF" },
792 { "magenta", "#FF00FF" },
793 { "orange", "#FFA500" },
794 { "red", "#FF0000" },
795 { "silver", "#C0C0C0" },
796 { "white", "#FFFFFF" },
797 { "yellow", "#FFFF00" }
803 if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerColorantValue
) &&
804 packet
->object_type
== CUPS_ASN1_OCTET_STRING
)
810 i
= packet
->object_name
[prtMarkerColorantValueOffset
];
812 fprintf(stderr
, "DEBUG2: prtMarkerColorantValue.1.%d = \"%s\"\n", i
,
813 (char *)packet
->object_value
.string
.bytes
);
815 for (j
= 0; j
< num_supplies
; j
++)
816 if (supplies
[j
].colorant
== i
)
818 for (k
= 0; k
< (int)(sizeof(colors
) / sizeof(colors
[0])); k
++)
819 if (!_cups_strcasecmp(colors
[k
][0],
820 (char *)packet
->object_value
.string
.bytes
))
822 strlcpy(supplies
[j
].color
, colors
[k
][1], sizeof(supplies
[j
].color
));
827 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesColorantIndex
))
830 * Get colorant index...
833 i
= packet
->object_name
[prtMarkerSuppliesColorantIndexOffset
];
834 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
835 packet
->object_type
!= CUPS_ASN1_INTEGER
)
838 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesColorantIndex.1.%d = %d\n", i
,
839 packet
->object_value
.integer
);
841 if (i
> num_supplies
)
844 supplies
[i
- 1].colorant
= packet
->object_value
.integer
;
846 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesDescription
))
849 * Get supply name/description...
852 i
= packet
->object_name
[prtMarkerSuppliesDescriptionOffset
];
853 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
854 packet
->object_type
!= CUPS_ASN1_OCTET_STRING
)
857 if (i
> num_supplies
)
862 case CUPS_TC_csASCII
:
863 case CUPS_TC_csUTF8
:
864 case CUPS_TC_csUnicodeASCII
:
865 strlcpy(supplies
[i
- 1].name
,
866 (char *)packet
->object_value
.string
.bytes
,
867 sizeof(supplies
[0].name
));
870 case CUPS_TC_csISOLatin1
:
871 case CUPS_TC_csUnicodeLatin1
:
872 cupsCharsetToUTF8((cups_utf8_t
*)supplies
[i
- 1].name
,
873 (char *)packet
->object_value
.string
.bytes
,
874 sizeof(supplies
[0].name
), CUPS_ISO8859_1
);
877 case CUPS_TC_csShiftJIS
:
878 case CUPS_TC_csWindows31J
: /* Close enough for our purposes */
879 cupsCharsetToUTF8((cups_utf8_t
*)supplies
[i
- 1].name
,
880 (char *)packet
->object_value
.string
.bytes
,
881 sizeof(supplies
[0].name
), CUPS_JIS_X0213
);
884 case CUPS_TC_csUCS4
:
885 case CUPS_TC_csUTF32
:
886 case CUPS_TC_csUTF32BE
:
887 case CUPS_TC_csUTF32LE
:
888 cupsUTF32ToUTF8((cups_utf8_t
*)supplies
[i
- 1].name
,
889 (cups_utf32_t
*)packet
->object_value
.string
.bytes
,
890 sizeof(supplies
[0].name
));
893 case CUPS_TC_csUnicode
:
894 case CUPS_TC_csUTF16BE
:
895 case CUPS_TC_csUTF16LE
:
896 utf16_to_utf8((cups_utf8_t
*)supplies
[i
- 1].name
,
897 packet
->object_value
.string
.bytes
,
898 packet
->object_value
.string
.num_bytes
,
899 sizeof(supplies
[0].name
), charset
== CUPS_TC_csUTF16LE
);
904 * If we get here, the printer is using an unknown character set and
905 * we just want to copy characters that look like ASCII...
909 char *src
, *dst
; /* Pointers into strings */
912 * Loop safe because both the object_value and supplies char arrays
913 * are CUPS_SNMP_MAX_STRING elements long.
916 for (src
= (char *)packet
->object_value
.string
.bytes
,
917 dst
= supplies
[i
- 1].name
;
921 if ((*src
& 0x80) || *src
< ' ' || *src
== 0x7f)
932 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesDescription.1.%d = \"%s\"\n", i
,
933 supplies
[i
- 1].name
);
936 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesLevel
))
942 i
= packet
->object_name
[prtMarkerSuppliesLevelOffset
];
943 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
944 packet
->object_type
!= CUPS_ASN1_INTEGER
)
947 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesLevel.1.%d = %d\n", i
,
948 packet
->object_value
.integer
);
950 if (i
> num_supplies
)
953 supplies
[i
- 1].level
= packet
->object_value
.integer
;
955 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesMaxCapacity
) &&
956 !(quirks
& CUPS_SNMP_CAPACITY
))
959 * Get max capacity...
962 i
= packet
->object_name
[prtMarkerSuppliesMaxCapacityOffset
];
963 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
964 packet
->object_type
!= CUPS_ASN1_INTEGER
)
967 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesMaxCapacity.1.%d = %d\n", i
,
968 packet
->object_value
.integer
);
970 if (i
> num_supplies
)
973 if (supplies
[i
- 1].max_capacity
== 0 &&
974 packet
->object_value
.integer
> 0)
975 supplies
[i
- 1].max_capacity
= packet
->object_value
.integer
;
977 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesClass
))
980 * Get marker class...
983 i
= packet
->object_name
[prtMarkerSuppliesClassOffset
];
984 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
985 packet
->object_type
!= CUPS_ASN1_INTEGER
)
988 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesClass.1.%d = %d\n", i
,
989 packet
->object_value
.integer
);
991 if (i
> num_supplies
)
994 supplies
[i
- 1].sclass
= packet
->object_value
.integer
;
996 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesType
))
1002 i
= packet
->object_name
[prtMarkerSuppliesTypeOffset
];
1003 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
1004 packet
->object_type
!= CUPS_ASN1_INTEGER
)
1007 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesType.1.%d = %d\n", i
,
1008 packet
->object_value
.integer
);
1010 if (i
> num_supplies
)
1013 supplies
[i
- 1].type
= packet
->object_value
.integer
;
1015 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesSupplyUnit
))
1018 * Get units for capacity...
1021 i
= packet
->object_name
[prtMarkerSuppliesSupplyUnitOffset
];
1022 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
1023 packet
->object_type
!= CUPS_ASN1_INTEGER
)
1026 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesSupplyUnit.1.%d = %d\n", i
,
1027 packet
->object_value
.integer
);
1029 if (i
> num_supplies
)
1032 if (packet
->object_value
.integer
== CUPS_TC_percent
)
1033 supplies
[i
- 1].max_capacity
= 100;
1039 * 'utf16_to_utf8()' - Convert UTF-16 text to UTF-8.
1044 cups_utf8_t
*dst
, /* I - Destination buffer */
1045 const unsigned char *src
, /* I - Source string */
1046 size_t srcsize
, /* I - Size of source string */
1047 size_t dstsize
, /* I - Size of destination buffer */
1048 int le
) /* I - Source is little-endian? */
1050 cups_utf32_t ch
, /* Current character */
1051 temp
[CUPS_SNMP_MAX_STRING
],
1053 *ptr
; /* Pointer into UTF-32 string */
1056 for (ptr
= temp
; srcsize
>= 2;)
1059 ch
= (cups_utf32_t
)(src
[0] | (src
[1] << 8));
1061 ch
= (cups_utf32_t
)((src
[0] << 8) | src
[1]);
1066 if (ch
>= 0xd800 && ch
<= 0xdbff && srcsize
>= 2)
1069 * Multi-word UTF-16 char...
1072 cups_utf32_t lch
; /* Lower word */
1076 lch
= (cups_utf32_t
)(src
[0] | (src
[1] << 8));
1078 lch
= (cups_utf32_t
)((src
[0] << 8) | src
[1]);
1080 if (lch
>= 0xdc00 && lch
<= 0xdfff)
1085 ch
= (((ch
& 0x3ff) << 10) | (lch
& 0x3ff)) + 0x10000;
1089 if (ptr
< (temp
+ CUPS_SNMP_MAX_STRING
- 1))
1095 cupsUTF32ToUTF8(dst
, temp
, (int)dstsize
);