├── .gitignore ├── LICENSE ├── README.md ├── database ├── centos_data.sql ├── db_create.sql └── db_update.sql ├── hash_pass.php ├── html ├── .htaccess ├── client │ ├── check-in.php │ ├── check-in.sh │ ├── check_updates.vbs │ ├── client_installer.php │ ├── get_commands.php │ ├── index.php │ ├── package_checker.sh │ ├── patch_checker.sh │ ├── run_commands.sh │ ├── send_packages.php │ └── send_patches.php ├── css │ ├── bootstrap-switch.min.css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ ├── bootstrapValidator.min.css │ ├── dashboard.css │ ├── index.php │ └── jquery.easy-pie-chart.css ├── favicon.ico ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── index.php ├── img │ ├── 404.png │ ├── admin.png │ ├── graph.png │ ├── icon │ │ ├── centos.png │ │ ├── debian.png │ │ ├── devuan.png │ │ ├── fedora.png │ │ ├── index.php │ │ ├── oracle.png │ │ ├── raspbian.png │ │ ├── rhel.png │ │ └── ubuntu.png │ ├── index.php │ ├── offline.png │ ├── report.png │ ├── settings.png │ └── ubuntu.png ├── inc │ ├── 404_body.inc.php │ ├── 404_footer.inc.php │ ├── 404_header.inc.php │ ├── 404_navbar.inc.php │ ├── body_template.php │ ├── footer.inc.php │ ├── header.inc.php │ ├── index.php │ ├── login.inc.php │ ├── logout.inc.php │ ├── navbar.inc.php │ ├── offline.inc.php │ ├── p_login.inc.php │ └── supressed_patches.inc.php ├── index.php ├── js │ ├── bootstrap-switch.min.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── bootstrapValidator.min.js │ ├── docs.min.js │ ├── excanvas.js │ ├── html5shiv.js │ ├── index.php │ ├── jquery.easy-pie-chart.js │ ├── jquery.metadata.js │ ├── jquery.min.js │ ├── jquery.tablesorter.js │ ├── jquery.tablesorter.min.js │ └── respond.min.js ├── lib │ ├── 3rdparty │ │ ├── freenac │ │ │ ├── LICENSE │ │ │ ├── index.php │ │ │ ├── wsus_getinfo.php │ │ │ └── wsus_sync.php │ │ └── index.php │ ├── db_config.php │ └── index.php └── plugins │ ├── admin │ ├── activate_server.inc.php │ ├── activate_user.inc.php │ ├── add_server.inc.php │ ├── add_suppression.inc.php │ ├── add_user.inc.php │ ├── deactivate_server.inc.php │ ├── deactivate_user.inc.php │ ├── delete_server.inc.php │ ├── delete_suppression.inc.php │ ├── delete_user.inc.php │ ├── edit_server.inc.php │ ├── edit_user.inc.php │ ├── global_suppress.inc.php │ ├── index.php │ ├── manage_plugins.inc.php │ ├── manage_servers.inc.php │ ├── manage_users.inc.php │ ├── p_add_user.inc.php │ ├── p_edit_server.inc.php │ └── p_edit_user.inc.php │ ├── index.php │ └── main │ ├── index.php │ ├── install_all.inc.php │ ├── p_profile.inc.php │ ├── packages.inc.php │ ├── patch_list.inc.php │ ├── patches.inc.php │ ├── preferences.inc.php │ ├── profile.inc.php │ └── search.inc.php ├── install.sh ├── nbproject ├── project.properties └── project.xml ├── scripts ├── add_server.sh ├── db.conf ├── db_config.php ├── install_packages.sh ├── package_checker.sh ├── patch_checker.sh ├── queue_package_check.sh ├── queue_patch_check.sh ├── run_get_package_list.php ├── run_patch_check.php ├── start_get_package_list.sh └── start_patch_check.sh └── sendmail.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/private/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | 179 | Copyright {2014} {Jon Harris} 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PatchMD 2 | ============== 3 | 4 | Patch Management Dashboard 5 | =============== 6 | Patch Management Dashboard does one thing, but it does it well: 7 | * Monitors for needed patches on your nodes. 8 | 9 | ~~In a future release (Hopefully sometime in August), it will give you the ability to suppress patches on 10 | a per-server or a global basis, as well as tell the system to install a single package, or fully update a 11 | server.~~ **Suppression is complete everywhere except the UI. If you want to add a suppressed package, you can do so with a command similar to this:** 12 | 13 | Single server named 'some server' 14 | 15 | ``` 16 | INSERT INTO `supressed` (`package_name`,`server_name`) VALUES('some_package','some-server'); 17 | ``` 18 | 19 | Global Suppression 20 | 21 | ``` 22 | INSERT INTO `supressed` (`package_name`,`server_name`) VALUES('some_package',0); 23 | ``` 24 | 25 | Compatible with: Debian Wheezy+/Ubuntu 10.04+/CentOS 5.x+/Red Hat EL5+/Fedora FC19+ 26 | 27 | News 28 | =============== 29 | 2015-07-11 30 | 31 | We are now back under active development. We apologize for the long hiatus, but when life 32 | happens, some times other things take back burner. 33 | 34 | Look for more frequent releases! 35 | 36 | 37 | v1.0-RC1 is now available. You can grab a copy [here](https://github.com/PatchDashboard/patchdashboard/releases/tag/v1.0-RC1) or by clicking on the one you want below: 38 | 39 | * [zip](https://github.com/PatchDashboard/patchdashboard/archive/v1.0-RC1.zip) 40 | * [tar.gz](https://github.com/PatchDashboard/patchdashboard/archive/v1.0-RC1.tar.gz) 41 | 42 | Please give it a spin. If you have any issues, please submit a [new issue](https://github.com/PatchDashboard/patchdashboard/issues/new) 43 | 44 | 45 | Install 46 | =============== 47 | 48 | To install: 49 | 50 | * simply clone this git (git clone https://github.com/PatchDashboard/patchdashboard) 51 | * cd into patchdashboard (cd /opt/patchdashboard) 52 | * run install.sh (./install.sh) 53 | 54 | It will ask you some questions. Simply provide the answers, or accept the default answers found in the "[]" boxes 55 | If it does not have anything in "[]", you must provide an answer. 56 | 57 | Finally To add a node: 58 | * run "/opt/patch_manager/add_server.sh" 59 | * follow instructions 60 | 61 | On each node: 62 | 63 | If you want to do the push method, follow the instructions below. 64 | * make sure root can log in, and is not blocked by sshd_config directives 65 | * if root cannot log in (their password is hashed, but SSH allows them), make sure to copy the contents of "id_rsa.pub" in /root/.ssh into /root/.ssh/authorized_keys on each node 66 | * after adding the shared key, from the patch server, ssh into the node as root to make sure the keypair works. 67 | * if root can log in (they have a password set), run this from the patch server: 68 | * ssh-copy-id root@SERVER_IP (change SERVER_IP to the nodes IP address) 69 | 70 | If you want to use the easier pull method: 71 | * go to your PatchDashboard Web UI 72 | * Click on "Add a Server" 73 | * Follow the second set of instructions, which will look something like this 74 | * "run the following on each node" 75 | * "curl https://1.1.1.1/client/client_installer.php|bash" 76 | 77 | =============== 78 | ##Links 79 | 80 | To discuss this product, or help direct the future of this project, join our [forums](http://community.patchdashboard.com) 81 | To keep up-to-date on the direction of this project, you are free to stop by the [blog](http://patchdashboard.com) (still in progress) 82 | 83 | 84 | TODO: NOT MUCH! 85 | =============== 86 | 87 | * Move everything to PDO, with prepared statements 88 | * Move to bcrypt for passwords 89 | * Complete installer script (WIP - please report errors and suggest improvements) **Done for Ubuntu/Debian/Fedora/RHEL/CentOS** 90 | * Add more distros (This one will be a continual one) 91 | * Configure Patch Suppression via the web UI. Back-end and DB are fully capable now. **In Progress -V1.0.1** 92 | * configure ability to install patches from the web interface **In Testing Phase** 93 | * configure ability to patch windows hosts **On roadmap for v1.5** 94 | 95 | =============== 96 | 97 | ROADMAP: 98 | =============== 99 | * v1.0 RC1 -- ~~ETC 2014-12-24~~ Released 100 | * Will be able to use Push or Pull methods to manage servers. Pull is by far the easiest method 101 | * Admin of servers and users is complete in this build 102 | * Installing via the web UI will be fully implemented by RC1 103 | 104 | * v1.0 -- ETC ~~2015-05-15~~ 2015-09-01 105 | * All the things we have done in v1.0 RC1, but with QA blessing 106 | 107 | * v1.0.1 -- ETC ~~2015-02-15~~ 2015-09-20 108 | * Suppression implemented 109 | 110 | * v1.1 111 | * First integration with off-site CVE database 112 | 113 | * v1.2 114 | * Addition of a plugin management system 115 | 116 | * v1.3 117 | * Alpha testing of Windows Server management 118 | 119 | * v1.4 120 | * Beta testing of Windows Server management 121 | 122 | * v1.5 123 | * Stable release of Windows Server management 124 | 125 | DEVELOPERS: 126 | =============== 127 | * jonsjava (Creator/Project guide/Underpinning Developer/PHP & BASH Developer/DBA) https://github.com/jonsjava 128 | * metalcated (BASH Developer) https://github.com/metalcated 129 | * gniltaws (Back-end/Front-end developer) https://github.com/gniltaws 130 | * wilsonma08 (DBA/BASH Developer) https://github.com/wilsonma08 131 | 132 | >If you like what we're doing, we could always use some donations to help with hosting costs. 133 | > 134 | > [paypal] 135 | 136 | 137 | A special thanks to Josh Reichardt (http://thepracticalsysadmin.com/) who helped a ton with my early release. You rock! 138 | -------------------------------------------------------------------------------- /hash_pass.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | -------------------------------------------------------------------------------- /html/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | RewriteCond %{REQUEST_FILENAME} !-f 3 | RewriteCond %{REQUEST_FILENAME} !-d 4 | RewriteRule patches/server/(.*)$ /patch_list?server=$1 [QSA,NC,L] 5 | 6 | RewriteCond %{REQUEST_FILENAME} !-f 7 | RewriteCond %{REQUEST_FILENAME} !-d 8 | RewriteRule packages/server/(.*)$ /packages?server=$1 [QSA,NC,L] 9 | 10 | RewriteCond %{REQUEST_FILENAME} !-f 11 | RewriteCond %{REQUEST_FILENAME} !-d 12 | RewriteRule search/exact/(.*)$ /search?package=$1&exact=true [QSA,NC,L] 13 | 14 | RewriteCond %{REQUEST_FILENAME} !-f 15 | RewriteCond %{REQUEST_FILENAME} !-d 16 | RewriteRule search/(.*)$ /search?package=$1 [QSA,NC,L] 17 | 18 | RewriteCond %{REQUEST_FILENAME} !-f 19 | RewriteCond %{REQUEST_FILENAME} !-d 20 | RewriteRule ^([^/]*)$ /index.php?page=$1 [QSA,L] 21 | 22 | RewriteCond %{REQUEST_FILENAME} !-f 23 | RewriteCond %{REQUEST_FILENAME} !-d 24 | RewriteRule ^([^/]*)$ /patchman2/index.php?page=$1 [QSA,L] 25 | 26 | #HTTP TO HTTPS 27 | #RewriteCond %{HTTPS} !=on 28 | #RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [QSA,L] -------------------------------------------------------------------------------- /html/client/check-in.php: -------------------------------------------------------------------------------- 1 | ${client_path}.patchrc 50 | else 51 | client_key=$(grep client_key ${client_path}.patchrc|awk -F\" {'print $2'}) 52 | fi 53 | # load client_key 54 | . ${client_path}.patchrc 55 | # remove any special characters 56 | client_os=$(echo $client_os|sed -e 's/[^a-zA-Z0-9]//g') 57 | curl -k -s -H "X-CLIENT-KEY: $client_key" -H "X-CLIENT-HOST: $client_host" -H "X-CLIENT-OS: $client_os" -H "X-CLIENT-OS-VER: $client_os_ver" $check_in > /tmp/check-in_$client_key 58 | cmds_line_count=$(cat /tmp/check-in_$client_key|wc -l) 59 | if [ "$cmds_line_count" -gt "1" ]; then 60 | . /tmp/check-in_$client_key 61 | if [ "$allowed" = "TRUE" ]; then 62 | if [ "$key_to_check" = "$auth_key" ]; then 63 | if [ "$check_patches" = "TRUE" ]; then 64 | ${client_path}patch_checker.sh& 65 | ${client_path}package_checker.sh& 66 | fi 67 | ${client_path}run_commands.sh& 68 | fi 69 | fi 70 | key_to_check=$(head -n 1 /tmp/check-in_$client_key) 71 | fi 72 | rm -rf /tmp/check-in_$client_key 73 | -------------------------------------------------------------------------------- /html/client/check_updates.vbs: -------------------------------------------------------------------------------- 1 | Option Explicit 2 | Dim nRebootStatusOption 3 | Dim cCriticalSecurityOption, cImportantSecurityOption 4 | Dim cModerateSecurityOption, cLowSecurityOption 5 | Dim cCriticalUpdateOption, cImportantUpdateOption 6 | Dim wCriticalSecurityOption, wImportantSecurityOption 7 | Dim wModerateSecurityOption, wLowSecurityOption 8 | Dim wCriticalUpdateOption, wImportantUpdateOption 9 | CONST rOK = 0 10 | CONST rWarning = 1 11 | CONST rCritical = 2 12 | CONST rUnknown = 3 13 | 14 | nRebootStatusOption = rWarning 15 | cCriticalSecurityOption = True 16 | cImportantSecurityOption = False 17 | cModerateSecurityOption = False 18 | cLowSecurityOption = False 19 | cCriticalUpdateOption = True 20 | cImportantUpdateOption = False 21 | wCriticalSecurityOption = False 22 | wImportantSecurityOption = True 23 | wModerateSecurityOption = True 24 | wLowSecurityOption = True 25 | wCriticalUpdateOption = False 26 | wImportantUpdateOption = True 27 | 28 | '====================== vars ======================== 29 | Dim objUpdateSession, objUpdateSearcher, colSearchResult, objUpdate, i, y 30 | Dim strCriticalSecurity, strImportantSecurity, strModerateSecurity, strLowSecurity 31 | Dim blnCriticalSecurity, blnImportantSecurity, blnModerateSecurity, blnLowSecurity 32 | 33 | Dim strCriticalUpdate, strImportantUpdate 34 | Dim blnCriticalUpdate, blnImportantUpdate 35 | Dim strReturnSummary, strReturnDetails, strReturnText 36 | 37 | Dim strScriptVersion 38 | Dim nCriticalSecurity, nImportantSecurity, nModerateSecurity, nLowSecurity, nCriticalUpdate, nImportantUpdate 39 | Dim bInvalidArgument, bDisplayHelp 40 | 41 | Set objUpdateSession = CreateObject("Microsoft.Update.Session") 42 | Set objUpdateSearcher = objUpdateSession.CreateUpdateSearcher() 43 | 44 | 45 | blnCriticalSecurity = False 46 | blnImportantSecurity = False 47 | 48 | Dim objFSO, ObjFile 49 | Set objFSO = CreateObject("Scripting.FileSystemObject") 50 | Set objFile = objFSO.CreateTextFile("C:\WindowsUpdates.log", True) 51 | 52 | ' Get Options from user 53 | GetOptions 54 | 55 | If (bDisplayHelp) Then 56 | DisplayHelp 57 | ElseIf (bInvalidArgument) Then 58 | DisplayInvalidArgument 59 | Else 60 | CheckUpdates 61 | End If 62 | 63 | ' ======================== subs/functions ======================== 64 | Sub CheckUpdates 65 | Dim colCategory, blnIsCritical 66 | Set colSearchResult = objUpdateSearcher.Search("IsInstalled=0 and Type='Software'") 67 | 68 | For i = 0 To colSearchResult.Updates.Count-1 69 | Set objUpdate = colSearchResult.Updates.Item(i) 70 | If (objUpdate.MsrcSeverity = "Critical") Then 71 | blnCriticalSecurity = True 72 | strCriticalSecurity = strCriticalSecurity & vbCrLF & objUpdate.Title 73 | Elseif (objUpdate.MsrcSeverity = "Important") Then 74 | blnImportantSecurity = True 75 | strImportantSecurity = strImportantSecurity & vbCrLF & objUpdate.Title 76 | Elseif (objUpdate.MsrcSeverity = "Moderate") Then 77 | blnModerateSecurity = True 78 | strModerateSecurity = strModerateSecurity & vbCrLF & objUpdate.Title 79 | Elseif (objUpdate.MsrcSeverity = "Low") Then 80 | blnLowSecurity = True 81 | strLowSecurity = strLowSecurity & vbCrLF & objUpdate.Title 82 | Elseif (objUpdate.AutoSelectOnWebSites = True) Then 83 | blnIsCritical = False 84 | For Each colCategory in objUpdate.Categories 85 | If (colCategory.Name = "Critical Updates") Then 86 | blnIsCritical = True 87 | End If 88 | Next 89 | If (blnIsCritical) Then 90 | blnCriticalUpdate = True 91 | strCriticalUpdate = strCriticalUpdate & vbCrLF & objUpdate.Title 92 | Else 93 | blnImportantUpdate = True 94 | strImportantUpdate = strImportantUpdate & vbCrLF & objUpdate.Title 95 | End If 96 | End If 97 | Next 98 | 99 | If (blnCriticalSecurity) Then 100 | strReturnSummary = nCriticalSecurity & " Critical Security" 101 | strReturnDetails = strCriticalSecurity 102 | End If 103 | If (blnImportantSecurity) Then 104 | If (Len(strReturnSummary) = 0) Then 105 | strReturnSummary = nImportantSecurity & " Important Security" 106 | Else 107 | strReturnSummary = strReturnSummary & ", " & nImportantSecurity & " Important Security" 108 | End If 109 | If (Len(strReturnDetails) = 0) Then 110 | strReturnDetails = strImportantSecurity 111 | Else 112 | strReturnDetails = strReturnDetails & strImportantSecurity 113 | End If 114 | End If 115 | If (blnModerateSecurity) Then 116 | If (Len(strReturnSummary) = 0) Then 117 | strReturnSummary = nModerateSecurity & " Moderate Security" 118 | Else 119 | strReturnSummary = strReturnSummary & ", " & nModerateSecurity & " Moderate Security" 120 | End If 121 | If (Len(strReturnDetails) = 0) Then 122 | strReturnDetails = strModerateSecurity 123 | Else 124 | strReturnDetails = strReturnDetails & strModerateSecurity 125 | End If 126 | End If 127 | If (blnLowSecurity) Then 128 | If (Len(strReturnSummary) = 0) Then 129 | strReturnSummary = nLowSecurity & " Low Security" 130 | Else 131 | strReturnSummary = strReturnSummary & ", " & nLowSecurity & " Low Security" 132 | End If 133 | If (Len(strReturnDetails) = 0) Then 134 | strReturnDetails = strLowSecurity 135 | Else 136 | strReturnDetails = strReturnDetails & strLowSecurity 137 | End If 138 | End If 139 | 140 | If (blnCriticalUpdate) Then 141 | If (Len(strReturnSummary) = 0) Then 142 | strReturnSummary = nCriticalUpdate & " Critical Updates" 143 | Else 144 | strReturnSummary = strReturnSummary & ", " & nCriticalUpdate & " Critical Updates" 145 | End If 146 | If (Len(strReturnDetails) = 0) Then 147 | strReturnDetails = strCriticalUpdate 148 | Else 149 | strReturnDetails = strReturnDetails & strCriticalUpdate 150 | End If 151 | End If 152 | 153 | If (blnImportantUpdate) Then 154 | If (Len(strReturnSummary) = 0) Then 155 | strReturnSummary = nImportantUpdate & " Important Updates" 156 | Else 157 | strReturnSummary = strReturnSummary & ", " & nImportantUpdate & " Important Updates" 158 | End If 159 | If (Len(strReturnDetails) = 0) Then 160 | strReturnDetails = strImportantUpdate 161 | Else 162 | strReturnDetails = strReturnDetails & strImportantUpdate 163 | End If 164 | End If 165 | 166 | strReturnText = "" 167 | If (Len(strReturnSummary) > 0) Then 168 | strReturnText = strReturnDetails 169 | End If 170 | 171 | 172 | If (blnCriticalSecurity = True And cCriticalSecurityOption = True) Then 173 | Wscript.Echo strReturnText 174 | objFile.WriteLine strReturnText 175 | Wscript.Quit(rCritical) 176 | Elseif (blnImportantSecurity = True And cImportantSecurityOption = True) Then 177 | Wscript.Echo strReturnText 178 | objFile.WriteLine strReturnText 179 | Wscript.Quit(rCritical) 180 | Elseif (blnModerateSecurity = True And cModerateSecurityOption = True) Then 181 | Wscript.Echo strReturnText 182 | objFile.WriteLine strReturnText 183 | Wscript.Quit(rCritical) 184 | Elseif (blnLowSecurity = True And cLowSecurityOption = True) Then 185 | Wscript.Echo strReturnText 186 | objFile.WriteLine strReturnText 187 | Wscript.Quit(rCritical) 188 | Elseif (blnCriticalUpdate = True And cCriticalUpdateOption = True) Then 189 | Wscript.Echo strReturnText 190 | objFile.WriteLine strReturnText 191 | Wscript.Quit(rCritical) 192 | Elseif (blnImportantUpdate = True And cImportantUpdateOption = True) Then 193 | Wscript.Echo strReturnText 194 | objFile.WriteLine strReturnText 195 | Wscript.Quit(rCritical) 196 | Elseif (blnCriticalSecurity = True And wCriticalSecurityOption = True) Then 197 | Wscript.Echo strReturnText 198 | objFile.WriteLine strReturnText 199 | Wscript.Quit(rWarning) 200 | Elseif (blnImportantSecurity = True And wImportantSecurityOption = True) Then 201 | Wscript.Echo strReturnText 202 | objFile.WriteLine strReturnText 203 | Wscript.Quit(rWarning) 204 | Elseif (blnModerateSecurity = True And wModerateSecurityOption = True) Then 205 | Wscript.Echo strReturnText 206 | objFile.WriteLine strReturnText 207 | Wscript.Quit(rWarning) 208 | Elseif (blnLowSecurity = True And wLowSecurityOption = True) Then 209 | Wscript.Echo strReturnText 210 | objFile.WriteLine strReturnText 211 | Wscript.Quit(rWarning) 212 | Elseif (blnCriticalUpdate = True And wCriticalUpdateOption = True) Then 213 | Wscript.Echo strReturnText 214 | objFile.WriteLine strReturnText 215 | Wscript.Quit(rWarning) 216 | Elseif (blnImportantUpdate = True And wImportantUpdateOption = True) Then 217 | Wscript.Echo strReturnText 218 | objFile.WriteLine strReturnText 219 | Wscript.Quit(rWarning) 220 | Else 221 | Dim objUpdateSystemInfo, blnRebootRequired 222 | Set objUpdateSystemInfo = CreateObject("Microsoft.Update.SystemInfo") 223 | blnRebootRequired = objUpdateSystemInfo.RebootRequired 224 | 225 | If (blnRebootRequired = True And nRebootStatusOption > 0) Then 226 | Wscript.Echo "A reboot is required" 227 | objFile.WriteLine "A reboot is required" 228 | Wscript.Quit(nRebootStatusOption) 229 | End If 230 | If (Len(strReturnText) > 0) Then 231 | Wscript.Echo strReturnText 232 | objFile.WriteLine strReturnText 233 | Else 234 | Wscript.Echo "OK: No Important or Critical patches missing (" & strScriptVersion & ")" 235 | objFile.WriteLine "OK: No Important or Critical patches missing (" & strScriptVersion & ")" 236 | End If 237 | Wscript.Quit(rOK) 238 | End If 239 | 240 | End Sub 241 | 242 | Sub DisplayHelp 243 | WScript.Echo "Check Available Updates" 244 | Wscript.Echo "----------------------------------------------" 245 | WScript.Echo "Usage: cscript.exe check_updates.vbs [options]" 246 | WScript.Echo VbCrLf 247 | WScript.Echo " -h - Display help" 248 | Wscript.Quit(rUnknown) 249 | End Sub 250 | 251 | 252 | Sub GetOptions() 253 | Dim objArgs, nArgs 254 | 255 | Set objArgs = WScript.Arguments 256 | If (objArgs.Count > 0) Then 257 | For nArgs = 0 To objArgs.Count - 1 258 | SetOptions objArgs(nArgs) 259 | Next 260 | End If 261 | End Sub 262 | 263 | Sub DisplayInvalidArgument() 264 | Wscript.Echo "Invalid arguments, check help with cscript.exe check_updates.vbs -h" 265 | Wscript.Quit(rUnknown) 266 | End Sub 267 | 268 | Sub SetOptions(strOption) 269 | Dim strFlag, strParameter 270 | Dim nArguments 271 | nArguments = Len(strOption) 272 | If (nArguments < 2) Then 273 | bInvalidArgument = True 274 | Else 275 | strFlag = Left(strOption,2) 276 | Select Case strFlag 277 | Case "-h" 278 | bDisplayHelp = True 279 | Case Else 280 | bInvalidArgument = True 281 | End Select 282 | End If 283 | End Sub 284 | -------------------------------------------------------------------------------- /html/client/client_installer.php: -------------------------------------------------------------------------------- 1 | /dev/null 2>&1 17 | if [[ \"$?\" = \"0\" ]]; then 18 | client_path=\"/opt/patch_client/\" 19 | echo \"Detected client install on PatchMD master server, installing in secondary directory: \${client_path}\" 20 | else 21 | client_path=\"/opt/patch_manager/\" 22 | fi 23 | ls \${client_path}*.sh > /dev/null 2>&1 24 | if [[ \"$?\" != \"0\" ]]; then 25 | mkdir -p \${client_path} 26 | curl -k -s ${SERVER_URI}client/check-in.sh > \${client_path}check-in.sh 27 | curl -k -s ${SERVER_URI}client/patch_checker.sh > \${client_path}patch_checker.sh 28 | curl -k -s ${SERVER_URI}client/package_checker.sh > \${client_path}package_checker.sh 29 | curl -k -s ${SERVER_URI}client/run_commands.sh > \${client_path}run_commands.sh 30 | else 31 | echo \"Updating existing install located at: \${client_path}\" 32 | rm -rf /opt/patch_manager/*.sh 33 | curl -k -s ${SERVER_URI}client/check-in.sh > \${client_path}check-in.sh 34 | curl -k -s ${SERVER_URI}client/patch_checker.sh > \${client_path}patch_checker.sh 35 | curl -k -s ${SERVER_URI}client/package_checker.sh > \${client_path}package_checker.sh 36 | curl -k -s ${SERVER_URI}client/run_commands.sh > \${client_path}run_commands.sh 37 | fi 38 | chmod +x \${client_path}*.sh > /dev/null 2>&1 39 | ls \"/etc/cron.d/patch-manager\" > /dev/null 2>&1 40 | if [[ \"$?\" != \"0\" ]]; then 41 | touch /etc/cron.d/patch-manager > /dev/null 2>&1 42 | fi 43 | grep \"\${client_path}check-in.sh\" \"/etc/cron.d/patch-manager\" > /dev/null 2>&1 44 | if [[ \"$?\" != \"0\" ]]; then 45 | cat </etc/cron.d/patch-manager 46 | export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 47 | * * * * * root \${client_path}check-in.sh >/dev/null 2>&1 48 | EOF 49 | else 50 | echo \"Crontab entry already exists in: /etc/cron.d/patch-manager\" 51 | fi 52 | echo \"Client Install completed.\" 53 | "; 54 | echo $script; 55 | -------------------------------------------------------------------------------- /html/client/get_commands.php: -------------------------------------------------------------------------------- 1 | 0){ 38 | $suppression_sql = "SELECT * FROM `supressed` WHERE `server_name` IN (0,'$server_name');"; 39 | $suppression_res = mysql_query($sql); 40 | if (mysql_num_rows($suppression_res) > 0) { 41 | while ($suppression_row = mysql_fetch_assoc($suppression_res)){ 42 | if (isset($suppression_row['package_name'])) { 43 | $suppression_array[] = $suppression_row['package_name']; 44 | } 45 | } 46 | while ($row3 = mysql_fetch_assoc($res3)){ 47 | $package_name = $row3['package_name']; 48 | if (!in_array($package_name, $suppression_array)){ 49 | $package_array[] = $package_name; 50 | } 51 | } 52 | foreach ($package_array as $val){ 53 | mysql_query("UPDATE `patches` SET `to_upgrade` = 0, `upgraded` = 1 WHERE `server_name` = '$server_name' AND `package_name` = '$val' LIMIT 1;"); 54 | } 55 | $package_string = implode(" ", $package_array); 56 | } 57 | } 58 | //CMD GOES HERE 59 | $company = YOUR_COMPANY; 60 | $company_sql = "SELECT * FROM `company` WHERE `display_name`='$company' LIMIT 1;"; 61 | $company_res = mysql_query($company_sql); 62 | $company_row = mysql_fetch_array($company_res); 63 | $key_to_check_array = explode(" ",$company_row['install_key']); 64 | $key_to_check = $key_to_check_array[0]; 65 | $cmd_sql = "SELECT d.upgrade_command as cmd from servers s left join distro d on s.distro_id=d.id where s.server_name='$server_name' LIMIT 1;"; 66 | $cmd_res = mysql_query($cmd_sql); 67 | $cmd_row = mysql_fetch_array($cmd_res); 68 | $cmd = $cmd_row['cmd']; 69 | // If it needs to be rebooted, lets add it on to the end of the rest of the cmd. 70 | if ($to_reboot == 1){ 71 | $reboot_cmd_sent_sql = "UPDATE `servers` SET `reboot_cmd_sent`=0 WHERE `id`=$id LIMIT 1;"; 72 | mysql_query($reboot_cmd_sent_sql); 73 | $add_after = "/sbin/reboot"; 74 | } 75 | else{ 76 | $add_after = ""; 77 | } 78 | if (isset($package_string)) { 79 | echo "key_to_check='$key_to_check' 80 | cmd_to_run='$cmd $package_string;$add_after'"; 81 | } else { 82 | echo "key_to_check='$key_to_check' 83 | cmd_to_run='$add_after'"; 84 | } 85 | } 86 | } 87 | mysql_close($link); 88 | -------------------------------------------------------------------------------- /html/client/index.php: -------------------------------------------------------------------------------- 1 | /dev/null 2>&1 48 | fi 49 | -------------------------------------------------------------------------------- /html/client/patch_checker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # generated installation key and server URI from install 4 | auth_key="__SERVER_AUTHKEY_SET_ME__" 5 | server_uri="__SERVER_URI_SET_ME__" 6 | submit_patch_uri="${server_uri}client/send_patches.php" 7 | # set client_path 8 | if [[ -d /opt/patch_client ]]; then 9 | client_path="/opt/patch_client/" 10 | else 11 | client_path="/opt/patch_manager/" 12 | fi 13 | # if $client_path does not exist 14 | if [[ ! -f "${client_path}.patchrc" ]]; then 15 | echo "Please run ${client_path}check-in.sh as root (sudo) before trying to run this manually" 16 | exit 0 17 | fi 18 | # load the file 19 | . ${client_path}.patchrc 20 | rm -rf /tmp/patch_$client_key > /dev/null 2>&1 21 | if [[ -f /etc/lsb-release && -f /etc/debian_version ]]; then 22 | os=$(lsb_release -s -d|head -1|awk {'print $1'}) 23 | elif [[ -f /etc/debian_version ]]; then 24 | os="$(cat /etc/issue|head -n 1|awk {'print $1'})" 25 | elif [[ -f /etc/redhat-release ]]; then 26 | os=$(cat /etc/redhat-release|head -1|awk {'print $1'}) 27 | if [[ "$os" = "Red" && $(grep -i enterprise /etc/redhat-release) != "" ]]; then 28 | os="RHEL" 29 | elif [[ "$os" = "Red" ]]; then 30 | os="RHEL" 31 | fi 32 | else 33 | os=$(uname -s -r|head -1|awk {'print $1'}) 34 | fi 35 | # remove any special characters 36 | os=$(echo $os|sed -e 's/[^a-zA-Z0-9]//g') 37 | # begin update checks 38 | if [[ "$os" = "CentOS" ]] || [[ "$os" = "Fedora" ]] || [[ "$os" = "RHEL" ]]; then 39 | need_patched="true" 40 | yum -q check-update| while read i 41 | do 42 | i=$(echo $i) #this strips off yum's irritating use of whitespace 43 | if [[ "${i}x" != "x" ]] 44 | then 45 | UVERSION=${i#*\ } 46 | UVERSION=${UVERSION%\ *} 47 | PNAME=${i%%\ *} 48 | PNAME=${PNAME%.*} 49 | #echo $(rpm -q "${PNAME}" --qf '%{NAME}:::%{VERSION}:::')${UVERSION} 50 | patches_to_install=$(echo $(rpm -q "${PNAME}" --qf '%{NAME}:::%{VERSION}:::')${UVERSION}) 51 | echo "$patches_to_install" >> /tmp/patch_$client_key 52 | fi 53 | done 54 | elif test "$os" = Ubuntu -o "$os" = Debian -o "$os" = Devuan -o "$os" = Raspbian; then 55 | need_patched="true" 56 | #apt-get --just-print upgrade 2>&1 | perl -ne 'if (/Inst\s([\w,\-,\d,\.,~,:,\+]+)\s\[([\w,\-,\d,\.,~,:,\+]+)\]\s\(([\w,\-,\d,\.,~,:,\+]+)\)? /i) {print "$1:::$2:::$3\n"}' 57 | patches_to_install=$(apt-get --just-print upgrade 2>&1 | perl -ne 'if (/Inst\s([\w,\-,\d,\.,~,:,\+]+)\s\[([\w,\-,\d,\.,~,:,\+]+)\]\s\(([\w,\-,\d,\.,~,:,\+]+)\)? /i) {print "$1:::$2:::$3\n"}') 58 | echo "$patches_to_install" >> /tmp/patch_$client_key 59 | elif [[ "$os" = "Linux" ]]; then 60 | echo "unspecified $os not supported" 61 | exit 0 62 | fi 63 | if [[ "$need_patched" == "true" ]]; then 64 | patch_list=$(cat /tmp/patch_$client_key) 65 | curl -k -s -H "X-CLIENT-KEY: $client_key" $submit_patch_uri -d "$patch_list" 66 | rm -rf /tmp/patch_$client_key > /dev/null 2>&1 67 | fi 68 | 69 | -------------------------------------------------------------------------------- /html/client/run_commands.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | auth_key="__SERVER_AUTHKEY_SET_ME__" 3 | server_uri="__SERVER_URI_SET_ME__" 4 | get_cmd_uri="${server_uri}client/get_commands.php" 5 | # set client_path 6 | if [[ -d /opt/patch_client ]]; then 7 | client_path="/opt/patch_client/" 8 | else 9 | client_path="/opt/patch_manager/" 10 | fi 11 | # force a run of check-in.sh if .patchrc is missing. 12 | if [[ ! -f "${client_path}.patchrc" ]]; then 13 | echo "Please run ${client_path}check-in.sh as root (sudo) before trying to run this manually" 14 | exit 0 15 | fi 16 | . ${client_path}.patchrc 17 | rm -rf /tmp/cmds_$client_key > /dev/null 2>&1 18 | curl -k -s -H "X-CLIENT-KEY: $client_key" $get_cmd_uri > /tmp/cmds_$client_key 19 | cmds_line_count=$(cat /tmp/cmds_$client_key|wc -l) 20 | if [ "$cmds_line_count" -gt "0" ]; then 21 | . /tmp/cmds_$client_key 22 | key_sum=$(echo $key_to_check|sha256sum) 23 | auth_sum=$(echo $auth_key|sha256sum) 24 | if [ "$key_sum" == "$auth_sum" ]; then 25 | echo $cmd_to_run|bash 26 | fi 27 | fi 28 | rm -rf /tmp/cmds_$client_key 29 | -------------------------------------------------------------------------------- /html/client/send_packages.php: -------------------------------------------------------------------------------- 1 | /dev/null|grep ''|head -1\""); 31 | $url = str_replace("/dev/null|grep '$package_to'|grep 'urgency='\""); 35 | if (stristr($urgency_curl, "emergency")) { 36 | $urgency = "emergency"; 37 | } elseif (stristr($urgency_curl, "high")) { 38 | $urgency = "high"; 39 | } elseif (stristr($urgency_curl, "medium")) { 40 | $urgency = "medium"; 41 | } elseif (stristr($urgency_curl, "low")) { 42 | $urgency = "low"; 43 | } else { 44 | $urgency = "unknown"; 45 | } 46 | if (!in_array($package_name, $suppression_array)) { 47 | $sql = "INSERT INTO patches(server_name,package_name,current,new,urgency,bug_url) VALUES('$server_name','$package_name','$package_from','$package_to','$urgency','$the_url');"; 48 | mysql_query($sql); 49 | } 50 | } 51 | } 52 | mysql_close(); 53 | -------------------------------------------------------------------------------- /html/css/bootstrap-switch.min.css: -------------------------------------------------------------------------------- 1 | /* ======================================================================== 2 | * bootstrap-switch - v3.2.2 3 | * http://www.bootstrap-switch.org 4 | * ======================================================================== 5 | * Copyright 2012-2013 Mattia Larentis 6 | * 7 | * ======================================================================== 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * ======================================================================== 20 | */ 21 | 22 | .bootstrap-switch{display:inline-block;cursor:pointer;border-radius:4px;border:1px solid;border-color:#ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:inline-block!important;height:100%;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-handle-off{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary,.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary{color:#fff;background:#428bca}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info,.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info{color:#fff;background:#5bc0de}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success,.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success{color:#fff;background:#5cb85c}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning,.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning{background:#f0ad4e;color:#fff}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger,.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger{color:#fff;background:#d9534f}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default,.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type=radio],.bootstrap-switch input[type=checkbox]{position:absolute!important;top:0;left:0;opacity:0;filter:alpha(opacity=0);z-index:-1}.bootstrap-switch input[type=radio].form-control,.bootstrap-switch input[type=checkbox].form-control{height:auto}.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label{padding:1px 5px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label{padding:5px 10px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label{padding:6px 16px;font-size:18px;line-height:1.33}.bootstrap-switch.bootstrap-switch-disabled,.bootstrap-switch.bootstrap-switch-readonly,.bootstrap-switch.bootstrap-switch-indeterminate{cursor:default!important}.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label{opacity:.5;filter:alpha(opacity=50);cursor:default!important}.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container{-webkit-transition:margin-left .5s;transition:margin-left .5s}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on{border-bottom-left-radius:0;border-top-left-radius:0;border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off{border-bottom-right-radius:0;border-top-right-radius:0;border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch.bootstrap-switch-focused{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px} 23 | -------------------------------------------------------------------------------- /html/css/bootstrapValidator.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * BootstrapValidator (http://bootstrapvalidator.com) 3 | * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3 4 | * 5 | * @version v0.5.3, built on 2014-11-05 9:14:18 PM 6 | * @author https://twitter.com/nghuuphuoc 7 | * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc 8 | * @license Commercial: http://bootstrapvalidator.com/license/ 9 | * Non-commercial: http://creativecommons.org/licenses/by-nc-nd/3.0/ 10 | */ 11 | 12 | .bv-form .help-block{margin-bottom:0}.bv-form .tooltip-inner{text-align:left}.nav-tabs li.bv-tab-success>a{color:#3c763d}.nav-tabs li.bv-tab-error>a{color:#a94442}.bv-form .bv-icon-no-label{top:0}.bv-form .bv-icon-input-group{top:0;z-index:100} 13 | -------------------------------------------------------------------------------- /html/css/dashboard.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Base structure 3 | */ 4 | 5 | /* Move down content because we have a fixed navbar that is 50px tall */ 6 | body { 7 | padding-top: 50px; 8 | } 9 | 10 | 11 | /* 12 | * Global add-ons 13 | */ 14 | 15 | .sub-header { 16 | padding-bottom: 10px; 17 | border-bottom: 1px solid #eee; 18 | } 19 | 20 | 21 | /* 22 | * Sidebar 23 | */ 24 | 25 | /* Hide for mobile, show later */ 26 | .sidebar { 27 | display: none; 28 | } 29 | @media (min-width: 768px) { 30 | .sidebar { 31 | position: fixed; 32 | top: 51px; 33 | bottom: 0; 34 | left: 0; 35 | z-index: 1000; 36 | display: block; 37 | padding: 20px; 38 | overflow-x: hidden; 39 | overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ 40 | background-color: #f5f5f5; 41 | border-right: 1px solid #eee; 42 | } 43 | } 44 | 45 | /* Sidebar navigation */ 46 | .nav-sidebar { 47 | margin-right: -21px; /* 20px padding + 1px border */ 48 | margin-bottom: 20px; 49 | margin-left: -20px; 50 | } 51 | .nav-sidebar > li > a { 52 | padding-right: 20px; 53 | padding-left: 20px; 54 | } 55 | .nav-sidebar > .active > a { 56 | color: #fff; 57 | background-color: #428bca; 58 | } 59 | 60 | 61 | /* 62 | * Main content 63 | */ 64 | 65 | .main { 66 | padding: 20px; 67 | } 68 | @media (min-width: 768px) { 69 | .main { 70 | padding-right: 40px; 71 | padding-left: 40px; 72 | } 73 | } 74 | .main .page-header { 75 | margin-top: 0; 76 | } 77 | 78 | 79 | /* 80 | * Placeholder dashboard ideas 81 | */ 82 | 83 | .placeholders { 84 | margin-bottom: 30px; 85 | text-align: center; 86 | } 87 | .placeholders h4 { 88 | margin-bottom: 0; 89 | } 90 | .placeholder { 91 | margin-bottom: 20px; 92 | } 93 | .placeholder img { 94 | display: inline-block; 95 | border-radius: 50%; 96 | } 97 | -------------------------------------------------------------------------------- /html/css/index.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | You messed up. Try again 4 |

5 | Oops!

6 |

7 | 404 Not Found

8 |
9 | Sorry, an error has occurred, The page you requested was not found! 10 |
11 |
14 |
15 | 16 | -------------------------------------------------------------------------------- /html/inc/404_footer.inc.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /html/inc/404_header.inc.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Patch Management Dashboard 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /html/inc/404_navbar.inc.php: -------------------------------------------------------------------------------- 1 | 2 |

Patches

3 |

Servers Needing patches

4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Server Name (2 servers)Patch Count (42 total patches available)
 server_121
 server_221
23 |
24 | -------------------------------------------------------------------------------- /html/inc/footer.inc.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /html/inc/header.inc.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Patch Management Dashboard 19 | 20 | 21 | 22 | 23 | 24 | 38 | 39 | 40 | 62 | -------------------------------------------------------------------------------- /html/inc/index.php: -------------------------------------------------------------------------------- 1 | 5 |
6 | × 7 | Error! $error 8 |
9 | "; 10 | unset($_SESSION['error']); 11 | unset($error); 12 | } 13 | else{ 14 | $error_html=""; 15 | } 16 | ?> 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 31 | Patch Management Dashboard 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 61 | 69 |
70 |
71 | 77 |
78 |
79 | 80 |
81 |
82 | 83 | 94 |
95 |
96 |
97 | 98 |
99 | 100 | 102 | 103 | 104 | 105 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /html/inc/logout.inc.php: -------------------------------------------------------------------------------- 1 | $val){ 17 | $plugin2 = $key; 18 | $plugin2_glyph = $val["glyph"]; 19 | $plugin_name = ucwords($plugin2); 20 | $data .= "
21 |
22 |

23 | 24 |   $plugin_name 25 |

26 |
27 |
28 |
29 | "; 30 | foreach ($val['page_and_glyph'] as $val2){ 31 | $tmp_array = explode(",",$val2); 32 | $page_string = $tmp_array[0]; 33 | $page_words = ucwords(str_replace("_"," ",$page_string)); 34 | if (isset($tmp_array[1])){ 35 | $page_glyph = "  "; 36 | } 37 | else{ 38 | $page_glyph = ""; 39 | } 40 | /* 41 | * Badge code: 42 | * 42 43 | * TODO: work the badge code in dynamically with patche count 44 | */ 45 | if ($page_string == "patches"){ 46 | $badge_code = "  $patches_to_apply_count"; 47 | } 48 | else{ 49 | $badge_code = ""; 50 | } 51 | $data .= " 52 | 55 | "; 56 | } 57 | $data .= "
53 | $page_glyph$page_words$badge_code 54 |
58 |
59 |
60 |
"; 61 | } 62 | if (!isset($_SESSION['error_notice'])){ 63 | $error_html = ""; 64 | } 65 | else{ 66 | $error_message = $_SESSION['error_notice']; 67 | $error_html = "
68 | × 69 | Error! $error_message 70 |
"; 71 | unset($_SESSION['error_notice']); 72 | unset($error_message); 73 | } 74 | 75 | if (!isset($_SESSION['good_notice'])){ 76 | $good_html = ""; 77 | } 78 | else{ 79 | $good_message = $_SESSION['good_notice']; 80 | $good_html = "
81 | × 82 | Notice: $good_message 83 |
"; 84 | unset($_SESSION['good_notice']); 85 | unset($good_message); 86 | } 87 | 88 | if (!isset($_SESSION['warning_notice'])){ 89 | $warning_html = ""; 90 | } 91 | else{ 92 | $warning_message = $_SESSION['warning_notice']; 93 | $warning_html = "
94 | × 95 | Warning: $warning_message 96 |
"; 97 | unset($_SESSION['warning_notice']); 98 | unset($warning_message); 99 | } 100 | $all_messages_to_send = "${warning_html}${good_html}${error_html}"; 101 | ?> 102 |
103 | 104 |
105 |
106 |
107 | 108 |
109 |
110 | -------------------------------------------------------------------------------- /html/inc/offline.inc.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | || OFFLINE || Patch Management Dashboard 19 | 20 | 21 | 22 | 23 | 24 | 44 | 45 |
46 |
47 |
48 |
49 |
50 |
51 |

52 | 53 |   Main 54 |

55 |
56 |
57 |
58 | 59 | 60 | 64 | 65 | 66 | 69 | 70 |
61 |   Patches 62 | 42 63 |
67 |   Manage Profile 68 |
71 |
72 |
73 |
74 |
75 |
76 |

77 | 78 |   Admin 79 |

80 |
81 |
82 |
83 | 84 | 85 | 88 | 89 | 90 | 93 | 94 | 95 | 98 | 99 |
86 |   Add User 87 |
91 |   Manage Users 92 |
96 |   Manage Servers 97 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |

Patches

107 |

Servers Needing patches

108 |
109 | The site is offline. Try again later, or contact the admin. 110 |

111 | OFFLINE

112 |

113 | PatchDashboard is currently offline

114 |
115 | I'm sorry, but right now, the site is offline. If you continue to experience issues, please contact the site administrator. 116 |
117 |
118 |
119 |
120 | 121 |
122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /html/inc/p_login.inc.php: -------------------------------------------------------------------------------- 1 | 0){ 12 | while ($row = mysql_fetch_assoc($res)){ 13 | $_SESSION['user_id'] = $row['user_id']; 14 | $_SESSION['is_admin'] = $row['admin']; 15 | $_SESSION['display_name'] = $row['display_name']; 16 | if ($row['active'] == 0){ 17 | $_SESSION['logged_in'] = false; 18 | $_SESSION['error'] = "Your account has been disabled. Please contact your systems administrator."; 19 | } 20 | else{ 21 | $time_sql = "UPDATE `users` SET `last_seen` = NOW() WHERE `user_id` = '$username' LIMIT 1;"; 22 | mysql_query($time_sql); 23 | $_SESSION['logged_in'] = true; 24 | } 25 | } 26 | } 27 | else{ 28 | $_SESSION['error'] = "Invalid username/password combination"; 29 | } 30 | } 31 | else{ 32 | $_SESSION['error'] = "Please be sure to enter both your username AND password"; 33 | } 34 | mysql_close($link); 35 | header("location:".BASE_PATH); -------------------------------------------------------------------------------- /html/inc/supressed_patches.inc.php: -------------------------------------------------------------------------------- 1 | article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f); 8 | if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d").get(0); 35 | _this.$el.append(_this.canvas); 36 | if (typeof G_vmlCanvasManager !== "undefined" && G_vmlCanvasManager !== null) { 37 | G_vmlCanvasManager.initElement(_this.canvas); 38 | } 39 | _this.ctx = _this.canvas.getContext('2d'); 40 | if (window.devicePixelRatio > 1.5) { 41 | $(_this.canvas).css({ 42 | width: _this.options.size, 43 | height: _this.options.size 44 | }); 45 | _this.canvas.width *= 2; 46 | _this.canvas.height *= 2; 47 | _this.ctx.scale(2, 2); 48 | } 49 | _this.ctx.translate(_this.options.size / 2, _this.options.size / 2); 50 | _this.$el.addClass('easyPieChart'); 51 | _this.$el.css({ 52 | width: _this.options.size, 53 | height: _this.options.size, 54 | lineHeight: "" + _this.options.size + "px" 55 | }); 56 | _this.update(percent); 57 | return _this; 58 | }; 59 | this.update = function(percent) { 60 | if (_this.options.animate === false) { 61 | return drawLine(percent); 62 | } else { 63 | return animateLine(_this.percentage, percent); 64 | } 65 | }; 66 | renderScale = function() { 67 | var i, _i, _results; 68 | _this.ctx.fillStyle = _this.options.scaleColor; 69 | _this.ctx.lineWidth = 1; 70 | _results = []; 71 | for (i = _i = 0; _i <= 24; i = ++_i) { 72 | _results.push(addScaleLine(i)); 73 | } 74 | return _results; 75 | }; 76 | addScaleLine = function(i) { 77 | var offset; 78 | offset = i % 6 === 0 ? 0 : _this.options.size * 0.017; 79 | _this.ctx.save(); 80 | _this.ctx.rotate(i * Math.PI / 12); 81 | _this.ctx.fillRect(_this.options.size / 2 - offset, 0, -_this.options.size * 0.05 + offset, 1); 82 | return _this.ctx.restore(); 83 | }; 84 | renderTrack = function() { 85 | var offset; 86 | offset = _this.options.size / 2 - _this.options.lineWidth / 2; 87 | if (_this.options.scaleColor !== false) { 88 | offset -= _this.options.size * 0.08; 89 | } 90 | _this.ctx.beginPath(); 91 | _this.ctx.arc(0, 0, offset, 0, Math.PI * 2, true); 92 | _this.ctx.closePath(); 93 | _this.ctx.strokeStyle = _this.options.trackColor; 94 | _this.ctx.lineWidth = _this.options.lineWidth; 95 | return _this.ctx.stroke(); 96 | }; 97 | renderBackground = function() { 98 | if (_this.options.scaleColor !== false) { 99 | renderScale(); 100 | } 101 | if (_this.options.trackColor !== false) { 102 | return renderTrack(); 103 | } 104 | }; 105 | drawLine = function(percent) { 106 | var offset; 107 | renderBackground(); 108 | _this.ctx.strokeStyle = $.isFunction(_this.options.barColor) ? _this.options.barColor(percent) : _this.options.barColor; 109 | _this.ctx.lineCap = _this.options.lineCap; 110 | _this.ctx.lineWidth = _this.options.lineWidth; 111 | offset = _this.options.size / 2 - _this.options.lineWidth / 2; 112 | if (_this.options.scaleColor !== false) { 113 | offset -= _this.options.size * 0.08; 114 | } 115 | _this.ctx.save(); 116 | _this.ctx.rotate(-Math.PI / 2); 117 | _this.ctx.beginPath(); 118 | _this.ctx.arc(0, 0, offset, 0, Math.PI * 2 * percent / 100, false); 119 | _this.ctx.stroke(); 120 | return _this.ctx.restore(); 121 | }; 122 | animateLine = function(from, to) { 123 | var currentStep, fps, steps; 124 | fps = 30; 125 | steps = fps * _this.options.animate / 1000; 126 | currentStep = 0; 127 | _this.options.onStart.call(_this); 128 | _this.percentage = to; 129 | if (_this.animation) { 130 | clearInterval(_this.animation); 131 | _this.animation = false; 132 | } 133 | return _this.animation = setInterval(function() { 134 | _this.ctx.clearRect(-_this.options.size / 2, -_this.options.size / 2, _this.options.size, _this.options.size); 135 | renderBackground.call(_this); 136 | drawLine.call(_this, [easeInOutQuad(currentStep, from, to - from, steps)]); 137 | currentStep++; 138 | if ((currentStep / steps) > 1) { 139 | clearInterval(_this.animation); 140 | _this.animation = false; 141 | return _this.options.onStop.call(_this); 142 | } 143 | }, 1000 / fps); 144 | }; 145 | easeInOutQuad = function(t, b, c, d) { 146 | var easeIn, easing; 147 | easeIn = function(t) { 148 | return Math.pow(t, 2); 149 | }; 150 | easing = function(t) { 151 | if (t < 1) { 152 | return easeIn(t); 153 | } else { 154 | return 2 - easeIn((t / 2) * -2 + 2); 155 | } 156 | }; 157 | t /= d / 2; 158 | return c / 2 * easing(t) + b; 159 | }; 160 | return this.init(); 161 | }; 162 | $.easyPieChart.defaultOptions = { 163 | barColor: '#ef1e25', 164 | trackColor: '#f2f2f2', 165 | scaleColor: '#dfe0e0', 166 | lineCap: 'round', 167 | size: 190, 168 | lineWidth: 3, 169 | animate: false, 170 | onStart: $.noop, 171 | onStop: $.noop 172 | }; 173 | $.fn.easyPieChart = function(options) { 174 | return $.each(this, function(i, el) { 175 | var $el; 176 | $el = $(el); 177 | if (!$el.data('easyPieChart')) { 178 | return $el.data('easyPieChart', new $.easyPieChart(el, options)); 179 | } 180 | }); 181 | }; 182 | return void 0; 183 | })(jQuery); 184 | 185 | }).call(this); 186 | 187 | -------------------------------------------------------------------------------- /html/js/jquery.metadata.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Metadata - jQuery plugin for parsing metadata from elements 3 | * 4 | * Copyright (c) 2006 John Resig, Yehuda Katz, J�örn Zaefferer, Paul McLanahan 5 | * 6 | * Dual licensed under the MIT and GPL licenses: 7 | * http://www.opensource.org/licenses/mit-license.php 8 | * http://www.gnu.org/licenses/gpl.html 9 | * 10 | * Revision: $Id$ 11 | * 12 | */ 13 | 14 | /** 15 | * Sets the type of metadata to use. Metadata is encoded in JSON, and each property 16 | * in the JSON will become a property of the element itself. 17 | * 18 | * There are three supported types of metadata storage: 19 | * 20 | * attr: Inside an attribute. The name parameter indicates *which* attribute. 21 | * 22 | * class: Inside the class attribute, wrapped in curly braces: { } 23 | * 24 | * elem: Inside a child element (e.g. a script tag). The 25 | * name parameter indicates *which* element. 26 | * 27 | * The metadata for an element is loaded the first time the element is accessed via jQuery. 28 | * 29 | * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements 30 | * matched by expr, then redefine the metadata type and run another $(expr) for other elements. 31 | * 32 | * @name $.metadata.setType 33 | * 34 | * @example

