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