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