1 /*
2  * IEEE-1284 support functions for CUPS.
3  *
4  * Copyright © 2007-2015 by Apple Inc.
5  * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
6  *
7  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
8  * information.
9  */
10 
11 /*
12  * Include necessary headers.
13  */
14 
15 #include "backend-private.h"
16 #include <cups/ppd-private.h>
17 
18 
19 /*
20  * 'backendGetDeviceID()' - Get the IEEE-1284 device ID string and
21  *                          corresponding URI.
22  */
23 
24 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)25 backendGetDeviceID(
26     int        fd,			/* I - File descriptor */
27     char       *device_id,		/* O - 1284 device ID */
28     int        device_id_size,		/* I - Size of buffer */
29     char       *make_model,		/* O - Make/model */
30     int        make_model_size,		/* I - Size of buffer */
31     const char *scheme,			/* I - URI scheme */
32     char       *uri,			/* O - Device URI */
33     int        uri_size)		/* I - Size of buffer */
34 {
35 #ifdef __APPLE__ /* This function is a no-op */
36   (void)fd;
37   (void)device_id;
38   (void)device_id_size;
39   (void)make_model;
40   (void)make_model_size;
41   (void)scheme;
42   (void)uri;
43   (void)uri_size;
44 
45   return (-1);
46 
47 #else /* Get the device ID from the specified file descriptor... */
48 #  ifdef __linux
49   int	length;				/* Length of device ID info */
50   int   got_id = 0;
51 #  endif /* __linux */
52 #  if defined(__sun) && defined(ECPPIOC_GETDEVID)
53   struct ecpp_device_id did;		/* Device ID buffer */
54 #  endif /* __sun && ECPPIOC_GETDEVID */
55   char	*ptr;				/* Pointer into device ID */
56 
57 
58  /*
59   * Range check input...
60   */
61 
62   if (!device_id || device_id_size < 32)
63   {
64     return (-1);
65   }
66 
67   if (make_model)
68     *make_model = '\0';
69 
70   if (fd >= 0)
71   {
72    /*
73     * Get the device ID string...
74     */
75 
76     *device_id = '\0';
77 
78 #  ifdef __linux
79     if (ioctl(fd, LPIOC_GET_DEVICE_ID((unsigned)device_id_size), device_id))
80     {
81      /*
82       * Linux has to implement things differently for every device it seems.
83       * Since the standard parallel port driver does not provide a simple
84       * ioctl() to get the 1284 device ID, we have to open the "raw" parallel
85       * device corresponding to this port and do some negotiation trickery
86       * to get the current device ID.
87       */
88 
89       if (uri && !strncmp(uri, "parallel:/dev/", 14))
90       {
91 	char	devparport[16];		/* /dev/parportN */
92 	int	devparportfd,		/* File descriptor for raw device */
93 		  mode;			/* Port mode */
94 
95 
96        /*
97 	* Since the Linux parallel backend only supports 4 parallel port
98 	* devices, just grab the trailing digit and use it to construct a
99 	* /dev/parportN filename...
100 	*/
101 
102 	snprintf(devparport, sizeof(devparport), "/dev/parport%s",
103 		 uri + strlen(uri) - 1);
104 
105 	if ((devparportfd = open(devparport, O_RDWR | O_NOCTTY)) != -1)
106 	{
107 	 /*
108 	  * Claim the device...
109 	  */
110 
111 	  if (!ioctl(devparportfd, PPCLAIM))
112 	  {
113 	    fcntl(devparportfd, F_SETFL, fcntl(devparportfd, F_GETFL) | O_NONBLOCK);
114 
115 	    mode = IEEE1284_MODE_COMPAT;
116 
117 	    if (!ioctl(devparportfd, PPNEGOT, &mode))
118 	    {
119 	     /*
120 	      * Put the device into Device ID mode...
121 	      */
122 
123 	      mode = IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID;
124 
125 	      if (!ioctl(devparportfd, PPNEGOT, &mode))
126 	      {
127 	       /*
128 		* Read the 1284 device ID...
129 		*/
130 
131 		if ((length = read(devparportfd, device_id, (size_t)device_id_size - 1)) >= 2)
132 		{
133 		  device_id[length] = '\0';
134 		  got_id = 1;
135 		}
136 	      }
137 	    }
138 
139 	   /*
140 	    * Release the device...
141 	    */
142 
143 	    ioctl(devparportfd, PPRELEASE);
144 	  }
145 
146 	  close(devparportfd);
147 	}
148       }
149     }
150     else
151       got_id = 1;
152 
153     if (got_id)
154     {
155      /*
156       * Extract the length of the device ID string from the first two
157       * bytes.  The 1284 spec says the length is stored MSB first...
158       */
159 
160       length = (int)((((unsigned)device_id[0] & 255) << 8) + ((unsigned)device_id[1] & 255));
161 
162      /*
163       * Check to see if the length is larger than our buffer; first
164       * assume that the vendor incorrectly implemented the 1284 spec,
165       * and then limit the length to the size of our buffer...
166       */
167 
168       if (length > device_id_size || length < 14)
169 	length = (int)((((unsigned)device_id[1] & 255) << 8) + ((unsigned)device_id[0] & 255));
170 
171       if (length > device_id_size)
172 	length = device_id_size;
173 
174      /*
175       * The length field counts the number of bytes in the string
176       * including the length field itself (2 bytes).  The minimum
177       * length for a valid/usable device ID is 14 bytes:
178       *
179       *     <LENGTH> MFG: <MFG> ;MDL: <MDL> ;
180       *        2  +   4  +  1  +  5 +  1 +  1
181       */
182 
183       if (length < 14)
184       {
185        /*
186 	* Can't use this device ID, so don't try to copy it...
187 	*/
188 
189 	device_id[0] = '\0';
190 	got_id       = 0;
191       }
192       else
193       {
194        /*
195 	* Copy the device ID text to the beginning of the buffer and
196 	* nul-terminate.
197 	*/
198 
199 	length -= 2;
200 
201 	memmove(device_id, device_id + 2, (size_t)length);
202 	device_id[length] = '\0';
203       }
204     }
205     else
206     {
207       *device_id = '\0';
208     }
209 #  endif /* __linux */
210 
211 #   if defined(__sun) && defined(ECPPIOC_GETDEVID)
212     did.mode = ECPP_CENTRONICS;
213     did.len  = device_id_size - 1;
214     did.rlen = 0;
215     did.addr = device_id;
216 
217     if (!ioctl(fd, ECPPIOC_GETDEVID, &did))
218     {
219      /*
220       * Nul-terminate the device ID text.
221       */
222 
223       if (did.rlen < (device_id_size - 1))
224 	device_id[did.rlen] = '\0';
225       else
226 	device_id[device_id_size - 1] = '\0';
227     }
228 #  endif /* __sun && ECPPIOC_GETDEVID */
229   }
230 
231  /*
232   * Check whether device ID is valid. Turn line breaks and tabs to spaces and
233   * reject device IDs with non-printable characters.
234   */
235 
236   for (ptr = device_id; *ptr; ptr ++)
237     if (_cups_isspace(*ptr))
238       *ptr = ' ';
239     else if ((*ptr & 255) < ' ' || *ptr == 127)
240     {
241       *device_id = '\0';
242       break;
243     }
244 
245   if (scheme && uri)
246     *uri = '\0';
247 
248   if (!*device_id)
249     return (-1);
250 
251  /*
252   * Get the make and model...
253   */
254 
255   if (make_model)
256     backendGetMakeModel(device_id, make_model, (size_t)make_model_size);
257 
258  /*
259   * Then generate a device URI...
260   */
261 
262   if (scheme && uri && uri_size > 32)
263   {
264     int			num_values;	/* Number of keys and values */
265     cups_option_t	*values;	/* Keys and values in device ID */
266     const char		*mfg,		/* Manufacturer */
267 			*mdl,		/* Model */
268 			*sern;		/* Serial number */
269     char		temp[256],	/* Temporary manufacturer string */
270 			*tempptr;	/* Pointer into temp string */
271 
272 
273    /*
274     * Get the make, model, and serial numbers...
275     */
276 
277     num_values = _cupsGet1284Values(device_id, &values);
278 
279     if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL)
280       if ((sern = cupsGetOption("SERN", num_values, values)) == NULL)
281         sern = cupsGetOption("SN", num_values, values);
282 
283     if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL)
284       mfg = cupsGetOption("MFG", num_values, values);
285 
286     if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL)
287       mdl = cupsGetOption("MDL", num_values, values);
288 
289     if (mfg)
290     {
291       if (!_cups_strcasecmp(mfg, "Hewlett-Packard"))
292         mfg = "HP";
293       else if (!_cups_strcasecmp(mfg, "Lexmark International"))
294         mfg = "Lexmark";
295     }
296     else
297     {
298       strlcpy(temp, make_model, sizeof(temp));
299 
300       if ((tempptr = strchr(temp, ' ')) != NULL)
301         *tempptr = '\0';
302 
303       mfg = temp;
304     }
305 
306     if (!mdl)
307       mdl = "";
308 
309     if (!_cups_strncasecmp(mdl, mfg, strlen(mfg)))
310     {
311       mdl += strlen(mfg);
312 
313       while (isspace(*mdl & 255))
314         mdl ++;
315     }
316 
317    /*
318     * Generate the device URI from the manufacturer, make_model, and
319     * serial number strings.
320     */
321 
322     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, uri_size, scheme, NULL, mfg, 0,
323                      "/%s%s%s", mdl, sern ? "?serial=" : "", sern ? sern : "");
324 
325     cupsFreeOptions(num_values, values);
326   }
327 
328   return (0);
329 #endif /* __APPLE__ */
330 }
331 
332 
333 /*
334  * 'backendGetMakeModel()' - Get the make and model string from the device ID.
335  */
336 
337 int					/* O - 0 on success, -1 on failure */
backendGetMakeModel(const char * device_id,char * make_model,size_t make_model_size)338 backendGetMakeModel(
339     const char *device_id,		/* O - 1284 device ID */
340     char       *make_model,		/* O - Make/model */
341     size_t     make_model_size)		/* I - Size of buffer */
342 {
343   int		num_values;		/* Number of keys and values */
344   cups_option_t	*values;		/* Keys and values */
345   const char	*mfg,			/* Manufacturer string */
346 		*mdl,			/* Model string */
347 		*des;			/* Description string */
348 
349 
350  /*
351   * Range check input...
352   */
353 
354   if (!device_id || !*device_id || !make_model || make_model_size < 32)
355     return (-1);
356 
357   *make_model = '\0';
358 
359  /*
360   * Look for the description field...
361   */
362 
363   num_values = _cupsGet1284Values(device_id, &values);
364 
365   if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL)
366     mdl = cupsGetOption("MDL", num_values, values);
367 
368   if (mdl)
369   {
370    /*
371     * Build a make-model string from the manufacturer and model attributes...
372     */
373 
374     if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL)
375       mfg = cupsGetOption("MFG", num_values, values);
376 
377     if (!mfg || !_cups_strncasecmp(mdl, mfg, strlen(mfg)))
378     {
379      /*
380       * Just copy the model string, since it has the manufacturer...
381       */
382 
383       _ppdNormalizeMakeAndModel(mdl, make_model, make_model_size);
384     }
385     else
386     {
387      /*
388       * Concatenate the make and model...
389       */
390 
391       char	temp[1024];		/* Temporary make and model */
392 
393       snprintf(temp, sizeof(temp), "%s %s", mfg, mdl);
394 
395       _ppdNormalizeMakeAndModel(temp, make_model, make_model_size);
396     }
397   }
398   else if ((des = cupsGetOption("DESCRIPTION", num_values, values)) != NULL ||
399            (des = cupsGetOption("DES", num_values, values)) != NULL)
400   {
401    /*
402     * Make sure the description contains something useful, since some
403     * printer manufacturers (HP) apparently don't follow the standards
404     * they helped to define...
405     *
406     * Here we require the description to be 8 or more characters in length,
407     * containing at least one space and one letter.
408     */
409 
410     if (strlen(des) >= 8)
411     {
412       const char	*ptr;		/* Pointer into description */
413       int		letters,	/* Number of letters seen */
414 			spaces;		/* Number of spaces seen */
415 
416 
417       for (ptr = des, letters = 0, spaces = 0; *ptr; ptr ++)
418       {
419 	if (isspace(*ptr & 255))
420 	  spaces ++;
421 	else if (isalpha(*ptr & 255))
422 	  letters ++;
423 
424 	if (spaces && letters)
425 	  break;
426       }
427 
428       if (spaces && letters)
429         _ppdNormalizeMakeAndModel(des, make_model, make_model_size);
430     }
431   }
432 
433   if (!make_model[0])
434   {
435    /*
436     * Use "Unknown" as the printer make and model...
437     */
438 
439     strlcpy(make_model, "Unknown", make_model_size);
440   }
441 
442   cupsFreeOptions(num_values, values);
443 
444   return (0);
445 }
446