Subversion Repositories oidplus

Rev

Rev 652 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
647 daniel-mar 1
#!/usr/bin/env php
2
<?php
3
 
4
/*
5
 * OIDplus 2.0
6
 * Copyright 2019 - 2021 Daniel Marschall, ViaThinkSoft
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
 
21
// This script will be called at the ViaThinkSoft server side
22
 
651 daniel-mar 23
// Generate keypair with:
24
//	openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:8192
25
//	openssl rsa -pubout -in private.pem -out public.pem
26
 
647 daniel-mar 27
$argc = $_SERVER['argc']; // to please Eclipse for PHP
28
$argv = $_SERVER['argv']; // to please Eclipse for PHP
29
 
30
if (PHP_SAPI != 'cli') {
31
	die("This file can only be invoked in CLI mode.\n");
32
}
33
 
651 daniel-mar 34
if ($argc != 4) {
35
	echo "Usage: ".$argv[0]." <targetpath> <privkey> <force(1|0)>\n";
647 daniel-mar 36
	exit(2);
37
}
38
 
39
$output_dir = $argv[1];
651 daniel-mar 40
$priv_key = $argv[2];
41
$force = $argv[3];
647 daniel-mar 42
 
43
if (!is_dir($output_dir)) {
44
	echo "Path $output_dir does not exist!\n";
45
	exit(1);
46
}
47
 
651 daniel-mar 48
if (!file_exists($priv_key)) {
49
	echo "Private key file $priv_key does not exist!\n";
50
	exit(1);
51
}
52
 
53
if (($force != '1') && ($force != '0')) {
54
	echo "Argument 'force' must be 0 or 1\n";
55
	exit(1);
56
}
57
 
647 daniel-mar 58
$outscript = '';
59
 
60
function getDirContents_del($dir_old, $dir_new, $basepath_old=null, $basepath_new=null) {
61
	global $outscript;
62
 
63
	if (is_null($basepath_old)) $basepath_old = $dir_old;
64
	$basepath_old = realpath($basepath_old) . DIRECTORY_SEPARATOR;
65
	if ($basepath_old == '/') die('ARG');
66
 
67
	$dir_old = realpath($dir_old) . DIRECTORY_SEPARATOR;
68
	$dir_new = realpath($dir_new) . DIRECTORY_SEPARATOR;
69
	$files_old = scandir($dir_old);
70
	$files_new = scandir($dir_new);
71
 
72
	foreach ($files_old as $file_old) {
73
		if ($file_old === '.') continue;
74
		if ($file_old === '..') continue;
75
		if ($file_old === '.svn') continue;
76
		if ($file_old === '.git') continue;
77
 
78
		$path_old = realpath($dir_old . DIRECTORY_SEPARATOR . $file_old);
79
		$path_new = realpath($dir_new . DIRECTORY_SEPARATOR . $file_old);
80
 
81
		$xpath_old = substr($path_old, strlen($basepath_old));
82
 
83
		if (is_dir($path_old)) {
84
			getDirContents_del($path_old, $path_new, $basepath_old, $basepath_new);
85
		}
86
 
87
		if (is_dir($path_old) && !is_dir($path_new)) {
88
			$outscript .= "// Dir deleted: $xpath_old\n";
89
			$outscript .= "@rmdir('$xpath_old');\n";
90
			$outscript .= "if (is_dir('$xpath_old'))\n\twarn('Directory could not be deleted (was not empty?): $xpath_old');\n\n";
91
 
92
		} else if (file_exists($path_old) && !file_exists($path_new)) {
93
			$outscript .= "// File deleted: $xpath_old\n";
94
			$outscript .= "@unlink('$xpath_old');\n";
95
			$outscript .= "if (file_exists('$xpath_old'))\n\twarn('File could not be deleted: $xpath_old');\n\n";
96
		}
97
	}
98
}
99
 
100
function getDirContents_diff($dir_old, $dir_new, $basepath_old=null, $basepath_new=null) {
101
	global $outscript;
102
 
103
	if (is_null($basepath_old)) $basepath_old = $dir_old;
104
	$basepath_old = realpath($basepath_old) . DIRECTORY_SEPARATOR;
105
	if ($basepath_old == '/') die('ARG');
106
 
107
	$dir_old = realpath($dir_old) . DIRECTORY_SEPARATOR;
108
	$dir_new = realpath($dir_new) . DIRECTORY_SEPARATOR;
109
	$files_old = scandir($dir_old);
110
	$files_new = scandir($dir_new);
111
 
112
	foreach ($files_old as $file_old) {
113
		if ($file_old === '.') continue;
114
		if ($file_old === '..') continue;
115
		if ($file_old === '.svn') continue;
116
		if ($file_old === '.git') continue;
117
 
118
		$path_old = realpath($dir_old . DIRECTORY_SEPARATOR . $file_old);
119
		$path_new = realpath($dir_new . DIRECTORY_SEPARATOR . $file_old);
120
 
121
		$xpath_old = substr($path_old, strlen($basepath_old));
122
 
123
		if (file_exists($path_old) && file_exists($path_new)) {
124
			if (file_get_contents($path_old) != file_get_contents($path_new)) {
125
				$outscript .= "// Files different: $xpath_old\n";
126
				$outscript .= "if (@sha1_file('$xpath_old') !== '".sha1_file($path_old)."')\n\twarn('File was probably modified. Will overwrite the changes now: $xpath_old');\n";
127
				$outscript .= "@file_put_contents('$xpath_old', base64_decode('".base64_encode(file_get_contents($path_new))."'));\n";
128
				$outscript .= "if (@sha1_file('$xpath_old') !== '".sha1_file($path_new)."')\n\twarn('File cannot be written (checksum mismatch): $xpath_old');\n\n";
129
			}
130
			if ((fileperms($path_old) & 0777) != (fileperms($path_new) & 0777)) {
131
				$outscript .= "// Different file chmod: $xpath_old\n";
132
				$outscript .= "if ((DIRECTORY_SEPARATOR === '/') && !@chmod('$xpath_old', 0".sprintf('%o', fileperms($path_new) & 0777)."))\n\twarn('Could not change file permissions of ".$xpath_old."');\n\n";
133
			}
134
		} else if (is_dir($path_old) && is_dir($path_new)) {
135
			if ((fileperms($path_old) & 0777) != (fileperms($path_new) & 0777)) {
136
				$outscript .= "// Different dir chmod: $xpath_old\n";
137
				$outscript .= "if ((DIRECTORY_SEPARATOR === '/') && !@chmod('$xpath_old', 0".sprintf('%o', fileperms($path_new) & 0777)."))\n\twarn('Could not change dir permissions of ".$xpath_old."');\n\n";
138
			}
139
		}
140
 
141
		if (is_dir($path_old)) {
142
			getDirContents_diff($path_old, $path_new, $basepath_old, $basepath_new);
143
		}
144
	}
145
}
146
 
147
function getDirContents_add($dir_old, $dir_new, $basepath_old=null, $basepath_new=null) {
148
	global $outscript;
149
 
150
	if (is_null($basepath_new)) $basepath_new = $dir_new;
151
	$basepath_new = realpath($basepath_new) . DIRECTORY_SEPARATOR;
152
	if ($basepath_new == '/') die('ARG');
153
 
154
	$dir_old = realpath($dir_old) . DIRECTORY_SEPARATOR;
155
	$dir_new = realpath($dir_new) . DIRECTORY_SEPARATOR;
156
	$files_old = scandir($dir_old);
157
	$files_new = scandir($dir_new);
158
 
159
	foreach ($files_new as $file_new) {
160
		if ($file_new === '.') continue;
161
		if ($file_new === '..') continue;
162
		if ($file_new === '.svn') continue;
163
		if ($file_new === '.git') continue;
164
 
165
		$path_old = realpath($dir_old . DIRECTORY_SEPARATOR . $file_new);
166
		$path_new = realpath($dir_new . DIRECTORY_SEPARATOR . $file_new);
167
 
168
		$xpath_new = substr($path_new, strlen($basepath_new));
169
 
170
		if (is_dir($path_new) && !is_dir($path_old)) {
171
			$outscript .= "// Dir added: $xpath_new\n";
172
			$outscript .= "@mkdir('$xpath_new');\n";
173
			$outscript .= "if (!is_dir('$xpath_new'))\n\twarn('Directory could not be created: $xpath_new');\n";
174
			$outscript .= "else if ((DIRECTORY_SEPARATOR === '/') && !@chmod('$xpath_new', 0".sprintf('%o', fileperms($path_new) & 0777)."))\n\twarn('Could not change directory permissions of ".$xpath_new."');\n\n";
175
		} else if (file_exists($path_new) && !file_exists($path_old)) {
176
			$outscript .= "// File added: $xpath_new\n";
177
			$outscript .= "@file_put_contents('$xpath_new', base64_decode('".base64_encode(file_get_contents($path_new))."'));\n";
178
			$outscript .= "if (!file_exists('$xpath_new'))\n\twarn('File cannot be created (not existing): $xpath_new');\n";
179
			$outscript .= "else if (sha1_file('$xpath_new') != '".sha1_file($path_new)."')\n\twarn('File cannot be created (checksum mismatch): $xpath_new');\n";
180
			$outscript .= "else if ((DIRECTORY_SEPARATOR === '/') && !@chmod('$xpath_new', 0".sprintf('%o', fileperms($path_new) & 0777)."))\n\twarn('Could not change file permissions of ".$xpath_new."');\n\n";
181
		}
182
 
183
		if (is_dir($path_new)) {
184
			getDirContents_add($path_old, $path_new, $basepath_old, $basepath_new);
185
		}
186
	}
187
}
188
 
189
function getDirContents($dir_old, $dir_new) {
190
	getDirContents_add($dir_old, $dir_new);
191
	getDirContents_diff($dir_old, $dir_new);
652 daniel-mar 192
	getDirContents_del($dir_old, $dir_new);
647 daniel-mar 193
}
194
 
195
 
196
$out = array();
197
$ec = -1;
198
exec('svn info https://svn.viathinksoft.com/svn/oidplus/trunk/ | grep "Revision:" | cut -d " " -f 2', $out, $ec);
199
$max_svn = implode("", $out);
200
 
201
 
202
for ($i=2; $i<=$max_svn; $i++) {
203
	echo "SVN revision $i / $max_svn\r";
204
 
205
	$outfile = $output_dir."/update_".($i-1)."_to_$i.txt";
651 daniel-mar 206
	if (!$force && file_exists($outfile)) continue;
647 daniel-mar 207
 
208
	$outdir_old = "/tmp/oidplus_svntmp2_".($i-1)."/";
209
	if (is_dir($outdir_old)) exec("rm -rf $outdir_old", $out, $ec);
210
	exec("svn co https://svn.viathinksoft.com/svn/oidplus/trunk/@".($i-1)." $outdir_old", $out, $ec);
211
 
212
	$outdir_new = "/tmp/oidplus_svntmp2_$i/";
213
	if (is_dir($outdir_new)) exec("rm -rf $outdir_new", $out, $ec);
214
	exec("svn co https://svn.viathinksoft.com/svn/oidplus/trunk/@$i $outdir_new", $out, $ec);
215
 
216
	$outscript  = "<?php\n";
217
	$outscript .= "\n";
218
	$outscript .= "/*\n";
219
	$outscript .= " * OIDplus 2.0\n";
220
	$outscript .= " * Copyright 2019 - ".date('Y')." Daniel Marschall, ViaThinkSoft\n";
221
	$outscript .= " *\n";
222
	$outscript .= " * Licensed under the Apache License, Version 2.0 (the \"License\");\n";
223
	$outscript .= " * you may not use this file except in compliance with the License.\n";
224
	$outscript .= " * You may obtain a copy of the License at\n";
225
	$outscript .= " *\n";
226
	$outscript .= " *     http://www.apache.org/licenses/LICENSE-2.0\n";
227
	$outscript .= " *\n";
228
	$outscript .= " * Unless required by applicable law or agreed to in writing, software\n";
229
	$outscript .= " * distributed under the License is distributed on an \"AS IS\" BASIS,\n";
230
	$outscript .= " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n";
231
	$outscript .= " * See the License for the specific language governing permissions and\n";
232
	$outscript .= " * limitations under the License.\n";
233
	$outscript .= " */\n";
