1/*
2 * "$Id: snmp-supplies.c 11433 2013-11-20 18:57:44Z msweet $"
3 *
4 *   SNMP supplies functions for CUPS.
5 *
6 *   Copyright 2008-2013 by Apple Inc.
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.
20 *   backend_walk_cb()       - Interpret the supply value responses.
21 *   utf16_to_utf8()         - Convert UTF-16 text to UTF-8.
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 */
37#define CUPS_SUPPLY_TIMEOUT	2.0	/* Timeout for SNMP lookups */
38
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 */
51
52#define CUPS_SNMP_NONE		0x0000
53#define CUPS_SNMP_CAPACITY	0x0001	/* Supply levels reported as percentages */
54
55
56/*
57 * Local structures...
58 */
59
60typedef struct				/**** Printer supply data ****/
61{
62  char	name[CUPS_SNMP_MAX_STRING],	/* Name of supply */
63	color[8];			/* Color: "#RRGGBB" or "none" */
64  int	colorant,			/* Colorant index */
65	sclass,				/* Supply class */
66	type,				/* Supply type */
67	max_capacity,			/* Maximum capacity */
68	level;				/* Current level value */
69} backend_supplies_t;
70
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
77
78/*
79 * Local globals...
80 */
81
82static http_addr_t	current_addr;	/* Current address */
83static int		current_state = -1;
84					/* Current device state bits */
85static int		charset = -1;	/* Character set for supply names */
86static unsigned		quirks = CUPS_SNMP_NONE;
87					/* Quirks we have to work around */
88static int		num_supplies = 0;
89					/* Number of supplies found */
90static backend_supplies_t supplies[CUPS_MAX_SUPPLIES];
91					/* Supply information */
92static int		supply_state = -1;
93					/* Supply state info */
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 */
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]));
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 */
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 */
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 */
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 */
173
174static const backend_state_t printer_states[] =
175			{
176			  /* { CUPS_TC_lowPaper, "media-low-report" }, */
177			  { CUPS_TC_noPaper | CUPS_TC_inputTrayEmpty, "media-empty-warning" },
178			  /* { CUPS_TC_lowToner, "toner-low-report" }, */ /* now use prtMarkerSupplies */
179			  /* { CUPS_TC_noToner, "toner-empty-warning" }, */ /* now use prtMarkerSupplies */
180			  { CUPS_TC_doorOpen, "door-open-report" },
181			  { CUPS_TC_jammed, "media-jam-warning" },
182			  /* { CUPS_TC_offline, "offline-report" }, */ /* unreliable */
183			  /* { CUPS_TC_serviceRequested | CUPS_TC_overduePreventMaint, "service-needed-warning" }, */ /* unreliable */
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
191static const backend_state_t supply_states[] =
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" },
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" },
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" },
205			};
206
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);
214static void	utf16_to_utf8(cups_utf8_t *dst, const unsigned char *src,
215			      size_t srcsize, size_t dstsize, int le);
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)
232    _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
233		  _cupsSNMPDefaultCommunity(), prtMarkerSuppliesLevel,
234		  CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL);
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  {
244    int		i,			/* Looping var */
245		percent,		/* Percent full */
246		new_state,		/* New state value */
247		change_state,		/* State change */
248		new_supply_state = 0;	/* Supply state */
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 */
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    {
260      if (supplies[i].max_capacity > 0 && supplies[i].level >= 0)
261	percent = 100 * supplies[i].level / supplies[i].max_capacity;
262      else if (supplies[i].level >= 0 && supplies[i].level <= 100 &&
263               (quirks & CUPS_SNMP_CAPACITY))
264        percent = supplies[i].level;
265      else
266        percent = 50;
267
268      if (supplies[i].sclass == CUPS_TC_receptacleThatIsFilled)
269        percent = 100 - percent;
270
271      if (percent <= 5)
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;
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;
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;
324        }
325      }
326
327      if (i)
328        *ptr++ = ',';
329
330      if ((supplies[i].max_capacity > 0 || (quirks & CUPS_SNMP_CAPACITY)) &&
331          supplies[i].level >= 0)
332        snprintf(ptr, sizeof(value) - (ptr - value), "%d", percent);
333      else
334        strlcpy(ptr, "-1", sizeof(value) - (ptr - value));
335    }
336
337    fprintf(stderr, "ATTR: marker-levels=%s\n", value);
338
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
359   /*
360    * Get the current printer status bits...
361    */
362
363    if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
364                       _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
365                       hrPrinterDetectedErrorState))
366      return (-1);
367
368    if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
369        packet.object_type != CUPS_ASN1_OCTET_STRING)
370      return (-1);
371
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];
375    else if (packet.object_value.string.num_bytes == 1)
376      new_state = (packet.object_value.string.bytes[0] << 8);
377    else
378      new_state = 0;
379
380    if (current_state < 0)
381      change_state = 0xffff;
382    else
383      change_state = current_state ^ new_state;
384
385    fprintf(stderr, "DEBUG: new_state=%x, change_state=%x\n", new_state,
386            change_state);
387
388    for (i = 0;
389         i < (int)(sizeof(printer_states) / sizeof(printer_states[0]));
390         i ++)
391      if (change_state & printer_states[i].bit)
392      {
393	fprintf(stderr, "STATE: %c%s\n",
394		(new_state & printer_states[i].bit) ? '+' : '-',
395		printer_states[i].keyword);
396      }
397
398    current_state = new_state;
399
400   /*
401    * Get the current printer state...
402    */
403
404    if (printer_state)
405    {
406      if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
407			 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
408			 hrPrinterStatus))
409	return (-1);
410
411      if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
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    {
424      if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
425			 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
426			 prtMarkerLifeCount))
427	return (-1);
428
429      if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
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 */
460		value[CUPS_MAX_SUPPLIES * (CUPS_SNMP_MAX_STRING * 4 + 3)],
461					/* Value string */
462		*ptr,			/* Pointer into value string */
463		*name_ptr;		/* Pointer into name string */
464  cups_snmp_t	packet;			/* SNMP response packet */
465  ppd_file_t	*ppd;			/* PPD file for this queue */
466  ppd_attr_t	*ppdattr;		/* cupsSNMPSupplies attribute */
467  static const char * const types[] =	/* Supply types */
468		{
469		  "other",
470		  "unknown",
471		  "toner",
472		  "waste-toner",
473		  "ink",
474		  "ink-cartridge",
475		  "ink-ribbon",
476		  "waste-ink",
477		  "opc",
478		  "developer",
479		  "fuser-oil",
480		  "solid-wax",
481		  "ribbon-wax",
482		  "waste-wax",
483		  "fuser",
484		  "corona-wire",
485		  "fuser-oil-wick",
486		  "cleaner-unit",
487		  "fuser-cleaning-pad",
488		  "transfer-unit",
489		  "toner-cartridge",
490		  "fuser-oiler",
491		  "water",
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",
500		  "staples",
501		  "inserts",
502		  "covers"
503		};
504
505
506 /*
507  * Reset state information...
508  */
509
510  current_addr  = *addr;
511  current_state = -1;
512  num_supplies  = -1;
513  charset       = -1;
514
515  memset(supplies, 0, sizeof(supplies));
516
517 /*
518  * See if we should be getting supply levels via SNMP...
519  */
520
521  if ((ppd = ppdOpenFile(getenv("PPD"))) == NULL ||
522      ((ppdattr = ppdFindAttr(ppd, "cupsSNMPSupplies", NULL)) != NULL &&
523       ppdattr->value && _cups_strcasecmp(ppdattr->value, "true")))
524  {
525    ppdClose(ppd);
526    return;
527  }
528
529  if ((ppdattr = ppdFindAttr(ppd, "cupsSNMPQuirks", NULL)) != NULL)
530  {
531    if (!_cups_strcasecmp(ppdattr->value, "capacity"))
532      quirks |= CUPS_SNMP_CAPACITY;
533  }
534
535  ppdClose(ppd);
536
537 /*
538  * Get the device description...
539  */
540
541  if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
542		     _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
543		     hrDeviceDescr))
544    return;
545
546  if (!_cupsSNMPRead(snmp_fd, &packet, CUPS_SUPPLY_TIMEOUT) ||
547      packet.object_type != CUPS_ASN1_OCTET_STRING)
548  {
549    strlcpy(description, "Unknown", sizeof(description));
550    num_supplies = 0;
551  }
552  else
553    strlcpy(description, (char *)packet.object_value.string.bytes,
554            sizeof(description));
555
556  fprintf(stderr, "DEBUG2: hrDeviceDesc=\"%s\"\n", description);
557
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    *
575    *     3 num_supplies charset
576    *     device description
577    *     supply structures...
578    */
579
580    if (cupsFileGets(cachefile, value, sizeof(value)))
581    {
582      if (sscanf(value, "3 %d%d", &num_supplies, &charset) == 2 &&
583          num_supplies <= CUPS_MAX_SUPPLIES &&
584          cupsFileGets(cachefile, value, sizeof(value)))
585      {
586        if (!strcmp(description, value))
587	  cupsFileRead(cachefile, (char *)supplies,
588	               num_supplies * sizeof(backend_supplies_t));
589        else
590	{
591	  num_supplies = -1;
592	  charset      = -1;
593	}
594      }
595      else
596      {
597        num_supplies = -1;
598	charset      = -1;
599      }
600    }
601
602    cupsFileClose(cachefile);
603  }
604
605 /*
606  * If the cache information isn't correct, scan for supplies...
607  */
608
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
658  if (num_supplies < 0)
659  {
660   /*
661    * Walk the printer configuration information...
662    */
663
664    _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
665		  _cupsSNMPDefaultCommunity(), prtMarkerSuppliesEntry,
666		  CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL);
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  {
678    cupsFilePrintf(cachefile, "3 %d %d\n", num_supplies, charset);
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 ++)
696    strlcpy(supplies[i].color, "none", sizeof(supplies[i].color));
697
698  _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
699                _cupsSNMPDefaultCommunity(), prtMarkerColorantValue,
700	        CUPS_SUPPLY_TIMEOUT, backend_walk_cb, NULL);
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
711    strlcpy(ptr, supplies[i].color, sizeof(value) - (ptr - value));
712  }
713
714  fprintf(stderr, "ATTR: marker-colors=%s\n", value);
715
716 /*
717  * Output the marker-names attribute (the double quoting is necessary to deal
718  * with embedded quotes and commas in the marker names...)
719  */
720
721  for (i = 0, ptr = value; i < num_supplies; i ++)
722  {
723    if (i)
724      *ptr++ = ',';
725
726    *ptr++ = '\'';
727    *ptr++ = '\"';
728    for (name_ptr = supplies[i].name; *name_ptr;)
729    {
730      if (*name_ptr == '\\' || *name_ptr == '\"' || *name_ptr == '\'')
731      {
732        *ptr++ = '\\';
733        *ptr++ = '\\';
734        *ptr++ = '\\';
735      }
736
737      *ptr++ = *name_ptr++;
738    }
739    *ptr++ = '\"';
740    *ptr++ = '\'';
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)
759      strlcpy(ptr, "unknown", sizeof(value) - (ptr - value));
760    else
761      strlcpy(ptr, types[type - CUPS_TC_other], sizeof(value) - (ptr - value));
762  }
763
764  fprintf(stderr, "ATTR: marker-types=%s\n", value);
765}
766
767
768/*
769 * 'backend_walk_cb()' - Interpret the supply value responses.
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 */
777  static const char * const colors[][2] =
778  {					/* Standard color names */
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" }
804  };
805
806
807  (void)data;
808
809  if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerColorantValue) &&
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,
819            (char *)packet->object_value.string.bytes);
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 ++)
825	  if (!_cups_strcasecmp(colors[k][0],
826	                        (char *)packet->object_value.string.bytes))
827	  {
828	    strlcpy(supplies[j].color, colors[k][1], sizeof(supplies[j].color));
829	    break;
830	  }
831      }
832  }
833  else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesColorantIndex))
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  }
852  else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesDescription))
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
863    if (i > num_supplies)
864      num_supplies = i;
865
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 :
884      case CUPS_TC_csWindows31J : /* Close enough for our purposes */
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
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
941  }
942  else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesLevel))
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  }
961  else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesMaxCapacity) &&
962           !(quirks & CUPS_SNMP_CAPACITY))
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
979    if (supplies[i - 1].max_capacity == 0 &&
980        packet->object_value.integer > 0)
981      supplies[i - 1].max_capacity = packet->object_value.integer;
982  }
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  }
1002  else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesType))
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  }
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  }
1041}
1042
1043
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
1105/*
1106 * End of "$Id: snmp-supplies.c 11433 2013-11-20 18:57:44Z msweet $".
1107 */
1108