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