Skip to content

Commit 56ff4e0

Browse files
committed
fix(#34): tempnam error
1 parent b7f76e5 commit 56ff4e0

File tree

6 files changed

+173
-25
lines changed

6 files changed

+173
-25
lines changed

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ secure-spreadsheet run --password=1 --input=/Users/nick/Encryptor/Book1.xlsx --o
2929

3030
In php
3131

32+
Usage Examples
33+
3234
```php
3335
require "vendor/autoload.php";
3436

@@ -40,15 +42,35 @@ $test->input('Book1.xlsx')
4042
->output('bb.xlsx');
4143
```
4244

45+
Memory-only input and output (no file interaction)
4346

44-
If you want to only use memory/variable output and input, and no file interaction
4547
```php
4648
$test = new Encrypt($nofile = true);
4749
$output = $test->input($binaryData)
4850
->password('111')
4951
->output();
5052
```
5153

54+
Memory output with a temporary folder
55+
56+
```php
57+
$test = new Encrypt(true);
58+
$out = $test->input('Book1.xlsx')
59+
->setTempPathFolder(__DIR__ . DIRECTORY_SEPARATOR . 'tmp')
60+
->password('111')
61+
->output();
62+
```
63+
64+
File input and file output with a temporary folder
65+
66+
```php
67+
$test = new Encrypt;
68+
$test->input('Book1.xlsx')
69+
->setTempPathFolder(__DIR__ . DIRECTORY_SEPARATOR . 'tmp')
70+
->password('111')
71+
->output('bb4.xlsx');
72+
```
73+
5274
## Credits
5375

5476
Thanks to [xlsx-populate](https://github.com/dtjohnson/xlsx-populate) for providing the encryption and password protection.

index.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,29 @@
44

55
use Nick\SecureSpreadsheet\Encrypt;
66

7-
$test = new Encrypt();
7+
// output file
8+
$test = new Encrypt;
89
$test->input('Book1.xlsx')
910
->password('111')
10-
->output('bb.xlsx');
11+
->output('bb2.xlsx');
12+
13+
// output file with nofile
14+
$test = new Encrypt(true);
15+
$out = $test->input('Book1.xlsx')
16+
->password('111')
17+
->output();
18+
19+
// output file with nofile and set temp path folder
20+
$test = new Encrypt(true);
21+
$out = $test->input('Book1.xlsx')
22+
->setTempPathFolder(__DIR__.DIRECTORY_SEPARATOR.'tmp')
23+
->password('111')
24+
->output();
25+
26+
27+
// output file with set temp path folder
28+
$test = new Encrypt;
29+
$test->input('Book1.xlsx')
30+
->setTempPathFolder(__DIR__.DIRECTORY_SEPARATOR.'tmp')
31+
->password('111')
32+
->output('bb4.xlsx');

src/Encrypt.php

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
66
use OLE;
77
use OLE_PPS_File;
88
use OLE_PPS_Root;
9+
use RuntimeException;
910

1011
class Encrypt
1112
{
1213
private $data;
14+
1315
private $password;
16+
1417
private $noFile = false;
1518

19+
private $tmpPathFolder = null;
20+
1621
public function __construct(bool $nofile = false)
1722
{
1823
$this->noFile = $nofile;
@@ -29,10 +34,10 @@ public function input(string $data)
2934
} else {
3035
$this->data = function () use ($data) {
3136
$fp = fopen($data, 'rb');
32-
if (!$fp) {
37+
if (! $fp) {
3338
throw new Exception('file not found');
3439
}
35-
while (!feof($fp)) {
40+
while (! feof($fp)) {
3641
yield unpack('C*', fread($fp, 4096));
3742
}
3843
fclose($fp);
@@ -45,12 +50,13 @@ public function input(string $data)
4550
public function password(string $password)
4651
{
4752
$this->password = $password;
53+
4854
return $this;
4955
}
5056

5157
public function output(?string $filePath = null)
5258
{
53-
if (!$this->noFile && is_null($filePath)) {
59+
if (! $this->noFile && is_null($filePath)) {
5460
throw new Exception('Output Filepath cannot be NULL when NOFILE is False');
5561
}
5662

@@ -64,7 +70,8 @@ public function output(?string $filePath = null)
6470
$encryptionInfo['package']['blockSize'],
6571
$encryptionInfo['package']['saltValue'],
6672
$packageKey,
67-
$this->data
73+
$this->data,
74+
$this->tmpPathFolder
6875
);
6976

7077
$encryptionInfo['dataIntegrity'] = $this->createDataIntegrity($encryptionInfo, $packageKey, $encryptedPackage['tmpFile']);
@@ -103,12 +110,11 @@ public function output(?string $filePath = null)
103110
$OLE2->append(pack('C*', ...$unpackEncryptedPackage));
104111
}
105112

106-
unlink($encryptedPackage['tmpFile']);
107-
108113
$root = new OLE_PPS_Root(1000000000, 1000000000, [$OLE, $OLE2]);
109114

110115
if ($this->noFile) {
111-
$filePath = tempnam(sys_get_temp_dir(), 'NOFILE');
116+
$tmp = new TempFileManager($this->tmpPathFolder);
117+
$filePath = $tmp->path('NOFILE');
112118
}
113119

114120
$root->save($filePath);
@@ -201,4 +207,15 @@ private function addVerifierHash(array &$encryptionInfo)
201207
$verifierHashValue
202208
);
203209
}
204-
}
210+
211+
public function setTempPathFolder(string $path)
212+
{
213+
if (! is_writable($path)) {
214+
throw new RuntimeException('Temp dir not writable.');
215+
}
216+
217+
$this->tmpPathFolder = $path;
218+
219+
return $this;
220+
}
221+
}

src/PackageEncryptor.php

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ public static function encrypt(
1111
int $blockSize,
1212
array $saltValue,
1313
array $key,
14-
callable $input
14+
callable $input,
15+
?string $tmpPathFolder = null
1516
) {
16-
$tmpOutputChunk = tempnam(sys_get_temp_dir(), 'outputChunk');
17-
$tmpFileHeaderLength = tempnam(sys_get_temp_dir(), 'fileHeaderLength');
18-
$tmpFile = tempnam(sys_get_temp_dir(), 'file');
17+
18+
$tmp = new TempFileManager($tmpPathFolder);
19+
20+
$tmpOutputChunk = $tmp->path('outputChunk');
21+
$tmpFileHeaderLength = $tmp->path('fileHeaderLength');
22+
$tmpFile = $tmp->path('file');
1923

2024
if (is_callable($input) && is_a($in = $input(), 'Generator')) {
2125
$inputCount = 0;
@@ -35,14 +39,11 @@ public static function encrypt(
3539
}
3640

3741
file_put_contents($tmpFileHeaderLength, pack('C*', ...CryptoHelper::createUInt32LEBuffer($inputCount, EncryptionConfig::PACKAGE_OFFSET)));
38-
file_put_contents($tmpFile, file_get_contents($tmpFileHeaderLength) . file_get_contents($tmpOutputChunk));
39-
40-
unlink($tmpOutputChunk);
41-
unlink($tmpFileHeaderLength);
42+
file_put_contents($tmpFile, file_get_contents($tmpFileHeaderLength).file_get_contents($tmpOutputChunk));
4243

4344
return ['tmpFile' => $tmpFile];
4445
}
4546

4647
return [];
4748
}
48-
}
49+
}

src/TempFileManager.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
namespace Nick\SecureSpreadsheet;
4+
5+
use RuntimeException;
6+
7+
class TempFileManager
8+
{
9+
private $baseDir;
10+
11+
private $jobDir;
12+
13+
private $cleaned = false;
14+
15+
public function __construct(?string $preferredDir = null)
16+
{
17+
$this->baseDir = $this->resolveBaseDir($preferredDir);
18+
$this->jobDir = $this->createJobDir();
19+
20+
// request shutdown function to cleanup temp files
21+
register_shutdown_function([$this, 'cleanup']);
22+
}
23+
24+
private function resolveBaseDir(?string $preferredDir): string
25+
{
26+
$candidates = array_filter([
27+
$preferredDir,
28+
'/var/app/tmp',
29+
sys_get_temp_dir(),
30+
]);
31+
32+
foreach ($candidates as $dir) {
33+
if ($this->isUsableDir($dir)) {
34+
return rtrim($dir, '/');
35+
}
36+
}
37+
38+
throw new RuntimeException('No usable temp directory.');
39+
}
40+
41+
private function isUsableDir(string $dir): bool
42+
{
43+
return is_dir($dir)
44+
&& is_writable($dir)
45+
&& ! is_link($dir);
46+
}
47+
48+
private function createJobDir(): string
49+
{
50+
$jobDir = $this->baseDir.'/job_'.bin2hex(random_bytes(8));
51+
52+
if (! mkdir($jobDir, 0700)) {
53+
throw new RuntimeException('Failed to create job temp directory');
54+
}
55+
56+
return $jobDir;
57+
}
58+
59+
public function path(string $name): string
60+
{
61+
return $this->jobDir.'/'.$name;
62+
}
63+
64+
public function cleanup(): void
65+
{
66+
if ($this->cleaned || ! is_dir($this->jobDir)) {
67+
return;
68+
}
69+
70+
$files = glob($this->jobDir.'/*') ?: [];
71+
foreach ($files as $file) {
72+
@unlink($file);
73+
}
74+
75+
@rmdir($this->jobDir);
76+
$this->cleaned = true;
77+
}
78+
}

tests/EncryptorTest.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,25 @@
22

33
namespace Illuminate\Tests\Auth;
44

5-
use PHPUnit\Framework\TestCase;
65
use Nick\SecureSpreadsheet\Encrypt;
6+
use PHPUnit\Framework\TestCase;
77

88
class EncryptorTest extends TestCase
99
{
1010
protected function setUp(): void
1111
{
12-
if (file_exists('bb.xlsx')) unlink('bb.xlsx');
12+
if (file_exists('bb.xlsx')) {
13+
unlink('bb.xlsx');
14+
}
1315
}
1416

15-
public function testEncryptor()
17+
public function test_encryptor()
1618
{
17-
(new Encrypt())->input('Book1.xlsx')->password('111')->output('bb.xlsx');
19+
(new Encrypt)->input('Book1.xlsx')->password('111')->output('bb.xlsx');
1820
$this->assertFileExists('bb.xlsx');
1921
}
2022

21-
public function testEncryptorWithBinaryData()
23+
public function test_encryptor_with_binary_data()
2224
{
2325
$data = 'Book1.xlsx';
2426
$fp = fopen($data, 'rb');
@@ -27,4 +29,10 @@ public function testEncryptorWithBinaryData()
2729
$str = (new Encrypt($nofile = true))->input($binaryData)->password('111')->output();
2830
$this->assertEquals(12288, strlen($str));
2931
}
32+
33+
public function test_encryptor_with_set_temp_path_folder()
34+
{
35+
(new Encrypt)->input('Book1.xlsx')->setTempPathFolder('/tmp')->password('111')->output('bb.xlsx');
36+
$this->assertFileExists('bb.xlsx');
37+
}
3038
}

0 commit comments

Comments
 (0)