Passed
Push — main ( f1540e...02d90d )
by Rafael
45:15
created

BasicIPP::_setOperationAttribute()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 16
nc 4
nop 2
dl 0
loc 22
rs 9.7333
c 0
b 0
f 0
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 = [
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 = [];
83
    public $jobs_uri = [];
84
    public $status = [];
85
    public $response_completed = [];
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 = [];
92
    public $printer_map = [];
93
    public $printers_uri = [];
94
    public $debug = [];
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 = []; //RFC3382
134
    protected $collection_depth = -1; //RFC3382
135
    protected $end_collection = false; //RFC3382
136
    protected $collection_nbr = []; //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
            $tz = @date_default_timezone_get();
145
        }
146
147
        date_default_timezone_set($tz);
148
        $this->meta = new \stdClass();
149
        $this->setup = new \stdClass();
150
        $this->values = new \stdClass();
151
        $this->serveroutput = new \stdClass();
152
        $this->error_generation = new \stdClass();
153
        $this->_parsing = new \stdClass();
154
        self::_initTags();
155
    }
156
157
    protected function _initTags()
158
    {
159
        $this->tags_types = [
160
            "unsupported" => [
161
                "tag" => chr(0x10),
162
                "build" => "",
163
            ],
164
            "reserved" => [
165
                "tag" => chr(0x11),
166
                "build" => "",
167
            ],
168
            "unknown" => [
169
                "tag" => chr(0x12),
170
                "build" => "",
171
            ],
172
            "no-value" => [
173
                "tag" => chr(0x13),
174
                "build" => "no_value",
175
            ],
176
            "integer" => [
177
                "tag" => chr(0x21),
178
                "build" => "integer",
179
            ],
180
            "boolean" => [
181
                "tag" => chr(0x22),
182
                "build" => "boolean",
183
            ],
184
            "enum" => [
185
                "tag" => chr(0x23),
186
                "build" => "enum",
187
            ],
188
            "octetString" => [
189
                "tag" => chr(0x30),
190
                "build" => "octet_string",
191
            ],
192
            "datetime" => [
193
                "tag" => chr(0x31),
194
                "build" => "datetime",
195
            ],
196
            "resolution" => [
197
                "tag" => chr(0x32),
198
                "build" => "resolution",
199
            ],
200
            "rangeOfInteger" => [
201
                "tag" => chr(0x33),
202
                "build" => "range_of_integers",
203
            ],
204
            "textWithLanguage" => [
205
                "tag" => chr(0x35),
206
                "build" => "string",
207
            ],
208
            "nameWithLanguage" => [
209
                "tag" => chr(0x36),
210
                "build" => "string",
211
            ],
212
            /*
213
            "text" => array ("tag" => chr(0x40),
214
            "build" => "string"),
215
            "text string" => array ("tag" => chr(0x40),
216
            "build" => "string"),
217
            */
218
            "textWithoutLanguage" => [
219
                "tag" => chr(0x41),
220
                "build" => "string",
221
            ],
222
            "nameWithoutLanguage" => [
223
                "tag" => chr(0x42),
224
                "buid" => "string",
225
            ],
226
            "keyword" => [
227
                "tag" => chr(0x44),
228
                "build" => "string",
229
            ],
230
            "uri" => [
231
                "tag" => chr(0x45),
232
                "build" => "string",
233
            ],
234
            "uriScheme" => [
235
                "tag" => chr(0x46),
236
                "build" => "string",
237
            ],
238
            "charset" => [
239
                "tag" => chr(0x47),
240
                "build" => "string",
241
            ],
242
            "naturalLanguage" => [
243
                "tag" => chr(0x48),
244
                "build" => "string",
245
            ],
246
            "mimeMediaType" => [
247
                "tag" => chr(0x49),
248
                "build" => "string",
249
            ],
250
            "extendedAttributes" => [
251
                "tag" => chr(0x7F),
252
                "build" => "extended",
253
            ],
254
        ];
255
        $this->operation_tags = [
256
            "compression" => [
257
                "tag" => "keyword",
258
            ],
259
            "document-natural-language" => [
260
                "tag" => "naturalLanguage",
261
            ],
262
            "job-k-octets" => [
263
                "tag" => "integer",
264
            ],
265
            "job-impressions" => [
266
                "tag" => "integer",
267
            ],
268
            "job-media-sheets" => [
269
                "tag" => "integer",
270
            ],
271
        ];
272
        $this->job_tags = [
273
            "job-priority" => [
274
                "tag" => "integer",
275
            ],
276
            "job-hold-until" => [
277
                "tag" => "keyword",
278
            ],
279
            "job-sheets" => [
280
                "tag" => "keyword",
281
            ], //banner page
282
            "multiple-document-handling" => [
283
                "tag" => "keyword",
284
            ],
285
            //"copies" => array("tag" => "integer"),
286
            "finishings" => [
287
                "tag" => "enum",
288
            ],
289
            //"page-ranges" => array("tag" => "rangeOfInteger"), // has its own function
290
            //"sides" => array("tag" => "keyword"), // has its own function
291
            "number-up" => [
292
                "tag" => "integer",
293
            ],
294
            "orientation-requested" => [
295
                "tag" => "enum",
296
            ],
297
            "media" => [
298
                "tag" => "keyword",
299
            ],
300
            "printer-resolution" => [
301
                "tag" => "resolution",
302
            ],
303
            "print-quality" => [
304
                "tag" => "enum",
305
            ],
306
            "job-message-from-operator" => [
307
                "tag" => "textWithoutLanguage",
308
            ],
309
        ];
310
        $this->printer_tags = [
311
            "requested-attributes" => [
312
                "tag" => "keyword",
313
            ],
314
        ];
315
    }
316
317
    public function setPort($port = '631')
318
    {
319
        $this->port = $port;
320
        self::_putDebug("Port is " . $this->port, 2);
321
    }
322
323
    protected function _putDebug($string, $level = 1)
324
    {
325
        if ($level === false) {
326
            return;
327
        }
328
329
        if ($level < $this->debug_level) {
330
            return;
331
        }
332
333
        $this->debug[$this->debug_count] = substr($string, 0, 1024);
334
        $this->debug_count++;
335
        //$this->debug .= substr($string,0,1024);
336
    }
337
338
    public function setUnix($socket = '/var/run/cups/cups.sock')
339
    {
340
        $this->host = $socket;
341
        $this->unix = true;
342
        self::_putDebug("Host is " . $this->host, 2);
343
    }
344
345
    public function setHost($host = 'localhost')
346
    {
347
        $this->host = $host;
348
        $this->unix = false;
349
        self::_putDebug("Host is " . $this->host, 2);
350
    }
351
352
    public function setTimeout($timeout)
353
    {
354
        $this->timeout = $timeout;
355
    }
356
357
    public function setData($data)
358
    {
359
        $this->data = $data;
360
        self::_putDebug("Data set", 2);
361
    }
362
363
    public function setRawText()
364
    {
365
        $this->setup->datatype = 'TEXT';
366
        $this->meta->mime_media_type = "";
367
        $this->setup->mime_media_type = 1;
368
        $this->datahead = chr(0x16);
369
        if (is_readable($this->data)) {
370
            //It's a filename.  Open and stream.
371
            $data = fopen($this->data, "rb");
372
            while (!feof($data)) {
373
                $output = fread($data, 8192);
374
            }
375
        } else {
376
            $output = $this->data;
377
        }
378
        if (substr($output, -1, 1) != chr(0x0c)) {
379
            if (!isset($this->setup->noFormFeed)) {
380
                $this->datatail = chr(0x0c);
381
            }
382
        }
383
        self::_putDebug(_("Forcing data to be interpreted as RAW TEXT"), 2);
384
    }
