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