summaryrefslogtreecommitdiff
path: root/buildscripts/phing/classes/phing/tasks/ext/zendguard/ZendGuardEncodeTask.php
blob: 33d2e4e36be99370711f24d7e90d27822853b568 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
<?php

/*
 *  $Id: cdbb2883ab70c650896a465a872b3da30f13eb00 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/MatchingTask.php';
include_once 'phing/util/SourceFileScanner.php';
include_once 'phing/mappers/MergeMapper.php';
include_once 'phing/util/StringHelper.php';

/**
 * Encodes files using Zeng Guard Encoder
 *
 * @author    Petr Rybak <petr@rynawe.net>
 * @version   $Id: cdbb2883ab70c650896a465a872b3da30f13eb00 $
 * @package   phing.tasks.ext.zendguard
 * @since     2.4.3
 */
class ZendGuardEncodeTask extends MatchingTask
{
    protected $filesets = array();
    protected $encodeCommand;


    /**
     * TASK PROPERTIES
     *
     * See http://static.zend.com/topics/Zend-Guard-User-Guidev5x.pdf
     * for more information on how to use ZendGuard
     *
     */
    /**
     * Permanently deletes (see warning below) the original source files specified in the
     * SourceInputPath and saves the encoded files in its place.
     * This option has no option parameter.
     * When this option is use, do not use the output_file parameter.
     *
     * Warning:
     * To avoid permanent loss of non-encoded scripts, make a backup. Deleted files
     * cannot be restored or recovered and will be permanently deleted with this option.
     * If you are unsure about deleting the source files, use the ––rename-source option
     * instead
     *
     * @var bool
     */
    protected $deleteSource = true;
    /**
     * Move the original source file to <input_file>.<renameSourceExt> and save the encoded file in its
     * place.
     *
     * If specified deleteSource will be automatically disabled.
     *
     * @var string
     */
    protected $renameSourceExt = null;
    /**
     * Turns short PHP tag (“<?” ) recognition either on or off.
     * On or off must be specified as an argument when using this option.
     * The default, when option is not used in the command-line, is - on
     *
     * @var bool
     */
    protected $shortTags = true;
    /**
     * Turn ASP tag (“<%” ) recognition on/off. (default: off). On or off must be specified
     * as an argument when using this option.
     * The default, when this option is not used in the command-line, is - off
     *
     * @var bool
     */
    protected $aspTags = false;
    /**
     *
     * Disables the PHP-compatible header that is added to the top of every encoded file
     * by default. Encoded files generated with this option will not display a meaningful
     * error when loaded by PHP that doesn't have the Zend Optimizer properly installed.
     * Using this option saves approximately 1.5KB for every encoded file. Do not use it
     * unless disk space constraints are critica
     *
     * @var bool
     */
    protected $noHeader = false;
    /**
     * If cryptography should be used to encode the source code
     *
     * @var bool
     */
    protected $useCrypto = false;
    /**
     * Force cooperation with other encoded files only. This option generates files that
     * work exclusively with associated encoded files. Associated encoded files are
     * those generated by the same company. Files that do not share the same encoded
     * company association cannot call these files
     *
     * @var bool
     */
    protected $encodedOnly = false;
    /**
     * Allow encoding previously encoded files. (NOT recommended!)
     *
     * @var bool
     */
    protected $forceEncode = false;
    /**
     * Make an encoded file to expire on the given date. Date is in yyyy-mm-dd format.
     *
     * @var string
     */
    protected $expires = null;
    /**
     * Level of obfuscation. Defaults to 0 (no obfuscation).
     *
     * @var int
     */
    protected $obfuscationLevel = 0;
    /**
     * Optimization mask. (default value: [+++++++])
     * opt_mask is an integer representing a bit-mask.
     * The default value enables all of the optimization passes.
     * Each optimization pass of the Zend Optimizer can be turned on or off based on
     * the mask entered
     *
     * @var int
     */
    protected $optMask = null;
    /**
     * Path to the zend encoder binary
     *
     * @var string
     */
    protected $zendEncoderPath = null;
    /**
     * Path to private key for licensing
     *
     * @var string
     */
    protected $privateKeyPath = null;
    /**
     * Enable licensing.
     * If enabled, productName must be defined.
     *
     * @var bool
     */
    protected $licenseProduct = false;
    /**
     * If true the ownership, permissions and timestamps
     * of the encoded files won't be preserved.
     *
     * @var bool
     */
    protected $ignoreFileModes = false;
    /**
     * Enable signing
     * If enabled, productName must be defined.
     *
     * @var bool
     */
    protected $signProduct = false;
    /**
     * Product name. Must be defined if licenseProduct
     * or signProduct is set to 1
     *
     * @var string
     */
    protected $productName = null;
    /**
     * Embed the information in the specified file into the header of the encoded file
     * (overrides noHeader)
     *
     * @var string
     */
    protected $prologFile = null;