385
386
    public function setFormFeed()
387
    {
388
        $this->datatail = "\r\n" . chr(0x0c);
389
        unset($this->setup->noFormFeed);
390
    }
391
392
    public function unsetFormFeed()
393
    {
394
        $this->datatail = '';
395
        $this->setup->noFormFeed = 1;
396
    }
397
398
    public function setDocumentName($document_name = "")
399
    {
400
        $this->meta->document_name = "";
401
        if (!$document_name) {
402
            return true;
403
        }
404
        $document_name = substr($document_name, 0, 1023);
405
        $length = strlen($document_name);
406
        $length = chr($length);
407
        while (strlen($length) < 2) {
408
            $length = chr(0x00) . $length;
409
        }
410
        self::_putDebug(sprintf(_("document name: %s"), $document_name), 2);
411
        $this->meta->document_name = chr(0x41) // textWithoutLanguage tag
412
            . chr(0x00) . chr(0x0d) // name-length
413
            . "document-name" // mimeMediaType
414
            . self::_giveMeStringLength($document_name) . $document_name; // value
415
    }
416
417
    public function setAuthentification($username, $password)
418
    {
419
        self::setAuthentication($username, $password);
420
    }
421
422
    public function setAuthentication($username, $password)
423
    {
424
        $this->password = $password;
425
        $this->username = $username;
426
        self::_putDebug(_("Setting password"), 2);
427
        $this->setup->password = 1;
428
    }
429
430
    public function setSides($sides = 2)
431
    {
432
        $this->meta->sides = '';
433
        if (!$sides) {
434
            return true;
435
        }
436
437
        switch ($sides) {
438
            case 1:
439
                $sides = "one-sided";
440
                break;
441
442
            case 2:
443
                $sides = "two-sided-long-edge";
444
                break;
445
446
            case "2CE":
447
                $sides = "two-sided-short-edge";
448
                break;
449
        }
450
451
        $this->meta->sides = chr(0x44) // keyword type | value-tag
452
            . chr(0x00) . chr(0x05) //        name-length
453
            . "sides" // sides |             name
454
            . self::_giveMeStringLength($sides) //               value-length
455
            . $sides; // one-sided |          value
456
        self::_putDebug(sprintf(_("Sides value set to %s"), $sides), 2);
457
    }
458
459
    // setDocumentFormat alias for backward compatibility
460
461
    public function setFidelity()
462
    {
463
        // whether the server can't replace any attributes
464
        // (eg, 2 sided print is not possible,
465
        // so print one sided) and DO NOT THE JOB.
466
        $this->meta->fidelity = chr(0x22) // boolean type  |  value-tag
467
            . chr(0x00) . chr(0x16) //                  name-length
468
            . "ipp-attribute-fidelity" // ipp-attribute-fidelity | name
469
            . chr(0x00) . chr(0x01) //  value-length
470
            . chr(0x01); //  true | value
471
        self::_putDebug(_("Fidelity attribute is set (paranoid mode)"), 3);
472
    }
473
474
    public function unsetFidelity()
475
    {
476
        // whether the server can replace any attributes
477
        // (eg, 2 sided print is not possible,
478
        // so print one sided) and DO THE JOB.
479
        $this->meta->fidelity = chr(0x22) //  boolean type | value-tag
480
            . chr(0x00) . chr(0x16) //        name-length
481
            . "ipp-attribute-fidelity" // ipp-attribute-fidelity | name
482
            . chr(0x00) . chr(0x01) //               value-length
483
            . chr(0x00); // false |                   value
484
        self::_putDebug(_("Fidelity attribute is unset"), 2);
485
    }
486
487
    public function setMessage($message = '')
488
    {
489
        $this->meta->message = '';
490
        if (!$message) {
491
            return true;
492
        }
493
        $this->meta->message =
494
            chr(0x41) // attribute type = textWithoutLanguage
495
            . chr(0x00)
496
            . chr(0x07)
497
            . "message"
498
            . self::_giveMeStringLength(substr($message, 0, 127))
499
            . substr($message, 0, 127);
500
        self::_putDebug(sprintf(_('Setting message to "%s"'), $message), 2);
501
    }
502
503
    public function setPageRanges($page_ranges)
504
    {
505
        // $pages_ranges = string:  "1:5 10:25 40:52 ..."
506
        // to unset, specify an empty string.
507
        $this->meta->page_range = '';
508
        if (!$page_ranges) {
509
            return true;
510
        }
511
        $page_ranges = trim(str_replace("-", ":", $page_ranges));
512
        $first = true;
513
        // $page_ranges = split(' ', $page_ranges);
514
        $page_ranges = preg_split('# #', $page_ranges);
515
        foreach ($page_ranges as $page_range) {
516
            $value = self::_rangeOfIntegerBuild($page_range);
517
            if ($first) {
518
                $this->meta->page_ranges .=
519
                    $this->tags_types['rangeOfInteger']['tag']
520
                    . self::_giveMeStringLength('page-ranges')
521
                    . 'page-ranges'
522
                    . self::_giveMeStringLength($value)
523
                    . $value;
524
            } else {
525
                $this->meta->page_ranges .=
526
                    $this->tags_types['rangeOfInteger']['tag']
527
                    . self::_giveMeStringLength('')
528
                    . self::_giveMeStringLength($value)
529
                    . $value;
530
                $first = false;
531
            }
532
        }
533
    }
534
535
    protected function _rangeOfIntegerBuild($integers)
536
    {
537
        // $integers = split(":", $integers);
538
        $integers = preg_split("#:#", $integers);
539
        for ($i = 0; $i < 2; $i++) {
540
            $outvalue[$i] = self::_integerBuild($integers[$i]);
541
        }
542
        return $outvalue[0] . $outvalue[1];
543
    }
544
545
    public function setAttribute($attribute, $values)
546
    {
547
        $operation_attributes_tags = array_keys($this->operation_tags);
548
        $job_attributes_tags = array_keys($this->job_tags);
549
        $printer_attributes_tags = array_keys($this->printer_tags);
550
        self::unsetAttribute($attribute);
551
        if (in_array($attribute, $operation_attributes_tags)) {
552
            if (!is_array($values)) {
553
                self::_setOperationAttribute($attribute, $values);
554
            } else {
555
                foreach ($values as $value) {
556
                    self::_setOperationAttribute($attribute, $value);
557
                }
558
            }
559
        } elseif (in_array($attribute, $job_attributes_tags)) {
560
            if (!is_array($values)) {
561
                self::_setJobAttribute($attribute, $values);
562
            } else {
563
                foreach ($values as $value) {
564
                    self::_setJobAttribute($attribute, $value);
565
                }
566
            }
567
        } elseif (in_array($attribute, $printer_attributes_tags)) {
568
            if (!is_array($values)) {
569
                self::_setPrinterAttribute($attribute, $values);
570
            } else {
571
                foreach ($values as $value) {
572
                    self::_setPrinterAttribute($attribute, $value);
573
                }
574
            }
575
        } else {
576
            trigger_error(
577
                sprintf(
578
                    _('SetAttribute: Tag "%s" is not a printer or a job attribute'),
579
                    $attribute), E_USER_NOTICE);
580
            self::_putDebug(
581
                sprintf(
582
                    _('SetAttribute: Tag "%s" is not a printer or a job attribute'),
583
                    $attribute), 3);
584
            self::_errorLog(
585
                sprintf(
586
                    _('SetAttribute: Tag "%s" is not a printer or a job attribute'),
587
                    $attribute), 2);
588
            return false;
589
        }
590
    }
591
592
    public function unsetAttribute($attribute)