234
	$outscript .= "\n";
235
	$outscript .= "function info(\$str) { echo \"INFO: \$str\\n\"; }\n";
236
	$outscript .= "function warn(\$str) { echo \"WARNING: \$str\\n\"; }\n";
237
	$outscript .= "function err(\$str) { die(\"FATAL ERROR: \$str\\n\"); }\n";
238
	$outscript .= "\n";
652 daniel-mar 239
	$outscript .= "@set_time_limit(0);\n";
240
	$outscript .= "\n";
241
	$outscript .= "@header('Content-Type: text/plain');\n";
242
	$outscript .= "\n";
647 daniel-mar 243
	$outscript .= "chdir(__DIR__);\n";
661 daniel-mar 244
	if ($i >= 662) {
245
		$outscript .= "if (trim(@file_get_contents('.version.php')) !== '<?php // Revision ".($i-1)."')\n\terr('This update can only be applied to OIDplus SVN Rev ".($i-1)."!');\n";
246
	} else {
247
		$outscript .= "if (trim(@file_get_contents('oidplus_version.txt')) !== 'Revision ".($i-1)."')\n\terr('This update can only be applied to OIDplus SVN Rev ".($i-1)."!');\n";
248
	}
647 daniel-mar 249
	$outscript .= "\n";
649 daniel-mar 250
	/*
251
	if ($i >= 99999) {
252
		... once we require PHP 7.1, we add the requirement here
253
		... also if we require fancy new PHP modules, we must add it here
254
		... the checks avoid that someone breaks their OIDplus installation if they update
255
	} else
256
	*/if ($i >= 2) {
257
		// Rev 2+ requires PHP 7.0.0
258
		$outscript .= "if (version_compare(PHP_VERSION, '7.0.0') < 0)\n\terr('You need PHP Version 7.0 to update to this version');\n";
259
	}
