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