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