]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/snmp-supplies.c
c26e72a2396149d301b3a3743a6cb659333fc234
[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 * This file is subject to the Apple OS-Developed Software exception.
13 */
14
15 /*
16 * Include necessary headers.
17 */
18
19 #include "backend-private.h"
20 #include <cups/ppd-private.h>
21 #include <cups/array.h>
22
23
24 /*
25 * Local constants...
26 */
27
28 #define CUPS_MAX_SUPPLIES 32 /* Maximum number of supplies for a printer */
29 #define CUPS_SUPPLY_TIMEOUT 2.0 /* Timeout for SNMP lookups */
30
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 */
43
44 #define CUPS_SNMP_NONE 0x0000
45 #define CUPS_SNMP_CAPACITY 0x0001 /* Supply levels reported as percentages */
46
47
48 /*
49 * Local structures...
50 */
51
52 typedef struct /**** Printer supply data ****/
53 {
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 */
61 } backend_supplies_t;
62
63 typedef struct /**** Printer state table ****/
64 {
65 int bit; /* State bit */
66 const char *keyword; /* IPP printer-state-reasons keyword */
67 } backend_state_t;
68
69
70 /*
71 * Local globals...
72 */
73
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 */
86
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 },
105 /* Colorant OID */
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 };
115 /* Supplies OID */
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 },
132 /* Level OID */
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 },
146 /* Class OID */
147 prtMarkerSuppliesClassOffset =
148 (sizeof(prtMarkerSuppliesClass) /
149 sizeof(prtMarkerSuppliesClass[0]));
150 /* Offset to supply index */
151 static const int prtMarkerSuppliesType[] =
152 { CUPS_OID_prtMarkerSuppliesType, -1 },
153 /* Type OID */
154 prtMarkerSuppliesTypeOffset =
155 (sizeof(prtMarkerSuppliesType) /
156 sizeof(prtMarkerSuppliesType[0]));
157 /* Offset to supply index */
158 static const int prtMarkerSuppliesSupplyUnit[] =
159 { CUPS_OID_prtMarkerSuppliesSupplyUnit, -1 },
160 /* Units OID */
161 prtMarkerSuppliesSupplyUnitOffset =
162 (sizeof(prtMarkerSuppliesSupplyUnit) /
163 sizeof(prtMarkerSuppliesSupplyUnit[0]));
164 /* Offset to supply index */
165
166 static const backend_state_t printer_states[] =
167 {
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" }
181 };
182
183 static const backend_state_t supply_states[] =
184 {
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" },
197 };
198
199
200 /*
201 * Local functions...
202 */
203
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);
208
209
210 /*
211 * 'backendSNMPSupplies()' - Get the current supplies for a device.
212 */
213
214 int /* O - 0 on success, -1 on error */
215 backendSNMPSupplies(
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 */
220 {
221 if (!httpAddrEqual(addr, &current_addr))
222 backend_init_supplies(snmp_fd, addr);
223 else if (num_supplies > 0)
224 _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
225 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesLevel,
226 CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL);
227
228 if (page_count)
229 *page_count = -1;
230
231 if (printer_state)
232 *printer_state = -1;
233
234 if (num_supplies > 0)
235 {
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 */
245
246 /*
247 * Generate the marker-levels value string...
248 */
249
250 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr))
251 {
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;
257 else
258 percent = 50;
259
260 if (supplies[i].sclass == CUPS_TC_receptacleThatIsFilled)
261 percent = 100 - percent;
262
263 if (percent <= 5)
264 {
265 switch (supplies[i].type)
266 {
267 case CUPS_TC_toner :
268 case CUPS_TC_tonerCartridge :
269 if (percent <= 1)
270 new_supply_state |= CUPS_TONER_EMPTY;
271 else
272 new_supply_state |= CUPS_TONER_LOW;
273 break;
274 case CUPS_TC_ink :
275 case CUPS_TC_inkCartridge :
276 case CUPS_TC_inkRibbon :
277 case CUPS_TC_solidWax :
278 case CUPS_TC_ribbonWax :
279 if (percent <= 1)
280 new_supply_state |= CUPS_MARKER_SUPPLY_EMPTY;
281 else
282 new_supply_state |= CUPS_MARKER_SUPPLY_LOW;
283 break;
284 case CUPS_TC_developer :
285 if (percent <= 1)
286 new_supply_state |= CUPS_DEVELOPER_EMPTY;
287 else
288 new_supply_state |= CUPS_DEVELOPER_LOW;
289 break;
290 case CUPS_TC_coronaWire :
291 case CUPS_TC_fuser :
292 case CUPS_TC_opc :
293 case CUPS_TC_transferUnit :
294 if (percent <= 1)
295 new_supply_state |= CUPS_OPC_LIFE_OVER;
296 else
297 new_supply_state |= CUPS_OPC_NEAR_EOL;
298 break;
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 :
305 if (percent <= 1)
306 new_supply_state |= CUPS_WASTE_FULL;
307 else
308 new_supply_state |= CUPS_WASTE_ALMOST_FULL;
309 break;
310 #endif /* 0 */
311 case CUPS_TC_cleanerUnit :
312 case CUPS_TC_fuserCleaningPad :
313 if (percent <= 1)
314 new_supply_state |= CUPS_CLEANER_LIFE_OVER;
315 else
316 new_supply_state |= CUPS_CLEANER_NEAR_EOL;
317 break;
318 }
319 }
320
321 if (i)
322 *ptr++ = ',';
323
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);
327 else
328 strlcpy(ptr, "-1", sizeof(value) - (size_t)(ptr - value));
329 }
330
331 fprintf(stderr, "ATTR: marker-levels=%s\n", value);
332
333 if (supply_state < 0)
334 change_state = 0xffff;
335 else
336 change_state = supply_state ^ new_supply_state;
337
338 fprintf(stderr, "DEBUG: new_supply_state=%x, change_state=%x\n",
339 new_supply_state, change_state);
340
341 for (i = 0;
342 i < (int)(sizeof(supply_states) / sizeof(supply_states[0]));
343 i ++)
344 if (change_state & supply_states[i].bit)
345 {
346 fprintf(stderr, "STATE: %c%s\n",
347 (new_supply_state & supply_states[i].bit) ? '+' : '-',
348 supply_states[i].keyword);
349 }
350
351 supply_state = new_supply_state;
352
353 /*
354 * Get the current printer status bits...
355 */
356
357 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
358 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
359 hrPrinterDetectedErrorState))
360 return (-1);
361
362 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
363 packet.object_type != CUPS_ASN1_OCTET_STRING)
364 return (-1);
365
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);
371 else
372 new_state = 0;
373
374 if (current_state < 0)
375 change_state = 0xffff;
376 else
377 change_state = current_state ^ new_state;
378
379 fprintf(stderr, "DEBUG: new_state=%x, change_state=%x\n", new_state,
380 change_state);
381
382 for (i = 0;
383 i < (int)(sizeof(printer_states) / sizeof(printer_states[0]));
384 i ++)
385 if (change_state & printer_states[i].bit)
386 {
387 fprintf(stderr, "STATE: %c%s\n",
388 (new_state & printer_states[i].bit) ? '+' : '-',
389 printer_states[i].keyword);
390 }
391
392 current_state = new_state;
393
394 /*
395 * Get the current printer state...
396 */
397
398 if (printer_state)
399 {
400 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
401 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
402 hrPrinterStatus))
403 return (-1);
404
405 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
406 packet.object_type != CUPS_ASN1_INTEGER)
407 return (-1);
408
409 *printer_state = packet.object_value.integer;
410 }
411
412 /*
413 * Get the current page count...
414 */
415
416 if (page_count)
417 {
418 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
419 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
420 prtMarkerLifeCount))
421 return (-1);
422
423 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
424 packet.object_type != CUPS_ASN1_COUNTER)
425 return (-1);
426
427 *page_count = packet.object_value.counter;
428 }
429
430 return (0);
431 }
432 else
433 return (-1);
434 }
435
436
437 /*
438 * 'backend_init_supplies()' - Initialize the supplies list.
439 */
440
441 static void
442 backend_init_supplies(
443 int snmp_fd, /* I - SNMP socket */
444 http_addr_t *addr) /* I - Printer address */
445 {
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)],
455 /* Value string */
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 */
462 {
463 "other",
464 "unknown",
465 "toner",
466 "waste-toner",
467 "ink",
468 "ink-cartridge",
469 "ink-ribbon",
470 "waste-ink",
471 "opc",
472 "developer",
473 "fuser-oil",
474 "solid-wax",
475 "ribbon-wax",
476 "waste-wax",
477 "fuser",
478 "corona-wire",
479 "fuser-oil-wick",
480 "cleaner-unit",
481 "fuser-cleaning-pad",
482 "transfer-unit",
483 "toner-cartridge",
484 "fuser-oiler",
485 "water",
486 "waste-water",
487 "glue-water-additive",
488 "waste-paper",
489 "binding-supply",
490 "banding-supply",
491 "stitching-wire",
492 "shrink-wrap",
493 "paper-wrap",
494 "staples",
495 "inserts",
496 "covers"
497 };
498
499
500 /*
501 * Reset state information...
502 */
503
504 current_addr = *addr;
505 current_state = -1;
506 num_supplies = -1;
507 charset = -1;
508
509 memset(supplies, 0, sizeof(supplies));
510
511 /*
512 * See if we should be getting supply levels via SNMP...
513 */
514
515 if ((ppd = ppdOpenFile(getenv("PPD"))) == NULL ||
516 ((ppdattr = ppdFindAttr(ppd, "cupsSNMPSupplies", NULL)) != NULL &&
517 ppdattr->value && _cups_strcasecmp(ppdattr->value, "true")))
518 {
519 ppdClose(ppd);
520 return;
521 }
522
523 if ((ppdattr = ppdFindAttr(ppd, "cupsSNMPQuirks", NULL)) != NULL)
524 {
525 if (!_cups_strcasecmp(ppdattr->value, "capacity"))
526 quirks |= CUPS_SNMP_CAPACITY;
527 }
528
529 ppdClose(ppd);
530
531 /*
532 * Get the device description...
533 */
534
535 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
536 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
537 hrDeviceDescr))
538 return;
539
540 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
541 packet.object_type != CUPS_ASN1_OCTET_STRING)
542 {
543 strlcpy(description, "Unknown", sizeof(description));
544 num_supplies = 0;
545 }
546 else
547 strlcpy(description, (char *)packet.object_value.string.bytes,
548 sizeof(description));
549
550 fprintf(stderr, "DEBUG2: hrDeviceDesc=\"%s\"\n", description);
551
552 /*
553 * See if we have already queried this device...
554 */
555
556 httpAddrString(addr, addrstr, sizeof(addrstr));
557
558 if ((cachedir = getenv("CUPS_CACHEDIR")) == NULL)
559 cachedir = CUPS_CACHEDIR;
560
561 snprintf(cachefilename, sizeof(cachefilename), "%s/%s.snmp", cachedir,
562 addrstr);
563
564 if ((cachefile = cupsFileOpen(cachefilename, "r")) != NULL)
565 {
566 /*
567 * Yes, read the cache file:
568 *
569 * 3 num_supplies charset
570 * device description
571 * supply structures...
572 */
573
574 if (cupsFileGets(cachefile, value, sizeof(value)))
575 {
576 if (sscanf(value, "3 %d%d", &num_supplies, &charset) == 2 &&
577 num_supplies <= CUPS_MAX_SUPPLIES &&
578 cupsFileGets(cachefile, value, sizeof(value)))
579 {
580 if (!strcmp(description, value))
581 cupsFileRead(cachefile, (char *)supplies,
582 (size_t)num_supplies * sizeof(backend_supplies_t));
583 else
584 {
585 num_supplies = -1;
586 charset = -1;
587 }
588 }
589 else
590 {
591 num_supplies = -1;
592 charset = -1;
593 }
594 }
595
596 cupsFileClose(cachefile);
597 }
598
599 /*
600 * If the cache information isn't correct, scan for supplies...
601 */
602
603 if (charset < 0)
604 {
605 /*
606 * Get the configured character set...
607 */
608
609 int oid[CUPS_SNMP_MAX_OID]; /* OID for character set */
610
611
612 if (!_cupsSNMPWrite(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
613 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
614 prtGeneralCurrentLocalization))
615 return;
616
617 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
618 packet.object_type != CUPS_ASN1_INTEGER)
619 {
620 fprintf(stderr,
621 "DEBUG: prtGeneralCurrentLocalization type is %x, expected %x!\n",
622 packet.object_type, CUPS_ASN1_INTEGER);
623 return;
624 }
625
626 fprintf(stderr, "DEBUG2: prtGeneralCurrentLocalization=%d\n",
627 packet.object_value.integer);
628
629 _cupsSNMPCopyOID(oid, prtLocalizationCharacterSet, CUPS_SNMP_MAX_OID);
630 oid[prtLocalizationCharacterSetOffset - 2] = packet.object_value.integer;
631
632
633 if (!_cupsSNMPWrite(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
634 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
635 oid))
636 return;
637
638 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
639 packet.object_type != CUPS_ASN1_INTEGER)
640 {
641 fprintf(stderr,
642 "DEBUG: prtLocalizationCharacterSet type is %x, expected %x!\n",
643 packet.object_type, CUPS_ASN1_INTEGER);
644 return;
645 }
646
647 fprintf(stderr, "DEBUG2: prtLocalizationCharacterSet=%d\n",
648 packet.object_value.integer);
649 charset = packet.object_value.integer;
650 }
651
652 if (num_supplies < 0)
653 {
654 /*
655 * Walk the printer configuration information...
656 */
657
658 _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
659 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesEntry,
660 CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL);
661 }
662
663 /*
664 * Save the cached information...
665 */
666
667 if (num_supplies < 0)
668 num_supplies = 0;
669
670 if ((cachefile = cupsFileOpen(cachefilename, "w")) != NULL)
671 {
672 cupsFilePrintf(cachefile, "3 %d %d\n", num_supplies, charset);
673 cupsFilePrintf(cachefile, "%s\n", description);
674
675 if (num_supplies > 0)
676 cupsFileWrite(cachefile, (char *)supplies,
677 (size_t)num_supplies * sizeof(backend_supplies_t));
678
679 cupsFileClose(cachefile);
680 }
681
682 if (num_supplies <= 0)
683 return;
684
685 /*
686 * Get the colors...
687 */
688
689 for (i = 0; i < num_supplies; i ++)
690 strlcpy(supplies[i].color, "none", sizeof(supplies[i].color));
691
692 _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
693 _cupsSNMPDefaultCommunity(), prtMarkerColorantValue,
694 CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL);
695
696 /*
697 * Output the marker-colors attribute...
698 */
699
700 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr))
701 {
702 if (i)
703 *ptr++ = ',';
704
705 strlcpy(ptr, supplies[i].color, sizeof(value) - (size_t)(ptr - value));
706 }
707
708 fprintf(stderr, "ATTR: marker-colors=%s\n", value);
709
710 /*
711 * Output the marker-names attribute (the double quoting is necessary to deal
712 * with embedded quotes and commas in the marker names...)
713 */
714
715 for (i = 0, ptr = value; i < num_supplies; i ++)
716 {
717 if (i)
718 *ptr++ = ',';
719
720 *ptr++ = '\'';
721 *ptr++ = '\"';
722 for (name_ptr = supplies[i].name; *name_ptr;)
723 {
724 if (*name_ptr == '\\' || *name_ptr == '\"' || *name_ptr == '\'')
725 {
726 *ptr++ = '\\';
727 *ptr++ = '\\';
728 *ptr++ = '\\';
729 }
730
731 *ptr++ = *name_ptr++;
732 }
733 *ptr++ = '\"';
734 *ptr++ = '\'';
735 }
736
737 *ptr = '\0';
738
739 fprintf(stderr, "ATTR: marker-names=%s\n", value);
740
741 /*
742 * Output the marker-types attribute...
743 */
744
745 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr))
746 {
747 if (i)
748 *ptr++ = ',';
749
750 type = supplies[i].type;
751
752 if (type < CUPS_TC_other || type > CUPS_TC_covers)
753 strlcpy(ptr, "unknown", sizeof(value) - (size_t)(ptr - value));
754 else
755 strlcpy(ptr, types[type - CUPS_TC_other], sizeof(value) - (size_t)(ptr - value));
756 }
757
758 fprintf(stderr, "ATTR: marker-types=%s\n", value);
759 }
760
761
762 /*
763 * 'backend_walk_cb()' - Interpret the supply value responses.
764 */
765
766 static void
767 backend_walk_cb(cups_snmp_t *packet, /* I - SNMP packet */
768 void *data) /* I - User data (unused) */
769 {
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" }
798 };
799
800
801 (void)data;
802
803 if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerColorantValue) &&
804 packet->object_type == CUPS_ASN1_OCTET_STRING)
805 {
806 /*
807 * Get colorant...
808 */
809
810 i = packet->object_name[prtMarkerColorantValueOffset];
811
812 fprintf(stderr, "DEBUG2: prtMarkerColorantValue.1.%d = \"%s\"\n", i,
813 (char *)packet->object_value.string.bytes);
814
815 for (j = 0; j < num_supplies; j ++)
816 if (supplies[j].colorant == i)
817 {
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))
821 {
822 strlcpy(supplies[j].color, colors[k][1], sizeof(supplies[j].color));
823 break;
824 }
825 }
826 }
827 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesColorantIndex))
828 {
829 /*
830 * Get colorant index...
831 */
832
833 i = packet->object_name[prtMarkerSuppliesColorantIndexOffset];
834 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
835 packet->object_type != CUPS_ASN1_INTEGER)
836 return;
837
838 fprintf(stderr, "DEBUG2: prtMarkerSuppliesColorantIndex.1.%d = %d\n", i,
839 packet->object_value.integer);
840
841 if (i > num_supplies)
842 num_supplies = i;
843
844 supplies[i - 1].colorant = packet->object_value.integer;
845 }
846 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesDescription))
847 {
848 /*
849 * Get supply name/description...
850 */
851
852 i = packet->object_name[prtMarkerSuppliesDescriptionOffset];
853 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
854 packet->object_type != CUPS_ASN1_OCTET_STRING)
855 return;
856
857 if (i > num_supplies)
858 num_supplies = i;
859
860 switch (charset)
861 {
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));
868 break;
869
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);
875 break;
876
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);
882 break;
883
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));
891 break;
892
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);
900 break;
901
902 default :
903 /*
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...
906 */
907
908 {
909 char *src, *dst; /* Pointers into strings */
910
911 /*
912 * Loop safe because both the object_value and supplies char arrays
913 * are CUPS_SNMP_MAX_STRING elements long.
914 */
915
916 for (src = (char *)packet->object_value.string.bytes,
917 dst = supplies[i - 1].name;
918 *src;
919 src ++)
920 {
921 if ((*src & 0x80) || *src < ' ' || *src == 0x7f)
922 *dst++ = '?';
923 else
924 *dst++ = *src;
925 }
926
927 *dst = '\0';
928 }
929 break;
930 }
931
932 fprintf(stderr, "DEBUG2: prtMarkerSuppliesDescription.1.%d = \"%s\"\n", i,
933 supplies[i - 1].name);
934
935 }
936 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesLevel))
937 {
938 /*
939 * Get level...
940 */
941
942 i = packet->object_name[prtMarkerSuppliesLevelOffset];
943 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
944 packet->object_type != CUPS_ASN1_INTEGER)
945 return;
946
947 fprintf(stderr, "DEBUG2: prtMarkerSuppliesLevel.1.%d = %d\n", i,
948 packet->object_value.integer);
949
950 if (i > num_supplies)
951 num_supplies = i;
952
953 supplies[i - 1].level = packet->object_value.integer;
954 }
955 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesMaxCapacity) &&
956 !(quirks & CUPS_SNMP_CAPACITY))
957 {
958 /*
959 * Get max capacity...
960 */
961
962 i = packet->object_name[prtMarkerSuppliesMaxCapacityOffset];
963 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
964 packet->object_type != CUPS_ASN1_INTEGER)
965 return;
966
967 fprintf(stderr, "DEBUG2: prtMarkerSuppliesMaxCapacity.1.%d = %d\n", i,
968 packet->object_value.integer);
969
970 if (i > num_supplies)
971 num_supplies = i;
972
973 if (supplies[i - 1].max_capacity == 0 &&
974 packet->object_value.integer > 0)
975 supplies[i - 1].max_capacity = packet->object_value.integer;
976 }
977 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesClass))
978 {
979 /*
980 * Get marker class...
981 */
982
983 i = packet->object_name[prtMarkerSuppliesClassOffset];
984 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
985 packet->object_type != CUPS_ASN1_INTEGER)
986 return;
987
988 fprintf(stderr, "DEBUG2: prtMarkerSuppliesClass.1.%d = %d\n", i,
989 packet->object_value.integer);
990
991 if (i > num_supplies)
992 num_supplies = i;
993
994 supplies[i - 1].sclass = packet->object_value.integer;
995 }
996 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesType))
997 {
998 /*
999 * Get marker type...
1000 */
1001
1002 i = packet->object_name[prtMarkerSuppliesTypeOffset];
1003 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
1004 packet->object_type != CUPS_ASN1_INTEGER)
1005 return;
1006
1007 fprintf(stderr, "DEBUG2: prtMarkerSuppliesType.1.%d = %d\n", i,
1008 packet->object_value.integer);
1009
1010 if (i > num_supplies)
1011 num_supplies = i;
1012
1013 supplies[i - 1].type = packet->object_value.integer;
1014 }
1015 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesSupplyUnit))
1016 {
1017 /*
1018 * Get units for capacity...
1019 */
1020
1021 i = packet->object_name[prtMarkerSuppliesSupplyUnitOffset];
1022 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
1023 packet->object_type != CUPS_ASN1_INTEGER)
1024 return;
1025
1026 fprintf(stderr, "DEBUG2: prtMarkerSuppliesSupplyUnit.1.%d = %d\n", i,
1027 packet->object_value.integer);
1028
1029 if (i > num_supplies)
1030 num_supplies = i;
1031
1032 if (packet->object_value.integer == CUPS_TC_percent)
1033 supplies[i - 1].max_capacity = 100;
1034 }
1035 }
1036
1037
1038 /*
1039 * 'utf16_to_utf8()' - Convert UTF-16 text to UTF-8.
1040 */
1041
1042 static void
1043 utf16_to_utf8(
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? */
1049 {
1050 cups_utf32_t ch, /* Current character */
1051 temp[CUPS_SNMP_MAX_STRING],
1052 /* UTF-32 string */
1053 *ptr; /* Pointer into UTF-32 string */
1054
1055
1056 for (ptr = temp; srcsize >= 2;)
1057 {
1058 if (le)
1059 ch = (cups_utf32_t)(src[0] | (src[1] << 8));
1060 else
1061 ch = (cups_utf32_t)((src[0] << 8) | src[1]);
1062
1063 src += 2;
1064 srcsize -= 2;
1065
1066 if (ch >= 0xd800 && ch <= 0xdbff && srcsize >= 2)
1067 {
1068 /*
1069 * Multi-word UTF-16 char...
1070 */
1071
1072 cups_utf32_t lch; /* Lower word */
1073
1074
1075 if (le)
1076 lch = (cups_utf32_t)(src[0] | (src[1] << 8));
1077 else
1078 lch = (cups_utf32_t)((src[0] << 8) | src[1]);
1079
1080 if (lch >= 0xdc00 && lch <= 0xdfff)
1081 {
1082 src += 2;
1083 srcsize -= 2;
1084
1085 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1086 }
1087 }
1088
1089 if (ptr < (temp + CUPS_SNMP_MAX_STRING - 1))
1090 *ptr++ = ch;
1091 }
1092
1093 *ptr = '\0';
1094
1095 cupsUTF32ToUTF8(dst, temp, (int)dstsize);
1096 }