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