    /**
     * TASK PROPERTIES SETTERS
     */
    public function setZendEncoderPath($value)
    {
        $this->zendEncoderPath = $value;
    }

    public function setPrivateKeyPath($value)
    {
        $this->privateKeyPath = $value;
    }

    public function setShortTags($value)
    {
        $this->shortTags = (bool) $value;
    }

    public function setAspTags($value)
    {
        $this->aspTags = (bool) $value;
    }

    public function setDeleteSource($value)
    {
        $this->shortTags = (bool) $value;
    }

    public function setUseCrypto($value)
    {
        $this->useCrypto = (bool) $value;
    }

    public function setObfuscationLevel($value)
    {
        $this->obfuscationLevel = (int) $value;
    }

    public function setLicenseProduct($value)
    {
        $this->licenseProduct = (bool) $value;
    }

    public function setPrologFile($value)
    {
        $this->prologFile = $value;
    }

    public function setSignProduct($value)
    {
        $this->signProduct = (bool) $value;
    }

    public function setForceEncode($value)
    {
        $this->forceEncode = (bool) $value;
    }

    public function setEncodedOnly($value)
    {
        $this->encodedOnly = (bool) $value;
    }

    public function setIgnoreFileModes($value)
    {
        $this->ignoreFileModes = (bool) $value;
    }

    public function setExpires($value)
    {
        $this->expires = $value;
    }

    public function setProductName($value)
    {
        $this->productName = $value;
    }

    public function setOptMask($value)
    {
        $this->optMask = (int) $value;
    }

    public function setRenameSourceExt($value)
    {
        $this->renameSourceExt = $value;
    }

    public function setNoHeader($value)
    {
        $this->noHeader = (bool) $value;
    }

    /**
     * Add a new fileset.
     *
     * @return FileSet
     */
    public function createFileSet()
    {
        $this->fileset = new ZendGuardFileSet();
        $this->filesets[] = $this->fileset;
        return $this->fileset;
    }

    /**
     * Verifies that the configuration is correct
     *
     * @throws BuildException
     */
    protected function verifyConfiguration()
    {
        // Check that the zend encoder path is specified
        if (empty($this->zendEncoderPath)) {
            throw new BuildException("Zend Encoder path must be specified");
        }

        // verify that the zend encoder binary exists
        if (!file_exists($this->zendEncoderPath)) {
            throw new BuildException("Zend Encoder not found on path " . $this->zendEncoderPath);
        }

        // if either sign or license is required the private key path needs to be defined
        // and the file has to exist and product name has to be specified
        if ($this->signProduct || $this->licenseProduct) {
            if (empty($this->privateKeyPath)) {
                throw new BuildException("Licensing or signing requested but privateKeyPath not provided.");
            }
            if (!is_readable($this->privateKeyPath)) {
                throw new BuildException("Licensing or signing requested but private key path doesn't exist or is unreadable.");
            }
            if (empty($this->productName)) {
                throw new BuildException("Licensing or signing requested but product name not provided.");
            }
        }

        // verify prolog file exists
        if (!empty($this->prologFile)) {
            if (!file_exists($this->prologFile)) {
                throw new BuildException("The prolog file doesn't exist: " . $this->prologFile);
            }
        }
    }

    /**
     * Do the work
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->verifyConfiguration();
        $this->prepareEncoderCommand();

        try {
            if (empty($this->filesets)) {
                throw new BuildException("You must supply nested fileset.",
                    $this->getLocation());
            }

            $encodedFilesCounter = 0;

            foreach ($this->filesets as $fs) {
                /* @var $fs FileSet */

