Multi-domain Software Updater
This software began from a client's request for an easy way to update the multi-domain licensed Master Form .PHP when there was an upgrade. The client has a number of customers who are using Master Form .PHP on their domains.
The idea was this: Update one domain. Then run a script to update the other domains.
As sometimes happens, the project expanded. The resulting software can transfer more than just PHP scripts. But it still remains within its original intent of updating software files from one domain to another.
The primary challenge was making the system secure, trying to think like a hacker who wanted to get files from someone else's server.
Security
The security was solved this way:
-
At every destination domain, the subdirectory where the files will be updated has a short PHP script. Upload this script once into that directory and you'll be good to go for receiving updates from then on.
-
The source domain also contains a PHP script, the source file sending script. This script needs to be launched in a browser. It can't be nudged from somewhere else and have it send files to an incorrect destination.
-
For additional security, the destination scripts and the source sending script all contain an identical security key, a string of characters that are compared to authorize the file transfer.
Because server access is needed to get the PHP scripts installed, and because of the security key requirement, hackers would need to break into the server itself to use the scripts — and if they did that then they would already have server access and wouldn't need the scripts to wreak havoc.
Features
Security Key —
The security key is a unique string of characters to make each installation secure within itself. Its purpose is to prevent other installations of Multi-domain Software Updater from using your installation.
Multiple Destinations —
The files can be copied from the source directory to any number of directories at other domains.
Overwrite Protection —
A setting in the source sending script tells the destination script whether or not it is okay to overwrite any existing file with the same name. Because this is an updater, overwrite is generally desired. But there may be occasions when it is not okay to do so.
Non-transfer Files —
Specify the names of files in the source directory that are not to be transferred to destination directories. Configuration files are an example; each destination is likely to have its own settings and you don't want to replace them with the wrong ones.
Two Transfer Modes —
There are plain text files and binary files, each type transferred in a different manner. Plain text files generally are source code used for web pages, like PHP, HTML, CSS, JavaScript, and CGI files. A customization in the source sending script tells the software which file extensions are plain text.
File Permissions —
If the file is a Perl CGI script, it can be given 755 permissions automatically when it is saved at the new location.
Because Multi-domain Software Updater is an updater, it will transfer files only from one directory. It will not walk subdirectories to transfer those files — files that are likely to be databases and settings unique to the installation of the domain where they exist.
The Software
As mentioned, Multi-domain Software Updater comes as two PHP scripts. The source sending script sends the file information. The destination script receives the file information.
The two PHP scripts are below, each followed with customization notes.
We'll do the destination script first, for two reasons. It has less customizations. And it needs to be installed before the source sending script customization can be finalized.
The Destination Script —
Save this file on your hard drive as Destination.php or other PHP file name that works for your implementation. The instructions assume Destination.php is its file name.
After customization, upload Destination.php into the subdirectory of every domain that will receive file updates from the source domain. The source sending script will contact Destination.php with information it needs for the file transfer.
<?php /* Multi-domain Software Updater -- Destination Version 1.0 April 20, 2018 Will Bontrager Software LLC https://www.willmaster.com/ This software is provided "AS IS," without any warranty of any kind, without even any implied warranty such as merchantability or fitness for a particular purpose. Will Bontrager Software LLC grants you a royalty free license to use this software provided this notice appears on all copies. */ /* Two places to customize. */ // (1) Specify a security key that the sending script must use // for a valid file transfer. Must be identical to that // specified in Source.php or whatever the source // sending script file name is. */ $CustomKey = ':B6([Xz@z7W/P^R"WR'; // (2) Between the lines with the PERMISSION755 string of // text, type the file name extensions that need to // have 755 permissions. Specifying a period before the // extension is optional. Separate extensions with one // or more linefeeds, spaces, and/or commas in any // combination. $ExtFor755 = <<<PERMISSION755 cgi, .pl PERMISSION755; /* No other customization required. */ /* ******************************** */ $List755 = array(); if( ! count($_POST) ) { ExitWithMessage(); } if( get_magic_quotes_gpc() ) { StripOnlySlashes($_POST); } if( $_POST['CustomKey'] != $CustomKey ) { ExitWithMessage(); } if( empty($_POST['Overwrite']) and file_exists($_POST['FileName']) ) { ExitWithMessage("File {$_POST['FileName']} exists."); } if( isset($_POST['FileURL']) and $_POST['FileURL'] ) { file_put_contents($_POST['FileName'],file_get_contents($_POST['FileURL'])); } elseif( isset($_POST['FileContent']) and $_POST['FileContent'] ) { file_put_contents($_POST['FileName'],$_POST['FileContent']); } foreach( preg_split('/\W+/',strtolower(trim($ExtFor755,",. \t\n\r\0\x0B"))) as $ext ) { $List755[trim($ext)] = true; } $ext = strtolower(preg_replace('/^[^\.]*\./','',$_POST['FileName'])); if( isset($List755[$ext]) ) { chmod($_POST['FileName'],0755); } ExitWithMessage('OK'); function ExitWithMessage($s='Inappropriate access.') { echo $s; exit; } function StripOnlySlashes(&$arr) { array_walk_recursive($arr, 'StripOnlySlashesFromArrayItems'); } function StripOnlySlashesFromArrayItems(&$item, $key) { $item = trim(stripslashes($item)); } ?>
Customizations —
There are two customization steps.
-
Find the
$CustomKey = ':B6([Xz@z7W/P^R"WR';
line in the above source code. The:B6([Xz@z7W/P^R"WR
is a security key. It is recommended to change it before installing the Multi-domain Software scripts.Whatever security key you specify here will need to be specified in the customization area of the source sending script.
-
Between the two lines containing the
PERMISSION755
string of characters, list the file name extensions of files that require 755 permissions. Specifying a period before the extension is optional.Separate extensions with one or more linefeeds, spaces, and/or commas in any combination.
The most common extensions that require 755 permissions are already listed in the script. Add any others that you need.
The Source Sending Script —
Save this file on your hard drive as Source.php or other PHP file name that works for your implementation. The instructions assume Source.php is its file name.
After customization, upload Source.php into the subdirectory that contains the files that will be sent to destination directories where Destination.php is installed. To run Source.php, type its URL into your browser.
<?php /* Multi-domain Software Updater -- Source Sending Version 1.0 April 20, 2018 Will Bontrager Software LLC https://www.willmaster.com/ This software is provided "AS IS," without any warranty of any kind, without even any implied warranty such as merchantability or fitness for a particular purpose. Will Bontrager Software LLC grants you a royalty free license to use this software provided this notice appears on all copies. */ /* Five places to customize. */ // (1) Specify a security key that the sending script must use // for a valid file transfer. Must be identical to that // specified in Destination.php or whatever the destination // script file name is. */ $CustomKey = ':B6([Xz@z7W/P^R"WR'; // (2) Between the lines with the DESTINATIONS string of // text, type the URLs of the Destination.php file for // each of the destinations that are to receive the file // transfer. Type one URL per line. $Destinations = <<<DESTINATIONS http://domain.com/books/Destination.php https://example.com/testing/Destination.php DESTINATIONS; // (3) Is it okay to overwrite existing file with same name // at destination? Specify true for meaning okay to // overwrite and specify false for meaning no overwrite. $Overwrite = false; // Use true or false, no quotes. // (4) Between the lines with the NOTRANSFER string of // text, type the file names that should not transfer // to the destination (the file name of this script // is already exempted). Separate file names with one // or more linefeeds, spaces, and/or commas in any // combination. $ExemptedFiles = <<<NOTRANSFER config.php NOTRANSFER; // (5) Between the lines with the PLAINTEXTSOURCE string of // text, type the file name extensions that are plain // text files. That would be .php, .html, .css, .js, // and other file name extensions that contain only // plain text and are not binary files. Specifying a // period before the extension is optional. Separate // extensions with one or more linefeeds, spaces, // and/or commas in any combination. $PlainTextExtensions = <<<PLAINTEXTSOURCE php html htm xhtml .css, .cgi, pl pm, .js txt PLAINTEXTSOURCE; /* No other customization required. */ /* ******************************** */ $BaseURL = isset($_SERVER['HTTPS']) ? 'https://' : 'http://'; $Message[] = $BaseURL; $BaseURL .= $_SERVER['HTTP_HOST']; $Message[] = $BaseURL; $BaseURL .= (preg_replace('!/[^/]*$!','',$_SERVER['PHP_SELF'])); $Message[] = $BaseURL; $SelfFileName = GetTheFileName($_SERVER['PHP_SELF']); $Dests = $Exempted = $PlainText = array(); foreach( preg_split('/[\r\n]+/',trim($Destinations)) as $url ) { $Dests[] = trim($url); } foreach( preg_split('/[\s,]+/',trim($ExemptedFiles,", \t\n\r\0\x0B")) as $exmp ) { $Exempted[trim($exmp)] = true; } foreach( preg_split('/\W+/',strtolower(trim($PlainTextExtensions,",. \t\n\r\0\x0B"))) as $ext ) { $PlainText[trim($ext)] = true; } foreach( glob('*') as $file ) { if( (!$file) or (!is_file($file)) ) { continue; } $fname = GetTheFileName($file); if( $fname == $SelfFileName ) { continue; } if( isset($Exempted[$fname]) ) { continue; } $postdata = array(); $postdata['FileName'] = $fname; $postdata['CustomKey'] = $CustomKey; $postdata['Overwrite'] = $Overwrite; $ext = strtolower(preg_replace('/^[^\.]*\./','',$fname)); if( empty($PlainText[$ext]) ) { $postdata['FileURL'] = "$BaseURL/$fname"; } else { $postdata['FileContent'] = file_get_contents($file); } foreach( $Dests as $url ) { $options = array( CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false, CURLOPT_CONNECTTIMEOUT => 120, CURLOPT_TIMEOUT => 120, CURLOPT_FOLLOWLOCATION => false, CURLOPT_USERAGENT => "Cross Domain Copy {$_SERVER['PHP_SELF']}", CURLOPT_POST => 1, CURLOPT_POSTFIELDS => $postdata, CURLOPT_VERBOSE => false ); $ch = curl_init($url); curl_setopt_array($ch,$options); $content = curl_exec($ch); $err = curl_errno($ch); $errmsg = curl_error($ch); curl_close($ch); if( $err ) { $content = $errmsg; } echo "<pre>Transfer to $url\n\t$fname: $content</pre>"; } } function GetTheFileName($s) { return preg_replace('!^.*/!','',$s); } ?>
There are five customization steps.
-
This first step is like the first step for the Destination.php script.
Find the
$CustomKey = ':B6([Xz@z7W/P^R"WR';
line in the above source code. The:B6([Xz@z7W/P^R"WR
is a security key. It is recommended to change it before installing the Multi-domain Software scripts.Whatever security key you specify here will need to be specified in the customization area of the Destination.php script.
-
Between the two lines containing the
DESTINATIONS
string of characters, list the URLs of each of the Destination.php scripts installed for this purpose. The URLs may be to any number of domains and to any public directories.Specify each URL on a separate line.
The script source code above has two example URLs listed.
-
The value
false
in the$Overwrite = false;
line tells the software to avoid overwriting any existing files at the destination domains and subdirectories.To allow overwriting, which generally is the case when updating files, replace
false
with the wordtrue
(no quotes). -
Between the two lines containing the
NOTRANSFER
string of characters, type the file names of each file that should not be copied to the destinations. The file name Source.php (or whatever you end up naming the file) is already exempted.Separate file names with one or more linefeeds, spaces, and/or commas in any combination.
-
Between the two lines containing the
PLAINTEXTSOURCE
string of characters, list the file name extensions of files that contain plain text, files that are not binary files. That would be .php, .html, .css, .js, and other file name extensions that contain only plain text and are not binary files. Specifying a period before the extension is optional.Separate extensions with one or more linefeeds, spaces, and/or commas in any combination.
The most common plain text file name extensions are already listed in the script. Add any others that you need.
Software can be updated quickly and easily on many domains by using Multi-domain Software Updater.
(This article first appeared with an issue of the Possibilities newsletter.)
Will Bontrager