260
	$outscript .= "\n";
647 daniel-mar 261
	$outscript .= "info('Update to SVN Rev $i...');\n";
262
	$outscript .= "\n";
263
	getDirContents($outdir_old, $outdir_new);
264
	$outscript .= "\n";
661 daniel-mar 265
	if ($i >= 661) {
266
		$outscript .= "file_put_contents('.version.php', \"<?php // Revision $i\\n\");\n";
267
		$outscript .= "if (trim(@file_get_contents('.version.php')) !== '<?php // Revision $i') err('Could not write to .version.php!');\n";
268
		if ($i == 661) {
269
			$outscript .= "@unlink('oidplus_version.txt');\n";
270
			$outscript .= "if (file_exists('oidplus_version.txt')) err('Could not delete oidplus_version.txt! Please delete it manually');\n";
271
		}
272
	} else {
273
		$outscript .= "file_put_contents('oidplus_version.txt', \"Revision $i\\n\");\n";
274
		$outscript .= "if (trim(@file_get_contents('oidplus_version.txt')) !== 'Revision $i') err('Could not write to oidplus_version.txt!');\n";
275
	}
647 daniel-mar 276
	$outscript .= "\n";
277
	$outscript .= "\n";
278
	$outscript .= "info('Update to SVN Rev $i done!');\n";
