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