<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
/* @(#) $Header: /sources/phpprintipp/phpprintipp/php_classes/CupsPrintIPP.php,v 1.1 2008/06/21 00:30:56 harding Exp $
 *
 * Class PrintIPP - Send extended IPP requests.
 *
 *   Copyright (C) 2005-2006  Thomas HARDING
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *   mailto:thomas.harding@laposte.net
 *   Thomas Harding, 56 rue de la bourie rouge, 45 000 ORLEANS -- FRANCE
 *   
 */
    
/*

    This class is intended to implement Internet Printing Protocol on client side.

    References needed to debug / add functionnalities:
        - RFC 2910
        - RFC 2911
        - RFC 3382
        - ...
        - CUPS-IPP-1.1
*/

require_once("ExtendedPrintIPP.php");

class CupsPrintIPP extends ExtendedPrintIPP {

    // {{{ variables declaration
    
    public $printers_attributes;
    public $defaults_attributes;
    
    // }}}

    // {{{ constructor
    public function __construct() {
        parent::__construct();
        self::_initTags();
    }
    // }}}

//
// OPERATIONS
//

    // {{{ cupsGetDefaults ($attributes="all")
    public function cupsGetDefaults($attributes=array("all")) {
    //The CUPS-Get-Default operation returns the default printer URI and attributes
     
        $this->jobs = array_merge($this->jobs,array(""));
        $this->jobs_uri = array_merge($this->jobs_uri,array(""));
        $this->parsed = array();
        unset($this->printer_attributes);
        
        if (!isset($this->setup->charset))
            self::setCharset('us-ascii');
        
        if (!isset($this->setup->language))
            self::setLanguage('en');
            
        self::_setOperationId();
        
        for($i = 0 ; $i < count($attributes) ; $i++)
            if ($i == 0)
                $this->meta->attributes = chr(0x44) // Keyword
                                        . self::_giveMeStringLength('requested-attributes')
                                        . 'requested-attributes'
                                        . self::_giveMeStringLength($attributes[0])
                                        . $attributes[0];
            else
                $this->meta->attributes .= chr(0x44) // Keyword
                                        .  chr(0x0).chr(0x0) // zero-length name
                                        .  self::_giveMeStringLength($attributes[$i])
                                        .  $attributes[$i];
                                        
        $this->stringjob = chr(0x01) . chr(0x01) // IPP version 1.1 
                         . chr(0x40). chr(0x01) // operation:  cups vendor extension: get defaults
                         . $this->meta->operation_id // request-id
                         . chr(0x01) // start operation-attributes | operation-attributes-tag
                         . $this->meta->charset
                         . $this->meta->language
                         . $this->meta->attributes
                         . chr(0x03); // end operations attribute
                        
        $this->output = $this->stringjob;
        
        self::_putDebug("Request: ".$this->output);
        
        $post_values = array( "Content-Type" => "application/ipp",
                              "Data" => $this->output);
            
        if (self::_sendHttp ($post_values,'/')) {
            
            if(self::_parseServerOutput())
                self::_parsePrinterAttributes();
            }
        
       $this->attributes = &$this->printer_attributes;
        
       if (isset($this->printer_attributes->printer_type)) {
                    $printer_type = $this->printer_attributes->printer_type->_value0;
                    $table = self::_interpretPrinterType($printer_type);
        
                    for($i = 0 ; $i < count($table) ; $i++ ) {
                        $index = '_value'.$i;
                        $this->printer_attributes->printer_type->$index = $table[$i];
                        }

                    }

        if (isset($this->serveroutput) && isset($this->serveroutput->status)) {
            
            $this->status = array_merge($this->status,array($this->serveroutput->status));
            if ($this->serveroutput->status == "successfull-ok")
                self::_errorLog("getting defaults: ".$this->serveroutput->status,3);
            else
                self::_errorLog("getting defaults: ".$this->serveroutput->status,1);
            
            return $this->serveroutput->status; 
            
        } else {
            $this->status = array_merge($this->status,array("OPERATION FAILED"));
            self::_errorLog("getting defaults : OPERATION FAILED",1);
            }
    return false;   
    }
    // }}}
    
