├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── ScreenShots ├── Create.png ├── Options.png ├── Overview.png └── Settings.png ├── admin ├── elements │ ├── conf │ │ ├── DEFAULT.ini │ │ └── directadmin.ini.sample │ ├── footer.php │ ├── functions.php │ ├── header.php │ ├── lang │ │ └── English.php │ ├── language.php │ ├── menu.php │ ├── notification.php │ ├── page.php │ ├── pages │ │ ├── create.php │ │ ├── options.php │ │ ├── overview.php │ │ └── settings.php │ ├── process │ │ ├── process_croncreate.php │ │ ├── process_options.php │ │ └── process_settings.php │ └── scripts │ │ ├── backup_job.sh │ │ ├── bash_ini_parser │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README │ │ ├── TODO │ │ └── read_ini.sh │ │ ├── manage_cron.sh │ │ ├── send_mail.sh │ │ ├── software_check.sh │ │ └── update_ini.sh └── index.html ├── hooks └── admin_txt.html ├── images ├── css │ └── default.css └── js │ └── default.js ├── plugin.conf ├── rclone_backup.tar.gz ├── scripts ├── install.sh ├── uninstall.sh └── update.sh └── version.html /.gitignore: -------------------------------------------------------------------------------- 1 | admin/elements/conf/directadmin.ini 2 | gzip.bat 3 | .idea -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v1.0.8](https://github.com/adrianb11/directadmin_rclone_backup/tree/v1.0.8) (2022-06-28) 4 | 5 | [Full Changelog](https://github.com/adrianb11/directadmin_rclone_backup/compare/v1.0.7...v1.0.8) 6 | 7 | **Implemented enhancements:** 8 | 9 | - Remove required from email when creating/editing job. 10 | - Code cleanup 11 | - Fix backup_up script exiting with code 127 12 | - Added option to delete cron from overview page 13 | - Added option to specify mysql login for use with .my.conf or mysql_config_editor 14 | 15 | ## [v1.0.7](https://github.com/adrianb11/directadmin_rclone_backup/tree/v1.0.7) (2022-01-22) 16 | 17 | [Full Changelog](https://github.com/adrianb11/directadmin_rclone_backup/compare/v1.0.6...v1.0.7) 18 | 19 | **Implemented enhancements:** 20 | 21 | - Code cleanup 22 | - Changed API command to get document roots for all users 23 | - Fix for SSL not verifying certificate 24 | 25 | ## [v1.0.6](https://github.com/adrianb11/directadmin_rclone_backup/tree/v1.0.6) (2022-01-22) 26 | 27 | [Full Changelog](https://github.com/adrianb11/directadmin_rclone_backup/compare/v1.0.5...v1.0.6) 28 | 29 | **Implemented enhancements:** 30 | 31 | - Amended README 32 | - Fix for error on install. 33 | - Directadmin.ini no longer overwritten on install/update. 34 | 35 | ## [v1.0.5](https://github.com/adrianb11/directadmin_rclone_backup/tree/v1.0.5) (2022-01-18) 36 | 37 | [Full Changelog](https://github.com/adrianb11/directadmin_rclone_backup/compare/v1.0.4...v1.0.5) 38 | 39 | **Implemented enhancements:** 40 | 41 | - Fix conf folders not being created. 42 | 43 | ## [v1.0.4](https://github.com/adrianb11/directadmin_rclone_backup/tree/v1.0.4) (2022-01-17) 44 | 45 | [Full Changelog](https://github.com/adrianb11/directadmin_rclone_backup/compare/v1.0.3...v1.0.4) 46 | 47 | **Implemented enhancements:** 48 | 49 | - Fix cron create page not loading. 50 | - Code cleanup. 51 | 52 | ## [v1.0.3](https://github.com/adrianb11/directadmin_rclone_backup/tree/v1.0.3) (2022-01-17) 53 | 54 | [Full Changelog](https://github.com/adrianb11/directadmin_rclone_backup/compare/v1.0.2...v1.0.3) 55 | 56 | **Implemented enhancements:** 57 | 58 | - Fix update scripts. 59 | 60 | ## [v1.0.2](https://github.com/adrianb11/directadmin_rclone_backup/tree/v1.0.2) (2022-01-17) 61 | 62 | [Full Changelog](https://github.com/adrianb11/directadmin_rclone_backup/compare/v1.0.1...v1.0.2) 63 | 64 | **Implemented enhancements:** 65 | 66 | - Stored version.html on GitHub for easier updating. 67 | - [Updated plugin.conf to reflect version changes](https://github.com/adrianb11/directadmin_rclone_backup/tree/master/plugin.conf) 68 | 69 | ## [v1.0.1](https://github.com/adrianb11/directadmin_rclone_backup/tree/v1.0.1) (2021-01-17) 70 | 71 | [Full Changelog](https://github.com/adrianb11/directadmin_rclone_backup/compare/v1.0.0...v1.0.1) 72 | 73 | **Implemented enhancements:** 74 | 75 | - [Added ScreenShots](https://github.com/adrianb11/directadmin_rclone_backup/tree/master/ScreenShots) 76 | - [Added README.md](README.md) 77 | 78 | ## [v1.0.0](https://github.com/adrianb11/directadmin_rclone_backup/tree/v1.0.0) (2021-01-16) 79 | 80 | **Initial Release** -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 adrianb11 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DirectAdmin RClone Backup Interface 2 | 3 | ![GitHub release (latest by date)](https://img.shields.io/github/v/release/adrianb11/directadmin_rclone_backup) 4 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) 5 | [![Scrutinizer code quality (GitHub/Bitbucket)](https://img.shields.io/scrutinizer/quality/g/adrianb11/directadmin_rclone_backup/master?color=teal)](https://scrutinizer-ci.com/g/adrianb11/directadmin_rclone_backup/?branch=master) 6 | 7 | ## Description 8 | 9 | This is a DirectAdmin plugin that creates Cron Jobs to backup files/databases and uses RClone to upload them to a 10 | filehost. 11 | 12 | * Files can be compressed into zip, tar, and gzip formats. 13 | * MySQL, PostgreSQL, and MongoDB databases are supported. 14 | 15 | This was built to make my life easier and because of this, is tailored more to my needs. 16 | 17 | Built for DirectAdmin 1.61.5 using Evolution skin. I can not guarantee other DA versions or skins will work correctly. I 18 | have also only tested this with Dropbox but other hosts should work too. My DA license is only for 1 account, so I 19 | haven't tested this on a multi account server. 20 | 21 | **Warning** - This plugin adds jobs to the root crontab and runs shell scripts as root. This is so all domains can be 22 | backed up from a single account. This also makes backing up databases easier as password does not need to be recorded. 23 | While this is not strictly necessary on a single account license (can be run as admin), files not accessible by admin 24 | cannot be backed up. If this makes you nervous, please do not install. 25 | 26 | ### Requirements 27 | 28 | * [RClone](https://github.com/rclone/rclone) 29 | * RClone remotes already created. 30 | 31 | ### Installation 32 | 33 | To install, simple download the package and install via the package manager in DirectAdmin. During installation, this 34 | plugin will check if RClone and other packages are installed 35 | 36 | ### How It Works 37 | 38 | There are 4 main sections to this plugin:- 39 | 40 | * **Configuration files** 41 | * Each job has its details saved into an .ini file which is used for the actual backup process and to display 42 | details on the web interface. 43 | * **The web interface** 44 | * This allows you to create, edit jobs and view any jobs which have been created. 45 | * Jobs which have been created are separated into active, inactive, and pending categories. 46 | * Each job on the overview tab can be expanded to show its details. 47 | * **manage_cron script** 48 | * This shell script is used to manage any newly created or edited jobs. 49 | * On install, a new cronjob is added runs manage_cron.sh on a regular basis. 50 | * On each run, this will scan for any newly created or edited jobs and create a new cronjob for them. 51 | * The cron created will include a path to the .ini for that job. 52 | * **backup_job script** 53 | * This is responsible for the actual backup of files and execution of RClone. 54 | * The backup_job script will use the .ini file passed to create a backup of files and databases requested. 55 | 56 | ### Usage 57 | 58 | * **Once installed, check "View Settings" tab to view software installed.** 59 | * Software check can be re-run individually or collectively. 60 | * manage_cron.sh script can be run from here instead of waiting from scheduled cronjob to run. 61 | * **Enter default options in "View/Change Options" tab.** 62 | * Language Selection:- 63 | * Language: Any language files created will be displayed in this dropdown box. 64 | * Default Compression:- 65 | * Compression: Sets the default compression method to use. 66 | * File Host:- 67 | * File Host Root Path: Sets the root path where files should be uploaded. This will be appended with the full 68 | directory path created. 69 | * Email:- 70 | * Enabled: Sets whether emailed reports should be sent by default. 71 | * Default Send Email: This is the email address reports will be sent from. 72 | * RClone:- 73 | * Available Remotes: List all available remotes you wish to be used. All remotes must be separated by a comma. 74 | * Folder Structure: Sets the path to be appended to Root Path. Options are **Case Sensitive!** 75 | * **Create a new job on the "Create/Edit Cronjob" tab.** 76 | * Cron Creator:- 77 | * Selectable fields to create cronjob times. 78 | * Links to select every minute, hour, day, month, and week day. 79 | * Cron Preset Templates:- 80 | * Clickable preset templates which automatically fill in cron time. 81 | * Settings:- 82 | * Enabled: Sets whether this job should be enabled (creates ini and cronjob) or disabled (creates ini only). 83 | * File Host Root Path: Sets the root path where files should be uploaded. This will be appended with the full 84 | directory path created. Field taken from default option saved in options tab. 85 | * List of all remotes saved in options tab. 86 | * Folder Structure: Sets the path to be appended to Root Path. Options are **Case Sensitive!**. Field taken from 87 | default option saved in options tab. 88 | * DirectAdmin:- 89 | * DirectAdmin Username: A selectable list of all users. 90 | * DirectAdmin Domain: A selectable list of all domains. When a username is selected, this field will only show 91 | domains assigned to it. 92 | * DirectAdmin Sub-Domain: A selectable list of all sub-domains. When a domain is selected, this field will only 93 | show sub-domains assigned to it. 94 | * Backup Path: This is automatically generated based on the username, domain, and sub-domain fields. Field can 95 | be edited to backup a specific folder. 96 | * Exclude Path: Specify the full path to a folder you wish to exclude from the backup. 97 | * Database To Backup: A dropdown list of all databases. 98 | * Database Type: Select the database type. 99 | * Compression: Sets the compression method to use. Field taken from default option saved in options tab. 100 | * Email Log:- 101 | * Send Email Notifications: Sets whether a notification should be sent when cron is completed. 102 | * Email Address: The email address report should be sent to. 103 | * **View created jobs on "Cron Overview" tab.** 104 | * Active: Displays all active jobs. 105 | * Inactive: Displays all inactive jobs. 106 | * Pending: Displays all pending jobs. 107 | 108 | ### Support 109 | 110 | If you find any issues or have a feature 111 | request, [please create a new issue](https://github.com/adrianb11/directadmin_rclone_backup/issues). 112 | 113 | ### ScreenShots 114 | 115 | #### Overview 116 | 117 | ![](https://github.com/adrianb11/directadmin_rclone_backup/raw/master/ScreenShots/Overview.png) 118 | 119 | #### Create/Edit Cronjob 120 | 121 | ![](https://github.com/adrianb11/directadmin_rclone_backup/raw/master/ScreenShots/Create.png) 122 | 123 | #### View/Change Options 124 | 125 | ![](https://github.com/adrianb11/directadmin_rclone_backup/raw/master/ScreenShots/Options.png) 126 | 127 | #### View Settings 128 | 129 | ![](https://github.com/adrianb11/directadmin_rclone_backup/raw/master/ScreenShots/Settings.png) 130 | 131 | ### Acknowledgments 132 | 133 | - [Thomas Ba - Cron Expression Generator](https://github.com/thomasba/cron-expression-generator) 134 | 135 | ### License 136 | 137 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. -------------------------------------------------------------------------------- /ScreenShots/Create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianb11/directadmin_rclone_backup/78df562ea90126f8a6b1e66c133fa69c1c8e7094/ScreenShots/Create.png -------------------------------------------------------------------------------- /ScreenShots/Options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianb11/directadmin_rclone_backup/78df562ea90126f8a6b1e66c133fa69c1c8e7094/ScreenShots/Options.png -------------------------------------------------------------------------------- /ScreenShots/Overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianb11/directadmin_rclone_backup/78df562ea90126f8a6b1e66c133fa69c1c8e7094/ScreenShots/Overview.png -------------------------------------------------------------------------------- /ScreenShots/Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianb11/directadmin_rclone_backup/78df562ea90126f8a6b1e66c133fa69c1c8e7094/ScreenShots/Settings.png -------------------------------------------------------------------------------- /admin/elements/conf/DEFAULT.ini: -------------------------------------------------------------------------------- 1 | [REFERENCE] 2 | in_progress = false 3 | last_ran = 4 | times_ran = 5 | 6 | [ACTIVE] 7 | active = false 8 | 9 | [CRON] 10 | cron_id = * 11 | cron_output_minutes = * 12 | cron_output_hours = * 13 | cron_output_dom = * 14 | cron_output_months = * 15 | cron_output_dow = * 16 | 17 | [RCLONE] 18 | remote = 19 | structure = UDSymd 20 | 21 | [FILEHOST] 22 | filehost_root_path = 23 | 24 | [DIRECTADMIN_WWW] 25 | directadmin_user = 26 | directadmin_domain = 27 | directadmin_subdomain = 28 | directadmin_backup_path = 29 | directadmin_exclude_path = 30 | 31 | [DIRECTADMIN_DB] 32 | database_name = 33 | database_type = 34 | 35 | [COMPRESSION] 36 | compression = gzip 37 | 38 | [EMAIL] 39 | send_email_enabled = false 40 | send_email_address = 41 | -------------------------------------------------------------------------------- /admin/elements/conf/directadmin.ini.sample: -------------------------------------------------------------------------------- 1 | [SETTINGS] 2 | developmentmode = false 3 | version = 1.0.7 4 | ignore_certificate = 0 5 | my_conf_login = 6 | my_conf_enabled = 0 7 | 8 | [LANGUAGE] 9 | language = English 10 | 11 | [RCLONE] 12 | remotes = 13 | structure = UDSymd 14 | 15 | [FILEHOST] 16 | filehost_root_path = 17 | 18 | [DIRECTADMIN_WWW] 19 | directadmin_backup_tmp = 20 | 21 | [COMPRESSION] 22 | compression = tgz 23 | 24 | [EMAIL] 25 | send_email_enabled = false 26 | send_email_address = 27 | 28 | [INSTALLED] 29 | rclone_installed = false 30 | zip_installed = false 31 | mysql_installed = false 32 | postgresql_installed = false 33 | mongodb_installed = false 34 | -------------------------------------------------------------------------------- /admin/elements/footer.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /admin/elements/functions.php: -------------------------------------------------------------------------------- 1 | $values) { 25 | //append the section 26 | $content .= "[" . $section . "]\n"; 27 | //append the values 28 | foreach ($values as $key => $value) { 29 | $content .= $key . "=" . $value . "\n"; 30 | } 31 | } 32 | 33 | //write it into file 34 | if (!$handle = fopen($filepath, 'w')) { 35 | return false; 36 | } 37 | $success = fwrite($handle, $content); 38 | fclose($handle); 39 | return $success; 40 | } 41 | 42 | 43 | /** 44 | * Get directory contents into array and return. 45 | * @param $dir 46 | * @return array 47 | */ 48 | function GetDirContents($dir) 49 | { 50 | return array_diff(scandir($dir), array('.', '..')); 51 | } 52 | 53 | 54 | /** 55 | * Save POST to config_data variable 56 | * @param $config_data 57 | * @param $exclude 58 | * @param $post 59 | * @return array 60 | */ 61 | function WriteConfigData($config_data, $exclude, $post) 62 | { 63 | foreach ($config_data as $sectionKEY => $sectionVALUE) { 64 | 65 | //Check if KEY is in exclude list 66 | if (!in_array($sectionKEY, $exclude)) { 67 | foreach ($sectionVALUE as $elementKEY => $elementVALUE) { 68 | if (isset($post[$elementKEY])) { 69 | $config_data[$sectionKEY][$elementKEY] = $post[$elementKEY]; 70 | } else { 71 | $config_data[$sectionKEY][$elementKEY] = ''; 72 | } 73 | } 74 | } 75 | } 76 | return $config_data; 77 | } 78 | 79 | 80 | /** 81 | * Function to communicate with DirectAdmin API without username and password. 82 | * Uses session data so user must be logged in. 83 | * @param $cmd 84 | * @return string 85 | */ 86 | function getApi($cmd) 87 | { 88 | $directadminarray = ReadINI("/usr/local/directadmin/plugins/rclone_backup/admin/elements/conf/directadmin.ini"); 89 | 90 | $addr = ""; 91 | 92 | if ($_SERVER["SSL"] === "1") 93 | { 94 | $addr .= "https://"; 95 | } 96 | 97 | $addr .= $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $cmd; 98 | 99 | try { 100 | $ch = curl_init($addr); 101 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 102 | curl_setopt($ch, CURLOPT_COOKIE, "session=" . $_SERVER["SESSION_ID"] . "; key=" . $_SERVER["SESSION_KEY"]); 103 | 104 | if ($directadminarray["SETTINGS"]["ignore_certificate"] === "1") { 105 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 106 | } 107 | 108 | $content = curl_exec($ch); 109 | 110 | // Check the return value of curl_exec() 111 | if ($content === false) { 112 | throw new Exception(curl_error($ch), curl_errno($ch)); 113 | } 114 | 115 | return curl_exec($ch); 116 | } catch(Exception $e) { 117 | 118 | if ($e->getCode() == "60") 119 | { 120 | echo "Fatal error: Curl failed with error #60: Please enable ignore peer certificates in plugin options"; 121 | } else { 122 | trigger_error(sprintf( 123 | 'Curl failed with error #%d: %s', 124 | $e->getCode(), $e->getMessage()), 125 | E_USER_ERROR); 126 | } 127 | } finally { 128 | // Close curl handle unless it failed to initialize 129 | if (is_resource($ch)) { 130 | curl_close($ch); 131 | } 132 | } 133 | } 134 | 135 | /** 136 | * Shortcut to print an array. 137 | * @param $array 138 | */ 139 | function printArray($array) 140 | { 141 | echo "
";
142 |     print_r($array);
143 |     echo "
"; 144 | } -------------------------------------------------------------------------------- /admin/elements/header.php: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 11 | 12 | 13 | 15 | 16 | "Monday", 6 | "TUESDAY" => "Tuesday", 7 | "WEDNESDAY" => "Wednesday", 8 | "THURSDAY" => "Thursday", 9 | "FRIDAY" => "Friday", 10 | "SATURDAY" => "Saturday", 11 | "SUNDAY" => "Sunday", 12 | 13 | //Months 14 | "JANUARY" => "January", 15 | "FEBRUARY" => "February", 16 | "MARCH" => "March", 17 | "APRIL" => "April", 18 | "MAY" => "May", 19 | "JUNE" => "June", 20 | "JULY" => "July", 21 | "AUGUST" => "August", 22 | "SEPTEMBER" => "September", 23 | "OCTOBER" => "October", 24 | "NOVEMBER" => "November", 25 | "DECEMBER" => "December", 26 | 27 | //Times 28 | "MINUTES" => "Minutes", 29 | "EVERY_MINUTE" => "Every Minute", 30 | "HOURS" => "Hours", 31 | "EVERY_HOUR" => "Every Hour", 32 | "MONTH" => "Month", 33 | "EVERY_MONTH" => "Every Month", 34 | "WEEK_DAY" => "Week Day", 35 | "EVERY_WEEK_DAY" => "Every Week Day", 36 | "DAY_OF_MONTH" => "Day Of Month", 37 | "EVERY_DAY" => "Every Day", 38 | "SELECT_ALL" => "Select All", 39 | "EVERY_5_MINUTES" => "Every 5 Minutes", 40 | "EVERY_15_MINUTES" => "Every 15 Minutes", 41 | "EVERY_30_MINUTES" => "Every 30 Minutes", 42 | "EVERY_3_HOURS" => "Every 3 Hours", 43 | "EVERY_4_HOURS" => "Every 4 Hours", 44 | "EVERY_6_HOURS" => "Every 6 Hours", 45 | "EVERY_12_HOURS" => "Every 12 Hours", 46 | "EVERY_DAY_AT" => "Every Day At", 47 | "EVERY_SUNDAY_AT" => "Every Sunday At", 48 | 49 | //Status 50 | "ENABLED" => "Enabled", 51 | "DISABLED" => "Disabled", 52 | "active" => "Active", 53 | "inactive" => "Inactive", 54 | "pending" => "Pending", 55 | "NOT_INSTALLED" => "NOT INSTALLED", 56 | "INSTALLED" => "INSTALLED", 57 | "TRUE" => "True", 58 | "FALSE" => "False", 59 | 60 | //Page Titles 61 | "OVERVIEW_PAGE_TITLE" => "Cron Overview", 62 | "CREATE_NEW_CRONJOB_TITLE" => "Create/Edit Cronjob", 63 | "OPTIONS_PAGE_TITLE" => "View/Change Options", 64 | "SETTINGS_PAGE_TITLE" => "View Settings", 65 | "RCLONE_NOT_FOUND" => "RClone Not Found", 66 | 67 | //Sentences 68 | "RCLONE_NOT_FOUND_DESC" => "RClone was not found on your system. Please check if it is installed correctly.", 69 | "LANGUAGE_DESC" => "Please select a language file. All currently available languages will be displayed.", 70 | "COMPRESSION_DESC" => "Please select which compression method to use. Available options are: Zip, tar, and gzip.", 71 | "DEFAULT_FILEHOST_ROOT_PATH_DESC" => "Please enter the default file host root path.", 72 | "FILEHOST_ROOT_PATH_DESC" => "Please enter the file host root path.", 73 | "ENABLED_DESC" => "Please select whether this should be enabled or disabled.", 74 | "DEFAULT_EMAIL_DESC" => "Please enter the default email address reports should be sent from.", 75 | "EMAIL_DESC" => "Please enter the email address reports should be sent to.", 76 | "AVAILABLE_REMOTES_DESC" => "Please enter available remotes. Multiple remotes should be separated by a comma.", 77 | "AVAILABLE_REMOTES_SELECT_DESC" => "Please select a remote to use.", 78 | "DEFAULT_FOLDER_STRUCTURE_DESC" => "Please enter the folder structure to use.", 79 | "FOLDER_STRUCTURE_DESC" => 'Available options are: U D S o e a y m d
e.g.
UDSo = admin/yourdomain.com/sub.yourdomain.com/Monthly/
UDSymd = admin/yourdomain/sub.yourdomain.com/2021/01/01/', 80 | "DIRECTADMIN_USERNAME_DESC" => "Please select the DirectAdmin username.", 81 | "DIRECTADMIN_DOMAIN_DESC" => "Please select the DirectAdmin domain.", 82 | "DIRECTADMIN_SUBDOMAIN_DESC" => "Please select the DirectAdmin sub-domain (not required).", 83 | "DIRECTADMIN_BACKUP_PATH_DESC" => "This backup path has been automatically generated. Please amend the path if required.", 84 | "DIRECTADMIN_EXCLUDE_PATH_DESC" => "If required, please enter the full path to a directory you wish to exclude from the backup (not required).", 85 | "DIRECTADMIN_DATABASE_DESC" => "Please select a database to backup (not required).", 86 | "DIRECTADMIN_TYPE_DESC" => "Please select the database type (not required).", 87 | "IGNORE_CERTIFICATE_DESC" => "Set to TRUE if root certificate cannot be verified.", 88 | "MYSQL_CONF_ENABLED_DESC" => "Set to TRUE to use .my.cnf file or mysql_config_editor to backup MySQL databases.", 89 | "MYSQL_CONF_LOGIN_DESC" => "Enter the username which has been added to your root .my.cnf file or mysql_config_editor.", 90 | 91 | 92 | //Operations 93 | "RECHECK_SOFTWARE" => "Recheck Installed Software", 94 | "RECHECK_ALL" => "Recheck All", 95 | "RUN" => "Run", 96 | "CREATE" => "Create", 97 | "SAVE_CRON" => "Save Cron", 98 | "SAVE_OPTIONS" => "Save Options", 99 | "EDIT" => "Edit", 100 | "SUBMIT" => "Submit", 101 | "RUN_SCRIPTS" => "Run Scripts", 102 | "NEW_CRON_CREATED" => "New cron created successfully.", 103 | "CRON_EDITED" => "Cron was edited successfully.", 104 | "DEFAULT_OPTIONS_SAVED" => "Default options were saved successfully.", 105 | "DELETE" => "Delete", 106 | "DELETE_CRON" => "Cron has been deleted.", 107 | "DELETE_CRON_CONFIRM" => "Are you sure you want to delete cron?", 108 | 109 | "LANGUAGE_SELECTION" => "Language Selection", 110 | "LANGUAGE" => "Language", 111 | "SETTINGS" => "Settings", 112 | "SELECT_OPTION" => "Select option", 113 | 114 | //Directories 115 | "FILEHOST_ROOT_PATH" => "File Host Root Path", 116 | "DEFAULT_FILEHOST_ROOT_PATH" => "Default File Host Root Path", 117 | "DEFAULT_FOLDER_STRUCTURE" => "Default Folder Structure", 118 | "FOLDER_STRUCTURE" => "Folder Structure", 119 | "BACKUP_PATH" => "Backup Path", 120 | "EXCLUDE_FOLDER" => "Exclude Folder", 121 | 122 | //Email 123 | "DEFAULT_SEND_EMAIL" => "Default Send Email", 124 | "EMAIL_LOG" => "Email Log", 125 | "SEND_EMAIL_NOTIFICATIONS" => "Send Email Notifications", 126 | "EMAIL_ADDRESS" => "Email Address", 127 | "EMAIL" => "Email", 128 | 129 | //Cron 130 | "CRON_CREATOR" => "Cron Creator", 131 | "CRON_PRESET_TEMPLATES" => "Cron Preset Templates", 132 | "CRON_OUTPUT" => "Cron Output", 133 | "RUN_CRON_MANAGE" => "Run Cron Manager", 134 | 135 | //RClone 136 | "RCLONE" => "RClone", 137 | "AVAILABLE_REMOTES" => "Available Remotes", 138 | "RCLONE_REMOTE" => "RClone Remote", 139 | 140 | //DirectAdmin 141 | "DIRECTADMIN" => "DirectAdmin", 142 | "DIRECTADMIN_USERNAME" => "DirectAdmin Username", 143 | "DIRECTADMIN_DOMAIN" => "DirectAdmin Domain", 144 | "DIRECTADMIN_SUBDOMAIN" => "DirectAdmin Sub-Domain", 145 | 146 | //Database 147 | "DATABASE_NAME" => "Database Name", 148 | "DATABASE_TYPE" => "Database Type", 149 | "DATABASE_TO_BACKUP" => "Database To Backup", 150 | "MYSQL_CONF_ENABLED" => "Enable .my.cnf file or mysql_config_editor", 151 | "MYSQL_CONF_LOGIN" => "Username from .my.cnf or mysql_config_editor", 152 | 153 | //Compression 154 | "DEFAULT_COMPRESSION" => "Default Compression", 155 | "COMPRESSION" => "Compression", 156 | 157 | "FILEHOST" => "File Host", 158 | 159 | //SSL 160 | "IGNORE_CERTIFICATE" => "Ignore Peer Certificate" 161 | ); -------------------------------------------------------------------------------- /admin/elements/language.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | -------------------------------------------------------------------------------- /admin/elements/notification.php: -------------------------------------------------------------------------------- 1 | 7 |
8 | 12 | 19 |
20 | 30 | 33 |
34 | 50 | 2 |
3 | 4 |
5 | -------------------------------------------------------------------------------- /admin/elements/pages/create.php: -------------------------------------------------------------------------------- 1 | elements. 26 | */ 27 | $domainJSON = getApi("/CMD_API_DOMAIN?action=document_root"); 28 | $domainRESULT = json_decode($domainJSON, true); 29 | if (json_last_error() != 0) { 30 | // JSON is not valid 31 | $domainRESULT = array(); 32 | } 33 | 34 | foreach ($domainRESULT["users"] as $userKEY => $userVALUE) { 35 | if ($config_data["DIRECTADMIN_WWW"]["directadmin_user"] == $userKEY) { 36 | $directadmin_user_select .= ""; 37 | } else { 38 | $directadmin_user_select .= ""; 39 | } 40 | 41 | foreach ($userVALUE["domains"] as $domainsKEY => $domainVALUE) { 42 | if ($config_data["DIRECTADMIN_WWW"]["directadmin_domain"] == $domainsKEY) { 43 | $directadmin_domain_select .= ""; 44 | } else { 45 | $directadmin_domain_select .= ""; 46 | } 47 | 48 | foreach ($domainVALUE["subdomains"] as $subdomainsKEY => $subdomainsVALUE) { 49 | if ($config_data["DIRECTADMIN_WWW"]["directadmin_subdomain"] == $subdomainsKEY) { 50 | $directadmin_subdomain_select .= ""; 51 | } else { 52 | $directadmin_subdomain_select .= ""; 53 | } 54 | } 55 | } 56 | } 57 | 58 | 59 | /** 60 | * Get list of all databases. 61 | * Used to create the elements. 80 | */ 81 | $remotes = explode(',', $directadminarray["RCLONE"]["remotes"]); 82 | foreach ($remotes as $remote) { 83 | if ($config_data["RCLONE"]["remote"] == $remote) { 84 | $remote_select .= ""; 85 | } else { 86 | $remote_select .= ""; 87 | } 88 | } 89 | 90 | ?> 91 | 92 | 95 |
96 |
97 | 98 | 101 |
102 |
103 | 104 |
105 |
106 |
107 |
108 | 171 |
172 |
173 | 200 |
201 |
202 | 236 |
237 |
238 | 253 |
254 |
255 | 265 |
266 |
267 |
268 |
269 |
270 | 271 | 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 | 310 |
311 |
312 | 313 |
314 |
315 |
316 |
317 |
318 | " 319 | id="cron_output_minutes" name="cron_output_minutes" class="form-control"/> 320 | 321 |
322 |
323 |
324 |
325 | " 326 | id="cron_output_hours" name="cron_output_hours" class="form-control"/> 327 | 328 |
329 |
330 |
331 |
332 | " id="cron_output_dom" 333 | name="cron_output_dom" class="form-control"/> 334 | 335 |
336 |
337 |
338 |
339 | " 340 | id="cron_output_months" name="cron_output_months" class="form-control"/> 341 | 342 |
343 |
344 |
345 |
346 | " id="cron_output_dow" 347 | name="cron_output_dow" class="form-control"/> 348 | 349 |
350 |
351 |
352 |
353 |
354 |
355 | 356 | 359 |
360 |
361 | 362 |
363 |
364 |
365 |
366 |
367 | 369 | 371 | 373 | 375 | 377 | 379 |
380 |
381 |
382 |
383 | 385 | 387 | 389 | 12:30am 391 | 12:10am 393 |
394 |
395 |
396 |
397 |
398 |
399 | 400 | 403 |
404 |
405 |
406 | 407 |
408 |
409 |
410 |
411 | 419 | 420 |
421 |
422 |
423 |
424 |
425 | 426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 | " required/> 437 | 438 |
439 |
440 |
441 |
442 |
443 | 444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 | 456 | 457 |
458 |
459 |
460 |
461 |
462 | 463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 | " 473 | oninput="this.value = this.value.replace(/[^UDSoeaymd]/, '')"/> 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 | 502 |
503 |
504 |
505 | 506 |
507 |
508 |
509 |
510 | 514 | 515 |
516 |
517 |
518 |
519 |
520 | 521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 | 533 | 534 |
535 |
536 |
537 |
538 |
539 | 540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 | 552 | 553 |
554 |
555 |
556 |
557 |
558 | 559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 | " 570 | required/> 571 | 572 |
573 |
574 |
575 |
576 |
577 | 578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 | "/> 589 | 590 |
591 |
592 |
593 |
594 |
595 | 596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 | 608 | 609 |
610 |
611 |
612 |
613 |
614 | 615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 | 656 | 657 |
658 |
659 |
660 |
661 |
662 | 663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 | 693 | 694 |
695 |
696 |
697 |
698 |
699 | 700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 | 708 | 711 |
712 |
713 |
714 | 715 |
716 |
717 |
718 |
719 | 727 | 728 |
729 |
730 |
731 |
732 |
733 | 734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 | "/> 745 | 746 |
747 |
748 |
749 |
750 |
751 | 752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 | 760 | 763 | " /> 767 | " /> 771 | 774 |
775 |
776 |
777 |
778 |
779 | 780 | 781 | 782 | 784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 | 792 | 854 | -------------------------------------------------------------------------------- /admin/elements/pages/options.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 |
14 |
15 | 16 | 19 |
20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 | 42 | 43 |
44 |
45 |
46 |
47 |
48 | 49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | 57 | 60 |
61 |
62 |
63 | 64 |
65 |
66 |
67 |
68 | 90 | 91 |
92 |
93 |
94 |
95 |
96 | 97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | 105 | 108 |
109 |
110 |
111 | 112 |
113 |
114 |
115 |
116 | "/> 119 | 120 |
121 |
122 |
123 |
124 |
125 | 126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | 134 | 137 |
138 |
139 |
140 | 141 |
142 |
143 |
144 |
145 | 157 | 158 |
159 |
160 |
161 |
162 |
163 | 164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 | "/> 175 | 176 |
177 |
178 |
179 |
180 |
181 | 182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 | 190 | 193 |
194 |
195 |
196 | RClone 197 |
198 |
199 |
200 |
201 | "/> 203 | 204 |
205 |
206 |
207 |
208 |
209 | 210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 | " 220 | oninput="this.value = this.value.replace(/[^UDSoeaymd]/, '')"/> 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 | 249 |
250 |
251 |
252 | 253 |
254 |
255 |
256 |
257 | 269 |
270 |
271 |
272 |
273 |
274 | 275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 | 283 | 286 |
287 |
288 |
289 | 290 |
291 |
292 |
293 |
294 | 306 |
307 |
308 |
309 |
310 |
311 | 312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 | " 321 | /> 322 | 323 |
324 |
325 |
326 |
327 |
328 | 329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 | 337 | 340 |
341 |
342 |
343 |
344 |
345 | 346 | 348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 | -------------------------------------------------------------------------------- /admin/elements/pages/overview.php: -------------------------------------------------------------------------------- 1 | GetDirContents("/usr/local/directadmin/plugins/rclone_backup/admin/elements/conf/active"), 7 | "inactive" => GetDirContents("/usr/local/directadmin/plugins/rclone_backup/admin/elements/conf/inactive"), 8 | "pending" => GetDirContents("/usr/local/directadmin/plugins/rclone_backup/admin/elements/conf/pending") 9 | ); 10 | ?> 11 |
12 |
13 |
14 | $dirContentsValue) { 15 | ?> 16 |
17 |

18 | 25 |

26 |
28 |
29 |
30 | 34 |
35 |

"> 37 | 44 |

45 |
" 46 | class="accordion-collapse collapse" 47 | aria-labelledby="heading-" 48 | data-bs-parent="#accordion-"> 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 | 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 | 125 |
126 |
127 |
128 | 131 |
132 |
133 |
134 |
135 | 138 |
139 |
140 | -------------------------------------------------------------------------------- /admin/elements/pages/settings.php: -------------------------------------------------------------------------------- 1 | 4 |
5 | 6 | 9 |
10 |
11 |
12 | 13 |
14 | 17 | 20 |
21 |
22 |
23 | 25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | * */ 39 | ?> 40 | 43 |
44 |
45 |
46 | 47 |
48 | 49 | 52 |
53 |
54 |
55 | 57 |
58 |
59 |
60 |
61 |
62 | ZIP 64 | 65 | 66 | 67 | 68 |
69 |
70 |
71 |
72 |
73 | 74 | 77 |
78 |
79 |
80 | 82 |
83 |
84 |
85 |
86 |
87 | MySQL 89 | 90 | 91 | 92 | 93 |
94 |
95 |
96 |
97 |
98 | 99 | 102 |
103 |
104 |
105 | 107 |
108 |
109 |
110 |
111 |
112 | PostgreSQL 114 | 115 | 116 | 117 | 118 |
119 |
120 |
121 |
122 |
123 | 124 | 127 |
128 |
129 |
130 | 132 |
133 |
134 |
135 |
136 |
137 | MongoDB 139 | 140 | 141 | 142 | 143 |
144 |
145 |
146 |
147 |
148 | 149 | 152 |
153 |
154 |
155 | 157 |
158 |
159 |
160 |
161 |
162 | RClone 164 | 165 | 166 | 167 | 168 |
169 |
170 |
171 |
172 |
173 | 174 | 177 |
178 |
179 |
180 | 182 |
183 |
184 |
185 |
186 |
187 | 188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | -------------------------------------------------------------------------------- /admin/elements/process/process_croncreate.php: -------------------------------------------------------------------------------- 1 | /dev/null 2>&1 197 | else 198 | zip -r "$TMP_BACKUP_DIR/$CRON_ID/$backup_file" "$BACKUP_PATH" -x "${EXCLUDE_PATH%/}/*" >/dev/null 2>&1 # remove slash at the end 199 | fi 200 | ret=$? 201 | if ! [ "$ret" -eq 0 ]; then 202 | send_mail " ERR" "Something went wrong while directory backup; zip: RETURN $ret" 203 | clear_backup 204 | exit 1 205 | fi 206 | ;; 207 | gzip) 208 | log "INFO" "Backup data to gzip-Archive" 209 | if [ -z "$EXCLUDE_PATH" ]; then 210 | tar -zvcf "$TMP_BACKUP_DIR/$CRON_ID/$backup_file" "$BACKUP_PATH" >/dev/null 2>&1 211 | else 212 | tar -zvcf "$TMP_BACKUP_DIR/$CRON_ID/$backup_file" --exclude="$EXCLUDE_PATH" "$BACKUP_PATH" >/dev/null 2>&1 213 | fi 214 | ret=$? 215 | if ! [ "$ret" -eq 0 ]; then 216 | send_mail " ERR" "Something went wrong while directory backup; tar (gzip): RETURN $ret" 217 | clear_backup 218 | exit 1 219 | fi 220 | ;; 221 | tar) 222 | log "INFO" "Backup data to tar-Archive" 223 | if [ -z "$EXCLUDE_PATH" ]; then 224 | tar -cvf "$TMP_BACKUP_DIR/$CRON_ID/$backup_file" "$BACKUP_PATH" >/dev/null 2>&1 225 | else 226 | tar -cvf "$TMP_BACKUP_DIR/$CRON_ID/$backup_file" --exclude="$EXCLUDE_PATH" "$BACKUP_PATH" >/dev/null 2>&1 227 | fi 228 | ret=$? 229 | if ! [ "$ret" -eq 0 ]; then 230 | send_mail " ERR" "Something went wrong while directory backup; tar: RETURN $ret" 231 | clear_backup 232 | exit 1 233 | fi 234 | ;; 235 | esac 236 | log "INFO" "Created directory backup $backup_file" 237 | } 238 | 239 | function database_backup() { 240 | # subdomain isn't mandatory, so check this 241 | if [ -z "$SUBDOMAIN" ]; then 242 | backup_file="${USERNAME}_${DOMAIN}_$(date "+%Y%m%d")_${CRON_ID}" 243 | else 244 | backup_file="${USERNAME}_${DOMAIN}_${SUBDOMAIN}_$(date "+%Y%m%d")_${CRON_ID}" 245 | fi 246 | # Create temp backup directory backup/cron_id 247 | mkdir -p "$TMP_BACKUP_DIR/$CRON_ID" 248 | case ${DB_TYPE} in 249 | mysql) 250 | log "INFO" "Backup mysql database" 251 | backup_file="$backup_file.sql" 252 | 253 | if [ "$MY_CONF_ENABLED" -eq 1 ]; then 254 | log "INFO" "Use .my.cnf" 255 | $MYSQLDUMP -u "$MY_CONF_LOGIN" "$DB_NAME" > "$TMP_BACKUP_DIR/$CRON_ID/${backup_file}" 256 | else 257 | $MYSQLDUMP "$DB_NAME" > "$TMP_BACKUP_DIR/$CRON_ID/${backup_file}" 258 | fi 259 | 260 | ret=$? 261 | if ! [ "$ret" -eq 0 ]; then 262 | send_mail " ERR" "Something went wrong while backup database; mysqldump: RETURN $ret" 263 | clear_backup 264 | exit 1 265 | fi 266 | ;; 267 | postgresql) 268 | log "INFO" "Backup postgresql database" 269 | backup_file="$backup_file.pg" 270 | pg_dump "$DB_NAME" -f "$TMP_BACKUP_DIR/$CRON_ID/${backup_file}" 271 | ret=$? 272 | if ! [ "$ret" -eq 0 ]; then 273 | send_mail " ERR" "Something went wrong while backup database; pg_dump: RETURN $ret" 274 | clear_backup 275 | exit 1 276 | fi 277 | ;; 278 | mongodb) 279 | log "INFO" "Backup mongodb database" 280 | backup_file="$backup_file.mongo" 281 | mongodump --db "$DB_NAME" > "$TMP_BACKUP_DIR/$CRON_ID/${backup_file}" 282 | ret=$? 283 | if ! [ "$ret" -eq 0 ]; then 284 | send_mail " ERR" "Something went wrong while backup database; mongodump: RETURN $ret" 285 | clear_backup 286 | exit 1 287 | fi 288 | ;; 289 | esac 290 | case ${COMPRESSION_ALG} in 291 | zip) 292 | log "INFO" "Compress database to zip-Archive" 293 | zip -r "$TMP_BACKUP_DIR/$CRON_ID/$backup_file.zip" "$TMP_BACKUP_DIR/$CRON_ID/$backup_file" >/dev/null 2>&1 294 | ret=$? 295 | if ! [ "$ret" -eq 0 ]; then 296 | send_mail " ERR" "Something went wrong while compressing database; zip: RETURN $ret" 297 | clear_backup 298 | exit 1 299 | fi 300 | ;; 301 | gzip) 302 | log "INFO" "Compress database to gzip-Archive" 303 | tar -zvcf "$TMP_BACKUP_DIR/$CRON_ID/$backup_file.gz" "$TMP_BACKUP_DIR/$CRON_ID/$backup_file" >/dev/null 2>&1 304 | ret=$? 305 | if ! [ "$ret" -eq 0 ]; then 306 | send_mail " ERR" "Something went wrong while compressing database; zip: RETURN $ret" 307 | clear_backup 308 | exit 1 309 | fi 310 | ;; 311 | tar) 312 | log "INFO" "Compress database to tar-Archive" 313 | tar -cvf "$TMP_BACKUP_DIR/$CRON_ID/$backup_file.tar" "$TMP_BACKUP_DIR/$CRON_ID/$backup_file" >/dev/null 2>&1 314 | ret=$? 315 | if ! [ "$ret" -eq 0 ]; then 316 | send_mail " ERR" "Something went wrong while compressing database; zip: RETURN $ret" 317 | clear_backup 318 | exit 1 319 | fi 320 | ;; 321 | esac 322 | # Remove uncompress database backup 323 | rm "$TMP_BACKUP_DIR/$CRON_ID/$backup_file" 324 | log "INFO" "Created database backup $backup_file" 325 | } 326 | 327 | function clear_backup() { 328 | log "INFO" "Removing local backup files $TMP_BACKUP_DIR/$CRON_ID" 329 | # :? = Failing if TMP_BACKUP_DIR or CRON_ID is empty. Avoidance of rm -r / 330 | if [ -d "${TMP_BACKUP_DIR:?}/${CRON_ID:?}" ]; then 331 | rm -r "${TMP_BACKUP_DIR:?}/${CRON_ID:?}" 332 | fi 333 | } 334 | 335 | # Check if the parameter (path + filename) exist. 336 | if [ -z "$1" ]; then 337 | send_mail " ERR" "Missing parameter when calling script" 338 | exit 1 339 | fi 340 | 341 | # Check if the file (parameter) exist. 342 | if ! [ -f "$1" ]; then 343 | send_mail " ERR" "File $1 doesn't exist" 344 | exit 1 345 | fi 346 | 347 | # 348 | # Parsing DA ini file 349 | # 350 | parse_ini "$ROOT_DIR/admin/elements/conf/directadmin.ini" "SETTINGS" 351 | if [ -z "$CONF__ALL_VARS" ]; then 352 | send_mail " ERR" "No SETTINGS section found in directadmin INI" 353 | exit 1 354 | fi 355 | MY_CONF_ENABLED="$CONF__SETTINGS__my_conf_enabled" 356 | MY_CONF_LOGIN="$CONF__SETTINGS__my_conf_login" 357 | 358 | # 359 | # Parsing the config file and 360 | # store the values in variables. 361 | # Check if variables are set. 362 | # 363 | 364 | # It is important to read the EMAIL as early as possible 365 | # in order to be able to send an e-mail notification in 366 | # case of an error. 367 | log "INFO" "Parsing EMAIL section" 368 | parse_ini "$1" "EMAIL" 369 | if [ -z "$CONF__ALL_VARS" ]; then 370 | log " ERR" "No EMAIL section found" 371 | # Couldn't send an email here 372 | exit 1 373 | fi 374 | EMAIL_ENABLED="$CONF__EMAIL__send_email_enabled" 375 | EMAIL_ADDR="$CONF__EMAIL__send_email_address" 376 | if [ -z "$EMAIL_ENABLED" ] || [ -z "$EMAIL_ADDR" ]; then 377 | if [ -z "$EMAIL_ENABLED" ]; then 378 | log " ERR" "Missing EMAIL.send_email_enabled" 379 | send_mail " ERR" "Missing EMAIL.send_email_enabled" 380 | fi 381 | if [ -z "$EMAIL_ADDR" ]; then 382 | log " ERR" "Missing EMAIL.send_email_address" 383 | # Couldn't send an email here 384 | else 385 | send_mail " ERR" "Missing EMAIL.send_email_enabled" 386 | fi 387 | exit 1 388 | else 389 | log "INFO" "EMAIL.send_email_enabled = $EMAIL_ENABLED" 390 | log "INFO" "EMAIL.send_email_address = $EMAIL_ADDR" 391 | fi 392 | 393 | # It is important to read the CRON_ID as early as possible 394 | # to be able to send an e-mail notification with the CRON_ID 395 | # in case of an error. 396 | log "INFO" "Parsing file $1" 397 | log "INFO" "Parsing CRON section" 398 | parse_ini "$1" "CRON" 399 | if [ -z "$CONF__ALL_VARS" ]; then 400 | send_mail " ERR" "No CRON section found" 401 | exit 1 402 | fi 403 | CRON_ID="$CONF__CRON__cron_id" 404 | if [ -z "$CRON_ID" ]; then 405 | send_mail " ERR" "Missing CRON.cron_id" 406 | exit 1 407 | else 408 | log "INFO" "CRON.cron_id = $CRON_ID" 409 | fi 410 | 411 | log "INFO" "Parsing FILEHOST section" 412 | parse_ini "$1" "FILEHOST" 413 | if [ -z "$CONF__ALL_VARS" ]; then 414 | send_mail " ERR" "No FILEHOST section found" 415 | exit 1 416 | fi 417 | FILEHOST_ROOT_PATH="$CONF__FILEHOST__filehost_root_path" 418 | if [ -z "$FILEHOST_ROOT_PATH" ]; then 419 | send_mail " ERR" "Missing FILEHOST options" 420 | exit 1 421 | else 422 | log "INFO" "FILEHOST.filehost_root_path = $FILEHOST_ROOT_PATH" 423 | fi 424 | 425 | log "INFO" "Parsing RCLONE section" 426 | parse_ini "$1" "RCLONE" 427 | if [ -z "$CONF__ALL_VARS" ]; then 428 | send_mail " ERR" "No RCLONE section found" 429 | exit 1 430 | fi 431 | RCLONE_REMOTE="$CONF__RCLONE__remote" 432 | RCLONE_STRUCT="$CONF__RCLONE__structure" 433 | if [ -z "$RCLONE_REMOTE" ] || [ -z "$RCLONE_STRUCT" ]; then 434 | send_mail " ERR" "Missing RCLONE options" 435 | exit 1 436 | else 437 | # Check if RCLONE.struct contains only allowed options 438 | check_struct "$RCLONE_STRUCT" 439 | res=$? 440 | if ! [ "$res" -eq 0 ]; then 441 | send_mail " ERR" "RCLONE.structure contains invalid options" 442 | exit 1 443 | fi 444 | log "INFO" "RCLONE.remote = $RCLONE_REMOTE" 445 | log "INFO" "RCLONE.structure = $RCLONE_STRUCT" 446 | fi 447 | 448 | log "INFO" "Parsing COMPRESSION section" 449 | parse_ini "$1" "COMPRESSION" 450 | if [ -z "$CONF__ALL_VARS" ]; then 451 | send_mail " ERR" "No COMPRESSION section found" 452 | exit 1 453 | fi 454 | COMPRESSION_ALG="${CONF__COMPRESSION__compression,,}" # to lowercase 455 | if [ -z "$COMPRESSION_ALG" ]; then 456 | send_mail " ERR" "Missing COMPRESSION options" 457 | exit 1 458 | else 459 | if [ "$COMPRESSION_ALG" == "zip" ] || [ "$COMPRESSION_ALG" == "gzip" ] || [ "$COMPRESSION_ALG" == "tar" ]; then 460 | log "INFO" "COMPRESSION.compression = $COMPRESSION_ALG" 461 | else 462 | send_mail " ERR" "Invalid COMPRESSION.compression defined" 463 | exit 1 464 | fi 465 | fi 466 | 467 | # 468 | # Parsing the config file options WWW or DB and 469 | # store the values in variables. 470 | # Check if variables are set. 471 | # 472 | log "INFO" "Parsing DIRECTADMIN_WWW section" 473 | parse_ini "$1" "DIRECTADMIN_WWW" 474 | if [ -n "$CONF__ALL_VARS" ]; then 475 | USERNAME="$CONF__DIRECTADMIN_WWW__directadmin_user" 476 | DOMAIN="$CONF__DIRECTADMIN_WWW__directadmin_domain" 477 | SUBDOMAIN="$CONF__DIRECTADMIN_WWW__directadmin_subdomain" 478 | BACKUP_PATH="$CONF__DIRECTADMIN_WWW__directadmin_backup_path" 479 | EXCLUDE_PATH="$CONF__DIRECTADMIN_WWW__directadmin_exclude_path" 480 | # DIRECTADMIN_WWW.directadmin_exclude_path isn't mandatory, so dont check this here 481 | if [ -z "$USERNAME" ] || [ -z "$DOMAIN" ] || [ -z "$BACKUP_PATH" ]; then 482 | send_mail " ERR" "Missing DIRECTADMIN_WWW options" 483 | exit 1 484 | else 485 | if ! [ -d "$BACKUP_PATH" ]; then 486 | send_mail " ERR" "The Path in DIRECTADMIN_WWW.directadmin_backup_path doesn't exist" 487 | exit 1 488 | else 489 | log "INFO" "DIRECTADMIN_WWW.directadmin_user = $USERNAME" 490 | log "INFO" "DIRECTADMIN_WWW.directadmin_domain = $DOMAIN" 491 | if ! [ -z "$SUBDOMAIN" ]; then 492 | log "INFO" "DIRECTADMIN_WWW.directadmin_subdomain = $SUBDOMAIN" 493 | fi 494 | log "INFO" "DIRECTADMIN_WWW.directadmin_backup_path = $BACKUP_PATH" 495 | if ! [ -z "$EXCLUDE_PATH" ]; then 496 | log "INFO" "DIRECTADMIN_WWW.directadmin_exclude_path = $EXCLUDE_PATH" 497 | fi 498 | fi 499 | fi 500 | # Perform a directory backup 501 | directory_backup 502 | else 503 | send_mail " ERR" "No DIRECTADMIN_WWW section found" 504 | exit 1 505 | fi 506 | 507 | log "INFO" "Parsing DIRECTADMIN_DB section" 508 | parse_ini "$1" "DIRECTADMIN_DB" 509 | if [ -n "$CONF__ALL_VARS" ]; then 510 | DB_NAME="$CONF__DIRECTADMIN_DB__database_name" 511 | DB_TYPE="${CONF__DIRECTADMIN_DB__database_type,,}" # to lowercase 512 | if ! [ -z "$DB_NAME" ] && ! [ -z "$DB_TYPE" ]; then 513 | check_db_type "$DB_TYPE" 514 | res=$? 515 | if ! [ "$res" -eq 0 ]; then 516 | send_mail " ERR" "DIRECTADMIN_DB.databases_type contains invalid options" 517 | clear_backup 518 | exit 1 519 | fi 520 | log "INFO" "DIRECTADMIN_DB.database_name = $DB_NAME" 521 | log "INFO" "DIRECTADMIN_DB.database_type = $DB_TYPE" 522 | # Perform a database backup 523 | database_backup 524 | else 525 | # When one of them is set then the other must be set 526 | if [[ -z "$DB_NAME" && ! -z "$DB_TYPE" ]] || [[ ! -z "$DB_NAME" && -z "$DB_TYPE" ]] ; then 527 | send_mail " ERR" "Missing DIRECTADMIN_DB options" 528 | clear_backup 529 | exit 1 530 | fi 531 | fi 532 | else 533 | # DATABASE section isn't mandatory 534 | log "INFO" "No DIRECTADMIN_DB section found" 535 | fi 536 | 537 | # 538 | # Prepare the filehost remote. 539 | # Create directories on the remote 540 | # 541 | REMOTE_PATH="$(struct_to_path "$RCLONE_STRUCT")" 542 | log "INFO" "Create path ${FILEHOST_ROOT_PATH%/}/${REMOTE_PATH} on remote $RCLONE_REMOTE" 543 | rclone mkdir "$RCLONE_REMOTE:${FILEHOST_ROOT_PATH%/}/${REMOTE_PATH}" 544 | res=$? 545 | if ! [ "$res" -eq 0 ]; then 546 | send_mail " ERR" "Something went wrong while create path on remote ($RCLONE_REMOTE:${FILEHOST_ROOT_PATH%/}/${REMOTE_PATH})" 547 | clear_backup 548 | exit 1 549 | fi 550 | 551 | # 552 | # Upload backup to remote 553 | # 554 | log "INFO" "Uploading backups to $RCLONE_REMOTE:${FILEHOST_ROOT_PATH%/}/${REMOTE_PATH}" 555 | rclone copy "$TMP_BACKUP_DIR/$CRON_ID/" "$RCLONE_REMOTE:${FILEHOST_ROOT_PATH%/}/${REMOTE_PATH}" 556 | res=$? 557 | if ! [ "$res" -eq 0 ]; then 558 | send_mail " ERR" "Something went wrong while upload to remote ($RCLONE_REMOTE:${FILEHOST_ROOT_PATH%/}/${REMOTE_PATH})" 559 | clear_backup 560 | exit 1 561 | fi 562 | 563 | # 564 | # Remove backup files 565 | # 566 | clear_backup 567 | 568 | send_mail " OK" "Job is completed" 569 | exit 0 570 | -------------------------------------------------------------------------------- /admin/elements/scripts/bash_ini_parser/.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | compiler: 4 | - gcc 5 | 6 | before_install: 7 | 8 | script: cd test && ./test.sh 9 | 10 | ## whitelist 11 | branches: 12 | only: 13 | - master 14 | - next 15 | 16 | notifications: 17 | email: 18 | - sweet_f_a@gmx.de 19 | -------------------------------------------------------------------------------- /admin/elements/scripts/bash_ini_parser/LICENSE: -------------------------------------------------------------------------------- 1 | This software is provided under the BSD license. The text of this license 2 | is provided below: 3 | 4 | -------------------------------------------------------------------------- 5 | 6 | Copyright (C) 2009 Kevin Porter / Advanced Web Construction Ltd 7 | Copyright (C) 2010-2014 Ruediger Meier 8 | All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions 12 | are met: 13 | 14 | 1. Redistributions of source code must retain the above copyright 15 | notice, this list of conditions and the following disclaimer. 16 | 17 | 2. Redistributions in binary form must reproduce the above copyright 18 | notice, this list of conditions and the following disclaimer in the 19 | documentation and/or other materials provided with the distribution. 20 | 21 | 3. Neither the name of the author nor the names of any contributors 22 | may be used to endorse or promote products derived from this 23 | software without specific prior written permission. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 26 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 27 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 28 | DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 32 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 33 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 34 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 35 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | -------------------------------------------------------------------------------- /admin/elements/scripts/bash_ini_parser/README: -------------------------------------------------------------------------------- 1 | 2 | bash_ini_parser -- Simple INI file parser 3 | ========================================= 4 | 5 | This is a comfortable and simple INI file parser to be used in 6 | bash scripts. 7 | 8 | 9 | 10 | 11 | COPYRIGHT 12 | --------- 13 | 14 | Copyright (c) 2009 Kevin Porter / Advanced Web Construction Ltd 15 | (http://coding.tinternet.info / http://webutils.co.uk) 16 | Copyright (c) 2010-2014 Ruediger Meier 17 | (https://github.com/rudimeier/) 18 | 19 | License: BSD-3-Clause, see LICENSE file 20 | 21 | 22 | 23 | 24 | USAGE 25 | ----- 26 | 27 | You must source the bash file into your script: 28 | 29 | > . read_ini.sh 30 | 31 | and then use the read_ini function, defined as: 32 | 33 | > read_ini INI_FILE [SECTION] [[--prefix|-p] PREFIX] [[--booleans|b] [0|1]] 34 | 35 | If SECTION is supplied, then only the specified section of the file will 36 | be processed. 37 | 38 | After running the read_ini function, variables corresponding to the ini 39 | file entries will be available to you. Naming convention for variable 40 | names is: 41 | 42 | PREFIX__SECTION__VARNAME 43 | 44 | PREFIX is 'INI' by default (but can be changed with the --prefix option), 45 | SECTION and VARNAME are the section name and variable name respectively. 46 | 47 | Additionally you can get a list of all these variable names: 48 | PREFIX__ALL_VARS 49 | and get a list of sections: 50 | PREFIX__ALL_SECTIONS 51 | and the number of sections: 52 | PREFIX__NUMSECTIONS 53 | 54 | For example, to read and output the variables of this ini file: 55 | 56 | -- START test1.ini file 57 | 58 | var1="VAR 1" 59 | var2 = VAR 2 60 | 61 | [section1] 62 | var1="section1 VAR 1" 63 | var2= section1 VAR 2 64 | 65 | 66 | -- END test1.ini file 67 | 68 | you could do this: 69 | 70 | -- START bash script 71 | 72 | . read_ini.sh 73 | 74 | read_ini test1.ini 75 | 76 | echo "var1 = ${INI__var1}" 77 | echo "var2 = ${INI__var2}" 78 | echo "section1 var1 = ${INI__section1__var1}" 79 | echo "section1 var2 = ${INI__section1__var2}" 80 | 81 | echo "list of all ini vars: ${INI__ALL_VARS}" 82 | echo "number of sections: ${INI__NUMSECTIONS}" 83 | 84 | -- END bash script 85 | 86 | 87 | 88 | 89 | OPTIONS 90 | ------- 91 | 92 | [--prefix | -p] PREFIX 93 | String to prepend to generated variable names (automatically followed by '__'). 94 | Default: INI 95 | 96 | [--booleans | -b] [0|1] 97 | Whether to interpret special unquoted string values 'yes', 'no', 'true', 98 | 'false', 'on', 'off' as booleans. 99 | Default: 1 100 | 101 | 102 | 103 | 104 | INI FILE FORMAT 105 | --------------- 106 | 107 | - Variables are stored as name/value pairs, eg: 108 | var=value 109 | 110 | - Leading and trailing whitespace of the name and the value is discarded. 111 | 112 | - Use double or single quotes to get whitespace in the values 113 | 114 | - Section names in square brackets, eg: 115 | [section1] 116 | var1 = value 117 | 118 | - Variable names can be re-used between sections (or out of section), eg: 119 | var1=value 120 | [section1] 121 | var1=value 122 | [section3] 123 | var1=value 124 | 125 | - Dots are converted to underscores in all variable names. 126 | 127 | - Special boolean values: unquoted strings 'yes', 'true' and 'on' are interpreted 128 | as 1; 'no', 'false' and 'off' are interpreted as 0 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /admin/elements/scripts/bash_ini_parser/TODO: -------------------------------------------------------------------------------- 1 | 2 | 3 | - Tabs/newlines to be preserved 4 | 5 | - [] notation for arrays (like PHP's parse_ini_file()) 6 | 7 | 8 | -------------------------------------------------------------------------------- /admin/elements/scripts/bash_ini_parser/read_ini.sh: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2009 Kevin Porter / Advanced Web Construction Ltd 3 | # (http://coding.tinternet.info, http://webutils.co.uk) 4 | # Copyright (c) 2010-2014 Ruediger Meier 5 | # (https://github.com/rudimeier/) 6 | # 7 | # License: BSD-3-Clause, see LICENSE.md file 8 | # 9 | # Simple INI file parser. 10 | # 11 | # See README for usage. 12 | # 13 | # 14 | 15 | 16 | 17 | 18 | function read_ini() 19 | { 20 | # Be strict with the prefix, since it's going to be run through eval 21 | function check_prefix() 22 | { 23 | if ! [[ "${VARNAME_PREFIX}" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] ;then 24 | echo "read_ini: invalid prefix '${VARNAME_PREFIX}'" >&2 25 | return 1 26 | fi 27 | } 28 | 29 | function check_ini_file() 30 | { 31 | if [ ! -r "$INI_FILE" ] ;then 32 | echo "read_ini: '${INI_FILE}' doesn't exist or not" \ 33 | "readable" >&2 34 | return 1 35 | fi 36 | } 37 | 38 | # enable some optional shell behavior (shopt) 39 | function pollute_bash() 40 | { 41 | if ! shopt -q extglob ;then 42 | SWITCH_SHOPT="${SWITCH_SHOPT} extglob" 43 | fi 44 | if ! shopt -q nocasematch ;then 45 | SWITCH_SHOPT="${SWITCH_SHOPT} nocasematch" 46 | fi 47 | shopt -q -s ${SWITCH_SHOPT} 48 | } 49 | 50 | # unset all local functions and restore shopt settings before returning 51 | # from read_ini() 52 | function cleanup_bash() 53 | { 54 | shopt -q -u ${SWITCH_SHOPT} 55 | unset -f check_prefix check_ini_file pollute_bash cleanup_bash 56 | } 57 | 58 | local INI_FILE="" 59 | local INI_SECTION="" 60 | 61 | # {{{ START Deal with command line args 62 | 63 | # Set defaults 64 | local BOOLEANS=1 65 | local VARNAME_PREFIX=INI 66 | local CLEAN_ENV=0 67 | 68 | # {{{ START Options 69 | 70 | # Available options: 71 | # --boolean Whether to recognise special boolean values: ie for 'yes', 'true' 72 | # and 'on' return 1; for 'no', 'false' and 'off' return 0. Quoted 73 | # values will be left as strings 74 | # Default: on 75 | # 76 | # --prefix=STRING String to begin all returned variables with (followed by '__'). 77 | # Default: INI 78 | # 79 | # First non-option arg is filename, second is section name 80 | 81 | while [ $# -gt 0 ] 82 | do 83 | 84 | case $1 in 85 | 86 | --clean | -c ) 87 | CLEAN_ENV=1 88 | ;; 89 | 90 | --booleans | -b ) 91 | shift 92 | BOOLEANS=$1 93 | ;; 94 | 95 | --prefix | -p ) 96 | shift 97 | VARNAME_PREFIX=$1 98 | ;; 99 | 100 | * ) 101 | if [ -z "$INI_FILE" ] 102 | then 103 | INI_FILE=$1 104 | else 105 | if [ -z "$INI_SECTION" ] 106 | then 107 | INI_SECTION=$1 108 | fi 109 | fi 110 | ;; 111 | 112 | esac 113 | 114 | shift 115 | done 116 | 117 | if [ -z "$INI_FILE" ] && [ "${CLEAN_ENV}" = 0 ] ;then 118 | echo -e "Usage: read_ini [-c] [-b 0| -b 1]] [-p PREFIX] FILE"\ 119 | "[SECTION]\n or read_ini -c [-p PREFIX]" >&2 120 | cleanup_bash 121 | return 1 122 | fi 123 | 124 | if ! check_prefix ;then 125 | cleanup_bash 126 | return 1 127 | fi 128 | 129 | local INI_ALL_VARNAME="${VARNAME_PREFIX}__ALL_VARS" 130 | local INI_ALL_SECTION="${VARNAME_PREFIX}__ALL_SECTIONS" 131 | local INI_NUMSECTIONS_VARNAME="${VARNAME_PREFIX}__NUMSECTIONS" 132 | if [ "${CLEAN_ENV}" = 1 ] ;then 133 | eval unset "\$${INI_ALL_VARNAME}" 134 | fi 135 | unset ${INI_ALL_VARNAME} 136 | unset ${INI_ALL_SECTION} 137 | unset ${INI_NUMSECTIONS_VARNAME} 138 | 139 | if [ -z "$INI_FILE" ] ;then 140 | cleanup_bash 141 | return 0 142 | fi 143 | 144 | if ! check_ini_file ;then 145 | cleanup_bash 146 | return 1 147 | fi 148 | 149 | # Sanitise BOOLEANS - interpret "0" as 0, anything else as 1 150 | if [ "$BOOLEANS" != "0" ] 151 | then 152 | BOOLEANS=1 153 | fi 154 | 155 | 156 | # }}} END Options 157 | 158 | # }}} END Deal with command line args 159 | 160 | local LINE_NUM=0 161 | local SECTIONS_NUM=0 162 | local SECTION="" 163 | 164 | # IFS is used in "read" and we want to switch it within the loop 165 | local IFS=$' \t\n' 166 | local IFS_OLD="${IFS}" 167 | 168 | # we need some optional shell behavior (shopt) but want to restore 169 | # current settings before returning 170 | local SWITCH_SHOPT="" 171 | pollute_bash 172 | 173 | while read -r line || [ -n "$line" ] 174 | do 175 | #echo line = "$line" 176 | 177 | ((LINE_NUM++)) 178 | 179 | # Skip blank lines and comments 180 | if [ -z "$line" -o "${line:0:1}" = ";" -o "${line:0:1}" = "#" ] 181 | then 182 | continue 183 | fi 184 | 185 | # Section marker? 186 | if [[ "${line}" =~ ^\[[a-zA-Z0-9_]{1,}\]$ ]] 187 | then 188 | 189 | # Set SECTION var to name of section (strip [ and ] from section marker) 190 | SECTION="${line#[}" 191 | SECTION="${SECTION%]}" 192 | eval "${INI_ALL_SECTION}=\"\${${INI_ALL_SECTION}# } $SECTION\"" 193 | ((SECTIONS_NUM++)) 194 | 195 | continue 196 | fi 197 | 198 | # Are we getting only a specific section? And are we currently in it? 199 | if [ ! -z "$INI_SECTION" ] 200 | then 201 | if [ "$SECTION" != "$INI_SECTION" ] 202 | then 203 | continue 204 | fi 205 | fi 206 | 207 | # Valid var/value line? (check for variable name and then '=') 208 | if ! [[ "${line}" =~ ^[a-zA-Z0-9._]{1,}[[:space:]]*= ]] 209 | then 210 | echo "Error: Invalid line:" >&2 211 | echo " ${LINE_NUM}: $line" >&2 212 | cleanup_bash 213 | return 1 214 | fi 215 | 216 | 217 | # split line at "=" sign 218 | IFS="=" 219 | read -r VAR VAL <<< "${line}" 220 | IFS="${IFS_OLD}" 221 | 222 | # delete spaces around the equal sign (using extglob) 223 | VAR="${VAR%%+([[:space:]])}" 224 | VAL="${VAL##+([[:space:]])}" 225 | VAR=$(echo $VAR) 226 | 227 | 228 | # Construct variable name: 229 | # ${VARNAME_PREFIX}__$SECTION__$VAR 230 | # Or if not in a section: 231 | # ${VARNAME_PREFIX}__$VAR 232 | # In both cases, full stops ('.') are replaced with underscores ('_') 233 | if [ -z "$SECTION" ] 234 | then 235 | VARNAME=${VARNAME_PREFIX}__${VAR//./_} 236 | else 237 | VARNAME=${VARNAME_PREFIX}__${SECTION}__${VAR//./_} 238 | fi 239 | eval "${INI_ALL_VARNAME}=\"\${${INI_ALL_VARNAME}# } ${VARNAME}\"" 240 | 241 | if [[ "${VAL}" =~ ^\".*\"$ ]] 242 | then 243 | # remove existing double quotes 244 | VAL="${VAL##\"}" 245 | VAL="${VAL%%\"}" 246 | elif [[ "${VAL}" =~ ^\'.*\'$ ]] 247 | then 248 | # remove existing single quotes 249 | VAL="${VAL##\'}" 250 | VAL="${VAL%%\'}" 251 | elif [ "$BOOLEANS" = 1 ] 252 | then 253 | # Value is not enclosed in quotes 254 | # Booleans processing is switched on, check for special boolean 255 | # values and convert 256 | 257 | # here we compare case insensitive because 258 | # "shopt nocasematch" 259 | case "$VAL" in 260 | yes | true | on ) 261 | VAL=1 262 | ;; 263 | no | false | off ) 264 | VAL=0 265 | ;; 266 | esac 267 | fi 268 | 269 | 270 | # enclose the value in single quotes and escape any 271 | # single quotes and backslashes that may be in the value 272 | VAL="${VAL//\\/\\\\}" 273 | VAL="\$'${VAL//\'/\'}'" 274 | 275 | eval "$VARNAME=$VAL" 276 | done <"${INI_FILE}" 277 | 278 | # return also the number of parsed sections 279 | eval "$INI_NUMSECTIONS_VARNAME=$SECTIONS_NUM" 280 | 281 | cleanup_bash 282 | } 283 | 284 | 285 | -------------------------------------------------------------------------------- /admin/elements/scripts/manage_cron.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # -u: Treat unset variables as an error when substituting. 4 | # -e: Exit immediately if a command exits with a non-zero status. 5 | # -o pipefail: the return value of a pipeline is the status of the 6 | # last command to exit with a non-zero status. 7 | set -o pipefail 8 | 9 | # Root directory which contains the other directories. 10 | # e.g. /usr/local/directadmin/plugins/rclone_backup 11 | ROOT_DIR="/usr/local/directadmin/plugins/rclone_backup" 12 | # Config directory which contains pending, active, inactive and remove directory. 13 | CONF_DIR="${ROOT_DIR}/admin/elements/conf" 14 | # Script directory which contains the CRON_CMD script. 15 | SCRIPT_DIR="${ROOT_DIR}/admin/elements/scripts" 16 | # crontab file. Should be adjusted. 17 | # e.g /var/spool/cron/crontabs/. 18 | # Requires root privileges. 19 | # CRON_FILE="/var/spool/cron/root" 20 | # Script which is executed in the crontab. 21 | CRON_CMD="backup_job.sh" 22 | 23 | # Include bash_ini_parser. Its an helper to parse INI-files. 24 | . "$SCRIPT_DIR/bash_ini_parser/read_ini.sh" 25 | 26 | # Parameter using in this script. 27 | # Can be overwritten via parameterization. 28 | START=0 29 | DRY_RUN=0 30 | VERBOSE=0 31 | 32 | CRON_ID="" 33 | 34 | pendingfilecount=$(find "$CONF_DIR"/pending/ -type f | wc -l) 35 | removefilecount=$(find "$CONF_DIR"/remove/ -type f | wc -l) 36 | 37 | if [ "$pendingfilecount" -eq 0 ]; then 38 | echo "No pending jobs found to process" 39 | fi 40 | if [ "$removefilecount" -eq 0 ]; then 41 | echo "No jobs found to remove" 42 | fi 43 | 44 | function show_usage() { 45 | printf "\n" 46 | printf "Usage:\n" 47 | printf "%s\n" " $0 [options [parameters]]" 48 | printf "\n" 49 | printf "Options:\n" 50 | printf "%s\t\t%s\n" " -r, --run" "It should be avoided to execute the script accidentally. This flag is mandatory." 51 | printf "%s\t\t%s\n" " -d, --dry-run" "Dry run. Do not edit crontab or moving files." 52 | printf "%s\t\t%s\n" " -v, --verbose" "Print verbose output." 53 | printf "%s\t\t%s\n" " -h, --help" "Print this help." 54 | printf "\n" 55 | 56 | return 0 57 | } 58 | 59 | # Helper function to log to stdout 60 | function log() { 61 | if [ -z "$1" ] || [ -z "$2" ]; then 62 | return 1 63 | fi 64 | if [ "$VERBOSE" -eq 1 ] || [ "${1# }" == "ERR" ]; then 65 | echo "[ $(basename "$0") ] $1 cron_id ${CRON_ID:-UNKNOWN}: $2" 66 | fi 67 | } 68 | 69 | # Helper function to send mails 70 | # @param $1: subject 71 | # @param $2: message 72 | function send_mail() { 73 | if [ -z "$EMAIL_ADDR" ]; then 74 | log "WARN" "E-mail isn't set. Couldn't send an email here" 75 | return 0 76 | fi 77 | # Fallback: If EMAIL_ENABLE isn't set yet, then send an email 78 | if ! [ -z "$EMAIL_ENABLED" ]; then 79 | # If disable return here 80 | if [ "$EMAIL_ENABLED" -eq 0 ]; then 81 | return 0 82 | fi 83 | fi 84 | if [ -z "$1" ] || [ -z "$2" ]; then 85 | return 1 86 | fi 87 | log "$1" "$2" 88 | "$SCRIPT_DIR"/send_mail.sh -t "$EMAIL_ADDR" -s "[ $(basename "$0") ] $1 cron_id ${CRON_ID:-UNKNOWN}" -b "$2" 89 | } 90 | 91 | # Parsing EMAIL and CRON_ID 92 | # @param $1: filename + path of the configfile 93 | function parse_main_info() { 94 | # Parsing EMAIL section 95 | # It is important to read the EMAIL as early as possible 96 | # in order to be able to send an e-mail notification in 97 | # case of an error. 98 | if [ -z "$1" ]; then 99 | return 1 100 | fi 101 | log "INFO" "Parsing EMAIL section" 102 | parse_ini "$1" "EMAIL" 103 | if [ -z "$CONF__ALL_VARS" ]; then 104 | log " ERR" "No EMAIL section found" 105 | # Couldn't send an email here 106 | exit 1 107 | fi 108 | EMAIL_ENABLED="$CONF__EMAIL__send_email_enabled" 109 | EMAIL_ADDR="$CONF__EMAIL__send_email_address" 110 | if [ -z "$EMAIL_ENABLED" ] || [ -z "$EMAIL_ADDR" ]; then 111 | if [ -z "$EMAIL_ENABLED" ]; then 112 | send_mail " ERR" "Missing EMAIL.send_email_enabled" 113 | fi 114 | if [ -z "$EMAIL_ADDR" ]; then 115 | log " ERR" "Missing EMAIL.send_email_address" 116 | # Couldn't send an email here 117 | else 118 | send_mail " ERR" "Missing EMAIL.send_email_enabled" 119 | fi 120 | exit 1 121 | else 122 | log "INFO" "EMAIL.send_email_enabled = $EMAIL_ENABLED" 123 | log "INFO" "EMAIL.send_email_address = $EMAIL_ADDR" 124 | fi 125 | 126 | # It is important to read the CRON_ID as early as possible 127 | # to be able to send an e-mail notification with the CRON_ID 128 | # in case of an error. 129 | log "INFO" "Parsing CRON section" 130 | parse_ini "$1" "CRON" 131 | if [ -z "$CONF__ALL_VARS" ]; then 132 | send_mail " ERR" "No CRON section found" 133 | exit 1 134 | fi 135 | CRON_ID="$CONF__CRON__cron_id" 136 | if [ -z "$CRON_ID" ]; then 137 | send_mail " ERR" "Missing CRON.cron_id" 138 | exit 1 139 | else 140 | log "INFO" "CRON.cron_id = $CRON_ID" 141 | fi 142 | 143 | } 144 | 145 | # helper function to delete a cronjob entry 146 | # @param $1: Pass the path + filename of the config 147 | function delete_cron_entry() { 148 | if [ -z "$1" ]; then 149 | log "No file supplied" 150 | return 1 151 | fi 152 | if [ "$DRY_RUN" -eq 0 ]; then 153 | ( crontab -l | grep -v -F "$(basename "$file")" ) | crontab - 154 | log "Deleted crontab entry." 155 | else 156 | log " DRY" "Deleted crontab entry." 157 | fi 158 | } 159 | 160 | # helper function to parse ini files 161 | # @param $1: Pass the path + filename of the config 162 | # @param $2: Pass the section name (optional) 163 | function parse_ini() { 164 | if [ -z "$1" ]; then 165 | return 1 166 | fi 167 | if [ -z "$2" ]; then 168 | read_ini "$1" -p CONF 169 | else 170 | read_ini "$1" "$2" -p CONF 171 | fi 172 | res=$? 173 | # Check for parsing errors or invalid INI files 174 | if ! [ "$res" -eq 0 ]; then 175 | send_mail " ERR" "Parsing error. Possibly the ini file is invalid" 176 | return 1 177 | fi 178 | } 179 | 180 | # *.conf file in pending directory 181 | # ACTIVE.active = true 182 | # will be checked if an existing job exists and deleted if found 183 | # the conf will be moved into the "active" folder, 184 | # finally a new cronjob will be created 185 | # @param $1: Pass the path + filename of the config 186 | function activate_cron() { 187 | if [ -z "$1" ]; then 188 | return 1 189 | fi 190 | 191 | log "INFO" "Search for existing cron and delete it." 192 | delete_cron_entry "$file" 193 | 194 | # Read the CRON section from ini file 195 | parse_ini "$file" "CRON" 196 | if [ "$DRY_RUN" -eq 0 ]; then 197 | log "INFO" "Added cronjob to crontab." 198 | COMMAND="/bin/sh $SCRIPT_DIR/$CRON_CMD $CONF_DIR/active/$(basename $file) # cron_id $CONF__CRON__cron_id" 199 | JOB="$CONF__CRON__cron_output_minutes $CONF__CRON__cron_output_hours $CONF__CRON__cron_output_dom $CONF__CRON__cron_output_months $CONF__CRON__cron_output_dow $COMMAND" 200 | ( crontab -l | grep -v -F "$COMMAND" || : ; echo "$JOB" ) | crontab - 201 | else 202 | log "INFO" "Added cronjob to crontab." 203 | fi 204 | log "INFO" "Moving file $file to $CONF_DIR/active." 205 | if [ "$DRY_RUN" -eq 0 ]; then 206 | mv "$file" "$CONF_DIR/active/$(basename "$file")" 207 | else 208 | log "INFO" "Moved file $file to $CONF_DIR/active." 209 | fi 210 | } 211 | 212 | # *.conf file in pending directory 213 | # ACTIVE.active = false 214 | # will be checked if an existing job exists and deleted if found 215 | # the conf will be moved into the "inactive" folder 216 | # no cronjob will be created 217 | # @param $1: Pass the path + filename of the config 218 | function deactivate_cron() { 219 | if [ -z "$1" ]; then 220 | return 1 221 | fi 222 | 223 | log "INFO" "Search for existing cron and delete it." 224 | delete_cron_entry "$file" 225 | 226 | log "INFO" "Moving file $file to $CONF_DIR/inactive." 227 | if [ "$DRY_RUN" -eq 0 ]; then 228 | mv "$file" "$CONF_DIR/inactive/$(basename "$file")" 229 | else 230 | log " DRY" "Moved file $file to $CONF_DIR/inactive." 231 | fi 232 | } 233 | 234 | # *.conf file in remove directory 235 | # will be checked if an existing job exists and deleted if found 236 | # the conf will be deleted 237 | # @param $1: Pass the path + filename of the config 238 | function delete_cron() { 239 | if [ -z "$1" ]; then 240 | return 1 241 | fi 242 | 243 | log "INFO" "Search for existing cron and delete it." 244 | delete_cron_entry "$file" 245 | 246 | if [ "$DRY_RUN" -eq 0 ]; then 247 | rm "${file:?}" 248 | else 249 | log " DRY" "Deleted file $file." 250 | fi 251 | } 252 | 253 | if [ $# -eq 0 ]; then 254 | show_usage 255 | exit 0 256 | fi 257 | 258 | while [ -n "$1" ]; do 259 | case $1 in 260 | --run|-r) 261 | START=1 262 | ;; 263 | --dry-run|-d) 264 | DRY_RUN=1 265 | ;; 266 | --verbose|-v) 267 | VERBOSE=1 268 | ;; 269 | *) 270 | log " ERR" "Invalid options." 271 | show_usage 272 | exit 1 273 | ;; 274 | esac 275 | shift || true 276 | done 277 | 278 | if [ "$START" -eq 1 ]; then 279 | # Processing the pending directory 280 | for file in "$CONF_DIR"/pending/*.ini; do 281 | if [ -f "$file" ]; then 282 | log "INFO" "Processing config file $file." 283 | parse_main_info "$file" 284 | # Read the ACTIVE section from ini file 285 | parse_ini "$file" "ACTIVE" 286 | ACTIVE_STATE="$CONF__ACTIVE__active" 287 | if [ -z "$ACTIVE_STATE" ]; then 288 | send_mail " ERR" "Missing ACTIVE.active" 289 | exit 1 290 | else 291 | if [ "$ACTIVE_STATE" -eq 1 ]; then 292 | log "INFO" "Activate cronjob for $file." 293 | activate_cron "$file" 294 | elif [ "$ACTIVE_STATE" -eq 0 ]; then 295 | log "INFO" "Deactivate cronjob for $file." 296 | deactivate_cron "$file" 297 | else 298 | send_mail " ERR" "ACTIVE.active has an invalid value." 299 | exit 1 300 | fi 301 | fi 302 | fi 303 | CRON_ID="" # Reset cron_id 304 | done 305 | 306 | # Processing the remove directory 307 | for file in "$CONF_DIR"/remove/*.ini; do 308 | if [ -f "$file" ]; then 309 | log "INFO" "Processing config file $file." 310 | # It's not neccessary to parse email info 311 | # Just delete config file an existing cronjob 312 | # parse_main_info "$file" 313 | log "INFO" "Delete cronjob for $file." 314 | delete_cron "$file" 315 | fi 316 | # CRON_ID="" # Reset cron_id 317 | done 318 | else 319 | show_usage 320 | exit 0 321 | fi 322 | 323 | exit 0 324 | -------------------------------------------------------------------------------- /admin/elements/scripts/send_mail.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | FROM="admin" 4 | TO="" 5 | SUBJECT="" 6 | BODY="" 7 | 8 | function show_usage() { 9 | printf "\n" 10 | printf "Usage:\n" 11 | printf "%s\n" " $0 [options [parameters]]" 12 | printf "\n" 13 | printf "Options:\n" 14 | printf "%s\t\t%s\n" " -f, --from" "Name of the sender (optional, default is admin)." 15 | printf "%s\t\t%s\n" " -t, --to" "E-mail address of the receiver." 16 | printf "%s\t\t%s\n" " -s, --subject" "Subject of the email." 17 | printf "%s\t\t%s\n" " -b, --body" "Body of the email." 18 | printf "%s\t\t%s\n" " -h, --help" "Print this help." 19 | printf "\n" 20 | 21 | return 0 22 | } 23 | 24 | while [ -n "$1" ]; do 25 | case $1 in 26 | --subject|-s) 27 | shift 28 | SUBJECT="$1" 29 | ;; 30 | --body|-b) 31 | shift 32 | BODY="$1" 33 | ;; 34 | --from|-f) 35 | shift 36 | FROM="$1" 37 | ;; 38 | --to|-t) 39 | shift 40 | TO="$1" 41 | ;; 42 | *) 43 | echo "ERROR: Invalid options." 44 | show_usage 45 | exit 1 46 | ;; 47 | esac 48 | shift || true 49 | done 50 | 51 | if [ -z "$TO" ] || [ -z "$SUBJECT" ] || [ -z "$BODY" ]; then 52 | echo "ERROR: Missing options." 53 | show_usage 54 | exit 1 55 | fi 56 | 57 | # sendmail command line optons: 58 | # -i - do not treat lines starting with dot specially 59 | # -t - read recipients lists from message headers: TO,CC,BCC 60 | # -v - use verbose mode (describe what is happening) 61 | 62 | /usr/sbin/sendmail -i -t << MESSAGE_END 63 | From: $FROM 64 | To: $TO 65 | Subject: $SUBJECT 66 | $BODY 67 | MESSAGE_END 68 | -------------------------------------------------------------------------------- /admin/elements/scripts/software_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIRECTADMIN_INI="/usr/local/directadmin/plugins/rclone_backup/admin/elements/conf/directadmin.ini" 4 | 5 | # Check if rclone is installed 6 | if [[ $1 == "all" || $1 == "rclone" ]]; then 7 | if [[ $2 == "v" ]]; then 8 | echo "Check if RClone is installed" 9 | fi 10 | if ! command rclone version &> /dev/null 11 | then 12 | echo "ERROR: RClone could not be found." 13 | sed -i '/rclone_installed*/ c\rclone_installed = false' $DIRECTADMIN_INI 14 | else 15 | echo "RClone was found successfully" 16 | sed -i '/rclone_installed*/ c\rclone_installed = true' $DIRECTADMIN_INI 17 | fi 18 | fi 19 | 20 | # Check if zip is installed 21 | if [[ $1 == "all" || $1 == "zip" ]]; then 22 | if [[ $2 == "v" ]]; then 23 | echo "Check if Zip is installed." 24 | fi 25 | if ! command -v zip &> /dev/null 26 | then 27 | echo "ERROR: Zip could not be found. Option will not be available." 28 | sed -i '/zip_installed*/ c\zip_installed = false' $DIRECTADMIN_INI 29 | else 30 | echo "Zip was found successfully" 31 | sed -i '/zip_installed*/ c\zip_installed = true' $DIRECTADMIN_INI 32 | fi 33 | fi 34 | 35 | # Check if mysql is installed 36 | if [[ $1 == "all" || $1 == "mysql" ]]; then 37 | if [[ $2 == "v" ]]; then 38 | echo "Check if MySQL is installed" 39 | fi 40 | if ! command -v mysql &> /dev/null 41 | then 42 | echo "ERROR: MySQL could not be found. Option will not be available." 43 | sed -i '/mysql_installed*/ c\mysql_installed = false' $DIRECTADMIN_INI 44 | else 45 | echo "MySQL was found successfully" 46 | sed -i '/mysql_installed*/ c\mysql_installed = true' $DIRECTADMIN_INI 47 | fi 48 | fi 49 | 50 | # Check if postgresql is installed 51 | if [[ $1 == "all" || $1 == "postgresql" ]]; then 52 | if [[ $2 == "v" ]]; then 53 | echo "Check if PostgreSQL is installed" 54 | fi 55 | if ! command -v psql &> /dev/null 56 | then 57 | echo "ERROR: PostgreSQL could not be found. Option will not be available." 58 | sed -i '/postgresql_installed*/ c\postgresql_installed = false' $DIRECTADMIN_INI 59 | else 60 | echo "PostgreSQL was found successfully" 61 | sed -i '/postgresql_installed*/ c\postgresql_installed = true' $DIRECTADMIN_INI 62 | fi 63 | fi 64 | 65 | # Check if mongodb is installed 66 | if [[ $1 == "all" || $1 == "mongodb" ]]; then 67 | if [[ $2 == "v" ]]; then 68 | echo "Check if MongoDB is installed" 69 | fi 70 | if ! command -v mongo &> /dev/null 71 | then 72 | echo "ERROR: MongoDB could not be found. Option will not be available." 73 | sed -i '/mongodb_installed*/ c\mongodb_installed = false' $DIRECTADMIN_INI 74 | else 75 | echo "MongoDB was found successfully" 76 | sed -i '/mongodb_installed*/ c\mongodb_installed = true' $DIRECTADMIN_INI 77 | fi 78 | fi 79 | -------------------------------------------------------------------------------- /admin/elements/scripts/update_ini.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ROOT_DIR="/usr/local/directadmin/plugins/rclone_backup" 4 | DIRECTADMIN_INI=$ROOT_DIR/admin/elements/conf/directadmin.ini 5 | 6 | # Update directadmin INI file 7 | echo "Update ini file" 8 | # Check if INI exists 9 | if [ -f "$DIRECTADMIN_INI" ]; 10 | then 11 | echo "File exists" 12 | iniVersion=$(awk -F "=" '/version/ {print $2}' $DIRECTADMIN_INI | tr -d ' ') 13 | echo "Current version is $iniVersion" 14 | 15 | # Check if $iniVersion has been set. Else version is less than 1.0.7 16 | if [ -z "$iniVersion" ] 17 | then 18 | echo "ini version is blank" 19 | iniVersion="1.0.6" 20 | fi 21 | 22 | # Run update for version 1.0.6 23 | if [ $iniVersion = "1.0.6" ]; then 24 | echo "updating ini file" 25 | #update 26 | sed -i '/^\[SETTINGS\]/a\ignore_certificate = false' $DIRECTADMIN_INI 27 | sed -i '/^\[SETTINGS\]/a\version = 1.0.7' $DIRECTADMIN_INI 28 | iniVersion=1.0.7 29 | echo "Updated ini to version 1.0.7" 30 | 31 | #Fix crontabs 32 | CRON_FILE="/var/spool/cron/root" 33 | 34 | tmp=$(mktemp) 35 | grep -v "$(basename "manage_cron.sh")" "$CRON_FILE" > "$tmp" && mv "$tmp" "$CRON_FILE" 36 | 37 | #Reset crons 38 | mv $ROOT_DIR/admin/elements/conf/active/* $ROOT_DIR/admin/elements/conf/pending/ 39 | mv $ROOT_DIR/admin/elements/conf/inactive/* $ROOT_DIR/admin/elements/conf/pending/ 40 | 41 | for file in "$CONF_DIR"/pending/*.ini; do 42 | if [ -f "$file" ]; then 43 | tmp=$(mktemp) 44 | grep -v "$(basename "$file")" "$CRON_FILE" > "$tmp" && mv "$tmp" "$CRON_FILE" 45 | fi 46 | done 47 | 48 | ./$ROOT_DIR/admin/elements/scripts/manage_cron.sh -r 49 | fi 50 | 51 | # Run update for version 1.0.7 52 | if [ $iniVersion = "1.0.7" ]; then 53 | echo "updating ini file" 54 | #update 55 | sed -i '/^\[SETTINGS\]/a\my_conf_login =' $DIRECTADMIN_INI 56 | sed -i '/^\[SETTINGS\]/a\my_conf_enabled = 0' $DIRECTADMIN_INI 57 | sed -i 's/1.0.7/1.0.8/g' $DIRECTADMIN_INI 58 | iniVersion=1.0.8 59 | echo "Updated ini to version 1.0.8" 60 | fi 61 | 62 | # Run next update. 63 | fi -------------------------------------------------------------------------------- /admin/index.html: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/php -n 2 | -------------------------------------------------------------------------------- /hooks/admin_txt.html: -------------------------------------------------------------------------------- 1 | RCLONE Backup Interface 2 | -------------------------------------------------------------------------------- /images/css/default.css: -------------------------------------------------------------------------------- 1 | .button { 2 | width: 100%; 3 | background-color: #785580; 4 | border: none; 5 | color: #fff; 6 | border-radius: 2px; 7 | padding: 7px 10px; 8 | } 9 | 10 | .form { 11 | border-radius: 3px; 12 | padding: 10px 15px; 13 | background-color: rgba(0, 0, 0, 0.2); 14 | } -------------------------------------------------------------------------------- /images/js/default.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JS dealing with cron time creation. 3 | * Created by thomasba - https://github.com/thomasba/cron-expression-generator 4 | * @type {string} 5 | */ 6 | 7 | let cron_minutes = "*"; 8 | let cron_hours = "*"; 9 | let cron_dom = "*"; 10 | let cron_months = "*"; 11 | let cron_dow = "*"; 12 | 13 | let cron_minutes_id = "#cron-minutes"; 14 | let cron_hours_id = "#cron-hours"; 15 | let cron_dom_id = "#cron-dom"; 16 | let cron_months_id = "#cron-months"; 17 | let cron_dow_id = "#cron-dow"; 18 | let cron_output_id = "#cron-output"; 19 | 20 | let cron_output_id_minutes = "#cron_output_minutes"; 21 | let cron_output_id_hours = "#cron_output_hours"; 22 | let cron_output_id_dom = "#cron_output_dom"; 23 | let cron_output_id_months = "#cron_output_months"; 24 | let cron_output_id_dow = "#cron_output_dow"; 25 | 26 | /**Convert a list of values to cron-syntax 27 | * @return string 28 | * @param zeroAllowed whether the number zero is allowed (true) or not (false) 29 | * @param max the maximum value (eg. 59 for minutes) 30 | * @param values a list of selected values 31 | */ 32 | function cronCalculate(zeroAllowed, max, values) { 33 | if (zeroAllowed === false) { 34 | values.push(0); 35 | } 36 | if (values.length > max || values.length === 0) { 37 | return "*"; 38 | } 39 | values.sort(function (a, b) { 40 | return a - b 41 | }); 42 | out: 43 | for (let d = 2; d <= Math.ceil(max / 2); d++) { 44 | let tmp = values.slice(); 45 | for (x = 0; x * d <= max; x++) { 46 | if (tmp.indexOf(x * d) === -1) { 47 | continue out; 48 | } 49 | else { 50 | tmp.splice(tmp.indexOf(x * d), 1); 51 | } 52 | } 53 | if (tmp.length === 0) { 54 | return "*/" + d; 55 | } 56 | } 57 | // if not allowed, remove 0 58 | if (zeroAllowed === false) { 59 | values.splice(values.indexOf(0), 1); 60 | } 61 | // ranges ("2,8,20,25-35") 62 | let output = values[0] + ""; 63 | let range = false; 64 | for (let i = 1; i < values.length; i++) { 65 | if (values[i - 1] + 1 === values[i]) { 66 | range = true; 67 | } else { 68 | if (range) { 69 | output = output + "-" + values[i - 1]; 70 | } 71 | range = false; 72 | output = output + "," + values[i]; 73 | } 74 | } 75 | if (range) { 76 | output = output + "-" + values[values.length - 1]; 77 | } 78 | return output; 79 | } 80 | 81 | /** Convert a cron-expression (one item) to a list of values 82 | * @return int[] 83 | * @param allowZero whether the number zero is allowed (true) or not (false) 84 | * @param maxValue the maximum value (eg. 59 for minutes) 85 | * @param value the cron expression (eg. "*") 86 | */ 87 | function cronValueItemToList(allowZero, maxValue, value) { 88 | let list = []; 89 | if (value === "*") { 90 | for (let i = allowZero ? 0 : 1; i <= maxValue; i++) { 91 | list.push(i); 92 | } 93 | } else if (value.match(/^\*\/[1-9][0-9]?$/)) { 94 | let c = parseInt(value.match(/^\*\/([1-9][0-9]?)$/)[1]); 95 | for (let i = allowZero ? 0 : 1; i <= maxValue; i++) { 96 | if (i % c === 0) { 97 | list.push(i); 98 | } 99 | } 100 | } else if (value.match(/^([0-9]+|[0-9]+-[0-9]+)(,[0-9]+|,[0-9]+-[0-9]+)*$/)) { 101 | let a = value.split(","); 102 | for (let i = 0; i < a.length; i++) { 103 | let e = a[i].split("-"); 104 | if (e.length === 2) { 105 | for (let j = parseInt(e[0]); j <= parseInt(e[1]); j++) { 106 | list.push(j); 107 | } 108 | } else { 109 | list.push(parseInt(e[0])); 110 | } 111 | } 112 | } else { 113 | return []; 114 | } 115 | return list; 116 | } 117 | 118 | /** Import the value of a given field to the variables 119 | * @param source the input field containing the string 120 | */ 121 | function importCronExpressionFromInput(source) { 122 | importCronExpression($(source).val()); 123 | } 124 | 125 | /** Import value of a given string to the variables 126 | * @param expression The string 127 | */ 128 | function importCronExpression(expression) { 129 | if (!expression.match(/^((\*(\/[1-9][0-9]?)?|([0-9]{1,2}(-[0-9]{1,2})?)(,[0-9]{1,2}(-[0-9]{1,2})?)*)( |$)){5}$/)) { 130 | return; 131 | } 132 | let parts = expression.split(" "); 133 | if (parts[0] !== cron_minutes) { 134 | cron_minutes = parts[0]; 135 | cronHelperSelectList(cron_minutes_id, cronValueItemToList(true, 59, parts[0])); 136 | } 137 | if (parts[1] !== cron_hours) { 138 | cron_hours = parts[1]; 139 | cronHelperSelectList(cron_hours_id, cronValueItemToList(true, 23, parts[1])); 140 | } 141 | if (parts[2] !== cron_dom) { 142 | cron_dom = parts[2]; 143 | cronHelperSelectList(cron_dom_id, cronValueItemToList(false, 31, parts[2])); 144 | } 145 | if (parts[3] !== cron_months) { 146 | cron_months = parts[3]; 147 | cronHelperSelectList(cron_months_id, cronValueItemToList(false, 12, parts[3])); 148 | } 149 | if (parts[4] !== cron_dow) { 150 | cron_dow = parts[4]; 151 | cronHelperSelectList(cron_dow_id, cronValueItemToList(true, 6, parts[4])); 152 | } 153 | } 154 | 155 | /** Returns a string containing the current cron-expression 156 | * @return string 157 | */ 158 | function getCronExpression() { 159 | return cron_minutes + " " + cron_hours + " " + cron_dom + " " + 160 | cron_months + " " + cron_dow; 161 | } 162 | 163 | /** Get a list of all selected items 164 | * @param id The select-ID 165 | * @return int[] 166 | */ 167 | function getSelectedElements(id) { 168 | return $.map($(id + ' > option:selected'), function (element) { 169 | if (parseInt(element.value) !== "NaN") { 170 | return parseInt(element.value); 171 | } else { 172 | return 0; 173 | } 174 | }); 175 | } 176 | 177 | /** Update the variables 178 | * @param type hours/minutes/dom/months/dow 179 | * @return nothing 180 | */ 181 | function updateField(type) { 182 | switch (type) { 183 | case "hours": 184 | cron_hours = cronCalculate(true, 23, getSelectedElements(cron_hours_id)); 185 | $(cron_output_id_hours).val(cron_hours); 186 | break; 187 | case "minutes": 188 | cron_minutes = cronCalculate(true, 59, getSelectedElements(cron_minutes_id)); 189 | $(cron_output_id_minutes).val(cron_minutes); 190 | break; 191 | case "dom": 192 | cron_dom = cronCalculate(false, 31, getSelectedElements(cron_dom_id)); 193 | $(cron_output_id_dom).val(cron_dom); 194 | break; 195 | case "months": 196 | cron_months = cronCalculate(false, 12, getSelectedElements(cron_months_id)); 197 | $(cron_output_id_months).val(cron_months); 198 | break; 199 | case "dow": 200 | cron_dow = cronCalculate(true, 6, getSelectedElements(cron_dow_id)); 201 | $(cron_output_id_dow).val(cron_dow); 202 | break; 203 | } 204 | $(cron_output_id).val(getCronExpression); 205 | } 206 | 207 | /** Set the fields to a given template 208 | * @param id the templates ID (see comments) 209 | */ 210 | function cronTemplate(id) { 211 | // select all: 212 | cronHelperSelectAll(cron_dom_id); 213 | cronHelperSelectAll(cron_months_id); 214 | cronHelperSelectAll(cron_dow_id); 215 | switch (id) { 216 | case 1: // every 5 minutes 217 | cronHelperSelectList(cron_minutes_id, [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]); 218 | cronHelperSelectAll(cron_hours_id); 219 | break; 220 | case 2: // every 15 minutes 221 | cronHelperSelectList(cron_minutes_id, [0, 15, 30, 45]); 222 | cronHelperSelectAll(cron_hours_id); 223 | break; 224 | case 3: // every 30 minutes 225 | cronHelperSelectList(cron_minutes_id, [0, 30]); 226 | cronHelperSelectAll(cron_hours_id); 227 | break; 228 | case 4: // every hour 229 | cronHelperSelectList(cron_minutes_id, [0]); 230 | cronHelperSelectAll(cron_hours_id); 231 | break; 232 | case 5: // every 3 hours 233 | cronHelperSelectList(cron_minutes_id, [0]); 234 | cronHelperSelectList(cron_hours_id, [0, 3, 6, 9, 12, 15, 18, 21]); 235 | break; 236 | case 6: // every 4 hours 237 | cronHelperSelectList(cron_minutes_id, [0]); 238 | cronHelperSelectList(cron_hours_id, [0, 4, 8, 12, 16, 20]); 239 | break; 240 | case 7: // every 6 hours 241 | cronHelperSelectList(cron_minutes_id, [0]); 242 | cronHelperSelectList(cron_hours_id, [0, 6, 12, 18]); 243 | break; 244 | case 8: // every 12 hours 245 | cronHelperSelectList(cron_minutes_id, [0]); 246 | cronHelperSelectList(cron_hours_id, [0, 12]); 247 | break; 248 | case 9: // every day at 00:30am 249 | cronHelperSelectList(cron_minutes_id, [30]); 250 | cronHelperSelectList(cron_hours_id, [0]); 251 | break; 252 | case 10: //every sunday at 00:10am 253 | cronHelperSelectList(cron_minutes_id, [10]); 254 | cronHelperSelectList(cron_hours_id, [0]); 255 | cronHelperSelectList(cron_dow_id, [0]); 256 | break; 257 | default: // select all 258 | cronHelperSelectAll(cron_minutes_id); 259 | cronHelperSelectAll(cron_hours_id); 260 | } 261 | $(cron_minutes_id).change(); 262 | $(cron_hours_id).change(); 263 | $(cron_dom_id).change(); 264 | $(cron_months_id).change(); 265 | $(cron_dow_id).change(); 266 | } 267 | 268 | /** List all select items contained in the list 269 | * @param id jQuery selector 270 | * @param list The values to select 271 | */ 272 | function cronHelperSelectList(id, list) { 273 | $(id).val(list); 274 | } 275 | 276 | /** Select all elements in a select element 277 | * @param id jQuery selector 278 | */ 279 | function cronHelperSelectAll(id) { 280 | $(id + ' > option').prop('selected', true); 281 | $(id).change(); 282 | } 283 | 284 | $("#form").on("submit", function () { 285 | $.ajax({ 286 | type: "POST", 287 | url: "", 288 | data: $(this).serialize(), 289 | error: function (xhr, desc, err) { 290 | console.log(err); 291 | } 292 | }); 293 | }); -------------------------------------------------------------------------------- /plugin.conf: -------------------------------------------------------------------------------- 1 | active=yes 2 | author=AdrianB 3 | id=rclone_backup 4 | installed=yes 5 | name=RClone Backup 6 | update_url=https://raw.githubusercontent.com/adrianb11/directadmin_rclone_backup/master/rclone_backup.tar.gz 7 | version=1.0.8 8 | version_url=https://raw.githubusercontent.com/adrianb11/directadmin_rclone_backup/master/version.html -------------------------------------------------------------------------------- /rclone_backup.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianb11/directadmin_rclone_backup/78df562ea90126f8a6b1e66c133fa69c1c8e7094/rclone_backup.tar.gz -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | CRON_FILE="/var/spool/cron/root" 3 | ROOT_DIR="/usr/local/directadmin/plugins/rclone_backup" 4 | 5 | echo "" 6 | echo "Create ini file" 7 | [ -e $ROOT_DIR/admin/elements/conf/directadmin.ini ] && echo "Config found" || mv $ROOT_DIR/admin/elements/conf/directadmin.ini.sample $ROOT_DIR/admin/elements/conf/directadmin.ini 8 | 9 | . /usr/local/directadmin/plugins/rclone_backup/admin/elements/scripts/update_ini.sh 10 | 11 | cd $ROOT_DIR 12 | 13 | echo "Creating required folders" 14 | mkdir -p admin/elements/conf/pending 15 | mkdir -p admin/elements/conf/active 16 | mkdir -p admin/elements/conf/inactive 17 | mkdir -p admin/elements/conf/remove 18 | 19 | echo "Changing permissions and ownership of files" 20 | chmod -R 755 admin/* 21 | chown -R admin:admin admin/* 22 | 23 | echo "Adding cron scheduler to crontab" 24 | COMMAND="/bin/sh $ROOT_DIR/admin/elements/scripts/manage_cron.sh -r # cron scheduler" 25 | JOB="*\5 * * * * $COMMAND" 26 | ( crontab -l | grep -v -F "$COMMAND" || : ; echo "$JOB" ) | crontab - 27 | 28 | echo "Checking for installed software" 29 | sh $ROOT_DIR/admin/elements/scripts/software_check.sh all v 30 | 31 | echo "Plugin Installed!" #NOT! :) 32 | 33 | exit 0 34 | -------------------------------------------------------------------------------- /scripts/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Plugin Un-Installed!" #NOT! :) 4 | 5 | ROOT_DIR="/usr/local/directadmin/plugins/rclone_backup" 6 | 7 | mv $ROOT_DIR/admin/elements/conf/active/* $ROOT_DIR/admin/elements/conf/remove/ 8 | mv $ROOT_DIR/admin/elements/conf/inactive/* $ROOT_DIR/admin/elements/conf/remove/ 9 | 10 | sh $ROOT_DIR/admin/elements/scripts/manage_cron.sh -r 11 | 12 | # Remove cron scheduler 13 | COMMAND="/bin/sh $ROOT_DIR/admin/elements/scripts/manage_cron.sh -r # cron scheduler" 14 | ( crontab -l | grep -v -F "$COMMAND" ) | crontab - 15 | 16 | exit 0 17 | -------------------------------------------------------------------------------- /scripts/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ROOT_DIR="/usr/local/directadmin/plugins/rclone_backup" 4 | cd $ROOT_DIR 5 | 6 | . /usr/local/directadmin/plugins/rclone_backup/admin/elements/scripts/update_ini.sh 7 | 8 | echo "Creating required folders" 9 | mkdir -p admin/elements/conf/pending 10 | mkdir -p admin/elements/conf/active 11 | mkdir -p admin/elements/conf/inactive 12 | mkdir -p admin/elements/conf/remove 13 | 14 | echo "Changing permissions and ownership of files" 15 | chmod -R 755 admin/* 16 | chown -R admin:admin admin/* 17 | 18 | echo "Adding cron scheduler to crontab" 19 | COMMAND="/bin/sh $ROOT_DIR/admin/elements/scripts/manage_cron.sh -r # cron scheduler" 20 | JOB="*\5 * * * * $COMMAND" 21 | ( crontab -l | grep -v -F "$COMMAND" || : ; echo "$JOB" ) | crontab - 22 | 23 | echo "Plugin has been updated!" #NOT! :) 24 | 25 | exit 0 26 | -------------------------------------------------------------------------------- /version.html: -------------------------------------------------------------------------------- 1 | 1.0.8 --------------------------------------------------------------------------------