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