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