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