]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/snmp-supplies.c
d6d8ae1b968e8771b30dc00e6c4fc4688cf3a596
[thirdparty/cups.git] / backend / snmp-supplies.c
1 /*
2 * "$Id$"
3 *
4 * SNMP supplies functions for CUPS.
5 *
6 * Copyright 2008-2011 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 1
40 #define CUPS_DEVELOPER_EMPTY 2
41 #define CUPS_MARKER_SUPPLY_LOW 4
42 #define CUPS_MARKER_SUPPLY_EMPTY 8
43 #define CUPS_MARKER_WASTE_ALMOST_FULL 16
44 #define CUPS_MARKER_WASTE_FULL 32
45 #define CUPS_OPC_NEAR_EOL 64
46 #define CUPS_OPC_LIFE_OVER 128
47 #define CUPS_TONER_LOW 256
48 #define CUPS_TONER_EMPTY 512
49
50
51 /*
52 * Local structures...
53 */
54
55 typedef struct /**** Printer supply data ****/
56 {
57 char name[CUPS_SNMP_MAX_STRING], /* Name of supply */
58 color[8]; /* Color: "#RRGGBB" or "none" */
59 int colorant, /* Colorant index */
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 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 prtMarkerSuppliesType[] =
145 { CUPS_OID_prtMarkerSuppliesType, -1 },
146 /* Type OID */
147 prtMarkerSuppliesTypeOffset =
148 (sizeof(prtMarkerSuppliesType) /
149 sizeof(prtMarkerSuppliesType[0]));
150 /* Offset to supply index */
151
152 static const backend_state_t const printer_states[] =
153 {
154 { CUPS_TC_lowPaper, "media-low-report" },
155 { CUPS_TC_noPaper | CUPS_TC_inputTrayEmpty, "media-empty-warning" },
156 /* { CUPS_TC_lowToner, "toner-low-report" }, */ /* now use prtMarkerSupplies */
157 /* { CUPS_TC_noToner, "toner-empty-warning" }, */ /* now use prtMarkerSupplies */
158 { CUPS_TC_doorOpen, "door-open-report" },
159 { CUPS_TC_jammed, "media-jam-warning" },
160 /* { CUPS_TC_offline, "offline-report" }, */ /* unreliable */
161 /* { CUPS_TC_serviceRequested | CUPS_TC_overduePreventMaint, "service-needed-warning" }, */ /* unreliable */
162 { CUPS_TC_inputTrayMissing, "input-tray-missing-warning" },
163 { CUPS_TC_outputTrayMissing, "output-tray-missing-warning" },
164 { CUPS_TC_markerSupplyMissing, "marker-supply-missing-warning" },
165 { CUPS_TC_outputNearFull, "output-area-almost-full-report" },
166 { CUPS_TC_outputFull, "output-area-full-warning" }
167 };
168
169 static const backend_state_t const supply_states[] =
170 {
171 { CUPS_DEVELOPER_LOW, "developer-low-report" },
172 { CUPS_DEVELOPER_EMPTY, "developer-empty-warning" },
173 { CUPS_MARKER_SUPPLY_LOW, "marker-supply-low-report" },
174 { CUPS_MARKER_SUPPLY_EMPTY, "marker-supply-empty-warning" },
175 { CUPS_MARKER_WASTE_ALMOST_FULL, "marker-waste-almost-full-report" },
176 { CUPS_MARKER_WASTE_FULL, "marker-waste-full-warning" },
177 { CUPS_OPC_NEAR_EOL, "opc-near-eol-report" },
178 { CUPS_OPC_LIFE_OVER, "opc-life-over-warning" },
179 { CUPS_TONER_LOW, "toner-low-report" },
180 { CUPS_TONER_EMPTY, "toner-empty-warning" }
181 };
182
183
184 /*
185 * Local functions...
186 */
187
188 static void backend_init_supplies(int snmp_fd, http_addr_t *addr);
189 static void backend_walk_cb(cups_snmp_t *packet, void *data);
190 static void utf16_to_utf8(cups_utf8_t *dst, const unsigned char *src,
191 size_t srcsize, size_t dstsize, int le);
192
193
194 /*
195 * 'backendSNMPSupplies()' - Get the current supplies for a device.
196 */
197
198 int /* O - 0 on success, -1 on error */
199 backendSNMPSupplies(
200 int snmp_fd, /* I - SNMP socket */
201 http_addr_t *addr, /* I - Printer address */
202 int *page_count, /* O - Page count */
203 int *printer_state) /* O - Printer state */
204 {
205 if (!httpAddrEqual(addr, &current_addr))
206 backend_init_supplies(snmp_fd, addr);
207 else if (num_supplies > 0)
208 _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
209 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesLevel,
210 CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL);
211
212 if (page_count)
213 *page_count = -1;
214
215 if (printer_state)
216 *printer_state = -1;
217
218 if (num_supplies > 0)
219 {
220 int i, /* Looping var */
221 percent, /* Percent full */
222 new_state, /* New state value */
223 change_state, /* State change */
224 new_supply_state = 0; /* Supply state */
225 char value[CUPS_MAX_SUPPLIES * 4],
226 /* marker-levels value string */
227 *ptr; /* Pointer into value string */
228 cups_snmp_t packet; /* SNMP response packet */
229
230 /*
231 * Generate the marker-levels value string...
232 */
233
234 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr))
235 {
236 percent = 100 * supplies[i].level / supplies[i].max_capacity;
237
238 if (percent <= 10)
239 {
240 switch (supplies[i].type)
241 {
242 case CUPS_TC_toner :
243 case CUPS_TC_tonerCartridge :
244 if (percent <= 1)
245 new_supply_state |= CUPS_TONER_EMPTY;
246 else
247 new_supply_state |= CUPS_TONER_LOW;
248 break;
249 case CUPS_TC_wasteToner :
250 case CUPS_TC_wasteInk :
251 if (percent <= 1)
252 new_supply_state |= CUPS_MARKER_WASTE_FULL;
253 else
254 new_supply_state |= CUPS_MARKER_WASTE_ALMOST_FULL;
255 break;
256 case CUPS_TC_ink :
257 case CUPS_TC_inkCartridge :
258 case CUPS_TC_inkRibbon :
259 case CUPS_TC_solidWax :
260 case CUPS_TC_ribbonWax :
261 if (percent <= 1)
262 new_supply_state |= CUPS_MARKER_SUPPLY_EMPTY;
263 else
264 new_supply_state |= CUPS_MARKER_SUPPLY_LOW;
265 break;
266 case CUPS_TC_developer :
267 if (percent <= 1)
268 new_supply_state |= CUPS_DEVELOPER_EMPTY;
269 else
270 new_supply_state |= CUPS_DEVELOPER_LOW;
271 break;
272 case CUPS_TC_coronaWire :
273 case CUPS_TC_fuser :
274 case CUPS_TC_opc :
275 case CUPS_TC_transferUnit :
276 if (percent <= 1)
277 new_supply_state |= CUPS_OPC_LIFE_OVER;
278 else
279 new_supply_state |= CUPS_OPC_NEAR_EOL;
280 break;
281 }
282 }
283
284 if (i)
285 *ptr++ = ',';
286
287 if (supplies[i].max_capacity > 0)
288 sprintf(ptr, "%d", percent);
289 else
290 strcpy(ptr, "-1");
291 }
292
293 fprintf(stderr, "ATTR: marker-levels=%s\n", value);
294
295 if (supply_state < 0)
296 change_state = 0xffff;
297 else
298 change_state = supply_state ^ new_supply_state;
299
300 fprintf(stderr, "DEBUG: new_supply_state=%x, change_state=%x\n",
301 new_supply_state, change_state);
302
303 for (i = 0;
304 i < (int)(sizeof(supply_states) / sizeof(supply_states[0]));
305 i ++)
306 if (change_state & supply_states[i].bit)
307 {
308 fprintf(stderr, "STATE: %c%s\n",
309 (new_supply_state & supply_states[i].bit) ? '+' : '-',
310 supply_states[i].keyword);
311 }
312
313 supply_state = new_supply_state;
314
315 /*
316 * Get the current printer status bits...
317 */
318
319 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
320 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
321 hrPrinterDetectedErrorState))
322 return (-1);
323
324 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
325 packet.object_type != CUPS_ASN1_OCTET_STRING)
326 return (-1);
327
328 if (packet.object_value.string.num_bytes == 2)
329 new_state = (packet.object_value.string.bytes[0] << 8) |
330 packet.object_value.string.bytes[1];
331 else if (packet.object_value.string.num_bytes == 1)
332 new_state = (packet.object_value.string.bytes[0] << 8);
333 else
334 new_state = 0;
335
336 if (current_state < 0)
337 change_state = 0xffff;
338 else
339 change_state = current_state ^ new_state;
340
341 fprintf(stderr, "DEBUG: new_state=%x, change_state=%x\n", new_state,
342 change_state);
343
344 for (i = 0;
345 i < (int)(sizeof(printer_states) / sizeof(printer_states[0]));
346 i ++)
347 if (change_state & printer_states[i].bit)
348 {
349 fprintf(stderr, "STATE: %c%s\n",
350 (new_state & printer_states[i].bit) ? '+' : '-',
351 printer_states[i].keyword);
352 }
353
354 current_state = new_state;
355
356 /*
357 * Get the current printer state...
358 */
359
360 if (printer_state)
361 {
362 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
363 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
364 hrPrinterStatus))
365 return (-1);
366
367 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
368 packet.object_type != CUPS_ASN1_INTEGER)
369 return (-1);
370
371 *printer_state = packet.object_value.integer;
372 }
373
374 /*
375 * Get the current page count...
376 */
377
378 if (page_count)
379 {
380 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
381 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
382 prtMarkerLifeCount))
383 return (-1);
384
385 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
386 packet.object_type != CUPS_ASN1_COUNTER)
387 return (-1);
388
389 *page_count = packet.object_value.counter;
390 }
391
392 return (0);
393 }
394 else
395 return (-1);
396 }
397
398
399 /*
400 * 'backend_init_supplies()' - Initialize the supplies list.
401 */
402
403 static void
404 backend_init_supplies(
405 int snmp_fd, /* I - SNMP socket */
406 http_addr_t *addr) /* I - Printer address */
407 {
408 int i, /* Looping var */
409 type; /* Current marker type */
410 cups_file_t *cachefile; /* Cache file */
411 const char *cachedir; /* CUPS_CACHEDIR value */
412 char addrstr[1024], /* Address string */
413 cachefilename[1024], /* Cache filename */
414 description[CUPS_SNMP_MAX_STRING],
415 /* Device description string */
416 value[CUPS_MAX_SUPPLIES * (CUPS_SNMP_MAX_STRING * 2 + 3)],
417 /* Value string */
418 *ptr, /* Pointer into value string */
419 *name_ptr; /* Pointer into name string */
420 cups_snmp_t packet; /* SNMP response packet */
421 ppd_file_t *ppd; /* PPD file for this queue */
422 ppd_attr_t *ppdattr; /* cupsSNMPSupplies attribute */
423 static const char * const types[] = /* Supply types */
424 {
425 "other",
426 "unknown",
427 "toner",
428 "wasteToner",
429 "ink",
430 "inkCartridge",
431 "inkRibbon",
432 "wasteInk",
433 "opc",
434 "developer",
435 "fuserOil",
436 "solidWax",
437 "ribbonWax",
438 "wasteWax",
439 "fuser",
440 "coronaWire",
441 "fuserOilWick",
442 "cleanerUnit",
443 "fuserCleaningPad",
444 "transferUnit",
445 "tonerCartridge",
446 "fuserOiler",
447 "water",
448 "wasteWater",
449 "glueWaterAdditive",
450 "wastePaper",
451 "bindingSupply",
452 "bandingSupply",
453 "stitchingWire",
454 "shrinkWrap",
455 "paperWrap",
456 "staples",
457 "inserts",
458 "covers"
459 };
460
461
462 /*
463 * Reset state information...
464 */
465
466 current_addr = *addr;
467 current_state = -1;
468 num_supplies = -1;
469 charset = -1;
470
471 memset(supplies, 0, sizeof(supplies));
472
473 /*
474 * See if we should be getting supply levels via SNMP...
475 */
476
477 if ((ppd = ppdOpenFile(getenv("PPD"))) == NULL ||
478 ((ppdattr = ppdFindAttr(ppd, "cupsSNMPSupplies", NULL)) != NULL &&
479 ppdattr->value && strcasecmp(ppdattr->value, "true")))
480 {
481 ppdClose(ppd);
482 return;
483 }
484
485 ppdClose(ppd);
486
487 /*
488 * Get the device description...
489 */
490
491 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
492 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
493 hrDeviceDescr))
494 return;
495
496 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
497 packet.object_type != CUPS_ASN1_OCTET_STRING)
498 {
499 strlcpy(description, "Unknown", sizeof(description));
500 num_supplies = 0;
501 }
502 else
503 strlcpy(description, (char *)packet.object_value.string.bytes,
504 sizeof(description));
505
506 fprintf(stderr, "DEBUG2: hrDeviceDesc=\"%s\"\n", description);
507
508 /*
509 * See if we have already queried this device...
510 */
511
512 httpAddrString(addr, addrstr, sizeof(addrstr));
513
514 if ((cachedir = getenv("CUPS_CACHEDIR")) == NULL)
515 cachedir = CUPS_CACHEDIR;
516
517 snprintf(cachefilename, sizeof(cachefilename), "%s/%s.snmp", cachedir,
518 addrstr);
519
520 if ((cachefile = cupsFileOpen(cachefilename, "r")) != NULL)
521 {
522 /*
523 * Yes, read the cache file:
524 *
525 * 2 num_supplies charset
526 * device description
527 * supply structures...
528 */
529
530 if (cupsFileGets(cachefile, value, sizeof(value)))
531 {
532 if (sscanf(value, "2 %d%d", &num_supplies, &charset) == 2 &&
533 num_supplies <= CUPS_MAX_SUPPLIES &&
534 cupsFileGets(cachefile, value, sizeof(value)))
535 {
536 if (!strcmp(description, value))
537 cupsFileRead(cachefile, (char *)supplies,
538 num_supplies * sizeof(backend_supplies_t));
539 else
540 {
541 num_supplies = -1;
542 charset = -1;
543 }
544 }
545 else
546 {
547 num_supplies = -1;
548 charset = -1;
549 }
550 }
551
552 cupsFileClose(cachefile);
553 }
554
555 /*
556 * If the cache information isn't correct, scan for supplies...
557 */
558
559 if (charset < 0)
560 {
561 /*
562 * Get the configured character set...
563 */
564
565 int oid[CUPS_SNMP_MAX_OID]; /* OID for character set */
566
567
568 if (!_cupsSNMPWrite(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
569 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
570 prtGeneralCurrentLocalization))
571 return;
572
573 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
574 packet.object_type != CUPS_ASN1_INTEGER)
575 {
576 fprintf(stderr,
577 "DEBUG: prtGeneralCurrentLocalization type is %x, expected %x!\n",
578 packet.object_type, CUPS_ASN1_INTEGER);
579 return;
580 }
581
582 fprintf(stderr, "DEBUG2: prtGeneralCurrentLocalization=%d\n",
583 packet.object_value.integer);
584
585 _cupsSNMPCopyOID(oid, prtLocalizationCharacterSet, CUPS_SNMP_MAX_OID);
586 oid[prtLocalizationCharacterSetOffset - 2] = packet.object_value.integer;
587
588
589 if (!_cupsSNMPWrite(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
590 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
591 oid))
592 return;
593
594 if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
595 packet.object_type != CUPS_ASN1_INTEGER)
596 {
597 fprintf(stderr,
598 "DEBUG: prtLocalizationCharacterSet type is %x, expected %x!\n",
599 packet.object_type, CUPS_ASN1_INTEGER);
600 return;
601 }
602
603 fprintf(stderr, "DEBUG2: prtLocalizationCharacterSet=%d\n",
604 packet.object_value.integer);
605 charset = packet.object_value.integer;
606 }
607
608 if (num_supplies < 0)
609 {
610 /*
611 * Walk the printer configuration information...
612 */
613
614 _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
615 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesEntry,
616 CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL);
617 }
618
619 /*
620 * Save the cached information...
621 */
622
623 if (num_supplies < 0)
624 num_supplies = 0;
625
626 if ((cachefile = cupsFileOpen(cachefilename, "w")) != NULL)
627 {
628 cupsFilePrintf(cachefile, "2 %d %d\n", num_supplies, charset);
629 cupsFilePrintf(cachefile, "%s\n", description);
630
631 if (num_supplies > 0)
632 cupsFileWrite(cachefile, (char *)supplies,
633 num_supplies * sizeof(backend_supplies_t));
634
635 cupsFileClose(cachefile);
636 }
637
638 if (num_supplies <= 0)
639 return;
640
641 /*
642 * Get the colors...
643 */
644
645 for (i = 0; i < num_supplies; i ++)
646 strcpy(supplies[i].color, "none");
647
648 _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
649 _cupsSNMPDefaultCommunity(), prtMarkerColorantValue,
650 CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL);
651
652 /*
653 * Output the marker-colors attribute...
654 */
655
656 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr))
657 {
658 if (i)
659 *ptr++ = ',';
660
661 strcpy(ptr, supplies[i].color);
662 }
663
664 fprintf(stderr, "ATTR: marker-colors=%s\n", value);
665
666 /*
667 * Output the marker-names attribute...
668 */
669
670 for (i = 0, ptr = value; i < num_supplies; i ++)
671 {
672 if (i)
673 *ptr++ = ',';
674
675 *ptr++ = '\"';
676 for (name_ptr = supplies[i].name; *name_ptr;)
677 {
678 if (*name_ptr == '\\' || *name_ptr == '\"')
679 *ptr++ = '\\';
680
681 *ptr++ = *name_ptr++;
682 }
683 *ptr++ = '\"';
684 }
685
686 *ptr = '\0';
687
688 fprintf(stderr, "ATTR: marker-names=%s\n", value);
689
690 /*
691 * Output the marker-types attribute...
692 */
693
694 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr))
695 {
696 if (i)
697 *ptr++ = ',';
698
699 type = supplies[i].type;
700
701 if (type < CUPS_TC_other || type > CUPS_TC_covers)
702 strcpy(ptr, "unknown");
703 else
704 strcpy(ptr, types[type - CUPS_TC_other]);
705 }
706
707 fprintf(stderr, "ATTR: marker-types=%s\n", value);
708 }
709
710
711 /*
712 * 'backend_walk_cb()' - Interpret the supply value responses.
713 */
714
715 static void
716 backend_walk_cb(cups_snmp_t *packet, /* I - SNMP packet */
717 void *data) /* I - User data (unused) */
718 {
719 int i, j, k; /* Looping vars */
720 static const char * const colors[8][2] =
721 { /* Standard color names */
722 { "black", "#000000" },
723 { "blue", "#0000FF" },
724 { "cyan", "#00FFFF" },
725 { "green", "#00FF00" },
726 { "magenta", "#FF00FF" },
727 { "red", "#FF0000" },
728 { "white", "#FFFFFF" },
729 { "yellow", "#FFFF00" }
730 };
731
732
733 (void)data;
734
735 if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerColorantValue) &&
736 packet->object_type == CUPS_ASN1_OCTET_STRING)
737 {
738 /*
739 * Get colorant...
740 */
741
742 i = packet->object_name[prtMarkerColorantValueOffset];
743
744 fprintf(stderr, "DEBUG2: prtMarkerColorantValue.1.%d = \"%s\"\n", i,
745 (char *)packet->object_value.string.bytes);
746
747 for (j = 0; j < num_supplies; j ++)
748 if (supplies[j].colorant == i)
749 {
750 for (k = 0; k < (int)(sizeof(colors) / sizeof(colors[0])); k ++)
751 if (!strcmp(colors[k][0], (char *)packet->object_value.string.bytes))
752 {
753 strcpy(supplies[j].color, colors[k][1]);
754 break;
755 }
756 }
757 }
758 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesColorantIndex))
759 {
760 /*
761 * Get colorant index...
762 */
763
764 i = packet->object_name[prtMarkerSuppliesColorantIndexOffset];
765 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
766 packet->object_type != CUPS_ASN1_INTEGER)
767 return;
768
769 fprintf(stderr, "DEBUG2: prtMarkerSuppliesColorantIndex.1.%d = %d\n", i,
770 packet->object_value.integer);
771
772 if (i > num_supplies)
773 num_supplies = i;
774
775 supplies[i - 1].colorant = packet->object_value.integer;
776 }
777 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesDescription))
778 {
779 /*
780 * Get supply name/description...
781 */
782
783 i = packet->object_name[prtMarkerSuppliesDescriptionOffset];
784 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
785 packet->object_type != CUPS_ASN1_OCTET_STRING)
786 return;
787
788 if (i > num_supplies)
789 num_supplies = i;
790
791 switch (charset)
792 {
793 case CUPS_TC_csASCII :
794 case CUPS_TC_csUTF8 :
795 case CUPS_TC_csUnicodeASCII :
796 strlcpy(supplies[i - 1].name,
797 (char *)packet->object_value.string.bytes,
798 sizeof(supplies[0].name));
799 break;
800
801 case CUPS_TC_csISOLatin1 :
802 case CUPS_TC_csUnicodeLatin1 :
803 cupsCharsetToUTF8((cups_utf8_t *)supplies[i - 1].name,
804 (char *)packet->object_value.string.bytes,
805 sizeof(supplies[0].name), CUPS_ISO8859_1);
806 break;
807
808 case CUPS_TC_csShiftJIS :
809 cupsCharsetToUTF8((cups_utf8_t *)supplies[i - 1].name,
810 (char *)packet->object_value.string.bytes,
811 sizeof(supplies[0].name), CUPS_JIS_X0213);
812 break;
813
814 case CUPS_TC_csUCS4 :
815 case CUPS_TC_csUTF32 :
816 case CUPS_TC_csUTF32BE :
817 case CUPS_TC_csUTF32LE :
818 cupsUTF32ToUTF8((cups_utf8_t *)supplies[i - 1].name,
819 (cups_utf32_t *)packet->object_value.string.bytes,
820 sizeof(supplies[0].name));
821 break;
822
823 case CUPS_TC_csUnicode :
824 case CUPS_TC_csUTF16BE :
825 case CUPS_TC_csUTF16LE :
826 utf16_to_utf8((cups_utf8_t *)supplies[i - 1].name,
827 packet->object_value.string.bytes,
828 packet->object_value.string.num_bytes,
829 sizeof(supplies[0].name), charset == CUPS_TC_csUTF16LE);
830 break;
831
832 default :
833 /*
834 * If we get here, the printer is using an unknown character set and
835 * we just want to copy characters that look like ASCII...
836 */
837
838 {
839 char *src, *dst; /* Pointers into strings */
840
841
842 /*
843 * Loop safe because both the object_value and supplies char arrays
844 * are CUPS_SNMP_MAX_STRING elements long.
845 */
846
847 for (src = (char *)packet->object_value.string.bytes,
848 dst = supplies[i - 1].name;
849 *src;
850 src ++)
851 {
852 if ((*src & 0x80) || *src < ' ' || *src == 0x7f)
853 *dst++ = '?';
854 else
855 *dst++ = *src;
856 }
857
858 *dst = '\0';
859 }
860 break;
861 }
862
863 fprintf(stderr, "DEBUG2: prtMarkerSuppliesDescription.1.%d = \"%s\"\n", i,
864 supplies[i - 1].name);
865
866 }
867 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesLevel))
868 {
869 /*
870 * Get level...
871 */
872
873 i = packet->object_name[prtMarkerSuppliesLevelOffset];
874 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
875 packet->object_type != CUPS_ASN1_INTEGER)
876 return;
877
878 fprintf(stderr, "DEBUG2: prtMarkerSuppliesLevel.1.%d = %d\n", i,
879 packet->object_value.integer);
880
881 if (i > num_supplies)
882 num_supplies = i;
883
884 supplies[i - 1].level = packet->object_value.integer;
885 }
886 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesMaxCapacity))
887 {
888 /*
889 * Get max capacity...
890 */
891
892 i = packet->object_name[prtMarkerSuppliesMaxCapacityOffset];
893 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
894 packet->object_type != CUPS_ASN1_INTEGER)
895 return;
896
897 fprintf(stderr, "DEBUG2: prtMarkerSuppliesMaxCapacity.1.%d = %d\n", i,
898 packet->object_value.integer);
899
900 if (i > num_supplies)
901 num_supplies = i;
902
903 supplies[i - 1].max_capacity = packet->object_value.integer;
904 }
905 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesType))
906 {
907 /*
908 * Get marker type...
909 */
910
911 i = packet->object_name[prtMarkerSuppliesTypeOffset];
912 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
913 packet->object_type != CUPS_ASN1_INTEGER)
914 return;
915
916 fprintf(stderr, "DEBUG2: prtMarkerSuppliesType.1.%d = %d\n", i,
917 packet->object_value.integer);
918
919 if (i > num_supplies)
920 num_supplies = i;
921
922 supplies[i - 1].type = packet->object_value.integer;
923 }
924 }
925
926
927 /*
928 * 'utf16_to_utf8()' - Convert UTF-16 text to UTF-8.
929 */
930
931 static void
932 utf16_to_utf8(
933 cups_utf8_t *dst, /* I - Destination buffer */
934 const unsigned char *src, /* I - Source string */
935 size_t srcsize, /* I - Size of source string */
936 size_t dstsize, /* I - Size of destination buffer */
937 int le) /* I - Source is little-endian? */
938 {
939 cups_utf32_t ch, /* Current character */
940 temp[CUPS_SNMP_MAX_STRING],
941 /* UTF-32 string */
942 *ptr; /* Pointer into UTF-32 string */
943
944
945 for (ptr = temp; srcsize >= 2;)
946 {
947 if (le)
948 ch = src[0] | (src[1] << 8);
949 else
950 ch = (src[0] << 8) | src[1];
951
952 src += 2;
953 srcsize -= 2;
954
955 if (ch >= 0xd800 && ch <= 0xdbff && srcsize >= 2)
956 {
957 /*
958 * Multi-word UTF-16 char...
959 */
960
961 int lch; /* Lower word */
962
963
964 if (le)
965 lch = src[0] | (src[1] << 8);
966 else
967 lch = (src[0] << 8) | src[1];
968
969 if (lch >= 0xdc00 && lch <= 0xdfff)
970 {
971 src += 2;
972 srcsize -= 2;
973
974 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
975 }
976 }
977
978 if (ptr < (temp + CUPS_SNMP_MAX_STRING - 1))
979 *ptr++ = ch;
980 }
981
982 *ptr = '\0';
983
984 cupsUTF32ToUTF8(dst, temp, dstsize);
985 }
986
987
988 /*
989 * End of "$Id$".
990 */