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