├── API.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── DOCUMENTATION.md
├── INSTALL.md
├── README.md
├── SECURITY.md
├── admin
├── admin-page.php
└── api-keys-page.php
├── images
├── API_Keys.png
├── Media_Library.png
└── Secure_Updates_Server_Settings01.png
└── secure-updates-server.php
/API.md:
--------------------------------------------------------------------------------
1 | # Secure Updates Server API Documentation
2 |
3 | The Secure Updates Server plugin provides several REST API endpoints for managing plugin updates securely. All endpoints are prefixed with `/wp-json/secure-updates-server/v1`.
4 |
5 | ## Authentication
6 |
7 | Most endpoints are public except for the Plugin List endpoint which requires authentication using a Bearer token. To authenticate:
8 |
9 | 1. Add your API key in the WordPress admin under Secure Updates Server > API Keys
10 | 2. Include the API key in requests using the Authorization header:
11 | ```
12 | Authorization: Bearer your-api-key-here
13 | ```
14 |
15 | ## Endpoints
16 |
17 | ### Download Plugin
18 | Get the latest version of a plugin package.
19 |
20 | ```
21 | GET /download/{slug}
22 | ```
23 |
24 | **Parameters:**
25 | - `slug` (path parameter): The plugin slug (e.g., "my-plugin")
26 |
27 | **Response:**
28 | - Success: Returns the plugin ZIP file for download
29 | - Error (404): Plugin not found or file doesn't exist
30 | ```json
31 | {
32 | "code": "plugin_not_found",
33 | "message": "Plugin not found or file does not exist.",
34 | "status": 404
35 | }
36 | ```
37 |
38 | ### Get Plugin Information
39 | Retrieve metadata about a specific plugin.
40 |
41 | ```
42 | GET /info/{slug}
43 | ```
44 |
45 | **Parameters:**
46 | - `slug` (path parameter): The plugin slug
47 |
48 | **Response:**
49 | - Success:
50 | ```json
51 | {
52 | "name": "Plugin Name",
53 | "slug": "plugin-slug",
54 | "version": "1.0.0",
55 | "author": "Author Name",
56 | "homepage": "https://example.com",
57 | "download_link": "https://example.com/wp-json/secure-updates-server/v1/download/plugin-slug",
58 | "sections": {
59 | "description": "Plugin description here.",
60 | "installation": "Installation instructions here.",
61 | "changelog": "Changelog here."
62 | }
63 | }
64 | ```
65 | - Error (404): Plugin not found
66 | ```json
67 | {
68 | "code": "plugin_not_found",
69 | "message": "Plugin not found",
70 | "status": 404
71 | }
72 | ```
73 |
74 | ### Check Connection
75 | Test if the update server is accessible and functioning.
76 |
77 | ```
78 | GET /connected
79 | ```
80 |
81 | **Response:**
82 | ```json
83 | {
84 | "status": "connected"
85 | }
86 | ```
87 |
88 | ### Verify Plugin File
89 | Check if a plugin file exists and get its verification information.
90 |
91 | ```
92 | GET /verify_file/{slug}
93 | ```
94 |
95 | **Parameters:**
96 | - `slug` (path parameter): The plugin slug
97 |
98 | **Response:**
99 | - Success:
100 | ```json
101 | {
102 | "status": "success",
103 | "message": "Plugin file is correctly hosted and accessible.",
104 | "file_exists": true,
105 | "file_path": "/path/to/plugin.zip",
106 | "checksum": "sha256-hash-of-file"
107 | }
108 | ```
109 | - Error (404): Plugin or file not found
110 | ```json
111 | {
112 | "code": "plugin_not_found",
113 | "message": "Plugin not found.",
114 | "status": 404
115 | }
116 | ```
117 | or
118 | ```json
119 | {
120 | "code": "file_not_found",
121 | "message": "Plugin file does not exist on the server.",
122 | "status": 404
123 | }
124 | ```
125 |
126 | ### Manage Plugin List
127 | Add or update multiple plugins on the server.
128 |
129 | ```
130 | POST /plugins
131 | ```
132 |
133 | **Authentication Required**: Bearer token in Authorization header
134 |
135 | **Request Body:**
136 | ```json
137 | {
138 | "plugins": ["plugin-slug-1", "plugin-slug-2"]
139 | }
140 | ```
141 |
142 | **Response:**
143 | - Success:
144 | ```json
145 | {
146 | "status": "success",
147 | "plugins": {
148 | "plugin-slug-1": "already mirrored",
149 | "plugin-slug-2": "mirrored successfully"
150 | }
151 | }
152 | ```
153 | - Error (400): Invalid request
154 | ```json
155 | {
156 | "code": "invalid_request",
157 | "message": "Invalid plugin list.",
158 | "status": 400
159 | }
160 | ```
161 | - Error (401): Unauthorized
162 | ```json
163 | {
164 | "code": "rest_forbidden",
165 | "message": "Sorry, you are not allowed to do that."
166 | }
167 | ```
168 |
169 | ## Error Handling
170 |
171 | All endpoints return appropriate HTTP status codes:
172 | - 200: Successful request
173 | - 400: Bad request (invalid parameters)
174 | - 401: Unauthorized (invalid or missing API key)
175 | - 404: Resource not found
176 | - 500: Server error
177 |
178 | Error responses follow the WordPress REST API format:
179 | ```json
180 | {
181 | "code": "error_code",
182 | "message": "Human readable error message",
183 | "status": 400
184 | }
185 | ```
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to the Secure Updates Server project will be documented in this file.
4 |
5 | ## [4.0] - 2024-11-05
6 |
7 | ### Added
8 | - API Key Management interface for secure client authentication
9 | - New REST API endpoint for plugin list handling
10 | - Direct plugin uploads with versioning support
11 | - Media Library integration for plugin storage
12 | - Enhanced admin dashboard with version histories
13 | - Improved logging and user notifications
14 |
15 | ### Changed
16 | - Updated scheduled tasks to hourly checks
17 | - Strengthened security measures across operations
18 | - Enhanced admin interface for better usability
19 |
20 | ### Security
21 | - Implemented rigorous input validation
22 | - Added nonce verification
23 | - Enhanced permission checks
24 |
25 | ## [3.0]
26 |
27 | ### Added
28 | - Direct plugin upload capability
29 | - Versioning and rollback features
30 | - Media Library storage integration
31 | - Enhanced logging and error handling
32 |
33 | ### Changed
34 | - Refined admin dashboard for better usability
35 |
36 | ## [2.0]
37 |
38 | ### Added
39 | - AWS S3 storage integration
40 | - On-demand plugin mirroring
41 | - Checksum verification
42 | - Enhanced admin interface
43 |
44 | ## [1.0]
45 |
46 | ### Added
47 | - Initial release
48 | - Basic plugin mirroring from WordPress.org
49 | - Admin interface for plugin management
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Secure Updates Server
2 |
3 | We welcome contributions from the community! This document outlines the process for contributing to the project.
4 |
5 | ## Getting Started
6 |
7 | 1. **Fork the Repository:**
8 | ```bash
9 | Click the 'Fork' button at the top of the repository page
10 | ```
11 |
12 | 2. **Clone Your Fork:**
13 | ```bash
14 | git clone https://github.com/yourusername/secure-updates-server.git
15 | ```
16 |
17 | 3. **Create a Branch:**
18 | ```bash
19 | git checkout -b feature/your-feature-name
20 | ```
21 |
22 | 4. **Make Changes:**
23 | - Implement your feature or fix
24 | - Follow WordPress coding standards
25 | - Add tests where appropriate
26 |
27 | 5. **Commit and Push:**
28 | ```bash
29 | git add .
30 | git commit -m "Description of your changes"
31 | git push origin feature/your-feature-name
32 | ```
33 |
34 | 6. **Submit a Pull Request:**
35 | - Go to the original repository
36 | - Submit a pull request from your branch
37 | - Provide a clear description of the changes
38 |
39 | ## Planned Enhancements
40 |
41 | 1. **Support for Additional Plugin Sources**
42 | - GitHub integration
43 | - Bitbucket integration
44 | - Custom source support
45 |
46 | 2. **Improved Rollback Functionality**
47 | - Automatic file replacements
48 | - Version transition handling
49 |
50 | 3. **Enhanced Error Handling**
51 | - Detailed error messages
52 | - Admin interface log viewer
53 |
54 | 4. **User Notifications**
55 | - Email notifications
56 | - Dashboard alerts
57 |
58 | 5. **API Enhancements**
59 | - Additional endpoints
60 | - Third-party integration support
61 |
62 | 6. **Testing & Quality Assurance**
63 | - Unit tests
64 | - Integration tests
65 | - Security audits
66 | - WordPress coding standards compliance
67 |
68 | ## Code Standards
69 |
70 | - Follow [WordPress Coding Standards](https://developer.wordpress.org/coding-standards/)
71 | - Include proper documentation
72 | - Write meaningful commit messages
73 | - Add/update tests as needed
74 |
75 | ## Testing
76 |
77 | - Run existing tests before submitting PR
78 | - Add new tests for new features
79 | - Ensure all tests pass
80 |
81 | ## Questions?
82 |
83 | Open an issue for any questions about contributing.
--------------------------------------------------------------------------------
/DOCUMENTATION.md:
--------------------------------------------------------------------------------
1 | # Secure Updates Server API Documentation
2 |
3 | The Secure Updates Server plugin provides several REST API endpoints for managing plugin updates securely. All endpoints are prefixed with `/wp-json/secure-updates-server/v1`.
4 |
5 | ## Authentication
6 |
7 | Most endpoints are public except for the Plugin List endpoint which requires authentication using a Bearer token. To authenticate:
8 |
9 | 1. Add your API key in the WordPress admin under Secure Updates Server > API Keys
10 | 2. Include the API key in requests using the Authorization header:
11 | ```
12 | Authorization: Bearer your-api-key-here
13 | ```
14 |
15 | ## Endpoints
16 |
17 | ### Download Plugin
18 | Get the latest version of a plugin package.
19 |
20 | ```
21 | GET /download/{slug}
22 | ```
23 |
24 | **Parameters:**
25 | - `slug` (path parameter): The plugin slug (e.g., "my-plugin")
26 |
27 | **Response:**
28 | - Success: Returns the plugin ZIP file for download
29 | - Error (404): Plugin not found or file doesn't exist
30 | ```json
31 | {
32 | "code": "plugin_not_found",
33 | "message": "Plugin not found or file does not exist.",
34 | "status": 404
35 | }
36 | ```
37 |
38 | ### Get Plugin Information
39 | Retrieve metadata about a specific plugin.
40 |
41 | ```
42 | GET /info/{slug}
43 | ```
44 |
45 | **Parameters:**
46 | - `slug` (path parameter): The plugin slug
47 |
48 | **Response:**
49 | - Success:
50 | ```json
51 | {
52 | "name": "Plugin Name",
53 | "slug": "plugin-slug",
54 | "version": "1.0.0",
55 | "author": "Author Name",
56 | "homepage": "https://example.com",
57 | "download_link": "https://example.com/wp-json/secure-updates-server/v1/download/plugin-slug",
58 | "sections": {
59 | "description": "Plugin description here.",
60 | "installation": "Installation instructions here.",
61 | "changelog": "Changelog here."
62 | }
63 | }
64 | ```
65 | - Error (404): Plugin not found
66 | ```json
67 | {
68 | "code": "plugin_not_found",
69 | "message": "Plugin not found",
70 | "status": 404
71 | }
72 | ```
73 |
74 | ### Check Connection
75 | Test if the update server is accessible and functioning.
76 |
77 | ```
78 | GET /connected
79 | ```
80 |
81 | **Response:**
82 | ```json
83 | {
84 | "status": "connected"
85 | }
86 | ```
87 |
88 | ### Verify Plugin File
89 | Check if a plugin file exists and get its verification information.
90 |
91 | ```
92 | GET /verify_file/{slug}
93 | ```
94 |
95 | **Parameters:**
96 | - `slug` (path parameter): The plugin slug
97 |
98 | **Response:**
99 | - Success:
100 | ```json
101 | {
102 | "status": "success",
103 | "message": "Plugin file is correctly hosted and accessible.",
104 | "file_exists": true,
105 | "file_path": "/path/to/plugin.zip",
106 | "checksum": "sha256-hash-of-file"
107 | }
108 | ```
109 | - Error (404): Plugin or file not found
110 | ```json
111 | {
112 | "code": "plugin_not_found",
113 | "message": "Plugin not found.",
114 | "status": 404
115 | }
116 | ```
117 | or
118 | ```json
119 | {
120 | "code": "file_not_found",
121 | "message": "Plugin file does not exist on the server.",
122 | "status": 404
123 | }
124 | ```
125 |
126 | ### Manage Plugin List
127 | Add or update multiple plugins on the server.
128 |
129 | ```
130 | POST /plugins
131 | ```
132 |
133 | **Authentication Required**: Bearer token in Authorization header
134 |
135 | **Request Body:**
136 | ```json
137 | {
138 | "plugins": ["plugin-slug-1", "plugin-slug-2"]
139 | }
140 | ```
141 |
142 | **Response:**
143 | - Success:
144 | ```json
145 | {
146 | "status": "success",
147 | "plugins": {
148 | "plugin-slug-1": "already mirrored",
149 | "plugin-slug-2": "mirrored successfully"
150 | }
151 | }
152 | ```
153 | - Error (400): Invalid request
154 | ```json
155 | {
156 | "code": "invalid_request",
157 | "message": "Invalid plugin list.",
158 | "status": 400
159 | }
160 | ```
161 | - Error (401): Unauthorized
162 | ```json
163 | {
164 | "code": "rest_forbidden",
165 | "message": "Sorry, you are not allowed to do that."
166 | }
167 | ```
168 |
169 | ## Error Handling
170 |
171 | All endpoints return appropriate HTTP status codes:
172 | - 200: Successful request
173 | - 400: Bad request (invalid parameters)
174 | - 401: Unauthorized (invalid or missing API key)
175 | - 404: Resource not found
176 | - 500: Server error
177 |
178 | Error responses follow the WordPress REST API format:
179 | ```json
180 | {
181 | "code": "error_code",
182 | "message": "Human readable error message",
183 | "status": 400
184 | }
185 | ```
--------------------------------------------------------------------------------
/INSTALL.md:
--------------------------------------------------------------------------------
1 | # Installation Guide
2 |
3 | ## Requirements
4 |
5 | - WordPress 5.0 or higher
6 | - PHP 7.4 or higher
7 | - ZIP extension enabled
8 | - Write permissions for wp-content/uploads
9 |
10 | ## Installation Steps
11 |
12 | 1. **Download the Plugin:**
13 | - Clone the repository or download the ZIP file from the [GitHub Repository](https://github.com/secure-updates/secure-updates-server)
14 |
15 | 2. **Install via WordPress Admin:**
16 | - Navigate to `Plugins` > `Add New` > `Upload Plugin`
17 | - Upload the `secure-updates-server.zip` file
18 | - Click `Install Now` and then `Activate`
19 |
20 | 3. **Configure the Plugin:**
21 |
22 | ### API Keys Setup
23 | - Go to `Secure Updates Server` > `API Keys`
24 | - Add a new API key that clients will use to authenticate
25 | - Ensure keys are securely stored and distributed
26 |
27 | ### Mirroring Plugins
28 | - Navigate to `Secure Updates Server` > `Secure Updates Server`
29 | - Enter plugin slug to mirror from WordPress.org
30 | - Click `Mirror Plugin`
31 |
32 | ### Uploading Plugins
33 | - Use the `Upload Your Plugin to the Server` section
34 | - Support for direct ZIP file uploads
35 | - Version management included
36 |
37 | ### Managing Plugins
38 | - View all plugins in the `Managed Plugins` section
39 | - Perform deletions or rollbacks as needed
40 |
41 | ## Usage Guide
42 |
43 | ### Managing API Keys
44 |
45 | 1. **Add a New API Key:**
46 | - Navigate to API Keys section
47 | - Enter secure key (16-64 characters)
48 | - Use only letters, numbers, underscores, hyphens
49 |
50 | 2. **Delete an API Key:**
51 | - Locate key in the list
52 | - Click Delete and confirm
53 |
54 | ### Plugin Management
55 |
56 | 1. **Mirroring Plugins:**
57 | - Enter plugin slug (e.g., `akismet`)
58 | - Click Mirror Plugin
59 | - Monitor progress in Managed Plugins table
60 |
61 | 2. **Direct Uploads:**
62 | - Use Upload section
63 | - Select ZIP file
64 | - Provide version information
65 |
66 | 3. **Version Management:**
67 | - View version history
68 | - Perform rollbacks
69 | - Delete outdated versions
70 |
71 | ## Client Configuration
72 |
73 | 1. Install Secure Updates Client plugin
74 | 2. Navigate to Settings > Secure Updates Client
75 | 3. Enter:
76 | - Custom Host URL (your server)
77 | - API key from server
78 | 4. Test connection
79 | 5. Enable updates
80 |
81 | ## Troubleshooting
82 |
83 | ### Common Issues
84 |
85 | 1. **Upload Failures:**
86 | - Check file permissions
87 | - Verify ZIP format
88 | - Confirm WordPress upload limits
89 |
90 | 2. **API Key Issues:**
91 | - Verify key format
92 | - Check client configuration
93 | - Confirm server settings
94 |
95 | 3. **Mirroring Problems:**
96 | - Check WordPress.org connectivity
97 | - Verify plugin slug
98 | - Monitor error logs
99 |
100 | ### Getting Help
101 |
102 | - Check the FAQ section
103 | - Review error logs
104 | - Open GitHub issue for support
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Secure Updates Server
2 |
3 | A WordPress plugin that provides secure plugin updates by allowing direct uploads and mirroring from repositories with Media Library integration.
4 |
5 | **Contributors:** Secure Updates Foundation
6 | **Tags:** updates server, plugin mirror, WordPress, media library, plugin updates, API keys
7 | **Requires at least:** 5.0
8 | **Tested up to:** 6.6.2
9 | **Stable tag:** 4.0
10 | **License:** GPLv2 or later
11 | **License URI:** [https://www.gnu.org/licenses/gpl-2.0.html](https://www.gnu.org/licenses/gpl-2.0.html)
12 |
13 | ## Beta Status
14 | This plugin is in active development and while it's feature-complete and follows security best practices, it hasn't undergone extensive production testing across different environments. Test thoroughly in a staging environment before deploying to production.
15 |
16 | 
17 |
18 | ## Description
19 |
20 | **Secure Updates Server** is a comprehensive WordPress plugin designed to provide secure and controlled plugin updates. It caters to two primary user groups:
21 |
22 | 1. **Companies Managing Client Sites**: Mirror plugins from centralized repositories like WordPress.org to maintain greater control over plugin updates across multiple client sites.
23 |
24 | 2. **Plugin Authors**: Simplify the distribution of updates by directly uploading plugins to the Secure Updates Server.
25 |
26 | ## Features
27 |
28 | - **API Key Integration**: Secure client authentication using API keys
29 | - **Direct Plugin Uploads & Versioning**: Upload and manage plugin versions
30 | - **1-Click Mirroring from WordPress.org**: Easy plugin mirroring
31 | - **Media Library Integration**: Seamless cloud storage integration
32 | - **REST API Endpoints**: Secure communication infrastructure
33 | - **Automated & Scheduled Updates**: Hourly update checks
34 | - **Checksum Verification**: File integrity verification
35 | - **Enhanced Security Measures**: Comprehensive security features
36 |
37 | 
38 |
39 |
40 |
41 | ## Installation
42 |
43 | 1. **Download the Plugin:**
44 | - Clone the repository or download the ZIP file
45 | - Install via WordPress Admin > Plugins > Add New > Upload Plugin
46 | - Activate the plugin
47 |
48 | 2. **Configure the Plugin:**
49 | - Navigate to Secure Updates Server in admin menu
50 | - Set up API keys
51 | - Configure plugin mirroring or uploads
52 | - Manage plugin versions
53 |
54 | For detailed installation and configuration instructions, see [INSTALL.md](INSTALL.md).
55 |
56 | ## Documentation
57 |
58 | - [Installation Guide](INSTALL.md) - Detailed setup instructions
59 | - [API Documentation](API.md) - REST API endpoints and usage
60 | - [Contributing Guide](CONTRIBUTING.md) - Development and contribution guidelines
61 | - [Changelog](CHANGELOG.md) - Version history and updates
62 | - [Security Policy](SECURITY.md) - Security guidelines and reporting
63 |
64 | ## Related Tools
65 |
66 | - **[Secure Updates Client](https://github.com/secure-updates/secure-updates-client)**: Plugin for client sites
67 | - **[Secure Updates Library](https://github.com/secure-updates/secure-updates-library)**: Integration library for plugin authors
68 |
69 | 
70 |
71 |
72 | ## Requirements
73 |
74 | - WordPress 5.0 or higher
75 | - PHP 7.4 or higher
76 | - ZIP extension enabled
77 | - Write permissions for wp-content/uploads
78 |
79 | ## Support
80 |
81 | For bug reports and feature requests, please use the [GitHub issue tracker](https://github.com/secure-updates/secure-updates-server/issues).
82 |
83 | ## License
84 |
85 | This project is licensed under the GPL v2 or later - see [LICENSE](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html) for details.
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | | Version | Supported |
6 | | ------- | ------------------ |
7 | | 4.0 | :white_check_mark: |
8 | | 3.0 | :white_check_mark: |
9 | | 2.0 | :x: |
10 | | 1.0 | :x: |
11 |
12 | ## Security Features
13 |
14 | 1. **API Key Authentication**
15 | - Secure client authentication
16 | - Key management interface
17 | - Token-based authorization
18 |
19 | 2. **File Integrity**
20 | - SHA256 checksum verification
21 | - Secure file uploads
22 | - Version tracking
23 |
24 | 3. **Access Control**
25 | - WordPress capability checks
26 | - Nonce verification
27 | - Input sanitization
28 |
29 | 4. **Data Protection**
30 | - Secure storage practices
31 | - Media Library integration
32 | - Cloud storage compatibility
33 |
34 | ## Reporting a Vulnerability
35 |
36 | 1. **Do Not Create Public Issues**
37 | - Security issues should be reported privately
38 |
39 | 2. **Contact Process**
40 | - Email: security@secure-updates.org
41 | - Include detailed description
42 | - Provide steps to reproduce
43 |
44 | 3. **Response Timeline**
45 | - Initial response: 48 hours
46 | - Assessment: 1 week
47 | - Fix development: Based on severity
48 |
49 | ## Best Practices
50 |
51 | 1. **API Key Management**
52 | - Regular key rotation
53 | - Secure distribution
54 | - Access logging
55 |
56 | 2. **Server Configuration**
57 | - HTTPS enforcement
58 | - Access restrictions
59 | - Regular updates
60 |
61 | 3. **Monitoring**
62 | - Activity logging
63 | - Error tracking
64 | - Update notifications
65 |
66 | ## Security Recommendations
67 |
68 | 1. **For Administrators**
69 | - Regular backups
70 | - Access control review
71 | - Update monitoring
72 |
73 | 2. **For Developers**
74 | - Code review process
75 | - Security testing
76 | - Documentation maintenance
77 |
78 | 3. **For Users**
79 | - API key protection
80 | - Regular updates
81 | - Security monitoring
--------------------------------------------------------------------------------
/admin/admin-page.php:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
40 |
41 |
44 |
45 |
48 |
49 |
50 |
51 |
52 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | |
103 | |
104 | |
105 | |
106 | |
107 |
108 |
109 |
110 |
111 | $plugin): ?>
112 |
113 | |
114 | |
115 |
116 | ';
119 | }
120 | ?>
121 | |
122 |
123 |
127 |
128 |
129 |
130 | |
131 |
132 |
133 |
142 | |
143 |
144 |
145 |
146 |
147 |
148 |
149 | |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/admin/api-keys-page.php:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
15 |
18 |
21 |
24 |
28 |
29 |
30 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | |
66 | |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | |
76 |
77 |
87 | |
88 |
89 |
90 |
91 |
92 |
93 |
94 | |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | -
110 |
111 |
Authorization: Bearer YOUR_API_KEY
112 |
113 | -
114 |
115 |
curl -X POST \
116 | \
117 | -H 'Authorization: Bearer YOUR_API_KEY' \
118 | -H 'Content-Type: application/json' \
119 | -d '{"plugins": ["plugin-slug-1", "plugin-slug-2"]}'
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/images/API_Keys.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secure-updates/secure-updates-server/3ed2dd1f9e3edd076c94a7198956409717ae0def/images/API_Keys.png
--------------------------------------------------------------------------------
/images/Media_Library.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secure-updates/secure-updates-server/3ed2dd1f9e3edd076c94a7198956409717ae0def/images/Media_Library.png
--------------------------------------------------------------------------------
/images/Secure_Updates_Server_Settings01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secure-updates/secure-updates-server/3ed2dd1f9e3edd076c94a7198956409717ae0def/images/Secure_Updates_Server_Settings01.png
--------------------------------------------------------------------------------
/secure-updates-server.php:
--------------------------------------------------------------------------------
1 | setup_hooks();
28 | }
29 |
30 | /**
31 | * Load plugin textdomain for translations
32 | */
33 | public function load_textdomain()
34 | {
35 | load_plugin_textdomain('secure-updates-server', false, dirname(plugin_basename(__FILE__)) . '/languages');
36 | }
37 |
38 | /**
39 | * Setup WordPress hooks
40 | */
41 | private function setup_hooks()
42 | {
43 | // Admin Menu
44 | add_action('admin_menu', [$this, 'setup_admin_menu']);
45 |
46 | // Handle Direct Plugin Upload
47 | add_action('admin_post_upload_plugin_to_server', [$this, 'upload_plugin_to_server']);
48 |
49 | // Handle Mirror Plugin via standard form submission
50 | add_action('admin_post_mirror_plugin', [$this, 'mirror_plugin']);
51 |
52 | // Handle Delete Plugin via standard form submission
53 | add_action('admin_post_delete_plugin', [$this, 'delete_plugin']);
54 |
55 | // Scheduled Updates
56 | add_action('secure_updates_server_check_updates', [$this, 'check_for_updates']);
57 |
58 | // REST API Endpoints
59 | add_action('rest_api_init', [$this, 'register_rest_routes']);
60 |
61 | // Activation and Deactivation Hooks
62 | register_activation_hook(__FILE__, [$this, 'activate_plugin']);
63 | register_deactivation_hook(__FILE__, [$this, 'deactivate_plugin']);
64 | }
65 |
66 | /**
67 | * Activate the plugin and schedule update checks
68 | */
69 | public function activate_plugin()
70 | {
71 | if (!wp_next_scheduled('secure_updates_server_check_updates')) {
72 | wp_schedule_event(time(), 'hourly', 'secure_updates_server_check_updates');
73 | }
74 | }
75 |
76 | /**
77 | * Deactivate the plugin and clear scheduled hooks
78 | */
79 | public function deactivate_plugin()
80 | {
81 | wp_clear_scheduled_hook('secure_updates_server_check_updates');
82 | }
83 |
84 | /**
85 | * Setup the admin menu in WordPress dashboard
86 | */
87 | public function setup_admin_menu()
88 | {
89 | add_menu_page(
90 | __('Secure Updates Server', 'secure-updates-server'),
91 | __('Secure Updates Server', 'secure-updates-server'),
92 | 'manage_options',
93 | 'secure-updates-server',
94 | [$this, 'admin_page'],
95 | 'dashicons-update',
96 | 6
97 | );
98 |
99 | add_submenu_page(
100 | 'secure-updates-server',
101 | __('API Keys', 'secure-updates-server'),
102 | __('API Keys', 'secure-updates-server'),
103 | 'manage_options',
104 | 'secure-updates-server-api-keys',
105 | [$this, 'api_keys_page']
106 | );
107 | }
108 |
109 | /**
110 | * Display the admin page for managing plugins
111 | */
112 | public function admin_page()
113 | {
114 | // Include the admin page content
115 | include plugin_dir_path(__FILE__) . 'admin/admin-page.php';
116 | }
117 |
118 | /**
119 | * Register REST API routes
120 | */
121 | public function register_rest_routes()
122 | {
123 | // Existing Download Endpoint
124 | register_rest_route('secure-updates-server/v1', '/download/(?P[a-zA-Z0-9-]+)', [
125 | 'methods' => 'GET',
126 | 'callback' => [$this, 'handle_download_request'],
127 | 'permission_callback' => '__return_true',
128 | ]);
129 |
130 | // Existing Info Endpoint
131 | register_rest_route('secure-updates-server/v1', '/info/(?P[a-zA-Z0-9-]+)', [
132 | 'methods' => 'GET',
133 | 'callback' => [$this, 'handle_info_request'],
134 | 'permission_callback' => '__return_true',
135 | ]);
136 |
137 | // Existing Connected Endpoint
138 | register_rest_route('secure-updates-server/v1', '/connected', [
139 | 'methods' => 'GET',
140 | 'callback' => [$this, 'handle_connected_request'],
141 | 'permission_callback' => '__return_true',
142 | ]);
143 |
144 | // Existing Verify File Endpoint
145 | register_rest_route('secure-updates-server/v1', '/verify_file/(?P[a-zA-Z0-9-]+)', [
146 | 'methods' => 'GET',
147 | 'callback' => [$this, 'handle_verify_file_request'],
148 | 'permission_callback' => '__return_true',
149 | ]);
150 |
151 | // New Plugin List Endpoint
152 | register_rest_route('secure-updates-server/v1', '/plugins', [
153 | 'methods' => 'POST',
154 | 'callback' => [$this, 'handle_plugin_list_request'],
155 | 'permission_callback' => [$this, 'verify_client_request'],
156 | ]);
157 | }
158 |
159 | /**
160 | * Verify client API key
161 | */
162 | public function verify_client_request($request)
163 | {
164 | $headers = $request->get_headers();
165 | if (isset($headers['authorization'])) {
166 | $auth = $headers['authorization'][0];
167 | if (strpos($auth, 'Bearer ') === 0) {
168 | $api_key = substr($auth, 7);
169 | $valid_api_keys = get_option('secure_updates_valid_api_keys', []);
170 | if (in_array($api_key, $valid_api_keys, true)) {
171 | return true;
172 | }
173 | }
174 | }
175 | return false;
176 | }
177 |
178 | /**
179 | * Handle REST API request for plugin download
180 | */
181 | public function handle_download_request($request)
182 | {
183 | $plugin_slug = sanitize_text_field($request['slug']);
184 |
185 | // Get the latest version of the plugin
186 | $secure_updates_plugins = get_option('secure_updates_plugins', []);
187 | if (isset($secure_updates_plugins[$plugin_slug])) {
188 | $latest_version = $secure_updates_plugins[$plugin_slug]['latest_version'];
189 | $attachment_id = $secure_updates_plugins[$plugin_slug]['versions'][$latest_version]['attachment_id'];
190 | $file_path = get_attached_file($attachment_id);
191 |
192 | if ($file_path && file_exists($file_path)) {
193 | // Serve the file for download
194 | header('Content-Description: File Transfer');
195 | header('Content-Type: application/zip');
196 | header('Content-Disposition: attachment; filename="' . basename($file_path) . '"');
197 | header('Content-Length: ' . filesize($file_path));
198 | header('Pragma: public');
199 | flush();
200 | readfile($file_path);
201 | exit;
202 | }
203 | }
204 |
205 | return new WP_Error('plugin_not_found', __('Plugin not found or file does not exist.', 'secure-updates-server'), ['status' => 404]);
206 | }
207 |
208 | /**
209 | * Handle REST API request for plugin information
210 | */
211 | public function handle_info_request($request)
212 | {
213 | $plugin_slug = sanitize_text_field($request['slug']);
214 |
215 | // Get plugin information
216 | $secure_updates_plugins = get_option('secure_updates_plugins', []);
217 | if (isset($secure_updates_plugins[$plugin_slug])) {
218 | // Retrieve plugin data
219 | $plugin_info = [
220 | 'name' => ucfirst($plugin_slug),
221 | 'slug' => $plugin_slug,
222 | 'version' => $secure_updates_plugins[$plugin_slug]['latest_version'],
223 | 'author' => 'Your Name',
224 | 'homepage' => home_url(),
225 | 'download_link' => rest_url('secure-updates-server/v1/download/' . $plugin_slug),
226 | 'sections' => [
227 | 'description' => __('Plugin description here.', 'secure-updates-server'),
228 | 'installation' => __('Installation instructions here.', 'secure-updates-server'),
229 | 'changelog' => __('Changelog here.', 'secure-updates-server'),
230 | ],
231 | ];
232 |
233 | return rest_ensure_response($plugin_info);
234 | }
235 |
236 | return new WP_Error('plugin_not_found', __('Plugin not found', 'secure-updates-server'), ['status' => 404]);
237 | }
238 |
239 | /**
240 | * Handle REST API request to test connection
241 | */
242 | public function handle_connected_request($request)
243 | {
244 | return rest_ensure_response(['status' => 'connected']);
245 | }
246 |
247 | /**
248 | * Handle REST API request to verify plugin file
249 | */
250 | public function handle_verify_file_request($request)
251 | {
252 | $plugin_slug = sanitize_text_field($request['slug']);
253 |
254 | // Get the plugin information
255 | $secure_updates_plugins = get_option('secure_updates_plugins', []);
256 | if (isset($secure_updates_plugins[$plugin_slug])) {
257 | $attachment_id = $secure_updates_plugins[$plugin_slug]['attachment_id'];
258 | $file_path = get_attached_file($attachment_id);
259 |
260 | if ($file_path && file_exists($file_path)) {
261 | // Calculate checksum
262 | $checksum = isset($secure_updates_plugins[$plugin_slug]['versions'][$secure_updates_plugins[$plugin_slug]['latest_version']]['checksum'])
263 | ? $secure_updates_plugins[$plugin_slug]['versions'][$secure_updates_plugins[$plugin_slug]['latest_version']]['checksum']
264 | : '';
265 |
266 | return rest_ensure_response([
267 | 'status' => 'success',
268 | 'message' => __('Plugin file is correctly hosted and accessible.', 'secure-updates-server'),
269 | 'file_exists' => true,
270 | 'file_path' => $file_path,
271 | 'checksum' => $checksum,
272 | ]);
273 | } else {
274 | return new WP_Error('file_not_found', __('Plugin file does not exist on the server.', 'secure-updates-server'), ['status' => 404]);
275 | }
276 | }
277 |
278 | return new WP_Error('plugin_not_found', __('Plugin not found.', 'secure-updates-server'), ['status' => 404]);
279 | }
280 |
281 | /**
282 | * Handle plugin list request
283 | */
284 | public function handle_plugin_list_request($request)
285 | {
286 | $params = $request->get_json_params();
287 | if (empty($params['plugins']) || !is_array($params['plugins'])) {
288 | return new WP_Error('invalid_request', __('Invalid plugin list.', 'secure-updates-server'), ['status' => 400]);
289 | }
290 |
291 | $plugin_slugs = array_map('sanitize_text_field', $params['plugins']);
292 | $responses = [];
293 |
294 | foreach ($plugin_slugs as $slug) {
295 | if ($this->is_plugin_mirrored($slug)) {
296 | $responses[$slug] = 'already mirrored';
297 | } else {
298 | $result = $this->mirror_plugin_by_slug($slug);
299 | if ($result) {
300 | $responses[$slug] = 'mirrored successfully';
301 | } else {
302 | $responses[$slug] = 'failed to mirror';
303 | }
304 | }
305 | }
306 |
307 | return rest_ensure_response(['status' => 'success', 'plugins' => $responses]);
308 | }
309 |
310 | /**
311 | * Mirror a plugin (Handles standard form submission)
312 | */
313 | public function mirror_plugin()
314 | {
315 | // Verify nonce
316 | if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'mirror_plugin_nonce')) {
317 | wp_redirect(admin_url('admin.php?page=secure-updates-server&status=error&message=nonce_failed'));
318 | exit;
319 | }
320 |
321 | // Capability check
322 | if (!current_user_can('manage_options')) {
323 | wp_redirect(admin_url('admin.php?page=secure-updates-server&status=error&message=insufficient_permissions'));
324 | exit;
325 | }
326 |
327 | $plugin_slug = isset($_POST['plugin_slug']) ? sanitize_text_field($_POST['plugin_slug']) : '';
328 |
329 | if (empty($plugin_slug)) {
330 | wp_redirect(admin_url('admin.php?page=secure-updates-server&status=error&message=empty_slug'));
331 | exit;
332 | }
333 |
334 | $this->log_message("Mirroring plugin: $plugin_slug");
335 |
336 | if ($this->mirror_plugin_by_slug($plugin_slug)) {
337 | wp_redirect(admin_url('admin.php?page=secure-updates-server&status=success'));
338 | } else {
339 | wp_redirect(admin_url('admin.php?page=secure-updates-server&status=error&message=mirror_failed'));
340 | }
341 | exit;
342 | }
343 |
344 | /**
345 | * Delete a mirrored or uploaded plugin (Handles standard form submission)
346 | */
347 | public function delete_plugin()
348 | {
349 | // Verify nonce
350 | if (!isset($_POST['delete_plugin_nonce_field']) || !wp_verify_nonce($_POST['delete_plugin_nonce_field'], 'delete_plugin_nonce')) {
351 | wp_redirect(admin_url('admin.php?page=secure-updates-server&status=error&message=nonce_failed'));
352 | exit;
353 | }
354 |
355 | // Capability check
356 | if (!current_user_can('manage_options')) {
357 | wp_redirect(admin_url('admin.php?page=secure-updates-server&status=error&message=insufficient_permissions'));
358 | exit;
359 | }
360 |
361 | $plugin_slug = isset($_POST['plugin_slug']) ? sanitize_text_field($_POST['plugin_slug']) : '';
362 |
363 | if (empty($plugin_slug)) {
364 | wp_redirect(admin_url('admin.php?page=secure-updates-server&status=error&message=empty_slug'));
365 | exit;
366 | }
367 |
368 | // Delete plugin data from the database
369 | $secure_updates_plugins = get_option('secure_updates_plugins', []);
370 | if (isset($secure_updates_plugins[$plugin_slug])) {
371 | // Delete all attachments related to the plugin
372 | foreach ($secure_updates_plugins[$plugin_slug]['versions'] as $version_info) {
373 | wp_delete_attachment($version_info['attachment_id'], true);
374 | }
375 |
376 | unset($secure_updates_plugins[$plugin_slug]);
377 | update_option('secure_updates_plugins', $secure_updates_plugins);
378 |
379 | $this->log_message("Deleted plugin: $plugin_slug");
380 |
381 | do_action('secure_updates_plugin_deleted', $plugin_slug);
382 |
383 | wp_redirect(admin_url('admin.php?page=secure-updates-server&status=deleted'));
384 | exit;
385 | }
386 |
387 | $this->log_message("Plugin not found: $plugin_slug");
388 | wp_redirect(admin_url('admin.php?page=secure-updates-server&status=error&message=plugin_not_found'));
389 | exit;
390 | }
391 |
392 | /**
393 | * Handle Direct Plugin Upload
394 | */
395 | public function upload_plugin_to_server()
396 | {
397 | // Verify nonce
398 | if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'upload_plugin_nonce')) {
399 | wp_die(__('Nonce verification failed', 'secure-updates-server'));
400 | }
401 |
402 | // Capability check
403 | if (!current_user_can('manage_options')) {
404 | wp_die(__('Insufficient permissions.', 'secure-updates-server'));
405 | }
406 |
407 | // Check for file upload
408 | if (empty($_FILES['plugin_zip_file']) || $_FILES['plugin_zip_file']['error'] !== UPLOAD_ERR_OK) {
409 | wp_die(__('Please select a valid ZIP file to upload.', 'secure-updates-server'));
410 | }
411 |
412 | // Handle file upload
413 | $file = $_FILES['plugin_zip_file'];
414 | $upload = wp_handle_upload($file, ['test_form' => false]);
415 |
416 | if (isset($upload['error']) && !empty($upload['error'])) {
417 | wp_die(__('Error uploading file: ', 'secure-updates-server') . esc_html($upload['error']));
418 | }
419 |
420 | // Insert the uploaded file into the WordPress Media Library
421 | $filetype = wp_check_filetype(basename($upload['file']), null);
422 | $attachment = [
423 | 'guid' => $upload['url'],
424 | 'post_mime_type' => $filetype['type'],
425 | 'post_title' => preg_replace('/\.[^.]+$/', '', basename($upload['file'])),
426 | 'post_content' => '',
427 | 'post_status' => 'inherit'
428 | ];
429 |
430 | $attachment_id = wp_insert_attachment($attachment, $upload['file']);
431 | require_once(ABSPATH . 'wp-admin/includes/image.php');
432 | $attach_data = wp_generate_attachment_metadata($attachment_id, $upload['file']);
433 | wp_update_attachment_metadata($attachment_id, $attach_data);
434 |
435 | // Extract plugin slug from file name
436 | $plugin_slug = sanitize_title(preg_replace('/\.[^.]+$/', '', basename($upload['file'])));
437 |
438 | // Extract version from plugin data if possible
439 | $plugin_data = $this->get_plugin_data_from_zip($upload['file']);
440 | $version = isset($plugin_data['Version']) ? sanitize_text_field($plugin_data['Version']) : '1.0.0';
441 |
442 | // Calculate checksum
443 | $checksum = $this->calculate_checksum($upload['file']);
444 |
445 | // Add plugin slug to served plugins list
446 | $secure_updates_plugins = get_option('secure_updates_plugins', []);
447 | if (!isset($secure_updates_plugins[$plugin_slug])) {
448 | $secure_updates_plugins[$plugin_slug] = [
449 | 'attachment_id' => $attachment_id,
450 | 'type' => 'direct_upload',
451 | 'versions' => [
452 | $version => [
453 | 'version' => $version,
454 | 'date' => current_time('mysql'),
455 | 'attachment_id' => $attachment_id,
456 | 'checksum' => $checksum,
457 | ]
458 | ],
459 | 'latest_version' => $version,
460 | ];
461 | } else {
462 | // Handle versioning for existing plugin
463 | $secure_updates_plugins[$plugin_slug]['versions'][$version] = [
464 | 'version' => $version,
465 | 'date' => current_time('mysql'),
466 | 'attachment_id' => $attachment_id,
467 | 'checksum' => $checksum,
468 | ];
469 | $secure_updates_plugins[$plugin_slug]['latest_version'] = $version;
470 | $secure_updates_plugins[$plugin_slug]['attachment_id'] = $attachment_id;
471 | }
472 |
473 | update_option('secure_updates_plugins', $secure_updates_plugins);
474 |
475 | $this->log_message("Uploaded plugin: $plugin_slug version: $version");
476 |
477 | do_action('secure_updates_plugin_uploaded', $plugin_slug, $version);
478 |
479 | // Redirect back with success message
480 | wp_redirect(admin_url('admin.php?page=secure-updates-server&status=upload_success'));
481 | exit;
482 | }
483 |
484 | /**
485 | * Check for updates (both mirrored and uploaded plugins)
486 | */
487 | public function check_for_updates()
488 | {
489 | $secure_updates_plugins = get_option('secure_updates_plugins', []);
490 |
491 | foreach ($secure_updates_plugins as $plugin_slug => $plugin_info) {
492 | if ($plugin_info['type'] === 'mirror') {
493 | $this->check_mirrored_plugin_update($plugin_slug, $plugin_info);
494 | }
495 | // For direct uploads, you might implement different update mechanisms if needed
496 | }
497 |
498 | $this->log_message('Checked for plugin updates.');
499 | }
500 |
501 | /**
502 | * Check and update a mirrored plugin
503 | */
504 | private function check_mirrored_plugin_update($plugin_slug, $plugin_info)
505 | {
506 | $plugin_data = $this->fetch_plugin_data($plugin_slug);
507 |
508 | if ($plugin_data && version_compare($plugin_data['version'], $plugin_info['latest_version'], '>')) {
509 | $version = sanitize_text_field($plugin_data['version']);
510 | $attachment_id = $this->upload_plugin_zip_to_media_library($plugin_slug, $plugin_data['download_link'], $version);
511 |
512 | if ($attachment_id) {
513 | // Calculate checksum
514 | $file_path = get_attached_file($attachment_id);
515 | $checksum = $this->calculate_checksum($file_path);
516 |
517 | // Update plugin information
518 | $this->update_plugin_info($plugin_slug, $version, $attachment_id, 'mirror', $checksum);
519 |
520 | $this->log_message("Updated mirrored plugin: $plugin_slug to version: $version");
521 |
522 | do_action('secure_updates_plugin_updated', $plugin_slug, $version);
523 | }
524 | }
525 | }
526 |
527 | /**
528 | * Check if a plugin is already mirrored
529 | */
530 | private function is_plugin_mirrored($slug)
531 | {
532 | $secure_updates_plugins = get_option('secure_updates_plugins', []);
533 | return isset($secure_updates_plugins[$slug]);
534 | }
535 |
536 | /**
537 | * Mirror a plugin by slug
538 | */
539 | private function mirror_plugin_by_slug($slug)
540 | {
541 | $plugin_data = $this->fetch_plugin_data($slug);
542 |
543 | if ($plugin_data && isset($plugin_data['download_link'])) {
544 | $version = sanitize_text_field($plugin_data['version']);
545 | $attachment_id = $this->upload_plugin_zip_to_media_library($slug, $plugin_data['download_link'], $version);
546 |
547 | if ($attachment_id) {
548 | $file_path = get_attached_file($attachment_id);
549 | $checksum = $this->calculate_checksum($file_path);
550 |
551 | $this->update_plugin_info($slug, $version, $attachment_id, 'mirror', $checksum);
552 |
553 | $this->log_message("Successfully mirrored plugin: $slug version: $version");
554 |
555 | do_action('secure_updates_plugin_mirrored', $slug, $version);
556 |
557 | return true;
558 | }
559 | }
560 |
561 | $this->log_message("Failed to mirror plugin: $slug");
562 | return false;
563 | }
564 |
565 | /**
566 | * Display API keys management page
567 | */
568 | public function api_keys_page()
569 | {
570 | // Handle form submission
571 | if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
572 | if (!wp_verify_nonce($_POST['_wpnonce'], 'manage_api_keys')) {
573 | wp_die(__('Security check failed', 'secure-updates-server'));
574 | }
575 |
576 | $valid_api_keys = get_option('secure_updates_valid_api_keys', []);
577 |
578 | if ($_POST['action'] === 'add_key' && isset($_POST['new_api_key'])) {
579 | $new_key = sanitize_text_field($_POST['new_api_key']);
580 | if (!empty($new_key)) {
581 | $valid_api_keys[] = $new_key;
582 | update_option('secure_updates_valid_api_keys', array_unique($valid_api_keys));
583 | }
584 | } elseif ($_POST['action'] === 'delete_key' && isset($_POST['api_key'])) {
585 | $key_to_delete = sanitize_text_field($_POST['api_key']);
586 | $valid_api_keys = array_diff($valid_api_keys, [$key_to_delete]);
587 | update_option('secure_updates_valid_api_keys', $valid_api_keys);
588 | }
589 | }
590 |
591 | // Display the page
592 | $valid_api_keys = get_option('secure_updates_valid_api_keys', []);
593 | include plugin_dir_path(__FILE__) . 'admin/api-keys-page.php';
594 | }
595 |
596 | /**
597 | * Fetch plugin data from WordPress.org
598 | */
599 | private function fetch_plugin_data($slug)
600 | {
601 | $transient_key = 'plugin_data_' . $slug;
602 | $plugin_data = get_transient($transient_key);
603 |
604 | if ($plugin_data === false) {
605 | $response = wp_remote_get("https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]=$slug");
606 |
607 | if (is_wp_error($response)) {
608 | $this->log_message("Error fetching data for plugin $slug: " . $response->get_error_message());
609 | return false;
610 | }
611 |
612 | $plugin_data = json_decode(wp_remote_retrieve_body($response), true);
613 | set_transient($transient_key, $plugin_data, HOUR_IN_SECONDS);
614 | }
615 |
616 | return $plugin_data;
617 | }
618 |
619 | /**
620 | * Upload plugin ZIP to Media Library
621 | */
622 | private function upload_plugin_zip_to_media_library($slug, $url, $version)
623 | {
624 | $tmp = download_url($url);
625 |
626 | if (is_wp_error($tmp)) {
627 | $this->log_message("Error downloading plugin $slug: " . $tmp->get_error_message());
628 | return false;
629 | }
630 |
631 | $file_array = [
632 | 'name' => $slug . '-' . $version . '.zip',
633 | 'tmp_name' => $tmp,
634 | ];
635 |
636 | // Handle the upload using WordPress's media library functions
637 | $attachment_id = media_handle_sideload($file_array, 0);
638 |
639 | // Check for errors during upload
640 | if (is_wp_error($attachment_id)) {
641 | $this->log_message("Error uploading plugin $slug to media library: " . $attachment_id->get_error_message());
642 | @unlink($tmp); // Delete the temporary file
643 | return false;
644 | }
645 |
646 | // Delete the temporary file
647 | @unlink($tmp);
648 |
649 | return $attachment_id;
650 | }
651 |
652 | /**
653 | * Handle logging of messages
654 | */
655 | private function log_message($message)
656 | {
657 | if (defined('WP_DEBUG') && WP_DEBUG) {
658 | error_log('[Secure Updates Server] ' . $message);
659 | }
660 | }
661 |
662 | /**
663 | * Calculate SHA256 checksum of a file
664 | */
665 | private function calculate_checksum($file_path)
666 | {
667 | return hash_file('sha256', $file_path);
668 | }
669 |
670 | /**
671 | * Update plugin information in the database
672 | */
673 | private function update_plugin_info($slug, $version, $attachment_id, $type, $checksum = '')
674 | {
675 | $secure_updates_plugins = get_option('secure_updates_plugins', []);
676 | if (!isset($secure_updates_plugins[$slug])) {
677 | $secure_updates_plugins[$slug] = [
678 | 'slug' => $slug,
679 | 'type' => $type,
680 | 'versions' => [],
681 | ];
682 | }
683 |
684 | $secure_updates_plugins[$slug]['versions'][$version] = [
685 | 'version' => $version,
686 | 'date' => current_time('mysql'),
687 | 'attachment_id' => $attachment_id,
688 | 'checksum' => $checksum,
689 | ];
690 |
691 | $secure_updates_plugins[$slug]['latest_version'] = $version;
692 | $secure_updates_plugins[$slug]['attachment_id'] = $attachment_id;
693 |
694 | update_option('secure_updates_plugins', $secure_updates_plugins);
695 | }
696 |
697 | /**
698 | * Extract plugin data from ZIP file
699 | */
700 | private function get_plugin_data_from_zip($file_path)
701 | {
702 | $plugin_data = [];
703 | $zip = new ZipArchive();
704 | if ($zip->open($file_path) === true) {
705 | // Look for main plugin file (assuming it has the same name as the folder)
706 | $plugin_slug = basename($file_path, '.zip');
707 | $main_plugin_file = "$plugin_slug.php";
708 |
709 | if ($zip->locateName($main_plugin_file)) {
710 | $plugin_content = $zip->getFromName($main_plugin_file);
711 | if ($plugin_content !== false) {
712 | $plugin_data = $this->parse_plugin_header($plugin_content);
713 | }
714 | }
715 | $zip->close();
716 | }
717 | return $plugin_data;
718 | }
719 |
720 | /**
721 | * Parse plugin header to get version and other data
722 | */
723 | private function parse_plugin_header($plugin_content)
724 | {
725 | $plugin_data = [];
726 | $headers = [
727 | 'Version' => 'Version',
728 | 'Plugin Name' => 'Name',
729 | // Add more headers if needed
730 | ];
731 |
732 | foreach ($headers as $field => $regex_field) {
733 | if (preg_match('/' . $field . ':\s*(.+)/i', $plugin_content, $matches)) {
734 | $plugin_data[$field] = trim($matches[1]);
735 | }
736 | }
737 |
738 | return $plugin_data;
739 | }
740 | }
741 |
742 | // Initialize the plugin
743 | new Secure_Updates_Server();
744 | }
--------------------------------------------------------------------------------