Test Failed
Push — main ( c8394f...8477f1 )
by Rafael
66:21
created

BasicIPP   F

Complexity

Total Complexity 271

Size/Duplication

Total Lines 1927
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1213
dl 0
loc 1927
rs 0.8
c 0
b 0
f 0
wmc 271

How to fix   Complexity   

Complex Class

Complex classes like BasicIPP often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BasicIPP, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* @(#) $Header: /sources/phpprintipp/phpprintipp/php_classes/BasicIPP.php,v 1.7 2012/03/01 17:21:04 harding Exp $
4
 *
5
 * Class BasicIPP - Send Basic IPP requests, Get and parses IPP Responses.
6
 *
7
 *   Copyright (C) 2005-2009  Thomas HARDING
8
 *   Parts Copyright (C) 2005-2006 Manuel Lemos
9
 *
10
 *   This library is free software; you can redistribute it and/or
11
 *   modify it under the terms of the GNU Library General Public
12
 *   License as published by the Free Software Foundation; either
13
 *   version 2 of the License, or (at your option) any later version.
14
 *
15
 *   This library is distributed in the hope that it will be useful,
16
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
 *   Library General Public License for more details.
19
 *
20
 *   You should have received a copy of the GNU Library General Public
21
 *   License along with this library; if not, write to the Free Software
22
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
 *
24
 *   mailto:[email protected]
25
 *   Thomas Harding, 56 rue de la bourie rouge, 45 000 ORLEANS -- FRANCE
26
 *
27
 */
28
/*
29
30
   This class is intended to implement Internet Printing Protocol on client side.
31
32
   References needed to debug / add functionnalities:
33
   - RFC 2910
34
   - RFC 2911
35
   - RFC 3380
36
   - RFC 3382
37
 */
38
39
require_once("http_class.php");
40
41
class ippException extends \Exception
42
{
43
    protected $errno;
44
45
    public function __construct($msg, $errno = null)
46
    {
47
        parent::__construct($msg);
48
        $this->errno = $errno;
49
    }
50
51
    public function getErrorFormatted()
52
    {
53
        $return = sprintf(
54
            "[ipp]: %s -- " . _(" file %s, line %s"),
55
            $this->getMessage(), $this->getFile(), $this->getLine());
56
        return $return;
57
    }
58
59
    public function getErrno()
60
    {
61
        return $this->errno;
62
    }
63
}
64
65
class BasicIPP
66
{
67
    public $paths = array(
68
        "root" => "/",
69
        "admin" => "/admin/",
70
        "printers" => "/printers/",
71
        "jobs" => "/jobs/"
72
    );
73
    public $http_timeout = 30; // timeout at http connection (seconds) 0 => default => 30.
74
    public $http_data_timeout = 30; // data reading timeout (milliseconds) 0 => default => 30.
75
    public $ssl = false;
76
    public $debug_level = 3; // max 3: almost silent
77
    public $alert_on_end_tag; // debugging purpose: echo "END tag OK" if (1 and  reads while end tag)
78
    public $with_exceptions = 1; // compatibility mode for old scripts      // DOL_LDR_CHANGE set this to 1
79
    public $handle_http_exceptions = 1;
80
81
    // readables variables
82
    public $jobs = array();
83
    public $jobs_uri = array();
84
    public $status = array();
85
    public $response_completed = array();
86
    public $last_job = "";
87
    public $attributes; // object you can read: attributes after validateJob()
88
    public $printer_attributes; // object you can read: printer's attributes after getPrinterAttributes()
89
    public $job_attributes; // object you can read: last job attributes
90
    public $jobs_attributes; // object you can read: jobs attributes after getJobs()
91
    public $available_printers = array();
92
    public $printer_map = array();
93
    public $printers_uri = array();
94
    public $debug = array();
95
    public $response;
96
    public $meta;
97
98
    // protected variables;
99
    protected $log_level = 2; // max 3: very verbose
100
    protected $log_type = 3; // 3: file | 1: e-mail | 0: logger
101
    protected $log_destination; // e-mail or file
102
    protected $serveroutput;
103
    protected $setup;
104
    protected $stringjob;
105
    protected $data;
106
    protected $debug_count = 0;
107
    protected $username;
108
    protected $charset;
109
    protected $password;
110
    protected $requesring_user;
111
    protected $client_hostname = "localhost";
112
    protected $stream;
113
    protected $host = "localhost";
114
    protected $port = "631";
115
    protected $requesting_user = '';
116
    protected $printer_uri;
117
    protected $timeout = "20"; //20 secs
118
    protected $errNo;
119
    protected $errStr;
120
    protected $datatype;
121
    protected $datahead;
122
    protected $datatail;
123
    protected $operation_id;
124
    protected $delay;
125
    protected $error_generation; //devel feature
126
    protected $debug_http = 0;
127
    protected $no_disconnect;
128
    protected $job_tags;
129
    protected $operation_tags;
130
    protected $index;
131
    protected $collection; //RFC3382
132
    protected $collection_index; //RFC3382
133
    protected $collection_key = array(); //RFC3382
134
    protected $collection_depth = - 1; //RFC3382
135
    protected $end_collection = false; //RFC3382
136
    protected $collection_nbr = array(); //RFC3382
137
    protected $unix = false; // true -> use unix sockets instead of http
138
    protected $output;
139
140
    public function __construct()
141
    {
142
        $tz = getenv("date.timezone");
143
        if (!$tz)
144
        {
145
            $tz = @date_default_timezone_get();
146
        }
147
148
        date_default_timezone_set($tz);
149
        $this->meta = new \stdClass();
150
        $this->setup = new \stdClass();
151
        $this->values = new \stdClass();
152
        $this->serveroutput = new \stdClass();
153
        $this->error_generation = new \stdClass();
154
        $this->_parsing = new \stdClass();
155
        self::_initTags();
156
    }
157
158
    public function setPort($port = '631')
159
    {
160
        $this->port = $port;
161
        self::_putDebug("Port is " . $this->port, 2);
162
    }
163
164
    public function setUnix($socket = '/var/run/cups/cups.sock')
165
    {
166
        $this->host = $socket;
167
        $this->unix = true;
168
        self::_putDebug("Host is " . $this->host, 2);
169
    }
170
171
    public function setHost($host = 'localhost')
172
    {
173
        $this->host = $host;
174
        $this->unix = false;
175
        self::_putDebug("Host is " . $this->host, 2);
176
    }
177
178
    public function setTimeout($timeout)
179
    {
180
        $this->timeout = $timeout;
181
    }
182
183
    public function setPrinterURI($uri)
184
    {
185
        $length = strlen($uri);
186
        $length = chr($length);
187
        while (strlen($length) < 2) $length = chr(0x00) . $length;
188
        $this->meta->printer_uri = chr(0x45) // uri type | value-tag
189
            . chr(0x00) . chr(0x0B) // name-length
190
            . "printer-uri" // printer-uri | name
191
            . $length . $uri;
192
        $this->printer_uri = $uri;
193
        self::_putDebug(sprintf(_("Printer URI: %s"), $uri), 2);
194
        $this->setup->uri = 1;
195
    }
196
197
    public function setData($data)
198
    {
199
        $this->data = $data;
200
        self::_putDebug("Data set", 2);
201
    }
202
203
    public function setRawText()
204
    {
205
        $this->setup->datatype = 'TEXT';
206
        $this->meta->mime_media_type = "";
207
        $this->setup->mime_media_type = 1;
208
        $this->datahead = chr(0x16);
209
        if (is_readable($this->data))
210
        {
211
            //It's a filename.  Open and stream.
212
            $data = fopen($this->data, "rb");
213
            while (!feof($data)) $output = fread($data, 8192);
214
        }
215
        else
216
        {
217
            $output = $this->data;
218
        }
219
        if (substr($output, -1, 1) != chr(0x0c)) {
220
            if (!isset($this->setup->noFormFeed))
221
            {
222
                $this->datatail = chr(0x0c);
223
            }
224
        }
225
        self::_putDebug(_("Forcing data to be interpreted as RAW TEXT"), 2);
226
    }
227
228
    public function unsetRawText()
229
    {
230
        $this->setup->datatype = 'BINARY';
231
        $this->datahead = '';
232
        $this->datatail = '';
233
        self::_putDebug(_("Unset forcing data to be interpreted as RAW TEXT"), 2);
234
    }
235
236
    public function setBinary()
237
    {
238
        self::unsetRawText();
239
    }
240
241
    public function setFormFeed()
242
    {
243
        $this->datatail = "\r\n" . chr(0x0c);
244
        unset($this->setup->noFormFeed);
245
    }
246
247
    public function unsetFormFeed()
248
    {
249
        $this->datatail = '';
250
        $this->setup->noFormFeed = 1;
251
    }
252
253
    public function setCharset($charset = 'utf-8')
254
    {
255
        $charset = strtolower($charset);
256
        $this->charset = $charset;
257
        $this->meta->charset = chr(0x47) // charset type | value-tag
258
            . chr(0x00) . chr(0x12) // name-length
259
            . "attributes-charset" // attributes-charset | name
260
            . self::_giveMeStringLength($charset) // value-length
261
            . $charset; // value
262
        self::_putDebug(sprintf(_("Charset: %s"), $charset), 2);
263
        $this->setup->charset = 1;
264
    }
265
266
    public function setLanguage($language = 'en_us')
267
    {
268
        $language = strtolower($language);
269
        $this->meta->language = chr(0x48) // natural-language type | value-tag
270
            . chr(0x00) . chr(0x1B) //  name-length
271
            . "attributes-natural-language" //attributes-natural-language
272
            . self::_giveMeStringLength($language) // value-length
273
            . $language; // value
274
        self::_putDebug(sprintf(_("Language: %s"), $language), 2);
275
        $this->setup->language = 1;
276
    }
277
278
    public function setDocumentFormat($mime_media_type = 'application/octet-stream')
279
    {
280
        self::setBinary();
281
        $length = chr(strlen($mime_media_type));
282
        while (strlen($length) < 2) $length = chr(0x00) . $length;
283
        self::_putDebug(sprintf(_("mime type: %s"), $mime_media_type), 2);
284
        $this->meta->mime_media_type = chr(0x49) // document-format tag
285
            . self::_giveMeStringLength('document-format') . 'document-format' //
286
            . self::_giveMeStringLength($mime_media_type) . $mime_media_type; // value
287
        $this->setup->mime_media_type = 1;
288
    }
289
290
    // setDocumentFormat alias for backward compatibility
291
    public function setMimeMediaType($mime_media_type = "application/octet-stream")
292
    {
293
        self::setDocumentFormat($mime_media_type);
294
    }
295
296
    public function setCopies($nbrcopies = 1)
297
    {
298
        $this->meta->copies = "";
299
300
        if ($nbrcopies == 1 || !$nbrcopies)
301
        {
302
            return true;
303
        }
304
305
        $copies = self::_integerBuild($nbrcopies);
306
        $this->meta->copies = chr(0x21) // integer type | value-tag
307
            . chr(0x00) . chr(0x06) //             name-length
308
            . "copies" // copies    |             name
309
            . self::_giveMeStringLength($copies) // value-length
310
            . $copies;
311
        self::_putDebug(sprintf(_("Copies: %s"), $nbrcopies), 2);
312
        $this->setup->copies = 1;
313
    }
314
315
    public function setDocumentName($document_name = "")
316
    {
317
        $this->meta->document_name = "";
318
        if (!$document_name) {
319
            return true;
320
        }
321
        $document_name = substr($document_name, 0, 1023);
322
        $length = strlen($document_name);
323
        $length = chr($length);
324
        while (strlen($length) < 2) $length = chr(0x00) . $length;
325
        self::_putDebug(sprintf(_("document name: %s"), $document_name), 2);
326
        $this->meta->document_name = chr(0x41) // textWithoutLanguage tag
327
            . chr(0x00) . chr(0x0d) // name-length
328
            . "document-name" // mimeMediaType
329
            . self::_giveMeStringLength($document_name) . $document_name; // value
330
    }
331
332
    public function setJobName($jobname = '', $absolute = false)
333
    {
334
        $this->meta->jobname = '';
335
        if ($jobname == '')
336
        {
337
            $this->meta->jobname = '';
338
            return true;
339
        }
340
        $postpend = date('-H:i:s-') . $this->_setJobId();
341
        if ($absolute) {
342
            $postpend = '';
343
        }
344
        if (isset($this->values->jobname) && $jobname == '(PHP)')
345
        {
346
            $jobname = $this->values->jobname;
347
        }
348
        $this->values->jobname = $jobname;
349
        $jobname .= $postpend;
350
        $this->meta->jobname = chr(0x42) // nameWithoutLanguage type || value-tag
351
            . chr(0x00) . chr(0x08) //  name-length
352
            . "job-name" //  job-name || name
353
            . self::_giveMeStringLength($jobname) // value-length
354
            . $jobname; // value
355
        self::_putDebug(sprintf(_("Job name: %s"), $jobname), 2);
356
        $this->setup->jobname = 1;
357
    }
358
359
    public function setUserName($username = 'PHP-SERVER')
360
    {
361
        $this->requesting_user = $username;
362
        $this->meta->username = '';
363
        if (!$username) {
364
            return true;
365
        }
366
        if ($username == 'PHP-SERVER' && isset($this->meta->username)) {
367
            return TRUE;
368
        }
369
        /*
370
        $value_length = 0x00;
371
        for ($i = 0; $i < strlen($username); $i++)
372
        {
373
            $value_length+= 0x01;
374
        }
375
        $value_length = chr($value_length);
376
        while (strlen($value_length) < 2) $value_length = chr(0x00) . $value_length;
377
        */
378
        $this->meta->username = chr(0x42) // keyword type || value-tag
379
            . chr(0x00) . chr(0x14) // name-length
380
            . "requesting-user-name"
381
            . self::_giveMeStringLength($username) // value-length
382
            . $username;
383
        self::_putDebug(sprintf(_("Username: %s"), $username), 2);
384
        $this->setup->username = 1;
385
    }
386
387
    public function setAuthentification($username, $password)
388
    {
389
        self::setAuthentication($username, $password);
390
    }
391
392
    public function setAuthentication($username, $password)
393
    {
394
        $this->password = $password;
395
        $this->username = $username;
396
        self::_putDebug(_("Setting password"), 2);
397
        $this->setup->password = 1;
398
    }
399
400
    public function setSides($sides = 2)
401
    {
402
        $this->meta->sides = '';
403
        if (!$sides)
404
        {
405
            return true;
406
        }
407
408
        switch ($sides)
409
        {
410
            case 1:
411
                $sides = "one-sided";
412
                break;
413
414
            case 2:
415
                $sides = "two-sided-long-edge";
416
                break;
417
418
            case "2CE":
419
                $sides = "two-sided-short-edge";
420
                break;
421
        }
422
423
        $this->meta->sides = chr(0x44) // keyword type | value-tag
424
            . chr(0x00) . chr(0x05) //        name-length
425
            . "sides" // sides |             name
426
            . self::_giveMeStringLength($sides) //               value-length
427
            . $sides; // one-sided |          value
428
        self::_putDebug(sprintf(_("Sides value set to %s"), $sides), 2);
429
    }
430
431
    public function setFidelity()
432
    {
433
        // whether the server can't replace any attributes
434
        // (eg, 2 sided print is not possible,
435
        // so print one sided) and DO NOT THE JOB.
436
        $this->meta->fidelity = chr(0x22) // boolean type  |  value-tag
437
            . chr(0x00) . chr(0x16) //                  name-length
438
            . "ipp-attribute-fidelity" // ipp-attribute-fidelity | name
439
            . chr(0x00) . chr(0x01) //  value-length
440
            . chr(0x01); //  true | value
441
        self::_putDebug(_("Fidelity attribute is set (paranoid mode)"), 3);
442
    }
443
444
    public function unsetFidelity()
445
    {
446
        // whether the server can replace any attributes
447
        // (eg, 2 sided print is not possible,
448
        // so print one sided) and DO THE JOB.
449
        $this->meta->fidelity = chr(0x22) //  boolean type | value-tag
450
            . chr(0x00) . chr(0x16) //        name-length
451
            . "ipp-attribute-fidelity" // ipp-attribute-fidelity | name
452
            . chr(0x00) . chr(0x01) //               value-length
453
            . chr(0x00); // false |                   value
454
        self::_putDebug(_("Fidelity attribute is unset"), 2);
455
    }
456
457
    public function setMessage($message = '')
458
    {
459
        $this->meta->message = '';
460
        if (!$message) {
461
            return true;
462
        }
463
        $this->meta->message =
464
            chr(0x41) // attribute type = textWithoutLanguage
465
                . chr(0x00)
466
                . chr(0x07)
467
                . "message"
468
                . self::_giveMeStringLength(substr($message, 0, 127))
469
                . substr($message, 0, 127);
470
        self::_putDebug(sprintf(_('Setting message to "%s"'), $message), 2);
471
    }
472
473
    public function setPageRanges($page_ranges)
474
    {
475
        // $pages_ranges = string:  "1:5 10:25 40:52 ..."
476
        // to unset, specify an empty string.
477
        $this->meta->page_range = '';
478
        if (!$page_ranges) {
479
            return true;
480
        }
481
        $page_ranges = trim(str_replace("-", ":", $page_ranges));
482
        $first = true;
483
        // $page_ranges = split(' ', $page_ranges);
484
        $page_ranges = preg_split('# #', $page_ranges);
485
        foreach($page_ranges as $page_range)
486
        {
487
            $value = self::_rangeOfIntegerBuild($page_range);
488
            if ($first)
489
            {
490
                $this->meta->page_ranges .=
491
                $this->tags_types['rangeOfInteger']['tag']
492
                    . self::_giveMeStringLength('page-ranges')
493
                    . 'page-ranges'
494
                    . self::_giveMeStringLength($value)
495
                    . $value;
496
            }
497
            else
498
            {
499
                $this->meta->page_ranges .=
500
                $this->tags_types['rangeOfInteger']['tag']
501
                    . self::_giveMeStringLength('')
502
                    . self::_giveMeStringLength($value)
503
                    . $value;
504
                $first = false;
505
            }
506
        }
507
    }
508
509
    public function setAttribute($attribute, $values)
510
    {
511
        $operation_attributes_tags = array_keys($this->operation_tags);
512
        $job_attributes_tags = array_keys($this->job_tags);
513
        $printer_attributes_tags = array_keys($this->printer_tags);
514
        self::unsetAttribute($attribute);
515
        if (in_array($attribute, $operation_attributes_tags))
516
        {
517
            if (!is_array($values))
518
            {
519
                self::_setOperationAttribute($attribute, $values);
520
            }
521
            else
522
            {
523
                foreach($values as $value)
524
                {
525
                    self::_setOperationAttribute($attribute, $value);
526
                }
527
            }
528
        }
529
        elseif (in_array($attribute, $job_attributes_tags))
530
        {
531
            if (!is_array($values))
532
            {
533
                self::_setJobAttribute($attribute, $values);
534
            }
535
            else
536
            {
537
                foreach($values as $value)
538
                {
539
                    self::_setJobAttribute($attribute, $value);
540
                }
541
            }
542
        }
543
        elseif (in_array($attribute, $printer_attributes_tags))
544
        {
545
            if (!is_array($values))
546
            {
547
                self::_setPrinterAttribute($attribute, $values);
548
            }
549
            else
550
            {
551
                foreach($values as $value)
552
                {
553
                    self::_setPrinterAttribute($attribute, $value);
554
                }
555
            }
556
        }
557
        else
558
        {
559
            trigger_error(
560
                sprintf(
561
                    _('SetAttribute: Tag "%s" is not a printer or a job attribute'),
562
                    $attribute), E_USER_NOTICE);
563
            self::_putDebug(
564
                sprintf(
565
                    _('SetAttribute: Tag "%s" is not a printer or a job attribute'),
566
                    $attribute), 3);
567
            self::_errorLog(
568
                sprintf(
569
                    _('SetAttribute: Tag "%s" is not a printer or a job attribute'),
570
                    $attribute), 2);
571
            return FALSE;
572
        }
573
    }
574
575
    public function unsetAttribute($attribute)
576
    {
577
        $operation_attributes_tags = array_keys($this->operation_tags);
578
        $job_attributes_tags = array_keys($this->job_tags);
579
        $printer_attributes_tags = array_keys($this->printer_tags);
580
        if (in_array($attribute, $operation_attributes_tags))
581
        {
582
            unset(
583
                $this->operation_tags[$attribute]['value'],
584
                $this->operation_tags[$attribute]['systag']
585
            );
586
        }
587
        elseif (in_array($attribute, $job_attributes_tags))
588
        {
589
            unset(
590
                $this->job_tags[$attribute]['value'],
591
                $this->job_tags[$attribute]['systag']
592
            );
593
        }
594
        elseif (in_array($attribute, $printer_attributes_tags))
595
        {
596
            unset(
597
                $this->printer_tags[$attribute]['value'],
598
                $this->printer_tags[$attribute]['systag']
599
            );
600
        }
601
        else
602
        {
603
            trigger_error(
604
                sprintf(
605
                    _('unsetAttribute: Tag "%s" is not a printer or a job attribute'),
606
                    $attribute), E_USER_NOTICE);
607
            self::_putDebug(
608
                sprintf(
609
                    _('unsetAttribute: Tag "%s" is not a printer or a job attribute'),
610
                    $attribute), 3);
611
            self::_errorLog(
612
                sprintf(
613
                    _('unsetAttribute: Tag "%s" is not a printer or a job attribute'),
614
                    $attribute), 2);
615
            return FALSE;
616
        }
617
        return true;
618
    }
619
620
    //
621
    // LOGGING / DEBUGGING
622
    //
623
    /**
624
     * Sets log file destination. Creates the file if has permission.
625
     *
626
     * @param string $log_destination
627
     * @param string $destination_type
628
     * @param int $level
629
     *
630
     * @throws ippException
631
     */
632
    public function setLog($log_destination, $destination_type = 'file', $level = 2)
633
    {
634
        if (!file_exists($log_destination) && is_writable(dirname($log_destination)))
635
        {
636
            touch($log_destination);
637
            chmod($log_destination, 0777);
638
        }
639
640
        switch ($destination_type)
641
        {
642
            case 'file':
643
            case 3:
644
                $this->log_destination = $log_destination;
645
                $this->log_type = 3;
646
                break;
647
648
            case 'logger':
649
            case 0:
650
                $this->log_destination = '';
651
                $this->log_type = 0;
652
                break;
653
654
            case 'e-mail':
655
            case 1:
656
                $this->log_destination = $log_destination;
657
                $this->log_type = 1;
658
                break;
659
        }
660
        $this->log_level = $level;
661
    }
662
663
    public function printDebug()
664
    {
665
        for ($i = 0; $i < $this->debug_count; $i++)
666
        {
667
            echo $this->debug[$i], "\n";
668
        }
669
        $this->debug = array();
670
        $this->debug_count = 0;
671
    }
672
673
    public function getDebug()
674
    {
675
        $debug = '';
676
        for ($i = 0; $i < $this->debug_count; $i++)
677
        {
678
            $debug .= $this->debug[$i];
679
        }
680
        $this->debug = array();
681
        $this->debug_count = 0;
682
        return $debug;
683
    }
684
685
    //
686
    // OPERATIONS
687
    //
688
    public function printJob()
689
    {
690
        // this BASIC version of printJob do not parse server
691
        // output for job's attributes
692
        self::_putDebug(
693
            sprintf(
694
                "************** Date: %s ***********",
695
                date('Y-m-d H:i:s')
696
            )
697
        );
698
        if (!$this->_stringJob()) {
699
            return FALSE;
700
        }
701
        if (is_readable($this->data))
702
        {
703
            self::_putDebug(_("Printing a FILE"));
704
            $this->output = $this->stringjob;
705
            if ($this->setup->datatype == "TEXT")
706
            {
707
                $this->output .= chr(0x16);
708
            }
709
            $post_values = array(
710
                "Content-Type" => "application/ipp",
711
                "Data" => $this->output,
712
                "File" => $this->data
713
            );
714
            if ($this->setup->datatype == "TEXT" && !isset($this->setup->noFormFeed))
715
            {
716
                $post_values = array_merge(
717
                    $post_values,
718
                    array(
719
                        "Filetype" => "TEXT"
720
                    )
721
                );
722
            }
723
        }
724
        else
725
        {
726
            self::_putDebug(_("Printing DATA"));
727
            $this->output =
728
                $this->stringjob
729
                    . $this->datahead
730
                    . $this->data
731
                    . $this->datatail;
732
            $post_values = array(
733
                "Content-Type" => "application/ipp",
734
                "Data" => $this->output
735
            );
736
        }
737
        if (self::_sendHttp($post_values, $this->paths["printers"]))
738
        {
739
            self::_parseServerOutput();
740
        }
741
        if (isset($this->serveroutput) && isset($this->serveroutput->status))
742
        {
743
            $this->status = array_merge($this->status, array(
744
                $this->serveroutput->status
745
            ));
746
            if ($this->serveroutput->status == "successfull-ok")
747
            {
748
                self::_errorLog(
749
                    sprintf("printing job %s: ", $this->last_job)
750
                        . $this->serveroutput->status,
751
                    3);
752
            }
753
            else
754
            {
755
                self::_errorLog(
756
                    sprintf("printing job: ", $this->last_job)
757
                        . $this->serveroutput->status,
758
                    1);
759
            }
760
                return $this->serveroutput->status;
761
        }
762
763
    $this->status =
764
        array_merge($this->status, array("OPERATION FAILED"));
765
        $this->jobs =
766
            array_merge($this->jobs, array(""));
767
        $this->jobs_uri =
768
            array_merge($this->jobs_uri, array(""));
769
770
        self::_errorLog("printing job : OPERATION FAILED", 1);
771
        return false;
772
    }
773
774
    //
775
    // HTTP OUTPUT
776
    //
777
    protected function _sendHttp($post_values, $uri)
778
    {
779
        /*
780
            This function Copyright (C) 2005-2006 Thomas Harding, Manuel Lemos
781
        */
782
        $this->response_completed[] = "no";
783
        unset($this->serverouptut);
784
        self::_putDebug(_("Processing HTTP request"), 2);
785
        $this->serveroutput->headers = array();
786
        $this->serveroutput->body = "";
787
        $http = new http_class();
788
        if (!$this->unix) {
789
            // DOL_LDR_CHANGE
790
            if (empty($this->host)) $this->host = '127.0.0.1';
791
            $http->host = $this->host;
792
        }
793
        else {
794
            $http->host = "localhost";
795
        }
796
        $http->with_exceptions = $this->with_exceptions;
797
        if ($this->debug_http)
798
        {
799
            $http->debug = 1;
800
            $http->html_debug = 0;
801
        }
802
        else
803
        {
804
            $http->debug = 0;
805
            $http->html_debug = 0;
806
        }
807
        $url = "http://" . $this->host;
808
        if ($this->ssl) {
809
            $url = "https://" . $this->host;
810
        }
811
        if ($this->unix) {
812
            $url = "unix://" . $this->host;
813
        }
814
        $http->port = $this->port;
815
        $http->timeout = $this->http_timeout;
816
        $http->data_timeout = $this->http_data_timeout;
817
        $http->force_multipart_form_post = false;
818
        $http->user = $this->username;
819
        $http->password = $this->password;
820
        $error = $http->GetRequestArguments($url, $arguments);
821
        $arguments["RequestMethod"] = "POST";
822
        $arguments["Headers"] = array(
823
            "Content-Type" => "application/ipp"
824
        );
825
        $arguments["BodyStream"] = array(
826
            array(
827
                "Data" => $post_values["Data"]
828
            )
829
        );
830
        if (isset($post_values["File"])) {
831
            $arguments["BodyStream"][] = array(
832
                "File" => $post_values["File"]
833
            );
834
        }
835
        if (
836
            isset($post_values["FileType"])
837
            && !strcmp($post_values["FileType"], "TEXT")
838
        )
839
        {
840
            $arguments["BodyStream"][] = array("Data" => Chr(12));
841
        }
842
        $arguments["RequestURI"] = $uri;
843
        if ($this->with_exceptions && $this->handle_http_exceptions)
844
        {
845
            try
846
            {
847
                $success = $http->Open($arguments);
848
            }
849
            catch(httpException $e)
850
            {
851
                throw new ippException(
852
                    sprintf("http error: %s", $e->getMessage()),
853
                        $e->getErrno());
854
            }
855
        }
856
        else
857
        {
858
            $success = $http->Open($arguments);
859
        }
860
        if ($success[0] == true)
861
        {
862
            $success = $http->SendRequest($arguments);
863
            if ($success[0] == true)
864
            {
865
                self::_putDebug("H T T P    R E Q U E S T :");
866
                self::_putDebug("Request headers:");
867
                for (Reset($http->request_headers) , $header = 0; $header < count($http->request_headers); Next($http->request_headers) , $header++)
868
                {
869
                    $header_name = Key($http->request_headers);
870
                    if (GetType($http->request_headers[$header_name]) == "array")
871
                    {
872
                        for ($header_value = 0; $header_value < count($http->request_headers[$header_name]); $header_value++)
873
                        {
874
                            self::_putDebug($header_name . ": " . $http->request_headers[$header_name][$header_value]);
875
                        }
876
                    }
877
                    else
878
                    {
879
                        self::_putDebug($header_name . ": " . $http->request_headers[$header_name]);
880
                    }
881
                }
882
                self::_putDebug("Request body:");
883
                self::_putDebug(
884
                    htmlspecialchars($http->request_body)
885
                        . "*********** END REQUEST BODY *********"
886
                );
887
                $i = 0;
888
                $headers = array();
889
                unset($this->serveroutput->headers);
890
                $http->ReadReplyHeaders($headers);
891
                self::_putDebug("H T T P    R E S P O N S E :");
892
                self::_putDebug("Response headers:");
893
                for (Reset($headers) , $header = 0; $header < count($headers); Next($headers) , $header++)
894
                {
895
                    $header_name = Key($headers);
896
                    if (GetType($headers[$header_name]) == "array")
897
                    {
898
                        for ($header_value = 0; $header_value < count($headers[$header_name]); $header_value++)
899
                        {
900
                            self::_putDebug($header_name . ": " . $headers[$header_name][$header_value]);
901
                            $this->serveroutput->headers[$i] =
902
                                $header_name . ": "
903
                                    . $headers[$header_name][$header_value];
904
                            $i++;
905
                        }
906
                    }
907
                    else
908
                    {
909
                        self::_putDebug($header_name . ": " . $headers[$header_name]);
910
                        $this->serveroutput->headers[$i] =
911
                            $header_name
912
                                . ": "
913
                                . $headers[$header_name];
914
                        $i++;
915
                    }
916
                }
917
                self::_putDebug("\n\nResponse body:\n");
918
                $this->serveroutput->body = "";
919
                for (;;)
920
                {
921
                    $http->ReadReplyBody($body, 1024);
922
                    if (strlen($body) == 0) {
923
                        break;
924
                    }
925
926
                    self::_putDebug(htmlentities($body));
927
                    $this->serveroutput->body .= $body;
928
                }
929
                self::_putDebug("********* END RESPONSE BODY ********");
930
            }
931
        }
932
        $http->Close();
933
        return true;
934
    }
935
936
    //
937
    // INIT
938
    //
939
    protected function _initTags()
940
    {
941
        $this->tags_types = array(
942
            "unsupported" => array(
943
                "tag" => chr(0x10) ,
944
                "build" => ""
945
            ) ,
946
            "reserved" => array(
947
                "tag" => chr(0x11) ,
948
                "build" => ""
949
            ) ,
950
            "unknown" => array(
951
                "tag" => chr(0x12) ,
952
                "build" => ""
953
            ) ,
954
            "no-value" => array(
955
                "tag" => chr(0x13) ,
956
                "build" => "no_value"
957
            ) ,
958
            "integer" => array(
959
                "tag" => chr(0x21) ,
960
                "build" => "integer"
961
            ) ,
962
            "boolean" => array(
963
                "tag" => chr(0x22) ,
964
                "build" => "boolean"
965
            ) ,
966
            "enum" => array(
967
                "tag" => chr(0x23) ,
968
                "build" => "enum"
969
            ) ,
970
            "octetString" => array(
971
                "tag" => chr(0x30) ,
972
                "build" => "octet_string"
973
            ) ,
974
            "datetime" => array(
975
                "tag" => chr(0x31) ,
976
                "build" => "datetime"
977
            ) ,
978
            "resolution" => array(
979
                "tag" => chr(0x32) ,
980
                "build" => "resolution"
981
            ) ,
982
            "rangeOfInteger" => array(
983
                "tag" => chr(0x33) ,
984
                "build" => "range_of_integers"
985
            ) ,
986
            "textWithLanguage" => array(
987
                "tag" => chr(0x35) ,
988
                "build" => "string"
989
            ) ,
990
            "nameWithLanguage" => array(
991
                "tag" => chr(0x36) ,
992
                "build" => "string"
993
            ) ,
994
            /*
995
            "text" => array ("tag" => chr(0x40),
996
            "build" => "string"),
997
            "text string" => array ("tag" => chr(0x40),
998
            "build" => "string"),
999
            */
1000
            "textWithoutLanguage" => array(
1001
                "tag" => chr(0x41) ,
1002
                "build" => "string"
1003
            ) ,
1004
            "nameWithoutLanguage" => array(
1005
                "tag" => chr(0x42) ,
1006
                "buid" => "string"
1007
            ) ,
1008
            "keyword" => array(
1009
                "tag" => chr(0x44) ,
1010
                "build" => "string"
1011
            ) ,
1012
            "uri" => array(
1013
                "tag" => chr(0x45) ,
1014
                "build" => "string"
1015
            ) ,
1016
            "uriScheme" => array(
1017
                "tag" => chr(0x46) ,
1018
                "build" => "string"
1019
            ) ,
1020
            "charset" => array(
1021
                "tag" => chr(0x47) ,
1022
                "build" => "string"
1023
            ) ,
1024
            "naturalLanguage" => array(
1025
                "tag" => chr(0x48) ,
1026
                "build" => "string"
1027
            ) ,
1028
            "mimeMediaType" => array(
1029
                "tag" => chr(0x49) ,
1030
                "build" => "string"
1031
            ) ,
1032
            "extendedAttributes" => array(
1033
                "tag" => chr(0x7F) ,
1034
                "build" => "extended"
1035
            ) ,
1036
        );
1037
        $this->operation_tags = array(
1038
            "compression" => array(
1039
                "tag" => "keyword"
1040
            ) ,
1041
            "document-natural-language" => array(
1042
                "tag" => "naturalLanguage"
1043
            ) ,
1044
            "job-k-octets" => array(
1045
                "tag" => "integer"
1046
            ) ,
1047
            "job-impressions" => array(
1048
                "tag" => "integer"
1049
            ) ,
1050
            "job-media-sheets" => array(
1051
                "tag" => "integer"
1052
            ) ,
1053
        );
1054
        $this->job_tags = array(
1055
            "job-priority" => array(
1056
                "tag" => "integer"
1057
            ) ,
1058
            "job-hold-until" => array(
1059
                "tag" => "keyword"
1060
            ) ,
1061
            "job-sheets" => array(
1062
                "tag" => "keyword"
1063
            ) , //banner page
1064
            "multiple-document-handling" => array(
1065
                "tag" => "keyword"
1066
            ) ,
1067
            //"copies" => array("tag" => "integer"),
1068
            "finishings" => array(
1069
                "tag" => "enum"
1070
            ) ,
1071
            //"page-ranges" => array("tag" => "rangeOfInteger"), // has its own function
1072
            //"sides" => array("tag" => "keyword"), // has its own function
1073
            "number-up" => array(
1074
                "tag" => "integer"
1075
            ) ,
1076
            "orientation-requested" => array(
1077
                "tag" => "enum"
1078
            ) ,
1079
            "media" => array(
1080
                "tag" => "keyword"
1081
            ) ,
1082
            "printer-resolution" => array(
1083
                "tag" => "resolution"
1084
            ) ,
1085
            "print-quality" => array(
1086
                "tag" => "enum"
1087
            ) ,
1088
            "job-message-from-operator" => array(
1089
                "tag" => "textWithoutLanguage"
1090
            ) ,
1091
        );
1092
        $this->printer_tags = array(
1093
            "requested-attributes" => array(
1094
                "tag" => "keyword"
1095
            )
1096
        );
1097
    }
1098
1099
    //
1100
    // SETUP
1101
    //
1102
    protected function _setOperationId()
1103
    {
1104
        $prepend = '';
1105
        $this->operation_id += 1;
1106
        $this->meta->operation_id = self::_integerBuild($this->operation_id);
1107
        self::_putDebug("operation id is: " . $this->operation_id, 2);
1108
    }
1109
1110
    protected function _setJobId()
1111
    {
1112
        $this->meta->jobid += 1;
1113
        $prepend = '';
1114
        $prepend_length = 4 - strlen($this->meta->jobid);
1115
        for ($i = 0; $i < $prepend_length; $i++) {
1116
            $prepend .= '0';
1117
        }
1118
        return $prepend . $this->meta->jobid;
1119
    }
1120
1121
    protected function _setJobUri($job_uri)
1122
    {
1123
        $this->meta->job_uri = chr(0x45) // type uri
1124
            . chr(0x00) . chr(0x07) // name-length
1125
            . "job-uri"
1126
            //. chr(0x00).chr(strlen($job_uri))
1127
            . self::_giveMeStringLength($job_uri) . $job_uri;
1128
        self::_putDebug("job-uri is: " . $job_uri, 2);
1129
    }
1130
1131
    //
1132
    // RESPONSE PARSING
1133
    //
1134
    protected function _parseServerOutput()
1135
    {
1136
        $this->serveroutput->response = array();
1137
        if (!self::_parseHttpHeaders()) {
1138
            return FALSE;
1139
        }
1140
        $this->_parsing->offset = 0;
1141
        self::_parseIppVersion();
1142
        self::_parseStatusCode();
1143
        self::_parseRequestID();
1144
        $this->_parseResponse();
1145
        //devel
1146
        self::_putDebug(
1147
            sprintf("***** IPP STATUS: %s ******", $this->serveroutput->status),
1148
            4);
1149
        self::_putDebug("****** END OF OPERATION ****");
1150
        return true;
1151
    }
1152
1153
    protected function _parseHttpHeaders()
1154
    {
1155
        $response = "";
1156
        switch ($this->serveroutput->headers[0])
1157
        {
1158
            case "http/1.1 200 ok: ":
1159
                $this->serveroutput->httpstatus = "HTTP/1.1 200 OK";
1160
                $response = "OK";
1161
                break;
1162
1163
            // primitive http/1.0 for Lexmark printers (from Rick Baril)
1164
            case "http/1.0 200 ok: ":
1165
                $this->serveroutput->httpstatus = "HTTP/1.0 200 OK";
1166
                $response = "OK";
1167
                break;
1168
1169
            case "http/1.1 100 continue: ":
1170
                $this->serveroutput->httpstatus = "HTTP/1.1 100 CONTINUE";
1171
                $response = "OK";
1172
                break;
1173
1174
            case "":
1175
                $this->serveroutput->httpstatus = "HTTP/1.1 000 No Response From Server";
1176
                $this->serveroutput->status = "HTTP-ERROR-000_NO_RESPONSE_FROM_SERVER";
1177
                trigger_error("No Response From Server", E_USER_WARNING);
1178
                self::_errorLog("No Response From Server", 1);
1179
                $this->disconnected = 1;
1180
                return FALSE;
1181
                break;
1182
1183
            default:
1184
                $server_response = preg_replace("/: $/", '', $this->serveroutput->headers[0]);
1185
                // $strings = split(' ', $server_response, 3);
1186
                $strings = preg_split('# #', $server_response, 3);
1187
                $errno = $strings[1];
1188
                $string = strtoupper(str_replace(' ', '_', $strings[2]));
1189
                trigger_error(
1190
                    sprintf(_("server responds %s"), $server_response),
1191
                    E_USER_WARNING);
1192
                self::_errorLog("server responds " . $server_response, 1);
1193
                $this->serveroutput->httpstatus =
1194
                    strtoupper($strings[0])
1195
                        . " "
1196
                        . $errno
1197
                        . " "
1198
                        . ucfirst($strings[2]);
1199
1200
                $this->serveroutput->status =
1201
                    "HTTP-ERROR-"
1202
                        . $errno
1203
                        . "-"
1204
                        . $string;
1205
                $this->disconnected = 1;
1206
                return FALSE;
1207
                break;
1208
        }
1209
        unset($this->serveroutput->headers);
1210
        return TRUE;
1211
    }
1212
1213
    protected function _parseIppVersion()
1214
    {
1215
        $ippversion =
1216
            (ord($this->serveroutput->body[$this->_parsing->offset]) * 256)
1217
                + ord($this->serveroutput->body[$this->_parsing->offset + 1]);
1218
        switch ($ippversion)
1219
        {
1220
            case 0x0101:
1221
                $this->serveroutput->ipp_version = "1.1";
1222
                break;
1223
1224
            default:
1225
                $this->serveroutput->ipp_version =
1226
                    sprintf(
1227
                        "%u.%u (Unknown)",
1228
                        ord($this->serveroutput->body[$this->_parsing->offset]) * 256,
1229
                        ord($this->serveroutput->body[$this->_parsing->offset + 1]));
1230
                break;
1231
        }
1232
        self::_putDebug("I P P    R E S P O N S E :\n\n");
1233
        self::_putDebug(
1234
            sprintf(
1235
                "IPP version %s%s: %s",
1236
                ord($this->serveroutput->body[$this->_parsing->offset]),
1237
                ord($this->serveroutput->body[$this->_parsing->offset + 1]),
1238
                $this->serveroutput->ipp_version));
1239
        $this->_parsing->offset += 2;
1240
        return;
1241
    }
1242
1243
    protected function _parseStatusCode()
1244
    {
1245
        $status_code =
1246
            (ord($this->serveroutput->body[$this->_parsing->offset]) * 256)
1247
            + ord($this->serveroutput->body[$this->_parsing->offset + 1]);
1248
        $this->serveroutput->status = "NOT PARSED";
1249
        $this->_parsing->offset += 2;
1250
        if (strlen($this->serveroutput->body) < $this->_parsing->offset)
1251
        {
1252
            return false;
1253
        }
1254
        if ($status_code < 0x00FF)
1255
        {
1256
            $this->serveroutput->status = "successfull";
1257
        }
1258
        elseif ($status_code < 0x01FF)
1259
        {
1260
            $this->serveroutput->status = "informational";
1261
        }
1262
        elseif ($status_code < 0x02FF)
1263
        {
1264
            $this->serveroutput->status = "redirection";
1265
        }
1266
        elseif ($status_code < 0x04FF)
1267
        {
1268
            $this->serveroutput->status = "client-error";
1269
        }
1270
        elseif ($status_code < 0x05FF)
1271
        {
1272
            $this->serveroutput->status = "server-error";
1273
        }
1274
        switch ($status_code)
1275
        {
1276
            case 0x0000:
1277
                $this->serveroutput->status = "successfull-ok";
1278
                break;
1279
1280
            case 0x0001:
1281
                $this->serveroutput->status = "successful-ok-ignored-or-substituted-attributes";
1282
                break;
1283
1284
            case 0x002:
1285
                $this->serveroutput->status = "successful-ok-conflicting-attributes";
1286
                break;
1287
1288
            case 0x0400:
1289
                $this->serveroutput->status = "client-error-bad-request";
1290
                break;
1291
1292
            case 0x0401:
1293
                $this->serveroutput->status = "client-error-forbidden";
1294
                break;
1295
1296
            case 0x0402:
1297
                $this->serveroutput->status = "client-error-not-authenticated";
1298
                break;
1299
1300
            case 0x0403:
1301
                $this->serveroutput->status = "client-error-not-authorized";
1302
                break;
1303
1304
            case 0x0404:
1305
                $this->serveroutput->status = "client-error-not-possible";
1306
                break;
1307
1308
            case 0x0405:
1309
                $this->serveroutput->status = "client-error-timeout";
1310
                break;
1311
1312
            case 0x0406:
1313
                $this->serveroutput->status = "client-error-not-found";
1314
                break;
1315
1316
            case 0x0407:
1317
                $this->serveroutput->status = "client-error-gone";
1318
                break;
1319
1320
            case 0x0408:
1321
                $this->serveroutput->status = "client-error-request-entity-too-large";
1322
                break;
1323
1324
            case 0x0409:
1325
                $this->serveroutput->status = "client-error-request-value-too-long";
1326
                break;
1327
1328
            case 0x040A:
1329
                $this->serveroutput->status = "client-error-document-format-not-supported";
1330
                break;
1331
1332
            case 0x040B:
1333
                $this->serveroutput->status = "client-error-attributes-or-values-not-supported";
1334
                break;
1335
1336
            case 0x040C:
1337
                $this->serveroutput->status = "client-error-uri-scheme-not-supported";
1338
                break;
1339
1340
            case 0x040D:
1341
                $this->serveroutput->status = "client-error-charset-not-supported";
1342
                break;
1343
1344
            case 0x040E:
1345
                $this->serveroutput->status = "client-error-conflicting-attributes";
1346
                break;
1347
1348
            case 0x040F:
1349
                $this->serveroutput->status = "client-error-compression-not-supported";
1350
                break;
1351
1352
            case 0x0410:
1353
                $this->serveroutput->status = "client-error-compression-error";
1354
                break;
1355
1356
            case 0x0411:
1357
                $this->serveroutput->status = "client-error-document-format-error";
1358
                break;
1359
1360
            case 0x0412:
1361
                $this->serveroutput->status = "client-error-document-access-error";
1362
                break;
1363
1364
            case 0x0413: // RFC3380
1365
                $this->serveroutput->status = "client-error-attributes-not-settable";
1366
                break;
1367
1368
            case 0x0500:
1369
                $this->serveroutput->status = "server-error-internal-error";
1370
                break;
1371
1372
            case 0x0501:
1373
                $this->serveroutput->status = "server-error-operation-not-supported";
1374
                break;
1375
1376
            case 0x0502:
1377
                $this->serveroutput->status = "server-error-service-unavailable";
1378
                break;
1379
1380
            case 0x0503:
1381
                $this->serveroutput->status = "server-error-version-not-supported";
1382
                break;
1383
1384
            case 0x0504:
1385
                $this->serveroutput->status = "server-error-device-error";
1386
                break;
1387
1388
            case 0x0505:
1389
                $this->serveroutput->status = "server-error-temporary-error";
1390
                break;
1391
1392
            case 0x0506:
1393
                $this->serveroutput->status = "server-error-not-accepting-jobs";
1394
                break;
1395
1396
            case 0x0507:
1397
                $this->serveroutput->status = "server-error-busy";
1398
                break;
1399
1400
            case 0x0508:
1401
                $this->serveroutput->status = "server-error-job-canceled";
1402
                break;
1403
1404
            case 0x0509:
1405
                $this->serveroutput->status = "server-error-multiple-document-jobs-not-supported";
1406
                break;
1407
1408
            default:
1409
                break;
1410
        }
1411
        self::_putDebug(
1412
            sprintf(
1413
                "status-code: %s%s: %s ",
1414
                $this->serveroutput->body[$this->_parsing->offset],
1415
                $this->serveroutput->body[$this->_parsing->offset + 1],
1416
                $this->serveroutput->status),
1417
            4);
1418
        return;
1419
    }
1420
1421
    protected function _parseRequestID()
1422
    {
1423
        $this->serveroutput->request_id =
1424
            self::_interpretInteger(
1425
                substr($this->serveroutput->body, $this->_parsing->offset, 4)
1426
            );
1427
        self::_putDebug("request-id " . $this->serveroutput->request_id, 2);
1428
        $this->_parsing->offset += 4;
1429
        return;
1430
    }
1431
1432
    protected function _interpretInteger($value)
1433
    {
1434
        // they are _signed_ integers
1435
        $value_parsed = 0;
1436
        for ($i = strlen($value); $i > 0; $i--)
1437
        {
1438
            $value_parsed +=
1439
                (
1440
                    (1 << (($i - 1) * 8))
1441
                        *
1442
                        ord($value[strlen($value) - $i])
1443
                );
1444
        }
1445
        if ($value_parsed >= 2147483648)
1446
        {
1447
            $value_parsed -= 4294967296;
1448
        }
1449
        return $value_parsed;
1450
    }
1451
1452
    protected function _parseResponse()
1453
    {
1454
    }
1455
1456
    //
1457
    // REQUEST BUILDING
1458
    //
1459
    protected function _stringJob()
1460
    {
1461
        if (!isset($this->setup->charset)) {
1462
            self::setCharset();
1463
        }
1464
        if (!isset($this->setup->datatype)) {
1465
            self::setBinary();
1466
        }
1467
        if (!isset($this->setup->uri))
1468
        {
1469
            $this->getPrinters();
1470
            unset($this->jobs[count($this->jobs) - 1]);
1471
            unset($this->jobs_uri[count($this->jobs_uri) - 1]);
1472
            unset($this->status[count($this->status) - 1]);
1473
            if (array_key_exists(0, $this->available_printers))
1474
            {
1475
                self::setPrinterURI($this->available_printers[0]);
1476
            }
1477
            else
1478
            {
1479
                trigger_error(
1480
                    _("_stringJob: Printer URI is not set: die"),
1481
                    E_USER_WARNING);
1482
                self::_putDebug(_("_stringJob: Printer URI is not set: die"), 4);
1483
                self::_errorLog(" Printer URI is not set, die", 2);
1484
                return FALSE;
1485
            }
1486
        }
1487
        if (!isset($this->setup->copies)) {
1488
            self::setCopies(1);
1489
        }
1490
        if (!isset($this->setup->language)) {
1491
            self::setLanguage('en_us');
1492
        }
1493
        if (!isset($this->setup->mime_media_type)) {
1494
            self::setMimeMediaType();
1495
        }
1496
        if (!isset($this->setup->jobname)) {
1497
            self::setJobName();
1498
        }
1499
        unset($this->setup->jobname);
1500
        if (!isset($this->meta->username)) {
1501
            self::setUserName();
1502
        }
1503
        if (!isset($this->meta->fidelity)) {
1504
            $this->meta->fidelity = '';
1505
        }
1506
        if (!isset($this->meta->document_name)) {
1507
            $this->meta->document_name = '';
1508
        }
1509
        if (!isset($this->meta->sides)) {
1510
            $this->meta->sides = '';
1511
        }
1512
        if (!isset($this->meta->page_ranges)) {
1513
            $this->meta->page_ranges = '';
1514
        }
1515
        $jobattributes = '';
1516
        $operationattributes = '';
1517
        $printerattributes = '';
1518
        $this->_buildValues($operationattributes, $jobattributes, $printerattributes);
1519
        self::_setOperationId();
1520
        if (!isset($this->error_generation->request_body_malformed))
1521
        {
1522
            $this->error_generation->request_body_malformed = "";
1523
        }
1524
        $this->stringjob = chr(0x01) . chr(0x01) // 1.1  | version-number
1525
            . chr(0x00) . chr(0x02) // Print-Job | operation-id
1526
            . $this->meta->operation_id //           request-id
1527
            . chr(0x01) // start operation-attributes | operation-attributes-tag
1528
            . $this->meta->charset
1529
            . $this->meta->language
1530
            . $this->meta->printer_uri
1531
            . $this->meta->username
1532
            . $this->meta->jobname
1533
            . $this->meta->fidelity
1534
            . $this->meta->document_name
1535
            . $this->meta->mime_media_type
1536
            . $operationattributes;
1537
        if ($this->meta->copies || $this->meta->sides || $this->meta->page_ranges || !empty($jobattributes))
1538
        {
1539
            $this->stringjob .=
1540
                chr(0x02) // start job-attributes | job-attributes-tag
1541
                    . $this->meta->copies
1542
                    . $this->meta->sides
1543
                    . $this->meta->page_ranges
1544
                    . $jobattributes;
1545
        }
1546
        $this->stringjob .= chr(0x03); // end-of-attributes | end-of-attributes-tag
1547
        self::_putDebug(
1548
            sprintf(
1549
                _("String sent to the server is: %s"),
1550
                $this->stringjob)
1551
            );
1552
        return TRUE;
1553
    }
1554
1555
    protected function _buildValues(&$operationattributes, &$jobattributes, &$printerattributes)
1556
    {
1557
        $operationattributes = '';
1558
        foreach($this->operation_tags as $key => $values)
1559
        {
1560
            $item = 0;
1561
            if (array_key_exists('value', $values))
1562
            {
1563
                foreach($values['value'] as $item_value)
1564
                {
1565
                    if ($item == 0)
1566
                    {
1567
                        $operationattributes .=
1568
                            $values['systag']
1569
                                . self::_giveMeStringLength($key)
1570
                                . $key
1571
                                . self::_giveMeStringLength($item_value)
1572
                                . $item_value;
1573
                    }
1574
                    else
1575
                    {
1576
                        $operationattributes .=
1577
                            $values['systag']
1578
                                . self::_giveMeStringLength('')
1579
                                . self::_giveMeStringLength($item_value)
1580
                                . $item_value;
1581
                    }
1582
                    $item++;
1583
                }
1584
            }
1585
        }
1586
        $jobattributes = '';
1587
        foreach($this->job_tags as $key => $values)
1588
        {
1589
            $item = 0;
1590
            if (array_key_exists('value', $values))
1591
            {
1592
                foreach($values['value'] as $item_value)
1593
                {
1594
                    if ($item == 0)
1595
                    {
1596
                        $jobattributes .=
1597
                            $values['systag']
1598
                                . self::_giveMeStringLength($key)
1599
                                . $key
1600
                                . self::_giveMeStringLength($item_value)
1601
                                . $item_value;
1602
                    }
1603
                    else
1604
                    {
1605
                        $jobattributes .=
1606
                            $values['systag']
1607
                                . self::_giveMeStringLength('')
1608
                                . self::_giveMeStringLength($item_value)
1609
                                . $item_value;
1610
                    }
1611
                    $item++;
1612
                }
1613
            }
1614
        }
1615
        $printerattributes = '';
1616
        foreach($this->printer_tags as $key => $values)
1617
        {
1618
            $item = 0;
1619
            if (array_key_exists('value', $values))
1620
            {
1621
                foreach($values['value'] as $item_value)
1622
                {
1623
                    if ($item == 0)
1624
                    {
1625
                        $printerattributes .=
1626
                            $values['systag']
1627
                                . self::_giveMeStringLength($key)
1628
                                . $key
1629
                                . self::_giveMeStringLength($item_value)
1630
                                . $item_value;
1631
                    }
1632
                    else
1633
                    {
1634
                        $printerattributes .=
1635
                            $values['systag']
1636
                                . self::_giveMeStringLength('')
1637
                                . self::_giveMeStringLength($item_value)
1638
                                . $item_value;
1639
                    }
1640
                    $item++;
1641
                }
1642
            }
1643
        }
1644
        reset($this->job_tags);
1645
        reset($this->operation_tags);
1646
        reset($this->printer_tags);
1647
        return true;
1648
    }
1649
1650
    protected function _giveMeStringLength($string)
1651
    {
1652
        $length = strlen($string);
1653
        if ($length > ((0xFF << 8) + 0xFF))
1654
        {
1655
            $errmsg = sprintf(
1656
                _('max string length for an ipp meta-information = %d, while here %d'),
1657
                ((0xFF << 8) + 0xFF), $length);
1658
1659
            if ($this->with_exceptions)
1660
            {
1661
                throw new ippException($errmsg);
1662
            }
1663
            else
1664
            {
1665
                trigger_error($errmsg, E_USER_ERROR);
1666
            }
1667
        }
1668
        $int1 = $length & 0xFF;
1669
        $length -= $int1;
1670
        $length = $length >> 8;
1671
        $int2 = $length & 0xFF;
1672
        return chr($int2) . chr($int1);
1673
    }
1674
1675
    protected function _enumBuild($tag, $value)
1676
    {
1677
        switch ($tag)
1678
        {
1679
            case "orientation-requested":
1680
                switch ($value)
1681
                {
1682
                    case 'portrait':
1683
                        $value = chr(3);
1684
                        break;
1685
1686
                    case 'landscape':
1687
                        $value = chr(4);
1688
                        break;
1689
1690
                    case 'reverse-landscape':
1691
                        $value = chr(5);
1692
                        break;
1693
1694
                    case 'reverse-portrait':
1695
                        $value = chr(6);
1696
                        break;
1697
                }
1698
                break;
1699
1700
            case "print-quality":
1701
                switch ($value)
1702
                {
1703
                    case 'draft':
1704
                        $value = chr(3);
1705
                        break;
1706
1707
                    case 'normal':
1708
                        $value = chr(4);
1709
                        break;
1710
1711
                    case 'high':
1712
                        $value = chr(5);
1713
                        break;
1714
                }
1715
                break;
1716
1717
            case "finishing":
1718
                switch ($value)
1719
                {
1720
                    case 'none':
1721
                        $value = chr(3);
1722
                        break;
1723
1724
                    case 'staple':
1725
                        $value = chr(4);
1726
                        break;
1727
1728
                    case 'punch':
1729
                        $value = chr(5);
1730
                        break;
1731
1732
                    case 'cover':
1733
                        $value = chr(6);
1734
                        break;
1735
1736
                    case 'bind':
1737
                        $value = chr(7);
1738
                        break;
1739
1740
                    case 'saddle-stitch':
1741
                        $value = chr(8);
1742
                        break;
1743
1744
                    case 'edge-stitch':
1745
                        $value = chr(9);
1746
                        break;
1747
1748
                    case 'staple-top-left':
1749
                        $value = chr(20);
1750
                        break;
1751
1752
                    case 'staple-bottom-left':
1753
                        $value = chr(21);
1754
                        break;
1755
1756
                    case 'staple-top-right':
1757
                        $value = chr(22);
1758
                        break;
1759
1760
                    case 'staple-bottom-right':
1761
                        $value = chr(23);
1762
                        break;
1763
1764
                    case 'edge-stitch-left':
1765
                        $value = chr(24);
1766
                        break;
1767
1768
                    case 'edge-stitch-top':
1769
                        $value = chr(25);
1770
                        break;
1771
1772
                    case 'edge-stitch-right':
1773
                        $value = chr(26);
1774
                        break;
1775
1776
                    case 'edge-stitch-bottom':
1777
                        $value = chr(27);
1778
                        break;
1779
1780
                    case 'staple-dual-left':
1781
                        $value = chr(28);
1782
                        break;
1783
1784
                    case 'staple-dual-top':
1785
                        $value = chr(29);
1786
                        break;
1787
1788
                    case 'staple-dual-right':
1789
                        $value = chr(30);
1790
                        break;
1791
1792
                    case 'staple-dual-bottom':
1793
                        $value = chr(31);
1794
                        break;
1795
                }
1796
                break;
1797
        }
1798
        $prepend = '';
1799
        while ((strlen($value) + strlen($prepend)) < 4)
1800
        {
1801
            $prepend .= chr(0);
1802
        }
1803
        return $prepend . $value;
1804
    }
1805
1806
    protected function _integerBuild($value)
1807
    {
1808
        if ($value >= 2147483647 || $value < - 2147483648)
1809
        {
1810
            trigger_error(
1811
                _("Values must be between -2147483648 and 2147483647: assuming '0'"), E_USER_WARNING);
1812
            return chr(0x00) . chr(0x00) . chr(0x00) . chr(0x00);
1813
        }
1814
        $initial_value = $value;
1815
        $int1 = $value & 0xFF;
1816
        $value -= $int1;
1817
        $value = $value >> 8;
1818
        $int2 = $value & 0xFF;
1819
        $value -= $int2;
1820
        $value = $value >> 8;
1821
        $int3 = $value & 0xFF;
1822
        $value -= $int3;
1823
        $value = $value >> 8;
1824
        $int4 = $value & 0xFF; //64bits
1825
        if ($initial_value < 0) {
1826
            $int4 = chr($int4) | chr(0x80);
1827
        }
1828
        else {
1829
            $int4 = chr($int4);
1830
        }
1831
        $value = $int4 . chr($int3) . chr($int2) . chr($int1);
1832
        return $value;
1833
    }
1834
1835
    protected function _rangeOfIntegerBuild($integers)
1836
    {
1837
        // $integers = split(":", $integers);
1838
        $integers = preg_split("#:#", $integers);
1839
        for ($i = 0; $i < 2; $i++) {
1840
            $outvalue[$i] = self::_integerBuild($integers[$i]);
1841
        }
1842
        return $outvalue[0] . $outvalue[1];
1843
    }
1844
1845
    protected function _setJobAttribute($attribute, $value)
1846
    {
1847
        //used by setAttribute
1848
        $tag_type = $this->job_tags[$attribute]['tag'];
1849
        switch ($tag_type)
1850
        {
1851
            case 'integer':
1852
                $this->job_tags[$attribute]['value'][] = self::_integerBuild($value);
1853
                break;
1854
1855
            case 'boolean':
1856
            case 'nameWithoutLanguage':
1857
            case 'nameWithLanguage':
1858
            case 'textWithoutLanguage':
1859
            case 'textWithLanguage':
1860
            case 'keyword':
1861
            case 'naturalLanguage':
1862
                $this->job_tags[$attribute]['value'][] = $value;
1863
                break;
1864
1865
            case 'enum':
1866
                $value = $this->_enumBuild($attribute, $value); // may be overwritten by children
1867
                $this->job_tags[$attribute]['value'][] = $value;
1868
                break;
1869
1870
            case 'rangeOfInteger':
1871
                // $value have to be: INT1:INT2 , eg 100:1000
1872
                $this->job_tags[$attribute]['value'][] = self::_rangeOfIntegerBuild($value);
1873
                break;
1874
1875
            case 'resolution':
1876
                if (preg_match("#dpi#", $value)) {
1877
                    $unit = chr(0x3);
1878
                }
1879
                if (preg_match("#dpc#", $value)) {
1880
                    $unit = chr(0x4);
1881
                }
1882
                $search = array(
1883
                    "#(dpi|dpc)#",
1884
                    '#(x|-)#'
1885
                );
1886
                $replace = array(
1887
                    "",
1888
                    ":"
1889
                );
1890
                $value = self::_rangeOfIntegerBuild(preg_replace($search, $replace, $value)) . $unit;
1891
                $this->job_tags[$attribute]['value'][] = $value;
1892
                break;
1893
1894
            default:
1895
                trigger_error(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), E_USER_NOTICE);
1896
                self::_putDebug(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), 2);
1897
                self::_errorLog(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), 2);
1898
                return FALSE;
1899
                break;
1900
        }