    // {{{ cupsAcceptJobs ($printer_uri)
    public function cupsAcceptJobs($printer_uri) {
    //The CUPS-Get-Default operation returns the default printer URI and attributes
     
        $this->jobs = array_merge($this->jobs,array(""));
        $this->jobs_uri = array_merge($this->jobs_uri,array(""));
        $this->parsed = array();
        unset($this->printer_attributes);
        
        if (!isset($this->setup->charset))
            self::setCharset('us-ascii');
        
        if (!isset($this->setup->language))
            self::setLanguage('en');
            
        self::_setOperationId();
       
       $this->stringjob = chr(0x01) . chr(0x01) // IPP version 1.1 
                         . chr(0x40). chr(0x08) // operation:  cups vendor extension: Accept-Jobs
                         . $this->meta->operation_id // request-id
                         . chr(0x01) // start operation-attributes | operation-attributes-tag
                         . $this->meta->charset
                         . $this->meta->language
                         . chr(0x45) // uri
                         . self::_giveMeStringLength('printer-uri')
                         . 'printer-uri'
                         . self::_giveMeStringLength($printer_uri)
                         . $printer_uri
                         . chr(0x03); // end operations attribute
                        
        $this->output = $this->stringjob;
        
        self::_putDebug("Request: ".$this->output);
        
        $post_values = array( "Content-Type" => "application/ipp",
                              "Data" => $this->output);
            
        if (self::_sendHttp ($post_values,'/admin/')) {
            
            if(self::_parseServerOutput())
                self::_parseAttributes();
            }
                
        if (isset($this->serveroutput) && isset($this->serveroutput->status)) {
            
            $this->status = array_merge($this->status,array($this->serveroutput->status));
            if ($this->serveroutput->status == "successfull-ok")
                self::_errorLog("getting defaults: ".$this->serveroutput->status,3);
            else
                self::_errorLog("getting defaults: ".$this->serveroutput->status,1);
            
            return $this->serveroutput->status; 
            
        } else {
            $this->status = array_merge($this->status,array("OPERATION FAILED"));
            self::_errorLog("getting defaults : OPERATION FAILED",1);
            }
    return false;   
    }
    // }}}

    // {{{ cupsRejectJobs ($printer_uri,$printer_state_message=false)
    public function cupsRejectJobs($printer_uri,$printer_state_message) {
    //The CUPS-Get-Default operation returns the default printer URI and attributes
     
        $this->jobs = array_merge($this->jobs,array(""));
        $this->jobs_uri = array_merge($this->jobs_uri,array(""));
        $this->parsed = array();
        unset($this->attributes);
        
        if (!isset($this->setup->charset))
            self::setCharset('us-ascii');
        
        if (!isset($this->setup->language))
            self::setLanguage('en');
            
        self::_setOperationId();
        
        $message = "";
        if ($printer_state_message)
            $message = chr(0x04) // start printer-attributes
                     . chr(0x41) // textWithoutLanguage
                     . self::_giveMeStringLength("printer-state-message")
                     . "printer-state-message"
                     . self::_giveMeStringLength($printer_state_message)
                     . $printer_state_message;

       $this->stringjob = chr(0x01) . chr(0x01) // IPP version 1.1 
                         . chr(0x40). chr(0x09) // operation:  cups vendor extension: Reject-Jobs
                         . $this->meta->operation_id // request-id
                         . chr(0x01) // start operation-attributes | operation-attributes-tag
                         . $this->meta->charset
                         . $this->meta->language
                         . chr(0x45) // uri
                         . self::_giveMeStringLength('printer-uri')
                         . 'printer-uri'
                         . self::_giveMeStringLength($printer_uri)
                         . $printer_uri
                         . $message
                         . chr(0x03); // end operations attribute
                        
        $this->output = $this->stringjob;
        
        self::_putDebug("Request: ".$this->output);
        
        $post_values = array( "Content-Type" => "application/ipp",
                              "Data" => $this->output);
            
        if (self::_sendHttp ($post_values,'/admin/')) {
            
            if(self::_parseServerOutput())
                self::_parseAttributes();
            }
                
        if (isset($this->serveroutput) && isset($this->serveroutput->status)) {
            
            $this->status = array_merge($this->status,array($this->serveroutput->status));
            if ($this->serveroutput->status == "successfull-ok")
                self::_errorLog("getting defaults: ".$this->serveroutput->status,3);
            else
                self::_errorLog("getting defaults: ".$this->serveroutput->status,1);
            
            return $this->serveroutput->status; 
            
        } else {
            $this->status = array_merge($this->status,array("OPERATION FAILED"));
            self::_errorLog("getting defaults : OPERATION FAILED",1);
            }
    return false;   
    }
    // }}}