593
    {
594
        $operation_attributes_tags = array_keys($this->operation_tags);
595
        $job_attributes_tags = array_keys($this->job_tags);
596
        $printer_attributes_tags = array_keys($this->printer_tags);
597
        if (in_array($attribute, $operation_attributes_tags)) {
598
            unset(
599
                $this->operation_tags[$attribute]['value'],
600
                $this->operation_tags[$attribute]['systag']
601
            );
602
        } elseif (in_array($attribute, $job_attributes_tags)) {
603
            unset(
604
                $this->job_tags[$attribute]['value'],
605
                $this->job_tags[$attribute]['systag']
606
            );
607
        } elseif (in_array($attribute, $printer_attributes_tags)) {
608
            unset(
609
                $this->printer_tags[$attribute]['value'],
610
                $this->printer_tags[$attribute]['systag']
611
            );
612
        } else {
613
            trigger_error(
614
                sprintf(
615
                    _('unsetAttribute: Tag "%s" is not a printer or a job attribute'),
616
                    $attribute), E_USER_NOTICE);
617
            self::_putDebug(
618
                sprintf(
619
                    _('unsetAttribute: Tag "%s" is not a printer or a job attribute'),
620
                    $attribute), 3);
621
            self::_errorLog(
622
                sprintf(
623
                    _('unsetAttribute: Tag "%s" is not a printer or a job attribute'),
624
                    $attribute), 2);
625
            return false;
626
        }
627
        return true;
628
    }
629
630
    protected function _errorLog($string_to_log, $level)
631
    {
632
        if ($level > $this->log_level) {
633
            return;
634
        }
635
636
        $string = sprintf('%s : %s:%s user %s : %s', basename($_SERVER['PHP_SELF']), $this->host, $this->port, $this->requesting_user, $string_to_log);
637
638
        if ($this->log_type == 0) {
639
            error_log($string);
640
            return;
641
        }
642
643
        $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);
644
        error_log($string, $this->log_type, $this->log_destination);
645
        return;
646
    }
647
648
    protected function _setOperationAttribute($attribute, $value)
649
    {
650
        //used by setAttribute
651
        $tag_type = $this->operation_tags[$attribute]['tag'];
652
        switch ($tag_type) {
653
            case 'integer':
654
                $this->operation_tags[$attribute]['value'][] = self::_integerBuild($value);
655
                break;
656
657
            case 'keyword':
658
            case 'naturalLanguage':
659
                $this->operation_tags[$attribute]['value'][] = $value;
660
                break;
661
662
            default:
663
                trigger_error(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), E_USER_NOTICE);
664
                self::_putDebug(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), 2);
665
                self::_errorLog(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), 2);
666
                return false;
667
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
668
        }
669
        $this->operation_tags[$attribute]['systag'] = $this->tags_types[$tag_type]['tag'];
670
    }
671
672
    protected function _setJobAttribute($attribute, $value)
673
    {
674
        //used by setAttribute
675
        $tag_type = $this->job_tags[$attribute]['tag'];
676
        switch ($tag_type) {
677
            case 'integer':
678
                $this->job_tags[$attribute]['value'][] = self::_integerBuild($value);
679
                break;
680
681
            case 'boolean':
682
            case 'nameWithoutLanguage':
683
            case 'nameWithLanguage':
684
            case 'textWithoutLanguage':
685
            case 'textWithLanguage':
686
            case 'keyword':
687
            case 'naturalLanguage':
688
                $this->job_tags[$attribute]['value'][] = $value;
689
                break;
690
691
            case 'enum':
692
                $value = $this->_enumBuild($attribute, $value); // may be overwritten by children
693
                $this->job_tags[$attribute]['value'][] = $value;
694
                break;
695
696
            case 'rangeOfInteger':
697
                // $value have to be: INT1:INT2 , eg 100:1000
698
                $this->job_tags[$attribute]['value'][] = self::_rangeOfIntegerBuild($value);
699
                break;
700
701
            case 'resolution':
702
                if (preg_match("#dpi#", $value)) {
703
                    $unit = chr(0x3);
704
                }
705
                if (preg_match("#dpc#", $value)) {
706
                    $unit = chr(0x4);
707
                }
708
                $search = [
709
                    "#(dpi|dpc)#",
710
                    '#(x|-)#',
711
                ];
712
                $replace = [
713
                    "",
714
                    ":",
715
                ];
716
                $value = self::_rangeOfIntegerBuild(preg_replace($search, $replace, $value)) . $unit;
717
                $this->job_tags[$attribute]['value'][] = $value;
718
                break;
719
720
            default:
721
                trigger_error(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), E_USER_NOTICE);
722
                self::_putDebug(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), 2);
723
                self::_errorLog(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), 2);
724
                return false;
725
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
726
        }
727
        $this->job_tags[$attribute]['systag'] = $this->tags_types[$tag_type]['tag'];
728
    }
729
730
    protected function _enumBuild($tag, $value)
731
    {
732
        switch ($tag) {
733
            case "orientation-requested":
734
                switch ($value) {
735
                    case 'portrait':
736
                        $value = chr(3);
737
                        break;
738
739
                    case 'landscape':
740
                        $value = chr(4);
741
                        break;
742
743
                    case 'reverse-landscape':
744
                        $value = chr(5);
745
                        break;
746
747
                    case 'reverse-portrait':
748
                        $value = chr(6);
749
                        break;
750
                }
751
                break;
752
753
            case "print-quality":
754
                switch ($value) {
755
                    case 'draft':
756
                        $value = chr(3);
757
                        break;
758
759
                    case 'normal':
760
                        $value = chr(4);
761
                        break;
762
763
                    case 'high':
764
                        $value = chr(5);
765
                        break;
766
                }
767
                break;
768
769
            case "finishing":
770
                switch ($value) {
771
                    case 'none':
772
                        $value = chr(3);
773
                        break;
774
775
                    case 'staple':
776
                        $value = chr(4);
777
                        break;
778
779
                    case 'punch':
780
                        $value = chr(5);
781
                        break;
782
783
                    case 'cover':
784
                        $value = chr(6);
785
                        break;
786
787
                    case 'bind':
788
                        $value = chr(7);
789
                        break;
790
791
                    case 'saddle-stitch':
792
                        $value = chr(8);
793
                        break;
794
795
                    case 'edge-stitch':
796
                        $value = chr(9);
797
                        break;
798
799
                    case 'staple-top-left':
800
                        $value = chr(20);
801
                        break;
802
803
                    case 'staple-bottom-left':
804
                        $value = chr(21);
805
                        break;
806
807
                    case 'staple-top-right':
808
                        $value = chr(22);
809
                        break;
810
811
                    case 'staple-bottom-right':
812
                        $value = chr(23);
813
                        break;
814
815
                    case 'edge-stitch-left':
816
                        $value = chr(24);
817
                        break;
818
819
                    case 'edge-stitch-top':
820
                        $value = chr(25);
821
                        break;
822
823
                    case 'edge-stitch-right':
824
                        $value = chr(26);
825
                        break;
826
827
                    case 'edge-stitch-bottom':
828
                        $value = chr(27);
829
                        break;
830
831
                    case 'staple-dual-left':
832
                        $value = chr(28);
833
                        break;
834
835
                    case 'staple-dual-top':
836
                        $value = chr(29);
837
                        break;
838
839
                    case 'staple-dual-right':
840
                        $value = chr(30);
841
                        break;
842
843
                    case 'staple-dual-bottom':
844
                        $value = chr(31);
845
                        break;
846
                }
847
                break;
848
        }
849
        $prepend = '';
850
        while ((strlen($value) + strlen($prepend)) < 4) {
851
            $prepend .= chr(0);
852
        }
853
        return $prepend . $value;
854
    }
