src/solaris/classes/sun/print/IPPPrintService.java

Print this page




  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     }