    // {{{ getPrinters()
    public function getPrinters($printer_location=false,$printer_info=false,$attributes=array()) {
        
        if (count($attributes) == 0)
        true;
            $attributes=array('printer-uri-supported','printer-location','printer-info','printer-type','color-supported');
        $this->jobs = array_merge($this->jobs,array(""));
        $this->jobs_uri = array_merge($this->jobs_uri,array(""));
        
        unset ($this->printers_attributes);
        
        if (!isset($this->setup->charset))
            self::setCharset('us-ascii');
        
        if (!isset($this->setup->language))
            self::setLanguage('en-us');
            
        self::_setOperationId();
        
        $this->meta->attributes='';
        
        if ($printer_location)
            $this->meta->attributes .= chr(0x41) // textWithoutLanguage
                                    . self::_giveMeStringLength('printer-location')
                                    . 'printer-location'
                                    . self::_giveMeStringLength($printer_location)
                                    . $printer_location;
        
        
        if ($printer_info)
            $this->meta->attributes .= chr(0x41) // textWithoutLanguage
                                    . self::_giveMeStringLength('printer-info')
                                    . 'printer-info'
                                    . self::_giveMeStringLength($printer_info)
                                    . $printer_info;
        
        for($i = 0 ; $i < count($attributes) ; $i++)
            if ($i == 0)
                $this->meta->attributes .= chr(0x44) // Keyword
                                        . self::_giveMeStringLength('requested-attributes')
                                        . 'requested-attributes'
                                        . self::_giveMeStringLength($attributes[0])
                                        . $attributes[0];
            else
                $this->meta->attributes .= chr(0x44) // Keyword
                                        .  chr(0x0).chr(0x0) // zero-length name
                                        .  self::_giveMeStringLength($attributes[$i])
                                        .  $attributes[$i];

        
        $this->stringjob = chr(0x01) . chr(0x01) // IPP version 1.1 
                         . chr(0x40). chr(0x02) // operation:  cups vendor extension: get printers
                         . $this->meta->operation_id //           request-id
                         . chr(0x01) // start operation-attributes | operation-attributes-tag
                         . $this->meta->charset
                         . $this->meta->language
                         . $this->meta->attributes
                         . chr(0x03); // end operations attribute
                        
        $this->output = $this->stringjob;
           
        $post_values = array( "Content-Type" => "application/ipp",
                              "Data" => $this->output);
            
        if (self::_sendHttp ($post_values,'/')) {
            
            if(self::_parseServerOutput())
                $this->_getAvailablePrinters();

            }
        
        if (isset($this->serveroutput) && isset($this->serveroutput->status)) {
            
            $this->status = array_merge($this->status,array($this->serveroutput->status));
            if ($this->serveroutput->status == "successfull-ok")
                self::_errorLog("getting printers: ".$this->serveroutput->status,3);
            else
                self::_errorLog("getting printers: ".$this->serveroutput->status,1);
            return $this->serveroutput->status; 
            
        } else {
            $this->status = array_merge($this->status,array("OPERATION FAILED"));
            self::_errorLog("getting printers : OPERATION FAILED",1);
            }
    return false;   
    }
    // }}}

    // {{{ cupsGetPrinters ()
    public function cupsGetPrinters () {
    // alias for getPrinters();
        self::getPrinters();
    }
    // }}}

    // {{{ getPrinterAttributes()
    public function getPrinterAttributes() {
    // complete informations from parent with Cups-specific stuff
    
        if(!$result = parent::getPrinterAttributes())
            return FALSE;
        if(!isset($this->printer_attributes))
            return FALSE;
        
        if (isset ($this->printer_attributes->printer_type)) {
            $printer_type = $this->printer_attributes->printer_type->_value0;
            $table = self::_interpretPrinterType($printer_type);
        
            for($i = 0 ; $i < count($table) ; $i++ ) {
                $index = '_value'.$i;
                $this->printer_attributes->printer_type->$index = $table[$i];
                }
            }

    return $result;
    }
    // }}}

//
// SETUP
//