855
856
    protected function _setPrinterAttribute($attribute, $value)
857
    {
858
        //used by setAttribute
859
        $tag_type = $this->printer_tags[$attribute]['tag'];
860
        switch ($tag_type) {
861
            case 'integer':
862
                $this->printer_tags[$attribute]['value'][] = self::_integerBuild($value);
863
                break;
864
865
            case 'keyword':
866
            case 'naturalLanguage':
867
                $this->printer_tags[$attribute]['value'][] = $value;
868
                break;
869
870
            default:
871
                trigger_error(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), E_USER_NOTICE);
872
                self::_putDebug(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), 2);
873
                self::_errorLog(sprintf(_('SetAttribute: Tag "%s": cannot set attribute'), $attribute), 2);
874
                return false;
875
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
876
        }
877
        $this->printer_tags[$attribute]['systag'] = $this->tags_types[$tag_type]['tag'];
878
    }
879
880
    /**
881
     * Sets log file destination. Creates the file if has permission.
882
     *
883
     * @param string $log_destination
884
     * @param string $destination_type
885
     * @param int    $level
886
     *
887
     * @throws ippException
888
     */
889
    public function setLog($log_destination, $destination_type = 'file', $level = 2)
890
    {
891
        if (!file_exists($log_destination) && is_writable(dirname($log_destination))) {
892
            touch($log_destination);
893
            chmod($log_destination, 0777);
894
        }
895
896
        switch ($destination_type) {
897
            case 'file':
898
            case 3:
899
                $this->log_destination = $log_destination;
900
                $this->log_type = 3;
901
                break;
902
903
            case 'logger':
904
            case 0:
905
                $this->log_destination = '';
906
                $this->log_type = 0;
907
                break;
908
909
            case 'e-mail':
910
            case 1:
911
                $this->log_destination = $log_destination;
912
                $this->log_type = 1;
913
                break;
914
        }
915
        $this->log_level = $level;
916
    }
917
918
    public function printDebug()
919
    {
920
        for ($i = 0; $i < $this->debug_count; $i++) {
921
            echo $this->debug[$i], "\n";
922
        }
923
        $this->debug = [];
924
        $this->debug_count = 0;
925
    }
926
927
    //
928
    // LOGGING / DEBUGGING
929
    //
930
931
    public function getDebug()
932
    {
933
        $debug = '';
934
        for ($i = 0; $i < $this->debug_count; $i++) {
935
            $debug .= $this->debug[$i];
936
        }
937
        $this->debug = [];
938
        $this->debug_count = 0;
939
        return $debug;
940
    }
941
942
    public function printJob()
943
    {
944
        // this BASIC version of printJob do not parse server
945
        // output for job's attributes
946
        self::_putDebug(
947
            sprintf(
948
                "************** Date: %s ***********",
949
                date('Y-m-d H:i:s')
950
            )
951
        );
952
        if (!$this->_stringJob()) {
953
            return false;
954
        }
955
        if (is_readable($this->data)) {
956
            self::_putDebug(_("Printing a FILE"));
957
            $this->output = $this->stringjob;
958
            if ($this->setup->datatype == "TEXT") {
959
                $this->output .= chr(0x16);
960
            }
961
            $post_values = [
962
                "Content-Type" => "application/ipp",
963
                "Data" => $this->output,
964
                "File" => $this->data,
965
            ];
966
            if ($this->setup->datatype == "TEXT" && !isset($this->setup->noFormFeed)) {
967
                $post_values = array_merge(
968
                    $post_values,
969
                    [
970
                        "Filetype" => "TEXT",
971
                    ]
972
                );
973
            }
974
        } else {
975
            self::_putDebug(_("Printing DATA"));
976
            $this->output =
977
                $this->stringjob
978
                . $this->datahead
979
                . $this->data
980
                . $this->datatail;
981
            $post_values = [
982
                "Content-Type" => "application/ipp",
983
                "Data" => $this->output,
984
            ];
985
        }
986
        if (self::_sendHttp($post_values, $this->paths["printers"])) {
987
            self::_parseServerOutput();
988
        }
989
        if (isset($this->serveroutput) && isset($this->serveroutput->status)) {
990
            $this->status = array_merge($this->status, [
991
                $this->serveroutput->status,
992
            ]);
993
            if ($this->serveroutput->status == "successfull-ok") {
994
                self::_errorLog(
995
                    sprintf("printing job %s: ", $this->last_job)
996
                    . $this->serveroutput->status,
997
                    3);
998
            } else {
999
                self::_errorLog(
1000
                    sprintf("printing job: ", $this->last_job)
1001
                    . $this->serveroutput->status,
1002
                    1);
1003
            }
1004
            return $this->serveroutput->status;
1005
        }
1006
1007
        $this->status =
1008
            array_merge($this->status, ["OPERATION FAILED"]);
1009
        $this->jobs =
1010
            array_merge($this->jobs, [""]);
1011
        $this->jobs_uri =
1012
            array_merge($this->jobs_uri, [""]);
1013
1014
        self::_errorLog("printing job : OPERATION FAILED", 1);
1015
        return false;
1016
    }
1017
1018
    protected function _stringJob()
1019
    {
1020
        if (!isset($this->setup->charset)) {
1021
            self::setCharset();
1022
        }
1023
        if (!isset($this->setup->datatype)) {
1024
            self::setBinary();
1025
        }
1026
        if (!isset($this->setup->uri)) {
1027
            $this->getPrinters();
0 ignored issues
show
introduced by
The method getPrinters() does not exist on BasicIPP. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1027
            $this->/** @scrutinizer ignore-call */ 
1028
                   getPrinters();
Loading history...
1028
            unset($this->jobs[count($this->jobs) - 1]);
1029
            unset($this->jobs_uri[count($this->jobs_uri) - 1]);
1030
            unset($this->status[count($this->status) - 1]);
1031
            if (array_key_exists(0, $this->available_printers)) {
1032
                self::setPrinterURI($this->available_printers[0]);
1033
            } else {
1034
                trigger_error(
1035
                    _("_stringJob: Printer URI is not set: die"),
1036
                    E_USER_WARNING);
1037
                self::_putDebug(_("_stringJob: Printer URI is not set: die"), 4);
1038
                self::_errorLog(" Printer URI is not set, die", 2);
1039
                return false;
1040
            }
1041
        }
1042
        if (!isset($this->setup->copies)) {
1043
            self::setCopies(1);
1044
        }
1045
        if (!isset($this->setup->language)) {
1046
            self::setLanguage('en_us');
1047
        }
1048
        if (!isset($this->setup->mime_media_type)) {
1049
            self::setMimeMediaType();
1050
        }
1051
        if (!isset($this->setup->jobname)) {
1052
            self::setJobName();
1053
        }
1054
        unset($this->setup->jobname);
1055
        if (!isset($this->meta->username)) {
1056
            self::setUserName();
1057
        }
1058
        if (!isset($this->meta->fidelity)) {
1059
            $this->meta->fidelity = '';
1060
        }
1061
        if (!isset($this->meta->document_name)) {
1062
            $this->meta->document_name = '';
1063
        }
1064
        if (!isset($this->meta->sides)) {
1065
            $this->meta->sides = '';
1066
        }
1067
        if (!isset($this->meta->page_ranges)) {
1068
            $this->meta->page_ranges = '';
1069
        }
1070
        $jobattributes = '';
1071
        $operationattributes = '';
1072
        $printerattributes = '';
1073
        $this->_buildValues($operationattributes, $jobattributes, $printerattributes);
1074
        self::_setOperationId();
1075
        if (!isset($this->error_generation->request_body_malformed)) {
1076
            $this->error_generation->request_body_malformed = "";
1077
        }
1078
        $this->stringjob = chr(0x01) . chr(0x01) // 1.1  | version-number
1079
            . chr(0x00) . chr(0x02) // Print-Job | operation-id
1080
            . $this->meta->operation_id //           request-id
1081
            . chr(0x01) // start operation-attributes | operation-attributes-tag
1082
            . $this->meta->charset
1083
            . $this->meta->language
1084
            . $this->meta->printer_uri
1085
            . $this->meta->username
1086
            . $this->meta->jobname
1087
            . $this->meta->fidelity
1088
            . $this->meta->document_name
1089
            . $this->meta->mime_media_type
1090
            . $operationattributes;
1091
        if ($this->meta->copies || $this->meta->sides || $this->meta->page_ranges || !empty($jobattributes)) {
1092
            $this->stringjob .=
1093
                chr(0x02) // start job-attributes | job-attributes-tag
1094
                . $this->meta->copies
1095
                . $this->meta->sides
1096
                . $this->meta->page_ranges
1097
                . $jobattributes;
1098
        }
1099
        $this->stringjob .= chr(0x03); // end-of-attributes | end-of-attributes-tag
1100
        self::_putDebug(
1101
            sprintf(
1102
                _("String sent to the server is: %s"),
1103
                $this->stringjob)
1104
        );
1105
        return true;
1106
    }