1901
        $this->job_tags[$attribute]['systag'] = $this->tags_types[$tag_type]['tag'];
1902
    }
1903
1904
    protected function _setOperationAttribute($attribute, $value)
1905
    {
1906
        //used by setAttribute
1907
        $tag_type = $this->operation_tags[$attribute]['tag'];
1908
        switch ($tag_type)
1909
        {
1910
            case 'integer':
1911
                $this->operation_tags[$attribute]['value'][] = self::_integerBuild($value);
1912
                break;
1913
1914
            case 'keyword':
1915
            case 'naturalLanguage':
1916
                $this->operation_tags[$attribute]['value'][] = $value;
1917
                break;
1918
1919
            default:
1920
                trigger_error(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), E_USER_NOTICE);
1921
                self::_putDebug(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), 2);
1922
                self::_errorLog(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), 2);
1923
                return FALSE;
1924
                break;
1925
        }
1926
        $this->operation_tags[$attribute]['systag'] = $this->tags_types[$tag_type]['tag'];
1927
    }
1928
1929
    protected function _setPrinterAttribute($attribute, $value)
1930
    {
1931
        //used by setAttribute
1932
        $tag_type = $this->printer_tags[$attribute]['tag'];
1933
        switch ($tag_type)
1934
        {
1935
            case 'integer':
1936
                $this->printer_tags[$attribute]['value'][] = self::_integerBuild($value);
1937
                break;
1938
1939
            case 'keyword':
1940
            case 'naturalLanguage':
1941
                $this->printer_tags[$attribute]['value'][] = $value;
1942
                break;
1943
1944
            default:
1945
                trigger_error(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), E_USER_NOTICE);
1946
                self::_putDebug(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), 2);