    // {{{ _initTags ()
    protected function _initTags () {
        
        // override parent with specific cups attributes
        
        $operation_tags = array (); 
        $this->operation_tags = array_merge ($this->operation_tags, $operation_tags);
               
        $job_tags = array ( "job-billing" => array("tag" => "textWithoutLanguage"),
                            "blackplot" => array("tag" => "boolean"),
                            "brightness" => array("tag" => "integer"),
                            "columns" => array("tag" => "integer"),
                            "cpi" => array("tag" => "enum"),
                            "fitplot" => array("tag" => "boolean"),
                            "gamma" => array("tag" => "integer"),
                            "hue" => array("tag" => "integer"),
                            "lpi" => array("tag" => "enum"),
                            "mirror" => array("tag","boolean"),
                            "natural-scaling" => array("tag" => "integer"),
                            "number-up-layout" => array("tag" => "keyword"),
                            "page-border" => array("tag" => "keyword"),
                            "page-bottom" => array("tag" => "integer"),
                            "page-label" => array("tag" => "textWithoutLanguage"),
                            "page-left" => array("tag" => "integer"),
                            "page-right" => array("tag" => "integer"),
                            "page-set" => array("tag" => "keyword"),
                            "page-top" => array("tag" => "integer"),
                            "penwidth" => array("tag" => "integer"),
                            "position" => array("tag" => "keyword"),
                            "ppi" => array("tag" => "integer"),
                            "prettyprint" => array("tag","boolean"),
                            "saturation" => array("tag" => "integer"),
                            "scaling" => array("tag" => "integer"),
                            "wrap" => array("tag","boolean"),
                            
                            );
        $this->job_tags = array_merge ($this->job_tags, $job_tags);
    }
    // }}}

//
// REQUEST BUILDING
//

    // {{{ _enumBuild ($tag,$value)
    protected function _enumBuild ($tag,$value) {
        
        $value_built = parent::_enumBuild($tag,$value);
       

        switch ($tag) {
           case "cpi":     
                switch ($value) {
                    case '10':
                        $value_built = chr(10);
                        break;
                    case '12':
                        $value_built = chr(12);
                        break;
                    case '17':
                        $value_built = chr(17);
                        break;
                    default:
                        $value_built = chr(10);
                }
            break;
            case "lpi":
                switch ($value) {
                    case '6':
                        $value_built = chr(6);
                        break;
                    case '8':
                        $value_built = chr(8);
                        break;
                    default:
                        $value_built = chr(6);
                }
            break;
            }

        $prepend = '';
        while ((strlen($value_built) + strlen($prepend)) < 4)
            $prepend .= chr(0);
    return $prepend.$value_built;
    }
    // }}}
    
//
// RESPONSE PARSING
//

    // {{{ _getAvailablePrinters ()
    private function _getAvailablePrinters () {
      
        $this->available_printers = array();
        $k = 0;
        $this->printers_attributes = new stdClass();

        for ($i = 0 ; (array_key_exists($i,$this->serveroutput->response)) ; $i ++)
            if (($this->serveroutput->response[$i]['attributes']) == "printer-attributes") {
                $phpname = "_printer".$k;
                $this->printers_attributes->$phpname = new stdClass();
                for ($j = 0 ; array_key_exists($j,$this->serveroutput->response[$i]) ; $j++) {
                
                    $value = $this->serveroutput->response[$i][$j]['value'];
                    $name = str_replace("-","_",$this->serveroutput->response[$i][$j]['name']);
                    
                    switch ($name) {
                        case "printer_uri_supported":
                            $this->available_printers = array_merge($this->available_printers,array($value));
                            break;
                        case "printer_type":
                            $table = self::_interpretPrinterType($value);
                            $this->printers_attributes->$phpname->$name = new stdClass();
        
                            for($l = 0 ; $l < count($table) ; $l++ ) {
                                $index = '_value'.$l;
                                $this->printers_attributes->$phpname->$name->$index = $table[$l];
                            }
                            
                            break;
                        case '':
                            break;
                        default:
                            $this->printers_attributes->$phpname->$name = $value;
                            break;
                        }

                    }    
                $k ++;
                }
    }
    // }}}
    