1107
1108
    //
1109
    // OPERATIONS
1110
    //
1111
1112
    public function setCharset($charset = 'utf-8')
1113
    {
1114
        $charset = strtolower($charset);
1115
        $this->charset = $charset;
1116
        $this->meta->charset = chr(0x47) // charset type | value-tag
1117
            . chr(0x00) . chr(0x12) // name-length
1118
            . "attributes-charset" // attributes-charset | name
1119
            . self::_giveMeStringLength($charset) // value-length
1120
            . $charset; // value
1121
        self::_putDebug(sprintf(_("Charset: %s"), $charset), 2);
1122
        $this->setup->charset = 1;
1123
    }
1124
1125
    //
1126
    // HTTP OUTPUT
1127
    //
1128
1129
    protected function _giveMeStringLength($string)
1130
    {
1131
        $length = strlen($string);
1132
        if ($length > ((0xFF << 8) + 0xFF)) {
1133
            $errmsg = sprintf(
1134
                _('max string length for an ipp meta-information = %d, while here %d'),
1135
                ((0xFF << 8) + 0xFF), $length);
1136
1137
            if ($this->with_exceptions) {
1138
                throw new ippException($errmsg);
1139
            } else {
1140
                trigger_error($errmsg, E_USER_ERROR);
1141
            }
1142
        }
1143
        $int1 = $length & 0xFF;
1144
        $length -= $int1;
1145
        $length = $length >> 8;
1146
        $int2 = $length & 0xFF;
1147
        return chr($int2) . chr($int1);
1148
    }
1149
1150
    //
1151
    // INIT
1152
    //
1153
1154
    public function setPrinterURI($uri)
1155
    {
1156
        $length = strlen($uri);
1157
        $length = chr($length);
1158
        while (strlen($length) < 2) {
1159
            $length = chr(0x00) . $length;
1160
        }
1161
        $this->meta->printer_uri = chr(0x45) // uri type | value-tag
1162
            . chr(0x00) . chr(0x0B) // name-length
1163
            . "printer-uri" // printer-uri | name
1164
            . $length . $uri;
1165
        $this->printer_uri = $uri;
1166
        self::_putDebug(sprintf(_("Printer URI: %s"), $uri), 2);
1167
        $this->setup->uri = 1;
1168
    }
1169
1170
    //
1171
    // SETUP
1172
    //
1173
1174
    public function setCopies($nbrcopies = 1)
1175
    {
1176
        $this->meta->copies = "";
1177
1178
        if ($nbrcopies == 1 || !$nbrcopies) {
1179
            return true;
1180
        }
1181
1182
        $copies = self::_integerBuild($nbrcopies);
1183
        $this->meta->copies = chr(0x21) // integer type | value-tag
1184
            . chr(0x00) . chr(0x06) //             name-length
1185
            . "copies" // copies    |             name
1186
            . self::_giveMeStringLength($copies) // value-length
1187
            . $copies;
1188
        self::_putDebug(sprintf(_("Copies: %s"), $nbrcopies), 2);
1189
        $this->setup->copies = 1;
1190
    }
1191
1192
    protected function _integerBuild($value)
1193
    {
1194
        if ($value >= 2147483647 || $value < -2147483648) {
1195
            trigger_error(
1196
                _("Values must be between -2147483648 and 2147483647: assuming '0'"), E_USER_WARNING);
1197
            return chr(0x00) . chr(0x00) . chr(0x00) . chr(0x00);
1198
        }
1199
        $initial_value = $value;
1200
        $int1 = $value & 0xFF;
1201
        $value -= $int1;
1202
        $value = $value >> 8;
1203
        $int2 = $value & 0xFF;
1204
        $value -= $int2;
1205
        $value = $value >> 8;
1206
        $int3 = $value & 0xFF;
1207
        $value -= $int3;
1208
        $value = $value >> 8;
1209
        $int4 = $value & 0xFF; //64bits
1210
        if ($initial_value < 0) {
1211
            $int4 = chr($int4) | chr(0x80);
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise | or did you mean ||?
Loading history...
1212
        } else {
1213
            $int4 = chr($int4);
1214
        }
1215
        $value = $int4 . chr($int3) . chr($int2) . chr($int1);
1216
        return $value;
1217
    }
1218
1219
    public function setLanguage($language = 'en_us')
1220
    {
1221
        $language = strtolower($language);
1222
        $this->meta->language = chr(0x48) // natural-language type | value-tag
1223
            . chr(0x00) . chr(0x1B) //  name-length
1224
            . "attributes-natural-language" //attributes-natural-language
1225
            . self::_giveMeStringLength($language) // value-length
1226
            . $language; // value
1227
        self::_putDebug(sprintf(_("Language: %s"), $language), 2);
1228
        $this->setup->language = 1;
1229
    }
1230
1231
    //
1232
    // RESPONSE PARSING
1233
    //
1234
1235
    public function setMimeMediaType($mime_media_type = "application/octet-stream")
1236
    {
1237
        self::setDocumentFormat($mime_media_type);
1238
    }
1239
1240
    public function setDocumentFormat($mime_media_type = 'application/octet-stream')
1241
    {
1242
        self::setBinary();
1243
        $length = chr(strlen($mime_media_type));
1244
        while (strlen($length) < 2) {
1245
            $length = chr(0x00) . $length;
1246
        }
1247
        self::_putDebug(sprintf(_("mime type: %s"), $mime_media_type), 2);
1248
        $this->meta->mime_media_type = chr(0x49) // document-format tag
1249
            . self::_giveMeStringLength('document-format') . 'document-format' //
1250
            . self::_giveMeStringLength($mime_media_type) . $mime_media_type; // value
1251
        $this->setup->mime_media_type = 1;
1252
    }
1253
1254
    public function setBinary()
1255
    {
1256
        self::unsetRawText();
1257
    }
1258
1259
    public function unsetRawText()
1260
    {
1261
        $this->setup->datatype = 'BINARY';
1262
        $this->datahead = '';
1263
        $this->datatail = '';
1264
        self::_putDebug(_("Unset forcing data to be interpreted as RAW TEXT"), 2);
1265
    }
1266
1267
    public function setJobName($jobname = '', $absolute = false)
