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