    // {{{ _getEnumVendorExtensions
    protected function _getEnumVendorExtensions ($value_parsed) {
        switch ($value_parsed) {
            case 0x4002:
                $value = 'Get-Availables-Printers';
                break;
            default:
                $value = sprintf('Unknown(Cups extension for operations): 0x%x',$value_parsed);
                break;
        }

    if (isset($value))
        return ($value);
  
    return sprintf('Unknown: 0x%x',$value_parsed);
    }
    // }}}

    // {{{ _interpretPrinterType($type)
    private function _interpretPrinterType($value) {
        $value_parsed = 0;
        for ($i = strlen($value) ; $i > 0 ; $i --)
            $value_parsed += pow(256,($i - 1)) * ord($value[strlen($value) - $i]);
       
        $type[0] = $type[1] = $type[2] = $type[3] = $type[4] = $type[5] = '';
        $type[6] = $type[7] = $type[8] = $type[9] = $type[10] = '';
        $type[11] = $type[12] = $type[13] = $type[14] = $type[15] = '';
        $type[16] = $type[17] = $type[18] = $type[19] = '';
        
        if ($value_parsed %2 == 1) {
            $type[0] = 'printer-class';
            $value_parsed -= 1;
            }
        if ($value_parsed %4 == 2 )  {
            $type[1] = 'remote-destination';
            $value_parsed -= 2;
            }
        if ($value_parsed %8 == 4 )  {
            $type[2] = 'print-black';
            $value_parsed -= 4;
            }
        if ($value_parsed %16 == 8 )  {
            $type[3] = 'print-color';
            $value_parsed -= 8;
            }
        if ($value_parsed %32 == 16) {
            $type[4] = 'hardware-print-on-both-sides';
            $value_parsed -= 16;
            }
        if ($value_parsed %64 == 32) {
            $type[5] = 'hardware-staple-output';
            $value_parsed -= 32;
            }
        if ($value_parsed %128 == 64) {
            $type[6] = 'hardware-fast-copies';
            $value_parsed -= 64;
            }
        if ($value_parsed %256 == 128) {
            $type[7] = 'hardware-fast-copy-collation';
            $value_parsed -= 128;
            }
        if ($value_parsed %512 == 256) {
            $type[8] = 'punch-output';
            $value_parsed -= 256;
            }
        if ($value_parsed %1024 == 512) {
            $type[9] = 'cover-output';
            $value_parsed -= 512;
            }
        if ($value_parsed %2048 == 1024) {
            $type[10] = 'bind-output';
            $value_parsed -= 1024;
            }
        if ($value_parsed %4096 == 2048) {
            $type[11] = 'sort-output';
            $value_parsed -= 2048;
            }
        if ($value_parsed %8192 == 4096) {
            $type[12] = 'handle-media-up-to-US-Legal-A4';
            $value_parsed -= 4096;
            }
        if ($value_parsed %16384 == 8192) {
            $type[13] = 'handle-media-between-US-Legal-A4-and-ISO_C-A2';
            $value_parsed -= 8192;
            }
        if ($value_parsed %32768 == 16384) {
            $type[14] = 'handle-media-larger-than-ISO_C-A2';
            $value_parsed -= 16384;
            }
        if ($value_parsed %65536 == 32768) {
            $type[15] = 'handle-user-defined-media-sizes';
            $value_parsed -= 32768;
            }
        if ($value_parsed %131072 == 65536) {
            $type[16] = 'implicit-server-generated-class';
            $value_parsed -= 65536;
            }
        if ($value_parsed %262144 == 131072) {
            $type[17] = 'network-default-printer';
            $value_parsed -= 131072;
            }
        if ($value_parsed %524288 == 262144) {
            $type[18] = 'fax-device';
            $value_parsed -= 262144;
            }
    return $type;   
    }
    // }}}

    // {{{ _interpretEnum()
    protected function _interpretEnum($attribute_name,$value) {
       
        $value_parsed = self::_interpretInteger($value);
        
        switch ($attribute_name) {
            case 'cpi':
            case 'lpi':
                $value = $value_parsed;
                break;
            default:
                $value = parent::_interpretEnum($attribute_name,$value);
                break;
            }
        
    
    return $value;
    }
    // }}}
    
};

/*
 * Local variables:
 * mode: php
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 */
?>