1268
    {
1269
        $this->meta->jobname = '';
1270
        if ($jobname == '') {
1271
            $this->meta->jobname = '';
1272
            return true;
1273
        }
1274
        $postpend = date('-H:i:s-') . $this->_setJobId();
1275
        if ($absolute) {
1276
            $postpend = '';
1277
        }
1278
        if (isset($this->values->jobname) && $jobname == '(PHP)') {
1279
            $jobname = $this->values->jobname;
1280
        }
1281
        $this->values->jobname = $jobname;
1282
        $jobname .= $postpend;
1283
        $this->meta->jobname = chr(0x42) // nameWithoutLanguage type || value-tag
1284
            . chr(0x00) . chr(0x08) //  name-length
1285
            . "job-name" //  job-name || name
1286
            . self::_giveMeStringLength($jobname) // value-length
1287
            . $jobname; // value
1288
        self::_putDebug(sprintf(_("Job name: %s"), $jobname), 2);
1289
        $this->setup->jobname = 1;
1290
    }
1291
1292
    protected function _setJobId()
1293
    {
1294
        $this->meta->jobid += 1;
1295
        $prepend = '';
1296
        $prepend_length = 4 - strlen($this->meta->jobid);
1297
        for ($i = 0; $i < $prepend_length; $i++) {
1298
            $prepend .= '0';
1299
        }
1300
        return $prepend . $this->meta->jobid;
1301
    }
1302
1303
    public function setUserName($username = 'PHP-SERVER')
1304
    {
1305
        $this->requesting_user = $username;
1306
        $this->meta->username = '';
1307
        if (!$username) {
1308
            return true;
1309
        }
1310
        if ($username == 'PHP-SERVER' && isset($this->meta->username)) {
1311
            return true;
1312
        }
1313
        /*
1314
        $value_length = 0x00;
1315
        for ($i = 0; $i < strlen($username); $i++)
1316
        {
1317
            $value_length+= 0x01;
1318
        }
1319
        $value_length = chr($value_length);
1320
        while (strlen($value_length) < 2) $value_length = chr(0x00) . $value_length;
1321
        */
1322
        $this->meta->username = chr(0x42) // keyword type || value-tag
1323
            . chr(0x00) . chr(0x14) // name-length
1324
            . "requesting-user-name"
1325
            . self::_giveMeStringLength($username) // value-length
1326
            . $username;
1327
        self::_putDebug(sprintf(_("Username: %s"), $username), 2);
1328
        $this->setup->username = 1;
1329
    }
1330
1331
    //
1332
    // REQUEST BUILDING
1333
    //
1334
1335
    protected function _buildValues(&$operationattributes, &$jobattributes, &$printerattributes)
1336
    {
1337
        $operationattributes = '';
1338
        foreach ($this->operation_tags as $key => $values) {
1339
            $item = 0;
1340
            if (array_key_exists('value', $values)) {
1341
                foreach ($values['value'] as $item_value) {
1342
                    if ($item == 0) {
1343
                        $operationattributes .=
1344
                            $values['systag']
1345
                            . self::_giveMeStringLength($key)
1346
                            . $key
1347
                            . self::_giveMeStringLength($item_value)
1348
                            . $item_value;
1349
                    } else {
1350
                        $operationattributes .=
1351
                            $values['systag']
1352
                            . self::_giveMeStringLength('')
1353
                            . self::_giveMeStringLength($item_value)
1354
                            . $item_value;
1355
                    }
1356
                    $item++;
1357
                }
1358
            }
1359
        }
1360
        $jobattributes = '';
1361
        foreach ($this->job_tags as $key => $values) {
1362
            $item = 0;
1363
            if (array_key_exists('value', $values)) {
1364
                foreach ($values['value'] as $item_value) {
1365
                    if ($item == 0) {
1366
                        $jobattributes .=
1367
                            $values['systag']
1368
                            . self::_giveMeStringLength($key)
1369
                            . $key
1370
                            . self::_giveMeStringLength($item_value)
1371
                            . $item_value;
1372
                    } else {
1373
                        $jobattributes .=
1374
                            $values['systag']
1375
                            . self::_giveMeStringLength('')
1376
                            . self::_giveMeStringLength($item_value)
1377
                            . $item_value;
1378
                    }
1379
                    $item++;
1380
                }
1381
            }
1382
        }
1383
        $printerattributes = '';
1384
        foreach ($this->printer_tags as $key => $values) {
1385
            $item = 0;
1386
            if (array_key_exists('value', $values)) {
1387
                foreach ($values['value'] as $item_value) {
1388
                    if ($item == 0) {
1389
                        $printerattributes .=
1390
                            $values['systag']
1391
                            . self::_giveMeStringLength($key)
1392
                            . $key
1393
                            . self::_giveMeStringLength($item_value)
1394
                            . $item_value;
1395
                    } else {
1396
                        $printerattributes .=
1397
                            $values['systag']
1398
                            . self::_giveMeStringLength('')
1399
                            . self::_giveMeStringLength($item_value)
1400
                            . $item_value;
1401
                    }
1402
                    $item++;
1403
                }
1404
            }
1405
        }
1406
        reset($this->job_tags);
1407
        reset($this->operation_tags);
1408
        reset($this->printer_tags);
1409
        return true;
1410
    }
1411
1412
    protected function _setOperationId()
1413
    {
1414
        $prepend = '';
1415
        $this->operation_id += 1;
1416
        $this->meta->operation_id = self::_integerBuild($this->operation_id);
1417
        self::_putDebug("operation id is: " . $this->operation_id, 2);
1418
    }
1419
1420
    protected function _sendHttp($post_values, $uri)
1421
    {
1422
        /*
1423
            This function Copyright (C) 2005-2006 Thomas Harding, Manuel Lemos
1424
        */
1425
        $this->response_completed[] = "no";
1426
        unset($this->serverouptut);
0 ignored issues
show
Bug introduced by
The property serverouptut does not exist on BasicIPP. Did you mean serveroutput?
Loading history...
1427
        self::_putDebug(_("Processing HTTP request"), 2);
1428
        $this->serveroutput->headers = [];
1429
        $this->serveroutput->body = "";
1430
        $http = new http_class();
1431
        if (!$this->unix) {
1432
            // DOL_LDR_CHANGE
1433
            if (empty($this->host)) {
1434
                $this->host = '127.0.0.1';
1435
            }
1436
            $http->host = $this->host;
1437
        } else {
1438
            $http->host = "localhost";
1439
        }
1440
        $http->with_exceptions = $this->with_exceptions;
1441
        if ($this->debug_http) {
1442
            $http->debug = 1;
1443
            $http->html_debug = 0;
1444
        } else {
1445
            $http->debug = 0;
1446
            $http->html_debug = 0;
1447
        }
1448
        $url = "http://" . $this->host;
1449
        if ($this->ssl) {
1450
            $url = "https://" . $this->host;
1451
        }
1452
        if ($this->unix) {
1453
            $url = "unix://" . $this->host;
1454
        }
1455
        $http->port = $this->port;
1456
        $http->timeout = $this->http_timeout;
1457
        $http->data_timeout = $this->http_data_timeout;
1458
        $http->force_multipart_form_post = false;
1459
        $http->user = $this->username;
1460
        $http->password = $this->password;
1461
        $error = $http->GetRequestArguments($url, $arguments);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $error is correct as $http->GetRequestArguments($url, $arguments) targeting http_class::GetRequestArguments() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1462
        $arguments["RequestMethod"] = "POST";
1463
        $arguments["Headers"] = [
1464
            "Content-Type" => "application/ipp",
1465
        ];
1466
        $arguments["BodyStream"] = [
1467
            [
1468
                "Data" => $post_values["Data"],
1469
            ],
1470
        ];
1471
        if (isset($post_values["File"])) {
1472
            $arguments["BodyStream"][] = [
1473
                "File" => $post_values["File"],
1474
            ];
1475
        }
1476
        if (
1477
            isset($post_values["FileType"])
1478
            && !strcmp($post_values["FileType"], "TEXT")
1479
        ) {
1480
            $arguments["BodyStream"][] = ["Data" => Chr(12)];
1481
        }
1482
        $arguments["RequestURI"] = $uri;
1483
        if ($this->with_exceptions && $this->handle_http_exceptions) {
1484
            try {
1485
                $success = $http->Open($arguments);
1486
            } catch (httpException $e) {
1487
                throw new ippException(
1488
                    sprintf("http error: %s", $e->getMessage()),
1489
                    $e->getErrno());
0 ignored issues
show
Bug introduced by
The method getErrno() does not exist on HttpException. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1489
                    $e->/** @scrutinizer ignore-call */ 
1490
                        getErrno());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1490
            }
1491
        } else {
1492
            $success = $http->Open($arguments);
1493
        }
