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