• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *   IEEE-1284 support functions for CUPS.
3  *
4  *   Copyright 2007-2011 by Apple Inc.
5  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
6  *
7  *   These coded instructions, statements, and computer programs are the
8  *   property of Apple Inc. and are protected by Federal copyright
9  *   law.  Distribution and use rights are outlined in the file "COPYING"
10  *   which should have been included with this file.
11  *
12  * Contents:
13  *
14  *   backendGetDeviceID()       - Get the IEEE-1284 device ID string and
15  *                                corresponding URI.
16  *   backendGetMakeModel()      - Get the make and model string from the device
17  *                                ID.
18  *   get_1284_values()          - Get 1284 device ID keys and values.
19  *   normalize_make_and_model() - Normalize a product/make-and-model string.
20  */
21 
22 /*
23  * Include necessary headers.
24  */
25 
26 #include "backend-private.h"
27 #include <cups/cups.h>
28 #include <string.h>
29 #include <ctype.h>
30 #define DEBUG_printf(x)
31 #define DEBUG_puts(x)
32 #define _cups_isspace(x) isspace((x) & 255)
33 #define _cups_strcasecmp strcasecmp
34 #define _cups_strncasecmp strncasecmp
35 
36 
37 /*
38  * Local functions...
39  */
40 
41 static int	get_1284_values(const char *device_id, cups_option_t **values);
42 static char	*normalize_make_and_model(const char *make_and_model,
43 		                          char *buffer, size_t bufsize);
44 
45 
46 /*
47  * 'backendGetDeviceID()' - Get the IEEE-1284 device ID string and
48  *                          corresponding URI.
49  */
50 
51 int					/* O - 0 on success, -1 on failure */
backendGetDeviceID(int fd,char * device_id,int device_id_size,char * make_model,int make_model_size,const char * scheme,char * uri,int uri_size)52 backendGetDeviceID(
53     int        fd,			/* I - File descriptor */
54     char       *device_id,		/* O - 1284 device ID */
55     int        device_id_size,		/* I - Size of buffer */
56     char       *make_model,		/* O - Make/model */
57     int        make_model_size,		/* I - Size of buffer */
58     const char *scheme,			/* I - URI scheme */
59     char       *uri,			/* O - Device URI */
60     int        uri_size)		/* I - Size of buffer */
61 {
62 #ifdef __APPLE__ /* This function is a no-op */
63   (void)fd;
64   (void)device_id;
65   (void)device_id_size;
66   (void)make_model;
67   (void)make_model_size;
68   (void)scheme;
69   (void)uri;
70   (void)uri_size;
71 
72   return (-1);
73 
74 #else /* Get the device ID from the specified file descriptor... */
75 #  ifdef __linux
76   int	length;				/* Length of device ID info */
77   int   got_id = 0;
78 #  endif /* __linux */
79 #  if defined(__sun) && defined(ECPPIOC_GETDEVID)
80   struct ecpp_device_id did;		/* Device ID buffer */
81 #  endif /* __sun && ECPPIOC_GETDEVID */
82   char	*ptr;				/* Pointer into device ID */
83 
84 
85   DEBUG_printf(("backendGetDeviceID(fd=%d, device_id=%p, device_id_size=%d, "
86                 "make_model=%p, make_model_size=%d, scheme=\"%s\", "
87 		"uri=%p, uri_size=%d)\n", fd, device_id, device_id_size,
88 		make_model, make_model_size, scheme ? scheme : "(null)",
89 		uri, uri_size));
90 
91  /*
92   * Range check input...
93   */
94 
95   if (!device_id || device_id_size < 32)
96   {
97     DEBUG_puts("backendGetDeviceID: Bad args!");
98     return (-1);
99   }
100 
101   if (make_model)
102     *make_model = '\0';
103 
104   if (fd >= 0)
105   {
106    /*
107     * Get the device ID string...
108     */
109 
110     *device_id = '\0';
111 
112 #  ifdef __linux
113     if (ioctl(fd, LPIOC_GET_DEVICE_ID(device_id_size), device_id))
114     {
115      /*
116       * Linux has to implement things differently for every device it seems.
117       * Since the standard parallel port driver does not provide a simple
118       * ioctl() to get the 1284 device ID, we have to open the "raw" parallel
119       * device corresponding to this port and do some negotiation trickery
120       * to get the current device ID.
121       */
122 
123       if (uri && !strncmp(uri, "parallel:/dev/", 14))
124       {
125 	char	devparport[16];		/* /dev/parportN */
126 	int	devparportfd,		/* File descriptor for raw device */
127 		  mode;			/* Port mode */
128 
129 
130        /*
131 	* Since the Linux parallel backend only supports 4 parallel port
132 	* devices, just grab the trailing digit and use it to construct a
133 	* /dev/parportN filename...
134 	*/
135 
136 	snprintf(devparport, sizeof(devparport), "/dev/parport%s",
137 		 uri + strlen(uri) - 1);
138 
139 	if ((devparportfd = open(devparport, O_RDWR | O_NOCTTY)) != -1)
140 	{
141 	 /*
142 	  * Claim the device...
143 	  */
144 
145 	  if (!ioctl(devparportfd, PPCLAIM))
146 	  {
147 	    fcntl(devparportfd, F_SETFL, fcntl(devparportfd, F_GETFL) | O_NONBLOCK);
148 
149 	    mode = IEEE1284_MODE_COMPAT;
150 
151 	    if (!ioctl(devparportfd, PPNEGOT, &mode))
152 	    {
153 	     /*
154 	      * Put the device into Device ID mode...
155 	      */
156 
157 	      mode = IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID;
158 
159 	      if (!ioctl(devparportfd, PPNEGOT, &mode))
160 	      {
161 	       /*
162 		* Read the 1284 device ID...
163 		*/
164 
165 		if ((length = read(devparportfd, device_id,
166 				   device_id_size - 1)) >= 2)
167 		{
168 		  device_id[length] = '\0';
169 		  got_id = 1;
170 		}
171 	      }
172 	    }
173 
174 	   /*
175 	    * Release the device...
176 	    */
177 
178 	    ioctl(devparportfd, PPRELEASE);
179 	  }
180 
181 	  close(devparportfd);
182 	}
183       }
184     }
185     else
186       got_id = 1;
187 
188     if (got_id)
189     {
190      /*
191       * Extract the length of the device ID string from the first two
192       * bytes.  The 1284 spec says the length is stored MSB first...
193       */
194 
195       length = (((unsigned)device_id[0] & 255) << 8) +
196 	       ((unsigned)device_id[1] & 255);
197 
198      /*
199       * Check to see if the length is larger than our buffer; first
200       * assume that the vendor incorrectly implemented the 1284 spec,
201       * and then limit the length to the size of our buffer...
202       */
203 
204       if (length > device_id_size || length < 14)
205 	length = (((unsigned)device_id[1] & 255) << 8) +
206 		 ((unsigned)device_id[0] & 255);
207 
208       if (length > device_id_size)
209 	length = device_id_size;
210 
211      /*
212       * The length field counts the number of bytes in the string
213       * including the length field itself (2 bytes).  The minimum
214       * length for a valid/usable device ID is 14 bytes:
215       *
216       *     <LENGTH> MFG: <MFG> ;MDL: <MDL> ;
217       *        2  +   4  +  1  +  5 +  1 +  1
218       */
219 
220       if (length < 14)
221       {
222        /*
223 	* Can't use this device ID, so don't try to copy it...
224 	*/
225 
226 	device_id[0] = '\0';
227 	got_id       = 0;
228       }
229       else
230       {
231        /*
232 	* Copy the device ID text to the beginning of the buffer and
233 	* nul-terminate.
234 	*/
235 
236 	length -= 2;
237 
238 	memmove(device_id, device_id + 2, length);
239 	device_id[length] = '\0';
240       }
241     }
242     else
243     {
244       DEBUG_printf(("backendGetDeviceID: ioctl failed - %s\n",
245                     strerror(errno)));
246       *device_id = '\0';
247     }
248 #  endif /* __linux */
249 
250 #   if defined(__sun) && defined(ECPPIOC_GETDEVID)
251     did.mode = ECPP_CENTRONICS;
252     did.len  = device_id_size - 1;
253     did.rlen = 0;
254     did.addr = device_id;
255 
256     if (!ioctl(fd, ECPPIOC_GETDEVID, &did))
257     {
258      /*
259       * Nul-terminate the device ID text.
260       */
261 
262       if (did.rlen < (device_id_size - 1))
263 	device_id[did.rlen] = '\0';
264       else
265 	device_id[device_id_size - 1] = '\0';
266     }
267 #    ifdef DEBUG
268     else
269       DEBUG_printf(("backendGetDeviceID: ioctl failed - %s\n",
270                     strerror(errno)));
271 #    endif /* DEBUG */
272 #  endif /* __sun && ECPPIOC_GETDEVID */
273   }
274 
275  /*
276   * Check whether device ID is valid. Turn line breaks and tabs to spaces and
277   * reject device IDs with non-printable characters.
278   */
279 
280   for (ptr = device_id; *ptr; ptr ++)
281     if (_cups_isspace(*ptr))
282       *ptr = ' ';
283     else if ((*ptr & 255) < ' ' || *ptr == 127)
284     {
285       DEBUG_printf(("backendGetDeviceID: Bad device_id character %d.",
286                     *ptr & 255));
287       *device_id = '\0';
288       break;
289     }
290 
291   DEBUG_printf(("backendGetDeviceID: device_id=\"%s\"\n", device_id));
292 
293   if (scheme && uri)
294     *uri = '\0';
295 
296   if (!*device_id)
297     return (-1);
298 
299  /*
300   * Get the make and model...
301   */
302 
303   if (make_model)
304     backendGetMakeModel(device_id, make_model, make_model_size);
305 
306  /*
307   * Then generate a device URI...
308   */
309 
310   if (scheme && uri && uri_size > 32)
311   {
312     int			num_values;	/* Number of keys and values */
313     cups_option_t	*values;	/* Keys and values in device ID */
314     const char		*mfg,		/* Manufacturer */
315 			*mdl,		/* Model */
316 			*sern;		/* Serial number */
317     char		temp[256],	/* Temporary manufacturer string */
318 			*tempptr;	/* Pointer into temp string */
319 
320 
321    /*
322     * Get the make, model, and serial numbers...
323     */
324 
325     num_values = get_1284_values(device_id, &values);
326 
327     if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL)
328       if ((sern = cupsGetOption("SERN", num_values, values)) == NULL)
329         sern = cupsGetOption("SN", num_values, values);
330 
331     if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL)
332       mfg = cupsGetOption("MFG", num_values, values);
333 
334     if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL)
335       mdl = cupsGetOption("MDL", num_values, values);
336 
337     if (mfg)
338     {
339       if (!_cups_strcasecmp(mfg, "Hewlett-Packard"))
340         mfg = "HP";
341       else if (!_cups_strcasecmp(mfg, "Lexmark International"))
342         mfg = "Lexmark";
343     }
344     else
345     {
346       strncpy(temp, make_model, sizeof(temp) - 1);
347       temp[sizeof(temp) - 1] = '\0';
348 
349       if ((tempptr = strchr(temp, ' ')) != NULL)
350         *tempptr = '\0';
351 
352       mfg = temp;
353     }
354 
355     if (!mdl)
356       mdl = "";
357 
358     if (!_cups_strncasecmp(mdl, mfg, strlen(mfg)))
359     {
360       mdl += strlen(mfg);
361 
362       while (isspace(*mdl & 255))
363         mdl ++;
364     }
365 
366    /*
367     * Generate the device URI from the manufacturer, make_model, and
368     * serial number strings.
369     */
370 
371     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, uri_size, scheme, NULL, mfg, 0,
372                      "/%s%s%s", mdl, sern ? "?serial=" : "", sern ? sern : "");
373 
374     cupsFreeOptions(num_values, values);
375   }
376 
377   return (0);
378 #endif /* __APPLE__ */
379 }
380 
381 
382 /*
383  * 'backendGetMakeModel()' - Get the make and model string from the device ID.
384  */
385 
386 int					/* O - 0 on success, -1 on failure */
backendGetMakeModel(const char * device_id,char * make_model,int make_model_size)387 backendGetMakeModel(
388     const char *device_id,		/* O - 1284 device ID */
389     char       *make_model,		/* O - Make/model */
390     int        make_model_size)		/* I - Size of buffer */
391 {
392   int		num_values;		/* Number of keys and values */
393   cups_option_t	*values;		/* Keys and values */
394   const char	*mfg,			/* Manufacturer string */
395 		*mdl,			/* Model string */
396 		*des;			/* Description string */
397 
398 
399   DEBUG_printf(("backendGetMakeModel(device_id=\"%s\", "
400                 "make_model=%p, make_model_size=%d)\n", device_id,
401 		make_model, make_model_size));
402 
403  /*
404   * Range check input...
405   */
406 
407   if (!device_id || !*device_id || !make_model || make_model_size < 32)
408   {
409     DEBUG_puts("backendGetMakeModel: Bad args!");
410     return (-1);
411   }
412 
413   *make_model = '\0';
414 
415  /*
416   * Look for the description field...
417   */
418 
419   num_values = get_1284_values(device_id, &values);
420 
421   if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL)
422     mdl = cupsGetOption("MDL", num_values, values);
423 
424   if (mdl)
425   {
426    /*
427     * Build a make-model string from the manufacturer and model attributes...
428     */
429 
430     if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL)
431       mfg = cupsGetOption("MFG", num_values, values);
432 
433     if (!mfg || !_cups_strncasecmp(mdl, mfg, strlen(mfg)))
434     {
435      /*
436       * Just copy the model string, since it has the manufacturer...
437       */
438 
439       normalize_make_and_model(mdl, make_model, make_model_size);
440     }
441     else
442     {
443      /*
444       * Concatenate the make and model...
445       */
446 
447       char	temp[1024];		/* Temporary make and model */
448 
449       snprintf(temp, sizeof(temp), "%s %s", mfg, mdl);
450 
451       normalize_make_and_model(temp, make_model, make_model_size);
452     }
453   }
454   else if ((des = cupsGetOption("DESCRIPTION", num_values, values)) != NULL ||
455            (des = cupsGetOption("DES", num_values, values)) != NULL)
456   {
457    /*
458     * Make sure the description contains something useful, since some
459     * printer manufacturers (HP) apparently don't follow the standards
460     * they helped to define...
461     *
462     * Here we require the description to be 8 or more characters in length,
463     * containing at least one space and one letter.
464     */
465 
466     if (strlen(des) >= 8)
467     {
468       const char	*ptr;		/* Pointer into description */
469       int		letters,	/* Number of letters seen */
470 			spaces;		/* Number of spaces seen */
471 
472 
473       for (ptr = des, letters = 0, spaces = 0; *ptr; ptr ++)
474       {
475 	if (isspace(*ptr & 255))
476 	  spaces ++;
477 	else if (isalpha(*ptr & 255))
478 	  letters ++;
479 
480 	if (spaces && letters)
481 	  break;
482       }
483 
484       if (spaces && letters)
485         normalize_make_and_model(des, make_model, make_model_size);
486     }
487   }
488 
489   if (!make_model[0])
490   {
491    /*
492     * Use "Unknown" as the printer make and model...
493     */
494 
495     strncpy(make_model, "Unknown", make_model_size - 1);
496     make_model[make_model_size - 1] = '\0';
497   }
498 
499   cupsFreeOptions(num_values, values);
500 
501   return (0);
502 }
503 
504 
505 /*
506  * 'get_1284_values()' - Get 1284 device ID keys and values.
507  *
508  * The returned dictionary is a CUPS option array that can be queried with
509  * cupsGetOption and freed with cupsFreeOptions.
510  */
511 
512 static int				/* O - Number of key/value pairs */
get_1284_values(const char * device_id,cups_option_t ** values)513 get_1284_values(
514     const char *device_id,		/* I - IEEE-1284 device ID string */
515     cups_option_t **values)		/* O - Array of key/value pairs */
516 {
517   int		num_values;		/* Number of values */
518   char		key[256],		/* Key string */
519 		value[256],		/* Value string */
520 		*ptr;			/* Pointer into key/value */
521 
522 
523  /*
524   * Range check input...
525   */
526 
527   if (values)
528     *values = NULL;
529 
530   if (!device_id || !values)
531     return (0);
532 
533  /*
534   * Parse the 1284 device ID value into keys and values.  The format is
535   * repeating sequences of:
536   *
537   *   [whitespace]key:value[whitespace];
538   */
539 
540   num_values = 0;
541   while (*device_id)
542   {
543     while (_cups_isspace(*device_id))
544       device_id ++;
545 
546     if (!*device_id)
547       break;
548 
549     for (ptr = key; *device_id && *device_id != ':'; device_id ++)
550       if (ptr < (key + sizeof(key) - 1))
551         *ptr++ = *device_id;
552 
553     if (!*device_id)
554       break;
555 
556     while (ptr > key && _cups_isspace(ptr[-1]))
557       ptr --;
558 
559     *ptr = '\0';
560     device_id ++;
561 
562     while (_cups_isspace(*device_id))
563       device_id ++;
564 
565     if (!*device_id)
566       break;
567 
568     for (ptr = value; *device_id && *device_id != ';'; device_id ++)
569       if (ptr < (value + sizeof(value) - 1))
570         *ptr++ = *device_id;
571 
572     if (!*device_id)
573       break;
574 
575     while (ptr > value && _cups_isspace(ptr[-1]))
576       ptr --;
577 
578     *ptr = '\0';
579     device_id ++;
580 
581     num_values = cupsAddOption(key, value, num_values, values);
582   }
583 
584   return (num_values);
585 }
586 
587 
588 /*
589  * 'normalize_make_and_model()' - Normalize a product/make-and-model string.
590  *
591  * This function tries to undo the mistakes made by many printer manufacturers
592  * to produce a clean make-and-model string we can use.
593  */
594 
595 static char *				/* O - Normalized make-and-model string or NULL on error */
normalize_make_and_model(const char * make_and_model,char * buffer,size_t bufsize)596 normalize_make_and_model(
597     const char *make_and_model,		/* I - Original make-and-model string */
598     char       *buffer,			/* I - String buffer */
599     size_t     bufsize)			/* I - Size of string buffer */
600 {
601   char	*bufptr;			/* Pointer into buffer */
602 
603 
604   if (!make_and_model || !buffer || bufsize < 1)
605   {
606     if (buffer)
607       *buffer = '\0';
608 
609     return (NULL);
610   }
611 
612  /*
613   * Skip leading whitespace...
614   */
615 
616   while (_cups_isspace(*make_and_model))
617     make_and_model ++;
618 
619  /*
620   * Remove parenthesis and add manufacturers as needed...
621   */
622 
623   if (make_and_model[0] == '(')
624   {
625     strncpy(buffer, make_and_model + 1, bufsize - 1);
626     buffer[bufsize - 1] = '\0';
627 
628     if ((bufptr = strrchr(buffer, ')')) != NULL)
629       *bufptr = '\0';
630   }
631   else if (!_cups_strncasecmp(make_and_model, "XPrint", 6))
632   {
633    /*
634     * Xerox XPrint...
635     */
636 
637     snprintf(buffer, bufsize, "Xerox %s", make_and_model);
638   }
639   else if (!_cups_strncasecmp(make_and_model, "Eastman", 7))
640   {
641    /*
642     * Kodak...
643     */
644 
645     snprintf(buffer, bufsize, "Kodak %s", make_and_model + 7);
646   }
647   else if (!_cups_strncasecmp(make_and_model, "laserwriter", 11))
648   {
649    /*
650     * Apple LaserWriter...
651     */
652 
653     snprintf(buffer, bufsize, "Apple LaserWriter%s", make_and_model + 11);
654   }
655   else if (!_cups_strncasecmp(make_and_model, "colorpoint", 10))
656   {
657    /*
658     * Seiko...
659     */
660 
661     snprintf(buffer, bufsize, "Seiko %s", make_and_model);
662   }
663   else if (!_cups_strncasecmp(make_and_model, "fiery", 5))
664   {
665    /*
666     * EFI...
667     */
668 
669     snprintf(buffer, bufsize, "EFI %s", make_and_model);
670   }
671   else if (!_cups_strncasecmp(make_and_model, "ps ", 3) ||
672 	   !_cups_strncasecmp(make_and_model, "colorpass", 9))
673   {
674    /*
675     * Canon...
676     */
677 
678     snprintf(buffer, bufsize, "Canon %s", make_and_model);
679   }
680   else if (!_cups_strncasecmp(make_and_model, "primera", 7))
681   {
682    /*
683     * Fargo...
684     */
685 
686     snprintf(buffer, bufsize, "Fargo %s", make_and_model);
687   }
688   else if (!_cups_strncasecmp(make_and_model, "designjet", 9) ||
689            !_cups_strncasecmp(make_and_model, "deskjet", 7))
690   {
691    /*
692     * HP...
693     */
694 
695     snprintf(buffer, bufsize, "HP %s", make_and_model);
696   }
697   else
698   {
699     strncpy(buffer, make_and_model, bufsize - 1);
700     buffer[bufsize - 1] = '\0';
701   }
702 
703  /*
704   * Clean up the make...
705   */
706 
707   if (!_cups_strncasecmp(buffer, "agfa", 4))
708   {
709    /*
710     * Replace with AGFA (all uppercase)...
711     */
712 
713     buffer[0] = 'A';
714     buffer[1] = 'G';
715     buffer[2] = 'F';
716     buffer[3] = 'A';
717   }
718   else if (!_cups_strncasecmp(buffer, "Hewlett-Packard hp ", 19))
719   {
720    /*
721     * Just put "HP" on the front...
722     */
723 
724     buffer[0] = 'H';
725     buffer[1] = 'P';
726     memmove(buffer + 2, buffer + 18, strlen(buffer + 18) + 1);
727   }
728   else if (!_cups_strncasecmp(buffer, "Hewlett-Packard ", 16))
729   {
730    /*
731     * Just put "HP" on the front...
732     */
733 
734     buffer[0] = 'H';
735     buffer[1] = 'P';
736     memmove(buffer + 2, buffer + 15, strlen(buffer + 15) + 1);
737   }
738   else if (!_cups_strncasecmp(buffer, "Lexmark International", 21))
739   {
740    /*
741     * Strip "International"...
742     */
743 
744     memmove(buffer + 8, buffer + 21, strlen(buffer + 21) + 1);
745   }
746   else if (!_cups_strncasecmp(buffer, "herk", 4))
747   {
748    /*
749     * Replace with LHAG...
750     */
751 
752     buffer[0] = 'L';
753     buffer[1] = 'H';
754     buffer[2] = 'A';
755     buffer[3] = 'G';
756   }
757   else if (!_cups_strncasecmp(buffer, "linotype", 8))
758   {
759    /*
760     * Replace with LHAG...
761     */
762 
763     buffer[0] = 'L';
764     buffer[1] = 'H';
765     buffer[2] = 'A';
766     buffer[3] = 'G';
767     memmove(buffer + 4, buffer + 8, strlen(buffer + 8) + 1);
768   }
769 
770  /*
771   * Remove trailing whitespace and return...
772   */
773 
774   for (bufptr = buffer + strlen(buffer) - 1;
775        bufptr >= buffer && _cups_isspace(*bufptr);
776        bufptr --);
777 
778   bufptr[1] = '\0';
779 
780   return (buffer[0] ? buffer : NULL);
781 }
782