                /* @var $fsBasedir PhingFile */
                $fsBasedir = $fs->getDir($this->project)->getAbsolutePath();

                $files = $fs->getFiles($this->project, false);

                foreach ($files as $file) {
                    $f = new PhingFile($fsBasedir, $file);

                    if ($f->isFile()) {
                        $path = $f->getAbsolutePath();

                        $this->log("Encoding " . $path, Project::MSG_VERBOSE);
                        $this->encodeFile($path);

                        $encodedFilesCounter++;
                    }
                }
            }

            $this->log("Encoded files: " . $encodedFilesCounter);
        } catch (IOException $ioe) {
            $msg = "Problem encoding files: " . $ioe->getMessage();
            throw new BuildException($msg, $ioe, $this->getLocation());
        }
    }

    /**
     * Prepares the main part of the command that will be
     * used to encode the given file(s).
     */
    protected function prepareEncoderCommand()
    {
        $command = $this->zendEncoderPath . " \\\n";

        if (!empty($this->renameSourceExt)) {
            $command .= " --rename-source " . $this->renameSourceExt . " \\\n";
        } elseif ($this->deleteSource) {
            // delete source
            $command .= " --delete-source \\\n";
        }

        // short tags
        $command .= " --short-tags " . (($this->shortTags) ? 'on' : 'off') . " \\\n";

        // asp tags
        $command .= " --asp-tags " . (($this->aspTags) ? 'on' : 'off') . " \\\n";

        // use crypto
        if ($this->useCrypto) {
            $command .= " --use-crypto  \\\n";
        }

        // ignore file modes
        if ($this->ignoreFileModes) {
            $command .= " --ignore-file-modes \\\n";
        }

        // force encode
        if ($this->forceEncode) {
            $command .= " --force-encode \\\n";
        }

        // expires
        if (!empty($this->expires)) {
            $command .= " --expires " . $this->expires . " \\\n";
        }

        // insert prolog file name or no-header
        if (!empty($this->prologFile)) {
            $command .= " --prolog-filename " . $this->prologFile . " \\\n";
        } elseif ($this->noHeader) {
            // no-header
            $command .= " --no-header \\\n";
        }

        // obfuscation level
        if ($this->obfuscationLevel > 0) {
            $command .= " --obfuscation-level " . $this->obfuscationLevel . " \\\n";
        }

        // encoded only
        if ($this->encodedOnly) {
            $command .= " --encoded-only  \\\n";
        }

        // opt mask
        if (null !== $this->optMask) {
            $command .= " --optimizations " . $this->optMask . " \\\n";
        }

        // Signing or licensing
        if ($this->signProduct) {
            $command .= " --sign-product " . $this->productName . " --private-key " . $this->privateKeyPath . " \\\n";
        } elseif ($this->licenseProduct) {
            $command .= " --license-product " . $this->productName . " --private-key " . $this->privateKeyPath . " \\\n";
        }

        // add a blank space
        $command .= " ";

        $this->encodeCommand = $command;

    }

    /**
     * Encodes a file using currently defined Zend Guard settings
     *
     * @param string $filePath Path to the encoded file
     */
    protected function encodeFile($filePath)
    {
        $command = $this->encodeCommand . $filePath . ' 2>&1';

        $this->log('Running: ' . $command, Project::MSG_VERBOSE);

        $tmp = exec($command, $output, $return_var);
        if ($return_var !== 0) {
            throw new BuildException("Encoding failed. \n Msg: " . $tmp . " \n Encode command: " . $command);
        }

        return true;
    }

}

/**
 * This is a FileSet with the to specify permissions.
 *
 * Permissions are currently not implemented by PEAR Archive_Tar,
 * but hopefully they will be in the future.
 *
 * @package phing.tasks.ext.zendguard
 */
class ZendGuardFileSet extends FileSet
{
    private $files = null;

    /**
     *  Get a list of files and directories specified in the fileset.
     *  @return array a list of file and directory names, relative to
     *    the baseDir for the project.
     */
    public function getFiles(Project $p, $includeEmpty = true)
    {

        if ($this->files === null) {

            $ds = $this->getDirectoryScanner($p);
            $this->files = $ds->getIncludedFiles();
        } // if ($this->files===null)

        return $this->files;
    }

}