279
	$outscript .= "\n";
280
	$outscript .= "unlink(__FILE__);\n";
281
	$outscript .= "\n";
282
 
651 daniel-mar 283
	// Now add digital signature
647 daniel-mar 284
 
651 daniel-mar 285
	if (strpos($outscript, '<?php') === false) {
286
		echo "Not a PHP file\n"; // Should not happen
287
		continue;
288
	}
289
 
290
	$naked = preg_replace('@<\?php /\* <ViaThinkSoftSignature>(.+)</ViaThinkSoftSignature> \*/ \?>\n@ismU', '', $outscript);
291
 
292
	$hash = hash("sha256", $naked.basename($outfile));
293
 
294
	$pkeyid = openssl_pkey_get_private('file://'.$priv_key);
295
	openssl_sign($hash, $signature, $pkeyid, OPENSSL_ALGO_SHA256);
296
	openssl_free_key($pkeyid);
297
 
298
	if (!$signature) {
299
		echo "ERROR: Signature failed\n";
300
		continue;
301
	}
302
 
303
	$sign_line = '<?php /* <ViaThinkSoftSignature>'."\n".split_equal_length(base64_encode($signature),65).'</ViaThinkSoftSignature> */ ?>';
304
 
305
	// We have to put the signature at the beginning, because we don't know if the end of the file lacks a PHP closing tag
306
	if (substr($outscript,0,2) === '#!') {
307
		// Preserve shebang
308
		$shebang_pos = strpos($naked, "\n");
309
		$shebang = substr($naked, 0, $shebang_pos);
310
		$rest = substr($naked, $shebang_pos+1);
311
		$outscript = $shebang."\n".$sign_line."\n".$rest;
312
	} else {
313
		$outscript = $sign_line."\n".$naked;
314
	}
315
 
316
	// Write the file
317
 
647 daniel-mar 318
	file_put_contents($outfile, $outscript);
650 daniel-mar 319
	file_put_contents($outfile.'.gz', gzencode($outscript));
651 daniel-mar 320
 
321
	// Delete temp dirs
322
 
323
	exec("rm -rf $outdir_old", $out, $ec);
324
	exec("rm -rf $outdir_new", $out, $ec);
647 daniel-mar 325
}
326
echo "\n";
648 daniel-mar 327
 
328
 
649 daniel-mar 329
// Now write the release messages (required by software update and vnag)
648 daniel-mar 330
 
331
 
332
$out = array();
333
$ec = 0;
334
exec('svn log https://svn.viathinksoft.com/svn/oidplus/trunk --xml', $out, $ec);
335
if ($ec != 0) die();
336
 
337
$str = implode("\n",$out);
338
 
339
$xml = simplexml_load_string($str);
340
 
341
$out = array();
342
 
343
foreach ($xml as $a) {
344
 
345
	$out[(int)$a->attributes()->revision] = array(
346
 
347
		'date' => date('Y-m-d H:i:s',strtotime((string)$a->date)),
348
		'author' => (string)$a->author,
349
		'msg' => trim((string)$a->msg),
350
 
351
	);
352
 
353
}
354
 
355
ksort($out);
356
 
357
file_put_contents($output_dir.'/releases.ser', serialize($out));
651 daniel-mar 358
 
359
# ---
360
 
361
function split_equal_length($data, $width=65) {
362
	$out = '';
363
	for ($i=0; $i<strlen($data); $i+=$width) {
364
		$out .= substr($data, $i, $width)."\n";
365
	}
366
	return $out;
661 daniel-mar 367
}