74 private static final String FORCE_PIPE_PROP = "sun.print.ippdebug";
75
76 static {
77 String debugStr = java.security.AccessController.doPrivileged(
78 new sun.security.action.GetPropertyAction(FORCE_PIPE_PROP));
79
80 debugPrint = "true".equalsIgnoreCase(debugStr);
81 }
82
83 private String printer;
84 private URI myURI;
85 private URL myURL;
86 transient private ServiceNotifier notifier = null;
87
88 private static int MAXCOPIES = 1000;
89 private static short MAX_ATTRIBUTE_LENGTH = 255;
90
91 private CUPSPrinter cps;
92 private HttpURLConnection urlConnection = null;
93 private DocFlavor[] supportedDocFlavors;
94 private Class[] supportedCats;
95 private MediaTray[] mediaTrays;
96 private MediaSizeName[] mediaSizeNames;
97 private CustomMediaSizeName[] customMediaSizeNames;
98 private int defaultMediaIndex;
99 private boolean isCupsPrinter;
100 private boolean init;
101 private Boolean isPS;
102 private HashMap getAttMap;
103 private boolean pngImagesAdded = false;
104 private boolean gifImagesAdded = false;
105 private boolean jpgImagesAdded = false;
106
107
108 /**
109 * IPP Status Codes
110 */
111 private static final byte STATUSCODE_SUCCESS = 0x00;
112
113 /**
114 * IPP Group Tags. Each tag is used once before the first attribute
115 * of that group.
116 */
117 // operation attributes group
118 private static final byte GRPTAG_OP_ATTRIBUTES = 0x01;
119 // job attributes group
120 private static final byte GRPTAG_JOB_ATTRIBUTES = 0x02;
121 // printer attributes group
122 private static final byte GRPTAG_PRINTER_ATTRIBUTES = 0x04;
407 // maybe use "&& (usePPD)" later?
408 // Another reason why we use PPD is because
409 // IPP currently does not support it but PPD does.
410
411 try {
412 cps = new CUPSPrinter(printer);
413 mediaSizeNames = cps.getMediaSizeNames();
414 mediaTrays = cps.getMediaTrays();
415 customMediaSizeNames = cps.getCustomMediaSizeNames();
416 urlConnection.disconnect();
417 init = true;
418 return;
419 } catch (Exception e) {
420 IPPPrintService.debug_println(debugPrefix+
421 "initAttributes, error creating CUPSPrinter e="+e);
422 }
423 }
424
425 // use IPP to get all media,
426 Media[] allMedia = getSupportedMedia();
427 ArrayList sizeList = new ArrayList();
428 ArrayList trayList = new ArrayList();
429 for (int i=0; i<allMedia.length; i++) {
430 if (allMedia[i] instanceof MediaSizeName) {
431 sizeList.add(allMedia[i]);
432 } else if (allMedia[i] instanceof MediaTray) {
433 trayList.add(allMedia[i]);
434 }
435 }
436
437 if (sizeList != null) {
438 mediaSizeNames = new MediaSizeName[sizeList.size()];
439 mediaSizeNames = (MediaSizeName[])sizeList.toArray(
440 mediaSizeNames);
441 }
442 if (trayList != null) {
443 mediaTrays = new MediaTray[trayList.size()];
444 mediaTrays = (MediaTray[])trayList.toArray(
445 mediaTrays);
446 }
447 urlConnection.disconnect();
448
449 init = true;
450 }
451 }
452
453
454 public DocPrintJob createPrintJob() {
455 SecurityManager security = System.getSecurityManager();
456 if (security != null) {
457 security.checkPrintJobAccess();
458 }
459 // REMIND: create IPPPrintJob
460 return new UnixPrintJob(this);
461 }
462
463
464 public synchronized Object
465 getSupportedAttributeValues(Class<? extends Attribute> category,
486 if (!isAttributeCategorySupported(category)) {
487 return null;
488 }
489
490 /* Test if the flavor is compatible with the attributes */
491 if (!isDestinationSupported(flavor, attributes)) {
492 return null;
493 }
494
495 initAttributes();
496
497 /* Test if the flavor is compatible with the category */
498 if ((category == Copies.class) ||
499 (category == CopiesSupported.class)) {
500 if (flavor == null ||
501 !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
502 flavor.equals(DocFlavor.URL.POSTSCRIPT) ||
503 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) {
504 CopiesSupported cs = new CopiesSupported(1, MAXCOPIES);
505 AttributeClass attribClass = (getAttMap != null) ?
506 (AttributeClass)getAttMap.get(cs.getName()) : null;
507 if (attribClass != null) {
508 int[] range = attribClass.getIntRangeValue();
509 cs = new CopiesSupported(range[0], range[1]);
510 }
511 return cs;
512 } else {
513 return null;
514 }
515 } else if (category == Chromaticity.class) {
516 if (flavor == null ||
517 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
518 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
519 !isIPPSupportedImages(flavor.getMimeType())) {
520 Chromaticity[]arr = new Chromaticity[1];
521 arr[0] = Chromaticity.COLOR;
522 return (arr);
523 } else {
524 return null;
525 }
526 } else if (category == Destination.class) {
528 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
529 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
530 try {
531 return new Destination((new File("out.ps")).toURI());
532 } catch (SecurityException se) {
533 try {
534 return new Destination(new URI("file:out.ps"));
535 } catch (URISyntaxException e) {
536 return null;
537 }
538 }
539 }
540 return null;
541 } else if (category == Fidelity.class) {
542 Fidelity []arr = new Fidelity[2];
543 arr[0] = Fidelity.FIDELITY_FALSE;
544 arr[1] = Fidelity.FIDELITY_TRUE;
545 return arr;
546 } else if (category == Finishings.class) {
547 AttributeClass attribClass = (getAttMap != null) ?
548 (AttributeClass)getAttMap.get("finishings-supported")
549 : null;
550 if (attribClass != null) {
551 int[] finArray = attribClass.getArrayOfIntValues();
552 if ((finArray != null) && (finArray.length > 0)) {
553 Finishings[] finSup = new Finishings[finArray.length];
554 for (int i=0; i<finArray.length; i++) {
555 finSup[i] = Finishings.NONE;
556 Finishings[] fAll = (Finishings[])
557 (new ExtFinishing(100)).getAll();
558 for (int j=0; j<fAll.length; j++) {
559 if (finArray[i] == fAll[j].getValue()) {
560 finSup[i] = fAll[j];
561 break;
562 }
563 }
564 }
565 return finSup;
566 }
567 }
568 } else if (category == JobName.class) {
631 //default printable area is that of default mediasize
632 return mpas;
633 }
634
635 for (int i=0; i<mediaSizeNames.length; i++) {
636 if (msn.equals(mediaSizeNames[i])) {
637 match = i;
638 }
639 }
640 }
641
642 if (match == -1) {
643 return null;
644 } else {
645 MediaPrintableArea []arr = new MediaPrintableArea[1];
646 arr[0] = mpas[match];
647 return arr;
648 }
649 } else if (category == NumberUp.class) {
650 AttributeClass attribClass = (getAttMap != null) ?
651 (AttributeClass)getAttMap.get("number-up-supported") : null;
652 if (attribClass != null) {
653 int[] values = attribClass.getArrayOfIntValues();
654 if (values != null) {
655 NumberUp[] nUp = new NumberUp[values.length];
656 for (int i=0; i<values.length; i++) {
657 nUp[i] = new NumberUp(values[i]);
658 }
659 return nUp;
660 } else {
661 return null;
662 }
663 }
664 } else if (category == OrientationRequested.class) {
665 if ((flavor != null) &&
666 (flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
667 flavor.equals(DocFlavor.URL.POSTSCRIPT) ||
668 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) {
669 return null;
670 }
671
672 boolean revPort = false;
673 OrientationRequested[] orientSup = null;
674
675 AttributeClass attribClass = (getAttMap != null) ?
676 (AttributeClass)getAttMap.get("orientation-requested-supported")
677 : null;
678 if (attribClass != null) {
679 int[] orientArray = attribClass.getArrayOfIntValues();
680 if ((orientArray != null) && (orientArray.length > 0)) {
681 orientSup =
682 new OrientationRequested[orientArray.length];
683 for (int i=0; i<orientArray.length; i++) {
684 switch (orientArray[i]) {
685 default:
686 case 3 :
687 orientSup[i] = OrientationRequested.PORTRAIT;
688 break;
689 case 4:
690 orientSup[i] = OrientationRequested.LANDSCAPE;
691 break;
692 case 5:
693 orientSup[i] =
694 OrientationRequested.REVERSE_LANDSCAPE;
695 break;
696 case 6:
731 arr[0] = new PageRanges(1, Integer.MAX_VALUE);
732 return arr;
733 } else {
734 // Returning null as this is not yet supported in UnixPrintJob.
735 return null;
736 }
737 } else if (category == RequestingUserName.class) {
738 String userName = "";
739 try {
740 userName = System.getProperty("user.name", "");
741 } catch (SecurityException se) {
742 }
743 return new RequestingUserName(userName, null);
744 } else if (category == Sides.class) {
745 // The printer takes care of Sides so if short-edge
746 // is chosen in a job, the rotation is done by the printer.
747 // Orientation is rotated by emulation if pageable
748 // or printable so if the document is in Landscape, this may
749 // result in double rotation.
750 AttributeClass attribClass = (getAttMap != null) ?
751 (AttributeClass)getAttMap.get("sides-supported")
752 : null;
753 if (attribClass != null) {
754 String[] sidesArray = attribClass.getArrayOfStringValues();
755 if ((sidesArray != null) && (sidesArray.length > 0)) {
756 Sides[] sidesSup = new Sides[sidesArray.length];
757 for (int i=0; i<sidesArray.length; i++) {
758 if (sidesArray[i].endsWith("long-edge")) {
759 sidesSup[i] = Sides.TWO_SIDED_LONG_EDGE;
760 } else if (sidesArray[i].endsWith("short-edge")) {
761 sidesSup[i] = Sides.TWO_SIDED_SHORT_EDGE;
762 } else {
763 sidesSup[i] = Sides.ONE_SIDED;
764 }
765 }
766 return sidesSup;
767 }
768 }
769 }
770
771 return null;
816 } else {
817 return unsupp;
818 }
819 }
820
821
822 public synchronized DocFlavor[] getSupportedDocFlavors() {
823
824 if (supportedDocFlavors != null) {
825 int len = supportedDocFlavors.length;
826 DocFlavor[] copyflavors = new DocFlavor[len];
827 System.arraycopy(supportedDocFlavors, 0, copyflavors, 0, len);
828 return copyflavors;
829 }
830 initAttributes();
831
832 if ((getAttMap != null) &&
833 getAttMap.containsKey("document-format-supported")) {
834
835 AttributeClass attribClass =
836 (AttributeClass)getAttMap.get("document-format-supported");
837 if (attribClass != null) {
838 String mimeType;
839 boolean psSupported = false;
840 String[] docFlavors = attribClass.getArrayOfStringValues();
841 DocFlavor[] flavors;
842 HashSet docList = new HashSet();
843 int j;
844 String hostEnc = DocFlavor.hostEncoding.
845 toLowerCase(Locale.ENGLISH);
846 boolean addHostEncoding = !hostEnc.equals("utf-8") &&
847 !hostEnc.equals("utf-16") && !hostEnc.equals("utf-16be") &&
848 !hostEnc.equals("utf-16le") && !hostEnc.equals("us-ascii");
849
850 for (int i = 0; i < docFlavors.length; i++) {
851 for (j=0; j<allDocFlavors.length; j++) {
852 flavors = (DocFlavor[])allDocFlavors[j];
853
854 mimeType = flavors[0].getMimeType();
855 if (mimeType.startsWith(docFlavors[i])) {
856
857 docList.addAll(Arrays.asList(flavors));
858
859 if (mimeType.equals("text/plain") &&
860 addHostEncoding) {
861 docList.add(Arrays.asList(textPlainHost));
862 } else if (mimeType.equals("text/html") &&
953 Media[] sizes = sampleSize.getSuperEnumTable();
954 for (int i=0; i<sizes.length; i++) {
955 if (mediaName.equals(""+sizes[i])) {
956 return sizes[i];
957 }
958 }
959 CustomMediaTray sampleTray = new CustomMediaTray("sample", "");
960 Media[] trays = sampleTray.getSuperEnumTable();
961 for (int i=0; i<trays.length; i++) {
962 if (mediaName.equals(""+trays[i])) {
963 return trays[i];
964 }
965 }
966 return null;
967 }
968
969 private Media[] getSupportedMedia() {
970 if ((getAttMap != null) &&
971 getAttMap.containsKey("media-supported")) {
972
973 AttributeClass attribClass =
974 (AttributeClass)getAttMap.get("media-supported");
975
976 if (attribClass != null) {
977 String[] mediaVals = attribClass.getArrayOfStringValues();
978 Media msn;
979 Media[] mediaNames =
980 new Media[mediaVals.length];
981 for (int i=0; i<mediaVals.length; i++) {
982 msn = getIPPMedia(mediaVals[i]);
983 //REMIND: if null, create custom?
984 mediaNames[i] = msn;
985 }
986 return mediaNames;
987 }
988 }
989 return new Media[0];
990 }
991
992
993 public synchronized Class[] getSupportedAttributeCategories() {
994 if (supportedCats != null) {
995 return supportedCats;
996 }
997
998 initAttributes();
999
1000 ArrayList catList = new ArrayList();
1001 Class cl;
1002
1003 for (int i=0; i < printReqAttribDefault.length; i++) {
1004 PrintRequestAttribute pra =
1005 (PrintRequestAttribute)printReqAttribDefault[i];
1006 if (getAttMap != null &&
1007 getAttMap.containsKey(pra.getName()+"-supported")) {
1008 cl = pra.getCategory();
1009 catList.add(cl);
1010 }
1011 }
1012
1013 // Some IPP printers like lexc710 do not have list of supported media
1014 // but CUPS can get the media from PPD, so we still report as
1015 // supported category.
1016 if (isCupsPrinter) {
1017 if (!catList.contains(Media.class)) {
1018 catList.add(Media.class);
1019 }
1020
1021 // Always add MediaPrintable for cups,
1022 // because we can get it from PPD.
1023 catList.add(MediaPrintableArea.class);
1024
1025 // this is already supported in UnixPrintJob
1026 catList.add(Destination.class);
1027
1028 // It is unfortunate that CUPS doesn't provide a way to query
1029 // if printer supports collation but since most printers
1030 // now supports collation and that most OS has a way
1031 // of setting it, it is a safe assumption to just always
1032 // include SheetCollate as supported attribute.
1033
1034 /*
1035 In Linux, we use Postscript for rendering but Linux still
1036 has issues in propagating Postscript-embedded setpagedevice
1037 setting like collation. Therefore, we temporarily exclude
1038 Linux.
1039 */
1040 if (!UnixPrintServiceLookup.isLinux()) {
1041 catList.add(SheetCollate.class);
1042 }
1043 }
1044
1045 // With the assumption that Chromaticity is equivalent to
1046 // ColorSupported.
1047 if (getAttMap != null && getAttMap.containsKey("color-supported")) {
1048 catList.add(Chromaticity.class);
1049 }
1050 supportedCats = new Class[catList.size()];
1051 catList.toArray(supportedCats);
1052 return supportedCats;
1053 }
1054
1055
1056 public boolean
1057 isAttributeCategorySupported(Class<? extends Attribute> category)
1058 {
1059 if (category == null) {
1060 throw new NullPointerException("null category");
1061 }
1062 if (!(Attribute.class.isAssignableFrom(category))) {
1063 throw new IllegalArgumentException(category +
1064 " is not an Attribute");
1065 }
1066
1067 if (supportedCats == null) {
1068 getSupportedAttributeCategories();
1069 }
1070
1071 // It is safe to assume that Orientation is always supported
1072 // and even if CUPS or an IPP device reports it as not,
1073 // our renderer can do portrait, landscape and
1074 // reverse landscape.
1075 if (category == OrientationRequested.class) {
1076 return true;
1077 }
1078
1079 for (int i=0;i<supportedCats.length;i++) {
1080 if (category == supportedCats[i]) {
1081 return true;
1082 }
1083 }
1084
1085 return false;
1086 }
1087
1088
1089 public synchronized <T extends PrintServiceAttribute>
1090 T getAttribute(Class<T> category)
1091 {
1092 if (category == null) {
1093 throw new NullPointerException("category");
1094 }
1095 if (!(PrintServiceAttribute.class.isAssignableFrom(category))) {
1096 throw new IllegalArgumentException("Not a PrintServiceAttribute");
1097 }
1098
1099 initAttributes();
1100
1101 if (category == PrinterName.class) {
1102 return (T)(new PrinterName(printer, null));
1103 } else if (category == PrinterInfo.class) {
1104 PrinterInfo pInfo = new PrinterInfo(printer, null);
1105 AttributeClass ac = (getAttMap != null) ?
1106 (AttributeClass)getAttMap.get(pInfo.getName())
1107 : null;
1108 if (ac != null) {
1109 return (T)(new PrinterInfo(ac.getStringValue(), null));
1110 }
1111 return (T)pInfo;
1112 } else if (category == QueuedJobCount.class) {
1113 QueuedJobCount qjc = new QueuedJobCount(0);
1114 AttributeClass ac = (getAttMap != null) ?
1115 (AttributeClass)getAttMap.get(qjc.getName())
1116 : null;
1117 if (ac != null) {
1118 qjc = new QueuedJobCount(ac.getIntValue());
1119 }
1120 return (T)qjc;
1121 } else if (category == PrinterIsAcceptingJobs.class) {
1122 PrinterIsAcceptingJobs accJob =
1123 PrinterIsAcceptingJobs.ACCEPTING_JOBS;
1124 AttributeClass ac = (getAttMap != null) ?
1125 (AttributeClass)getAttMap.get(accJob.getName())
1126 : null;
1127 if ((ac != null) && (ac.getByteValue() == 0)) {
1128 accJob = PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS;
1129 }
1130 return (T)accJob;
1131 } else if (category == ColorSupported.class) {
1132 ColorSupported cs = ColorSupported.SUPPORTED;
1133 AttributeClass ac = (getAttMap != null) ?
1134 (AttributeClass)getAttMap.get(cs.getName())
1135 : null;
1136 if ((ac != null) && (ac.getByteValue() == 0)) {
1137 cs = ColorSupported.NOT_SUPPORTED;
1138 }
1139 return (T)cs;
1140 } else if (category == PDLOverrideSupported.class) {
1141
1142 if (isCupsPrinter) {
1143 // Documented: For CUPS this will always be false
1144 return (T)PDLOverrideSupported.NOT_ATTEMPTED;
1145 } else {
1146 // REMIND: check attribute values
1147 return (T)PDLOverrideSupported.NOT_ATTEMPTED;
1148 }
1149 } else if (category == PrinterURI.class) {
1150 return (T)(new PrinterURI(myURI));
1151 } else {
1152 return null;
1153 }
1154 }
1155
1156
1157 public synchronized PrintServiceAttributeSet getAttributes() {
1158 // update getAttMap by sending again get-attributes IPP request
1159 init = false;
1160 initAttributes();
1161
1162 HashPrintServiceAttributeSet attrs =
1163 new HashPrintServiceAttributeSet();
1164
1165 for (int i=0; i < serviceAttributes.length; i++) {
1166 String name = (String)serviceAttributes[i][1];
1167 if (getAttMap != null && getAttMap.containsKey(name)) {
1168 Class c = (Class)serviceAttributes[i][0];
1169 PrintServiceAttribute psa = getAttribute(c);
1170 if (psa != null) {
1171 attrs.add(psa);
1172 }
1173 }
1174 }
1175 return AttributeSetUtilities.unmodifiableView(attrs);
1176 }
1177
1178 public boolean isIPPSupportedImages(String mimeType) {
1179 if (supportedDocFlavors == null) {
1180 getSupportedDocFlavors();
1181 }
1182
1183 if (mimeType.equals("image/png") && pngImagesAdded) {
1184 return true;
1185 } else if (mimeType.equals("image/gif") && gifImagesAdded) {
1186 return true;
1187 } else if (mimeType.equals("image/jpeg") && jpgImagesAdded) {
1188 return true;
1262 return false;
1263 }
1264 return true;
1265 }
1266
1267
1268 public boolean isAttributeValueSupported(Attribute attr,
1269 DocFlavor flavor,
1270 AttributeSet attributes) {
1271 if (attr == null) {
1272 throw new NullPointerException("null attribute");
1273 }
1274 if (flavor != null) {
1275 if (!isDocFlavorSupported(flavor)) {
1276 throw new IllegalArgumentException(flavor +
1277 " is an unsupported flavor");
1278 } else if (isAutoSense(flavor)) {
1279 return false;
1280 }
1281 }
1282 Class category = attr.getCategory();
1283 if (!isAttributeCategorySupported(category)) {
1284 return false;
1285 }
1286
1287 /* Test if the flavor is compatible with the attributes */
1288 if (!isDestinationSupported(flavor, attributes)) {
1289 return false;
1290 }
1291
1292 /* Test if the flavor is compatible with the category */
1293 if (attr.getCategory() == Chromaticity.class) {
1294 if ((flavor == null) ||
1295 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1296 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
1297 !isIPPSupportedImages(flavor.getMimeType())) {
1298 return attr == Chromaticity.COLOR;
1299 } else {
1300 return false;
1301 }
1302 } else if (attr.getCategory() == Copies.class) {
1380 throw new IllegalArgumentException(category +
1381 " is not an Attribute");
1382 }
1383 if (!isAttributeCategorySupported(category)) {
1384 return null;
1385 }
1386
1387 initAttributes();
1388
1389 String catName = null;
1390 for (int i=0; i < printReqAttribDefault.length; i++) {
1391 PrintRequestAttribute pra =
1392 (PrintRequestAttribute)printReqAttribDefault[i];
1393 if (pra.getCategory() == category) {
1394 catName = pra.getName();
1395 break;
1396 }
1397 }
1398 String attribName = catName+"-default";
1399 AttributeClass attribClass = (getAttMap != null) ?
1400 (AttributeClass)getAttMap.get(attribName) : null;
1401
1402 if (category == Copies.class) {
1403 if (attribClass != null) {
1404 return new Copies(attribClass.getIntValue());
1405 } else {
1406 return new Copies(1);
1407 }
1408 } else if (category == Chromaticity.class) {
1409 return Chromaticity.COLOR;
1410 } else if (category == Destination.class) {
1411 try {
1412 return new Destination((new File("out.ps")).toURI());
1413 } catch (SecurityException se) {
1414 try {
1415 return new Destination(new URI("file:out.ps"));
1416 } catch (URISyntaxException e) {
1417 return null;
1418 }
1419 }
1420 } else if (category == Fidelity.class) {
1583 }
1584
1585 public String getName() {
1586 /*
1587 * Mac is using printer-info IPP attribute for its human-readable printer
1588 * name and is also the identifier used in NSPrintInfo:setPrinter.
1589 */
1590 if (UnixPrintServiceLookup.isMac()) {
1591 PrintServiceAttributeSet psaSet = this.getAttributes();
1592 if (psaSet != null) {
1593 PrinterInfo pName = (PrinterInfo)psaSet.get(PrinterInfo.class);
1594 if (pName != null) {
1595 return pName.toString();
1596 }
1597 }
1598 }
1599 return printer;
1600 }
1601
1602
1603 public boolean usesClass(Class c) {
1604 return (c == sun.print.PSPrinterJob.class);
1605 }
1606
1607
1608 public static HttpURLConnection getIPPConnection(URL url) {
1609 HttpURLConnection connection;
1610 URLConnection urlc;
1611 try {
1612 urlc = url.openConnection();
1613 } catch (java.io.IOException ioe) {
1614 return null;
1615 }
1616 if (!(urlc instanceof HttpURLConnection)) {
1617 return null;
1618 }
1619 connection = (HttpURLConnection)urlc;
1620 connection.setUseCaches(false);
1621 connection.setDefaultUseCaches(false);
1622 connection.setDoInput(true);
1623 connection.setDoOutput(true);
1658 }
1659 return isPS.booleanValue();
1660 }
1661
1662
1663 private void opGetAttributes() {
1664 try {
1665 debug_println(debugPrefix+"opGetAttributes myURI "+myURI+" myURL "+myURL);
1666
1667 AttributeClass attClNoUri[] = {
1668 AttributeClass.ATTRIBUTES_CHARSET,
1669 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE};
1670
1671 AttributeClass attCl[] = {
1672 AttributeClass.ATTRIBUTES_CHARSET,
1673 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE,
1674 new AttributeClass("printer-uri",
1675 AttributeClass.TAG_URI,
1676 ""+myURI)};
1677
1678 OutputStream os = (OutputStream)java.security.AccessController.
1679 doPrivileged(new java.security.PrivilegedAction() {
1680 public Object run() {
1681 try {
1682 return urlConnection.getOutputStream();
1683 } catch (Exception e) {
1684 }
1685 return null;
1686 }
1687 });
1688
1689 if (os == null) {
1690 return;
1691 }
1692
1693 boolean success = (myURI == null) ?
1694 writeIPPRequest(os, OP_GET_ATTRIBUTES, attClNoUri) :
1695 writeIPPRequest(os, OP_GET_ATTRIBUTES, attCl);
1696 if (success) {
1697 InputStream is = null;
1698 if ((is = urlConnection.getInputStream())!=null) {
1699 HashMap[] responseMap = readIPPResponse(is);
1700
1701 if (responseMap != null && responseMap.length > 0) {
1702 getAttMap = responseMap[0];
1703 }
1704 } else {
1705 debug_println(debugPrefix+"opGetAttributes - null input stream");
1706 }
1707 is.close();
1708 }
1709 os.close();
1710 } catch (java.io.IOException e) {
1711 debug_println(debugPrefix+"opGetAttributes - input/output stream: "+e);
1712 }
1713 }
1714
1715
1716 public static boolean writeIPPRequest(OutputStream os,
1717 String operCode,
1718 AttributeClass[] attCl) {
1719 OutputStreamWriter osw;
1754 ac.getType() <= AttributeClass.TAG_MIME_MEDIATYPE){
1755 valStr = (String)ac.getObjectValue();
1756 bytes[0] = 0; bytes[1] = (char)valStr.length();
1757 osw.write(bytes, 0, 2);
1758 osw.write(valStr, 0, valStr.length());
1759 } // REMIND: need to support other value tags but for CUPS
1760 // string is all we need.
1761 }
1762
1763 osw.write(GRPTAG_END_ATTRIBUTES);
1764 osw.flush();
1765 osw.close();
1766 } catch (java.io.IOException ioe) {
1767 debug_println(debugPrefix+"writeIPPRequest, IPPPrintService Exception in writeIPPRequest: "+ioe);
1768 return false;
1769 }
1770 return true;
1771 }
1772
1773
1774 public static HashMap[] readIPPResponse(InputStream inputStream) {
1775
1776 if (inputStream == null) {
1777 return null;
1778 }
1779
1780 byte response[] = new byte[MAX_ATTRIBUTE_LENGTH];
1781 try {
1782
1783 DataInputStream ois = new DataInputStream(inputStream);
1784
1785 // read status and ID
1786 if ((ois.read(response, 0, 8) > -1) &&
1787 (response[2] == STATUSCODE_SUCCESS)) {
1788
1789 ByteArrayOutputStream outObj;
1790 int counter=0;
1791 short len = 0;
1792 String attribStr = null;
1793 // assign default value
1794 byte valTagByte = AttributeClass.TAG_KEYWORD;
1795 ArrayList respList = new ArrayList();
1796 HashMap responseMap = new HashMap();
1797
1798 response[0] = ois.readByte();
1799
1800 // check for group tags
1801 while ((response[0] >= GRPTAG_OP_ATTRIBUTES) &&
1802 (response[0] <= GRPTAG_PRINTER_ATTRIBUTES)
1803 && (response[0] != GRPTAG_END_ATTRIBUTES)) {
1804 debug_println(debugPrefix+"readIPPResponse, checking group tag, response[0]= "+
1805 response[0]);
1806
1807 outObj = new ByteArrayOutputStream();
1808 //make sure counter and attribStr are re-initialized
1809 counter = 0;
1810 attribStr = null;
1811
1812 // read value tag
1813 response[0] = ois.readByte();
1814 while (response[0] >= AttributeClass.TAG_UNSUPPORTED_VALUE &&
1815 response[0] <= AttributeClass.TAG_MEMBER_ATTRNAME) {
1816 // read name length
1817 len = ois.readShort();
1818
1819 // If current value is not part of previous attribute
1820 // then close stream and add it to HashMap.
1821 // It is part of previous attribute if name length=0.
1822 if ((len != 0) && (attribStr != null)) {
1823 //last byte is the total # of values
1824 outObj.write(counter);
1825 outObj.flush();
1826 outObj.close();
1827 byte outArray[] = outObj.toByteArray();
1828
1829 // if key exists, new HashMap
1830 if (responseMap.containsKey(attribStr)) {
1831 respList.add(responseMap);
1832 responseMap = new HashMap();
1833 }
1834
1835 // exclude those that are unknown
1836 if (valTagByte >= AttributeClass.TAG_INT) {
1837 AttributeClass ac =
1838 new AttributeClass(attribStr,
1839 valTagByte,
1840 outArray);
1841
1842 responseMap.put(ac.getName(), ac);
1843 debug_println(debugPrefix+ "readIPPResponse "+ac);
1844 }
1845
1846 outObj = new ByteArrayOutputStream();
1847 counter = 0; //reset counter
1848 }
1849 //check if this is new value tag
1850 if (counter == 0) {
1851 valTagByte = response[0];
1852 }
1868 if (len > MAX_ATTRIBUTE_LENGTH) {
1869 response = new byte[len]; // expand as needed
1870 }
1871 ois.read(response, 0, len);
1872 // write value of "len" length
1873 outObj.write(response, 0, len);
1874 counter++;
1875 // read next byte
1876 response[0] = ois.readByte();
1877 }
1878
1879 if (attribStr != null) {
1880 outObj.write(counter);
1881 outObj.flush();
1882 outObj.close();
1883
1884 // if key exists in old HashMap, new HashMap
1885 if ((counter != 0) &&
1886 responseMap.containsKey(attribStr)) {
1887 respList.add(responseMap);
1888 responseMap = new HashMap();
1889 }
1890
1891 byte outArray[] = outObj.toByteArray();
1892
1893 AttributeClass ac =
1894 new AttributeClass(attribStr,
1895 valTagByte,
1896 outArray);
1897 responseMap.put(ac.getName(), ac);
1898 }
1899 }
1900 ois.close();
1901 if ((responseMap != null) && (responseMap.size() > 0)) {
1902 respList.add(responseMap);
1903 }
1904 return (HashMap[])respList.toArray(
1905 new HashMap[respList.size()]);
1906 } else {
1907 debug_println(debugPrefix+
1908 "readIPPResponse client error, IPP status code: 0x"+
1909 toHex(response[2]) + toHex(response[3]));
1910 return null;
1911 }
1912
1913 } catch (java.io.IOException e) {
1914 debug_println(debugPrefix+"readIPPResponse: "+e);
1915 if (debugPrint) {
1916 e.printStackTrace();
1917 }
1918 return null;
1919 }
1920 }
1921
1922 private static String toHex(byte v) {
1923 String s = Integer.toHexString(v&0xff);
1924 return (s.length() == 2) ? s : "0"+s;
1925 }
|
74 private static final String FORCE_PIPE_PROP = "sun.print.ippdebug";
75
76 static {
77 String debugStr = java.security.AccessController.doPrivileged(
78 new sun.security.action.GetPropertyAction(FORCE_PIPE_PROP));
79
80 debugPrint = "true".equalsIgnoreCase(debugStr);
81 }
82
83 private String printer;
84 private URI myURI;
85 private URL myURL;
86 transient private ServiceNotifier notifier = null;
87
88 private static int MAXCOPIES = 1000;
89 private static short MAX_ATTRIBUTE_LENGTH = 255;
90
91 private CUPSPrinter cps;
92 private HttpURLConnection urlConnection = null;
93 private DocFlavor[] supportedDocFlavors;
94 private Class<?>[] supportedCats;
95 private MediaTray[] mediaTrays;
96 private MediaSizeName[] mediaSizeNames;
97 private CustomMediaSizeName[] customMediaSizeNames;
98 private int defaultMediaIndex;
99 private boolean isCupsPrinter;
100 private boolean init;
101 private Boolean isPS;
102 private HashMap<String, AttributeClass> getAttMap;
103 private boolean pngImagesAdded = false;
104 private boolean gifImagesAdded = false;
105 private boolean jpgImagesAdded = false;
106
107
108 /**
109 * IPP Status Codes
110 */
111 private static final byte STATUSCODE_SUCCESS = 0x00;
112
113 /**
114 * IPP Group Tags. Each tag is used once before the first attribute
115 * of that group.
116 */
117 // operation attributes group
118 private static final byte GRPTAG_OP_ATTRIBUTES = 0x01;
119 // job attributes group
120 private static final byte GRPTAG_JOB_ATTRIBUTES = 0x02;
121 // printer attributes group
122 private static final byte GRPTAG_PRINTER_ATTRIBUTES = 0x04;
407 // maybe use "&& (usePPD)" later?
408 // Another reason why we use PPD is because
409 // IPP currently does not support it but PPD does.
410
411 try {
412 cps = new CUPSPrinter(printer);
413 mediaSizeNames = cps.getMediaSizeNames();
414 mediaTrays = cps.getMediaTrays();
415 customMediaSizeNames = cps.getCustomMediaSizeNames();
416 urlConnection.disconnect();
417 init = true;
418 return;
419 } catch (Exception e) {
420 IPPPrintService.debug_println(debugPrefix+
421 "initAttributes, error creating CUPSPrinter e="+e);
422 }
423 }
424
425 // use IPP to get all media,
426 Media[] allMedia = getSupportedMedia();
427 ArrayList<Media> sizeList = new ArrayList<>();
428 ArrayList<Media> trayList = new ArrayList<>();
429 for (int i=0; i<allMedia.length; i++) {
430 if (allMedia[i] instanceof MediaSizeName) {
431 sizeList.add(allMedia[i]);
432 } else if (allMedia[i] instanceof MediaTray) {
433 trayList.add(allMedia[i]);
434 }
435 }
436
437 if (sizeList != null) {
438 mediaSizeNames = new MediaSizeName[sizeList.size()];
439 mediaSizeNames = sizeList.toArray(mediaSizeNames);
440 }
441 if (trayList != null) {
442 mediaTrays = new MediaTray[trayList.size()];
443 mediaTrays = trayList.toArray(mediaTrays);
444 }
445 urlConnection.disconnect();
446
447 init = true;
448 }
449 }
450
451
452 public DocPrintJob createPrintJob() {
453 SecurityManager security = System.getSecurityManager();
454 if (security != null) {
455 security.checkPrintJobAccess();
456 }
457 // REMIND: create IPPPrintJob
458 return new UnixPrintJob(this);
459 }
460
461
462 public synchronized Object
463 getSupportedAttributeValues(Class<? extends Attribute> category,
484 if (!isAttributeCategorySupported(category)) {
485 return null;
486 }
487
488 /* Test if the flavor is compatible with the attributes */
489 if (!isDestinationSupported(flavor, attributes)) {
490 return null;
491 }
492
493 initAttributes();
494
495 /* Test if the flavor is compatible with the category */
496 if ((category == Copies.class) ||
497 (category == CopiesSupported.class)) {
498 if (flavor == null ||
499 !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
500 flavor.equals(DocFlavor.URL.POSTSCRIPT) ||
501 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) {
502 CopiesSupported cs = new CopiesSupported(1, MAXCOPIES);
503 AttributeClass attribClass = (getAttMap != null) ?
504 getAttMap.get(cs.getName()) : null;
505 if (attribClass != null) {
506 int[] range = attribClass.getIntRangeValue();
507 cs = new CopiesSupported(range[0], range[1]);
508 }
509 return cs;
510 } else {
511 return null;
512 }
513 } else if (category == Chromaticity.class) {
514 if (flavor == null ||
515 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
516 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
517 !isIPPSupportedImages(flavor.getMimeType())) {
518 Chromaticity[]arr = new Chromaticity[1];
519 arr[0] = Chromaticity.COLOR;
520 return (arr);
521 } else {
522 return null;
523 }
524 } else if (category == Destination.class) {
526 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
527 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
528 try {
529 return new Destination((new File("out.ps")).toURI());
530 } catch (SecurityException se) {
531 try {
532 return new Destination(new URI("file:out.ps"));
533 } catch (URISyntaxException e) {
534 return null;
535 }
536 }
537 }
538 return null;
539 } else if (category == Fidelity.class) {
540 Fidelity []arr = new Fidelity[2];
541 arr[0] = Fidelity.FIDELITY_FALSE;
542 arr[1] = Fidelity.FIDELITY_TRUE;
543 return arr;
544 } else if (category == Finishings.class) {
545 AttributeClass attribClass = (getAttMap != null) ?
546 getAttMap.get("finishings-supported")
547 : null;
548 if (attribClass != null) {
549 int[] finArray = attribClass.getArrayOfIntValues();
550 if ((finArray != null) && (finArray.length > 0)) {
551 Finishings[] finSup = new Finishings[finArray.length];
552 for (int i=0; i<finArray.length; i++) {
553 finSup[i] = Finishings.NONE;
554 Finishings[] fAll = (Finishings[])
555 (new ExtFinishing(100)).getAll();
556 for (int j=0; j<fAll.length; j++) {
557 if (finArray[i] == fAll[j].getValue()) {
558 finSup[i] = fAll[j];
559 break;
560 }
561 }
562 }
563 return finSup;
564 }
565 }
566 } else if (category == JobName.class) {
629 //default printable area is that of default mediasize
630 return mpas;
631 }
632
633 for (int i=0; i<mediaSizeNames.length; i++) {
634 if (msn.equals(mediaSizeNames[i])) {
635 match = i;
636 }
637 }
638 }
639
640 if (match == -1) {
641 return null;
642 } else {
643 MediaPrintableArea []arr = new MediaPrintableArea[1];
644 arr[0] = mpas[match];
645 return arr;
646 }
647 } else if (category == NumberUp.class) {
648 AttributeClass attribClass = (getAttMap != null) ?
649 getAttMap.get("number-up-supported") : null;
650 if (attribClass != null) {
651 int[] values = attribClass.getArrayOfIntValues();
652 if (values != null) {
653 NumberUp[] nUp = new NumberUp[values.length];
654 for (int i=0; i<values.length; i++) {
655 nUp[i] = new NumberUp(values[i]);
656 }
657 return nUp;
658 } else {
659 return null;
660 }
661 }
662 } else if (category == OrientationRequested.class) {
663 if ((flavor != null) &&
664 (flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
665 flavor.equals(DocFlavor.URL.POSTSCRIPT) ||
666 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) {
667 return null;
668 }
669
670 boolean revPort = false;
671 OrientationRequested[] orientSup = null;
672
673 AttributeClass attribClass = (getAttMap != null) ?
674 getAttMap.get("orientation-requested-supported")
675 : null;
676 if (attribClass != null) {
677 int[] orientArray = attribClass.getArrayOfIntValues();
678 if ((orientArray != null) && (orientArray.length > 0)) {
679 orientSup =
680 new OrientationRequested[orientArray.length];
681 for (int i=0; i<orientArray.length; i++) {
682 switch (orientArray[i]) {
683 default:
684 case 3 :
685 orientSup[i] = OrientationRequested.PORTRAIT;
686 break;
687 case 4:
688 orientSup[i] = OrientationRequested.LANDSCAPE;
689 break;
690 case 5:
691 orientSup[i] =
692 OrientationRequested.REVERSE_LANDSCAPE;
693 break;
694 case 6:
729 arr[0] = new PageRanges(1, Integer.MAX_VALUE);
730 return arr;
731 } else {
732 // Returning null as this is not yet supported in UnixPrintJob.
733 return null;
734 }
735 } else if (category == RequestingUserName.class) {
736 String userName = "";
737 try {
738 userName = System.getProperty("user.name", "");
739 } catch (SecurityException se) {
740 }
741 return new RequestingUserName(userName, null);
742 } else if (category == Sides.class) {
743 // The printer takes care of Sides so if short-edge
744 // is chosen in a job, the rotation is done by the printer.
745 // Orientation is rotated by emulation if pageable
746 // or printable so if the document is in Landscape, this may
747 // result in double rotation.
748 AttributeClass attribClass = (getAttMap != null) ?
749 getAttMap.get("sides-supported")
750 : null;
751 if (attribClass != null) {
752 String[] sidesArray = attribClass.getArrayOfStringValues();
753 if ((sidesArray != null) && (sidesArray.length > 0)) {
754 Sides[] sidesSup = new Sides[sidesArray.length];
755 for (int i=0; i<sidesArray.length; i++) {
756 if (sidesArray[i].endsWith("long-edge")) {
757 sidesSup[i] = Sides.TWO_SIDED_LONG_EDGE;
758 } else if (sidesArray[i].endsWith("short-edge")) {
759 sidesSup[i] = Sides.TWO_SIDED_SHORT_EDGE;
760 } else {
761 sidesSup[i] = Sides.ONE_SIDED;
762 }
763 }
764 return sidesSup;
765 }
766 }
767 }
768
769 return null;
814 } else {
815 return unsupp;
816 }
817 }
818
819
820 public synchronized DocFlavor[] getSupportedDocFlavors() {
821
822 if (supportedDocFlavors != null) {
823 int len = supportedDocFlavors.length;
824 DocFlavor[] copyflavors = new DocFlavor[len];
825 System.arraycopy(supportedDocFlavors, 0, copyflavors, 0, len);
826 return copyflavors;
827 }
828 initAttributes();
829
830 if ((getAttMap != null) &&
831 getAttMap.containsKey("document-format-supported")) {
832
833 AttributeClass attribClass =
834 getAttMap.get("document-format-supported");
835 if (attribClass != null) {
836 String mimeType;
837 boolean psSupported = false;
838 String[] docFlavors = attribClass.getArrayOfStringValues();
839 DocFlavor[] flavors;
840 HashSet<Object> docList = new HashSet<>();
841 int j;
842 String hostEnc = DocFlavor.hostEncoding.
843 toLowerCase(Locale.ENGLISH);
844 boolean addHostEncoding = !hostEnc.equals("utf-8") &&
845 !hostEnc.equals("utf-16") && !hostEnc.equals("utf-16be") &&
846 !hostEnc.equals("utf-16le") && !hostEnc.equals("us-ascii");
847
848 for (int i = 0; i < docFlavors.length; i++) {
849 for (j=0; j<allDocFlavors.length; j++) {
850 flavors = (DocFlavor[])allDocFlavors[j];
851
852 mimeType = flavors[0].getMimeType();
853 if (mimeType.startsWith(docFlavors[i])) {
854
855 docList.addAll(Arrays.asList(flavors));
856
857 if (mimeType.equals("text/plain") &&
858 addHostEncoding) {
859 docList.add(Arrays.asList(textPlainHost));
860 } else if (mimeType.equals("text/html") &&
951 Media[] sizes = sampleSize.getSuperEnumTable();
952 for (int i=0; i<sizes.length; i++) {
953 if (mediaName.equals(""+sizes[i])) {
954 return sizes[i];
955 }
956 }
957 CustomMediaTray sampleTray = new CustomMediaTray("sample", "");
958 Media[] trays = sampleTray.getSuperEnumTable();
959 for (int i=0; i<trays.length; i++) {
960 if (mediaName.equals(""+trays[i])) {
961 return trays[i];
962 }
963 }
964 return null;
965 }
966
967 private Media[] getSupportedMedia() {
968 if ((getAttMap != null) &&
969 getAttMap.containsKey("media-supported")) {
970
971 AttributeClass attribClass = getAttMap.get("media-supported");
972
973 if (attribClass != null) {
974 String[] mediaVals = attribClass.getArrayOfStringValues();
975 Media msn;
976 Media[] mediaNames =
977 new Media[mediaVals.length];
978 for (int i=0; i<mediaVals.length; i++) {
979 msn = getIPPMedia(mediaVals[i]);
980 //REMIND: if null, create custom?
981 mediaNames[i] = msn;
982 }
983 return mediaNames;
984 }
985 }
986 return new Media[0];
987 }
988
989
990 public synchronized Class<?>[] getSupportedAttributeCategories() {
991 if (supportedCats != null) {
992 return supportedCats;
993 }
994
995 initAttributes();
996
997 ArrayList<Class<?>> catList = new ArrayList<>();
998
999 for (int i=0; i < printReqAttribDefault.length; i++) {
1000 PrintRequestAttribute pra =
1001 (PrintRequestAttribute)printReqAttribDefault[i];
1002 if (getAttMap != null &&
1003 getAttMap.containsKey(pra.getName()+"-supported")) {
1004 catList.add(pra.getCategory());
1005 }
1006 }
1007
1008 // Some IPP printers like lexc710 do not have list of supported media
1009 // but CUPS can get the media from PPD, so we still report as
1010 // supported category.
1011 if (isCupsPrinter) {
1012 if (!catList.contains(Media.class)) {
1013 catList.add(Media.class);
1014 }
1015
1016 // Always add MediaPrintable for cups,
1017 // because we can get it from PPD.
1018 catList.add(MediaPrintableArea.class);
1019
1020 // this is already supported in UnixPrintJob
1021 catList.add(Destination.class);
1022
1023 // It is unfortunate that CUPS doesn't provide a way to query
1024 // if printer supports collation but since most printers
1025 // now supports collation and that most OS has a way
1026 // of setting it, it is a safe assumption to just always
1027 // include SheetCollate as supported attribute.
1028
1029 /*
1030 In Linux, we use Postscript for rendering but Linux still
1031 has issues in propagating Postscript-embedded setpagedevice
1032 setting like collation. Therefore, we temporarily exclude
1033 Linux.
1034 */
1035 if (!UnixPrintServiceLookup.isLinux()) {
1036 catList.add(SheetCollate.class);
1037 }
1038 }
1039
1040 // With the assumption that Chromaticity is equivalent to
1041 // ColorSupported.
1042 if (getAttMap != null && getAttMap.containsKey("color-supported")) {
1043 catList.add(Chromaticity.class);
1044 }
1045 supportedCats = new Class<?>[catList.size()];
1046 catList.toArray(supportedCats);
1047 return supportedCats;
1048 }
1049
1050
1051 public boolean
1052 isAttributeCategorySupported(Class<? extends Attribute> category)
1053 {
1054 if (category == null) {
1055 throw new NullPointerException("null category");
1056 }
1057 if (!(Attribute.class.isAssignableFrom(category))) {
1058 throw new IllegalArgumentException(category +
1059 " is not an Attribute");
1060 }
1061
1062 if (supportedCats == null) {
1063 getSupportedAttributeCategories();
1064 }
1065
1066 // It is safe to assume that Orientation is always supported
1067 // and even if CUPS or an IPP device reports it as not,
1068 // our renderer can do portrait, landscape and
1069 // reverse landscape.
1070 if (category == OrientationRequested.class) {
1071 return true;
1072 }
1073
1074 for (int i=0;i<supportedCats.length;i++) {
1075 if (category == supportedCats[i]) {
1076 return true;
1077 }
1078 }
1079
1080 return false;
1081 }
1082
1083 @SuppressWarnings("unchecked")
1084 public synchronized <T extends PrintServiceAttribute>
1085 T getAttribute(Class<T> category)
1086 {
1087 if (category == null) {
1088 throw new NullPointerException("category");
1089 }
1090 if (!(PrintServiceAttribute.class.isAssignableFrom(category))) {
1091 throw new IllegalArgumentException("Not a PrintServiceAttribute");
1092 }
1093
1094 initAttributes();
1095
1096 if (category == PrinterName.class) {
1097 return (T)(new PrinterName(printer, null));
1098 } else if (category == PrinterInfo.class) {
1099 PrinterInfo pInfo = new PrinterInfo(printer, null);
1100 AttributeClass ac = (getAttMap != null) ?
1101 getAttMap.get(pInfo.getName())
1102 : null;
1103 if (ac != null) {
1104 return (T)(new PrinterInfo(ac.getStringValue(), null));
1105 }
1106 return (T)pInfo;
1107 } else if (category == QueuedJobCount.class) {
1108 QueuedJobCount qjc = new QueuedJobCount(0);
1109 AttributeClass ac = (getAttMap != null) ?
1110 getAttMap.get(qjc.getName())
1111 : null;
1112 if (ac != null) {
1113 qjc = new QueuedJobCount(ac.getIntValue());
1114 }
1115 return (T)qjc;
1116 } else if (category == PrinterIsAcceptingJobs.class) {
1117 PrinterIsAcceptingJobs accJob =
1118 PrinterIsAcceptingJobs.ACCEPTING_JOBS;
1119 AttributeClass ac = (getAttMap != null) ?
1120 getAttMap.get(accJob.getName())
1121 : null;
1122 if ((ac != null) && (ac.getByteValue() == 0)) {
1123 accJob = PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS;
1124 }
1125 return (T)accJob;
1126 } else if (category == ColorSupported.class) {
1127 ColorSupported cs = ColorSupported.SUPPORTED;
1128 AttributeClass ac = (getAttMap != null) ?
1129 getAttMap.get(cs.getName())
1130 : null;
1131 if ((ac != null) && (ac.getByteValue() == 0)) {
1132 cs = ColorSupported.NOT_SUPPORTED;
1133 }
1134 return (T)cs;
1135 } else if (category == PDLOverrideSupported.class) {
1136
1137 if (isCupsPrinter) {
1138 // Documented: For CUPS this will always be false
1139 return (T)PDLOverrideSupported.NOT_ATTEMPTED;
1140 } else {
1141 // REMIND: check attribute values
1142 return (T)PDLOverrideSupported.NOT_ATTEMPTED;
1143 }
1144 } else if (category == PrinterURI.class) {
1145 return (T)(new PrinterURI(myURI));
1146 } else {
1147 return null;
1148 }
1149 }
1150
1151
1152 public synchronized PrintServiceAttributeSet getAttributes() {
1153 // update getAttMap by sending again get-attributes IPP request
1154 init = false;
1155 initAttributes();
1156
1157 HashPrintServiceAttributeSet attrs =
1158 new HashPrintServiceAttributeSet();
1159
1160 for (int i=0; i < serviceAttributes.length; i++) {
1161 String name = (String)serviceAttributes[i][1];
1162 if (getAttMap != null && getAttMap.containsKey(name)) {
1163 @SuppressWarnings("unchecked")
1164 Class<PrintServiceAttribute> c = (Class<PrintServiceAttribute>)serviceAttributes[i][0];
1165 PrintServiceAttribute psa = getAttribute(c);
1166 if (psa != null) {
1167 attrs.add(psa);
1168 }
1169 }
1170 }
1171 return AttributeSetUtilities.unmodifiableView(attrs);
1172 }
1173
1174 public boolean isIPPSupportedImages(String mimeType) {
1175 if (supportedDocFlavors == null) {
1176 getSupportedDocFlavors();
1177 }
1178
1179 if (mimeType.equals("image/png") && pngImagesAdded) {
1180 return true;
1181 } else if (mimeType.equals("image/gif") && gifImagesAdded) {
1182 return true;
1183 } else if (mimeType.equals("image/jpeg") && jpgImagesAdded) {
1184 return true;
1258 return false;
1259 }
1260 return true;
1261 }
1262
1263
1264 public boolean isAttributeValueSupported(Attribute attr,
1265 DocFlavor flavor,
1266 AttributeSet attributes) {
1267 if (attr == null) {
1268 throw new NullPointerException("null attribute");
1269 }
1270 if (flavor != null) {
1271 if (!isDocFlavorSupported(flavor)) {
1272 throw new IllegalArgumentException(flavor +
1273 " is an unsupported flavor");
1274 } else if (isAutoSense(flavor)) {
1275 return false;
1276 }
1277 }
1278 Class<? extends Attribute> category = attr.getCategory();
1279 if (!isAttributeCategorySupported(category)) {
1280 return false;
1281 }
1282
1283 /* Test if the flavor is compatible with the attributes */
1284 if (!isDestinationSupported(flavor, attributes)) {
1285 return false;
1286 }
1287
1288 /* Test if the flavor is compatible with the category */
1289 if (attr.getCategory() == Chromaticity.class) {
1290 if ((flavor == null) ||
1291 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1292 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
1293 !isIPPSupportedImages(flavor.getMimeType())) {
1294 return attr == Chromaticity.COLOR;
1295 } else {
1296 return false;
1297 }
1298 } else if (attr.getCategory() == Copies.class) {
1376 throw new IllegalArgumentException(category +
1377 " is not an Attribute");
1378 }
1379 if (!isAttributeCategorySupported(category)) {
1380 return null;
1381 }
1382
1383 initAttributes();
1384
1385 String catName = null;
1386 for (int i=0; i < printReqAttribDefault.length; i++) {
1387 PrintRequestAttribute pra =
1388 (PrintRequestAttribute)printReqAttribDefault[i];
1389 if (pra.getCategory() == category) {
1390 catName = pra.getName();
1391 break;
1392 }
1393 }
1394 String attribName = catName+"-default";
1395 AttributeClass attribClass = (getAttMap != null) ?
1396 getAttMap.get(attribName) : null;
1397
1398 if (category == Copies.class) {
1399 if (attribClass != null) {
1400 return new Copies(attribClass.getIntValue());
1401 } else {
1402 return new Copies(1);
1403 }
1404 } else if (category == Chromaticity.class) {
1405 return Chromaticity.COLOR;
1406 } else if (category == Destination.class) {
1407 try {
1408 return new Destination((new File("out.ps")).toURI());
1409 } catch (SecurityException se) {
1410 try {
1411 return new Destination(new URI("file:out.ps"));
1412 } catch (URISyntaxException e) {
1413 return null;
1414 }
1415 }
1416 } else if (category == Fidelity.class) {
1579 }
1580
1581 public String getName() {
1582 /*
1583 * Mac is using printer-info IPP attribute for its human-readable printer
1584 * name and is also the identifier used in NSPrintInfo:setPrinter.
1585 */
1586 if (UnixPrintServiceLookup.isMac()) {
1587 PrintServiceAttributeSet psaSet = this.getAttributes();
1588 if (psaSet != null) {
1589 PrinterInfo pName = (PrinterInfo)psaSet.get(PrinterInfo.class);
1590 if (pName != null) {
1591 return pName.toString();
1592 }
1593 }
1594 }
1595 return printer;
1596 }
1597
1598
1599 public boolean usesClass(Class<?> c) {
1600 return (c == sun.print.PSPrinterJob.class);
1601 }
1602
1603
1604 public static HttpURLConnection getIPPConnection(URL url) {
1605 HttpURLConnection connection;
1606 URLConnection urlc;
1607 try {
1608 urlc = url.openConnection();
1609 } catch (java.io.IOException ioe) {
1610 return null;
1611 }
1612 if (!(urlc instanceof HttpURLConnection)) {
1613 return null;
1614 }
1615 connection = (HttpURLConnection)urlc;
1616 connection.setUseCaches(false);
1617 connection.setDefaultUseCaches(false);
1618 connection.setDoInput(true);
1619 connection.setDoOutput(true);
1654 }
1655 return isPS.booleanValue();
1656 }
1657
1658
1659 private void opGetAttributes() {
1660 try {
1661 debug_println(debugPrefix+"opGetAttributes myURI "+myURI+" myURL "+myURL);
1662
1663 AttributeClass attClNoUri[] = {
1664 AttributeClass.ATTRIBUTES_CHARSET,
1665 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE};
1666
1667 AttributeClass attCl[] = {
1668 AttributeClass.ATTRIBUTES_CHARSET,
1669 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE,
1670 new AttributeClass("printer-uri",
1671 AttributeClass.TAG_URI,
1672 ""+myURI)};
1673
1674 OutputStream os = java.security.AccessController.
1675 doPrivileged(new java.security.PrivilegedAction<OutputStream>() {
1676 public OutputStream run() {
1677 try {
1678 return urlConnection.getOutputStream();
1679 } catch (Exception e) {
1680 }
1681 return null;
1682 }
1683 });
1684
1685 if (os == null) {
1686 return;
1687 }
1688
1689 boolean success = (myURI == null) ?
1690 writeIPPRequest(os, OP_GET_ATTRIBUTES, attClNoUri) :
1691 writeIPPRequest(os, OP_GET_ATTRIBUTES, attCl);
1692 if (success) {
1693 InputStream is = null;
1694 if ((is = urlConnection.getInputStream())!=null) {
1695 HashMap<String, AttributeClass>[] responseMap = readIPPResponse(is);
1696
1697 if (responseMap != null && responseMap.length > 0) {
1698 getAttMap = responseMap[0];
1699 }
1700 } else {
1701 debug_println(debugPrefix+"opGetAttributes - null input stream");
1702 }
1703 is.close();
1704 }
1705 os.close();
1706 } catch (java.io.IOException e) {
1707 debug_println(debugPrefix+"opGetAttributes - input/output stream: "+e);
1708 }
1709 }
1710
1711
1712 public static boolean writeIPPRequest(OutputStream os,
1713 String operCode,
1714 AttributeClass[] attCl) {
1715 OutputStreamWriter osw;
1750 ac.getType() <= AttributeClass.TAG_MIME_MEDIATYPE){
1751 valStr = (String)ac.getObjectValue();
1752 bytes[0] = 0; bytes[1] = (char)valStr.length();
1753 osw.write(bytes, 0, 2);
1754 osw.write(valStr, 0, valStr.length());
1755 } // REMIND: need to support other value tags but for CUPS
1756 // string is all we need.
1757 }
1758
1759 osw.write(GRPTAG_END_ATTRIBUTES);
1760 osw.flush();
1761 osw.close();
1762 } catch (java.io.IOException ioe) {
1763 debug_println(debugPrefix+"writeIPPRequest, IPPPrintService Exception in writeIPPRequest: "+ioe);
1764 return false;
1765 }
1766 return true;
1767 }
1768
1769
1770 public static HashMap<String, AttributeClass>[] readIPPResponse(InputStream inputStream) {
1771
1772 if (inputStream == null) {
1773 return null;
1774 }
1775
1776 byte response[] = new byte[MAX_ATTRIBUTE_LENGTH];
1777 try {
1778
1779 DataInputStream ois = new DataInputStream(inputStream);
1780
1781 // read status and ID
1782 if ((ois.read(response, 0, 8) > -1) &&
1783 (response[2] == STATUSCODE_SUCCESS)) {
1784
1785 ByteArrayOutputStream outObj;
1786 int counter=0;
1787 short len = 0;
1788 String attribStr = null;
1789 // assign default value
1790 byte valTagByte = AttributeClass.TAG_KEYWORD;
1791 ArrayList<HashMap<String, AttributeClass>> respList = new ArrayList<>();
1792 HashMap<String, AttributeClass> responseMap = new HashMap<>();
1793
1794 response[0] = ois.readByte();
1795
1796 // check for group tags
1797 while ((response[0] >= GRPTAG_OP_ATTRIBUTES) &&
1798 (response[0] <= GRPTAG_PRINTER_ATTRIBUTES)
1799 && (response[0] != GRPTAG_END_ATTRIBUTES)) {
1800 debug_println(debugPrefix+"readIPPResponse, checking group tag, response[0]= "+
1801 response[0]);
1802
1803 outObj = new ByteArrayOutputStream();
1804 //make sure counter and attribStr are re-initialized
1805 counter = 0;
1806 attribStr = null;
1807
1808 // read value tag
1809 response[0] = ois.readByte();
1810 while (response[0] >= AttributeClass.TAG_UNSUPPORTED_VALUE &&
1811 response[0] <= AttributeClass.TAG_MEMBER_ATTRNAME) {
1812 // read name length
1813 len = ois.readShort();
1814
1815 // If current value is not part of previous attribute
1816 // then close stream and add it to HashMap.
1817 // It is part of previous attribute if name length=0.
1818 if ((len != 0) && (attribStr != null)) {
1819 //last byte is the total # of values
1820 outObj.write(counter);
1821 outObj.flush();
1822 outObj.close();
1823 byte outArray[] = outObj.toByteArray();
1824
1825 // if key exists, new HashMap
1826 if (responseMap.containsKey(attribStr)) {
1827 respList.add(responseMap);
1828 responseMap = new HashMap<>();
1829 }
1830
1831 // exclude those that are unknown
1832 if (valTagByte >= AttributeClass.TAG_INT) {
1833 AttributeClass ac =
1834 new AttributeClass(attribStr,
1835 valTagByte,
1836 outArray);
1837
1838 responseMap.put(ac.getName(), ac);
1839 debug_println(debugPrefix+ "readIPPResponse "+ac);
1840 }
1841
1842 outObj = new ByteArrayOutputStream();
1843 counter = 0; //reset counter
1844 }
1845 //check if this is new value tag
1846 if (counter == 0) {
1847 valTagByte = response[0];
1848 }
1864 if (len > MAX_ATTRIBUTE_LENGTH) {
1865 response = new byte[len]; // expand as needed
1866 }
1867 ois.read(response, 0, len);
1868 // write value of "len" length
1869 outObj.write(response, 0, len);
1870 counter++;
1871 // read next byte
1872 response[0] = ois.readByte();
1873 }
1874
1875 if (attribStr != null) {
1876 outObj.write(counter);
1877 outObj.flush();
1878 outObj.close();
1879
1880 // if key exists in old HashMap, new HashMap
1881 if ((counter != 0) &&
1882 responseMap.containsKey(attribStr)) {
1883 respList.add(responseMap);
1884 responseMap = new HashMap<>();
1885 }
1886
1887 byte outArray[] = outObj.toByteArray();
1888
1889 AttributeClass ac =
1890 new AttributeClass(attribStr,
1891 valTagByte,
1892 outArray);
1893 responseMap.put(ac.getName(), ac);
1894 }
1895 }
1896 ois.close();
1897 if ((responseMap != null) && (responseMap.size() > 0)) {
1898 respList.add(responseMap);
1899 }
1900 @SuppressWarnings({"unchecked", "rawtypes"})
1901 HashMap<String, AttributeClass>[] tmp =
1902 respList.toArray((HashMap<String, AttributeClass>[])new HashMap[respList.size()]);
1903 return tmp;
1904 } else {
1905 debug_println(debugPrefix+
1906 "readIPPResponse client error, IPP status code: 0x"+
1907 toHex(response[2]) + toHex(response[3]));
1908 return null;
1909 }
1910
1911 } catch (java.io.IOException e) {
1912 debug_println(debugPrefix+"readIPPResponse: "+e);
1913 if (debugPrint) {
1914 e.printStackTrace();
1915 }
1916 return null;
1917 }
1918 }
1919
1920 private static String toHex(byte v) {
1921 String s = Integer.toHexString(v&0xff);
1922 return (s.length() == 2) ? s : "0"+s;
1923 }
|