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