1947
                self::_errorLog(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), 2);
1948
                return FALSE;
1949
                break;
1950
        }
1951
        $this->printer_tags[$attribute]['systag'] = $this->tags_types[$tag_type]['tag'];
1952
    }
1953
1954
    //
1955
    // DEBUGGING
1956
    //
1957
    protected function _putDebug($string, $level = 1)
1958
    {
1959
        if ($level === false) {
1960
            return;
1961
        }
1962
1963
        if ($level < $this->debug_level) {
1964
            return;
1965
        }
1966
1967
        $this->debug[$this->debug_count] = substr($string, 0, 1024);
1968
        $this->debug_count++;
1969
        //$this->debug .= substr($string,0,1024);
1970
    }
1971
1972
    //
1973
    // LOGGING
1974
    //
1975
    protected function _errorLog($string_to_log, $level)
1976
    {
1977
        if ($level > $this->log_level) {
1978
            return;
1979
        }
1980
1981
        $string = sprintf('%s : %s:%s user %s : %s', basename($_SERVER['PHP_SELF']), $this->host, $this->port, $this->requesting_user, $string_to_log);
1982
1983
        if ($this->log_type == 0)
1984
        {
1985
            error_log($string);
1986
            return;
1987
        }
1988
1989
        $string = sprintf("%s %s Host %s:%s user %s : %s\n", date('M d H:i:s'), basename($_SERVER['PHP_SELF']), $this->host, $this->port, $this->requesting_user, $string_to_log);
1990
        error_log($string, $this->log_type, $this->log_destination);
1991
        return;
1992
    }
1993
}
1994