1494
        if ($success[0] == true) {
1495
            $success = $http->SendRequest($arguments);
1496
            if ($success[0] == true) {
1497
                self::_putDebug("H T T P    R E Q U E S T :");
1498
                self::_putDebug("Request headers:");
1499
                for (Reset($http->request_headers), $header = 0; $header < count($http->request_headers); Next($http->request_headers), $header++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1500
                    $header_name = Key($http->request_headers);
1501
                    if (GetType($http->request_headers[$header_name]) == "array") {
1502
                        for ($header_value = 0; $header_value < count($http->request_headers[$header_name]); $header_value++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1503
                            self::_putDebug($header_name . ": " . $http->request_headers[$header_name][$header_value]);
1504
                        }
1505
                    } else {
1506
                        self::_putDebug($header_name . ": " . $http->request_headers[$header_name]);
1507
                    }
1508
                }
1509
                self::_putDebug("Request body:");
1510
                self::_putDebug(
1511
                    htmlspecialchars($http->request_body)
1512
                    . "*********** END REQUEST BODY *********"
1513
                );
1514
                $i = 0;
1515
                $headers = [];
1516
                unset($this->serveroutput->headers);
1517
                $http->ReadReplyHeaders($headers);
1518
                self::_putDebug("H T T P    R E S P O N S E :");
1519
                self::_putDebug("Response headers:");
1520
                for (Reset($headers), $header = 0; $header < count($headers); Next($headers), $header++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1521
                    $header_name = Key($headers);
1522
                    if (GetType($headers[$header_name]) == "array") {
1523
                        for ($header_value = 0; $header_value < count($headers[$header_name]); $header_value++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1524
                            self::_putDebug($header_name . ": " . $headers[$header_name][$header_value]);
1525
                            $this->serveroutput->headers[$i] =
1526
                                $header_name . ": "
1527
                                . $headers[$header_name][$header_value];
1528
                            $i++;
1529
                        }
1530
                    } else {
1531
                        self::_putDebug($header_name . ": " . $headers[$header_name]);
1532
                        $this->serveroutput->headers[$i] =
1533
                            $header_name
1534
                            . ": "
1535
                            . $headers[$header_name];
1536
                        $i++;
1537
                    }
1538
                }
1539
                self::_putDebug("\n\nResponse body:\n");
1540
                $this->serveroutput->body = "";
1541
                for (; ;) {
1542
                    $http->ReadReplyBody($body, 1024);
1543
                    if (strlen($body) == 0) {
1544
                        break;
1545
                    }
1546
1547
                    self::_putDebug(htmlentities($body));
1548
                    $this->serveroutput->body .= $body;
1549
                }
1550
                self::_putDebug("********* END RESPONSE BODY ********");
1551
            }
1552
        }
1553
        $http->Close();
1554
        return true;
1555
    }
1556
1557
    protected function _parseServerOutput()
1558
    {
1559
        $this->serveroutput->response = [];
1560
        if (!self::_parseHttpHeaders()) {
1561
            return false;
1562
        }
1563
        $this->_parsing->offset = 0;
1564
        self::_parseIppVersion();
1565
        self::_parseStatusCode();
1566
        self::_parseRequestID();
1567
        $this->_parseResponse();
1568
        //devel
1569
        self::_putDebug(
1570
            sprintf("***** IPP STATUS: %s ******", $this->serveroutput->status),
1571
            4);
1572
        self::_putDebug("****** END OF OPERATION ****");
1573
        return true;
1574
    }
1575
1576
    protected function _parseHttpHeaders()
1577
    {
1578
        $response = "";
1579
        switch ($this->serveroutput->headers[0]) {
1580
            case "http/1.1 200 ok: ":
1581
                $this->serveroutput->httpstatus = "HTTP/1.1 200 OK";
1582
                $response = "OK";
1583
                break;
1584
1585
            // primitive http/1.0 for Lexmark printers (from Rick Baril)
1586
            case "http/1.0 200 ok: ":
1587
                $this->serveroutput->httpstatus = "HTTP/1.0 200 OK";
1588
                $response = "OK";
1589
                break;
1590
1591
            case "http/1.1 100 continue: ":
1592
                $this->serveroutput->httpstatus = "HTTP/1.1 100 CONTINUE";
1593
                $response = "OK";
1594
                break;
1595
1596
            case "":
1597
                $this->serveroutput->httpstatus = "HTTP/1.1 000 No Response From Server";
1598
                $this->serveroutput->status = "HTTP-ERROR-000_NO_RESPONSE_FROM_SERVER";
1599
                trigger_error("No Response From Server", E_USER_WARNING);
1600
                self::_errorLog("No Response From Server", 1);
1601
                $this->disconnected = 1;
1602
                return false;
1603
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1604
1605
            default:
1606
                $server_response = preg_replace("/: $/", '', $this->serveroutput->headers[0]);
1607
                // $strings = split(' ', $server_response, 3);
1608
                $strings = preg_split('# #', $server_response, 3);
1609
                $errno = $strings[1];
1610
                $string = strtoupper(str_replace(' ', '_', $strings[2]));
1611
                trigger_error(
1612
                    sprintf(_("server responds %s"), $server_response),
1613
                    E_USER_WARNING);
1614
                self::_errorLog("server responds " . $server_response, 1);
1615
                $this->serveroutput->httpstatus =
1616
                    strtoupper($strings[0])
1617
                    . " "
1618
                    . $errno
1619
                    . " "
1620
                    . ucfirst($strings[2]);
1621
1622
                $this->serveroutput->status =
1623
                    "HTTP-ERROR-"
1624
                    . $errno
1625
                    . "-"
1626
                    . $string;
1627
                $this->disconnected = 1;
1628
                return false;
1629
                break;
1630
        }
1631
        unset($this->serveroutput->headers);
1632
        return true;
1633
    }
1634
1635
    protected function _parseIppVersion()
1636
    {
1637
        $ippversion =
1638
            (ord($this->serveroutput->body[$this->_parsing->offset]) * 256)
1639
            + ord($this->serveroutput->body[$this->_parsing->offset + 1]);
1640
        switch ($ippversion) {
1641
            case 0x0101:
1642
                $this->serveroutput->ipp_version = "1.1";
1643
                break;
1644
1645
            default:
1646
                $this->serveroutput->ipp_version =
1647
                    sprintf(
1648
                        "%u.%u (Unknown)",
1649
                        ord($this->serveroutput->body[$this->_parsing->offset]) * 256,
1650
                        ord($this->serveroutput->body[$this->_parsing->offset + 1]));
1651
                break;
1652
        }
1653
        self::_putDebug("I P P    R E S P O N S E :\n\n");
1654
        self::_putDebug(
1655
            sprintf(
1656
                "IPP version %s%s: %s",
1657
                ord($this->serveroutput->body[$this->_parsing->offset]),
1658
                ord($this->serveroutput->body[$this->_parsing->offset + 1]),
1659
                $this->serveroutput->ipp_version));
1660
        $this->_parsing->offset += 2;
1661
        return;
1662
    }
