      1: <?php
      2:
      3: namespace Neodynamic\SDK\Web;
      4: use Exception;
      5: use ZipArchive;
      6:
      7: // Setting WebClientPrint
      8: WebClientPrint::$licenseOwner = '';
      9: WebClientPrint::$licenseKey = '';
     10:
     11: //IMPORTANT SETTINGS:
     12: //===================
     13: //Set ABSOLUTE URL to WebClientPrint.php file
     14: WebClientPrint::$webClientPrintAbsoluteUrl = Utils::getRoot().'/wcpdev/source/WebClientPrint.php';
     15: //Set wcpcache folder RELATIVE to WebClientPrint.php file
     16: //FILE WRITE permission on this folder is required!!!
     17: WebClientPrint::$wcpCacheFolder = 'wcpcache/';
     18: //===================
     19:
     20: // Clean built-in Cache
     21: // NOTE: Remove it if you implement your own cache system
     22: WebClientPrint::cacheClean(5); //in minutes
     23:
     24: // Process request
     25: $urlParts = parse_url($_SERVER['REQUEST_URI']);
     26: if (isset($urlParts['query'])){
     27:     if (Utils::strContains($urlParts['query'], WebClientPrint::WCP)){
     28:         WebClientPrint::processRequest($urlParts['query']);
     29:     }
     30: }
     31:
     32:
     33: /**
     34:  * WebClientPrint provides functions for registering the "WebClientPrint for PHP" solution
     35:  * script code in PHP web pages as well as for processing client requests and managing the
     36:  * internal cache.
     37:  *
     38:  * @author Neodynamic <http://neodynamic.com/support>
     39:  * @copyright (c) 2013, Neodynamic SRL
     40:  * @license http://neodynamic.com/eula Neodynamic EULA
     41:  */
     42: class WebClientPrint {
     43:
     44:     const VERSION = '2.0.0.0';
     45:     const CLIENT_PRINT_JOB = "clientPrint";
     46:     const WCP = 'WEB_CLIENT_PRINT';
     47:
     48:     const WCP_CACHE_WCPP_INSTALLED = 'WCPP_INSTALLED';
     49:     const WCP_CACHE_WCPP_VER = 'WCPP_VER';
     50:     const WCP_CACHE_PRINTERS = 'PRINTERS';
     51:
     52:     /**
     53:      * Gets or sets the License Owner
     54:      * @var string
     55:      */
     56:     static $licenseOwner = '';
     57:     /**
     58:      * Gets or sets the License Key
     59:      * @var string
     60:      */
     61:     static $licenseKey = '';
     62:     /**
     63:      * Gets or sets the ABSOLUTE URL to WebClientPrint.php file
     64:      * @var string
     65:      */
     66:     static $webClientPrintAbsoluteUrl = '';
     67:     /**
     68:      * Gets or sets the wcpcache folder URL RELATIVE to WebClientPrint.php file.
     69:      * FILE WRITE permission on this folder is required!!!
     70:      * @var string
     71:      */
     72:     static $wcpCacheFolder = '';
     73:
     74:     /**
     75:      * Adds a new entry to the built-in file system cache.
     76:      * @param string $sid The user's session id
     77:      * @param string $key The cache entry key
     78:      * @param string $val The data value to put in the cache
     79:      * @throws Exception
     80:      */
     81:     public static function cacheAdd($sid, $key, $val){
     82:         if (Utils::isNullOrEmptyString(self::$wcpCacheFolder)){
     83:             throw new Exception('WebClientPrint wcpCacheFolder is missing, please specify it.');
     84:         }
     85:         if (Utils::isNullOrEmptyString($sid)){
     86:             throw new Exception('WebClientPrint FileName cache is missing, please specify it.');
     87:         }
     88:         $cacheFileName = (Utils::strEndsWith(self::$wcpCacheFolder, '/')?self::$wcpCacheFolder:self::$wcpCacheFolder.'/').$sid.'.wcpcache';
     89:         $dataWCPP_VER = '';
     90:         $dataPRINTERS = '';
     91:
     92:         if(file_exists($cacheFileName)){
     93:             $cache_info = parse_ini_file($cacheFileName);
     94:
     95:             $dataWCPP_VER = $cache_info[self::WCP_CACHE_WCPP_VER];
     96:             $dataPRINTERS = $cache_info[self::WCP_CACHE_PRINTERS];
     97:         }
     98:
     99:         if ($key === self::WCP_CACHE_WCPP_VER){
    100:             $dataWCPP_VER = self::WCP_CACHE_WCPP_VER.'='.'"'.$val.'"';
    101:             $dataPRINTERS = self::WCP_CACHE_PRINTERS.'='.'"'.$dataPRINTERS.'"';
    102:         } else if ($key === self::WCP_CACHE_PRINTERS){
    103:             $dataWCPP_VER = self::WCP_CACHE_WCPP_VER.'='.'"'.$dataWCPP_VER.'"';
    104:             $dataPRINTERS = self::WCP_CACHE_PRINTERS.'='.'"'.$val.'"';
    105:         }
    106:
    107:         $data = $dataWCPP_VER.chr(13).chr(10).$dataPRINTERS;
    108:         $handle = fopen($cacheFileName, 'w') or die('Cannot open file:  '.$cacheFileName);
    109:         fwrite($handle, $data);
    110:         fclose($handle);
    111:
    112:     }
    113:
    114:     /**
    115:      * Gets a value from the built-in file system cache based on the specified sid & key
    116:      * @param string $sid The user's session id
    117:      * @param string $key The cache entry key
    118:      * @return string Returns the value from the cache for the specified sid & key if it's found; or an empty string otherwise.
    119:      * @throws Exception
    120:      */
    121:     public static function cacheGet($sid, $key){
    122:         if (Utils::isNullOrEmptyString(self::$wcpCacheFolder)){
    123:             throw new Exception('WebClientPrint wcpCacheFolder is missing, please specify it.');
    124:         }
    125:         if (Utils::isNullOrEmptyString($sid)){
    126:             throw new Exception('WebClientPrint FileName cache is missing, please specify it.');
    127:         }
    128:         $cacheFileName = (Utils::strEndsWith(self::$wcpCacheFolder, '/')?self::$wcpCacheFolder:self::$wcpCacheFolder.'/').$sid.'.wcpcache';
    129:         if(file_exists($cacheFileName)){
    130:             $cache_info = parse_ini_file($cacheFileName);
    131:
    132:             if($key===self::WCP_CACHE_WCPP_VER || $key===self::WCP_CACHE_WCPP_INSTALLED){
    133:                 return $cache_info[self::WCP_CACHE_WCPP_VER];
    134:             }else if($key===self::WCP_CACHE_PRINTERS){
    135:                 return $cache_info[self::WCP_CACHE_PRINTERS];
    136:             }else{
    137:                 return '';
    138:             }
    139:         }else{
    140:             return '';
    141:         }
    142:     }
    143:
    144:     /**
    145:      * Cleans the built-in file system cache
    146:      * @param type $minutes The number of minutes after any files on the cache will be removed.
    147:      */
    148:     public static function cacheClean($minutes){
    149:         if (!Utils::isNullOrEmptyString(self::$wcpCacheFolder)){
    150:             $cacheDir = (Utils::strEndsWith(self::$wcpCacheFolder, '/')?self::$wcpCacheFolder:self::$wcpCacheFolder.'/');
    151:             if ($handle = opendir($cacheDir)) {
    152:                  while (false !== ($file = readdir($handle))) {
    153:                     if ($file!='.' && $file!='..' && (time()-filectime($cacheDir.$file)) > (60*$minutes)) {
    154:                         unlink($cacheDir.$file);
    155:                     }
    156:                  }
    157:                  closedir($handle);
    158:             }
    159:         }
    160:     }
    161:
    162:     /**
    163:      * Returns script code for detecting whether WCPP is installed at the client machine.
    164:      *
    165:      * The WCPP-detection script code ends with a 'success' or 'failure' status.
    166:      * You can handle both situation by creating two javascript functions which names
    167:      * must be wcppDetectOnSuccess() and wcppDetectOnFailure().
    168:      * These two functions will be automatically invoked by the WCPP-detection script code.
    169:      *
    170:      * The WCPP-detection script uses a delay time variable which by default is 10000 ms (10 sec).
    171:      * You can change it by creating a javascript global variable which name must be wcppPingDelay_ms.
    172:      * For example, to use 5 sec instead of 10, you should add this to your script:
    173:      *
    174:      * var wcppPingDelay_ms = 5000;
    175:      *
    176:      * @return string A [script] tag linking to the WCPP-detection script code.
    177:      * @throws Exception
    178:      */
    179:     public static function createWcppDetectionScript(){
    180:
    181:         if (Utils::isNullOrEmptyString(self::$webClientPrintAbsoluteUrl)){
    182:             throw new Exception('WebClientPrint absolute URL is missing, please specify it.');
    183:         }
    184:
    185:         $buffer = '<script type="text/javascript">';
    186:         if(Utils::isIE6to9() || Utils::isIE10orGreater()){
    187:             $buffer .= 'var wcppPingNow=false;';
    188:         } else {
    189:             $buffer .= 'var wcppPingNow=true;';
    190:         }
    191:         $buffer .= '</script>';
    192:
    193:         $wcpHandler = self::$webClientPrintAbsoluteUrl.'?'.self::WCP.'&d='.session_id();
    194:         $buffer .= '<script src="'.$wcpHandler.'" type="text/javascript"></script>';
    195:
    196:         if(Utils::isIE6to9() || Utils::isIE10orGreater()){
    197:             $crlf = chr(13).chr(10);
    198:             $buffer .= $crlf.$crlf;
    199:             $buffer .= '<!--[if WCPP]>'.$crlf;
    200:             $buffer .= '<script type="text/javascript">'.$crlf;
    201:             $buffer .= '$(document).ready(function(){jsWCPP.ping();});'.$crlf;
    202:             $buffer .= '</script>'.$crlf;
    203:             $buffer .= '<![endif]-->'.$crlf;
    204:             $buffer .= '<!--[if !WCPP]>'.$crlf;
    205:             $buffer .= '<script type="text/javascript">'.$crlf;
    206:             $buffer .= '$(document).ready(function(){wcppDetectOnFailure();});'.$crlf;
    207:             $buffer .= '</script>'.$crlf;
    208:             $buffer .= '<![endif]-->'.$crlf;
    209:          }
    210:
    211:          return $buffer;
    212:     }
    213:
    214:     /**
    215:      * Returns a string containing a HTML meta tag for the WCPP-detection procedure.
    216:      *
    217:      * The meta tag X-UA-Compatible is generated for Internet Explorer (IE) 10 or greater to emulate IE9.
    218:      * If this meta tag is not generated, then IE10 or greater will display some unwanted dialog box to
    219:      * the user when the WCPP-detection script is executed.
    220:      *
    221:      * @return string A string containing a HTML meta tag for the WCPP-detection procedure.
    222:      */
    223:     public static function getWcppDetectionMetaTag(){
    224:          return Utils::isIE10orGreater()?'<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9" />':'';
    225:     }
    226:
    227:     /**
    228:      * Returns a [script] tag linking to the WebClientPrint script code by using
    229:      * the specified URL for the client print job generation.
    230:      *
    231:      * @param string $clientPrintJobUrl The URL to the php file that creates ClientPrintJob objects.
    232:      * @return string A [script] tag linking to the WebClientPrint script code by using the specified URL for the client print job generation.
    233:      * @throws Exception
    234:      */
    235:     public static function createScript($clientPrintJobUrl){
    236:         if (Utils::isNullOrEmptyString(self::$webClientPrintAbsoluteUrl)){
    237:             throw new Exception('WebClientPrint absolute URL is missing, please specify it.');
    238:         }
    239:         $wcpHandler = self::$webClientPrintAbsoluteUrl.'?';
    240:         $wcpHandler .= self::WCP;
    241:         $wcpHandler .= '&';
    242:         $wcpHandler .= self::VERSION;
    243:         $wcpHandler .= '&';
    244:         $wcpHandler .= microtime(true);
    245:         $wcpHandler .= '&u=';
    246:         $wcpHandler .= base64_encode($clientPrintJobUrl);
    247:         return '<script src="'.$wcpHandler.'" type="text/javascript"></script>';
    248:     }
    249:
    250:     public static function processRequest($data){
    251:
    252:         $PING = 'wcppping';
    253:         $SID = 'sid';
    254:         $HAS_WCPP = 'wcppInstalled';
    255:
    256:         $GEN_WCP_SCRIPT_VERSION = 'v2.0.0.0';
    257:         $GEN_WCP_SCRIPT_URL = 'u';
    258:         $GEN_DETECT_WCPP_SCRIPT = 'd';
    259:         $WCP_SCRIPT_AXD_GET_PRINTERS = 'getPrinters';
    260:         $WCPP_SET_PRINTERS = 'printers';
    261:         $WCP_SCRIPT_AXD_GET_WCPPVERSION = 'getWcppVersion';
    262:         $WCPP_SET_VERSION = 'wcppVer';
    263:
    264:         $wcpUrl=Utils::getRoot().$_SERVER['REQUEST_URI'];
    265:         $wcpUrl=substr($wcpUrl, 0, strpos($wcpUrl, '.php?')+4);
    266:
    267:         parse_str($data, $qs);
    268:
    269:         if(isset($qs[$SID])){
    270:             if(isset($qs[$PING])){
    271:                 if (isset($qs[$WCPP_SET_VERSION])){
    272:                     self::cacheAdd($qs[$SID], self::WCP_CACHE_WCPP_VER, $qs[$WCPP_SET_VERSION]);
    273:                 }else{
    274:                     self::cacheAdd($qs[$SID], self::WCP_CACHE_WCPP_VER,'1.0.0.0');
    275:                 }
    276:             }else if(isset($qs[$WCPP_SET_PRINTERS])){
    277:                 self::cacheAdd($qs[$SID], self::WCP_CACHE_PRINTERS, strlen($qs[$WCPP_SET_PRINTERS]) > 0 ? base64_decode($qs[$WCPP_SET_PRINTERS]) : '');
    278:             }else if(strpos($data, $WCP_SCRIPT_AXD_GET_PRINTERS)>0){
    279:                 ob_clean();
    280:                 header('Content-type: text/plain');
    281:                 echo self::cacheGet($qs[$SID], self::WCP_CACHE_PRINTERS);
    282:             }else if(isset($qs[$WCPP_SET_VERSION])){
    283:                 if(strlen($qs[$WCPP_SET_VERSION]) > 0){
    284:                     self::cacheAdd($qs[$SID], self::WCP_CACHE_WCPP_VER, $qs[$WCPP_SET_VERSION]);
    285:                 }
    286:             }else if(strpos($data, $WCP_SCRIPT_AXD_GET_WCPPVERSION)>0){
    287:                 ob_clean();
    288:                 header('Content-type: text/plain');
    289:                 echo self::cacheGet($qs[$SID], self::WCP_CACHE_WCPP_VER);
    290:             }else{
    291:                 ob_clean();
    292:                 header('Content-type: text/plain');
    293:                 echo self::cacheGet($qs[$SID], self::WCP_CACHE_WCPP_INSTALLED);
    294:             }
    295:         }else if(isset($qs[$GEN_DETECT_WCPP_SCRIPT])){
    296:
    297:             $curSID = $qs[$GEN_DETECT_WCPP_SCRIPT];
    298:             $onSuccessScript = 'wcppDetectOnSuccess(data);';
    299:             $onFailureScript = 'wcppDetectOnFailure();';
    300:             $dynamicIframeId = 'i'.substr(uniqid(), 0, 3);
    301:             $absoluteWcpAxd = $wcpUrl.'?'.self::WCP.'&'.$SID.'='.$curSID;
    302:
    303:             $s1 = ' ';
    304:             $s2 = base64_decode($s1);
    305:             $s2 = str_replace('XXX-NEO-HTML-ID-XXX', $dynamicIframeId, $s2);
    306:             $s2 = str_replace('XXX-NEO-PING-URL-XXX', $absoluteWcpAxd.'&'.$PING, $s2);
    307:             $s2 = str_replace('XXX-NEO-USER-HAS-WCPP-XXX', $absoluteWcpAxd, $s2);
    308:             $s2 = str_replace('XXX-NEO-ON-SUCCESS-SCRIPT-XXX', $onSuccessScript, $s2);
    309:             $s2 = str_replace('XXX-NEO-ON-FAILURE-SCRIPT-XXX', $onFailureScript, $s2);
    310:
    311:             ob_clean();
    312:             header('Content-type: application/javascript');
    313:             echo $s2;
    314:
    315:         }else if(isset($qs[$GEN_WCP_SCRIPT_URL])){
    316:
    317:             $clientPrintJobUrl = base64_decode($qs[$GEN_WCP_SCRIPT_URL]);
    318:             if (strpos($clientPrintJobUrl, '?')>0){
    319:                 $clientPrintJobUrl .= '&';
    320:             }else{
    321:                 $clientPrintJobUrl .= '?';
    322:             }
    323:             $clientPrintJobUrl .= self::CLIENT_PRINT_JOB;
    324:             $absoluteWcpAxd = $wcpUrl;
    325:             $wcppGetPrintersParam = '-getPrinters:'.$absoluteWcpAxd.'?'.self::WCP.'&'.$SID.'=';
    326:             $wcpHandlerGetPrinters = $absoluteWcpAxd.'?'.self::WCP.'&'.$WCP_SCRIPT_AXD_GET_PRINTERS.'&'.$SID.'=';
    327:             $wcppGetWcppVerParam = '-getWcppVersion:'.$absoluteWcpAxd.'?'.self::WCP.'&'.$SID.'=';
    328:             $wcpHandlerGetWcppVer = $absoluteWcpAxd.'?'.self::WCP.'&'.$WCP_SCRIPT_AXD_GET_WCPPVERSION.'&'.$SID.'=';
    329:
    330:             $s1 = ' ';
    331:             $s2 = base64_decode($s1);
    332:             $s2 = str_replace('URL_PRINT_JOB', $clientPrintJobUrl, $s2);
    333:             $s2 = str_replace('URL_WCP_AXD_WITH_GET_PRINTERS_COMMAND', $wcppGetPrintersParam, $s2);
    334:             $s2 = str_replace('URL_WCP_AXD_GET_PRINTERS', $wcpHandlerGetPrinters, $s2);
    335:             $s2 = str_replace('URL_WCP_AXD_WITH_GET_WCPPVERSION_COMMAND', $wcppGetWcppVerParam, $s2);
    336:             $s2 = str_replace('URL_WCP_AXD_GET_WCPPVERSION', $wcpHandlerGetWcppVer, $s2);
    337:
    338:             ob_clean();
    339:             header('Content-type: application/javascript');
    340:             echo $s2;
    341:         }
    342:
    343:     }
    344:
    345: }
    346:
    347: /**
    348:  * The base class for all kind of printers supported at the client side.
    349:  */
    350: abstract class ClientPrinter{
    351:
    352:     public $printerId;
    353:     public function serialize(){
    354:
    355:     }
    356: }
    357:
    358: /**
    359:  * It represents the default printer installed in the client machine.
    360:  */
    361: class DefaultPrinter extends ClientPrinter{
    362:     public function __construct() {
    363:         $this->printerId = chr(0);
    364:     }
    365:
    366:     public function serialize() {
    367:         return $this->printerId;
    368:     }
    369: }
    370:
    371: /**
    372:  * It represents a printer installed in the client machine with an associated OS driver.
    373:  */
    374: class InstalledPrinter extends ClientPrinter{
    375:
    376:     /**
    377:      * Gets or sets the name of the printer installed in the client machine. Default value is an empty string.
    378:      * @var string
    379:      */
    380:     public $printerName = '';
    381:
    382:     /**
    383:      * Creates an instance of the InstalledPrinter class with the specified printer name.
    384:      * @param string $printerName The name of the printer installed in the client machine.
    385:      */
    386:     public function __construct($printerName) {
    387:         $this->printerId = chr(1);
    388:         $this->printerName = $printerName;
    389:     }
    390:
    391:
    392:     public function serialize() {
    393:
    394:         if (Utils::isNullOrEmptyString($this->printerName)){
    395:              throw new Exception("The specified printer name is null or empty.");
    396:         }
    397:
    398:         return $this->printerId.$this->printerName;
    399:     }
    400: }
    401:
    402: /**
    403:  * It represents a printer which is connected through a parallel port in the client machine.
    404:  */
    405: class ParallelPortPrinter extends ClientPrinter{
    406:
    407:     /**
    408:      * Gets or sets the parallel port name, for example LPT1. Default value is "LPT1"
    409:      * @var string
    410:      */
    411:     public $portName = "LPT1";
    412:
    413:     /**
    414:      * Creates an instance of the ParallelPortPrinter class with the specified port name.
    415:      * @param string $portName The parallel port name, for example LPT1.
    416:      */
    417:     public function __construct($portName) {
    418:         $this->printerId = chr(2);
    419:         $this->portName = $portName;
    420:     }
    421:
    422:     public function serialize() {
    423:
    424:         if (Utils::isNullOrEmptyString($this->portName)){
    425:              throw new Exception("The specified parallel port name is null or empty.");
    426:         }
    427:
    428:         return $this->printerId.$this->portName;
    429:     }
    430: }
    431:
    432: /**
    433:  * It represents a printer which is connected through a serial port in the client machine.
    434:  */
    435: class SerialPortPrinter extends ClientPrinter{
    436:
    437:     /**
    438:      * Gets or sets the serial port name, for example COM1. Default value is "COM1"
    439:      * @var string
    440:      */
    441:     public $portName = "COM1";
    442:     /**
    443:      * Gets or sets the serial port baud rate in bits per second. Default value is 9600
    444:      * @var integer
    445:      */
    446:     public $baudRate = 9600;
    447:     /**
    448:      * Gets or sets the serial port parity-checking protocol. Default value is NONE = 0
    449:      * NONE = 0, ODD = 1, EVEN = 2, MARK = 3, SPACE = 4
    450:      * @var integer
    451:      */
    452:     public $parity = SerialPortParity::NONE;
    453:     /**
    454:      * Gets or sets the serial port standard number of stopbits per byte. Default value is ONE = 1
    455:      * ONE = 1, TWO = 2, ONE_POINT_FIVE = 3
    456:      * @var integer
    457:      */
    458:     public $stopBits = SerialPortStopBits::ONE;
    459:     /**
    460:      * Gets or sets the serial port standard length of data bits per byte. Default value is 8
    461:      * @var integer
    462:      */
    463:     public $dataBits = 8;
    464:     /**
    465:      * Gets or sets the handshaking protocol for serial port transmission of data. Default value is XON_XOFF = 1
    466:      * NONE = 0, REQUEST_TO_SEND = 2, REQUEST_TO_SEND_XON_XOFF = 3, XON_XOFF = 1
    467:      * @var integer
    468:      */
    469:     public $flowControl = SerialPortHandshake::XON_XOFF;
    470:
    471:     /**
    472:      * Creates an instance of the SerialPortPrinter class wiht the specified information.
    473:      * @param string $portName The serial port name, for example COM1.
    474:      * @param integer $baudRate The serial port baud rate in bits per second.
    475:      * @param integer $parity The serial port parity-checking protocol.
    476:      * @param integer $stopBits The serial port standard number of stopbits per byte.
    477:      * @param integer $dataBits The serial port standard length of data bits per byte.
    478:      * @param integer $flowControl The handshaking protocol for serial port transmission of data.
    479:      */
    480:     public function __construct($portName, $baudRate, $parity, $stopBits, $dataBits, $flowControl) {
    481:         $this->printerId = chr(3);
    482:         $this->portName = $portName;
    483:         $this->baudRate = $baudRate;
    484:         $this->parity = $parity;
    485:         $this->stopBits = $stopBits;
    486:         $this->dataBits = $dataBits;
    487:         $this->flowControl = $flowControl;
    488:     }
    489:
    490:     public function serialize() {
    491:
    492:         if (Utils::isNullOrEmptyString($this->portName)){
    493:              throw new Exception("The specified serial port name is null or empty.");
    494:         }
    495:
    496:         return $this->printerId.$this->portName.Utils::SER_SEP.$this->baudRate.Utils::SER_SEP.$this->dataBits.Utils::SER_SEP.$this->flowControl.Utils::SER_SEP.$this->parity.Utils::SER_SEP.$this->stopBits;
    497:     }
    498: }
    499:
    500: /**
    501:  * It represents a Network IP/Ethernet printer which can be reached from the client machine.
    502:  */
    503: class NetworkPrinter extends ClientPrinter{
    504:
    505:     /**
    506:      * Gets or sets the DNS name assigned to the printer. Default is an empty string
    507:      * @var string
    508:      */
    509:     public $dnsName = "";
    510:     /**
    511:      * Gets or sets the Internet Protocol (IP) address assigned to the printer. Default value is an empty string
    512:      * @var string
    513:      */
    514:     public $ipAddress = "";
    515:     /**
    516:      * Gets or sets the port number assigned to the printer. Default value is 0
    517:      * @var integer
    518:      */
    519:     public $port = 0;
    520:
    521:     /**
    522:      * Creates an instance of the NetworkPrinter class with the specified DNS name or IP Address, and port number.
    523:      * @param string $dnsName The DNS name assigned to the printer.
    524:      * @param string $ipAddress The Internet Protocol (IP) address assigned to the printer.
    525:      * @param integer $port The port number assigned to the printer.
    526:      */
    527:     public function __construct($dnsName, $ipAddress, $port) {
    528:         $this->printerId = chr(4);
    529:         $this->dnsName = $dnsName;
    530:         $this->ipAddress = $ipAddress;
    531:         $this->port = $port;
    532:     }
    533:
    534:     public function serialize() {
    535:
    536:         if (Utils::isNullOrEmptyString($this->dnsName) && Utils::isNullOrEmptyString($this->ipAddress)){
    537:              throw new Exception("The specified network printer settings is not valid. You must specify the DNS Printer Name or its IP address.");
    538:         }
    539:
    540:         return $this->printerId.$this->dnsName.Utils::SER_SEP.$this->ipAddress.Utils::SER_SEP.$this->port;
    541:     }
    542: }
    543:
    544: /**
    545:  *  It represents a printer which will be selected by the user in the client machine. The user will be prompted with a print dialog.
    546:  */
    547: class UserSelectedPrinter extends ClientPrinter{
    548:     public function __construct() {
    549:         $this->printerId = chr(5);
    550:     }
    551:
    552:     public function serialize() {
    553:         return $this->printerId;
    554:     }
    555: }
    556:
    557: /**
    558:  * Specifies the parity bit for Serial Port settings.
    559:  */
    560: class SerialPortParity{
    561:     const NONE = 0;
    562:     const ODD = 1;
    563:     const EVEN = 2;
    564:     const MARK = 3;
    565:     const SPACE = 4;
    566: }
    567:
    568: /**
    569:  * Specifies the number of stop bits used for Serial Port settings.
    570:  */
    571: class SerialPortStopBits{
    572:     const NONE = 0;
    573:     const ONE = 1;
    574:     const TWO = 2;
    575:     const ONE_POINT_FIVE = 3;
    576: }
    577:
    578: /**
    579:  * Specifies the control protocol used in establishing a serial port communication.
    580:  */
    581: class SerialPortHandshake{
    582:     const NONE = 0;
    583:     const REQUEST_TO_SEND = 2;
    584:     const REQUEST_TO_SEND_XON_XOFF = 3;
    585:     const XON_XOFF = 1;
    586: }
    587:
    588: /**
    589:  * It represents a file in the server that will be printed at the client side.
    590:  */
    591: class PrintFile{
    592:
    593:     /**
    594:      * Gets or sets the path of the file at the server side that will be printed at the client side.
    595:      * @var string
    596:      */
    597:     public $filePath = '';
    598:     /**
    599:      * Gets or sets the file name that will be created at the client side.
    600:      * It must include the file extension like .pdf, .txt, .doc, .xls, etc.
    601:      * @var string
    602:      */
    603:     public $fileName = '';
    604:     /**
    605:      * Gets or sets the binary content of the file at the server side that will be printed at the client side.
    606:      * @var string
    607:      */
    608:     public $fileBinaryContent = '';
    609:
    610:     const PREFIX = 'wcpPF:';
    611:     const SEP = '|';
    612:
    613:     /**
    614:      *
    615:      * @param string $filePath The path of the file at the server side that will be printed at the client side.
    616:      * @param string $fileName The file name that will be created at the client side. It must include the file extension like .pdf, .txt, .doc, .xls, etc.
    617:      * @param string $fileBinaryContent The binary content of the file at the server side that will be printed at the client side.
    618:      */
    619:     public function __construct($filePath, $fileName, $fileBinaryContent) {
    620:         $this->filePath = $filePath;
    621:         $this->fileName = $fileName;
    622:         $this->fileBinaryContent = $fileBinaryContent;
    623:
    624:     }
    625:
    626:     public function serialize() {
    627:         return self::PREFIX.$this->fileName.self::SEP.$this->getFileContent();
    628:     }
    629:
    630:     public function getFileContent(){
    631:         $content = $this->fileBinaryContent;
    632:         if(!Utils::isNullOrEmptyString($this->filePath)){
    633:             $handle = fopen($this->filePath, 'rb');
    634:             $content = fread($handle, filesize($this->filePath));
    635:             fclose($handle);
    636:         }
    637:         return $content;
    638:     }
    639: }
    640:
    641: /**
    642:  * Some utility functions used by WebClientPrint for PHP solution.
    643:  */
    644: class Utils{
    645:     const SER_SEP = "|";
    646:
    647:     static function isNullOrEmptyString($s){
    648:         return (!isset($s) || trim($s)==='');
    649:     }
    650:
    651:     static function formatHexValues($s){
    652:
    653:         $buffer = '';
    654:
    655:         $l = strlen($s);
    656:         $i = 0;
    657:
    658:         while ($i < $l)
    659:         {
    660:             if ($s[$i] == '0')
    661:             {
    662:                 if ($i + 1 < $l && ($s[$i] == '0' && $s[$i + 1] == 'x'))
    663:                 {
    664:                     if ($i + 2 < $l &&
    665:                         (($s[$i + 2] >= '0' && $s[$i + 2] <= '9') || ($s[$i + 2] >= 'a' && $s[$i + 2] <= 'f') || ($s[$i + 2] >= 'A' && $s[$i + 2] <= 'F')))
    666:                     {
    667:                         if ($i + 3 < $l &&
    668:                            (($s[$i + 3] >= '0' && $s[$i + 3] <= '9') || ($s[$i + 3] >= 'a' && $s[$i + 3] <= 'f') || ($s[$i + 3] >= 'A' && $s[$i + 3] <= 'F')))
    669:                         {
    670:                             try{
    671:                                 $buffer .= chr(substr($s, $i, 4));
    672:                                 $i += 4;
    673:                                 continue;
    674:
    675:                             } catch (Exception $ex) {
    676:                                 throw new Exception("Invalid hex notation in the specified printer commands at index: ".$i);
    677:                             }
    678:
    679:
    680:                         }
    681:                         else
    682:                         {
    683:                             try{
    684:
    685:                                 $buffer .= chr(substr($s, $i, 3));
    686:                                 $i += 3;
    687:                                 continue;
    688:
    689:                             } catch (Exception $ex) {
    690:                                 throw new ArgumentException("Invalid hex notation in the specified printer commands at index: ".$i);
    691:                             }
    692:                         }
    693:                     }
    694:                 }
    695:             }
    696:
    697:             $buffer .= substr($s, $i, 1);
    698:
    699:             $i++;
    700:         }
    701:
    702:         return $buffer;
    703:
    704:     }
    705:
    706:     public static function intToArray($i){
    707:         return pack("L", $i);
    708:     }
    709:
    710:     public static function getRoot(){
    711:         $s = empty($_SERVER["HTTPS"]) ? '' : ($_SERVER["HTTPS"] == "on") ? "s" : "";
    712:         $protocol = static::strleft(strtolower($_SERVER["SERVER_PROTOCOL"]), "/").$s;
    713:         $port = ($_SERVER["SERVER_PORT"] == "80") ? "" : (":".$_SERVER["SERVER_PORT"]);
    714:         return $protocol."://".$_SERVER['SERVER_NAME'].$port;
    715:     }
    716:
    717:     public static function strleft($s1, $s2) {
    718:     return substr($s1, 0, strpos($s1, $s2));
    719:     }
    720:
    721:     public static function strContains($s1, $s2){
    722:         return (strpos($s1, $s2) !== false);
    723:     }
    724:
    725:     public static function strEndsWith($s1, $s2)
    726:     {
    727:         return substr($s1, -strlen($s2)) === $s2;
    728:     }
    729:
    730:     public static function getUA(){
    731:         return $_SERVER['HTTP_USER_AGENT'];
    732:     }
    733:
    734:     public static function isIE6to9(){
    735:         $ua = static::getUA();
    736:         return ((static::strContains($ua,"MSIE 6.") ||
    737:                  static::strContains($ua,"MSIE 7.") ||
    738:                  static::strContains($ua,"MSIE 8.") ||
    739:                  static::strContains($ua,"MSIE 9.")) &&
    740:                  static::strContains($ua,"Opera") === false);
    741:     }
    742:
    743:     public static function isIE5orOlder(){
    744:         $ua = static::getUA();
    745:         return ((static::strContains($ua,"MSIE 5.") ||
    746:                  static::strContains($ua,"MSIE 4.") ||
    747:                  static::strContains($ua,"MSIE 3.") ||
    748:                  static::strContains($ua,"MSIE 2.")) &&
    749:                  static::strContains($ua,"Opera") === false);
    750:     }
    751:
    752:     public static function isIE10orGreater(){
    753:         $ua = static::getUA();
    754:         return (static::strContains($ua,"MSIE") || static::strContains($ua,"Trident/")) && static::isIE6to9() === false && static::isIE5orOlder() === false && static::strContains($ua,"Opera") === false;
    755:     }
    756:
    757: }
    758:
    759: /**
    760:  * Specifies information about the print job to be processed at the client side.
    761:  */
    762: class ClientPrintJob{
    763:
    764:     /**
    765:      * Gets or sets the ClientPrinter object. Default is NULL.
    766:      * The ClientPrinter object refers to the kind of printer that the client machine has attached or can reach.
    767:      * - Use a DefaultPrinter object for using the default printer installed in the client machine.
    768:      * - Use a InstalledPrinter object for using a printer installed in the client machine with an associated Windows driver.
    769:      * - Use a ParallelPortPrinter object for using a printer which is connected through a parallel port in the client machine.
    770:      * - Use a SerialPortPrinter object for using a printer which is connected through a serial port in the client machine.
    771:      * - Use a NetworkPrinter object for using a Network IP/Ethernet printer which can be reached from the client machine.
    772:      * @var ClientPrinter
    773:      */
    774:     public $clientPrinter = null;
    775:     /**
    776:      * Gets or sets the printer's commands in text plain format. Default is an empty string.
    777:      * @var string
    778:      */
    779:     public $printerCommands = '';
    780:     /**
    781:      * Gets or sets whether the printer commands have chars expressed in hexadecimal notation. Default is false.
    782:      * The string set to the $printerCommands property can contain chars expressed in hexadecimal notation.
    783:      * Many printer languages have commands which are represented by non-printable chars and to express these commands
    784:      * in a string could require many concatenations and hence be not so readable.
    785:      * By using hex notation, you can make it simple and elegant. Here is an example: if you need to encode ASCII 27 (escape),
    786:      * then you can represent it as 0x27.
    787:      * @var boolean
    788:      */
    789:     public $formatHexValues = false;
    790:     /**
    791:      * Gets or sets the PrintFile object to be printed at the client side. Default is NULL.
    792:      * @var PrintFile
    793:      */
    794:     public $printFile = null;
    795:     /**
    796:      * Gets or sets an array of PrintFile objects to be printed at the client side. Default is NULL.
    797:      * @var array
    798:      */
    799:     public $printFileGroup = null;
    800:
    801:
    802:     /**
    803:      * Sends this ClientPrintJob object to the client for further processing.
    804:      * The ClientPrintJob object will be processed by the WCPP installed at the client machine.
    805:      * @return string A string representing a ClientPrintJob object.
    806:      */
    807:     public function sendToClient(){
    808:
    809:         header('Content-type: application/octet-stream');
    810:
    811:         $cpjHeader = chr(99).chr(112).chr(106).chr(2);
    812:
    813:         $buffer = '';
    814:
    815:         if (!Utils::isNullOrEmptyString($this->printerCommands)){
    816:             if($this->formatHexValues){
    817:                 $buffer = Utils::formatHexValues ($this->printerCommands);
    818:             } else {
    819:                 $buffer = $this->printerCommands;
    820:             }
    821:         } else if (isset ($this->printFile)){
    822:             $buffer = $this->printFile->serialize();
    823:         } else if (isset ($this->printFileGroup)){
    824:             $buffer = 'wcpPFG:';
    825:             $zip = new ZipArchive;
    826:             $cacheFileName = (Utils::strEndsWith(WebClientPrint::$wcpCacheFolder, '/')?WebClientPrint::$wcpCacheFolder:WebClientPrint::$wcpCacheFolder.'/').'PFG'.uniqid().'.zip';
    827:             $res = $zip->open($cacheFileName, ZipArchive::CREATE);
    828:             if ($res === TRUE) {
    829:                 foreach ($this->printFileGroup as $printFile) {
    830:                     $zip->addFromString($printFile->fileName, $printFile->getFileContent());
    831:                 }
    832:                 $zip->close();
    833:                 $handle = fopen($cacheFileName, 'rb');
    834:                 $buffer .= fread($handle, filesize($cacheFileName));
    835:                 fclose($handle);
    836:                 unlink($cacheFileName);
    837:             } else {
    838:                 $buffer='Creating PrintFileGroup failed. Cannot create zip file.';
    839:             }
    840:         }
    841:
    842:         $arrIdx1 = Utils::intToArray(strlen($buffer));
    843:
    844:         if (!isset($this->clientPrinter)){
    845:             $this->clientPrinter = new UserSelectedPrinter();
    846:         }
    847:
    848:         $buffer .= $this->clientPrinter->serialize();
    849:
    850:         $arrIdx2 = Utils::intToArray(strlen($buffer));
    851:
    852:         $lo = '';
    853:         if(Utils::isNullOrEmptyString(WebClientPrint::$licenseOwner)){
    854:             $lo = substr(uniqid(), 0, 8);
    855:         }  else {
    856:             $lo = WebClientPrint::$licenseOwner;
    857:         }
    858:         $lk = '';
    859:         if(Utils::isNullOrEmptyString(WebClientPrint::$licenseKey)){
    860:             $lk = substr(uniqid(), 0, 8);
    861:         }  else {
    862:             $lk = WebClientPrint::$licenseKey;
    863:         }
    864:         $buffer .= $lo.chr(124).$lk;
    865:
    866:         return $cpjHeader.$arrIdx1.$arrIdx2.$buffer;
    867:     }
    868:
    869: }