1: 
  37: 
  38: 
  39: package ;
  40: 
  41: import ;
  42: import ;
  43: import ;
  44: import ;
  45: import ;
  46: import ;
  47: import ;
  48: import ;
  49: import ;
  50: import ;
  51: 
  52: import ;
  53: import ;
  54: import ;
  55: import ;
  56: import ;
  57: import ;
  58: import ;
  59: import ;
  60: import ;
  61: import ;
  62: import ;
  63: import ;
  64: 
  65: import ;
  66: import ;
  67: import ;
  68: import ;
  69: import ;
  70: import ;
  71: import ;
  72: import ;
  73: import ;
  74: import ;
  75: import ;
  76: import ;
  77: import ;
  78: import ;
  79: import ;
  80: import ;
  81: import ;
  82: import ;
  83: import ;
  84: import ;
  85: import ;
  86: import ;
  87: import ;
  88: import ;
  89: import ;
  90: import ;
  91: import ;
  92: import ;
  93: import ;
  94: import ;
  95: import ;
  96: import ;
  97: import ;
  98: import ;
  99: 
 100: 
 118: public class IppRequest
 119: {
 120: 
 121:   
 124:   private static final int timeout = 1000;
 125: 
 126:   
 132:   class RequestWriter
 133:   {
 134:     private DataOutputStream out;
 135: 
 136:     
 141:     RequestWriter(DataOutputStream stream)
 142:     {
 143:       out = stream;
 144:     }
 145: 
 146:     
 151:     private void write(IntegerSyntax attribute) throws IOException
 152:     {
 153:       String name = ((Attribute) attribute).getName();
 154:       out.writeByte(IppValueTag.INTEGER);
 155:       out.writeShort(name.length());
 156:       out.write(name.getBytes());
 157:       out.writeShort(4); 
 158:       out.writeInt(attribute.getValue());
 159:     }
 160: 
 161:     
 166:     private void write(EnumSyntax attribute) throws IOException
 167:     {
 168:       
 169:       String name = ((Attribute) attribute).getName();
 170: 
 171:       
 172:       if (attribute instanceof Finishings
 173:           || attribute instanceof OrientationRequested
 174:           || attribute instanceof PrintQuality)
 175:         {
 176:           out.writeByte(IppValueTag.ENUM);
 177:           out.writeShort(name.length());
 178:           out.write(name.getBytes());
 179:           out.writeShort(4); 
 180:           out.writeInt(attribute.getValue());
 181:         }
 182:       
 183:       else if (attribute instanceof Fidelity)
 184:         {
 185:           out.writeByte(IppValueTag.BOOLEAN);
 186:           out.writeShort(name.length());
 187:           out.write(name.getBytes());
 188:           out.writeShort(1); 
 189:           out.writeByte(attribute.getValue() == 0 ? 0x00 : 0x01);
 190:         }
 191:       
 192:       else
 193:         {
 194:           String keyword = attribute.toString();
 195:           out.writeByte(IppValueTag.KEYWORD);
 196:           out.writeShort(name.length());
 197:           out.write(name.getBytes());
 198:           out.writeShort(keyword.length());
 199:           out.write(keyword.getBytes());
 200:         }
 201:     }
 202: 
 203:     
 208:     private void write(SetOfIntegerSyntax attribute) throws IOException
 209:     {
 210:       String name = ((Attribute) attribute).getName();
 211:       int[][] ranges = attribute.getMembers();
 212:       for (int i = 0; i < ranges.length; i++)
 213:         {
 214:           out.writeByte(IppValueTag.RANGEOFINTEGER);
 215:           if (i == 0)
 216:             {
 217:               out.writeShort(name.length());
 218:               out.write(name.getBytes());
 219:             }
 220:           else
 221:             out.writeShort(0x0000); 
 222: 
 223:           out.writeShort(8); 
 224:           out.writeInt(ranges[i][0]);
 225:           out.writeInt(ranges[i][1]);
 226:         }
 227:     }
 228: 
 229:     
 234:     private void write(ResolutionSyntax attribute) throws IOException
 235:     {
 236:       String name = ((Attribute) attribute).getName();
 237:       out.writeByte(IppValueTag.RESOLUTION);
 238:       out.writeShort(name.length());
 239:       out.write(name.getBytes());
 240:       out.writeShort(9); 
 241:       out.writeInt(attribute.getCrossFeedResolution(ResolutionSyntax.DPI));
 242:       out.writeInt(attribute.getFeedResolution(ResolutionSyntax.DPI));
 243:       out.writeByte(ResolutionSyntax.DPI);
 244:     }
 245: 
 246:     
 256:     private void write(DateTimeSyntax attribute) throws IOException
 257:     {
 258:       String name = ((Attribute) attribute).getName();
 259:       out.writeByte(IppValueTag.DATETIME);
 260:       out.writeShort(name.length());
 261:       out.write(name.getBytes());
 262:       out.writeShort(11); 
 263: 
 264:       Date date = attribute.getValue();
 265:       Calendar cal = new GregorianCalendar();
 266:       cal.setTime(date);
 267: 
 268:       out.writeShort(cal.get(Calendar.YEAR));
 269:       out.writeByte(cal.get(Calendar.MONTH));
 270:       out.writeByte(cal.get(Calendar.DAY_OF_MONTH));
 271:       out.writeByte(cal.get(Calendar.HOUR_OF_DAY));
 272:       out.writeByte(cal.get(Calendar.MINUTE));
 273:       int second = cal.get(Calendar.SECOND);
 274:       out.writeByte(second == 0 ? 60 : second);
 275:       out.writeByte(cal.get(Calendar.MILLISECOND) / 100);
 276: 
 277:       int offsetInMillis = cal.get(Calendar.ZONE_OFFSET);
 278:       char directionFromUTC = '+';
 279:       if (offsetInMillis < 0)
 280:         {
 281:           directionFromUTC = '-';
 282:           offsetInMillis = offsetInMillis * (-1);
 283:         }
 284: 
 285:       out.writeByte(directionFromUTC);
 286:       out.writeByte(offsetInMillis / 3600000); 
 287:       out.writeByte((offsetInMillis % 3600000) / 60000); 
 288:     }
 289: 
 290:     
 303:     private void write(TextSyntax attribute) throws IOException
 304:     {
 305:       
 306:       String name = ((Attribute) attribute).getName();
 307: 
 308:       if (attribute instanceof RequestingUserName
 309:           || attribute instanceof JobName
 310:           || attribute instanceof DocumentName
 311:           || attribute instanceof JobOriginatingUserName)
 312:         out.writeByte(IppValueTag.NAME_WITHOUT_LANGUAGE);
 313:       else if (attribute instanceof DocumentFormat)
 314:         out.writeByte(IppValueTag.MIME_MEDIA_TYPE);
 315:       else
 316:         out.writeByte(IppValueTag.TEXT_WITHOUT_LANGUAGE);
 317: 
 318:       out.writeShort(name.length());
 319:       out.write(name.getBytes());
 320:       out.writeShort(attribute.getValue().length());
 321:       out.write(attribute.getValue().getBytes());
 322:     }
 323: 
 324:     
 330:     private void write(URISyntax attribute) throws IOException
 331:     {
 332:       
 333:       
 334:       String name = ((Attribute) attribute).getName();
 335:       String uriAscii = attribute.getURI().toASCIIString();
 336:       out.writeByte(IppValueTag.URI);
 337:       out.writeShort(name.length());
 338:       out.write(name.getBytes());
 339:       out.writeShort(uriAscii.length());
 340:       out.write(uriAscii.getBytes());
 341:     }
 342: 
 343:     
 349:     private void write(CharsetSyntax attribute) throws IOException
 350:     {
 351:       String name = ((Attribute) attribute).getName();
 352:       out.writeByte(IppValueTag.CHARSET);
 353:       out.writeShort(name.length());
 354:       out.write(name.getBytes());
 355:       out.writeShort(attribute.getValue().length());
 356:       out.write(attribute.getValue().getBytes());
 357:     }
 358: 
 359:     
 365:     private void write(NaturalLanguageSyntax attribute) throws IOException
 366:     {
 367:       String name = ((Attribute) attribute).getName();
 368:       out.writeByte(IppValueTag.NATURAL_LANGUAGE);
 369:       out.writeShort(name.length());
 370:       out.write(name.getBytes());
 371:       out.writeShort(attribute.getValue().length());
 372:       out.write(attribute.getValue().getBytes());
 373:     }
 374: 
 375:     
 381:     private void write(RequestedAttributes attribute) throws IOException
 382:     {
 383:       String[] values = attribute.getValues();
 384: 
 385:       String name = ((Attribute) attribute).getName();
 386:       out.writeByte(IppValueTag.KEYWORD);
 387:       out.writeShort(name.length());
 388:       out.write(name.getBytes());
 389:       out.writeShort(values[0].length());
 390:       out.write(values[0].getBytes());
 391: 
 392:       for (int i=1; i < values.length; i++)
 393:         {
 394:           out.writeByte(IppValueTag.KEYWORD);
 395:           out.writeShort(0x0000); 
 396:           out.writeShort(values[i].length());
 397:           out.write(values[i].getBytes());
 398:         }
 399:     }
 400: 
 401: 
 402:     
 412:     public void writeOperationAttributes(AttributeSet attributes)
 413:         throws IOException, IppException
 414:     {
 415:       out.write(IppDelimiterTag.OPERATION_ATTRIBUTES_TAG);
 416: 
 417:       
 418:       Attribute att = attributes.get(AttributesCharset.class);
 419:       write((CharsetSyntax) att);
 420: 
 421:       logger.log(Component.IPP, "Attribute: Name: <"
 422:         + att.getCategory().getName() + "> Value: <" + att.toString() + ">");
 423: 
 424:       attributes.remove(AttributesCharset.class);
 425: 
 426:       att = attributes.get(AttributesNaturalLanguage.class);
 427:       write((NaturalLanguageSyntax) att);
 428:       attributes.remove(AttributesNaturalLanguage.class);
 429: 
 430:       logger.log(Component.IPP, "Attribute: Name: <"
 431:         + att.getCategory().getName() + "> Value: <" + att.toString() + ">");
 432: 
 433:       
 434:       PrinterURI printerUri = (PrinterURI) attributes.get(PrinterURI.class);
 435:       JobUri jobUri = (JobUri) attributes.get(JobUri.class);
 436:       JobId jobId = (JobId) attributes.get(JobId.class);
 437:       RequestedAttributes reqAttrs
 438:         = (RequestedAttributes)attributes.get(RequestedAttributes.class);
 439:       if (printerUri != null && jobId == null && jobUri == null)
 440:         {
 441:           write(printerUri);
 442:           attributes.remove(PrinterURI.class);
 443:           logger.log(Component.IPP, "Attribute: Name: <" + printerUri
 444:             .getCategory().getName() + "> Value: <" + printerUri.toString() + ">");
 445:         }
 446:       else if (jobUri != null && jobId == null && printerUri == null)
 447:         {
 448:           write(jobUri);
 449:           attributes.remove(JobUri.class);
 450:           logger.log(Component.IPP, "Attribute: Name: <" + jobUri
 451:             .getCategory().getName() + "> Value: <" + jobUri.toString() + ">");
 452:         }
 453:       else if (printerUri != null && jobId != null && jobUri == null)
 454:         {
 455:           write(printerUri); 
 456:           write(jobId);
 457:           attributes.remove(PrinterURI.class);
 458:           attributes.remove(JobId.class);
 459:           logger.log(Component.IPP, "Attribute: Name: <" + printerUri
 460:             .getCategory().getName() + "> Value: <" + printerUri.toString() + ">");
 461:           logger.log(Component.IPP, "Attribute: Name: <" + jobId.getCategory()
 462:             .getName() + "> Value: <" + jobId.toString() + ">");
 463:         }
 464:       else if (jobUri != null && jobId != null)
 465:         {
 466:           write(jobUri);
 467:           attributes.remove(JobUri.class);
 468:           attributes.remove(JobId.class); 
 469:           logger.log(Component.IPP, "Attribute: Name: <" + jobUri.getCategory()
 470:             .getName() + "> Value: <" + jobUri.toString() + ">");
 471:         }
 472:       else if (reqAttrs != null)
 473:         {
 474:           write(reqAttrs);
 475:           attributes.remove(RequestedAttributes.class);
 476:           logger.log(Component.IPP, "RequestedAttributes: <" + reqAttrs + ">");
 477:         }
 478:       else
 479:         {
 480:           throw new IppException("Unknown target operation attribute combination.");
 481:         }
 482: 
 483:       writeAttributes(attributes);
 484:     }
 485: 
 486:     
 496:     public void writeAttributes(AttributeSet attributes)
 497:         throws IOException, IppException
 498:     {
 499:       Attribute[] attributeArray = attributes.toArray();
 500:       for (int i = 0; i < attributeArray.length; i++)
 501:         {
 502:           logger.log(Component.IPP, "Attribute: Name: <" + attributeArray[i]
 503:             .getCategory().getName() + "> Value: <"
 504:             + attributeArray[i].toString() + ">");
 505: 
 506:           if (attributeArray[i] instanceof IntegerSyntax)
 507:             write((IntegerSyntax) attributeArray[i]);
 508:           else if (attributeArray[i] instanceof TextSyntax)
 509:             write((TextSyntax) attributeArray[i]);
 510:           else if (attributeArray[i] instanceof DateTimeSyntax)
 511:             write((DateTimeSyntax) attributeArray[i]);
 512:           else if (attributeArray[i] instanceof ResolutionSyntax)
 513:             write((ResolutionSyntax) attributeArray[i]);
 514:           else if (attributeArray[i] instanceof SetOfIntegerSyntax)
 515:             write((SetOfIntegerSyntax) attributeArray[i]);
 516:           else if (attributeArray[i] instanceof EnumSyntax)
 517:             write((EnumSyntax) attributeArray[i]);
 518:           else if (attributeArray[i] instanceof URISyntax)
 519:             write((URISyntax) attributeArray[i]);
 520:           else if (attributeArray[i] instanceof CharsetSyntax)
 521:             write((CharsetSyntax) attributeArray[i]);
 522:           else if (attributeArray[i] instanceof NaturalLanguageSyntax)
 523:             write((NaturalLanguageSyntax) attributeArray[i]);
 524:           else if (attributeArray[i] instanceof RequestedAttributes)
 525:             write((RequestedAttributes) attributeArray[i]);
 526:           else
 527:             throw new IppException("Unknown syntax type");
 528:         }
 529:     }
 530: 
 531:   }
 532: 
 533:   
 537:   static final Logger logger = SystemLogger.SYSTEM;
 538: 
 539:   
 543:   private static int requestIdCounter = 1;
 544: 
 545:   
 546:   private static final short VERSION = 0x0101;
 547: 
 548:   
 549:   private boolean alreadySent = false;
 550: 
 551:   
 552:   private short operation_id;
 553: 
 554:   
 558:   private final int request_id;
 559: 
 560:   private AttributeSet operationAttributes;
 561: 
 562:   private AttributeSet printerAttributes;
 563: 
 564:   private AttributeSet jobAttributes;
 565: 
 566:   private Object data;
 567: 
 568:   private URI requestUri;
 569: 
 570:   
 571:   private HttpURLConnection  connection;
 572: 
 573:   
 580:   public IppRequest(URI uri, String user, String password)
 581:   {
 582:     request_id = incrementRequestIdCounter();
 583:     requestUri = uri;
 584: 
 585:     try
 586:       {
 587:         URL url = new URL("http",
 588:                       user == null
 589:                       ? uri.getHost() : user + ":"
 590:                       + password + "@" + uri.getHost(),
 591:                       uri.getPort(), uri.getPath());
 592: 
 593:         connection = (HttpURLConnection) url.openConnection();
 594:         connection.setRequestMethod("POST");
 595:         connection.setDoOutput(true);
 596: 
 597:         connection.setRequestProperty("Content-type", "application/ipp");
 598:         connection.setRequestProperty("Accept", "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2");
 599:       }
 600:     catch (IOException e)
 601:       {
 602:         
 603:         
 604:         
 605:         
 606:         logger.log(Component.IPP, "Unexpected IOException", e);
 607:       }
 608: 
 609:     logger.log(Component.IPP, "[IppConnection] Host: " + uri.getHost()
 610:                               + " Port: " + uri.getPort() + " Path: "
 611:                               + uri.getPath());
 612:   }
 613: 
 614:   
 620:   private synchronized int incrementRequestIdCounter()
 621:   {
 622:     return IppRequest.requestIdCounter++;
 623:   }
 624: 
 625:   
 630:   public int getRequestID()
 631:   {
 632:     return request_id;
 633:   }
 634: 
 635:   
 642:   public void setData(InputStream stream)
 643:   {
 644:     data = stream;
 645:   }
 646: 
 647:   
 654:   public void setData(byte[] bytes)
 655:   {
 656:     data = bytes;
 657:   }
 658: 
 659:   
 664:   public void setOperationID(short id)
 665:   {
 666:     operation_id = id;
 667:   }
 668: 
 669:   
 674:   public void setOperationAttributeDefaults()
 675:   {
 676:     if (operationAttributes == null)
 677:       operationAttributes = new HashAttributeSet();
 678: 
 679:     operationAttributes.add(AttributesCharset.UTF8);
 680:     operationAttributes.add(AttributesNaturalLanguage.EN);
 681:   }
 682: 
 683:   
 689:   public void addJobAttribute(Attribute attribute)
 690:   {
 691:     if (jobAttributes == null)
 692:       jobAttributes = new HashAttributeSet();
 693: 
 694:     jobAttributes.add(attribute);
 695:   }
 696: 
 697:   
 703:   public void addPrinterAttributes(Attribute attribute)
 704:   {
 705:     if (printerAttributes == null)
 706:       printerAttributes = new HashAttributeSet();
 707: 
 708:     printerAttributes.add(attribute);
 709:   }
 710: 
 711:   
 716:   public void addOperationAttribute(Attribute attribute)
 717:   {
 718:     if (operationAttributes == null)
 719:       operationAttributes = new HashAttributeSet();
 720: 
 721:     operationAttributes.add(attribute);
 722:   }
 723: 
 724:   
 730:   public void addAndFilterJobOperationAttributes(AttributeSet set)
 731:   {
 732:     if (operationAttributes == null)
 733:       operationAttributes = new HashAttributeSet();
 734: 
 735:     
 736:     
 737:     Attribute[] tmp = set.toArray();
 738:     for (int i = 0; i < tmp.length; i++)
 739:       {
 740:         if (tmp[i].getCategory().equals(JobName.class)
 741:             || tmp[i].getCategory().equals(Fidelity.class)
 742:             || tmp[i].getCategory().equals(JobImpressions.class)
 743:             || tmp[i].getCategory().equals(JobKOctets.class)
 744:             || tmp[i].getCategory().equals(JobMediaSheets.class)
 745:             || tmp[i].getCategory().equals(Compression.class)
 746:             || tmp[i].getCategory().equals(DocumentName.class)
 747:             || tmp[i].getCategory().equals(RequestingUserName.class))
 748: 
 749:           operationAttributes.add(tmp[i]);
 750:       }
 751:   }
 752: 
 753:   
 759:   public void addAndFilterJobTemplateAttributes(AttributeSet set)
 760:   {
 761:     if (jobAttributes == null)
 762:       jobAttributes = new HashAttributeSet();
 763: 
 764:     
 765:     
 766:     Attribute[] tmp = set.toArray();
 767:     for (int i = 0; i < tmp.length; i++)
 768:       {
 769:         if (tmp[i].getCategory().equals(JobPriority.class)
 770:             || tmp[i].getCategory().equals(JobHoldUntil.class)
 771:             || tmp[i].getCategory().equals(JobSheets.class)
 772:             || tmp[i].getCategory().equals(MultipleDocumentHandling.class)
 773:             || tmp[i].getCategory().equals(Copies.class)
 774:             || tmp[i].getCategory().equals(Finishings.class)
 775:             || tmp[i].getCategory().equals(PageRanges.class)
 776:             || tmp[i].getCategory().equals(NumberUp.class)
 777:             || tmp[i].getCategory().equals(OrientationRequested.class)
 778:             || tmp[i].getCategory().equals(Media.class)
 779:             || tmp[i].getCategory().equals(PrinterResolution.class)
 780:             || tmp[i].getCategory().equals(PrintQuality.class)
 781:             || tmp[i].getCategory().equals(SheetCollate.class)
 782:             || tmp[i].getCategory().equals(Sides.class))
 783: 
 784:           jobAttributes.add(tmp[i]);
 785:       }
 786:   }
 787: 
 788:   
 798:   public IppResponse send() throws IppException, IOException
 799:   {
 800:     if (alreadySent)
 801:       throw new IllegalStateException("Request is already sent");
 802: 
 803:     alreadySent = true;
 804: 
 805:     OutputStream stream = connection.getOutputStream();
 806:     DataOutputStream out = new DataOutputStream(stream);
 807: 
 808:     
 809:     out.writeShort(VERSION);
 810:     out.writeShort(operation_id);
 811:     out.writeInt(request_id);
 812: 
 813:     logger.log(Component.IPP, "OperationID: " + Integer.toHexString(operation_id)
 814:       + " RequestID: " + request_id);
 815: 
 816:     
 817:     
 818:     logger.log(Component.IPP, "Operation Attributes");
 819: 
 820:     RequestWriter writer = new RequestWriter(out);
 821:     writer.writeOperationAttributes(operationAttributes);
 822: 
 823:     if (jobAttributes != null)
 824:       {
 825:         logger.log(Component.IPP, "Job Attributes");
 826:         out.write(IppDelimiterTag.JOB_ATTRIBUTES_TAG);
 827:         writer.writeAttributes(jobAttributes);
 828:       }
 829:     if (printerAttributes != null)
 830:       {
 831:         logger.log(Component.IPP, "Printer Attributes");
 832:         out.write(IppDelimiterTag.PRINTER_ATTRIBUTES_TAG);
 833:         writer.writeAttributes(printerAttributes);
 834:       }
 835: 
 836:     
 837:     out.write(IppDelimiterTag.END_OF_ATTRIBUTES_TAG);
 838: 
 839:     
 840:     if (data instanceof InputStream)
 841:       {
 842:         byte[] readbuf = new byte[2048];
 843:         int len = 0;
 844:         while( (len = ((InputStream) data).read(readbuf)) > 0)
 845:           out.write(readbuf, 0, len);
 846:       }
 847:     else if (data != null)
 848:       {
 849:         out.write((byte[]) data);
 850:       }
 851: 
 852:     out.flush();
 853:     stream.flush();
 854: 
 855:     
 856:     
 857:     
 858:     connection.setConnectTimeout( timeout );
 859: 
 860:     int responseCode = connection.getResponseCode();
 861: 
 862:     if (responseCode == HttpURLConnection.HTTP_OK)
 863:       {
 864:         IppResponse response = new IppResponse(requestUri, operation_id);
 865:         response.setResponseData(connection.getInputStream());
 866:         return response;
 867:       }
 868: 
 869:     logger.log(Component.IPP, "HTTP-Statuscode: " + responseCode);
 870: 
 871:     throw new IppException("Request failed got HTTP status code "
 872:                            + responseCode);
 873:   }
 874: 
 875: }