1663
1664
    protected function _parseStatusCode()
1665
    {
1666
        $status_code =
1667
            (ord($this->serveroutput->body[$this->_parsing->offset]) * 256)
1668
            + ord($this->serveroutput->body[$this->_parsing->offset + 1]);
1669
        $this->serveroutput->status = "NOT PARSED";
1670
        $this->_parsing->offset += 2;
1671
        if (strlen($this->serveroutput->body) < $this->_parsing->offset) {
1672
            return false;
1673
        }
1674
        if ($status_code < 0x00FF) {
1675
            $this->serveroutput->status = "successfull";
1676
        } elseif ($status_code < 0x01FF) {
1677
            $this->serveroutput->status = "informational";
1678
        } elseif ($status_code < 0x02FF) {
1679
            $this->serveroutput->status = "redirection";
1680
        } elseif ($status_code < 0x04FF) {
1681
            $this->serveroutput->status = "client-error";
1682
        } elseif ($status_code < 0x05FF) {
1683
            $this->serveroutput->status = "server-error";
1684
        }
1685
        switch ($status_code) {
1686
            case 0x0000:
1687
                $this->serveroutput->status = "successfull-ok";
1688
                break;
1689
1690
            case 0x0001:
1691
                $this->serveroutput->status = "successful-ok-ignored-or-substituted-attributes";
1692
                break;
1693
1694
            case 0x002:
1695
                $this->serveroutput->status = "successful-ok-conflicting-attributes";
1696
                break;
1697
1698
            case 0x0400:
1699
                $this->serveroutput->status = "client-error-bad-request";
1700
                break;
1701
1702
            case 0x0401:
1703
                $this->serveroutput->status = "client-error-forbidden";
1704
                break;
1705
1706
            case 0x0402:
1707
                $this->serveroutput->status = "client-error-not-authenticated";
1708
                break;
1709
1710
            case 0x0403:
1711
                $this->serveroutput->status = "client-error-not-authorized";
1712
                break;
1713
1714
            case 0x0404:
1715
                $this->serveroutput->status = "client-error-not-possible";
1716
                break;
1717
1718
            case 0x0405:
1719
                $this->serveroutput->status = "client-error-timeout";
1720
                break;
1721
1722
            case 0x0406:
1723
                $this->serveroutput->status = "client-error-not-found";
1724
                break;
1725
1726
            case 0x0407:
1727
                $this->serveroutput->status = "client-error-gone";
1728
                break;
1729
1730
            case 0x0408:
1731
                $this->serveroutput->status = "client-error-request-entity-too-large";
1732
                break;
1733
1734
            case 0x0409:
1735
                $this->serveroutput->status = "client-error-request-value-too-long";
1736
                break;
1737
1738
            case 0x040A:
1739
                $this->serveroutput->status = "client-error-document-format-not-supported";
1740
                break;
1741
1742
            case 0x040B:
1743
                $this->serveroutput->status = "client-error-attributes-or-values-not-supported";
1744
                break;
1745
1746
            case 0x040C:
1747
                $this->serveroutput->status = "client-error-uri-scheme-not-supported";
1748
                break;
1749
1750
            case 0x040D:
1751
                $this->serveroutput->status = "client-error-charset-not-supported";
1752
                break;
1753
1754
            case 0x040E:
1755
                $this->serveroutput->status = "client-error-conflicting-attributes";
1756
                break;
1757
1758
            case 0x040F:
1759
                $this->serveroutput->status = "client-error-compression-not-supported";
1760
                break;
1761
1762
            case 0x0410:
1763
                $this->serveroutput->status = "client-error-compression-error";
1764
                break;
1765
1766
            case 0x0411:
1767
                $this->serveroutput->status = "client-error-document-format-error";
1768
                break;
1769
1770
            case 0x0412:
1771
                $this->serveroutput->status = "client-error-document-access-error";
1772
                break;
1773
1774
            case 0x0413: // RFC3380
1775
                $this->serveroutput->status = "client-error-attributes-not-settable";
1776
                break;
1777
1778
            case 0x0500:
1779
                $this->serveroutput->status = "server-error-internal-error";
1780
                break;
1781
1782
            case 0x0501:
1783
                $this->serveroutput->status = "server-error-operation-not-supported";
1784
                break;
1785
1786
            case 0x0502:
1787
                $this->serveroutput->status = "server-error-service-unavailable";
1788
                break;
1789
1790
            case 0x0503:
1791
                $this->serveroutput->status = "server-error-version-not-supported";
1792
                break;
1793
1794
            case 0x0504:
1795
                $this->serveroutput->status = "server-error-device-error";
1796
                break;
1797
1798
            case 0x0505:
1799
                $this->serveroutput->status = "server-error-temporary-error";
1800
                break;
1801
1802
            case 0x0506:
1803
                $this->serveroutput->status = "server-error-not-accepting-jobs";
1804
                break;
1805
1806
            case 0x0507:
1807
                $this->serveroutput->status = "server-error-busy";
1808
                break;
1809
1810
            case 0x0508:
1811
                $this->serveroutput->status = "server-error-job-canceled";
1812
                break;
1813
1814
            case 0x0509:
1815
                $this->serveroutput->status = "server-error-multiple-document-jobs-not-supported";
1816
                break;
1817
1818
            default:
1819
                break;
1820
        }
1821
        self::_putDebug(
1822
            sprintf(
1823
                "status-code: %s%s: %s ",
1824
                $this->serveroutput->body[$this->_parsing->offset],
1825
                $this->serveroutput->body[$this->_parsing->offset + 1],
1826
                $this->serveroutput->status),
1827
            4);
1828
        return;
1829
    }
1830
1831
    protected function _parseRequestID()
1832
    {
1833
        $this->serveroutput->request_id =
1834
            self::_interpretInteger(
1835
                substr($this->serveroutput->body, $this->_parsing->offset, 4)
1836
            );
1837
        self::_putDebug("request-id " . $this->serveroutput->request_id, 2);
1838
        $this->_parsing->offset += 4;
1839
        return;
1840
    }
1841
1842
    protected function _interpretInteger($value)
1843
    {
1844
        // they are _signed_ integers
1845
        $value_parsed = 0;
1846
        for ($i = strlen($value); $i > 0; $i--) {
1847
            $value_parsed +=
1848
                (
1849
                    (1 << (($i - 1) * 8))
1850
                    *
1851
                    ord($value[strlen($value) - $i])
1852
                );
1853
        }
1854
        if ($value_parsed >= 2147483648) {
1855
            $value_parsed -= 4294967296;
1856
        }
1857
        return $value_parsed;
1858
    }
1859
1860
    //
1861
    // DEBUGGING
1862
    //
1863
1864
    protected function _parseResponse()
1865
    {
1866
    }
1867
1868
    //
1869
    // LOGGING
1870
    //
1871
1872
    protected function _setJobUri($job_uri)
1873
    {
1874
        $this->meta->job_uri = chr(0x45) // type uri
1875
            . chr(0x00) . chr(0x07) // name-length
1876
            . "job-uri"
1877
            //. chr(0x00).chr(strlen($job_uri))
1878
            . self::_giveMeStringLength($job_uri) . $job_uri;
1879
        self::_putDebug("job-uri is: " . $job_uri, 2);
1880
    }
1881
}
1882