This is a p

35 | * @before $.metadata.setType("class") 36 | * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" 37 | * @desc Reads metadata from the class attribute 38 | * 39 | * @example

This is a p

40 | * @before $.metadata.setType("attr", "data") 41 | * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" 42 | * @desc Reads metadata from a "data" attribute 43 | * 44 | * @example

This is a p

45 | * @before $.metadata.setType("elem", "script") 46 | * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" 47 | * @desc Reads metadata from a nested script element 48 | * 49 | * @param String type The encoding type 50 | * @param String name The name of the attribute to be used to get metadata (optional) 51 | * @cat Plugins/Metadata 52 | * @descr Sets the type of encoding to be used when loading metadata for the first time 53 | * @type undefined 54 | * @see metadata() 55 | */ 56 | 57 | (function($) { 58 | 59 | $.extend({ 60 | metadata : { 61 | defaults : { 62 | type: 'class', 63 | name: 'metadata', 64 | cre: /({.*})/, 65 | single: 'metadata' 66 | }, 67 | setType: function( type, name ){ 68 | this.defaults.type = type; 69 | this.defaults.name = name; 70 | }, 71 | get: function( elem, opts ){ 72 | var settings = $.extend({},this.defaults,opts); 73 | // check for empty string in single property 74 | if ( !settings.single.length ) settings.single = 'metadata'; 75 | 76 | var data = $.data(elem, settings.single); 77 | // returned cached data if it already exists 78 | if ( data ) return data; 79 | 80 | data = "{}"; 81 | 82 | if ( settings.type == "class" ) { 83 | var m = settings.cre.exec( elem.className ); 84 | if ( m ) 85 | data = m[1]; 86 | } else if ( settings.type == "elem" ) { 87 | if( !elem.getElementsByTagName ) 88 | return undefined; 89 | var e = elem.getElementsByTagName(settings.name); 90 | if ( e.length ) 91 | data = $.trim(e[0].innerHTML); 92 | } else if ( elem.getAttribute != undefined ) { 93 | var attr = elem.getAttribute( settings.name ); 94 | if ( attr ) 95 | data = attr; 96 | } 97 | 98 | if ( data.indexOf( '{' ) <0 ) 99 | data = "{" + data + "}"; 100 | 101 | data = eval("(" + data + ")"); 102 | 103 | $.data( elem, settings.single, data ); 104 | return data; 105 | } 106 | } 107 | }); 108 | 109 | /** 110 | * Returns the metadata object for the first member of the jQuery object. 111 | * 112 | * @name metadata 113 | * @descr Returns element's metadata object 114 | * @param Object opts An object contianing settings to override the defaults 115 | * @type jQuery 116 | * @cat Plugins/Metadata 117 | */ 118 | $.fn.metadata = function( opts ){ 119 | return $.metadata.get( this[0], opts ); 120 | }; 121 | 122 | })(jQuery); -------------------------------------------------------------------------------- /html/js/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl 2 | * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT 3 | * */ 4 | 5 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;bdebug("Connecting to " . $conf->wsus_dbalias . " " . $conf->wsus_db, 1); 59 | $msconnect = mssql_connect($conf->wsus_dbalias, $wsus_dbuser, $wsus_dbpass); 60 | if (!$msconnect) { 61 | $logger->logit("Cannot connect to WSUS server " . $conf->wsus_dbalias . ":" . mssql_get_last_message(), LOG_ERR); 62 | return false; 63 | } 64 | $db = mssql_select_db($conf->wsus_db, $msconnect); 65 | if (!$db) { 66 | $logger->logit("Couldn't open database " . $conf->wsus_db . " " . mssql_get_last_message(), LOG_ERR); 67 | return false; 68 | } 69 | } 70 | 71 | /** 72 | * Returns the hostname part of an fqdn thus everything before the first dot 73 | */ 74 | function get_hostname($fqdn) { 75 | global $logger; 76 | 77 | $dot_pos = strpos($fqdn, '.'); // find position of first dot 78 | $hostname; 79 | if ($dot_pos) { 80 | $hostname = substr($fqdn, 0, $dot_pos); // take everything before the first dot 81 | } else { 82 | $hostname = $fqdn; 83 | } 84 | 85 | $logger->debug("Converting $fqdn to $hostname", 2); 86 | 87 | return strtolower($hostname); 88 | } 89 | 90 | /** 91 | * Look up a wsus hostname in the vmps table and return the vmps id if and only if there's exactly one entry 92 | */ 93 | function get_vmps_id($hostname) { 94 | global $logger; 95 | 96 | $logger->debug("Looking for vmps system id for hostname $hostname", 1); 97 | 98 | $query = "select id from systems where substring_index(last_hostname, '.', 1) = '$hostname';"; 99 | $logger->debug("Executing: $query", 3); 100 | $result = mysql_query($query); 101 | if (!$result) { 102 | $logger->logit("Could not obtain vmps id for $hostname, " . mysql_error(), LOG_WARNING); 103 | return false; 104 | } 105 | $num_rows = mysql_num_rows($result); //TODO: exception handling 106 | if ($num_rows == 0) { 107 | //$logger->logit("No vmps id for system $hostname found", LOG_WARNING); 108 | $logger->logit("No vmps id for system $hostname found"); // don't flag as warning until 100% right, its flooding logcheck 109 | return false; 110 | } elseif ($num_rows == 1) { 111 | $row = mysql_fetch_row($result); //TODO: exception handling 112 | $logger->debug("hostname $hostname has vmps id $row[0]", 1); 113 | return $row[0]; 114 | } else { 115 | //$logger->logit("$hostname is not unique in vmps", LOG_WARNING); 116 | $logger->logit("$hostname is not unique in vmps"); // don't flag as warning until 100% right, its flooding logcheck 117 | return false; 118 | } 119 | } 120 | 121 | /** 122 | * Empty all wsus tables. 123 | */ 124 | function empty_tables() { 125 | global $logger; 126 | 127 | $logger->debug("Emptying wsus tables", 1); 128 | # As of MySQL 5.1.6, truncate requires the DROP privilege 129 | #if( !mysql_query('truncate table wsus_systems;') ) { 130 | if (!mysql_query('delete from wsus_systems;')) { 131 | $logger->logit("Could not empty wsus_systems, " . mysql_error(), LOG_ERR); 132 | return false; 133 | } 134 | #if( !mysql_query('truncate table wsus_neededUpdates;') ) { 135 | if (!mysql_query('delete from wsus_neededUpdates;')) { 136 | $logger->logit("Could not empty wsus_neededUpdates, " . mysql_error(), LOG_ERR); 137 | return false; 138 | } 139 | #if( !mysql_query('truncate table wsus_systemToUpdates;') ) { 140 | if (!mysql_query('delete from wsus_systemToUpdates;')) { 141 | $logger->logit("Could not empty wsus_systemToUpdate, " . mysql_error(), LOG_ERR); 142 | return false; 143 | } 144 | 145 | return true; 146 | } 147 | 148 | /** 149 | * Get global list of needed updates from wsus db and store in openac db 150 | */ 151 | function get_global_update_list() { 152 | global $logger, $timestamp; 153 | 154 | // A summarizationstate of 4 references updates that a properly installed. These are the only ones we are not interested in. Languageid 1033 is english, thus we only fetch the english descriptions for the udpates 155 | $query = "select distinct u.localupdateid, lp.title, lp.description, p.msrcseverity, p.creationdate, p.receivedfromcreatorservice from susdb.dbo.tbupdatestatuspercomputer us left join susdb.dbo.tbupdate u on us.localupdateid = u.localupdateid left join dbo.tbrevision r on u.localupdateid = r.localupdateid left join dbo.tbproperty p on r.revisionid = p.revisionid left join dbo.tblocalizedpropertyforrevision lpr on r.revisionid = lpr.revisionid left join dbo.tblocalizedproperty lp on lpr.localizedpropertyid = lp.localizedpropertyid where (us.summarizationstate <> 4) and (us.summarizationstate <> 1) and lpr.languageid = 1033 and r.revisionid = (select max(r.revisionid) from tbrevision r where r.localupdateid = u.localupdateid)"; 156 | 157 | $logger->debug("Executing: " . $query, 3); 158 | $result = mssql_query($query); 159 | if (!$result) { 160 | $logger->logit("Could not fetch global update list, " . mssql_get_last_message(), LOG_ERR); 161 | return false; 162 | } 163 | 164 | ; 165 | while ($row = mssql_fetch_assoc($result)) { 166 | $query = sprintf("insert into wsus_neededUpdates(localupdateid, title, description, msrcseverity, creationdate, receiveddate, lastsync) values('%s', '%s', '%s', '%s', '%s', '%s', '%s');", validate($row['localupdateid']), validate($row['title']), validate($row['description']), validate($row['msrcseverity']), convert_date($row['creationdate']), convert_date($row['receivedfromcreatorservice']), $timestamp); 167 | $logger->debug("Executing: $query", 3); 168 | 169 | if (!mysql_query($query)) { 170 | $logger->logit("Could insert update list into vmps db, " . mysql_error(), LOG_ERR); 171 | return false; 172 | } 173 | } 174 | 175 | return true; 176 | } 177 | 178 | /** 179 | * Obtain list of system which are registerd in the wsus server, fetch their status for each necessary update and write everything into the vmps db 180 | */ 181 | function get_systems() { 182 | global $logger, $timestamp; 183 | 184 | $query = "select t.targetid, t.fulldomainname, t.ipaddress, t.lastreportedstatustime, s.notinstalled, s.downloaded, s.installedpendingreboot, s.failed, d.computermake, d.computermodel, o.oslongname from dbo.tbcomputertarget t left join dbo.tbcomputersummaryformicrosoftupdates s on t.targetid = s.targetid left join dbo.tbcomputertargetdetail d on t.targetid = d.targetid left join dbo.tbosmap o on (d.osminorversion = o.osminorversion and d.osmajorversion = o.osmajorversion and d.osservicepackmajornumber = o.osservicepackmajornumber) where (o.processorarchitecture is null or o.processorarchitecture = 1)"; 185 | 186 | $logger->debug("Executing: $query", 3); 187 | $result = mssql_query($query); 188 | if (!$result) { 189 | $logger->logit("Failed to obtain systems from wsus, " . mssql_get_last_message(), LOG_ERR); 190 | return false; 191 | } 192 | 193 | while ($sys_row = mssql_fetch_assoc($result)) { 194 | $hostname = get_hostname($sys_row['fulldomainname']); 195 | $id = get_vmps_id($hostname); 196 | if (!$id) { 197 | continue; 198 | } 199 | 200 | 201 | $query = sprintf("select us.localupdateid from dbo.tbupdatestatuspercomputer us where (us.summarizationstate <> 4) and (us.summarizationstate <> 1) and us.targetid = '%s'", $sys_row['targetid']); 202 | $logger->debug("Executing: $query", 3); 203 | $result_update = mssql_query($query); 204 | if (!$result) { // whenever there occurs an error we skip the current system and continue with the next one 205 | $logger->logit("Could not fetch update details for " . $sys_row['fulldomainname'] . ", skipping this system", LOG_WARNING); 206 | continue; 207 | } 208 | // insert system into wsus_systems 209 | $query = sprintf("insert into wsus_systems values('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');", $id, $hostname, validate($sys_row['ipaddress']), convert_date($sys_row['lastreportedstatustime']), validate($sys_row['oslongname']), validate($sys_row['computermake']), validate($sys_row['computermodel']), validate($sys_row['notinstalled']), validate($sys_row['downloaded']), validate($sys_row['installedpendingreboot']), validate($sys_row['failed']), $timestamp); 210 | 211 | $logger->debug("Executing: $query", 3); 212 | if (!mysql_query($query)) { 213 | $logger->logit("Could not insert system $hostname, " . mysql_error(), LOG_WARNING); 214 | continue; 215 | } 216 | 217 | // insert mapping to needed updates 218 | while ($update_row = mssql_fetch_assoc($result_update)) { 219 | $query = sprintf("insert into wsus_systemToUpdates(sid, localupdateid, lastsync) values('%s', '%s', '%s');", $id, validate($update_row['localupdateid']), $timestamp); 220 | $logger->debug("Executing $query", 3); 221 | if (!mysql_query($query)) { 222 | $logger->logit("Could not insert update relation $id, " . $update_row['localupdateid'], LOG_WARNING); 223 | } 224 | } 225 | } 226 | 227 | return true; 228 | } 229 | 230 | /** 231 | * 232 | */ 233 | function cleanup() { 234 | global $logger; 235 | 236 | // TODO: exception handling 237 | 238 | mssql_close(); 239 | mysql_close(); 240 | 241 | $logger->logit("Done syncing WSUS"); 242 | 243 | exit; 244 | } 245 | ?> 246 | -------------------------------------------------------------------------------- /html/lib/3rdparty/index.php: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /html/plugins/admin/activate_user.inc.php: -------------------------------------------------------------------------------- 1 | 32 | 33 | -------------------------------------------------------------------------------- /html/plugins/admin/add_server.inc.php: -------------------------------------------------------------------------------- 1 |

Because you're using HTTPS, we advise using the pull method.

"; 10 | $curl_cmd = "curl -s -k"; 11 | } 12 | else{ 13 | $advice = "

Because you're not using HTTPS, we HIGHLY advise against the pull method.

"; 14 | $curl_cmd = "curl"; 15 | } 16 | include '../lib/db_config.php'; 17 | $SERVER_URI = $protocol.$_SERVER['HTTP_HOST'].BASE_PATH; 18 | ?> 19 |
20 |
21 |

Adding a server

22 |

Howto:

23 |
24 |

To add a server, you have 2 options -- The Push method, and The Pull method.

25 |

The Push method is a little more in-depth, but more secure if you aren't running this site on HTTPS
26 | Simply put, you would merely do this from the PatchDashboard server:

27 |
/opt/patch_manager/add_server.sh
28 |

It will ask you some questions, then instruct you on how to complete the setup on each node (controlled server).

29 | 30 |

The easier method -- the Pull Method, if you are running this via HTTPS, or you implicitly trust all traffic on your network (from each guest machine/node):

31 |
sudo -i
32 |   | bash
33 |


We do not ever plan on allowing the addition of servers via the web interface. We belive that keeping the trust (Pull method) or managing the shared keys (Push method) should only be done via the terminal.

34 |
35 |
36 | Take Me Home 37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /html/plugins/admin/add_suppression.inc.php: -------------------------------------------------------------------------------- 1 | 9 |
10 | 11 | 34 |
-------------------------------------------------------------------------------- /html/plugins/admin/deactivate_server.inc.php: -------------------------------------------------------------------------------- 1 | #!*& :-("; 18 | header('location:'.BASE_PATH.'manage_servers'); 19 | } 20 | else{ 21 | $_SESSION['error_notice'] = "A required field was not filled in"; 22 | header('location:'.BASE_PATH."manage_servers"); 23 | } 24 | } 25 | else{ 26 | $_SESSION['warning_notice'] = "You didn't pick a server to distrust"; 27 | header('location:'.BASE_PATH."manage_servers"); 28 | } 29 | } 30 | else{ 31 | $_SESSION['error_notice'] = "You do not have permission to distrust servers. This even thas been logged, and the admin has been notified."; 32 | header('location:'.BASE_PATH); 33 | exit(); 34 | } 35 | ?> 36 | 37 | -------------------------------------------------------------------------------- /html/plugins/admin/deactivate_user.inc.php: -------------------------------------------------------------------------------- 1 | 36 | 37 | -------------------------------------------------------------------------------- /html/plugins/admin/delete_server.inc.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /html/plugins/admin/delete_suppression.inc.php: -------------------------------------------------------------------------------- 1 | 30 | 31 | -------------------------------------------------------------------------------- /html/plugins/admin/edit_server.inc.php: -------------------------------------------------------------------------------- 1 | 12 |
13 | 14 | 17 |
18 | "; 25 | 26 | $sql_edit_server = "SELECT * FROM `servers` WHERE id=$id limit 1;"; 27 | $res_edit_server = mysql_query($sql_edit_server); 28 | $row = mysql_fetch_array($res_edit_server); 29 | $id = $row['id']; 30 | $server_name = $row['server_name']; 31 | $server_alias = $row['server_alias']; 32 | $server_group = $row['server_group']; 33 | $distro_id_main = $row['distro_id']; 34 | $server_ip = $row['server_ip']; 35 | $last_checkin = $row['last_checked']; 36 | $seen = $row['last_seen']; 37 | $trusted = $row['trusted']; 38 | $distro_version_main = $row['distro_version']; 39 | while ($distro_map_row = mysql_fetch_assoc($distro_map_res)) { 40 | $distro_id = $distro_map_row['distro_id']; 41 | $distro_ver_id = $distro_map_row['version_id']; 42 | $distro_ver_name = str_replace("_", " ", $distro_map_row['distro_name'] . " " . $distro_map_row['version_num']); 43 | if ("${distro_id}-${distro_ver_id}" == "${distro_id_main}-${distro_version_main}") { 44 | $select_html .= "\t\t\t\t\t\n"; 45 | } else { 46 | $select_html .= "\t\t\t\t\t\n"; 47 | } 48 | $distro_array[$distro_map_row['distro_id']][$distro_map_row['version_id']] = $distro_ver_name; 49 | } 50 | if ($trusted == 1) { 51 | $trusted_checked = "checked"; 52 | } else { 53 | $trusted_checked = ""; 54 | } 55 | $select_html .= "\t\t\t\t"; 56 | $distro_name = $distro_array[$distro_id_main][$distro_version_main]; 57 | $client_key = $row['client_key']; 58 | if ($seen == "0000-00-00 00:00:00") { 59 | $last_seen = "Never"; 60 | } else { 61 | $last_seen = $seen; 62 | } 63 | if ($last_checkin == "2001-01-01 00:00:00"){ 64 | $last_check = "Never"; 65 | } 66 | else{ 67 | $last_check = $last_checkin; 68 | } 69 | ?> 70 |
71 | 72 | 86 |
87 | 12 |
13 | 14 | 17 |
18 |

" . mysql_error()); 25 | 26 | $row_edit_user = mysql_fetch_array($res_edit_user); 27 | $username = $row_edit_user['user_id']; 28 | $email_address = $row_edit_user['email']; 29 | $admin = $row_edit_user['admin']; 30 | $display_name = $row_edit_user['display_name']; 31 | $seen = $row_edit_user['last_seen']; 32 | if ($seen == "0000-00-00 00:00:00") { 33 | $last_seen = "Never"; 34 | } else { 35 | $last_seen = $seen; 36 | } 37 | $alerts = $row_edit_user['receive_alerts']; 38 | 39 | if ($admin == 1) { 40 | $admin_checked = "checked"; 41 | } else { 42 | $admin_checked = ""; 43 | } 44 | if ($alerts == 1) { 45 | $alerts_checked = "checked"; 46 | } else { 47 | $alerts_checked = ""; 48 | } 49 | ?> 50 |
51 | 52 | 66 |
67 | Deactivate/Distrust"; 43 | } 44 | else{ 45 | $active_action = "Reactivate/Trust"; 46 | } 47 | $table .=" 48 | $server_alias 49 | $server_group 50 | $distro_name 51 | $server_ip 52 | $trust 53 | $last_seen 54 | Edit | $active_action | Delete 55 | 56 | "; 57 | } 58 | ?> 59 |

All Servers

60 |
61 |
62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
Server Name (Alias)Server GroupDistroServer IPTrusted?Last Check-inActions
78 |
79 |
80 | -------------------------------------------------------------------------------- /html/plugins/admin/manage_users.inc.php: -------------------------------------------------------------------------------- 1 | Deactivate"; 36 | } 37 | else{ 38 | $active_action = "Reactivate"; 39 | } 40 | $table .=" 41 | $username 42 | $email 43 | $group 44 | $last_seen 45 | $alerts 46 | Edit | $active_action | Delete 47 | 48 | "; 49 | } 50 | ?> 51 |

List Users

52 |
53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
UsernameE-mailGroupLast LoginAlerts?Actions
69 |
70 |
-------------------------------------------------------------------------------- /html/plugins/admin/p_add_user.inc.php: -------------------------------------------------------------------------------- 1 | 50 | 51 | -------------------------------------------------------------------------------- /html/plugins/admin/p_edit_server.inc.php: -------------------------------------------------------------------------------- 1 | 68 | -------------------------------------------------------------------------------- /html/plugins/admin/p_edit_user.inc.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /html/plugins/index.php: -------------------------------------------------------------------------------- 1 | 0) { 31 | $suppression_list = implode(", ", $suppression_array); 32 | $sql3 .= " AND `package_name` NOT IN ($suppression_list);"; 33 | } 34 | mysql_query($sql3); 35 | $_SESSION['good_notice'] = "All non-suppressed packages set to upgrade on $server_name. $message_injection Bionic machine closer than I thought."; 36 | header('location:' . BASE_PATH . "patches/server/$server_name"); 37 | exit(); 38 | } 39 | mysql_close($link); 40 | } else { 41 | session_unset(); 42 | header('location:' . BASE_PATH); 43 | exit(); 44 | } 45 | -------------------------------------------------------------------------------- /html/plugins/main/p_profile.inc.php: -------------------------------------------------------------------------------- 1 | 0) { 30 | $replacement_parts = implode(", ", $sql_array); 31 | $sql = "UPDATE `users` SET $replacement_parts WHERE `user_id`='$username' AND id=$id LIMIT 1;"; 32 | $link = mysql_connect(DB_HOST, DB_USER, DB_PASS); 33 | mysql_select_db(DB_NAME, $link); 34 | mysql_query($sql); 35 | mysql_close($link); 36 | $_SESSION['good_notice'] = "Profile modified! All your base are belong to us!"; 37 | sleep(1); 38 | header('location:' . BASE_PATH . "profile"); 39 | } else { 40 | $_SESSION['error_notice'] = "Nothing modified because no information was posted!"; 41 | sleep(1); 42 | header('location:' . BASE_PATH . "profile"); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /html/plugins/main/packages.inc.php: -------------------------------------------------------------------------------- 1 | 24 | $package_name 25 | $package_version 26 | 27 | "; 28 | } 29 | ?> 30 |

Full Package List

31 |

32 |
33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
Package NamePackage Version
45 |
46 |
47 | -------------------------------------------------------------------------------- /html/plugins/main/patch_list.inc.php: -------------------------------------------------------------------------------- 1 | (SUPRESSED)"; 35 | } 36 | else{ 37 | $apt_cmd .= " $package_name"; 38 | $package_count++; 39 | } 40 | $current = $row1['current']; 41 | $new = $row1['new']; 42 | $urgency = $row1['urgency']; 43 | $bug_url = $row1['bug_url']; 44 | if ($bug_url != ''){ 45 | if (stristr($bug_url,'debian')){ 46 | $url_array = explode("/",$bug_url); 47 | $cve = end($url_array); 48 | $url = "Debian $cve"; 49 | } 50 | else{ 51 | $url_array = explode("/",$bug_url); 52 | $bug = end($url_array); 53 | $url = "Launchpad Bug #$bug"; 54 | } 55 | } else { $url = " "; } 56 | if (in_array($urgency,array('high','emergency'))){ 57 | $urgency = "$urgency"; 58 | } 59 | elseif ($urgency == "medium"){ 60 | $urgency = "medium"; 61 | } 62 | elseif ($urgency == "low") { 63 | $urgency = "$urgency"; 64 | } 65 | else{ 66 | $urgency = "$urgency"; 67 | } 68 | $table .= " 69 | $package_name 70 | $current 71 | $new 72 | $urgency " . (isset($url) ? $url : '') . " 73 | 74 | "; 75 | } 76 | if ($package_count == 0){ 77 | $apt_cmd = ""; 78 | } 79 | else{ 80 | $apt_cmd = "$apt_cmd"; 81 | } 82 | ?> 83 |

List Packages to Install

84 |

(List all installed packages)


(Install all patches not suppressed | Install all patches not suppressed and reboot)

85 |
86 |
87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
Package NameCurrent VersionNew VersionUrgency LevelBug Report Name/Page
101 | 102 |
103 |
104 | -------------------------------------------------------------------------------- /html/plugins/main/patches.inc.php: -------------------------------------------------------------------------------- 1 | 41 |  $server_alias 42 | $count 43 | 44 | "; 45 | } 46 | mysql_close($link); 47 | $percent_needing_upgrade = round((($nsupressed_total / $server_count)*100)); 48 | $percent_good_to_go = 100 - $percent_needing_upgrade; 49 | if ($percent_good_to_go < 0){ 50 | $percent_good_to_go = 0; 51 | } 52 | ?> 53 |
54 |

Patch List

55 |
56 |
%
57 |
Percent of servers not needing upgrades/patches
58 |
59 | 60 |
61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
Server Name ( servers)Patch Count ( total patches available)
72 |
73 |
74 | -------------------------------------------------------------------------------- /html/plugins/main/preferences.inc.php: -------------------------------------------------------------------------------- 1 |

".mysql_error()); 14 | $row_edit_user = mysql_fetch_array($res_edit_user); 15 | $email_address = $row_edit_user['email']; 16 | $admin = $row_edit_user['admin']; 17 | $display_name = $row_edit_user['display_name']; 18 | $seen = $row_edit_user['last_seen']; 19 | if ($seen == "0000-00-00 00:00:00"){ 20 | $last_seen = "Never"; 21 | } 22 | else{ 23 | $last_seen = $seen; 24 | } 25 | $alerts = $row_edit_user['receive_alerts']; 26 | 27 | if ($admin == 1){ 28 | $admin_checked = "checked"; 29 | } 30 | else{ 31 | $admin_checked = ""; 32 | } 33 | if ($alerts == 1){ 34 | $alerts_checked = "checked"; 35 | } 36 | else{ 37 | $alerts_checked = ""; 38 | } 39 | ?> 40 |
41 | 42 | 54 |
-------------------------------------------------------------------------------- /html/plugins/main/search.inc.php: -------------------------------------------------------------------------------- 1 | 27 | $server_name 28 | $package_name 29 | $package_version 30 | 31 | "; 32 | } 33 | ?> 34 |

Search

35 |

Results for search "" ( found)

36 |
37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
Server NamePackage NamePackage Version
50 |
51 |
52 | -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- 1 | include.path=${php.global.include.path} 2 | php.version=PHP_53 3 | source.encoding=UTF-8 4 | src.dir=. 5 | tags.asp=false 6 | tags.short=false 7 | web.root=. 8 | -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.php.project 4 | 5 | 6 | patchdashboard2 7 | 8 | 9 | PatchDashboard 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /scripts/add_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # load db config 4 | . /opt/patch_manager/db.conf 5 | 6 | # get osversion from host 7 | function OSVersion() 8 | { 9 | if [[ -f /etc/lsb-release ]]; then 10 | export osversion=$(lsb_release -s -d) 11 | elif [[ -f /etc/debian_version ]]; then 12 | export osversion="Debian $(cat /etc/debian_version)" 13 | elif test -f /etc/devuan_version; then 14 | export osversion="Devuan $(cat /etc/devuan_version)" 15 | elif [[ -f /etc/redhat-release ]]; then 16 | export osversion=`cat /etc/redhat-release` 17 | else 18 | export osversion="$(uname -s) $(uname -r)" 19 | fi 20 | } 21 | 22 | function SingleHost() 23 | { 24 | distro_names=`mysql -h $DB_HOST -u $DB_USER -p"$DB_PASS" -Nse 'SELECT distro_name from distro order by id ASC;' $DB_NAME` 25 | for os in $distro_names; do 26 | distro_num=`mysql -h $DB_HOST -u $DB_USER -p"$DB_PASS" -Nse "SELECT id from distro where distro_name='$os';" $DB_NAME` 27 | echo -e "[\e[32m$distro_num\e[0m] $os" 28 | done 29 | echo 30 | unset dist_num_selected 31 | read -p "Please enter the number corresponding to your distro: " dist_num_selected 32 | while [[ "$dist_num_selected" = "" ]]; do 33 | read -p "Please enter the number corresponding to your distro: " dist_num_selected 34 | done 35 | 36 | distro_ver=`mysql -h $DB_HOST -u $DB_USER -p"$DB_PASS" -Nse "SELECT id from distro_version where distro_id='$dist_num_selected' order by id ASC;" $DB_NAME` 37 | for osv in $distro_ver; do 38 | ver_name=`mysql -h $DB_HOST -u $DB_USER -p"$DB_PASS" -Nse "SELECT version_num from distro_version where id='$osv';" $DB_NAME` 39 | echo -e "[\e[32m$osv\e[0m] $ver_name" 40 | done 41 | echo 42 | unset dist_ver_selected 43 | read -p "Please select which version of $os you're using on this node: " dist_ver_selected 44 | while [[ "$dist_ver_selected" = "" ]]; do 45 | read -p "Please select which version of $distro_name you're using on this node: " dist_ver_selected 46 | done 47 | 48 | mysql -h $DB_HOST -u $DB_USER -p"$DB_PASS" -D $DB_NAME -e "INSERT INTO servers(server_name,distro_id,server_ip,distro_version) VALUES('$sHost',$dist_num_selected,'$ipAddr',$dist_ver_selected);" 49 | echo -e "\n\e[32mNOTICE\e[0m: Server successfully added! 50 | Next: 51 | If you have access via SSH to the root user on the remote system, run this: 52 | ssh-copy-id root@$ipAddr 53 | If you don't have access via SSH to the root user on the remote system, do this: 54 | echo /root/.ssh/id_rsa.pub 55 | copy the output of the above command 56 | 57 | ON THE REMOTE SYSTEM: 58 | add what you copied from the previous step to 59 | /root/.ssh/authorized_keys 60 | If the file does not exist, run this: 61 | mkdir /root/.ssh; touch /root/.ssh/authorized_keys; chmod 700 /root/.ssh; chmod 700 /root/.ssh/authorized_keys 62 | then add the content of the clipboard to that file 63 | 64 | Next: test: 65 | ssh root@$ipAddr 66 | If it doesn't let you right in after accepting the key, something isn't working correctly. 67 | " 68 | } 69 | 70 | # show menu to chose OS type 71 | echo -e "\n###################################################" 72 | echo "######### Patch Management Host Installer #########" 73 | echo -e "###################################################\n" 74 | 75 | function ShowHelp() 76 | { 77 | echo -e " Available Arguments: -s [host] -ip [ip address] -or- -l ./hosts.txt\n" 78 | echo -e " $0 -s hostname -ip 192.168.0.10" 79 | echo -e " $0 -l ./hosts.txt (list format: host ip)" 80 | echo -e "\n####################################################" 81 | } 82 | 83 | # get token from user input 84 | while getopts ":s:l:h?" opts; do 85 | case ${opts} in 86 | h|\?) 87 | ShowHelp 88 | exit 1 89 | ;; 90 | s) 91 | sHost=${OPTARG} 92 | echo "$@"|grep -q '\-ip' 93 | if [[ "$?" = 0 ]]; then 94 | ipAddr=`echo $@|grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'|sed -e 's/-ip//g'` 95 | SingleHost 96 | else 97 | echo -e "\n\e[31mERROR\e[0m: Bad format or missing IP, please try again.\n" 98 | exit 0 99 | fi 100 | ;; 101 | l) 102 | fHost=${OPTARG} 103 | ListOfHosts 104 | ;; 105 | esac 106 | done 107 | 108 | # show menu if not arguments 109 | if [[ -z $@ ]]; then 110 | ShowHelp 111 | exit 1 112 | fi 113 | if [[ ! -z ${OPTARG} ]]; then 114 | ShowHelp 115 | exit 1 116 | fi 117 | -------------------------------------------------------------------------------- /scripts/db.conf: -------------------------------------------------------------------------------- 1 | DB_USER="YOUR USER" 2 | DB_PASS="YOUR PASS" 3 | DB_NAME="YOUR NAME" 4 | DB_HOST="localhost" 5 | -------------------------------------------------------------------------------- /scripts/db_config.php: -------------------------------------------------------------------------------- 1 | ${client_path}.patchrc 27 | fi 28 | # load the file 29 | . ${client_path}.patchrc 30 | rm -rf /tmp/$client_key > /dev/null 2>&1 31 | if [[ -f /etc/lsb-release && -f /etc/debian_version ]]; then 32 | os=$(lsb_release -s -d|head -1|awk {'print $1'}) 33 | elif test -f /etc/debian_version -o -f /etc/devuan_version; then 34 | os="$(cat /etc/issue|head -n 1|awk {'print $1'})" 35 | elif [[ -f /etc/redhat-release ]]; then 36 | os=$(cat /etc/redhat-release|head -1|awk {'print $1'}) 37 | if [[ "$os" = "Red" && $(grep -i enterprise /etc/redhat-release) != "" ]]; then 38 | os="RHEL" 39 | elif [[ "$os" = "Red" ]]; then 40 | os="RHEL" 41 | fi 42 | else 43 | os=$(uname -s -r|head -1|awk {'print $1'}) 44 | fi 45 | # remove any special characters 46 | os=$(echo $os|sed -e 's/[^a-zA-Z0-9]//g') 47 | # begin update checks 48 | if [[ "$os" = "CentOS" ]] || [[ "$os" = "Fedora" ]] || [[ "$os" = "Red" ]]; then 49 | need_patched="true" 50 | yum -q check-update| while read i 51 | do 52 | i=$(echo $i) #this strips off yum's irritating use of whitespace 53 | if [[ "${i}x" != "x" ]] 54 | then 55 | UVERSION=${i#*\ } 56 | UVERSION=${UVERSION%\ *} 57 | PNAME=${i%%\ *} 58 | PNAME=${PNAME%.*} 59 | echo $(rpm -q "${PNAME}" --qf '%{NAME}:::%{VERSION}:::')${UVERSION} 60 | patches_to_install=$(echo $(rpm -q "${PNAME}" --qf '%{NAME}:::%{VERSION}:::')${UVERSION}) 61 | echo "$patches_to_install" >> /tmp/$client_key 62 | fi 63 | done 64 | elif test "$os" = Ubuntu -o "$os" = Debian -o "$os" = Devuan -o "$os" = Raspbian; then 65 | need_patched="true" 66 | apt-get --just-print upgrade 2>&1 | perl -ne 'if (/Inst\s([\w,\-,\d,\.,~,:,\+]+)\s\[([\w,\-,\d,\.,~,:,\+]+)\]\s\(([\w,\-,\d,\.,~,:,\+]+)\)? /i) {print "$1:::$2:::$3\n"}' 67 | patches_to_install=$(apt-get --just-print upgrade 2>&1 | perl -ne 'if (/Inst\s([\w,\-,\d,\.,~,:,\+]+)\s\[([\w,\-,\d,\.,~,:,\+]+)\]\s\(([\w,\-,\d,\.,~,:,\+]+)\)? /i) {print "$1:::$2:::$3\n"}') 68 | echo "$patches_to_install\n" >> /tmp/$client_key 69 | elif [[ "$os" = "Linux" ]]; then 70 | echo "unspecified $os not supported" 71 | exit 0 72 | fi 73 | if [[ "$need_patched" = "true" ]]; then 74 | patch_list=$(cat /tmp/$client_key) 75 | curl -H "X-CLIENT-KEY: $client_key" $submit_patch_uri -d "$patch_list" > /dev/null 2>&1 76 | rm -rf /tmp/$client_key > /dev/null 2>&1 77 | fi 78 | -------------------------------------------------------------------------------- /scripts/queue_package_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # load DB config 4 | . /opt/patch_manager/db.conf 5 | 6 | # define server from argument 7 | server="$1" 8 | 9 | # connect to DB and clean up packages 10 | sql="DELETE FROM patch_allpackages where server_name='$server';" 11 | 12 | # define other functions 13 | mysql -u $DB_USER -p"$DB_PASS" -D $DB_NAME -e "$sql" -h $DB_HOST 14 | script='/opt/patch_manager/package_checker.sh' 15 | export server sql script 16 | 17 | function useHost() 18 | { 19 | ssh -i /root/.ssh/id_rsa root@$server 'mkdir -p /opt/patch_manager/' 20 | scp -i /root/.ssh/id_rsa $script root@$server:/opt/patch_manager/ 21 | ssh -i /root/.ssh/id_rsa root@$server "chmod +x $script" 22 | data=`ssh -i /root/.ssh/id_rsa root@${server} "bash $script"` 23 | /opt/patch_manager/run_get_package_list.php "$server" "$data" 24 | } 25 | 26 | function useIP() 27 | { 28 | server_ip=`mysql -h $DB_HOST -u $DB_USER -p"$DB_PASS" -Nse "SELECT server_ip from servers where server_name='$server';" $DB_NAME` 29 | ssh -i /root/.ssh/id_rsa root@$server_ip 'mkdir -p /opt/patch_manager/' 30 | scp -i /root/.ssh/id_rsa $script root@$server_ip:/opt/patch_manager/ 31 | ssh -i /root/.ssh/id_rsa root@$server_ip "chmod +x $script" 32 | data=`ssh -i /root/.ssh/id_rsa root@${server_ip} "bash $script"` 33 | /opt/patch_manager/run_get_package_list.php "$server" "$data" 34 | } 35 | 36 | # check provided hostname and if not connect use IP from DB 37 | ping -c 1 $server > /dev/null 2>&1 38 | if [[ $? -eq 0 ]]; then 39 | useHost 40 | else 41 | useIP 42 | fi 43 | -------------------------------------------------------------------------------- /scripts/queue_patch_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # load DB config 4 | . /opt/patch_manager/db.conf 5 | 6 | # define server from argument 7 | server="$1" 8 | 9 | # connect to DB and clean up packages 10 | sql="DELETE FROM patches where server_name='$server';" 11 | 12 | # define other functions 13 | supression_list=`mysql -u $DB_USER -p"$DB_PASS" -Nse "SELECT package_name from supressed where server_name in ('$server', 0);" $DB_NAME` 14 | mysql -u $DB_USER -p"$DB_PASS" -D $DB_NAME -e "$sql" -h $DB_HOST 15 | script='/opt/patch_manager/patch_checker.sh' 16 | export server supression_list sql script 17 | 18 | function useHost() 19 | { 20 | ssh -i /root/.ssh/id_rsa root@$server 'mkdir -p /opt/patch_manager/' 21 | scp -i /root/.ssh/id_rsa $script root@$server:/opt/patch_manager/ 22 | ssh -i /root/.ssh/id_rsa root@$server "chmod +x $script" 23 | data=`ssh -i /root/.ssh/id_rsa root@${server} "bash $script"` 24 | /opt/patch_manager/run_patch_check.php "$server" "$data" "$supression_list" 25 | } 26 | 27 | function useIP() 28 | { 29 | server_ip=`mysql -h $DB_HOST -u $DB_USER -p"$DB_PASS" -Nse "SELECT server_ip from servers where server_name='$server';" $DB_NAME` 30 | ssh -i /root/.ssh/id_rsa root@$server_ip 'mkdir -p /opt/patch_manager/' 31 | scp -i /root/.ssh/id_rsa $script root@$server_ip:/opt/patch_manager/ 32 | ssh -i /root/.ssh/id_rsa root@$server_ip "chmod +x $script" 33 | data=`ssh -i /root/.ssh/id_rsa root@${server_ip} "bash $script"` 34 | /opt/patch_manager/run_patch_check.php "$server" "$data" "$supression_list" 35 | } 36 | 37 | # check provided hostname and if not connect use IP from DB 38 | ping -c 1 $server > /dev/null 2>&1 39 | if [[ $? -eq 0 ]]; then 40 | useHost 41 | else 42 | useIP 43 | fi 44 | -------------------------------------------------------------------------------- /scripts/run_get_package_list.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | 18 | -------------------------------------------------------------------------------- /scripts/run_patch_check.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | ' 2>/dev/null|head -1\""); 17 | $url = str_replace(" 43 | -------------------------------------------------------------------------------- /scripts/start_get_package_list.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . /opt/patch_manager/db.conf 3 | server_list=`mysql -h $DB_HOST -u $DB_USER -p"$DB_PASS" -Nse 'SELECT server_name from servers' $DB_NAME` 4 | for server in `echo $server_list`; do 5 | /opt/patch_manager/queue_package_check.sh "$server" & 6 | done 7 | -------------------------------------------------------------------------------- /scripts/start_patch_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . /opt/patch_manager/db.conf 3 | server_list=`mysql -h $DB_HOST -u $DB_USER -p"$DB_PASS" -Nse 'SELECT server_name from servers' $DB_NAME` 4 | for server in `echo $server_list`; do 5 | /opt/patch_manager/queue_patch_check.sh "$server" & 6 | done 7 | -------------------------------------------------------------------------------- /sendmail.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/expect 2 | 3 | set timeout 20 4 | set server [lindex $argv 0] 5 | set sndr_mail [lindex $argv 1] 6 | set rcpt_mail [lindex $argv 2] 7 | set mail_subj [lindex $argv 3] 8 | set mail_body [lindex $argv 4] 9 | 10 | spawn telnet $server 25 11 | 12 | expect "Connected to " 13 | expect "220 " 14 | send "HELO $server\n" 15 | expect "250 " 16 | send "MAIL FROM:<$sndr_mail>\n" 17 | expect "250 " 18 | send "RCPT TO:<$rcpt_mail>\n" 19 | expect "250 " 20 | send "DATA\n" 21 | expect "354 " 22 | send "From:$sndr_mail\n" 23 | send "To:$rcpt_mail\n" 24 | send "Subject:$mail_subj\n\n" 25 | send "$mail_body\n" 26 | send ".\n" 27 | expect "250 " 28 | send "quit\n" 29 | expect "221 " 30 | 31 | --------------------------------------------------------------------------------