Update high

This commit is contained in:
Kevin Feiler
2025-10-16 01:15:48 +02:00
parent 15815e3a9d
commit ceee8d9663
92 changed files with 20860 additions and 34 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

62
CHANGELOG.md Normal file
View File

@@ -0,0 +1,62 @@
# Changelog
All notable changes to the TeaSpeak WHMCS Module will be documented in this file.
## [3.0.0] - 2024 - Updated Edition
**Updated by:** Kevin Feiler / AVVGO
### Added
- PHP 8.x compatibility (8.0, 8.1, 8.2, 8.3)
- WHMCS API 1.2 support
- Strict type declarations (`declare(strict_types=1)`)
- Type hints for all function parameters and return types
- Modern array syntax (`[]` instead of `array()`)
- Bootstrap 5 template option (`overview_modern.tpl`)
- Font Awesome 6 icon support
- Comprehensive code documentation
- Author credits and version information in file headers
### Changed
- Updated to modern PHP syntax throughout
- Improved error handling and messages
- Better input validation
- Enhanced security practices
- Optimized database queries
- Removed hardcoded URLs
- Improved code structure and readability
- Updated README with detailed installation and upgrade instructions
### Improved
- Mobile responsiveness in templates
- User experience with better UI design
- Code maintainability
- Performance optimizations
- Error logging and debugging capabilities
### Fixed
- Various bugs and edge cases
- Deprecated PHP syntax
- Security vulnerabilities
- Code consistency issues
---
## [2.1.4] - Previous Version
**Original Author:** planetteaspeak.de
### Features
- TeamSpeak server provisioning
- Client area management
- Token/Privileges management
- Ban management
- Backup functionality
- TSDNS integration
- Bootstrap 3 design
- PHP 7.x compatibility
- WHMCS API 1.1
---
**Note:** This changelog covers the major update from version 2.1.4 to 3.0.0. The update focuses on modernization, compatibility, and code quality improvements while maintaining backward compatibility with existing installations.

150
INFO.txt Normal file
View File

@@ -0,0 +1,150 @@
╔═══════════════════════════════════════════════════════════════╗
║ ║
║ TeaSpeak WHMCS Provisioning Module v3.0.0 ║
║ Updated Edition 2024 ║
║ ║
╚═══════════════════════════════════════════════════════════════╝
📦 WAS WURDE GEMACHT?
════════════════════════════════════════════════════════════════
✅ PHP 8.x Kompatibilität
- Strict Types
- Type Hints
- Moderne Array-Syntax
- Verbesserte Fehlerbehandlung
✅ WHMCS API 1.2
- Aktualisiert auf die neueste API-Version
✅ Design Verbesserungen
- Bootstrap 5 Template (optional)
- Font Awesome 6 Icons
- Besseres Mobile Design
- Verbesserte Benutzerfreundlichkeit
✅ Code Verbesserungen
- Bessere Dokumentation
- Entfernte Hardcoded URLs
- Sicherheitsverbesserungen
- Code-Cleanup und Optimierung
📂 DATEIEN
════════════════════════════════════════════════════════════════
Hauptmodul:
TeaSpeak/servers/teamspeak/teamspeak.php (modernisiert)
TeaSpeak/servers/teamspeak/teamspeak_additional.php (neu)
Templates:
TeaSpeak/servers/teamspeak/templates/overview.tpl (original)
TeaSpeak/servers/teamspeak/templates/overview_modern.tpl (neu)
Dokumentation:
README.md (vollständige Dokumentation)
CHANGELOG.md (Änderungsprotokoll)
INSTALLATION.md (Installationsanleitung)
INFO.txt (diese Datei)
🚀 INSTALLATION
════════════════════════════════════════════════════════════════
1. Upload der Dateien:
- TeaSpeak/servers/teamspeak/ → modules/servers/teamspeak/
- TeaSpeak/addons/teamspeak/ → modules/addons/teamspeak/
2. Addon aktivieren:
WHMCS Admin → Setup → Addon Modules
3. Server hinzufügen:
WHMCS Admin → Setup → Servers
4. Produkt erstellen mit Custom Fields:
- Token (Text Box)
- Port (Text Box)
- Subdomain (Text Box, optional)
5. Configurable Options hinzufügen:
- Slots (Quantity)
- MBots (Quantity)
📝 WICHTIGE HINWEISE
════════════════════════════════════════════════════════════════
⚠️ Die Datenbankstruktur ist kompatibel mit der alten Version
⚠️ Backup vor dem Update empfohlen
⚠️ Custom Field Namen sind case-sensitive (Token, Port, Subdomain)
⚠️ Configurable Option Namen sind case-sensitive (Slots, MBots)
🎨 NEUES DESIGN AKTIVIEREN (optional)
════════════════════════════════════════════════════════════════
In teamspeak.php, Funktion teamspeak_ClientArea():
Ändere:
'tabOverviewReplacementTemplate' => 'templates/overview.tpl',
Nach:
'tabOverviewReplacementTemplate' => 'templates/overview_modern.tpl',
🔄 UPGRADE VON ALTER VERSION
════════════════════════════════════════════════════════════════
1. Backup erstellen:
- Dateien: modules/servers/teamspeak/
- Dateien: modules/addons/teamspeak/
- Datenbank: mysqldump
2. Neue Dateien hochladen und überschreiben
3. Cache leeren:
WHMCS Admin → Utilities → System → Clear Cache
4. Testen:
- Bestehende Services prüfen
- Neuen Test-Service erstellen
✨ FEATURES
════════════════════════════════════════════════════════════════
✓ Server erstellen/löschen
✓ Server suspendieren/reaktivieren
✓ Server starten/stoppen
✓ Server neuinstallieren
✓ Berechtigungen zurücksetzen
✓ Einstellungen verwalten
✓ Token/Privilegien verwalten
✓ Bans verwalten
✓ Backups erstellen/wiederherstellen
✓ TSDNS Integration
📧 SUPPORT & CREDITS
════════════════════════════════════════════════════════════════
Original Modul: planetteaspeak.de
Update & Design: Kevin Feiler / AVVGO (2024)
Version: 3.0.0
TeaSpeak: https://teaspeak.de
📖 WEITERE INFOS
════════════════════════════════════════════════════════════════
Siehe README.md für die vollständige Dokumentation mit:
- Detaillierte Installationsanleitung
- Konfigurationsoptionen
- Troubleshooting
- Sicherheitshinweise
- FAQ
════════════════════════════════════════════════════════════════
Viel Erfolg mit dem aktualisierten TeaSpeak WHMCS Modul!
════════════════════════════════════════════════════════════════

184
INSTALLATION.md Normal file
View File

@@ -0,0 +1,184 @@
# TeaSpeak WHMCS Module - Quick Installation Guide
## 📋 Requirements
- **WHMCS**: 8.0 or higher
- **PHP**: 8.0 or higher
- **MySQL/MariaDB**: 5.7+ / 10.2+
- **PHP Extensions**: curl, json, pdo_mysql, mbstring
## 🚀 Quick Installation
### Step 1: Upload Files
Upload the module files to your WHMCS installation:
```bash
/path/to/whmcs/modules/servers/teamspeak/
/path/to/whmcs/modules/addons/teamspeak/
```
### Step 2: Activate Addon Module
1. Login to WHMCS Admin Area
2. Navigate to: **Setup > Addon Modules**
3. Find "TeaSpeak Provisioning"
4. Click **Activate**
5. Configure module settings:
- License Key
- Server Name
- Port Range (Min/Max)
- TSDNS Settings (if applicable)
- Branding options
### Step 3: Add Server
1. Go to: **Setup > Products/Services > Servers**
2. Click **Add New Server**
3. Fill in details:
- Name: Your server name
- Hostname/IP: Your TeamSpeak server IP
- Type: Select **TeaSpeak Provisioning Server**
- ServerQuery Port: 10101 (default)
- Username: ServerQuery username
- Password: ServerQuery password
4. Click **Test Connection** to verify
5. Save
### Step 4: Create Product
1. Go to: **Setup > Products/Services > Products/Services**
2. Create a new product or edit existing
3. Module Settings:
- Module: **TeaSpeak Provisioning Server**
- Select your server from dropdown
### Step 5: Add Custom Fields
Add these custom fields to your product:
**Required:**
1. **Token**
- Field Name: `Token`
- Field Type: Text Box
- Description: Admin Token
- Show on Order Form: No
2. **Port**
- Field Name: `Port`
- Field Type: Text Box
- Description: Server Port
- Show on Order Form: No
3. **Subdomain** (only if using TSDNS)
- Field Name: `Subdomain`
- Field Type: Text Box
- Description: TSDNS Subdomain
- Show on Order Form: Yes
- Required: Yes
### Step 6: Add Configurable Options
Create a configurable options group with:
1. **Slots**
- Option Name: `Slots`
- Option Type: Quantity
- Description: Number of voice slots
2. **MBots**
- Option Name: `MBots`
- Option Type: Quantity
- Description: Music bot limit
### Step 7: Test
1. Create a test order
2. Check if server is provisioned correctly
3. Test all functions in client area:
- Start/Stop
- Reinstall
- Settings
- Backups
- Tokens
- Bans
## 🎨 Enable Modern Design (Optional)
To use the Bootstrap 5 template:
1. Open `modules/servers/teamspeak/teamspeak.php`
2. Find the `teamspeak_ClientArea` function
3. Change template file:
```php
// From:
'tabOverviewReplacementTemplate' => 'templates/overview.tpl',
// To:
'tabOverviewReplacementTemplate' => 'templates/overview_modern.tpl',
```
## 🔄 Upgrading from Old Version
1. **Backup everything:**
```bash
cp -r modules/servers/teamspeak /backup/
cp -r modules/addons/teamspeak /backup/
mysqldump -u username -p database_name > backup.sql
```
2. **Replace files:**
- Upload new module files
- Overwrite existing files
3. **Clear cache:**
- WHMCS Admin > Utilities > System > Clear Cache
4. **Test:**
- Test existing services
- Create new test service
- Verify all functions work
## ⚠️ Important Notes
- Custom field names are **case-sensitive** (`Token`, `Port`, `Subdomain`)
- Configurable option names are **case-sensitive** (`Slots`, `MBots`)
- Make sure ServerQuery port (10101) is accessible
- TSDNS is optional - only configure if needed
- Database structure is compatible with old version
## 🆘 Troubleshooting
### "Port does not exist in custom fields"
- Check custom field "Port" exists
- Field name must be exactly: `Port`
- Assigned to correct product
### "Slots parameter not found"
- Check configurable option "Slots" exists
- Option name must be exactly: `Slots`
### "Could not connect to TeamSpeak server"
- Verify IP address and port
- Check firewall rules
- Verify ServerQuery credentials
- Test connection from WHMCS server:
```bash
telnet YOUR_IP 10101
```
### TSDNS not working
- Enable TSDNS in addon module settings
- Verify TSDNS API URL and key
- Check custom field "Subdomain" exists
## 📚 More Information
See `README.md` for detailed documentation.
---
**Version**: 3.0.0
**Updated by**: Kevin Feiler / AVVGO
**Original**: planetteaspeak.de

View File

@@ -1,4 +1,4 @@
# TeaSpeak WHMCS Provisioning Module - Modernized Edition
# TeaSpeak WHMCS Provisioning Module - Updated Edition
[![PHP Version](https://img.shields.io/badge/PHP-8.0%2B-blue.svg)](https://www.php.net/)
[![WHMCS Version](https://img.shields.io/badge/WHMCS-8.0%2B-green.svg)](https://www.whmcs.com/)
@@ -6,31 +6,29 @@
## 🚀 What's New in This Version
This is a **fully modernized** version of the TeaSpeak WHMCS module with the following improvements:
This is an **updated and redesigned** version of the TeaSpeak WHMCS module with the following improvements:
### ✨ Major Updates
### ✨ Updates & Improvements
- **PHP 8.x Compatible** - Full support for PHP 8.0, 8.1, 8.2, and 8.3
- **WHMCS API 1.2** - Updated to the latest WHMCS module API
- **Modern PHP Features**:
- Strict types (`declare(strict_types=1)`)
- Type hints for all parameters and return types
- Modern array syntax (`[]` instead of `array()`)
- Arrow functions and null coalescing operators
- **PHP 8.x Compatible** - Updated for PHP 8.0+
- **WHMCS API 1.2** - Updated to current WHMCS module API
- **Modern PHP Syntax**:
- Strict types
- Type hints for parameters and return types
- Modern array syntax
- Improved error handling
- **Bootstrap 5 Design** - Beautiful, responsive UI
- Modern card-based layout
- **Design Updates** - Refreshed UI
- Bootstrap 5 template option
- Font Awesome 6 icons
- Improved mobile responsiveness
- Better UX with hover effects and animations
- Better user experience
- **Code Quality**:
- PSR-12 coding standards
- Comprehensive documentation
- Better separation of concerns
- **Code Improvements**:
- Better documentation
- Improved security practices
- Removed hardcoded URLs and made them configurable
- Removed hardcoded URLs
- Code cleanup and optimization
### 🔧 Technical Improvements
@@ -263,19 +261,18 @@ Languages are automatically selected based on client's WHMCS language setting.
## 📝 Changelog
### Version 3.0.0 (2024) - Modernized Edition
- Full PHP 8.x compatibility
### Version 3.0.0 (2024) - Updated Edition by Kevin Feiler / AVVGO
- ✨ PHP 8.x compatibility update
- ✨ WHMCS API 1.2 support
- ✨ Bootstrap 5 modern template
- ✨ Bootstrap 5 template
- ✨ Type hints and strict types
- ✨ Modern array syntax
- ✨ Improved error handling
-Better security practices
- ✨ Code documentation
-Design improvements
- ✨ Code cleanup
- ✨ Removed hardcoded URLs
- ✨ Performance improvements
- 🐛 Fixed various bugs
- 🔒 Security enhancements
- 🐛 Bug fixes
- 🔒 Security improvements
### Version 2.1.4 (Previous)
- Original version
@@ -285,7 +282,7 @@ Languages are automatically selected based on client's WHMCS language setting.
## 🤝 Contributing
This is a proprietary module. For bug reports or feature requests, please contact the module maintainer.
This module has been updated and improved. For bug reports or feature requests, please contact the maintainer.
## 📄 License
@@ -293,16 +290,16 @@ Proprietary license. See LICENSE file for details.
## 🆘 Support
For support, please contact:
For support regarding the original module:
- **Website**: https://planetteaspeak.de
- **Email**: support@planetteaspeak.de
## 🙏 Credits
- Original Module: planetteaspeak.de
- Modernization: 2024 Update
- TeaSpeak: https://teaspeak.de
- WHMCS: https://whmcs.com
- **Original Module**: planetteaspeak.de
- **Update & Design**: Kevin Feiler / AVVGO (2024)
- **TeaSpeak**: https://teaspeak.de
- **WHMCS**: https://whmcs.com
## 📚 Additional Resources
@@ -313,4 +310,5 @@ For support, please contact:
---
**Last Updated**: 2024
**Version**: 3.0.0 (Modernized Edition)
**Version**: 3.0.0 (Updated Edition)
**Updated by**: Kevin Feiler / AVVGO

176
SUMMARY.md Normal file
View File

@@ -0,0 +1,176 @@
# TeaSpeak WHMCS Module - Update Summary
## 📊 Zusammenfassung der Änderungen
**Version:** 3.0.0
**Updated by:** Kevin Feiler / AVVGO
**Datum:** 2024
---
## ✨ Was wurde aktualisiert?
### 1. **PHP 8.x Kompatibilität**
- ✅ Strict types in allen Dateien
- ✅ Type hints für Parameter und Return-Werte
- ✅ Moderne Array-Syntax (`[]` statt `array()`)
- ✅ Null coalescing operator (`??`)
- ✅ Verbesserte Error Handling
### 2. **WHMCS API**
- ✅ Von API 1.1 auf 1.2 aktualisiert
- ✅ Neue API-Features integriert
### 3. **Design**
- ✅ Neues Bootstrap 5 Template erstellt (`overview_modern.tpl`)
- ✅ Font Awesome 6 Icons
- ✅ Besseres Responsive Design
- ✅ Moderne Card-basierte Layouts
- ✅ Hover-Effekte und Animationen
### 4. **Code-Qualität**
- ✅ Vollständige Dokumentation
- ✅ Author Credits hinzugefügt
- ✅ Hardcoded URLs entfernt
- ✅ Sicherheitsverbesserungen
- ✅ Code-Cleanup
- ✅ Konsistente Formatierung
---
## 📁 Neue & Geänderte Dateien
### Neu erstellt:
- `teamspeak_additional.php` - Zusätzliche Funktionen (Backups, Tokens, Bans, Settings)
- `templates/overview_modern.tpl` - Modernes Bootstrap 5 Template
- `README.md` - Umfassende Dokumentation
- `CHANGELOG.md` - Versionshistorie
- `INSTALLATION.md` - Installationsanleitung
- `INFO.txt` - Schnell-Info
- `SUMMARY.md` - Diese Datei
### Aktualisiert:
- `teamspeak.php` - Hauptmodul vollständig modernisiert
- Alle Core-Funktionen mit Type-Hints und modernem PHP
### Unverändert (Kompatibilität):
- `lib/` - Bibliotheken bleiben kompatibel
- Original Templates - Bleiben für Kompatibilität
- Datenbank-Struktur - Vollständig kompatibel
---
## 🔧 Technische Details
### PHP-Änderungen:
```php
// Vorher:
function teamspeak_CreateAccount(array $params)
{
return 'success';
}
// Nachher:
function teamspeak_CreateAccount(array $params): string
{
try {
// Moderner Code mit Type-Safety
$slots = (int)($params["configoptions"]["Slots"] ?? 0);
return "success";
} catch (Exception $e) {
logModuleCall("teamspeak", __FUNCTION__, $params, $e->getMessage());
return $e->getMessage();
}
}
```
### Error Handling:
- Bessere Exception-Nachrichten
- Detailliertes Logging
- User-freundliche Fehlermeldungen
### Security:
- Input-Validierung verbessert
- SQL-Injection-Schutz (Capsule ORM)
- XSS-Prevention in Templates
---
## 📦 Installations-Optionen
### Option 1: Nur Code-Update (empfohlen)
- Alte Funktionalität bleibt erhalten
- PHP 8.x Kompatibilität
- Altes Template funktioniert weiter
### Option 2: Mit Design-Update
- Zusätzlich neues Bootstrap 5 Template aktivieren
- Moderneres Aussehen
- Bessere UX
---
## 🧪 Getestet mit:
- ✅ PHP 8.0, 8.1, 8.2, 8.3
- ✅ WHMCS 8.x
- ✅ MySQL 5.7+ / MariaDB 10.2+
- ✅ Bestehende Installationen (Upgrade-Kompatibilität)
- ✅ Neue Installationen
---
## ⚠️ Wichtige Hinweise für Upgrades
1. **Backup erstellen** vor dem Update!
2. **Testen** auf Entwicklungsumgebung empfohlen
3. **Custom Fields** bleiben erhalten
4. **Configurable Options** bleiben erhalten
5. **Datenbank** bleibt kompatibel
6. **Keine** Breaking Changes für Endnutzer
---
## 🎯 Vorteile des Updates
### Für Entwickler:
- ✅ Moderne PHP-Syntax
- ✅ Bessere IDE-Unterstützung
- ✅ Type-Safety
- ✅ Einfacheres Debugging
- ✅ Bessere Wartbarkeit
### Für Nutzer:
- ✅ Optionales modernes Design
- ✅ Bessere Mobile-Erfahrung
- ✅ Stabilere Performance
- ✅ Keine funktionalen Änderungen (außer Verbesserungen)
### Für Admins:
- ✅ PHP 8.x Zukunftssicherheit
- ✅ Besseres Error-Logging
- ✅ Einfacheres Troubleshooting
- ✅ Security-Verbesserungen
---
## 📞 Support & Feedback
**Original Modul:** planetteaspeak.de
**Update & Design:** Kevin Feiler / AVVGO
**Version:** 3.0.0 - Updated Edition 2024
---
## 🚀 Nächste Schritte
1. Backup erstellen
2. Dateien hochladen
3. WHMCS Cache leeren
4. Testen
5. Optional: Neues Design aktivieren
**Siehe:** `INSTALLATION.md` für detaillierte Anleitung
---
*Dieses Update wurde mit Fokus auf Kompatibilität, Sicherheit und moderne Best Practices erstellt. Keine Breaking Changes für Endnutzer!*

BIN
TeaSpeak/.DS_Store vendored Normal file

Binary file not shown.

BIN
TeaSpeak/addons/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,51 @@
@import url('https://fonts.googleapis.com/css?family=Open+Sans');
@import url('https://fonts.googleapis.com/css?family=Lato');
.teamspeak {
font-family: 'Lato', sans-serif;
}
.teamspeak > .panel-heading {
background-color: transparent;
border-bottom-style: dashed;
}
.teamspeak .panel-heading {
padding: 5px 15px;
}
.teamspeak .nav-tabs > li > a {
color: #555;
border: 1px solid #ddd;
border-bottom-color: transparent;
}
.teamspeak .head {
padding-bottom: 10px;
}
.teamspeak .head > .icon {
font-size: 40px;
float: left;
padding: 10px;
}
.teamspeak .head > .head-name {
font-size: 50px;
font-weight: 700;
}
.teamspeak .head > .head-name > span {
font-size: 15px;
}
#contentarea > div:nth-child(1) > h1:nth-child(1) {
display: none;
}
.tab-content > .active {
border: 1px solid #ccc;
border-top: transparent;
padding: 10px 15px;
border-radius: 0 0 4px 4px;
}

View File

@@ -0,0 +1,42 @@
function test(host, hash) {
// Fork it
var request;
// fire off the request to /form.php
request = $.ajax({
url: '../modules/addons/teamspeak/lib/functions.php',
type: 'get',
data: {
host: host
},
beforeSend: function () {
$('.verify-status').show()
}
});
// callback handler that will be called on success
request.done(function (response, textStatus, jqXHR) {
var status = response.status;
var statusClass;
if (status) {
statusClass = 'success';
$('#' + hash + ' span.manager').removeClass('invisible');
} else {
statusClass = 'danger';
}
$('#' + hash).removeClass('success danger').addClass(statusClass);
});
// callback handler that will be called on failure
request.fail(function (jqXHR, textStatus, errorThrown) {
// log the error to the console
console.error(
"The following error occured: " +
textStatus, errorThrown
);
});
request.always(function () {
setTimeout(function () {
$('.verify-status').hide();
}, 2000);
})
}

View File

@@ -0,0 +1,110 @@
<?php
$_ADDONLANG["general"] = "General";
$_ADDONLANG["settings"] = "Indstillinger";
$_ADDONLANG["tokens"] = "Token";
$_ADDONLANG["bans"] = "Bans";
$_ADDONLANG["backups"] = "Backup";
$_ADDONLANG["status"] = "Status";
$_ADDONLANG["commands"] = "Muligheder";
$_ADDONLANG["ip_address"] = "IP-Adresse";
$_ADDONLANG["port"] = "Port";
$_ADDONLANG["slot"] = "Slots";
$_ADDONLANG["uptime"] = "Oppetid";
$_ADDONLANG["edit"] = "Rediger";
$_ADDONLANG["destination"] = "Bestemmelsessted";
$_ADDONLANG["password_protected"] = "Adgangskodebeskyttet";
$_ADDONLANG["online"] = "Online";
$_ADDONLANG["offline"] = "Offline";
$_ADDONLANG["hostname"] = "Værtsnavn";
$_ADDONLANG["min_port"] = "Min Port";
$_ADDONLANG["max_port"] = "Max Port";
$_ADDONLANG["welcome_message"] = "Velkomstbesked";
$_ADDONLANG["welcome_message_info"] = "Det vises i chatten, når en bruger kommer ind på serveren";
$_ADDONLANG["join_password"] = "Deltag Adgangskode";
$_ADDONLANG["create"] = "Opret";
$_ADDONLANG["add"] = "Tilføj";
$_ADDONLANG["start"] = "Start";
$_ADDONLANG["stop"] = "Stop";
$_ADDONLANG["close"] = "Luk";
$_ADDONLANG["to_save"] = "at Gemme";
$_ADDONLANG["reinstall"] = "Geninstaller";
$_ADDONLANG["reinstall_warning"] = "Er du sikker på, at du vil geninstallere din Tea*Speak-server?";
$_ADDONLANG["reset_warning"] = "Vil du virkelig nulstille serviceindstillingerne?";
$_ADDONLANG["reboot"] = "Genstart";
$_ADDONLANG["reset_permissions"] = "Nulstil Tilladelser";
$_ADDONLANG["update"] = "Opdatering";
$_ADDONLANG["restore"] = "Gendan";
$_ADDONLANG["delete"] = "Slet";
$_ADDONLANG["token_id"] = "Token ID";
$_ADDONLANG[ "description"] = "Beskrivelse";
$_ADDONLANG["name"] = "Navn";
$_ADDONLANG["error"] = "Fejl";
$_ADDONLANG["success"] = "Succes";
$_ADDONLANG["uid"] = "User-ID";
$_ADDONLANG["date"] = "Dato";
$_ADDONLANG["no"] = "Nej";
$_ADDONLANG["yes"] = "Ja";
$_ADDONLANG["zone"] = "Zone";
$_ADDONLANG["service_management"] = "Service Management";
$_ADDONLANG["logs"] = "Logs";
$_ADDONLANG["html_viewer"] = "Html Viewer";
$_ADDONLANG["viewer_description"] = "Kopier denne HTML-kode, og indsæt den for at få vist din Tea*Speak Viewer på dit websted.";
$_ADDONLANG["globaly_instance_settings"] = "Globale instansindstillinger";
$_ADDONLANG["servers"] = "Server";
$_ADDONLANG["servers_name"] = "Server Navn";
$_ADDONLANG["servers_name_info"] = "Vil være standardservernavnet (lad det være tomt som standard)";
$_ADDONLANG["servers_message"] = "Server Besked";
$_ADDONLANG["servers_button"] = "Server Knap";
$_ADDONLANG["servers_banner"] = "Server Banner";
$_ADDONLANG["servers_button_image_url"] = "Knapbillede URL";
$_ADDONLANG["servers_banner_link_url"] = "Knaplink URL";
$_ADDONLANG["servers_banner_message_tooltip"] = "Knapmeddelelse i værktøjstip";
$_ADDONLANG["servers_banner_image_url"] = "Billede URL";
$_ADDONLANG["servers_banner_display_mode"] = "Banner displaytilstand";
$_ADDONLANG["servers_banner_display_mode_not_resize"] = "juster ikke størrelsen";
$_ADDONLANG["servers_banner_display_mode_ignoring_aspect"] = "Skifter størrelse, mens den ignorerer aspektet";
$_ADDONLANG["servers_banner_display_mode_maintaining_appearance"] = "Skifter størrelse, mens den opretholder udseendet";
$_ADDONLANG["servers_message_display_mode"] = "Servermeddelelsesvisningstilstand";
$_ADDONLANG["servers_message_display_mode_not_shown"] = "Vis ikke besked";
$_ADDONLANG["servers_message_display_mode_in_chat"] = "Vis besked i chat";
$_ADDONLANG["servers_message_display_in_modal"] = "Vis besked i et vindue";
$_ADDONLANG["servers_message_display_in_modal_and_leave"] = "Vis meddelelsen i et vindue, og luk serveren";
$_ADDONLANG["servers_message_info"] = "Denne meddelelse vises, når brugeren kommer ind på serveren (lad den være tom, så den ikke vises).";
$_ADDONLANG["servers_file_transfer_upload_quota"] = "Upload-kvote";
$_ADDONLANG["servers_file_transfer_download_quota"] = "Download-kvote";
$_ADDONLANG["servers_file_transfer"] = "Filoverførsel";
$_ADDONLANG["servers_file_transfer_max_upload_speed"] = "Maks uploadhastighed";
$_ADDONLANG["servers_file_transfer_max_download_speed"] = "Maksimal downloadhastighed";
$_ADDONLANG["teamspeak_dns"] = "Tea*Speak DNS";
$_ADDONLANG["teamspeak_dns_enable"] = "TSDNS Aktiver";
$_ADDONLANG["teamspeak_dns_domain"] = "TSDNS Domæne";
$_ADDONLANG["teamspeak_dns_domain_info"] = "Eksempel: example.com";
$_ADDONLANG["teamspeak_dns_adress"] = "TSDNS Adresse";
$_ADDONLANG["teamspeak_dns_adress_info"] = "Eksempel: http://1.2.3.4:3000";
$_ADDONLANG["teamspeak_dns_api_key"] = "TSDNS Api Nøgle";
$_ADDONLANG["manage"] = "Styre";
$_ADDONLANG["back_home"] = "Tilbage til hjem";
$_ADDONLANG["checking_servers"] = "Kontrol af serverstatus";
$_ADDONLANG["installed_version"] = "Installeret version";
$_ADDONLANG["new_version"] = "Ny version";
$_ADDONLANG["new_version_aviable"] = "Ny version er tilgængelig!";
$_ADDONLANG["no_new_version_aviable"] = "Du bruger den nyeste version.";
$_ADDONLANG["alert_ops_something_happened"] = "Ups! Noget fungerer ikke!";
$_ADDONLANG["error_server_cant_start"] = "Serveren kan ikke starte.";
$_ADDONLANG["error_server_cant_stop"] = "Server kan ikke stoppes.";
$_ADDONLANG["error_server_cant_deleted"] = "Server kan ikke slettes.";
$_ADDONLANG["error_no_servers"] = "Der blev ikke fundet nogen server til dette IP!";
$_ADDONLANG["error_no_zones"] = "Der blev ikke fundet nogen zone til dette IP!";
$_ADDONLANG["success_start"] = "Serveren er startet.";
$_ADDONLANG["success_stop"] = "Serveren er stoppet.";
$_ADDONLANG["success_deleted"] = "Serveren er blevet slette.";
$_ADDONLANG["edit_zone"] = "Rediger Zone";
$_ADDONLANG["zone_exclusion_confirm"] = "Bekræftelse af sletning af zonen";
$_ADDONLANG["zone_delete_info"] = "Hvis du virkelig ønsker at slette zonen";
$_ADDONLANG["zone_delete_info_1"] = "Vil du slette denne zone?";
$_ADDONLANG["setting_info"] = "De foretagne ændringer anvendes kun på de nye servere, de nuværende forbliver med de allerede definerede konfigurationer.";
$_ADDONLANG["alert_congratulations_everything_worked"] = "Tillykke! Alt fungerede!"
?>

View File

@@ -0,0 +1,109 @@
<?php
$_ADDONLANG["general"] = "General";
$_ADDONLANG["settings"] = "Settings";
$_ADDONLANG["tokens"] = "Token";
$_ADDONLANG["bans"] = "Bans";
$_ADDONLANG["backups"] = "Backup";
$_ADDONLANG["status"] = "Status";
$_ADDONLANG["commands"] = "Options";
$_ADDONLANG["ip_address"] = "IP-Adress";
$_ADDONLANG["port"] = "Port";
$_ADDONLANG["slot"] = "Slots";
$_ADDONLANG["uptime"] = "Uptime";
$_ADDONLANG["edit"] = "Edit";
$_ADDONLANG["destination"] = "Destination";
$_ADDONLANG["password_protected"] = "Password Protected";
$_ADDONLANG["online"] = "Online";
$_ADDONLANG["offline"] = "Offline";
$_ADDONLANG["hostname"] = "Hostname";
$_ADDONLANG["min_port"] = "Min Port";
$_ADDONLANG["max_port"] = "Max Port";
$_ADDONLANG["welcome_message"] = "Wellcome Message";
$_ADDONLANG["welcome_message_info"] = "It is displayed in the chat when a user enters the server";
$_ADDONLANG["join_password"] = "Join Password";
$_ADDONLANG["create"] = "Create";
$_ADDONLANG["add"] = "Add";
$_ADDONLANG["start"] = "Start";
$_ADDONLANG["stop"] = "Stop";
$_ADDONLANG["close"] = "Close";
$_ADDONLANG["to_save"] = "to Save";
$_ADDONLANG["reinstall"] = "Reinstall";
$_ADDONLANG["reinstall_warning"] = "Are you sure you want to reinstall your Teamspeak server?";
$_ADDONLANG["reset_warning"] = "do you really want to reset the service settings?";
$_ADDONLANG["reboot"] = "Reboot";
$_ADDONLANG["reset_permissions"] = "Reset Permissions";
$_ADDONLANG["update"] = "Update";
$_ADDONLANG["restore"] = "Restore";
$_ADDONLANG["delete"] = "Delete";
$_ADDONLANG["token_id"] = "Token ID";
$_ADDONLANG[ "description"] = "Description";
$_ADDONLANG["name"] = "Name";
$_ADDONLANG["error"] = "Error";
$_ADDONLANG["success"] = "Success";
$_ADDONLANG["uid"] = "User-ID";
$_ADDONLANG["date"] = "Date";
$_ADDONLANG["no"] = "No";
$_ADDONLANG["yes"] = "Yes";
$_ADDONLANG["zone"] = "Zone";
$_ADDONLANG["service_management"] = "Service Management";
$_ADDONLANG["logs"] = "Logs";
$_ADDONLANG["html_viewer"] = "Html Viewer";
$_ADDONLANG["viewer_description"] = "Copy this HTML code and paste it to display your Teamspeak Viewer on your website.";
$_ADDONLANG["globaly_instance_settings"] = "Global instance settings";
$_ADDONLANG["servers"] = "Server";
$_ADDONLANG["servers_name"] = "Server Name";
$_ADDONLANG["servers_name_info"] = "Will be the default server name (leave blank by default)";
$_ADDONLANG["servers_message"] = "Server Message";
$_ADDONLANG["servers_button"] = "Server Button";
$_ADDONLANG["servers_banner"] = "Server Banner";
$_ADDONLANG["servers_button_image_url"] = "Button Image URL";
$_ADDONLANG["servers_banner_link_url"] = "Button Link URL";
$_ADDONLANG["servers_banner_message_tooltip"] = "Button Message in Tooltip";
$_ADDONLANG["servers_banner_image_url"] = "Image URL";
$_ADDONLANG["servers_banner_display_mode"] = "Banner Display Mode";
$_ADDONLANG["servers_banner_display_mode_not_resize"] = "do not adjust size";
$_ADDONLANG["servers_banner_display_mode_ignoring_aspect"] = "Change the size while ignoring the aspect";
$_ADDONLANG["servers_banner_display_mode_maintaining_appearance"] = "Change the size while maintaining the appearance";
$_ADDONLANG["servers_message_display_mode"] = "Server Message Display Mode";
$_ADDONLANG["servers_message_display_mode_not_shown"] = "Don't show message";
$_ADDONLANG["servers_message_display_mode_in_chat"] = "Show message in chat";
$_ADDONLANG["servers_message_display_in_modal"] = "Show message in a window";
$_ADDONLANG["servers_message_display_in_modal_and_leave"] = "Display the message in a window and exit the server";
$_ADDONLANG["servers_message_info"] = "This message is displayed when the user enters the server (leave blank so that it is not displayed).";
$_ADDONLANG["servers_file_transfer_upload_quota"] = "Upload Quota";
$_ADDONLANG["servers_file_transfer_download_quota"] = "Download Quota";
$_ADDONLANG["servers_file_transfer"] = "File Transfer";
$_ADDONLANG["servers_file_transfer_max_upload_speed"] = "Max Upload Speed";
$_ADDONLANG["servers_file_transfer_max_download_speed"] = "Max Download Speed";
$_ADDONLANG["teamspeak_dns"] = "Teamspeak DNS";
$_ADDONLANG["teamspeak_dns_enable"] = "TSDNS Enable";
$_ADDONLANG["teamspeak_dns_domain"] = "TSDNS Domain";
$_ADDONLANG["teamspeak_dns_domain_info"] = "Example: example.com";
$_ADDONLANG["teamspeak_dns_adress"] = "TSDNS Adress";
$_ADDONLANG["teamspeak_dns_adress_info"] = "Example: http://1.2.3.4:3000";
$_ADDONLANG["teamspeak_dns_api_key"] = "TSDNS Api Key";
$_ADDONLANG["manage"] = "Manage";
$_ADDONLANG["back_home"] = "Back to Home";
$_ADDONLANG["checking_servers"] = "Checking Server Status";
$_ADDONLANG["installed_version"] = "Installed Version";
$_ADDONLANG["new_version"] = "New Version";
$_ADDONLANG["new_version_aviable"] = "New version is available!";
$_ADDONLANG["no_new_version_aviable"] = "You are using the latest version.";
$_ADDONLANG["alert_ops_something_happened"] = "Oops! Something is not working!";
$_ADDONLANG["error_server_cant_start"] = "Server can not start.";
$_ADDONLANG["error_server_cant_stop"] = "Server can not be stopped.";
$_ADDONLANG["error_server_cant_deleted"] = "Server can not be deleted.";
$_ADDONLANG["error_no_servers"] = "No server was found for this IP!";
$_ADDONLANG["error_no_zones"] = "No zone was found for this IP!";
$_ADDONLANG["success_start"] = "Server has started.";
$_ADDONLANG["success_stop"] = "Server has been stopped.";
$_ADDONLANG["success_deleted"] = "Server has been deleted.";
$_ADDONLANG["edit_zone"] = "Edit Zone";
$_ADDONLANG["zone_exclusion_confirm"] = "Confirmation of zone deletion";
$_ADDONLANG["zone_delete_info"] = "If you really want to delete the zone";
$_ADDONLANG["zone_delete_info_1"] = "Do you want to delete this zone?";
$_ADDONLANG["setting_info"] = "The changes made are only applied to the new servers, the current ones remain with the configurations already defined.";
$_ADDONLANG["alert_congratulations_everything_worked"] = "Congratulations! Everything worked!"
?>

View File

@@ -0,0 +1,109 @@
<?php
$_ADDONLANG["general"] = "Allgemein";
$_ADDONLANG["settings"] = "Einstellungen";
$_ADDONLANG["tokens"] = "Token";
$_ADDONLANG["bans"] = "Ban";
$_ADDONLANG["backups"] = "Backup";
$_ADDONLANG["status"] = "Status";
$_ADDONLANG["commands"] = "Optionen";
$_ADDONLANG["ip_address"] = "IP-Adresse";
$_ADDONLANG["port"] = "Port";
$_ADDONLANG["slot"] = "Slots";
$_ADDONLANG["uptime"] = "Uptime";
$_ADDONLANG["edit"] = "Bearbeiten";
$_ADDONLANG["destination"] = "Ziel";
$_ADDONLANG["password_protected"] = "Passwortschutz";
$_ADDONLANG["online"] = "Online";
$_ADDONLANG["offline"] = "Offline";
$_ADDONLANG["hostname"] = "Hostname";
$_ADDONLANG["min_port"] = "Min Port";
$_ADDONLANG["max_port"] = "Max Port";
$_ADDONLANG["welcome_message"] = "Willkommensnachricht";
$_ADDONLANG["welcome_message_info"] = "Es wird im Chat angezeigt, wenn ein Benutzer den Server betritt";
$_ADDONLANG["join_password"] = "Zugangspasswort";
$_ADDONLANG["create"] = "Erstellen";
$_ADDONLANG["add"] = "Hinzuf&uuml;gen";
$_ADDONLANG["start"] = "Starten";
$_ADDONLANG["stop"] = "Stoppen";
$_ADDONLANG["close"] = "Schließen";
$_ADDONLANG["to_save"] = "zum Speichern";
$_ADDONLANG["reinstall"] = "Neu installieren";
$_ADDONLANG["reinstall_warning"] = "Sind Sie sicher; dass Sie Ihren Teamspeak Server neu installieren m&ouml;chten?";
$_ADDONLANG["reset_warning"] = "M&ouml;chten Sie die Serviceeinstellungen wirklich zur&uuml;cksetzen?";
$_ADDONLANG["reboot"] = "Neustart";
$_ADDONLANG["reset_permissions"] = "Berechtigungen zur&uuml;cksetzen";
$_ADDONLANG["update"] = "Speichern";
$_ADDONLANG["restore"] = "Wiederherstellen";
$_ADDONLANG["delete"] = "L&ouml;schen";
$_ADDONLANG["token_id"] = "Token / Berechtigungsschl&uuml;ssel";
$_ADDONLANG[ "description"] = "Beschreibung";
$_ADDONLANG["name"] = "Name";
$_ADDONLANG["error"] = "Fehler";
$_ADDONLANG["success"] = "Erfolg";
$_ADDONLANG["uid"] = "Eindeutige User-ID";
$_ADDONLANG["date"] = "Datum";
$_ADDONLANG["no"] = "Nein";
$_ADDONLANG["yes"] = "Ja";
$_ADDONLANG["zone"] = "Zone";
$_ADDONLANG["service_management"] = "Service verwalten";
$_ADDONLANG["logs"] = "Logs";
$_ADDONLANG["html_viewer"] = "Html Viewer";
$_ADDONLANG["viewer_description"] = "Kopieren Sie diesen HTML-Code und f&uuml;gen Sie ihn ein, um Ihren Teamspeak Viewer auf Ihrer Webseite anzuzeigen.";
$_ADDONLANG["globaly_instance_settings"] = "Globale Instanz Einstellungen";
$_ADDONLANG["servers"] = "Server";
$_ADDONLANG["servers_name"] = "Server Name";
$_ADDONLANG["servers_name_info"] = "Wird der Standardname der Server sein (Standardmäßig leer lassen)";
$_ADDONLANG["servers_message"] = "Server Nachricht";
$_ADDONLANG["servers_button"] = "Server Button";
$_ADDONLANG["servers_banner"] = "Server Banner";
$_ADDONLANG["servers_button_image_url"] = "Button Bild URL";
$_ADDONLANG["servers_banner_link_url"] = "Button Link URL";
$_ADDONLANG["servers_banner_message_tooltip"] = "Button Nachricht im Tooltip";
$_ADDONLANG["servers_banner_image_url"] = "Bild URL";
$_ADDONLANG["servers_banner_display_mode"] = "Banner Anzeige Modus";
$_ADDONLANG["servers_banner_display_mode_not_resize"] = "Gr&ouml;ße nicht Anpassen";
$_ADDONLANG["servers_banner_display_mode_ignoring_aspect"] = "&Auml;ndern Sie die Gr&ouml;ße, w&auml;hrend Sie den Aspekt ignorieren";
$_ADDONLANG["servers_banner_display_mode_maintaining_appearance"] = "&Auml;ndern Sie die Gr&ouml;ße, w&auml;hrend Sie das Erscheinungsbild beibehalten";
$_ADDONLANG["servers_message_display_mode"] = "Server Nachricht anzeige Modus";
$_ADDONLANG["servers_message_display_mode_not_shown"] = "Nachricht nicht anzeigen";
$_ADDONLANG["servers_message_display_mode_in_chat"] = "Nachricht im Chat anzeigen";
$_ADDONLANG["servers_message_display_in_modal"] = "Nachricht in einem Fenster anzeigen";
$_ADDONLANG["servers_message_display_in_modal_and_leave"] = "Nachricht in einem Fenster anzeigen und Server verlassen";
$_ADDONLANG["servers_message_info"] = "Diese Meldung wird angezeigt, wenn der Benutzer den Server betritt (leer lassen, damit sie nicht angezeigt wird).";
$_ADDONLANG["servers_file_transfer_upload_quota"] = "Upload Quota";
$_ADDONLANG["servers_file_transfer_download_quota"] = "Download Quota";
$_ADDONLANG["servers_file_transfer"] = "Datei &Uuml;bertragung";
$_ADDONLANG["servers_file_transfer_max_upload_speed"] = "Max Upload Geschwindigkeit";
$_ADDONLANG["servers_file_transfer_max_download_speed"] = "Max Download Geschwindigkeit";
$_ADDONLANG["teamspeak_dns"] = "Teamspeak DNS";
$_ADDONLANG["teamspeak_dns_enable"] = "TSDNS Aktivieren";
$_ADDONLANG["teamspeak_dns_domain"] = "TSDNS Domain";
$_ADDONLANG["teamspeak_dns_domain_info"] = "Beispiel: example.com";
$_ADDONLANG["teamspeak_dns_adress"] = "TSDNS Adresse";
$_ADDONLANG["teamspeak_dns_adress_info"] = "Beispiel: http://1.2.3.4:3000";
$_ADDONLANG["teamspeak_dns_api_key"] = "TSDNS Api Key";
$_ADDONLANG["manage"] = "Verwaltung";
$_ADDONLANG["back_home"] = "Zur&uuml;ck zur &Uuml;bersicht";
$_ADDONLANG["checking_servers"] = "&Uuml;berpr&uuml;fe Server Status";
$_ADDONLANG["installed_version"] = "Installierte Version";
$_ADDONLANG["new_version"] = "Neue Version";
$_ADDONLANG["new_version_aviable"] = "Neue Version steht zur verf&uuml;gung!";
$_ADDONLANG["no_new_version_aviable"] = "Sie nutzen die aktuellste Version.";
$_ADDONLANG["alert_ops_something_happened"] = "Hoppla! Etwas funktioniert nicht!";
$_ADDONLANG["error_server_cant_start"] = "Server kann nicht gestartet werden.";
$_ADDONLANG["error_server_cant_stop"] = "Server kann nicht gestopt werden.";
$_ADDONLANG["error_server_cant_deleted"] = "Server kann nicht gel&ouml;scht werden.";
$_ADDONLANG["error_no_servers"] = "F&uuml;r diese IP wurde kein Server gefunden!";
$_ADDONLANG["error_no_zones"] = "F&uuml;r diese IP wurde keine Zone gefunden!";
$_ADDONLANG["success_start"] = "Server wurde gestartet.";
$_ADDONLANG["success_stop"] = "Server wurde gestopt.";
$_ADDONLANG["success_deleted"] = "Server wurde gel&ouml;scht.";
$_ADDONLANG["edit_zone"] = "Zone Bearbeiten";
$_ADDONLANG["zone_exclusion_confirm"] = "Best&auml;tigung des Zonenl&ouml;schung";
$_ADDONLANG["zone_delete_info"] = "Wenn Sie die Zone wirklich l&ouml;schen m&ouml;chten";
$_ADDONLANG["zone_delete_info_1"] = "M&öuml;chten Sie diese Zone l&ouml;schen?";
$_ADDONLANG["setting_info"] = "Die vorgenommenen &Auml;nderungen werden nur auf die neuen Server angewendet, die aktuellen bleiben bei den bereits definierten Konfigurationen.";
$_ADDONLANG["alert_congratulations_everything_worked"] = "Gl&uuml;ckwunsch! Alles hat funktioniert!"
?>

View File

@@ -0,0 +1,110 @@
<?php
$_ADDONLANG["general"] = "Geral";
$_ADDONLANG["settings"] = "Definições";
$_ADDONLANG["tokens"] = "Chaves de Privilégio";
$_ADDONLANG["bans"] = "Bans";
$_ADDONLANG["backups"] = "Backup";
$_ADDONLANG["status"] = "Estado";
$_ADDONLANG["commands"] = "Opções";
$_ADDONLANG["ip_address"] = "Endereço de IP";
$_ADDONLANG["port"] = "Porta";
$_ADDONLANG["slot"] = "Slots";
$_ADDONLANG["uptime"] = "Tempo de atividade";
$_ADDONLANG["edit"] = "Editar";
$_ADDONLANG["destination"] = "Destino";
$_ADDONLANG["password_protected"] = "Protegido por senha";
$_ADDONLANG["online"] = "Ligado";
$_ADDONLANG["offline"] = "Desligado";
$_ADDONLANG["hostname"] = "Hostname";
$_ADDONLANG["min_port"] = "Min Porta";
$_ADDONLANG["max_port"] = "Max Porta";
$_ADDONLANG["welcome_message"] = "Mensagem de Bem-Vindo";
$_ADDONLANG["welcome_message_info"] = "É mostrado no chat quando um usuário entra no servidor";
$_ADDONLANG["join_password"] = "Senha de Entrada";
$_ADDONLANG["create"] = "Criar";
$_ADDONLANG["add"] = "Adicionar";
$_ADDONLANG["start"] = "Iniciar";
$_ADDONLANG["stop"] = "Parar";
$_ADDONLANG["close"] = "Fechar";
$_ADDONLANG["to_save"] = "para salvar";
$_ADDONLANG["reinstall"] = "Reinstalar";
$_ADDONLANG["reinstall_warning"] = "Tem certeza que deseja reinstalar seu servidor de Tea*Speak?";
$_ADDONLANG["reset_warning"] = "Voce realmente quer resetar as configurações de serviço?";
$_ADDONLANG["reboot"] = "Reiniciar";
$_ADDONLANG["reset_permissions"] = "Resetar permissões";
$_ADDONLANG["update"] = "Atualizar";
$_ADDONLANG["restore"] = "Restaurar";
$_ADDONLANG["delete"] = "Apagar";
$_ADDONLANG["token_id"] = "ID da Chaves de Privilégio";
$_ADDONLANG[ "description"] = "Descrição";
$_ADDONLANG["name"] = "Nome";
$_ADDONLANG["error"] = "Erro";
$_ADDONLANG["success"] = "Sucesso";
$_ADDONLANG["uid"] = "ID de Utilizador";
$_ADDONLANG["date"] = "Data";
$_ADDONLANG["no"] = "Não";
$_ADDONLANG["yes"] = "Sim";
$_ADDONLANG["zone"] = "Zona";
$_ADDONLANG["service_management"] = "Gerenciar Serviço";
$_ADDONLANG["logs"] = "Logs";
$_ADDONLANG["html_viewer"] = "Visão de HTML";
$_ADDONLANG["viewer_description"] = "Copie este código HTML e cole ele para exibir o Tea*Speak Viewer no seu site.";
$_ADDONLANG["globaly_instance_settings"] = "Configurações de instância global";
$_ADDONLANG["servers"] = "Servidor";
$_ADDONLANG["servers_name"] = "Nome do Servidor";
$_ADDONLANG["servers_name_info"] = "Será o nome do servidor padrão (deixe em branco por padrão)";
$_ADDONLANG["servers_message"] = "Mensagem do Servidor";
$_ADDONLANG["servers_button"] = "Botão do Servidor";
$_ADDONLANG["servers_banner"] = "Banner do Servidor";
$_ADDONLANG["servers_button_image_url"] = "URL da imagem do botão";
$_ADDONLANG["servers_banner_link_url"] = "URL do botão";
$_ADDONLANG["servers_banner_message_tooltip"] = "Mensagem do botão na dica de ferramenta";
$_ADDONLANG["servers_banner_image_url"] = "Imagem URL";
$_ADDONLANG["servers_banner_display_mode"] = "Modo de exibição do banner";
$_ADDONLANG["servers_banner_display_mode_not_resize"] = "Não ajustar";
$_ADDONLANG["servers_banner_display_mode_ignoring_aspect"] = "Ajustar, mas ignorar a taxa de proporção";
$_ADDONLANG["servers_banner_display_mode_maintaining_appearance"] = "Ajustar e manter taxa de proporção";
$_ADDONLANG["servers_message_display_mode"] = "Modo de exibição de mensagens do servidor";
$_ADDONLANG["servers_message_display_mode_not_shown"] = "Não mostrar mensagem";
$_ADDONLANG["servers_message_display_mode_in_chat"] = "Mostra uma mensagem no chat";
$_ADDONLANG["servers_message_display_in_modal"] = "Mostrar mensagem em uma janela";
$_ADDONLANG["servers_message_display_in_modal_and_leave"] = "Exiba a mensagem em uma janela e desconecta do servidor";
$_ADDONLANG["servers_message_info"] = "Esta mensagem é exibida quando o usuário entra no servidor (deixe em branco para que não seja exibido).";
$_ADDONLANG["servers_file_transfer_upload_quota"] = "Cota de Upload";
$_ADDONLANG["servers_file_transfer_download_quota"] = "Cota de Download";
$_ADDONLANG["servers_file_transfer"] = "Transferência de Arquivos";
$_ADDONLANG["servers_file_transfer_max_upload_speed"] = "Velocidade máxima de upload";
$_ADDONLANG["servers_file_transfer_max_download_speed"] = "Velocidade máxima de download";
$_ADDONLANG["teamspeak_dns"] = "Tea*Speak DNS";
$_ADDONLANG["teamspeak_dns_enable"] = "TSDNS Ativado";
$_ADDONLANG["teamspeak_dns_domain"] = "TSDNS Domínio ";
$_ADDONLANG["teamspeak_dns_domain_info"] = "Exemplo: exemplo.com";
$_ADDONLANG["teamspeak_dns_adress"] = "TSDNS Endereço";
$_ADDONLANG["teamspeak_dns_adress_info"] = "Exemplo: http://1.2.3.4:3000";
$_ADDONLANG["teamspeak_dns_api_key"] = "Chave de API TSDNS";
$_ADDONLANG["manage"] = "Gerenciar";
$_ADDONLANG["back_home"] = "Voltar à página inicial";
$_ADDONLANG["checking_servers"] = "Verificando o estado do servidor";
$_ADDONLANG["installed_version"] = "Versão instalada";
$_ADDONLANG["new_version"] = "Nova versão";
$_ADDONLANG["new_version_aviable"] = "Nova versão disponível!";
$_ADDONLANG["no_new_version_aviable"] = "Esta usando a última versão.";
$_ADDONLANG["alert_ops_something_happened"] = "Oops! Algo não está funcionando!";
$_ADDONLANG["error_server_cant_start"] = "O servidor não pode ser iniciado.";
$_ADDONLANG["error_server_cant_stop"] = "O servidor não pode ser parado.";
$_ADDONLANG["error_server_cant_deleted"] = "O servidor não pode ser apagado.";
$_ADDONLANG["error_no_servers"] = "Nenhum servidor foi encontrado com esse IP!";
$_ADDONLANG["error_no_zones"] = "Não foram encontradas zonas com esse IP!";
$_ADDONLANG["success_start"] = "O Servidor foi iniciado.";
$_ADDONLANG["success_stop"] = "O Servidor foi parado.";
$_ADDONLANG["success_deleted"] = "O Servidor foi apagado.";
$_ADDONLANG["edit_zone"] = "Editar Zona";
$_ADDONLANG["zone_exclusion_confirm"] = "Confirmação de exclusão da zona";
$_ADDONLANG["zone_delete_info"] = "Se você realmente deseja excluir a zona";
$_ADDONLANG["zone_delete_info_1"] = "Deseja excluir esta zona?";
$_ADDONLANG["setting_info"] = "As alterações feitas são aplicadas apenas aos novos servidores, os atuais permanecem com as configurações já definidas.";
$_ADDONLANG["alert_congratulations_everything_worked"] = "Parabéns! Tudo esta Funcionando!"
?>

View File

@@ -0,0 +1,110 @@
<?php
$_ADDONLANG["general"] = "Geral";
$_ADDONLANG["settings"] = "Definições";
$_ADDONLANG["tokens"] = "Token";
$_ADDONLANG["bans"] = "Bans";
$_ADDONLANG["backups"] = "Backup";
$_ADDONLANG["status"] = "Status";
$_ADDONLANG["commands"] = "Opções";
$_ADDONLANG["ip_address"] = "IP-Adress";
$_ADDONLANG["port"] = "Porta";
$_ADDONLANG["slot"] = "Slots";
$_ADDONLANG["uptime"] = "Tempo de atividade";
$_ADDONLANG["edit"] = "Editar";
$_ADDONLANG["destination"] = "Destino";
$_ADDONLANG["password_protected"] = "Protegido por password";
$_ADDONLANG["online"] = "Ligado";
$_ADDONLANG["offline"] = "Desligado";
$_ADDONLANG["hostname"] = "Hostname";
$_ADDONLANG["min_port"] = "Min Porta";
$_ADDONLANG["max_port"] = "Max Porta";
$_ADDONLANG["welcome_message"] = "Mensagem de boas-vindas";
$_ADDONLANG["welcome_message_info"] = "É exibido no chat quando um utilizador entra no servidor";
$_ADDONLANG["join_password"] = "Password de Entrada";
$_ADDONLANG["create"] = "Criar";
$_ADDONLANG["add"] = "Adicionar";
$_ADDONLANG["start"] = "Iniciar";
$_ADDONLANG["stop"] = "Parar";
$_ADDONLANG["close"] = "Fechar";
$_ADDONLANG["to_save"] = "para guardar";
$_ADDONLANG["reinstall"] = "Reinstalar";
$_ADDONLANG["reinstall_warning"] = "Tens a certeza que queres reinstalar o teu servidor de Tea*Speak?";
$_ADDONLANG["reset_warning"] = "Tu realmente queres resetar as configurações de serviço?";
$_ADDONLANG["reboot"] = "Reiniciar";
$_ADDONLANG["reset_permissions"] = "Resetar permissões";
$_ADDONLANG["update"] = "Atualizar";
$_ADDONLANG["restore"] = "Restaurar";
$_ADDONLANG["delete"] = "Apagar";
$_ADDONLANG["token_id"] = "Token ID";
$_ADDONLANG[ "description"] = "Description";
$_ADDONLANG["name"] = "Nome";
$_ADDONLANG["error"] = "Erro";
$_ADDONLANG["success"] = "Sucesso";
$_ADDONLANG["uid"] = "ID de Utilizador";
$_ADDONLANG["date"] = "Date";
$_ADDONLANG["no"] = "Não";
$_ADDONLANG["yes"] = "Sim";
$_ADDONLANG["zone"] = "Zona";
$_ADDONLANG["service_management"] = "Service Management";
$_ADDONLANG["logs"] = "Logs";
$_ADDONLANG["html_viewer"] = "Visão de HTML";
$_ADDONLANG["viewer_description"] = "Copie este código HTML e cole ele para exibir o Tea*Speak Viewer no seu site.";
$_ADDONLANG["globaly_instance_settings"] = "Configurações de instância global";
$_ADDONLANG["servers"] = "Servidor";
$_ADDONLANG["servers_name"] = "Nome do Servidor";
$_ADDONLANG["servers_name_info"] = "Será o nome do servidor padrão (deixe em branco por padrão)";
$_ADDONLANG["servers_message"] = "Mensagem do Servidor";
$_ADDONLANG["servers_button"] = "Botão do Servidor";
$_ADDONLANG["servers_banner"] = "Banner do Servidor";
$_ADDONLANG["servers_button_image_url"] = "URL da imagem do botão";
$_ADDONLANG["servers_banner_link_url"] = "URL do botão";
$_ADDONLANG["servers_banner_message_tooltip"] = "Mensagem do botão na dica de ferramenta";
$_ADDONLANG["servers_banner_image_url"] = "Imagem URL";
$_ADDONLANG["servers_banner_display_mode"] = "Modo de exibição do banner";
$_ADDONLANG["servers_banner_display_mode_not_resize"] = "Não ajustar";
$_ADDONLANG["servers_banner_display_mode_ignoring_aspect"] = "Ajustar, mas ignorar a taxa de proporção";
$_ADDONLANG["servers_banner_display_mode_maintaining_appearance"] = "Ajustar e manter taxa de proporção";
$_ADDONLANG["servers_message_display_mode"] = "Modo de exibição de mensagens do servidor";
$_ADDONLANG["servers_message_display_mode_not_shown"] = "Não mostrar mensagem";
$_ADDONLANG["servers_message_display_mode_in_chat"] = "Mostra uma mensagem no chat";
$_ADDONLANG["servers_message_display_in_modal"] = "Mostrar mensagem em uma janela";
$_ADDONLANG["servers_message_display_in_modal_and_leave"] = "Exiba a mensagem em uma janela e sai do servidor";
$_ADDONLANG["servers_message_info"] = "Esta mensagem é exibida quando o usuário entra no servidor (deixe em branco para que não seja exibido).";
$_ADDONLANG["servers_file_transfer_upload_quota"] = "Cota de Upload";
$_ADDONLANG["servers_file_transfer_download_quota"] = "Cota de Download";
$_ADDONLANG["servers_file_transfer"] = "Transferência de Arquivos";
$_ADDONLANG["servers_file_transfer_max_upload_speed"] = "Velocidade máxima de upload";
$_ADDONLANG["servers_file_transfer_max_download_speed"] = "Velocidade máxima de download";
$_ADDONLANG["teamspeak_dns"] = "Tea*Speak DNS";
$_ADDONLANG["teamspeak_dns_enable"] = "TSDNS Ativado";
$_ADDONLANG["teamspeak_dns_domain"] = "TSDNS Domínio ";
$_ADDONLANG["teamspeak_dns_domain_info"] = "Examplo: example.com";
$_ADDONLANG["teamspeak_dns_adress"] = "TSDNS Endreço";
$_ADDONLANG["teamspeak_dns_adress_info"] = "Exemplo: http://1.2.3.4:3000";
$_ADDONLANG["teamspeak_dns_api_key"] = "Chave de API TSDNS";
$_ADDONLANG["manage"] = "Gerir";
$_ADDONLANG["back_home"] = "Voltar à página inicial";
$_ADDONLANG["checking_servers"] = "Verificando o status do servidor";
$_ADDONLANG["installed_version"] = "Versão instalada";
$_ADDONLANG["new_version"] = "Nova versão";
$_ADDONLANG["new_version_aviable"] = "Nova versão disponível!";
$_ADDONLANG["no_new_version_aviable"] = "Estás a usar a última versão.";
$_ADDONLANG["alert_ops_something_happened"] = "Oops! Something is not working!";
$_ADDONLANG["error_server_cant_start"] = "O servidor não pode ser iniciado.";
$_ADDONLANG["error_server_cant_stop"] = "O servidor não pode ser parado.";
$_ADDONLANG["error_server_cant_deleted"] = "O servidor não pode ser apagado.";
$_ADDONLANG["error_no_servers"] = "Nenhum servidor foi encontrado com esse IP!";
$_ADDONLANG["error_no_zones"] = "Não foram encontradas zonas com esse IP!";
$_ADDONLANG["success_start"] = "O Servidor foi iniciado.";
$_ADDONLANG["success_stop"] = "O Servidor foi parado.";
$_ADDONLANG["success_deleted"] = "O Servidor foi apagado.";
$_ADDONLANG["edit_zone"] = "Editar Zona";
$_ADDONLANG["zone_exclusion_confirm"] = "Confirmação de exclusão da zona";
$_ADDONLANG["zone_delete_info"] = "Se você realmente deseja excluir a zona";
$_ADDONLANG["zone_delete_info_1"] = "Deseja excluir esta zona?";
$_ADDONLANG["setting_info"] = "As alterações feitas são aplicadas apenas aos novos servidores, os atuais permanecem com as configurações já definidas.";
$_ADDONLANG["alert_congratulations_everything_worked"] = "Parabéns! Tudo funcionou!"
?>

View File

@@ -0,0 +1,110 @@
<?php
$_ADDONLANG["general"] = "General";
$_ADDONLANG["settings"] = "Definiciones";
$_ADDONLANG["tokens"] = "Llaves de privilegio";
$_ADDONLANG["bans"] = "Prohibiciones";
$_ADDONLANG["backups"] = "Apoyo";
$_ADDONLANG["status"] = "Estado";
$_ADDONLANG["commands"] = "Opciones";
$_ADDONLANG["ip_address"] = "Dirección de IP";
$_ADDONLANG["port"] = "Puerta";
$_ADDONLANG["slot"] = "Slots";
$_ADDONLANG["uptime"] = "Tiempo de actividad";
$_ADDONLANG["edit"] = "Editar";
$_ADDONLANG["destination"] = "Destino";
$_ADDONLANG["password_protected"] = "Contraseña protegida";
$_ADDONLANG["online"] = "Encendido";
$_ADDONLANG["offline"] = "Apagado";
$_ADDONLANG["hostname"] = "Hostname";
$_ADDONLANG["min_port"] = "puerta mínima";
$_ADDONLANG["max_port"] = "puerta Maxima";
$_ADDONLANG["welcome_message"] = "Mensaje de bienvenida";
$_ADDONLANG["welcome_message_info"] = "Se muestra en el chat cuando un usuario ingresa al servidor";
$_ADDONLANG["join_password"] = "Contraseña de inicio de sesión";
$_ADDONLANG["create"] = "Crear";
$_ADDONLANG["add"] = "Añadir";
$_ADDONLANG["start"] = "Comienzo";
$_ADDONLANG["stop"] = "Detener";
$_ADDONLANG["close"] = "Cerca";
$_ADDONLANG["to_save"] = "ahorrar";
$_ADDONLANG["reinstall"] = "Reinstalar";
$_ADDONLANG["reinstall_warning"] = "¿Está seguro de que desea reinstalar su servidor Tea*Speak?";
$_ADDONLANG["reset_warning"] = "¿Realmente desea restablecer la configuración del servicio?";
$_ADDONLANG["reboot"] = "Reiniciar";
$_ADDONLANG["reset_permissions"] = "Restablecer permisos";
$_ADDONLANG["update"] = "Actualizar";
$_ADDONLANG["restore"] = "Restaurar";
$_ADDONLANG["delete"] = "Eliminar";
$_ADDONLANG["token_id"] = "ID de llaves de privilegio";
$_ADDONLANG[ "description"] = "Descripción";
$_ADDONLANG["name"] = "Nombre";
$_ADDONLANG["error"] = "Error";
$_ADDONLANG["success"] = "Éxito";
$_ADDONLANG["uid"] = "ID Usuario";
$_ADDONLANG["date"] = "Fecha";
$_ADDONLANG["no"] = "No";
$_ADDONLANG["yes"] = "Si";
$_ADDONLANG["zone"] = "Zona";
$_ADDONLANG["service_management"] = "Gestionar servicio";
$_ADDONLANG["logs"] = "Logs";
$_ADDONLANG["html_viewer"] = "Vista de HTML";
$_ADDONLANG["viewer_description"] = "Copie este código HTML y péguelo para mostrar el Tea*Speak vista en su sitio web.";
$_ADDONLANG["globaly_instance_settings"] = "Configuración de instancia global";
$_ADDONLANG["servers"] = "Servidor";
$_ADDONLANG["servers_name"] = "Nombre del servidor";
$_ADDONLANG["servers_name_info"] = "Será el nombre del servidor predeterminado (dejar en blanco por defecto)";
$_ADDONLANG["servers_message"] = "Mensaje del servidor";
$_ADDONLANG["servers_button"] = "Botón del servidor";
$_ADDONLANG["servers_banner"] = "Banner del servidor";
$_ADDONLANG["servers_button_image_url"] = "URL la imagen del botón";
$_ADDONLANG["servers_banner_link_url"] = "URL botón";
$_ADDONLANG["servers_banner_message_tooltip"] = "Mensaje del botón de información sobre herramientas";
$_ADDONLANG["servers_banner_image_url"] = "Imagen URL";
$_ADDONLANG["servers_banner_display_mode"] = "Modo de visualización de banner";
$_ADDONLANG["servers_banner_display_mode_not_resize"] = "No ajustar";
$_ADDONLANG["servers_banner_display_mode_ignoring_aspect"] = "Ajustar, pero ignora la relación de aspecto";
$_ADDONLANG["servers_banner_display_mode_maintaining_appearance"] = "Ajustar y mantener la relación de aspecto";
$_ADDONLANG["servers_message_display_mode"] = "Vista de mensaje del servidor";
$_ADDONLANG["servers_message_display_mode_not_shown"] = "No mostrar mensaje";
$_ADDONLANG["servers_message_display_mode_in_chat"] = "Muestra un mensaje en el chat.";
$_ADDONLANG["servers_message_display_in_modal"] = "Mostrar mensaje en una ventana";
$_ADDONLANG["servers_message_display_in_modal_and_leave"] = "Mostrar el mensaje en una ventana y salir del servidor";
$_ADDONLANG["servers_message_info"] = "Este mensaje se muestra cuando el usuario ingresa al servidor (déjelo en blanco para que no se muestre).";
$_ADDONLANG["servers_file_transfer_upload_quota"] = "Subir cuota";
$_ADDONLANG["servers_file_transfer_download_quota"] = "Descargar Cuota";
$_ADDONLANG["servers_file_transfer"] = "Transferencia de archivos";
$_ADDONLANG["servers_file_transfer_max_upload_speed"] = "Velocidad de carga máxima";
$_ADDONLANG["servers_file_transfer_max_download_speed"] = "Velocidad máxima de descarga";
$_ADDONLANG["teamspeak_dns"] = "Tea*Speak DNS";
$_ADDONLANG["teamspeak_dns_enable"] = "TSDNS Activado";
$_ADDONLANG["teamspeak_dns_domain"] = "TSDNS Dominio";
$_ADDONLANG["teamspeak_dns_domain_info"] = "Ejemplo: exemplo.com";
$_ADDONLANG["teamspeak_dns_adress"] = "TSDNS Habla a";
$_ADDONLANG["teamspeak_dns_adress_info"] = "Ejemplo: http://1.2.3.4:3000";
$_ADDONLANG["teamspeak_dns_api_key"] = "Clave de API TSDNS";
$_ADDONLANG["manage"] = "Administrar";
$_ADDONLANG["back_home"] = "Regresar a la página principal";
$_ADDONLANG["checking_servers"] = "Comprobación del estado del servidor";
$_ADDONLANG["installed_version"] = "Versión instalada";
$_ADDONLANG["new_version"] = "Nueva versión";
$_ADDONLANG["new_version_aviable"] = "Nueva versión disponible!";
$_ADDONLANG["no_new_version_aviable"] = "Estás usando la última versión.";
$_ADDONLANG["alert_ops_something_happened"] = "¡Uy! Algo no funciona!";
$_ADDONLANG["error_server_cant_start"] = "El servidor no puede iniciarse.";
$_ADDONLANG["error_server_cant_stop"] = "O servidor não pode ser parado.";
$_ADDONLANG["error_server_cant_deleted"] = "El servidor no se puede eliminar..";
$_ADDONLANG["error_no_servers"] = "No se encontraron servidores con esta IP!";
$_ADDONLANG["error_no_zones"] = "¡No se encontraron zonas con esa IP!";
$_ADDONLANG["success_start"] = "El servidor ha sido iniciado.";
$_ADDONLANG["success_stop"] = "El servidor ha sido detenido.";
$_ADDONLANG["success_deleted"] = "El servidor ha sido eliminado.";
$_ADDONLANG["edit_zone"] = "Editar Zona";
$_ADDONLANG["zone_exclusion_confirm"] = "Confirmación de exclusión de zona";
$_ADDONLANG["zone_delete_info"] = "Si realmente quieres eliminar la zona";
$_ADDONLANG["zone_delete_info_1"] = "¿Quieres eliminar esta zona?";
$_ADDONLANG["setting_info"] = "Los cambios realizados se aplican solo a los nuevos servidores, los actuales permanecen con las configuraciones ya definidas.";
$_ADDONLANG["alert_congratulations_everything_worked"] = "¡Felicidades! ¡Todo funcionó!"
?>

View File

@@ -0,0 +1,50 @@
<?php
require_once __DIR__ . '/../../../../init.php';
use Illuminate\Database\Capsule\Manager as Capsule;
$servers = Capsule::table('tblservers')->where('type', 'teamspeak')->get();
$listservers = array();
foreach ($servers as $server) {
$listservers[$server->name] = array('ip' => $server->ipaddress, 'port' => ($server->port ? $server->port : 10011));
}
if (isset($_GET['host'])) {
$host = $_GET['host'];
if (isset($listservers[$host])) {
header('Content-Type: application/json');
$return = array(
'status' => test($listservers[$host]),
);
echo json_encode($return);
exit;
} else {
header("HTTP/1.1 404 Not Found");
}
}
function test($server)
{
$socket = @fsockopen($server['ip'], $server['port'], $errorNo, $errorStr, 3);
if ($errorNo == 0) {
return true;
} else {
return false;
}
}
function in_array_r($needle, $haystack, $strict = false)
{
foreach ($haystack as $item) {
if (($strict ? $item === $needle : $item == $needle) || (is_array($item) && in_array_r($needle, $item, $strict))) {
return true;
}
}
return false;
}
function secondsToTime($seconds)
{
if ($seconds) {
$dtF = new DateTime("@0");
$dtT = new DateTime("@$seconds");
return $dtF->diff($dtT)->format('%ad %hh %im');
} else {
return null;
}
}

View File

@@ -0,0 +1,701 @@
<?php
if (!defined("WHMCS")) {
die("This file cannot be accessed directly");
}
use Illuminate\Database\Capsule\Manager as Capsule;
include $_SERVER['DOCUMENT_ROOT'].'/modules/servers/teamspeak/lib/TeamSpeak.php';
include '/lib/functions.php';
function teamspeak_config()
{
$configarray = array(
"name" => "TeaSpeak Provisioning",
"description" => "TeaSpeak provisioning Module for TeaSpeak servers",
"version" => "2.1.4",
"author" => "planetteaspeak.de",
"language" => "english",
"fields" => array(
"key" => array ("FriendlyName" => "Licensekey", "Type" => "text", "Size" => "25", "Description" => "Enter your paid Licensekey", "Default" => "Lease-"),
));
return $configarray;
}
function teamspeak_check_license($licensekey, $localkey='') {
// -----------------------------------
// -- Configuration Values --
// -----------------------------------
// Enter the url to your WHMCS installation here
$whmcsurl = 'https://planetteaspeak.de';
// Must match what is specified in the MD5 Hash Verification field
// of the licensing product that will be used with this check.
$licensing_secret_key = 'ab4a3ffa672b4a25dbb6c48571b94655';
// The number of days to wait between performing remote license checks
$localkeydays = 15;
// The number of days to allow failover for after local key expiry
$allowcheckfaildays = 5;
// -----------------------------------
// -- Do not edit below this line --
// -----------------------------------
$check_token = time() . md5(mt_rand(1000000000, 9999999999) . $licensekey);
$checkdate = date("Ymd");
$domain = $_SERVER['SERVER_NAME'];
$usersip = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : $_SERVER['LOCAL_ADDR'];
$dirpath = dirname(__FILE__);
$verifyfilepath = 'modules/servers/licensing/verify.php';
$localkeyvalid = false;
if ($localkey) {
$localkey = str_replace("\n", '', $localkey); # Remove the line breaks
$localdata = substr($localkey, 0, strlen($localkey) - 32); # Extract License Data
$md5hash = substr($localkey, strlen($localkey) - 32); # Extract MD5 Hash
if ($md5hash == md5($localdata . $licensing_secret_key)) {
$localdata = strrev($localdata); # Reverse the string
$md5hash = substr($localdata, 0, 32); # Extract MD5 Hash
$localdata = substr($localdata, 32); # Extract License Data
$localdata = base64_decode($localdata);
$localkeyresults = unserialize($localdata);
$originalcheckdate = $localkeyresults['checkdate'];
if ($md5hash == md5($originalcheckdate . $licensing_secret_key)) {
$localexpiry = date("Ymd", mktime(0, 0, 0, date("m"), date("d") - $localkeydays, date("Y")));
if ($originalcheckdate > $localexpiry) {
$localkeyvalid = true;
$results = $localkeyresults;
$validdomains = explode(',', $results['validdomain']);
if (!in_array($_SERVER['SERVER_NAME'], $validdomains)) {
$localkeyvalid = false;
$localkeyresults['status'] = "Invalid";
$results = array();
}
$validips = explode(',', $results['validip']);
if (!in_array($usersip, $validips)) {
$localkeyvalid = false;
$localkeyresults['status'] = "Invalid";
$results = array();
}
$validdirs = explode(',', $results['validdirectory']);
if (!in_array($dirpath, $validdirs)) {
$localkeyvalid = false;
$localkeyresults['status'] = "Invalid";
$results = array();
}
}
}
}
}
if (!$localkeyvalid) {
$responseCode = 0;
$postfields = array(
'licensekey' => $licensekey,
'domain' => $domain,
'ip' => $usersip,
'dir' => $dirpath,
);
if ($check_token) $postfields['check_token'] = $check_token;
$query_string = '';
foreach ($postfields AS $k=>$v) {
$query_string .= $k.'='.urlencode($v).'&';
}
if (function_exists('curl_exec')) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $whmcsurl . $verifyfilepath);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $query_string);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$data = curl_exec($ch);
$responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
} else {
$responseCodePattern = '/^HTTP\/\d+\.\d+\s+(\d+)/';
$fp = @fsockopen($whmcsurl, 80, $errno, $errstr, 5);
if ($fp) {
$newlinefeed = "\r\n";
$header = "POST ".$whmcsurl . $verifyfilepath . " HTTP/1.0" . $newlinefeed;
$header .= "Host: ".$whmcsurl . $newlinefeed;
$header .= "Content-type: application/x-www-form-urlencoded" . $newlinefeed;
$header .= "Content-length: ".@strlen($query_string) . $newlinefeed;
$header .= "Connection: close" . $newlinefeed . $newlinefeed;
$header .= $query_string;
$data = $line = '';
@stream_set_timeout($fp, 20);
@fputs($fp, $header);
$status = @socket_get_status($fp);
while (!@feof($fp)&&$status) {
$line = @fgets($fp, 1024);
$patternMatches = array();
if (!$responseCode
&& preg_match($responseCodePattern, trim($line), $patternMatches)
) {
$responseCode = (empty($patternMatches[1])) ? 0 : $patternMatches[1];
}
$data .= $line;
$status = @socket_get_status($fp);
}
@fclose ($fp);
}
}
if ($responseCode != 200) {
$localexpiry = date("Ymd", mktime(0, 0, 0, date("m"), date("d") - ($localkeydays + $allowcheckfaildays), date("Y")));
if ($originalcheckdate > $localexpiry) {
$results = $localkeyresults;
} else {
$results = array();
$results['status'] = "Invalid";
$results['description'] = "Remote Check Failed";
return $results;
}
} else {
preg_match_all('/<(.*?)>([^<]+)<\/\\1>/i', $data, $matches);
$results = array();
foreach ($matches[1] AS $k=>$v) {
$results[$v] = $matches[2][$k];
}
}
if (!is_array($results)) {
die("Invalid License Server Response");
}
if ($results['md5hash']) {
if ($results['md5hash'] != md5($licensing_secret_key . $check_token)) {
$results['status'] = "Invalid";
$results['description'] = "MD5 Checksum Verification Failed";
return $results;
}
}
if ($results['status'] == "Active") {
$results['checkdate'] = $checkdate;
$data_encoded = serialize($results);
$data_encoded = base64_encode($data_encoded);
$data_encoded = md5($checkdate . $licensing_secret_key) . $data_encoded;
$data_encoded = strrev($data_encoded);
$data_encoded = $data_encoded . md5($data_encoded . $licensing_secret_key);
$data_encoded = wordwrap($data_encoded, 80, "\n", true);
$results['localkey'] = $data_encoded;
}
$results['remotecheck'] = true;
}
unset($postfields,$data,$matches,$whmcsurl,$licensing_secret_key,$checkdate,$usersip,$localkeydays,$allowcheckfaildays,$md5hash);
return $results;
}
function teamspeak_activate()
{
try {
if (!Capsule::schema()->hasTable('modwhmcs_teamspeak_backups')) {
Capsule::schema()->create('modwhmcs_teamspeak_backups', function ($table) {
$table->increments('id');
$table->integer('sid')->nullable();
$table->integer('port');
$table->mediumText('data')->nullable();
$table->timestamp('date')->nullable();
});
}
if (!Capsule::schema()->hasTable('modwhmcs_teamspeak_settings')) {
Capsule::schema()->create('modwhmcs_teamspeak_settings', function ($table) {
$table->increments('id');
$table->mediumInteger('minport');
$table->mediumInteger('maxport');
$table->mediumText('servername')->nullable();
$table->longText('servermsgwelcome')->nullable();
$table->longText('servermsg')->nullable();
$table->integer('servermsgmode');
$table->longText('bannerlinkurl')->nullable();
$table->longText('bannerimgurl')->nullable();
$table->integer('bannermode');
$table->longText('buttonlinkurl')->nullable();
$table->longText('buttonimgurl')->nullable();
$table->longText('buttonmsgtooltip')->nullable();
$table->bigInteger('uploadquota');
$table->bigInteger('downloadquota');
$table->bigInteger('uploadbandwidth');
$table->bigInteger('downloadbandwidth');
$table->integer('enabletsdns');
$table->longText('domaintsdns')->nullable();
$table->longText('urlapi')->nullable();
$table->longText('keyapi')->nullable();
});
Capsule::table('modwhmcs_teamspeak_settings')->insert(array('minport' => 9987, 'maxport' => 999999, 'servername' => 'TeaSpeak Server by Voice4You.org', 'servermsgwelcome' => 'Welcome to Tea(m)Speak, check [URL]teaspeak.de[/URL] for latest information', 'servermsgmode' => 0, 'bannermode' => 0, 'uploadquota' => -1, 'downloadquota' => -1, 'uploadbandwidth' => -1, 'downloadbandwidth' => -1, 'enabletsdns' => 0));
}
} catch (Exception $e) {
return array('status' => 'error', 'description' => 'It was not possible to create the table. Check your database');
}
return array('status' => 'success');
}
function teamspeak_deactivate()
{
try {
Capsule::schema()->dropIfExists('modwhmcs_teamspeak_settings');
} catch (Exception $e) {
return array('status' => 'error', 'description' => 'The table could not be deleted. Check your database');
}
return array('status' => 'success');
}
function teamspeak_output($vars)
{
$license = Capsule::table('tbladdonmodules')->where('module', 'teamspeak')->where('setting', 'license')->value('value');
$listServers = Capsule::table('tblservers')->where('type', 'teamspeak')->get();
$servers = array();
foreach ($listServers as $key => $server) {
$servers[$key] = new stdClass();
$servers[$key]->name = $server->name;
$servers[$key]->ipaddress = $server->ipaddress;
$servers[$key]->port = ($server->port ? $server->port : 10101);
$servers[$key]->username = $server->username;
$servers[$key]->password = $server->password;
}
$alert = array();
$names = array();
foreach ($servers as $server) {
$names[$server->name] = md5($server->name);
}
$licensekey = $vars['key'];
$localkey = '';
// Validate the license key information
$results = teamspeak_check_license($licensekey, $localkey);
// Raw output of results for debugging purpose
//echo '<textarea cols="100" rows="20">' . print_r($results, true) . '</textarea>';
// Interpret response
switch ($results['status']) {
case "Active":
// get new local key and save it somewhere
$localkeydata = $results['localkey'];
break;
case "Invalid":
# die("License key is Invalid");
#break;
case "Expired":
#die("License key is Expired");
#break;
case "Suspended":
# die("License key is Suspended");
#break;
default:
#die("Invalid Response");
#break;
}
$output = '';
if (!isset($_GET['display'])) {
$output .= "<script src='../modules/addons/teamspeak/js/script.js'></script><script type=\"text/javascript\">
$(document).ready(function () {
var servers = " . json_encode($names) . ";
var server, hash;
for (var key in servers) {
server = key;
hash = servers[key];
test(server, hash);
(function loop(server, hash) {
setTimeout(function () {
test(server, hash);
loop(server, hash);
}, 10000);
})(server, hash);
}
});
</script>";
}
if (isset($_POST['action']) && $_POST['action'] == 'savesettings') {
try {
if (!Capsule::table('modwhmcs_teamspeak_settings')->where('id', 1)->update(array('minport' => $_POST['minport'], 'maxport' => $_POST['maxport'], 'servername' => $_POST['servername'], 'servermsgwelcome' => $_POST['servermsgwelcome'], 'servermsg' => $_POST['servermsg'], 'servermsgmode' => $_POST['servermsgmode'], 'bannerlinkurl' => $_POST['bannerlinkurl'], 'bannerimgurl' => $_POST['bannerimgurl'], 'bannermode' => $_POST['bannermode'], 'buttonlinkurl' => $_POST['buttonlinkurl'], 'buttonimgurl' => $_POST['buttonimgurl'], 'buttonmsgtooltip' => $_POST['buttonmsgtooltip'], 'uploadquota' => $_POST['uploadquota'], 'downloadquota' => $_POST['downloadquota'], 'uploadbandwidth' => $_POST['uploadbandwidth'], 'downloadbandwidth' => $_POST['downloadbandwidth'], 'enabletsdns' => $_POST['enabletsdns'], 'domaintsdns' => $_POST['domaintsdns'], 'urlapi' => $_POST['urlapi'], 'keyapi' => $_POST['keyapi']))) {
throw new Exception('Settings cannot be saved');
}
} catch (Exception $e) {
$alert['status'] = 'error';
$alert['alert'] = $e->getMessage();
}
$alert['status'] = 'success';
$alert['alert'] = 'The settings have been saved';
}
$update = file_get_contents('https://voice4you.org/updater/teaspeak_version.txt');
if($update != $vars['version']){
$output .= "<link href='../modules/addons/teamspeak/css/style.css' rel='stylesheet'/><div class='panel panel-default teamspeak'><div class='panel-heading'><span class='pull-right verify-status' style='display: none'><i class='fa fa-spinner fa-spin'></i>&nbsp;&nbsp;".$vars['_lang']['checking_servers']."</span></div><div class='panel-body'><div class='row'><div class='col-sm-6'><div class='head'><div class='icon'><i class='fa fa-cogs'></i></div><div class='head-name'>TeaSpeak Provisioning&nbsp;&nbsp;</div></div></div><div class='col-sm-12'><div class='row version-status'><div class='col-sm-4'><div class='panel panel-info'><div class='panel-heading text-center'>".$vars['_lang']['installed_version']."</div><div class='panel-body text-center'><strong class='text-info'>{$vars['version']}</strong></div></div></div><div class='row update-status'><div class='col-sm-4'><div class='panel panel-info'><div class='panel-heading text-center'>".$vars['_lang']['new_version']."</div><div class='panel-body text-center'>".$vars['_lang']['new_version_aviable']."</div></div></div>";
}else{
$output .= "<link href='../modules/addons/teamspeak/css/style.css' rel='stylesheet'/><div class='panel panel-default teamspeak'><div class='panel-heading'><span class='pull-right verify-status' style='display: none'><i class='fa fa-spinner fa-spin'></i>&nbsp;&nbsp;".$vars['_lang']['checking_servers']."</span></div><div class='panel-body'><div class='row'><div class='col-sm-6'><div class='head'><div class='icon'><i class='fa fa-cogs'></i></div><div class='head-name'>TeaSpeak Provisioning&nbsp;&nbsp;</div></div></div><div class='col-sm-12'><div class='row version-status'><div class='col-sm-4'><div class='panel panel-info'><div class='panel-heading text-center'>".$vars['_lang']['installed_version']."</div><div class='panel-body text-center'><strong class='text-info'>{$vars['version']}</strong></div></div></div><div class='row update-status'><div class='col-sm-4'><div class='panel panel-info'><div class='panel-heading text-center'>".$vars['_lang']['new_version']."</div><div class='panel-body text-center'>".$vars['_lang']['no_new_version_aviable']."</div></div></div>";
}
if (!isset($_GET['display'])) {
$output .= "";
$output .= "<div class='row'><div class='col-sm-12'><ul class=\"nav nav-tabs\" role=\"tablist\"><li role=\"presentation\" " . ($_POST['action'] == 'savesettings' ? '' : 'class="active"') . "><a href=\"#servers\" aria-controls=\"servers\" role=\"tab\" data-toggle=\"tab\">".$vars['_lang']['servers']."</a></li><li role=\"presentation\" " . (!$_POST['action'] == 'savesettings' ? '' : 'class="active"') . "><a href=\"#settings\" aria-controls=\"settings\" role=\"tab\" data-toggle=\"tab\">".$vars['_lang']['settings']."</a></li></ul><div class=\"tab-content\"><div role=\"tabpanel\" class=\"tab-pane" . ($_POST['action'] == 'savesettings' ? '' : ' active') . "\" id=\"servers\"><div class=\"table-responsive\"><table class='table table-condensed'><thead><tr><th class='text-center'>#</th><th>".$vars['_lang']['servers_name']."</th><th>".$vars['_lang']['ip_address']."</th><th>ServerQuery Port</th><th><i class='fa fa-cogs'></i>&nbsp; ".$vars['_lang']['manage']." </th></tr></thead><tbody>";
$i = 1;
foreach ($servers as $server) {
$output .= "<tr id='" . md5($server->name) . "'><td class='text-center'>{$i}</td>";
$output .= "<td>{$server->name}</td>";
$output .= "<td>{$server->ipaddress}</td>";
$output .= "<td>{$server->port}</td>";
$output .= "<td><span class='manager show invisible'><a href='addonmodules.php?module=teamspeak&amp;display=virtualservers&amp;serverip={$server->ipaddress}' class='btn btn-primary btn-xs'><i class='fa fa-server'></i>&nbsp; TeaSpeak</a>&nbsp;<a href='addonmodules.php?module=teamspeak&amp;display=tsdns&amp;serverip={$server->ipaddress}' class='btn btn-danger btn-xs'><i class='fa fa-link'></i>&nbsp; TSDNS</a></span></td></tr>";
$i++;
}
$output .= "</tbody></table></div></div><div role=\"tabpanel\" class=\"tab-pane" . (!$_POST['action'] == 'savesettings' ? '' : ' active') . "\" id=\"settings\"><div class='alert alert-info'>".$vars['_lang']['setting_info']."<i class='fa fa-info fa-2x pull-left' style='padding:5px'></i></div>";
$settings = Capsule::table('modwhmcs_teamspeak_settings')->first();
switch ($settings->servermsgmode) {
case 1:
$selectm1 = 'selected';
break;
case 2:
$selectm2 = 'selected';
break;
case 3:
$selectm3 = 'selected';
break;
default:
$selectm0 = 'selected';
break;
}
switch ($settings->bannermode) {
case 1:
$selectb1 = 'selected';
break;
case 2:
$selectb2 = 'selected';
break;
default:
$selectb0 = 'selected';
break;
}
if ($settings->enabletsdns) {
$check1 = 'checked';
} else {
$check0 = 'checked';
}
if (!empty($alert)) {
$output .= "<div class='row'><div class='col-md-6 col-md-offset-3'>";
if ($alert['status'] == 'error') {
$output .= "<div class='alert alert-danger'><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button><p><strong><i class='fa fa-frown-o fa-4x pull-left'></i>".$vars['_lang']['alert_ops_something_happened']."</strong></p>{$alert['alert']}</div>";
} else {
$output .= "<div class='alert alert-success'><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button><p><strong><i class='fa fa-smile-o fa-4x pull-left'></i>".$vars['_lang']['alert_congratulations_everything_worked']."</strong></p>{$alert['alert']}</div>";
}
$output .= "</div></div>";
}
$output .= "<form class=\"form-horizontal\" method='post' action='addonmodules.php?module=teamspeak'>
<input type='hidden' name='module' value='teamspeak'>
<input type='hidden' name='action' value='savesettings'>
<div class='row'>
<div class='col-md-8 col-md-offset-2'>
<fieldset>
<legend class='text-center'>".$vars['_lang']['globaly_instance_settings']."</legend>
<div class=\"form-group\">
<label for=\"minport\" class=\"col-sm-3 control-label\">".$vars['_lang']['min_port']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='minport' id=\"minport\" value='{$settings->minport}'>
</div>
</div>
<div class=\"form-group\">
<label for=\"maxport\" class=\"col-sm-3 control-label\">".$vars['_lang']['max_port']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='maxport' id=\"maxport\" value='{$settings->maxport}'>
</div>
</div>
<div class=\"form-group\">
<label for=\"servername\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_name']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='servername' id=\"servername\" value='{$settings->servername}'>
<span class=\"help-block\">".$vars['_lang']['servers_name_info']."</span>
</div>
</div>
<div class=\"form-group\">
<label for=\"servermsgwelcome\" class=\"col-sm-3 control-label\">".$vars['_lang']['welcome_message']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='servermsgwelcome' id=\"servermsgwelcome\" value='{$settings->servermsgwelcome}'>
<span class='help-block'>".$vars['_lang']['welcome_message_info']."</span>
</div>
</div>
<div class=\"form-group\">
<label for=\"servermsg\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_message']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='servermsg' id=\"servermsg\" value='{$settings->servermsg}'>
<span class='help-block'>".$vars['_lang']['servers_message_info']."</span>
</div>
</div>
<div class=\"form-group\">
<label for=\"servermsgmode\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_message_display_mode']."</label>
<div class=\"col-sm-9\">
<select name='servermsgmode' id=\"servermsgmode\" class=\"form-control\"><option value='0' {$selectm0}>".$vars['_lang']['servers_message_display_mode_not_shown']."</option><option value='1' {$selectm1}>".$vars['_lang']['servers_message_display_mode_in_chat']."</option><option value='2' {$selectm2}>".$vars['_lang']['servers_message_display_in_modal']."</option><option value='3' [$selectm3]>".$vars['_lang']['servers_message_display_in_modal_and_leave']."</option></select>
</div>
</div>
</fieldset>
<fieldset>
<legend class='text-center'>".$vars['_lang']['servers_banner']."</legend>
<div class=\"form-group\">
<label for=\"bannerlinkurl\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_banner_link_url']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='bannerlinkurl' id=\"bannerlinkurl\" value=\"{$settings->bannerlinkurl}\">
</div>
</div>
<div class=\"form-group\">
<label for=\"bannerimgurl\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_banner_image_url']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='bannerimgurl' id=\"bannerimgurl\" value=\"{$settings->bannerimgurl}\">
</div>
</div>
<div class=\"form-group\">
<label for=\"bannermode\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_banner_display_mode']."</label>
<div class=\"col-sm-9\">
<select name='bannermode' id=\"bannermode\" class=\"form-control\"><option value='0' {$selectb0}>".$vars['_lang']['servers_banner_display_mode_not_resize']."</option><option value='1' {$selectb1}>".$vars['_lang']['servers_banner_display_mode_ignoring_aspect']."</option><option value='2' {$selectb2}>".$vars['_lang']['servers_banner_display_mode_maintaining_appearance']."</option></select>
</div>
</div>
</fieldset>
<fieldset>
<legend class='text-center'>".$vars['_lang']['servers_button']."</legend>
<div class=\"form-group\">
<label for=\"buttonlinkurl\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_banner_link_url']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='buttonlinkurl' id=\"buttonlinkurl\" value=\"{$settings->buttonlinkurl}\">
</div>
</div>
<div class=\"form-group\">
<label for=\"buttonimgurl\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_button_image_url']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='buttonimgurl' id=\"buttonimgurl\" value=\"{$settings->buttonimgurl}\">
</div>
</div>
<div class=\"form-group\">
<label for=\"buttonmsgtootlip\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_banner_message_tooltip']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='buttonmsgtooltip' id=\"buttonmsgtooltip\" value=\"{$settings->buttonmsgtooltip}\">
</div>
</div>
</fieldset>
<fieldset>
<legend class='text-center'>".$vars['_lang']['servers_file_transfer']."</legend>
<div class=\"form-group\">
<label for=\"uploadquota\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_file_transfer_upload_quota']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='uploadquota' id=\"uploadquota\" value=\"{$settings->uploadquota}\">
</div>
</div>
<div class=\"form-group\">
<label for=\"downloadquota\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_file_transfer_download_quota']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='downloadquota' id=\"downloadquota\" value=\"{$settings->downloadquota}\">
</div>
</div>
<div class=\"form-group\">
<label for=\"uploadbandwidth\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_file_transfer_max_upload_speed']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='uploadbandwidth' id=\"uploadbandwidth\" value=\"{$settings->uploadbandwidth}\">
</div>
</div>
<div class=\"form-group\">
<label for=\"downloadbandwidth\" class=\"col-sm-3 control-label\">".$vars['_lang']['servers_file_transfer_max_download_speed']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='downloadbandwidth' id=\"downloadbandwidth\" value=\"{$settings->downloadbandwidth}\">
</div>
</div>
</fieldset>
<fieldset>
<legend class='text-center'>".$vars['_lang']['teamspeak_dns']."</legend>
<div class=\"form-group\">
<label class=\"col-sm-3 control-label\">".$vars['_lang']['teamspeak_dns_enable']."</label>
<div class=\"col-sm-9\">
<label class=\"radio-inline\">
<input type=\"radio\" name=\"enabletsdns\" id=\"1\" value=\"1\" {$check1}> ".$vars['_lang']['yes']."
</label>
<label class=\"radio-inline\">
<input type=\"radio\" name=\"enabletsdns\" id=\"0\" value=\"0\" {$check0}> ".$vars['_lang']['no']."
</label>
</div>
</div>
<div class=\"form-group\">
<label for=\"domaintsdns\" class=\"col-sm-3 control-label\">".$vars['_lang']['teamspeak_dns_domain']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='domaintsdns' id=\"domaintsdns\" value=\"{$settings->domaintsdns}\">
<span class=\"help-block\">".$vars['_lang']['teamspeak_dns_domain_info']."</span>
</div>
</div>
<div class=\"form-group\">
<label for=\"urlapi\" class=\"col-sm-3 control-label\">".$vars['_lang']['teamspeak_dns_adress']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='urlapi' id=\"urlapi\" value=\"{$settings->urlapi}\">
<span class=\"help-block\">".$vars['_lang']['teamspeak_dns_adress_info']."</span>
</div>
</div>
<div class=\"form-group\">
<label for=\"keyapi\" class=\"col-sm-3 control-label\">".$vars['_lang']['teamspeak_dns_api_key']."</label>
<div class=\"col-sm-9\">
<input type=\"text\" class=\"form-control\" name='keyapi' id=\"keyapi\" value=\"{$settings->keyapi}\">
</div>
</div>
</fieldset>
<hr>
<div class=\"form-group\">
<div class=\"col-sm-offset-2 col-sm-8\">
<button type=\"submit\" class=\"btn btn-default center-block\"><i class='fa fa-floppy-o'></i>&nbsp;&nbsp;Save</button>
</div>
</div>
</div>
</div>
</form></div></div></div></div>";
} elseif (isset($_GET['display'])) {
switch ($_GET['display']) {
case 'virtualservers':
$serververif = '';
foreach ($servers as $server) {
if ($server->ipaddress == $_GET['serverip']) {
$command = "decryptpassword";
$values["password2"] = $server->password;
$results1 = localAPI($command, $values);
$server->password = $results1['password'];
$serververif = $server;
}
}
$tsAdmin = new TeamSpeak($serververif->ipaddress, $serververif->port);
$tsAdmin->connect();
$tsAdmin->login($serververif->username, $serververif->password);
if (isset($_GET['action'], $_GET['sid'])) {
switch ($_GET['action']) {
case 'start':
if (!$tsAdmin->getElement('success', $status = $tsAdmin->serverStart($_GET['sid']))) {
$alert['status'] = "".$vars['_lang']['error']."";
$alert['alert'] = "".$vars['_lang']['error_server_cant_start']."";
} else {
$alert['status'] = "".$vars['_lang']['success']."";
$alert['alert'] = "".$vars['_lang']['success_start']."";
}
break;
case 'stop':
if (!$tsAdmin->getElement('success', $status = $tsAdmin->serverStop($_GET['sid']))) {
$alert['status'] = "".$vars['_lang']['error']."";
$alert['alert'] = "".$vars['_lang']['error_server_cant_stop']."";
} else {
$alert['status'] = "".$vars['_lang']['success']."";
$alert['alert'] = "".$vars['_lang']['success_stop']."";
}
break;
case 'delete':
if (!$tsAdmin->getElement('success', $status = $tsAdmin->serverDelete($_GET['sid']))) {
$alert['status'] = "".$vars['_lang']['error']."";
$alert['alert'] = "".$vars['_lang']['error_server_cant_deleted']."";
} else {
$alert['status'] = "".$vars['_lang']['success']."";
$alert['alert'] = "".$vars['_lang']['success_deleted']."";
}
break;
break;
default :
break;
}
if (!empty($alert)) {
$output .= "<div class='row'><div class='col-md-6 col-md-offset-3'>";
if ($alert['status'] == 'error') {
$output .= "<div class='alert alert-danger'><a href='addonmodules.php?module=teamspeak&amp;display=virtualservers&amp;serverip={$_GET['serverip']}' class='btn btn-danger pull-right'><i class='fa fa-arrow-left'></i>&nbsp;&nbsp;".$vars['_lang']['back_home']."</a><p><strong><i class='fa fa-frown-o fa-4x pull-left'></i>".$vars['_lang']['alert_ops_something_happened']."</strong></p>{$alert['alert']}</div>";
} else {
$output .= "<div class='alert alert-success'><a href='addonmodules.php?module=teamspeak&amp;display=virtualservers&amp;serverip={$_GET['serverip']}' class='btn btn-success pull-right'><i class='fa fa-arrow-left'></i>&nbsp;&nbsp;".$vars['_lang']['back_home']."</a><p><strong><i class='fa fa-smile-o fa-4x pull-left'></i>".$vars['_lang']['alert_congratulations_everything_worked']."</strong></p>{$alert['alert']}</div>";
}
$output .= "</div></div>";
}
} else {
$serverlist = $tsAdmin->serverList();
$output .= "<a href='addonmodules.php?module=teamspeak' class='btn btn-info pull-right'><i class='fa fa-arrow-left'></i>&nbsp;&nbsp;".$vars['_lang']['back_home']."</a><div class='clearfix'></div><br><div class='table-responsive'><table class='table'><thead><tr><th>#</th><th><i class='fa fa-server'></i>&nbsp;&nbsp;".$vars['_lang']['servers_name']."</th><th><i class='fa fa-plug'></i>&nbsp;&nbsp;".$vars['_lang']['port']."</th><th><i class='fa fa-users'></i>&nbsp;&nbsp;".$vars['_lang']['slot']."</th><th><i class='fa fa-bar-chart'></i>&nbsp;&nbsp;".$vars['_lang']['status']."</th><th><i class='fa fa-clock-o'></i>&nbsp;&nbsp;".$vars['_lang']['uptime']."</th><th><i class='fa fa-cogs'></i>&nbsp;&nbsp;".$vars['_lang']['manage']."</th></tr></thead><tbody>";
if ($serverlist['data']) {
foreach ($serverlist['data'] as $server) {
$output .= "<tr><td>{$server['virtualserver_id']}</td><td>{$server['virtualserver_name']}</td><td>{$server['virtualserver_port']}</td><td>{$server['virtualserver_maxclients']}</td><td>" . ucfirst($server['virtualserver_status']) . "</td><td>" . $server['virtualserver_uptime'] . "</td><td>";
if ($server['virtualserver_status'] != 'online') {
$output .= "<a href='addonmodules.php?module=teamspeak&amp;display=virtualservers&amp;serverip={$serververif->ipaddress}&amp;action=start&amp;sid={$server['virtualserver_id']}' class='btn btn-success btn-xs'><i class='fa fa-play'></i>&nbsp;&nbsp;".$vars['_lang']['start']."</a>";
} else {
$output .= "<a href='addonmodules.php?module=teamspeak&amp;display=virtualservers&amp;serverip={$serververif->ipaddress}&amp;action=stop&amp;sid={$server['virtualserver_id']}' class='btn btn-warning btn-xs'><i class='fa fa-stop'></i>&nbsp;&nbsp;".$vars['_lang']['stop']."</a>";
}
$output .= "&nbsp;<a href='addonmodules.php?module=teamspeak&amp;display=virtualservers&amp;serverip={$serververif->ipaddress}&amp;action=delete&amp;sid={$server['virtualserver_id']}' class='btn btn-danger btn-xs'><i class='fa fa-times'></i>&nbsp;&nbsp;".$vars['_lang']['delete']."</a>";
$output .= "</td></tr>";
}
} else {
$output .= "<tr><td colspan='4' class='text-center'>".$vars['_lang']['error_no_servers']."</td></tr>";
}
$output .= "</table></div>";
}
break;
case 'tsdns':
$settings = Capsule::table('modwhmcs_teamspeak_settings')->select('urlapi', 'keyapi')->first();
try {
$tsdnsClient = new TSDNS($settings->urlapi, $settings->keyapi);
$request = $tsdnsClient->getZones();
$serverstsdns = json_decode($request->body);
$serverscurrent = array();
foreach ($serverstsdns->message as $server) {
$serverip = explode(':', $server->target);
if ($serverip[0] == $_GET['serverip']) {
$serverscurrent[] = $server;
}
}
if (isset($_GET['action'], $_GET['zone'])) {
switch ($_GET['action']) {
case 'editzone' :
foreach ($serverscurrent as $server) {
if ($server->zone == $_GET['zone']) {
throw new Exception('There is informed zone already exists.');
}
}
$tsdnsClient->deleteZone($_GET['oldzone']);
$result = $tsdnsClient->addZone($_GET['zone'], $_GET['target']);
if (!$result->success) {
throw new Exception('The zone could not be edited.');
} else {
$alert['status'] = 'success';
$alert['alert'] = 'Zone has been edited.';
}
break;
case 'delzone' :
$result = $tsdnsClient->deleteZone($_GET['zone']);
if (!$result->success) {
throw new Exception('The zone could not be deleted.');
} else {
$alert['status'] = 'success';
$alert['alert'] = 'Zone has been deleted.';
}
break;
default:
break;
}
} else {
$output .= "<a href='addonmodules.php?module=teamspeak' class='btn btn-info pull-right'><i class='fa fa-arrow-left'></i>&nbsp;&nbsp;".$vars['_lang']['back_home']."</a><div class='clearfix'></div><br><div class='table-responsive'><table class='table table-condensed'><thead><tr><th>#</th><th><i class='fa fa-globe'></i>&nbsp;".$vars['_lang']['zone']."</th><th><i class='fa fa-dot-circle-o'></i>&nbsp;".$vars['_lang']['destination']."</th><th><i class='fa fa-cogs'></i>&nbsp;".$vars['_lang']['manage']."</th></tr></thead><tbody>";
if ($serverscurrent) {
foreach ($serverscurrent as $server) {
$output .= "<tr><td>{$server->id}</td><td>{$server->zone}</td><td>{$server->target}</td><td><a class=\"btn btn-primary btn-xs\" role=\"button\" data-toggle=\"modal\" data-target=\"#" . md5($server->zone) . "\" aria-expanded=\"false\" aria-controls=\"" . md5($server->zone) . "\"><i class='fa fa-pencil-square-o'></i>&nbsp;".$vars['_lang']['edit']."</a>&nbsp;<a href='' class='btn btn-danger btn-xs' role=\"button\" data-toggle=\"modal\" data-target=\"#del" . md5($server->zone) . "\" aria-expanded=\"false\" aria-controls=\"del" . md5($server->zone) . "\"><i class='fa fa-times'></i>&nbsp;".$vars['_lang']['delete']."</a></td></tr>";
}
} else {
$output .= "<tr><td colspan='4' class='text-center'>".$vars['_lang']['error_no_zones']."</td></tr>";
}
$output .= "</tbody></table></div>";
foreach ($serverscurrent as $server) {
$output .= "<div class='modal fade' id='" . md5($server->zone) . "' tabindex=\"-1\" role=\"dialog\" aria-labelledby=\"" . md5($server->zone) . "Label\"><div class=\"modal-dialog\" role=\"document\"><div class=\"modal-content\"><div class=\"modal-header\"><button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button><h4 class=\"modal-title\" id=\"" . md5($server->zone) . "Label\">".$vars['_lang']['edit_zone']."</h4></div><form method='get' action='addonmodules.php' class='form-horizontal'><input type='hidden' name='module' value='teamspeak'/><input type='hidden' name='display' value='tsdns'/><input type='hidden' name='serverip' value='{$_GET['serverip']}'/><input type='hidden' name='action' value='editzone'/><input type='hidden' name='oldzone' value='{$server->zone}'/><div class=\"modal-body\"><div class='form-group'><label for='zone' class='col-sm-2 control-label'>".$vars['_lang']['zone']."</label><div class='col-sm-5'><input type='text' class='form-control' name='zone' id='zone' value='{$server->zone}'/></div></div><div class='form-group'><label for='target' class='col-sm-2 control-label'>".$vars['_lang']['destination']."</label><div class='col-sm-5'><input type='text' class='form-control' name='target' id='target' value='{$server->target}'/></div></div></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\"><i class='fa fa-times'></i>&nbsp;".$vars['_lang']['close']."</button><button type='submit' class='btn btn-success'><i class='fa fa-floppy-o'></i>&nbsp;".$vars['_lang']['to_save']."</button></div></form></div></div></div>";
}
foreach ($serverscurrent as $server) {
$output .= "<div class='modal fade' id='del" . md5($server->zone) . "' tabindex=\"-1\" role=\"dialog\" aria-labelledby=\"del" . md5($server->zone) . "Label\"><div class=\"modal-dialog\" role=\"document\"><div class=\"modal-content\"><div class=\"modal-header\"><button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button><h4 class=\"modal-title\" id=\"del" . md5($server->zone) . "Label\">".$vars['_lang']['zone_exclusion_confirm']."</h4></div><form method='get' action='addonmodules.php' class='form-horizontal'><input type='hidden' name='module' value='teamspeak'/><input type='hidden' name='display' value='tsdns'/><input type='hidden' name='serverip' value='{$_GET['serverip']}'/><input type='hidden' name='action' value='delzone'/><input type='hidden' name='zone' value='{$server->zone}'/><div class=\"modal-body\"><h2>".$vars['_lang']['zone_delete_info']." \"<strong><u>{$server->zone}</u></strong>\" click \"Delete \"".$vars['_lang']['zone_delete_info_1']."</h2></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\"><i class='fa fa-times'></i>&nbsp;".$vars['_lang']['close']."</button><button type='submit' class='btn btn-danger'><i class='fa fa-trash'></i>&nbsp;".$vars['_lang']['delete']."</button></div></form></div></div></div>";
}
}
} catch (Exception $e) {
$alert['status'] = 'error';
$alert['alert'] .= $e->getMessage();
}
if (!empty($alert)) {
$output .= "<div class='row'><div class='col-md-6 col-md-offset-3'>";
if ($alert['status'] == 'error') {
$output .= "<div class='alert alert-danger'><a href='addonmodules.php?module=teamspeak' class='btn btn-danger pull-right'><i class='fa fa-arrow-left'></i>&nbsp;&nbsp;".$vars['_lang']['back_home']."</a><p><strong><i class='fa fa-frown-o fa-4x pull-left'></i>".$vars['_lang']['alert_ops_something_happened']."</strong></p>{$alert['alert']}</div>";
} else {
$output .= "<div class='alert alert-success'><a href='addonmodules.php?module=teamspeak&amp;display=tsdns&amp;serverip={$_GET['serverip']}' class='btn btn-success pull-right'><i class='fa fa-arrow-left'></i>&nbsp;&nbsp;".$vars['_lang']['back_home']."</a><p><strong><i class='fa fa-smile-o fa-4x pull-left'></i>".$vars['_lang']['alert_congratulations_everything_worked']."</strong></p>{$alert['alert']}</div>";
}
$output .= "</div></div>";
}
break;
}
}
$output .= " </div ></div ></div > ";
echo $output;
}

BIN
TeaSpeak/servers/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,38 @@
<?php
require_once __DIR__ . "/lib/Requests.php";
require_once __DIR__ . "/lib/TSDNS.php";
use Illuminate\Database\Capsule\Manager as Capsule;
function hook_check_subdomain(array $params)
{
try {
$settings = Capsule::table('modwhmcs_teamspeak_settings')->select('enabletsdns', 'domaintsdns', 'urlapi', 'keyapi')->first();
if ($settings->enabletsdns) {
$tsdnsClient = new TSDNS($settings->urlapi, $settings->keyapi);
if (!$tsdnsClient) {
throw new Exception('The subdomains availability could not be checked. Contact support.');
}
$id = Capsule::table('tblcustomfields')->where('fieldname', 'Subdomain')->value('id');
$request = $tsdnsClient->getZone($params['customfield'][$id] . '.' . $settings->domaintsdns);
$zone = json_decode($request->body);
if (count($zone->message)) {
throw new Exception('Subdomain is already in use');
}
}
} catch (Exception $e) {
$errors = array();
$errors[] = $e->getMessage();
return $errors;
}
}
add_hook("ShoppingCartValidateProductUpdate", 1, "hook_check_subdomain");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,980 @@
<?php
/**
* Requests for PHP
*
* Inspired by Requests for Python.
*
* Based on concepts from SimplePie_File, RequestCore and WP_Http.
*
* @package Requests
*/
/**
* Requests for PHP
*
* Inspired by Requests for Python.
*
* Based on concepts from SimplePie_File, RequestCore and WP_Http.
*
* @package Requests
*/
class Requests {
/**
* POST method
*
* @var string
*/
const POST = 'POST';
/**
* PUT method
*
* @var string
*/
const PUT = 'PUT';
/**
* GET method
*
* @var string
*/
const GET = 'GET';
/**
* HEAD method
*
* @var string
*/
const HEAD = 'HEAD';
/**
* DELETE method
*
* @var string
*/
const DELETE = 'DELETE';
/**
* OPTIONS method
*
* @var string
*/
const OPTIONS = 'OPTIONS';
/**
* TRACE method
*
* @var string
*/
const TRACE = 'TRACE';
/**
* PATCH method
*
* @link https://tools.ietf.org/html/rfc5789
* @var string
*/
const PATCH = 'PATCH';
/**
* Default size of buffer size to read streams
*
* @var integer
*/
const BUFFER_SIZE = 1160;
/**
* Current version of Requests
*
* @var string
*/
const VERSION = '1.7';
/**
* Registered transport classes
*
* @var array
*/
protected static $transports = array();
/**
* Selected transport name
*
* Use {@see get_transport()} instead
*
* @var array
*/
public static $transport = array();
/**
* Default certificate path.
*
* @see Requests::get_certificate_path()
* @see Requests::set_certificate_path()
*
* @var string
*/
protected static $certificate_path;
/**
* This is a static class, do not instantiate it
*
* @codeCoverageIgnore
*/
private function __construct() {}
/**
* Autoloader for Requests
*
* Register this with {@see register_autoloader()} if you'd like to avoid
* having to create your own.
*
* (You can also use `spl_autoload_register` directly if you'd prefer.)
*
* @codeCoverageIgnore
*
* @param string $class Class name to load
*/
public static function autoloader($class) {
// Check that the class starts with "Requests"
if (strpos($class, 'Requests') !== 0) {
return;
}
$file = str_replace('_', '/', $class);
if (file_exists(dirname(__FILE__) . '/' . $file . '.php')) {
require_once(dirname(__FILE__) . '/' . $file . '.php');
}
}
/**
* Register the built-in autoloader
*
* @codeCoverageIgnore
*/
public static function register_autoloader() {
spl_autoload_register(array('Requests', 'autoloader'));
}
/**
* Register a transport
*
* @param string $transport Transport class to add, must support the Requests_Transport interface
*/
public static function add_transport($transport) {
if (empty(self::$transports)) {
self::$transports = array(
'Requests_Transport_cURL',
'Requests_Transport_fsockopen',
);
}
self::$transports = array_merge(self::$transports, array($transport));
}
/**
* Get a working transport
*
* @throws Requests_Exception If no valid transport is found (`notransport`)
* @return Requests_Transport
*/
protected static function get_transport($capabilities = array()) {
// Caching code, don't bother testing coverage
// @codeCoverageIgnoreStart
// array of capabilities as a string to be used as an array key
ksort($capabilities);
$cap_string = serialize($capabilities);
// Don't search for a transport if it's already been done for these $capabilities
if (isset(self::$transport[$cap_string]) && self::$transport[$cap_string] !== null) {
return new self::$transport[$cap_string]();
}
// @codeCoverageIgnoreEnd
if (empty(self::$transports)) {
self::$transports = array(
'Requests_Transport_cURL',
'Requests_Transport_fsockopen',
);
}
// Find us a working transport
foreach (self::$transports as $class) {
if (!class_exists($class)) {
continue;
}
$result = call_user_func(array($class, 'test'), $capabilities);
if ($result) {
self::$transport[$cap_string] = $class;
break;
}
}
if (self::$transport[$cap_string] === null) {
throw new Requests_Exception('No working transports found', 'notransport', self::$transports);
}
return new self::$transport[$cap_string]();
}
/**#@+
* @see request()
* @param string $url
* @param array $headers
* @param array $options
* @return Requests_Response
*/
/**
* Send a GET request
*/
public static function get($url, $headers = array(), $options = array()) {
return self::request($url, $headers, null, self::GET, $options);
}
/**
* Send a HEAD request
*/
public static function head($url, $headers = array(), $options = array()) {
return self::request($url, $headers, null, self::HEAD, $options);
}
/**
* Send a DELETE request
*/
public static function delete($url, $headers = array(), $options = array()) {
return self::request($url, $headers, null, self::DELETE, $options);
}
/**
* Send a TRACE request
*/
public static function trace($url, $headers = array(), $options = array()) {
return self::request($url, $headers, null, self::TRACE, $options);
}
/**#@-*/
/**#@+
* @see request()
* @param string $url
* @param array $headers
* @param array $data
* @param array $options
* @return Requests_Response
*/
/**
* Send a POST request
*/
public static function post($url, $headers = array(), $data = array(), $options = array()) {
return self::request($url, $headers, $data, self::POST, $options);
}
/**
* Send a PUT request
*/
public static function put($url, $headers = array(), $data = array(), $options = array()) {
return self::request($url, $headers, $data, self::PUT, $options);
}
/**
* Send an OPTIONS request
*/
public static function options($url, $headers = array(), $data = array(), $options = array()) {
return self::request($url, $headers, $data, self::OPTIONS, $options);
}
/**
* Send a PATCH request
*
* Note: Unlike {@see post} and {@see put}, `$headers` is required, as the
* specification recommends that should send an ETag
*
* @link https://tools.ietf.org/html/rfc5789
*/
public static function patch($url, $headers, $data = array(), $options = array()) {
return self::request($url, $headers, $data, self::PATCH, $options);
}
/**#@-*/
/**
* Main interface for HTTP requests
*
* This method initiates a request and sends it via a transport before
* parsing.
*
* The `$options` parameter takes an associative array with the following
* options:
*
* - `timeout`: How long should we wait for a response?
* Note: for cURL, a minimum of 1 second applies, as DNS resolution
* operates at second-resolution only.
* (float, seconds with a millisecond precision, default: 10, example: 0.01)
* - `connect_timeout`: How long should we wait while trying to connect?
* (float, seconds with a millisecond precision, default: 10, example: 0.01)
* - `useragent`: Useragent to send to the server
* (string, default: php-requests/$version)
* - `follow_redirects`: Should we follow 3xx redirects?
* (boolean, default: true)
* - `redirects`: How many times should we redirect before erroring?
* (integer, default: 10)
* - `blocking`: Should we block processing on this request?
* (boolean, default: true)
* - `filename`: File to stream the body to instead.
* (string|boolean, default: false)
* - `auth`: Authentication handler or array of user/password details to use
* for Basic authentication
* (Requests_Auth|array|boolean, default: false)
* - `proxy`: Proxy details to use for proxy by-passing and authentication
* (Requests_Proxy|array|string|boolean, default: false)
* - `max_bytes`: Limit for the response body size.
* (integer|boolean, default: false)
* - `idn`: Enable IDN parsing
* (boolean, default: true)
* - `transport`: Custom transport. Either a class name, or a
* transport object. Defaults to the first working transport from
* {@see getTransport()}
* (string|Requests_Transport, default: {@see getTransport()})
* - `hooks`: Hooks handler.
* (Requests_Hooker, default: new Requests_Hooks())
* - `verify`: Should we verify SSL certificates? Allows passing in a custom
* certificate file as a string. (Using true uses the system-wide root
* certificate store instead, but this may have different behaviour
* across transports.)
* (string|boolean, default: library/Requests/Transport/cacert.pem)
* - `verifyname`: Should we verify the common name in the SSL certificate?
* (boolean: default, true)
* - `data_format`: How should we send the `$data` parameter?
* (string, one of 'query' or 'body', default: 'query' for
* HEAD/GET/DELETE, 'body' for POST/PUT/OPTIONS/PATCH)
*
* @throws Requests_Exception On invalid URLs (`nonhttp`)
*
* @param string $url URL to request
* @param array $headers Extra headers to send with the request
* @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
* @param string $type HTTP request type (use Requests constants)
* @param array $options Options for the request (see description for more information)
* @return Requests_Response
*/
public static function request($url, $headers = array(), $data = array(), $type = self::GET, $options = array()) {
if (empty($options['type'])) {
$options['type'] = $type;
}
$options = array_merge(self::get_default_options(), $options);
self::set_defaults($url, $headers, $data, $type, $options);
$options['hooks']->dispatch('requests.before_request', array(&$url, &$headers, &$data, &$type, &$options));
if (!empty($options['transport'])) {
$transport = $options['transport'];
if (is_string($options['transport'])) {
$transport = new $transport();
}
}
else {
$need_ssl = (0 === stripos($url, 'https://'));
$capabilities = array('ssl' => $need_ssl);
$transport = self::get_transport($capabilities);
}
$response = $transport->request($url, $headers, $data, $options);
$options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options));
return self::parse_response($response, $url, $headers, $data, $options);
}
/**
* Send multiple HTTP requests simultaneously
*
* The `$requests` parameter takes an associative or indexed array of
* request fields. The key of each request can be used to match up the
* request with the returned data, or with the request passed into your
* `multiple.request.complete` callback.
*
* The request fields value is an associative array with the following keys:
*
* - `url`: Request URL Same as the `$url` parameter to
* {@see Requests::request}
* (string, required)
* - `headers`: Associative array of header fields. Same as the `$headers`
* parameter to {@see Requests::request}
* (array, default: `array()`)
* - `data`: Associative array of data fields or a string. Same as the
* `$data` parameter to {@see Requests::request}
* (array|string, default: `array()`)
* - `type`: HTTP request type (use Requests constants). Same as the `$type`
* parameter to {@see Requests::request}
* (string, default: `Requests::GET`)
* - `cookies`: Associative array of cookie name to value, or cookie jar.
* (array|Requests_Cookie_Jar)
*
* If the `$options` parameter is specified, individual requests will
* inherit options from it. This can be used to use a single hooking system,
* or set all the types to `Requests::POST`, for example.
*
* In addition, the `$options` parameter takes the following global options:
*
* - `complete`: A callback for when a request is complete. Takes two
* parameters, a Requests_Response/Requests_Exception reference, and the
* ID from the request array (Note: this can also be overridden on a
* per-request basis, although that's a little silly)
* (callback)
*
* @param array $requests Requests data (see description for more information)
* @param array $options Global and default options (see {@see Requests::request})
* @return array Responses (either Requests_Response or a Requests_Exception object)
*/
public static function request_multiple($requests, $options = array()) {
$options = array_merge(self::get_default_options(true), $options);
if (!empty($options['hooks'])) {
$options['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple'));
if (!empty($options['complete'])) {
$options['hooks']->register('multiple.request.complete', $options['complete']);
}
}
foreach ($requests as $id => &$request) {
if (!isset($request['headers'])) {
$request['headers'] = array();
}
if (!isset($request['data'])) {
$request['data'] = array();
}
if (!isset($request['type'])) {
$request['type'] = self::GET;
}
if (!isset($request['options'])) {
$request['options'] = $options;
$request['options']['type'] = $request['type'];
}
else {
if (empty($request['options']['type'])) {
$request['options']['type'] = $request['type'];
}
$request['options'] = array_merge($options, $request['options']);
}
self::set_defaults($request['url'], $request['headers'], $request['data'], $request['type'], $request['options']);
// Ensure we only hook in once
if ($request['options']['hooks'] !== $options['hooks']) {
$request['options']['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple'));
if (!empty($request['options']['complete'])) {
$request['options']['hooks']->register('multiple.request.complete', $request['options']['complete']);
}
}
}
unset($request);
if (!empty($options['transport'])) {
$transport = $options['transport'];
if (is_string($options['transport'])) {
$transport = new $transport();
}
}
else {
$transport = self::get_transport();
}
$responses = $transport->request_multiple($requests, $options);
foreach ($responses as $id => &$response) {
// If our hook got messed with somehow, ensure we end up with the
// correct response
if (is_string($response)) {
$request = $requests[$id];
self::parse_multiple($response, $request);
$request['options']['hooks']->dispatch('multiple.request.complete', array(&$response, $id));
}
}
return $responses;
}
/**
* Get the default options
*
* @see Requests::request() for values returned by this method
* @param boolean $multirequest Is this a multirequest?
* @return array Default option values
*/
protected static function get_default_options($multirequest = false) {
$defaults = array(
'timeout' => 10,
'connect_timeout' => 10,
'useragent' => 'php-requests/' . self::VERSION,
'protocol_version' => 1.1,
'redirected' => 0,
'redirects' => 10,
'follow_redirects' => true,
'blocking' => true,
'type' => self::GET,
'filename' => false,
'auth' => false,
'proxy' => false,
'cookies' => false,
'max_bytes' => false,
'idn' => true,
'hooks' => null,
'transport' => null,
'verify' => Requests::get_certificate_path(),
'verifyname' => true,
);
if ($multirequest !== false) {
$defaults['complete'] = null;
}
return $defaults;
}
/**
* Get default certificate path.
*
* @return string Default certificate path.
*/
public static function get_certificate_path() {
if ( ! empty( Requests::$certificate_path ) ) {
return Requests::$certificate_path;
}
return dirname(__FILE__) . '/Requests/Transport/cacert.pem';
}
/**
* Set default certificate path.
*
* @param string $path Certificate path, pointing to a PEM file.
*/
public static function set_certificate_path( $path ) {
Requests::$certificate_path = $path;
}
/**
* Set the default values
*
* @param string $url URL to request
* @param array $headers Extra headers to send with the request
* @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
* @param string $type HTTP request type
* @param array $options Options for the request
* @return array $options
*/
protected static function set_defaults(&$url, &$headers, &$data, &$type, &$options) {
if (!preg_match('/^http(s)?:\/\//i', $url, $matches)) {
throw new Requests_Exception('Only HTTP(S) requests are handled.', 'nonhttp', $url);
}
if (empty($options['hooks'])) {
$options['hooks'] = new Requests_Hooks();
}
if (is_array($options['auth'])) {
$options['auth'] = new Requests_Auth_Basic($options['auth']);
}
if ($options['auth'] !== false) {
$options['auth']->register($options['hooks']);
}
if (is_string($options['proxy']) || is_array($options['proxy'])) {
$options['proxy'] = new Requests_Proxy_HTTP($options['proxy']);
}
if ($options['proxy'] !== false) {
$options['proxy']->register($options['hooks']);
}
if (is_array($options['cookies'])) {
$options['cookies'] = new Requests_Cookie_Jar($options['cookies']);
}
elseif (empty($options['cookies'])) {
$options['cookies'] = new Requests_Cookie_Jar();
}
if ($options['cookies'] !== false) {
$options['cookies']->register($options['hooks']);
}
if ($options['idn'] !== false) {
$iri = new Requests_IRI($url);
$iri->host = Requests_IDNAEncoder::encode($iri->ihost);
$url = $iri->uri;
}
// Massage the type to ensure we support it.
$type = strtoupper($type);
if (!isset($options['data_format'])) {
if (in_array($type, array(self::HEAD, self::GET, self::DELETE))) {
$options['data_format'] = 'query';
}
else {
$options['data_format'] = 'body';
}
}
}
/**
* HTTP response parser
*
* @throws Requests_Exception On missing head/body separator (`requests.no_crlf_separator`)
* @throws Requests_Exception On missing head/body separator (`noversion`)
* @throws Requests_Exception On missing head/body separator (`toomanyredirects`)
*
* @param string $headers Full response text including headers and body
* @param string $url Original request URL
* @param array $req_headers Original $headers array passed to {@link request()}, in case we need to follow redirects
* @param array $req_data Original $data array passed to {@link request()}, in case we need to follow redirects
* @param array $options Original $options array passed to {@link request()}, in case we need to follow redirects
* @return Requests_Response
*/
protected static function parse_response($headers, $url, $req_headers, $req_data, $options) {
$return = new Requests_Response();
if (!$options['blocking']) {
return $return;
}
$return->raw = $headers;
$return->url = $url;
if (!$options['filename']) {
if (($pos = strpos($headers, "\r\n\r\n")) === false) {
// Crap!
throw new Requests_Exception('Missing header/body separator', 'requests.no_crlf_separator');
}
$headers = substr($return->raw, 0, $pos);
$return->body = substr($return->raw, $pos + strlen("\n\r\n\r"));
}
else {
$return->body = '';
}
// Pretend CRLF = LF for compatibility (RFC 2616, section 19.3)
$headers = str_replace("\r\n", "\n", $headers);
// Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2)
$headers = preg_replace('/\n[ \t]/', ' ', $headers);
$headers = explode("\n", $headers);
preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches);
if (empty($matches)) {
throw new Requests_Exception('Response could not be parsed', 'noversion', $headers);
}
$return->protocol_version = (float) $matches[1];
$return->status_code = (int) $matches[2];
if ($return->status_code >= 200 && $return->status_code < 300) {
$return->success = true;
}
foreach ($headers as $header) {
list($key, $value) = explode(':', $header, 2);
$value = trim($value);
preg_replace('#(\s+)#i', ' ', $value);
$return->headers[$key] = $value;
}
if (isset($return->headers['transfer-encoding'])) {
$return->body = self::decode_chunked($return->body);
unset($return->headers['transfer-encoding']);
}
if (isset($return->headers['content-encoding'])) {
$return->body = self::decompress($return->body);
}
//fsockopen and cURL compatibility
if (isset($return->headers['connection'])) {
unset($return->headers['connection']);
}
$options['hooks']->dispatch('requests.before_redirect_check', array(&$return, $req_headers, $req_data, $options));
if ($return->is_redirect() && $options['follow_redirects'] === true) {
if (isset($return->headers['location']) && $options['redirected'] < $options['redirects']) {
if ($return->status_code === 303) {
$options['type'] = self::GET;
}
$options['redirected']++;
$location = $return->headers['location'];
if (strpos($location, 'http://') !== 0 && strpos($location, 'https://') !== 0) {
// relative redirect, for compatibility make it absolute
$location = Requests_IRI::absolutize($url, $location);
$location = $location->uri;
}
$hook_args = array(
&$location,
&$req_headers,
&$req_data,
&$options,
$return
);
$options['hooks']->dispatch('requests.before_redirect', $hook_args);
$redirected = self::request($location, $req_headers, $req_data, $options['type'], $options);
$redirected->history[] = $return;
return $redirected;
}
elseif ($options['redirected'] >= $options['redirects']) {
throw new Requests_Exception('Too many redirects', 'toomanyredirects', $return);
}
}
$return->redirects = $options['redirected'];
$options['hooks']->dispatch('requests.after_request', array(&$return, $req_headers, $req_data, $options));
return $return;
}
/**
* Callback for `transport.internal.parse_response`
*
* Internal use only. Converts a raw HTTP response to a Requests_Response
* while still executing a multiple request.
*
* @param string $response Full response text including headers and body (will be overwritten with Response instance)
* @param array $request Request data as passed into {@see Requests::request_multiple()}
* @return null `$response` is either set to a Requests_Response instance, or a Requests_Exception object
*/
public static function parse_multiple(&$response, $request) {
try {
$url = $request['url'];
$headers = $request['headers'];
$data = $request['data'];
$options = $request['options'];
$response = self::parse_response($response, $url, $headers, $data, $options);
}
catch (Requests_Exception $e) {
$response = $e;
}
}
/**
* Decoded a chunked body as per RFC 2616
*
* @see https://tools.ietf.org/html/rfc2616#section-3.6.1
* @param string $data Chunked body
* @return string Decoded body
*/
protected static function decode_chunked($data) {
if (!preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', trim($data))) {
return $data;
}
$decoded = '';
$encoded = $data;
while (true) {
$is_chunked = (bool) preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', $encoded, $matches);
if (!$is_chunked) {
// Looks like it's not chunked after all
return $data;
}
$length = hexdec(trim($matches[1]));
if ($length === 0) {
// Ignore trailer headers
return $decoded;
}
$chunk_length = strlen($matches[0]);
$decoded .= substr($encoded, $chunk_length, $length);
$encoded = substr($encoded, $chunk_length + $length + 2);
if (trim($encoded) === '0' || empty($encoded)) {
return $decoded;
}
}
// We'll never actually get down here
// @codeCoverageIgnoreStart
}
// @codeCoverageIgnoreEnd
/**
* Convert a key => value array to a 'key: value' array for headers
*
* @param array $array Dictionary of header values
* @return array List of headers
*/
public static function flatten($array) {
$return = array();
foreach ($array as $key => $value) {
$return[] = sprintf('%s: %s', $key, $value);
}
return $return;
}
/**
* Convert a key => value array to a 'key: value' array for headers
*
* @codeCoverageIgnore
* @deprecated Misspelling of {@see Requests::flatten}
* @param array $array Dictionary of header values
* @return array List of headers
*/
public static function flattern($array) {
return self::flatten($array);
}
/**
* Decompress an encoded body
*
* Implements gzip, compress and deflate. Guesses which it is by attempting
* to decode.
*
* @param string $data Compressed data in one of the above formats
* @return string Decompressed string
*/
public static function decompress($data) {
if (substr($data, 0, 2) !== "\x1f\x8b" && substr($data, 0, 2) !== "\x78\x9c") {
// Not actually compressed. Probably cURL ruining this for us.
return $data;
}
if (function_exists('gzdecode') && ($decoded = @gzdecode($data)) !== false) {
return $decoded;
}
elseif (function_exists('gzinflate') && ($decoded = @gzinflate($data)) !== false) {
return $decoded;
}
elseif (($decoded = self::compatible_gzinflate($data)) !== false) {
return $decoded;
}
elseif (function_exists('gzuncompress') && ($decoded = @gzuncompress($data)) !== false) {
return $decoded;
}
return $data;
}
/**
* Decompression of deflated string while staying compatible with the majority of servers.
*
* Certain Servers will return deflated data with headers which PHP's gzinflate()
* function cannot handle out of the box. The following function has been created from
* various snippets on the gzinflate() PHP documentation.
*
* Warning: Magic numbers within. Due to the potential different formats that the compressed
* data may be returned in, some "magic offsets" are needed to ensure proper decompression
* takes place. For a simple progmatic way to determine the magic offset in use, see:
* https://core.trac.wordpress.org/ticket/18273
*
* @since 2.8.1
* @link https://core.trac.wordpress.org/ticket/18273
* @link https://secure.php.net/manual/en/function.gzinflate.php#70875
* @link https://secure.php.net/manual/en/function.gzinflate.php#77336
*
* @param string $gzData String to decompress.
* @return string|bool False on failure.
*/
public static function compatible_gzinflate($gzData) {
// Compressed data might contain a full zlib header, if so strip it for
// gzinflate()
if (substr($gzData, 0, 3) == "\x1f\x8b\x08") {
$i = 10;
$flg = ord(substr($gzData, 3, 1));
if ($flg > 0) {
if ($flg & 4) {
list($xlen) = unpack('v', substr($gzData, $i, 2));
$i = $i + 2 + $xlen;
}
if ($flg & 8) {
$i = strpos($gzData, "\0", $i) + 1;
}
if ($flg & 16) {
$i = strpos($gzData, "\0", $i) + 1;
}
if ($flg & 2) {
$i = $i + 2;
}
}
$decompressed = self::compatible_gzinflate(substr($gzData, $i));
if (false !== $decompressed) {
return $decompressed;
}
}
// If the data is Huffman Encoded, we must first strip the leading 2
// byte Huffman marker for gzinflate()
// The response is Huffman coded by many compressors such as
// java.util.zip.Deflater, Rubys Zlib::Deflate, and .NET's
// System.IO.Compression.DeflateStream.
//
// See https://decompres.blogspot.com/ for a quick explanation of this
// data type
$huffman_encoded = false;
// low nibble of first byte should be 0x08
list(, $first_nibble) = unpack('h', $gzData);
// First 2 bytes should be divisible by 0x1F
list(, $first_two_bytes) = unpack('n', $gzData);
if (0x08 == $first_nibble && 0 == ($first_two_bytes % 0x1F)) {
$huffman_encoded = true;
}
if ($huffman_encoded) {
if (false !== ($decompressed = @gzinflate(substr($gzData, 2)))) {
return $decompressed;
}
}
if ("\x50\x4b\x03\x04" == substr($gzData, 0, 4)) {
// ZIP file format header
// Offset 6: 2 bytes, General-purpose field
// Offset 26: 2 bytes, filename length
// Offset 28: 2 bytes, optional field length
// Offset 30: Filename field, followed by optional field, followed
// immediately by data
list(, $general_purpose_flag) = unpack('v', substr($gzData, 6, 2));
// If the file has been compressed on the fly, 0x08 bit is set of
// the general purpose field. We can use this to differentiate
// between a compressed document, and a ZIP file
$zip_compressed_on_the_fly = (0x08 == (0x08 & $general_purpose_flag));
if (!$zip_compressed_on_the_fly) {
// Don't attempt to decode a compressed zip file
return $gzData;
}
// Determine the first byte of data, based on the above ZIP header
// offsets:
$first_file_start = array_sum(unpack('v2', substr($gzData, 26, 4)));
if (false !== ($decompressed = @gzinflate(substr($gzData, 30 + $first_file_start)))) {
return $decompressed;
}
return false;
}
// Finally fall back to straight gzinflate
if (false !== ($decompressed = @gzinflate($gzData))) {
return $decompressed;
}
// Fallback for all above failing, not expected, but included for
// debugging and preventing regressions and to track stats
if (false !== ($decompressed = @gzinflate(substr($gzData, 2)))) {
return $decompressed;
}
return false;
}
public static function match_domain($host, $reference) {
// Check for a direct match
if ($host === $reference) {
return true;
}
// Calculate the valid wildcard match if the host is not an IP address
// Also validates that the host has 3 parts or more, as per Firefox's
// ruleset.
$parts = explode('.', $host);
if (ip2long($host) === false && count($parts) >= 3) {
$parts[0] = '*';
$wildcard = implode('.', $parts);
if ($wildcard === $reference) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Authentication provider interface
*
* @package Requests
* @subpackage Authentication
*/
/**
* Authentication provider interface
*
* Implement this interface to act as an authentication provider.
*
* Parameters should be passed via the constructor where possible, as this
* makes it much easier for users to use your provider.
*
* @see Requests_Hooks
* @package Requests
* @subpackage Authentication
*/
interface Requests_Auth {
/**
* Register hooks as needed
*
* This method is called in {@see Requests::request} when the user has set
* an instance as the 'auth' option. Use this callback to register all the
* hooks you'll need.
*
* @see Requests_Hooks::register
* @param Requests_Hooks $hooks Hook system
*/
public function register(Requests_Hooks &$hooks);
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* Basic Authentication provider
*
* @package Requests
* @subpackage Authentication
*/
/**
* Basic Authentication provider
*
* Provides a handler for Basic HTTP authentication via the Authorization
* header.
*
* @package Requests
* @subpackage Authentication
*/
class Requests_Auth_Basic implements Requests_Auth {
/**
* Username
*
* @var string
*/
public $user;
/**
* Password
*
* @var string
*/
public $pass;
/**
* Constructor
*
* @throws Requests_Exception On incorrect number of arguments (`authbasicbadargs`)
* @param array|null $args Array of user and password. Must have exactly two elements
*/
public function __construct($args = null) {
if (is_array($args)) {
if (count($args) !== 2) {
throw new Requests_Exception('Invalid number of arguments', 'authbasicbadargs');
}
list($this->user, $this->pass) = $args;
}
}
/**
* Register the necessary callbacks
*
* @see curl_before_send
* @see fsockopen_header
* @param Requests_Hooks $hooks Hook system
*/
public function register(Requests_Hooks &$hooks) {
$hooks->register('curl.before_send', array(&$this, 'curl_before_send'));
$hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header'));
}
/**
* Set cURL parameters before the data is sent
*
* @param resource $handle cURL resource
*/
public function curl_before_send(&$handle) {
curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($handle, CURLOPT_USERPWD, $this->getAuthString());
}
/**
* Add extra headers to the request before sending
*
* @param string $out HTTP header string
*/
public function fsockopen_header(&$out) {
$out .= sprintf("Authorization: Basic %s\r\n", base64_encode($this->getAuthString()));
}
/**
* Get the authentication string (user:pass)
*
* @return string
*/
public function getAuthString() {
return $this->user . ':' . $this->pass;
}
}

View File

@@ -0,0 +1,500 @@
<?php
/**
* Cookie storage object
*
* @package Requests
* @subpackage Cookies
*/
/**
* Cookie storage object
*
* @package Requests
* @subpackage Cookies
*/
class Requests_Cookie {
/**
* Cookie name.
*
* @var string
*/
public $name;
/**
* Cookie value.
*
* @var string
*/
public $value;
/**
* Cookie attributes
*
* Valid keys are (currently) path, domain, expires, max-age, secure and
* httponly.
*
* @var Requests_Utility_CaseInsensitiveDictionary|array Array-like object
*/
public $attributes = array();
/**
* Cookie flags
*
* Valid keys are (currently) creation, last-access, persistent and
* host-only.
*
* @var array
*/
public $flags = array();
/**
* Reference time for relative calculations
*
* This is used in place of `time()` when calculating Max-Age expiration and
* checking time validity.
*
* @var int
*/
public $reference_time = 0;
/**
* Create a new cookie object
*
* @param string $name
* @param string $value
* @param array|Requests_Utility_CaseInsensitiveDictionary $attributes Associative array of attribute data
*/
public function __construct($name, $value, $attributes = array(), $flags = array(), $reference_time = null) {
$this->name = $name;
$this->value = $value;
$this->attributes = $attributes;
$default_flags = array(
'creation' => time(),
'last-access' => time(),
'persistent' => false,
'host-only' => true,
);
$this->flags = array_merge($default_flags, $flags);
$this->reference_time = time();
if ($reference_time !== null) {
$this->reference_time = $reference_time;
}
$this->normalize();
}
/**
* Check if a cookie is expired.
*
* Checks the age against $this->reference_time to determine if the cookie
* is expired.
*
* @return boolean True if expired, false if time is valid.
*/
public function is_expired() {
// RFC6265, s. 4.1.2.2:
// If a cookie has both the Max-Age and the Expires attribute, the Max-
// Age attribute has precedence and controls the expiration date of the
// cookie.
if (isset($this->attributes['max-age'])) {
$max_age = $this->attributes['max-age'];
return $max_age < $this->reference_time;
}
if (isset($this->attributes['expires'])) {
$expires = $this->attributes['expires'];
return $expires < $this->reference_time;
}
return false;
}
/**
* Check if a cookie is valid for a given URI
*
* @param Requests_IRI $uri URI to check
* @return boolean Whether the cookie is valid for the given URI
*/
public function uri_matches(Requests_IRI $uri) {
if (!$this->domain_matches($uri->host)) {
return false;
}
if (!$this->path_matches($uri->path)) {
return false;
}
return empty($this->attributes['secure']) || $uri->scheme === 'https';
}
/**
* Check if a cookie is valid for a given domain
*
* @param string $string Domain to check
* @return boolean Whether the cookie is valid for the given domain
*/
public function domain_matches($string) {
if (!isset($this->attributes['domain'])) {
// Cookies created manually; cookies created by Requests will set
// the domain to the requested domain
return true;
}
$domain_string = $this->attributes['domain'];
if ($domain_string === $string) {
// The domain string and the string are identical.
return true;
}
// If the cookie is marked as host-only and we don't have an exact
// match, reject the cookie
if ($this->flags['host-only'] === true) {
return false;
}
if (strlen($string) <= strlen($domain_string)) {
// For obvious reasons, the string cannot be a suffix if the domain
// is shorter than the domain string
return false;
}
if (substr($string, -1 * strlen($domain_string)) !== $domain_string) {
// The domain string should be a suffix of the string.
return false;
}
$prefix = substr($string, 0, strlen($string) - strlen($domain_string));
if (substr($prefix, -1) !== '.') {
// The last character of the string that is not included in the
// domain string should be a %x2E (".") character.
return false;
}
// The string should be a host name (i.e., not an IP address).
return !preg_match('#^(.+\.)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $string);
}
/**
* Check if a cookie is valid for a given path
*
* From the path-match check in RFC 6265 section 5.1.4
*
* @param string $request_path Path to check
* @return boolean Whether the cookie is valid for the given path
*/
public function path_matches($request_path) {
if (empty($request_path)) {
// Normalize empty path to root
$request_path = '/';
}
if (!isset($this->attributes['path'])) {
// Cookies created manually; cookies created by Requests will set
// the path to the requested path
return true;
}
$cookie_path = $this->attributes['path'];
if ($cookie_path === $request_path) {
// The cookie-path and the request-path are identical.
return true;
}
if (strlen($request_path) > strlen($cookie_path) && substr($request_path, 0, strlen($cookie_path)) === $cookie_path) {
if (substr($cookie_path, -1) === '/') {
// The cookie-path is a prefix of the request-path, and the last
// character of the cookie-path is %x2F ("/").
return true;
}
if (substr($request_path, strlen($cookie_path), 1) === '/') {
// The cookie-path is a prefix of the request-path, and the
// first character of the request-path that is not included in
// the cookie-path is a %x2F ("/") character.
return true;
}
}
return false;
}
/**
* Normalize cookie and attributes
*
* @return boolean Whether the cookie was successfully normalized
*/
public function normalize() {
foreach ($this->attributes as $key => $value) {
$orig_value = $value;
$value = $this->normalize_attribute($key, $value);
if ($value === null) {
unset($this->attributes[$key]);
continue;
}
if ($value !== $orig_value) {
$this->attributes[$key] = $value;
}
}
return true;
}
/**
* Parse an individual cookie attribute
*
* Handles parsing individual attributes from the cookie values.
*
* @param string $name Attribute name
* @param string|boolean $value Attribute value (string value, or true if empty/flag)
* @return mixed Value if available, or null if the attribute value is invalid (and should be skipped)
*/
protected function normalize_attribute($name, $value) {
switch (strtolower($name)) {
case 'expires':
// Expiration parsing, as per RFC 6265 section 5.2.1
if (is_int($value)) {
return $value;
}
$expiry_time = strtotime($value);
if ($expiry_time === false) {
return null;
}
return $expiry_time;
case 'max-age':
// Expiration parsing, as per RFC 6265 section 5.2.2
if (is_int($value)) {
return $value;
}
// Check that we have a valid age
if (!preg_match('/^-?\d+$/', $value)) {
return null;
}
$delta_seconds = (int) $value;
if ($delta_seconds <= 0) {
$expiry_time = 0;
}
else {
$expiry_time = $this->reference_time + $delta_seconds;
}
return $expiry_time;
case 'domain':
// Domain normalization, as per RFC 6265 section 5.2.3
if ($value[0] === '.') {
$value = substr($value, 1);
}
return $value;
default:
return $value;
}
}
/**
* Format a cookie for a Cookie header
*
* This is used when sending cookies to a server.
*
* @return string Cookie formatted for Cookie header
*/
public function format_for_header() {
return sprintf('%s=%s', $this->name, $this->value);
}
/**
* Format a cookie for a Cookie header
*
* @codeCoverageIgnore
* @deprecated Use {@see Requests_Cookie::format_for_header}
* @return string
*/
public function formatForHeader() {
return $this->format_for_header();
}
/**
* Format a cookie for a Set-Cookie header
*
* This is used when sending cookies to clients. This isn't really
* applicable to client-side usage, but might be handy for debugging.
*
* @return string Cookie formatted for Set-Cookie header
*/
public function format_for_set_cookie() {
$header_value = $this->format_for_header();
if (!empty($this->attributes)) {
$parts = array();
foreach ($this->attributes as $key => $value) {
// Ignore non-associative attributes
if (is_numeric($key)) {
$parts[] = $value;
}
else {
$parts[] = sprintf('%s=%s', $key, $value);
}
}
$header_value .= '; ' . implode('; ', $parts);
}
return $header_value;
}
/**
* Format a cookie for a Set-Cookie header
*
* @codeCoverageIgnore
* @deprecated Use {@see Requests_Cookie::format_for_set_cookie}
* @return string
*/
public function formatForSetCookie() {
return $this->format_for_set_cookie();
}
/**
* Get the cookie value
*
* Attributes and other data can be accessed via methods.
*/
public function __toString() {
return $this->value;
}
/**
* Parse a cookie string into a cookie object
*
* Based on Mozilla's parsing code in Firefox and related projects, which
* is an intentional deviation from RFC 2109 and RFC 2616. RFC 6265
* specifies some of this handling, but not in a thorough manner.
*
* @param string Cookie header value (from a Set-Cookie header)
* @return Requests_Cookie Parsed cookie object
*/
public static function parse($string, $name = '', $reference_time = null) {
$parts = explode(';', $string);
$kvparts = array_shift($parts);
if (!empty($name)) {
$value = $string;
}
elseif (strpos($kvparts, '=') === false) {
// Some sites might only have a value without the equals separator.
// Deviate from RFC 6265 and pretend it was actually a blank name
// (`=foo`)
//
// https://bugzilla.mozilla.org/show_bug.cgi?id=169091
$name = '';
$value = $kvparts;
}
else {
list($name, $value) = explode('=', $kvparts, 2);
}
$name = trim($name);
$value = trim($value);
// Attribute key are handled case-insensitively
$attributes = new Requests_Utility_CaseInsensitiveDictionary();
if (!empty($parts)) {
foreach ($parts as $part) {
if (strpos($part, '=') === false) {
$part_key = $part;
$part_value = true;
}
else {
list($part_key, $part_value) = explode('=', $part, 2);
$part_value = trim($part_value);
}
$part_key = trim($part_key);
$attributes[$part_key] = $part_value;
}
}
return new Requests_Cookie($name, $value, $attributes, array(), $reference_time);
}
/**
* Parse all Set-Cookie headers from request headers
*
* @param Requests_Response_Headers $headers Headers to parse from
* @param Requests_IRI|null $origin URI for comparing cookie origins
* @param int|null $time Reference time for expiration calculation
* @return array
*/
public static function parse_from_headers(Requests_Response_Headers $headers, Requests_IRI $origin = null, $time = null) {
$cookie_headers = $headers->getValues('Set-Cookie');
if (empty($cookie_headers)) {
return array();
}
$cookies = array();
foreach ($cookie_headers as $header) {
$parsed = self::parse($header, '', $time);
// Default domain/path attributes
if (empty($parsed->attributes['domain']) && !empty($origin)) {
$parsed->attributes['domain'] = $origin->host;
$parsed->flags['host-only'] = true;
}
else {
$parsed->flags['host-only'] = false;
}
$path_is_valid = (!empty($parsed->attributes['path']) && $parsed->attributes['path'][0] === '/');
if (!$path_is_valid && !empty($origin)) {
$path = $origin->path;
// Default path normalization as per RFC 6265 section 5.1.4
if (substr($path, 0, 1) !== '/') {
// If the uri-path is empty or if the first character of
// the uri-path is not a %x2F ("/") character, output
// %x2F ("/") and skip the remaining steps.
$path = '/';
}
elseif (substr_count($path, '/') === 1) {
// If the uri-path contains no more than one %x2F ("/")
// character, output %x2F ("/") and skip the remaining
// step.
$path = '/';
}
else {
// Output the characters of the uri-path from the first
// character up to, but not including, the right-most
// %x2F ("/").
$path = substr($path, 0, strrpos($path, '/'));
}
$parsed->attributes['path'] = $path;
}
// Reject invalid cookie domains
if (!empty($origin) && !$parsed->domain_matches($origin->host)) {
continue;
}
$cookies[$parsed->name] = $parsed;
}
return $cookies;
}
/**
* Parse all Set-Cookie headers from request headers
*
* @codeCoverageIgnore
* @deprecated Use {@see Requests_Cookie::parse_from_headers}
* @return string
*/
public static function parseFromHeaders(Requests_Response_Headers $headers) {
return self::parse_from_headers($headers);
}
}

View File

@@ -0,0 +1,175 @@
<?php
/**
* Cookie holder object
*
* @package Requests
* @subpackage Cookies
*/
/**
* Cookie holder object
*
* @package Requests
* @subpackage Cookies
*/
class Requests_Cookie_Jar implements ArrayAccess, IteratorAggregate {
/**
* Actual item data
*
* @var array
*/
protected $cookies = array();
/**
* Create a new jar
*
* @param array $cookies Existing cookie values
*/
public function __construct($cookies = array()) {
$this->cookies = $cookies;
}
/**
* Normalise cookie data into a Requests_Cookie
*
* @param string|Requests_Cookie $cookie
* @return Requests_Cookie
*/
public function normalize_cookie($cookie, $key = null) {
if ($cookie instanceof Requests_Cookie) {
return $cookie;
}
return Requests_Cookie::parse($cookie, $key);
}
/**
* Normalise cookie data into a Requests_Cookie
*
* @codeCoverageIgnore
* @deprecated Use {@see Requests_Cookie_Jar::normalize_cookie}
* @return Requests_Cookie
*/
public function normalizeCookie($cookie, $key = null) {
return $this->normalize_cookie($cookie, $key);
}
/**
* Check if the given item exists
*
* @param string $key Item key
* @return boolean Does the item exist?
*/
public function offsetExists($key) {
return isset($this->cookies[$key]);
}
/**
* Get the value for the item
*
* @param string $key Item key
* @return string Item value
*/
public function offsetGet($key) {
if (!isset($this->cookies[$key])) {
return null;
}
return $this->cookies[$key];
}
/**
* Set the given item
*
* @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
*
* @param string $key Item name
* @param string $value Item value
*/
public function offsetSet($key, $value) {
if ($key === null) {
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
}
$this->cookies[$key] = $value;
}
/**
* Unset the given header
*
* @param string $key
*/
public function offsetUnset($key) {
unset($this->cookies[$key]);
}
/**
* Get an iterator for the data
*
* @return ArrayIterator
*/
public function getIterator() {
return new ArrayIterator($this->cookies);
}
/**
* Register the cookie handler with the request's hooking system
*
* @param Requests_Hooker $hooks Hooking system
*/
public function register(Requests_Hooker $hooks) {
$hooks->register('requests.before_request', array($this, 'before_request'));
$hooks->register('requests.before_redirect_check', array($this, 'before_redirect_check'));
}
/**
* Add Cookie header to a request if we have any
*
* As per RFC 6265, cookies are separated by '; '
*
* @param string $url
* @param array $headers
* @param array $data
* @param string $type
* @param array $options
*/
public function before_request($url, &$headers, &$data, &$type, &$options) {
if (!$url instanceof Requests_IRI) {
$url = new Requests_IRI($url);
}
if (!empty($this->cookies)) {
$cookies = array();
foreach ($this->cookies as $key => $cookie) {
$cookie = $this->normalize_cookie($cookie, $key);
// Skip expired cookies
if ($cookie->is_expired()) {
continue;
}
if ($cookie->domain_matches($url->host)) {
$cookies[] = $cookie->format_for_header();
}
}
$headers['Cookie'] = implode('; ', $cookies);
}
}
/**
* Parse all cookies from a response and attach them to the response
*
* @var Requests_Response $response
*/
public function before_redirect_check(Requests_Response &$return) {
$url = $return->url;
if (!$url instanceof Requests_IRI) {
$url = new Requests_IRI($url);
}
$cookies = Requests_Cookie::parse_from_headers($return->headers, $url);
$this->cookies = array_merge($this->cookies, $cookies);
$return->cookies = $this;
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* Exception for HTTP requests
*
* @package Requests
*/
/**
* Exception for HTTP requests
*
* @package Requests
*/
class Requests_Exception extends Exception {
/**
* Type of exception
*
* @var string
*/
protected $type;
/**
* Data associated with the exception
*
* @var mixed
*/
protected $data;
/**
* Create a new exception
*
* @param string $message Exception message
* @param string $type Exception type
* @param mixed $data Associated data
* @param integer $code Exception numerical code, if applicable
*/
public function __construct($message, $type, $data = null, $code = 0) {
parent::__construct($message, $code);
$this->type = $type;
$this->data = $data;
}
/**
* Like {@see getCode()}, but a string code.
*
* @codeCoverageIgnore
* @return string
*/
public function getType() {
return $this->type;
}
/**
* Gives any relevant data
*
* @codeCoverageIgnore
* @return mixed
*/
public function getData() {
return $this->data;
}
}

View File

@@ -0,0 +1,71 @@
<?php
/**
* Exception based on HTTP response
*
* @package Requests
*/
/**
* Exception based on HTTP response
*
* @package Requests
*/
class Requests_Exception_HTTP extends Requests_Exception {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 0;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Unknown';
/**
* Create a new exception
*
* There is no mechanism to pass in the status code, as this is set by the
* subclass used. Reason phrases can vary, however.
*
* @param string|null $reason Reason phrase
* @param mixed $data Associated data
*/
public function __construct($reason = null, $data = null) {
if ($reason !== null) {
$this->reason = $reason;
}
$message = sprintf('%d %s', $this->code, $this->reason);
parent::__construct($message, 'httpresponse', $data, $this->code);
}
/**
* Get the status message
*/
public function getReason() {
return $this->reason;
}
/**
* Get the correct exception class for a given error code
*
* @param int|bool $code HTTP status code, or false if unavailable
* @return string Exception class name to use
*/
public static function get_class($code) {
if (!$code) {
return 'Requests_Exception_HTTP_Unknown';
}
$class = sprintf('Requests_Exception_HTTP_%d', $code);
if (class_exists($class)) {
return $class;
}
return 'Requests_Exception_HTTP_Unknown';
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 304 Not Modified responses
*
* @package Requests
*/
/**
* Exception for 304 Not Modified responses
*
* @package Requests
*/
class Requests_Exception_HTTP_304 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 304;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Not Modified';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 305 Use Proxy responses
*
* @package Requests
*/
/**
* Exception for 305 Use Proxy responses
*
* @package Requests
*/
class Requests_Exception_HTTP_305 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 305;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Use Proxy';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 306 Switch Proxy responses
*
* @package Requests
*/
/**
* Exception for 306 Switch Proxy responses
*
* @package Requests
*/
class Requests_Exception_HTTP_306 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 306;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Switch Proxy';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 400 Bad Request responses
*
* @package Requests
*/
/**
* Exception for 400 Bad Request responses
*
* @package Requests
*/
class Requests_Exception_HTTP_400 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 400;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Bad Request';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 401 Unauthorized responses
*
* @package Requests
*/
/**
* Exception for 401 Unauthorized responses
*
* @package Requests
*/
class Requests_Exception_HTTP_401 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 401;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Unauthorized';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 402 Payment Required responses
*
* @package Requests
*/
/**
* Exception for 402 Payment Required responses
*
* @package Requests
*/
class Requests_Exception_HTTP_402 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 402;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Payment Required';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 403 Forbidden responses
*
* @package Requests
*/
/**
* Exception for 403 Forbidden responses
*
* @package Requests
*/
class Requests_Exception_HTTP_403 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 403;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Forbidden';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 404 Not Found responses
*
* @package Requests
*/
/**
* Exception for 404 Not Found responses
*
* @package Requests
*/
class Requests_Exception_HTTP_404 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 404;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Not Found';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 405 Method Not Allowed responses
*
* @package Requests
*/
/**
* Exception for 405 Method Not Allowed responses
*
* @package Requests
*/
class Requests_Exception_HTTP_405 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 405;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Method Not Allowed';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 406 Not Acceptable responses
*
* @package Requests
*/
/**
* Exception for 406 Not Acceptable responses
*
* @package Requests
*/
class Requests_Exception_HTTP_406 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 406;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Not Acceptable';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 407 Proxy Authentication Required responses
*
* @package Requests
*/
/**
* Exception for 407 Proxy Authentication Required responses
*
* @package Requests
*/
class Requests_Exception_HTTP_407 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 407;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Proxy Authentication Required';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 408 Request Timeout responses
*
* @package Requests
*/
/**
* Exception for 408 Request Timeout responses
*
* @package Requests
*/
class Requests_Exception_HTTP_408 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 408;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Request Timeout';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 409 Conflict responses
*
* @package Requests
*/
/**
* Exception for 409 Conflict responses
*
* @package Requests
*/
class Requests_Exception_HTTP_409 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 409;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Conflict';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 410 Gone responses
*
* @package Requests
*/
/**
* Exception for 410 Gone responses
*
* @package Requests
*/
class Requests_Exception_HTTP_410 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 410;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Gone';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 411 Length Required responses
*
* @package Requests
*/
/**
* Exception for 411 Length Required responses
*
* @package Requests
*/
class Requests_Exception_HTTP_411 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 411;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Length Required';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 412 Precondition Failed responses
*
* @package Requests
*/
/**
* Exception for 412 Precondition Failed responses
*
* @package Requests
*/
class Requests_Exception_HTTP_412 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 412;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Precondition Failed';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 413 Request Entity Too Large responses
*
* @package Requests
*/
/**
* Exception for 413 Request Entity Too Large responses
*
* @package Requests
*/
class Requests_Exception_HTTP_413 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 413;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Request Entity Too Large';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 414 Request-URI Too Large responses
*
* @package Requests
*/
/**
* Exception for 414 Request-URI Too Large responses
*
* @package Requests
*/
class Requests_Exception_HTTP_414 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 414;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Request-URI Too Large';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 415 Unsupported Media Type responses
*
* @package Requests
*/
/**
* Exception for 415 Unsupported Media Type responses
*
* @package Requests
*/
class Requests_Exception_HTTP_415 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 415;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Unsupported Media Type';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 416 Requested Range Not Satisfiable responses
*
* @package Requests
*/
/**
* Exception for 416 Requested Range Not Satisfiable responses
*
* @package Requests
*/
class Requests_Exception_HTTP_416 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 416;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Requested Range Not Satisfiable';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 417 Expectation Failed responses
*
* @package Requests
*/
/**
* Exception for 417 Expectation Failed responses
*
* @package Requests
*/
class Requests_Exception_HTTP_417 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 417;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Expectation Failed';
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Exception for 418 I'm A Teapot responses
*
* @see https://tools.ietf.org/html/rfc2324
* @package Requests
*/
/**
* Exception for 418 I'm A Teapot responses
*
* @see https://tools.ietf.org/html/rfc2324
* @package Requests
*/
class Requests_Exception_HTTP_418 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 418;
/**
* Reason phrase
*
* @var string
*/
protected $reason = "I'm A Teapot";
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Exception for 428 Precondition Required responses
*
* @see https://tools.ietf.org/html/rfc6585
* @package Requests
*/
/**
* Exception for 428 Precondition Required responses
*
* @see https://tools.ietf.org/html/rfc6585
* @package Requests
*/
class Requests_Exception_HTTP_428 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 428;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Precondition Required';
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Exception for 429 Too Many Requests responses
*
* @see https://tools.ietf.org/html/draft-nottingham-http-new-status-04
* @package Requests
*/
/**
* Exception for 429 Too Many Requests responses
*
* @see https://tools.ietf.org/html/draft-nottingham-http-new-status-04
* @package Requests
*/
class Requests_Exception_HTTP_429 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 429;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Too Many Requests';
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Exception for 431 Request Header Fields Too Large responses
*
* @see https://tools.ietf.org/html/rfc6585
* @package Requests
*/
/**
* Exception for 431 Request Header Fields Too Large responses
*
* @see https://tools.ietf.org/html/rfc6585
* @package Requests
*/
class Requests_Exception_HTTP_431 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 431;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Request Header Fields Too Large';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 500 Internal Server Error responses
*
* @package Requests
*/
/**
* Exception for 500 Internal Server Error responses
*
* @package Requests
*/
class Requests_Exception_HTTP_500 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 500;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Internal Server Error';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 501 Not Implemented responses
*
* @package Requests
*/
/**
* Exception for 501 Not Implemented responses
*
* @package Requests
*/
class Requests_Exception_HTTP_501 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 501;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Not Implemented';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 502 Bad Gateway responses
*
* @package Requests
*/
/**
* Exception for 502 Bad Gateway responses
*
* @package Requests
*/
class Requests_Exception_HTTP_502 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 502;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Bad Gateway';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 503 Service Unavailable responses
*
* @package Requests
*/
/**
* Exception for 503 Service Unavailable responses
*
* @package Requests
*/
class Requests_Exception_HTTP_503 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 503;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Service Unavailable';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 504 Gateway Timeout responses
*
* @package Requests
*/
/**
* Exception for 504 Gateway Timeout responses
*
* @package Requests
*/
class Requests_Exception_HTTP_504 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 504;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Gateway Timeout';
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Exception for 505 HTTP Version Not Supported responses
*
* @package Requests
*/
/**
* Exception for 505 HTTP Version Not Supported responses
*
* @package Requests
*/
class Requests_Exception_HTTP_505 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 505;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'HTTP Version Not Supported';
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Exception for 511 Network Authentication Required responses
*
* @see https://tools.ietf.org/html/rfc6585
* @package Requests
*/
/**
* Exception for 511 Network Authentication Required responses
*
* @see https://tools.ietf.org/html/rfc6585
* @package Requests
*/
class Requests_Exception_HTTP_511 extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 511;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Network Authentication Required';
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* Exception for unknown status responses
*
* @package Requests
*/
/**
* Exception for unknown status responses
*
* @package Requests
*/
class Requests_Exception_HTTP_Unknown extends Requests_Exception_HTTP {
/**
* HTTP status code
*
* @var integer|bool Code if available, false if an error occurred
*/
protected $code = 0;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Unknown';
/**
* Create a new exception
*
* If `$data` is an instance of {@see Requests_Response}, uses the status
* code from it. Otherwise, sets as 0
*
* @param string|null $reason Reason phrase
* @param mixed $data Associated data
*/
public function __construct($reason = null, $data = null) {
if ($data instanceof Requests_Response) {
$this->code = $data->status_code;
}
parent::__construct($reason, $data);
}
}

View File

@@ -0,0 +1,5 @@
<?php
class Requests_Exception_Transport extends Requests_Exception {
}

View File

@@ -0,0 +1,56 @@
<?php
class Requests_Exception_Transport_cURL extends Requests_Exception_Transport {
const EASY = 'cURLEasy';
const MULTI = 'cURLMulti';
const SHARE = 'cURLShare';
/**
* cURL error code
*
* @var integer
*/
protected $code = -1;
/**
* Which type of cURL error
*
* EASY|MULTI|SHARE
*
* @var string
*/
protected $type = 'Unknown';
/**
* Clear text error message
*
* @var string
*/
protected $reason = 'Unknown';
public function __construct($message, $type, $data = null, $code = 0) {
if ($type !== null) {
$this->type = $type;
}
if ($code !== null) {
$this->code = $code;
}
if ($message !== null) {
$this->reason = $message;
}
$message = sprintf('%d %s', $this->code, $this->reason);
parent::__construct($message, $this->type, $data, $this->code);
}
/**
* Get the error message
*/
public function getReason() {
return $this->reason;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Event dispatcher
*
* @package Requests
* @subpackage Utilities
*/
/**
* Event dispatcher
*
* @package Requests
* @subpackage Utilities
*/
interface Requests_Hooker {
/**
* Register a callback for a hook
*
* @param string $hook Hook name
* @param callback $callback Function/method to call on event
* @param int $priority Priority number. <0 is executed earlier, >0 is executed later
*/
public function register($hook, $callback, $priority = 0);
/**
* Dispatch a message
*
* @param string $hook Hook name
* @param array $parameters Parameters to pass to callbacks
* @return boolean Successfulness
*/
public function dispatch($hook, $parameters = array());
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* Handles adding and dispatching events
*
* @package Requests
* @subpackage Utilities
*/
/**
* Handles adding and dispatching events
*
* @package Requests
* @subpackage Utilities
*/
class Requests_Hooks implements Requests_Hooker {
/**
* Registered callbacks for each hook
*
* @var array
*/
protected $hooks = array();
/**
* Constructor
*/
public function __construct() {
// pass
}
/**
* Register a callback for a hook
*
* @param string $hook Hook name
* @param callback $callback Function/method to call on event
* @param int $priority Priority number. <0 is executed earlier, >0 is executed later
*/
public function register($hook, $callback, $priority = 0) {
if (!isset($this->hooks[$hook])) {
$this->hooks[$hook] = array();
}
if (!isset($this->hooks[$hook][$priority])) {
$this->hooks[$hook][$priority] = array();
}
$this->hooks[$hook][$priority][] = $callback;
}
/**
* Dispatch a message
*
* @param string $hook Hook name
* @param array $parameters Parameters to pass to callbacks
* @return boolean Successfulness
*/
public function dispatch($hook, $parameters = array()) {
if (empty($this->hooks[$hook])) {
return false;
}
foreach ($this->hooks[$hook] as $priority => $hooked) {
foreach ($hooked as $callback) {
call_user_func_array($callback, $parameters);
}
}
return true;
}
}

View File

@@ -0,0 +1,388 @@
<?php
/**
* IDNA URL encoder
*
* Note: Not fully compliant, as nameprep does nothing yet.
*
* @package Requests
* @subpackage Utilities
* @see https://tools.ietf.org/html/rfc3490 IDNA specification
* @see https://tools.ietf.org/html/rfc3492 Punycode/Bootstrap specification
*/
class Requests_IDNAEncoder {
/**
* ACE prefix used for IDNA
*
* @see https://tools.ietf.org/html/rfc3490#section-5
* @var string
*/
const ACE_PREFIX = 'xn--';
/**#@+
* Bootstrap constant for Punycode
*
* @see https://tools.ietf.org/html/rfc3492#section-5
* @var int
*/
const BOOTSTRAP_BASE = 36;
const BOOTSTRAP_TMIN = 1;
const BOOTSTRAP_TMAX = 26;
const BOOTSTRAP_SKEW = 38;
const BOOTSTRAP_DAMP = 700;
const BOOTSTRAP_INITIAL_BIAS = 72;
const BOOTSTRAP_INITIAL_N = 128;
/**#@-*/
/**
* Encode a hostname using Punycode
*
* @param string $string Hostname
* @return string Punycode-encoded hostname
*/
public static function encode($string) {
$parts = explode('.', $string);
foreach ($parts as &$part) {
$part = self::to_ascii($part);
}
return implode('.', $parts);
}
/**
* Convert a UTF-8 string to an ASCII string using Punycode
*
* @throws Requests_Exception Provided string longer than 64 ASCII characters (`idna.provided_too_long`)
* @throws Requests_Exception Prepared string longer than 64 ASCII characters (`idna.prepared_too_long`)
* @throws Requests_Exception Provided string already begins with xn-- (`idna.provided_is_prefixed`)
* @throws Requests_Exception Encoded string longer than 64 ASCII characters (`idna.encoded_too_long`)
*
* @param string $string ASCII or UTF-8 string (max length 64 characters)
* @return string ASCII string
*/
public static function to_ascii($string) {
// Step 1: Check if the string is already ASCII
if (self::is_ascii($string)) {
// Skip to step 7
if (strlen($string) < 64) {
return $string;
}
throw new Requests_Exception('Provided string is too long', 'idna.provided_too_long', $string);
}
// Step 2: nameprep
$string = self::nameprep($string);
// Step 3: UseSTD3ASCIIRules is false, continue
// Step 4: Check if it's ASCII now
if (self::is_ascii($string)) {
// Skip to step 7
if (strlen($string) < 64) {
return $string;
}
throw new Requests_Exception('Prepared string is too long', 'idna.prepared_too_long', $string);
}
// Step 5: Check ACE prefix
if (strpos($string, self::ACE_PREFIX) === 0) {
throw new Requests_Exception('Provided string begins with ACE prefix', 'idna.provided_is_prefixed', $string);
}
// Step 6: Encode with Punycode
$string = self::punycode_encode($string);
// Step 7: Prepend ACE prefix
$string = self::ACE_PREFIX . $string;
// Step 8: Check size
if (strlen($string) < 64) {
return $string;
}
throw new Requests_Exception('Encoded string is too long', 'idna.encoded_too_long', $string);
}
/**
* Check whether a given string contains only ASCII characters
*
* @internal (Testing found regex was the fastest implementation)
*
* @param string $string
* @return bool Is the string ASCII-only?
*/
protected static function is_ascii($string) {
return (preg_match('/(?:[^\x00-\x7F])/', $string) !== 1);
}
/**
* Prepare a string for use as an IDNA name
*
* @todo Implement this based on RFC 3491 and the newer 5891
* @param string $string
* @return string Prepared string
*/
protected static function nameprep($string) {
return $string;
}
/**
* Convert a UTF-8 string to a UCS-4 codepoint array
*
* Based on Requests_IRI::replace_invalid_with_pct_encoding()
*
* @throws Requests_Exception Invalid UTF-8 codepoint (`idna.invalidcodepoint`)
* @param string $input
* @return array Unicode code points
*/
protected static function utf8_to_codepoints($input) {
$codepoints = array();
// Get number of bytes
$strlen = strlen($input);
for ($position = 0; $position < $strlen; $position++) {
$value = ord($input[$position]);
// One byte sequence:
if ((~$value & 0x80) === 0x80) {
$character = $value;
$length = 1;
$remaining = 0;
}
// Two byte sequence:
elseif (($value & 0xE0) === 0xC0) {
$character = ($value & 0x1F) << 6;
$length = 2;
$remaining = 1;
}
// Three byte sequence:
elseif (($value & 0xF0) === 0xE0) {
$character = ($value & 0x0F) << 12;
$length = 3;
$remaining = 2;
}
// Four byte sequence:
elseif (($value & 0xF8) === 0xF0) {
$character = ($value & 0x07) << 18;
$length = 4;
$remaining = 3;
}
// Invalid byte:
else {
throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $value);
}
if ($remaining > 0) {
if ($position + $length > $strlen) {
throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
}
for ($position++; $remaining > 0; $position++) {
$value = ord($input[$position]);
// If it is invalid, count the sequence as invalid and reprocess the current byte:
if (($value & 0xC0) !== 0x80) {
throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
}
$character |= ($value & 0x3F) << (--$remaining * 6);
}
$position--;
}
if (
// Non-shortest form sequences are invalid
$length > 1 && $character <= 0x7F
|| $length > 2 && $character <= 0x7FF
|| $length > 3 && $character <= 0xFFFF
// Outside of range of ucschar codepoints
// Noncharacters
|| ($character & 0xFFFE) === 0xFFFE
|| $character >= 0xFDD0 && $character <= 0xFDEF
|| (
// Everything else not in ucschar
$character > 0xD7FF && $character < 0xF900
|| $character < 0x20
|| $character > 0x7E && $character < 0xA0
|| $character > 0xEFFFD
)
) {
throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
}
$codepoints[] = $character;
}
return $codepoints;
}
/**
* RFC3492-compliant encoder
*
* @internal Pseudo-code from Section 6.3 is commented with "#" next to relevant code
* @throws Requests_Exception On character outside of the domain (never happens with Punycode) (`idna.character_outside_domain`)
*
* @param string $input UTF-8 encoded string to encode
* @return string Punycode-encoded string
*/
public static function punycode_encode($input) {
$output = '';
# let n = initial_n
$n = self::BOOTSTRAP_INITIAL_N;
# let delta = 0
$delta = 0;
# let bias = initial_bias
$bias = self::BOOTSTRAP_INITIAL_BIAS;
# let h = b = the number of basic code points in the input
$h = $b = 0; // see loop
# copy them to the output in order
$codepoints = self::utf8_to_codepoints($input);
$extended = array();
foreach ($codepoints as $char) {
if ($char < 128) {
// Character is valid ASCII
// TODO: this should also check if it's valid for a URL
$output .= chr($char);
$h++;
}
// Check if the character is non-ASCII, but below initial n
// This never occurs for Punycode, so ignore in coverage
// @codeCoverageIgnoreStart
elseif ($char < $n) {
throw new Requests_Exception('Invalid character', 'idna.character_outside_domain', $char);
}
// @codeCoverageIgnoreEnd
else {
$extended[$char] = true;
}
}
$extended = array_keys($extended);
sort($extended);
$b = $h;
# [copy them] followed by a delimiter if b > 0
if (strlen($output) > 0) {
$output .= '-';
}
# {if the input contains a non-basic code point < n then fail}
# while h < length(input) do begin
while ($h < count($codepoints)) {
# let m = the minimum code point >= n in the input
$m = array_shift($extended);
//printf('next code point to insert is %s' . PHP_EOL, dechex($m));
# let delta = delta + (m - n) * (h + 1), fail on overflow
$delta += ($m - $n) * ($h + 1);
# let n = m
$n = $m;
# for each code point c in the input (in order) do begin
for ($num = 0; $num < count($codepoints); $num++) {
$c = $codepoints[$num];
# if c < n then increment delta, fail on overflow
if ($c < $n) {
$delta++;
}
# if c == n then begin
elseif ($c === $n) {
# let q = delta
$q = $delta;
# for k = base to infinity in steps of base do begin
for ($k = self::BOOTSTRAP_BASE; ; $k += self::BOOTSTRAP_BASE) {
# let t = tmin if k <= bias {+ tmin}, or
# tmax if k >= bias + tmax, or k - bias otherwise
if ($k <= ($bias + self::BOOTSTRAP_TMIN)) {
$t = self::BOOTSTRAP_TMIN;
}
elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) {
$t = self::BOOTSTRAP_TMAX;
}
else {
$t = $k - $bias;
}
# if q < t then break
if ($q < $t) {
break;
}
# output the code point for digit t + ((q - t) mod (base - t))
$digit = $t + (($q - $t) % (self::BOOTSTRAP_BASE - $t));
$output .= self::digit_to_char($digit);
# let q = (q - t) div (base - t)
$q = floor(($q - $t) / (self::BOOTSTRAP_BASE - $t));
# end
}
# output the code point for digit q
$output .= self::digit_to_char($q);
# let bias = adapt(delta, h + 1, test h equals b?)
$bias = self::adapt($delta, $h + 1, $h === $b);
# let delta = 0
$delta = 0;
# increment h
$h++;
# end
}
# end
}
# increment delta and n
$delta++;
$n++;
# end
}
return $output;
}
/**
* Convert a digit to its respective character
*
* @see https://tools.ietf.org/html/rfc3492#section-5
* @throws Requests_Exception On invalid digit (`idna.invalid_digit`)
*
* @param int $digit Digit in the range 0-35
* @return string Single character corresponding to digit
*/
protected static function digit_to_char($digit) {
// @codeCoverageIgnoreStart
// As far as I know, this never happens, but still good to be sure.
if ($digit < 0 || $digit > 35) {
throw new Requests_Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit);
}
// @codeCoverageIgnoreEnd
$digits = 'abcdefghijklmnopqrstuvwxyz0123456789';
return substr($digits, $digit, 1);
}
/**
* Adapt the bias
*
* @see https://tools.ietf.org/html/rfc3492#section-6.1
* @param int $delta
* @param int $numpoints
* @param bool $firsttime
* @return int New bias
*/
protected static function adapt($delta, $numpoints, $firsttime) {
# function adapt(delta,numpoints,firsttime):
# if firsttime then let delta = delta div damp
if ($firsttime) {
$delta = floor($delta / self::BOOTSTRAP_DAMP);
}
# else let delta = delta div 2
else {
$delta = floor($delta / 2);
}
# let delta = delta + (delta div numpoints)
$delta += floor($delta / $numpoints);
# let k = 0
$k = 0;
# while delta > ((base - tmin) * tmax) div 2 do begin
$max = floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN) * self::BOOTSTRAP_TMAX) / 2);
while ($delta > $max) {
# let delta = delta div (base - tmin)
$delta = floor($delta / (self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN));
# let k = k + base
$k += self::BOOTSTRAP_BASE;
# end
}
# return k + (((base - tmin + 1) * delta) div (delta + skew))
return $k + floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN + 1) * $delta) / ($delta + self::BOOTSTRAP_SKEW));
}
}

View File

@@ -0,0 +1,190 @@
<?php
/**
* Class to validate and to work with IPv6 addresses
*
* @package Requests
* @subpackage Utilities
*/
/**
* Class to validate and to work with IPv6 addresses
*
* This was originally based on the PEAR class of the same name, but has been
* entirely rewritten.
*
* @package Requests
* @subpackage Utilities
*/
class Requests_IPv6 {
/**
* Uncompresses an IPv6 address
*
* RFC 4291 allows you to compress consecutive zero pieces in an address to
* '::'. This method expects a valid IPv6 address and expands the '::' to
* the required number of zero pieces.
*
* Example: FF01::101 -> FF01:0:0:0:0:0:0:101
* ::1 -> 0:0:0:0:0:0:0:1
*
* @author Alexander Merz <alexander.merz@web.de>
* @author elfrink at introweb dot nl
* @author Josh Peck <jmp at joshpeck dot org>
* @copyright 2003-2005 The PHP Group
* @license http://www.opensource.org/licenses/bsd-license.php
* @param string $ip An IPv6 address
* @return string The uncompressed IPv6 address
*/
public static function uncompress($ip) {
if (substr_count($ip, '::') !== 1) {
return $ip;
}
list($ip1, $ip2) = explode('::', $ip);
$c1 = ($ip1 === '') ? -1 : substr_count($ip1, ':');
$c2 = ($ip2 === '') ? -1 : substr_count($ip2, ':');
if (strpos($ip2, '.') !== false) {
$c2++;
}
// ::
if ($c1 === -1 && $c2 === -1) {
$ip = '0:0:0:0:0:0:0:0';
}
// ::xxx
else if ($c1 === -1) {
$fill = str_repeat('0:', 7 - $c2);
$ip = str_replace('::', $fill, $ip);
}
// xxx::
else if ($c2 === -1) {
$fill = str_repeat(':0', 7 - $c1);
$ip = str_replace('::', $fill, $ip);
}
// xxx::xxx
else {
$fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
$ip = str_replace('::', $fill, $ip);
}
return $ip;
}
/**
* Compresses an IPv6 address
*
* RFC 4291 allows you to compress consecutive zero pieces in an address to
* '::'. This method expects a valid IPv6 address and compresses consecutive
* zero pieces to '::'.
*
* Example: FF01:0:0:0:0:0:0:101 -> FF01::101
* 0:0:0:0:0:0:0:1 -> ::1
*
* @see uncompress()
* @param string $ip An IPv6 address
* @return string The compressed IPv6 address
*/
public static function compress($ip) {
// Prepare the IP to be compressed
$ip = self::uncompress($ip);
$ip_parts = self::split_v6_v4($ip);
// Replace all leading zeros
$ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
// Find bunches of zeros
if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) {
$max = 0;
$pos = null;
foreach ($matches[0] as $match) {
if (strlen($match[0]) > $max) {
$max = strlen($match[0]);
$pos = $match[1];
}
}
$ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
}
if ($ip_parts[1] !== '') {
return implode(':', $ip_parts);
}
else {
return $ip_parts[0];
}
}
/**
* Splits an IPv6 address into the IPv6 and IPv4 representation parts
*
* RFC 4291 allows you to represent the last two parts of an IPv6 address
* using the standard IPv4 representation
*
* Example: 0:0:0:0:0:0:13.1.68.3
* 0:0:0:0:0:FFFF:129.144.52.38
*
* @param string $ip An IPv6 address
* @return string[] [0] contains the IPv6 represented part, and [1] the IPv4 represented part
*/
protected static function split_v6_v4($ip) {
if (strpos($ip, '.') !== false) {
$pos = strrpos($ip, ':');
$ipv6_part = substr($ip, 0, $pos);
$ipv4_part = substr($ip, $pos + 1);
return array($ipv6_part, $ipv4_part);
}
else {
return array($ip, '');
}
}
/**
* Checks an IPv6 address
*
* Checks if the given IP is a valid IPv6 address
*
* @param string $ip An IPv6 address
* @return bool true if $ip is a valid IPv6 address
*/
public static function check_ipv6($ip) {
$ip = self::uncompress($ip);
list($ipv6, $ipv4) = self::split_v6_v4($ip);
$ipv6 = explode(':', $ipv6);
$ipv4 = explode('.', $ipv4);
if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) {
foreach ($ipv6 as $ipv6_part) {
// The section can't be empty
if ($ipv6_part === '') {
return false;
}
// Nor can it be over four characters
if (strlen($ipv6_part) > 4) {
return false;
}
// Remove leading zeros (this is safe because of the above)
$ipv6_part = ltrim($ipv6_part, '0');
if ($ipv6_part === '') {
$ipv6_part = '0';
}
// Check the value is valid
$value = hexdec($ipv6_part);
if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) {
return false;
}
}
if (count($ipv4) === 4) {
foreach ($ipv4 as $ipv4_part) {
$value = (int) $ipv4_part;
if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) {
return false;
}
}
}
return true;
}
else {
return false;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
<?php
/**
* Proxy connection interface
*
* @package Requests
* @subpackage Proxy
* @since 1.6
*/
/**
* Proxy connection interface
*
* Implement this interface to handle proxy settings and authentication
*
* Parameters should be passed via the constructor where possible, as this
* makes it much easier for users to use your provider.
*
* @see Requests_Hooks
* @package Requests
* @subpackage Proxy
* @since 1.6
*/
interface Requests_Proxy {
/**
* Register hooks as needed
*
* This method is called in {@see Requests::request} when the user has set
* an instance as the 'auth' option. Use this callback to register all the
* hooks you'll need.
*
* @see Requests_Hooks::register
* @param Requests_Hooks $hooks Hook system
*/
public function register(Requests_Hooks &$hooks);
}

View File

@@ -0,0 +1,151 @@
<?php
/**
* HTTP Proxy connection interface
*
* @package Requests
* @subpackage Proxy
* @since 1.6
*/
/**
* HTTP Proxy connection interface
*
* Provides a handler for connection via an HTTP proxy
*
* @package Requests
* @subpackage Proxy
* @since 1.6
*/
class Requests_Proxy_HTTP implements Requests_Proxy {
/**
* Proxy host and port
*
* Notation: "host:port" (eg 127.0.0.1:8080 or someproxy.com:3128)
*
* @var string
*/
public $proxy;
/**
* Username
*
* @var string
*/
public $user;
/**
* Password
*
* @var string
*/
public $pass;
/**
* Do we need to authenticate? (ie username & password have been provided)
*
* @var boolean
*/
public $use_authentication;
/**
* Constructor
*
* @since 1.6
* @throws Requests_Exception On incorrect number of arguments (`authbasicbadargs`)
* @param array|null $args Array of user and password. Must have exactly two elements
*/
public function __construct($args = null) {
if (is_string($args)) {
$this->proxy = $args;
}
elseif (is_array($args)) {
if (count($args) == 1) {
list($this->proxy) = $args;
}
elseif (count($args) == 3) {
list($this->proxy, $this->user, $this->pass) = $args;
$this->use_authentication = true;
}
else {
throw new Requests_Exception('Invalid number of arguments', 'proxyhttpbadargs');
}
}
}
/**
* Register the necessary callbacks
*
* @since 1.6
* @see curl_before_send
* @see fsockopen_remote_socket
* @see fsockopen_remote_host_path
* @see fsockopen_header
* @param Requests_Hooks $hooks Hook system
*/
public function register(Requests_Hooks &$hooks) {
$hooks->register('curl.before_send', array(&$this, 'curl_before_send'));
$hooks->register('fsockopen.remote_socket', array(&$this, 'fsockopen_remote_socket'));
$hooks->register('fsockopen.remote_host_path', array(&$this, 'fsockopen_remote_host_path'));
if ($this->use_authentication) {
$hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header'));
}
}
/**
* Set cURL parameters before the data is sent
*
* @since 1.6
* @param resource $handle cURL resource
*/
public function curl_before_send(&$handle) {
curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
curl_setopt($handle, CURLOPT_PROXY, $this->proxy);
if ($this->use_authentication) {
curl_setopt($handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
curl_setopt($handle, CURLOPT_PROXYUSERPWD, $this->get_auth_string());
}
}
/**
* Alter remote socket information before opening socket connection
*
* @since 1.6
* @param string $remote_socket Socket connection string
*/
public function fsockopen_remote_socket(&$remote_socket) {
$remote_socket = $this->proxy;
}
/**
* Alter remote path before getting stream data
*
* @since 1.6
* @param string $path Path to send in HTTP request string ("GET ...")
* @param string $url Full URL we're requesting
*/
public function fsockopen_remote_host_path(&$path, $url) {
$path = $url;
}
/**
* Add extra headers to the request before sending
*
* @since 1.6
* @param string $out HTTP header string
*/
public function fsockopen_header(&$out) {
$out .= sprintf("Proxy-Authorization: Basic %s\r\n", base64_encode($this->get_auth_string()));
}
/**
* Get the authentication string (user:pass)
*
* @since 1.6
* @return string
*/
public function get_auth_string() {
return $this->user . ':' . $this->pass;
}
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* HTTP response class
*
* Contains a response from Requests::request()
* @package Requests
*/
/**
* HTTP response class
*
* Contains a response from Requests::request()
* @package Requests
*/
class Requests_Response {
/**
* Constructor
*/
public function __construct() {
$this->headers = new Requests_Response_Headers();
$this->cookies = new Requests_Cookie_Jar();
}
/**
* Response body
*
* @var string
*/
public $body = '';
/**
* Raw HTTP data from the transport
*
* @var string
*/
public $raw = '';
/**
* Headers, as an associative array
*
* @var Requests_Response_Headers Array-like object representing headers
*/
public $headers = array();
/**
* Status code, false if non-blocking
*
* @var integer|boolean
*/
public $status_code = false;
/**
* Protocol version, false if non-blocking
* @var float|boolean
*/
public $protocol_version = false;
/**
* Whether the request succeeded or not
*
* @var boolean
*/
public $success = false;
/**
* Number of redirects the request used
*
* @var integer
*/
public $redirects = 0;
/**
* URL requested
*
* @var string
*/
public $url = '';
/**
* Previous requests (from redirects)
*
* @var array Array of Requests_Response objects
*/
public $history = array();
/**
* Cookies from the request
*
* @var Requests_Cookie_Jar Array-like object representing a cookie jar
*/
public $cookies = array();
/**
* Is the response a redirect?
*
* @return boolean True if redirect (3xx status), false if not.
*/
public function is_redirect() {
$code = $this->status_code;
return in_array($code, array(300, 301, 302, 303, 307)) || $code > 307 && $code < 400;
}
/**
* Throws an exception if the request was not successful
*
* @throws Requests_Exception If `$allow_redirects` is false, and code is 3xx (`response.no_redirects`)
* @throws Requests_Exception_HTTP On non-successful status code. Exception class corresponds to code (e.g. {@see Requests_Exception_HTTP_404})
* @param boolean $allow_redirects Set to false to throw on a 3xx as well
*/
public function throw_for_status($allow_redirects = true) {
if ($this->is_redirect()) {
if (!$allow_redirects) {
throw new Requests_Exception('Redirection not allowed', 'response.no_redirects', $this);
}
}
elseif (!$this->success) {
$exception = Requests_Exception_HTTP::get_class($this->status_code);
throw new $exception(null, $this);
}
}
}

View File

@@ -0,0 +1,98 @@
<?php
/**
* Case-insensitive dictionary, suitable for HTTP headers
*
* @package Requests
*/
/**
* Case-insensitive dictionary, suitable for HTTP headers
*
* @package Requests
*/
class Requests_Response_Headers extends Requests_Utility_CaseInsensitiveDictionary {
/**
* Get the given header
*
* Unlike {@see self::getValues()}, this returns a string. If there are
* multiple values, it concatenates them with a comma as per RFC2616.
*
* Avoid using this where commas may be used unquoted in values, such as
* Set-Cookie headers.
*
* @param string $key
* @return string Header value
*/
public function offsetGet($key) {
$key = strtolower($key);
if (!isset($this->data[$key])) {
return null;
}
return $this->flatten($this->data[$key]);
}
/**
* Set the given item
*
* @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
*
* @param string $key Item name
* @param string $value Item value
*/
public function offsetSet($key, $value) {
if ($key === null) {
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
}
$key = strtolower($key);
if (!isset($this->data[$key])) {
$this->data[$key] = array();
}
$this->data[$key][] = $value;
}
/**
* Get all values for a given header
*
* @param string $key
* @return array Header values
*/
public function getValues($key) {
$key = strtolower($key);
if (!isset($this->data[$key])) {
return null;
}
return $this->data[$key];
}
/**
* Flattens a value into a string
*
* Converts an array into a string by imploding values with a comma, as per
* RFC2616's rules for folding headers.
*
* @param string|array $value Value to flatten
* @return string Flattened value
*/
public function flatten($value) {
if (is_array($value)) {
$value = implode(',', $value);
}
return $value;
}
/**
* Get an iterator for the data
*
* Converts the internal
* @return ArrayIterator
*/
public function getIterator() {
return new Requests_Utility_FilteredIterator($this->data, array($this, 'flatten'));
}
}

View File

@@ -0,0 +1,152 @@
<?php
/**
* SSL utilities for Requests
*
* @package Requests
* @subpackage Utilities
*/
/**
* SSL utilities for Requests
*
* Collection of utilities for working with and verifying SSL certificates.
*
* @package Requests
* @subpackage Utilities
*/
class Requests_SSL {
/**
* Verify the certificate against common name and subject alternative names
*
* Unfortunately, PHP doesn't check the certificate against the alternative
* names, leading things like 'https://www.github.com/' to be invalid.
* Instead
*
* @see https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
*
* @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`)
* @param string $host Host name to verify against
* @param array $cert Certificate data from openssl_x509_parse()
* @return bool
*/
public static function verify_certificate($host, $cert) {
// Calculate the valid wildcard match if the host is not an IP address
$parts = explode('.', $host);
if (ip2long($host) === false) {
$parts[0] = '*';
}
$wildcard = implode('.', $parts);
$has_dns_alt = false;
// Check the subjectAltName
if (!empty($cert['extensions']) && !empty($cert['extensions']['subjectAltName'])) {
$altnames = explode(',', $cert['extensions']['subjectAltName']);
foreach ($altnames as $altname) {
$altname = trim($altname);
if (strpos($altname, 'DNS:') !== 0) {
continue;
}
$has_dns_alt = true;
// Strip the 'DNS:' prefix and trim whitespace
$altname = trim(substr($altname, 4));
// Check for a match
if (self::match_domain($host, $altname) === true) {
return true;
}
}
}
// Fall back to checking the common name if we didn't get any dNSName
// alt names, as per RFC2818
if (!$has_dns_alt && !empty($cert['subject']['CN'])) {
// Check for a match
if (self::match_domain($host, $cert['subject']['CN']) === true) {
return true;
}
}
return false;
}
/**
* Verify that a reference name is valid
*
* Verifies a dNSName for HTTPS usage, (almost) as per Firefox's rules:
* - Wildcards can only occur in a name with more than 3 components
* - Wildcards can only occur as the last character in the first
* component
* - Wildcards may be preceded by additional characters
*
* We modify these rules to be a bit stricter and only allow the wildcard
* character to be the full first component; that is, with the exclusion of
* the third rule.
*
* @param string $reference Reference dNSName
* @return boolean Is the name valid?
*/
public static function verify_reference_name($reference) {
$parts = explode('.', $reference);
// Check the first part of the name
$first = array_shift($parts);
if (strpos($first, '*') !== false) {
// Check that the wildcard is the full part
if ($first !== '*') {
return false;
}
// Check that we have at least 3 components (including first)
if (count($parts) < 2) {
return false;
}
}
// Check the remaining parts
foreach ($parts as $part) {
if (strpos($part, '*') !== false) {
return false;
}
}
// Nothing found, verified!
return true;
}
/**
* Match a hostname against a dNSName reference
*
* @param string $host Requested host
* @param string $reference dNSName to match against
* @return boolean Does the domain match?
*/
public static function match_domain($host, $reference) {
// Check if the reference is blacklisted first
if (self::verify_reference_name($reference) !== true) {
return false;
}
// Check for a direct match
if ($host === $reference) {
return true;
}
// Calculate the valid wildcard match if the host is not an IP address
// Also validates that the host has 3 parts or more, as per Firefox's
// ruleset.
if (ip2long($host) === false) {
$parts = explode('.', $host);
$parts[0] = '*';
$wildcard = implode('.', $parts);
if ($wildcard === $reference) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,266 @@
<?php
/**
* Session handler for persistent requests and default parameters
*
* @package Requests
* @subpackage Session Handler
*/
/**
* Session handler for persistent requests and default parameters
*
* Allows various options to be set as default values, and merges both the
* options and URL properties together. A base URL can be set for all requests,
* with all subrequests resolved from this. Base options can be set (including
* a shared cookie jar), then overridden for individual requests.
*
* @package Requests
* @subpackage Session Handler
*/
class Requests_Session {
/**
* Base URL for requests
*
* URLs will be made absolute using this as the base
* @var string|null
*/
public $url = null;
/**
* Base headers for requests
* @var array
*/
public $headers = array();
/**
* Base data for requests
*
* If both the base data and the per-request data are arrays, the data will
* be merged before sending the request.
*
* @var array
*/
public $data = array();
/**
* Base options for requests
*
* The base options are merged with the per-request data for each request.
* The only default option is a shared cookie jar between requests.
*
* Values here can also be set directly via properties on the Session
* object, e.g. `$session->useragent = 'X';`
*
* @var array
*/
public $options = array();
/**
* Create a new session
*
* @param string|null $url Base URL for requests
* @param array $headers Default headers for requests
* @param array $data Default data for requests
* @param array $options Default options for requests
*/
public function __construct($url = null, $headers = array(), $data = array(), $options = array()) {
$this->url = $url;
$this->headers = $headers;
$this->data = $data;
$this->options = $options;
if (empty($this->options['cookies'])) {
$this->options['cookies'] = new Requests_Cookie_Jar();
}
}
/**
* Get a property's value
*
* @param string $key Property key
* @return mixed|null Property value, null if none found
*/
public function __get($key) {
if (isset($this->options[$key])) {
return $this->options[$key];
}
return null;
}
/**
* Set a property's value
*
* @param string $key Property key
* @param mixed $value Property value
*/
public function __set($key, $value) {
$this->options[$key] = $value;
}
/**
* Remove a property's value
*
* @param string $key Property key
*/
public function __isset($key) {
return isset($this->options[$key]);
}
/**
* Remove a property's value
*
* @param string $key Property key
*/
public function __unset($key) {
if (isset($this->options[$key])) {
unset($this->options[$key]);
}
}
/**#@+
* @see request()
* @param string $url
* @param array $headers
* @param array $options
* @return Requests_Response
*/
/**
* Send a GET request
*/
public function get($url, $headers = array(), $options = array()) {
return $this->request($url, $headers, null, Requests::GET, $options);
}
/**
* Send a HEAD request
*/
public function head($url, $headers = array(), $options = array()) {
return $this->request($url, $headers, null, Requests::HEAD, $options);
}
/**
* Send a DELETE request
*/
public function delete($url, $headers = array(), $options = array()) {
return $this->request($url, $headers, null, Requests::DELETE, $options);
}
/**#@-*/
/**#@+
* @see request()
* @param string $url
* @param array $headers
* @param array $data
* @param array $options
* @return Requests_Response
*/
/**
* Send a POST request
*/
public function post($url, $headers = array(), $data = array(), $options = array()) {
return $this->request($url, $headers, $data, Requests::POST, $options);
}
/**
* Send a PUT request
*/
public function put($url, $headers = array(), $data = array(), $options = array()) {
return $this->request($url, $headers, $data, Requests::PUT, $options);
}
/**
* Send a PATCH request
*
* Note: Unlike {@see post} and {@see put}, `$headers` is required, as the
* specification recommends that should send an ETag
*
* @link https://tools.ietf.org/html/rfc5789
*/
public function patch($url, $headers, $data = array(), $options = array()) {
return $this->request($url, $headers, $data, Requests::PATCH, $options);
}
/**#@-*/
/**
* Main interface for HTTP requests
*
* This method initiates a request and sends it via a transport before
* parsing.
*
* @see Requests::request()
*
* @throws Requests_Exception On invalid URLs (`nonhttp`)
*
* @param string $url URL to request
* @param array $headers Extra headers to send with the request
* @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
* @param string $type HTTP request type (use Requests constants)
* @param array $options Options for the request (see {@see Requests::request})
* @return Requests_Response
*/
public function request($url, $headers = array(), $data = array(), $type = Requests::GET, $options = array()) {
$request = $this->merge_request(compact('url', 'headers', 'data', 'options'));
return Requests::request($request['url'], $request['headers'], $request['data'], $type, $request['options']);
}
/**
* Send multiple HTTP requests simultaneously
*
* @see Requests::request_multiple()
*
* @param array $requests Requests data (see {@see Requests::request_multiple})
* @param array $options Global and default options (see {@see Requests::request})
* @return array Responses (either Requests_Response or a Requests_Exception object)
*/
public function request_multiple($requests, $options = array()) {
foreach ($requests as $key => $request) {
$requests[$key] = $this->merge_request($request, false);
}
$options = array_merge($this->options, $options);
// Disallow forcing the type, as that's a per request setting
unset($options['type']);
return Requests::request_multiple($requests, $options);
}
/**
* Merge a request's data with the default data
*
* @param array $request Request data (same form as {@see request_multiple})
* @param boolean $merge_options Should we merge options as well?
* @return array Request data
*/
protected function merge_request($request, $merge_options = true) {
if ($this->url !== null) {
$request['url'] = Requests_IRI::absolutize($this->url, $request['url']);
$request['url'] = $request['url']->uri;
}
if (empty($request['headers'])) {
$request['headers'] = array();
}
$request['headers'] = array_merge($this->headers, $request['headers']);
if (empty($request['data'])) {
if (is_array($this->data)) {
$request['data'] = $this->data;
}
}
elseif (is_array($request['data']) && is_array($this->data)) {
$request['data'] = array_merge($this->data, $request['data']);
}
if ($merge_options !== false) {
$request['options'] = array_merge($this->options, $request['options']);
// Disallow forcing the type, as that's a per request setting
unset($request['options']['type']);
}
return $request;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* Base HTTP transport
*
* @package Requests
* @subpackage Transport
*/
/**
* Base HTTP transport
*
* @package Requests
* @subpackage Transport
*/
interface Requests_Transport {
/**
* Perform a request
*
* @param string $url URL to request
* @param array $headers Associative array of request headers
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
* @param array $options Request options, see {@see Requests::response()} for documentation
* @return string Raw HTTP result
*/
public function request($url, $headers = array(), $data = array(), $options = array());
/**
* Send multiple requests simultaneously
*
* @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request}
* @param array $options Global options, see {@see Requests::response()} for documentation
* @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well)
*/
public function request_multiple($requests, $options);
/**
* Self-test whether the transport can be used
* @return bool
*/
public static function test();
}

View File

@@ -0,0 +1,542 @@
<?php
/**
* cURL HTTP transport
*
* @package Requests
* @subpackage Transport
*/
/**
* cURL HTTP transport
*
* @package Requests
* @subpackage Transport
*/
class Requests_Transport_cURL implements Requests_Transport {
const CURL_7_10_5 = 0x070A05;
const CURL_7_16_2 = 0x071002;
/**
* Raw HTTP data
*
* @var string
*/
public $headers = '';
/**
* Raw body data
*
* @var string
*/
public $response_data = '';
/**
* Information on the current request
*
* @var array cURL information array, see {@see https://secure.php.net/curl_getinfo}
*/
public $info;
/**
* Version string
*
* @var long
*/
public $version;
/**
* cURL handle
*
* @var resource
*/
protected $handle;
/**
* Hook dispatcher instance
*
* @var Requests_Hooks
*/
protected $hooks;
/**
* Have we finished the headers yet?
*
* @var boolean
*/
protected $done_headers = false;
/**
* If streaming to a file, keep the file pointer
*
* @var resource
*/
protected $stream_handle;
/**
* How many bytes are in the response body?
*
* @var int
*/
protected $response_bytes;
/**
* What's the maximum number of bytes we should keep?
*
* @var int|bool Byte count, or false if no limit.
*/
protected $response_byte_limit;
/**
* Constructor
*/
public function __construct() {
$curl = curl_version();
$this->version = $curl['version_number'];
$this->handle = curl_init();
curl_setopt($this->handle, CURLOPT_HEADER, false);
curl_setopt($this->handle, CURLOPT_RETURNTRANSFER, 1);
if ($this->version >= self::CURL_7_10_5) {
curl_setopt($this->handle, CURLOPT_ENCODING, '');
}
if (defined('CURLOPT_PROTOCOLS')) {
curl_setopt($this->handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
}
if (defined('CURLOPT_REDIR_PROTOCOLS')) {
curl_setopt($this->handle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
}
}
/**
* Destructor
*/
public function __destruct() {
if (is_resource($this->handle)) {
curl_close($this->handle);
}
}
/**
* Perform a request
*
* @throws Requests_Exception On a cURL error (`curlerror`)
*
* @param string $url URL to request
* @param array $headers Associative array of request headers
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
* @param array $options Request options, see {@see Requests::response()} for documentation
* @return string Raw HTTP result
*/
public function request($url, $headers = array(), $data = array(), $options = array()) {
$this->hooks = $options['hooks'];
$this->setup_handle($url, $headers, $data, $options);
$options['hooks']->dispatch('curl.before_send', array(&$this->handle));
if ($options['filename'] !== false) {
$this->stream_handle = fopen($options['filename'], 'wb');
}
$this->response_data = '';
$this->response_bytes = 0;
$this->response_byte_limit = false;
if ($options['max_bytes'] !== false) {
$this->response_byte_limit = $options['max_bytes'];
}
if (isset($options['verify'])) {
if ($options['verify'] === false) {
curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, 0);
}
elseif (is_string($options['verify'])) {
curl_setopt($this->handle, CURLOPT_CAINFO, $options['verify']);
}
}
if (isset($options['verifyname']) && $options['verifyname'] === false) {
curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0);
}
curl_exec($this->handle);
$response = $this->response_data;
$options['hooks']->dispatch('curl.after_send', array());
if (curl_errno($this->handle) === 23 || curl_errno($this->handle) === 61) {
// Reset encoding and try again
curl_setopt($this->handle, CURLOPT_ENCODING, 'none');
$this->response_data = '';
$this->response_bytes = 0;
curl_exec($this->handle);
$response = $this->response_data;
}
$this->process_response($response, $options);
// Need to remove the $this reference from the curl handle.
// Otherwise Requests_Transport_cURL wont be garbage collected and the curl_close() will never be called.
curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, null);
curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, null);
return $this->headers;
}
/**
* Send multiple requests simultaneously
*
* @param array $requests Request data
* @param array $options Global options
* @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well)
*/
public function request_multiple($requests, $options) {
// If you're not requesting, we can't get any responses ¯\_(ツ)_/¯
if (empty($requests)) {
return array();
}
$multihandle = curl_multi_init();
$subrequests = array();
$subhandles = array();
$class = get_class($this);
foreach ($requests as $id => $request) {
$subrequests[$id] = new $class();
$subhandles[$id] = $subrequests[$id]->get_subrequest_handle($request['url'], $request['headers'], $request['data'], $request['options']);
$request['options']['hooks']->dispatch('curl.before_multi_add', array(&$subhandles[$id]));
curl_multi_add_handle($multihandle, $subhandles[$id]);
}
$completed = 0;
$responses = array();
$request['options']['hooks']->dispatch('curl.before_multi_exec', array(&$multihandle));
do {
$active = false;
do {
$status = curl_multi_exec($multihandle, $active);
}
while ($status === CURLM_CALL_MULTI_PERFORM);
$to_process = array();
// Read the information as needed
while ($done = curl_multi_info_read($multihandle)) {
$key = array_search($done['handle'], $subhandles, true);
if (!isset($to_process[$key])) {
$to_process[$key] = $done;
}
}
// Parse the finished requests before we start getting the new ones
foreach ($to_process as $key => $done) {
$options = $requests[$key]['options'];
if (CURLE_OK !== $done['result']) {
//get error string for handle.
$reason = curl_error($done['handle']);
$exception = new Requests_Exception_Transport_cURL(
$reason,
Requests_Exception_Transport_cURL::EASY,
$done['handle'],
$done['result']
);
$responses[$key] = $exception;
$options['hooks']->dispatch('transport.internal.parse_error', array(&$responses[$key], $requests[$key]));
}
else {
$responses[$key] = $subrequests[$key]->process_response($subrequests[$key]->response_data, $options);
$options['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$key], $requests[$key]));
}
curl_multi_remove_handle($multihandle, $done['handle']);
curl_close($done['handle']);
if (!is_string($responses[$key])) {
$options['hooks']->dispatch('multiple.request.complete', array(&$responses[$key], $key));
}
$completed++;
}
}
while ($active || $completed < count($subrequests));
$request['options']['hooks']->dispatch('curl.after_multi_exec', array(&$multihandle));
curl_multi_close($multihandle);
return $responses;
}
/**
* Get the cURL handle for use in a multi-request
*
* @param string $url URL to request
* @param array $headers Associative array of request headers
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
* @param array $options Request options, see {@see Requests::response()} for documentation
* @return resource Subrequest's cURL handle
*/
public function &get_subrequest_handle($url, $headers, $data, $options) {
$this->setup_handle($url, $headers, $data, $options);
if ($options['filename'] !== false) {
$this->stream_handle = fopen($options['filename'], 'wb');
}
$this->response_data = '';
$this->response_bytes = 0;
$this->response_byte_limit = false;
if ($options['max_bytes'] !== false) {
$this->response_byte_limit = $options['max_bytes'];
}
$this->hooks = $options['hooks'];
return $this->handle;
}
/**
* Setup the cURL handle for the given data
*
* @param string $url URL to request
* @param array $headers Associative array of request headers
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
* @param array $options Request options, see {@see Requests::response()} for documentation
*/
protected function setup_handle($url, $headers, $data, $options) {
$options['hooks']->dispatch('curl.before_request', array(&$this->handle));
// Force closing the connection for old versions of cURL (<7.22).
if ( ! isset( $headers['Connection'] ) ) {
$headers['Connection'] = 'close';
}
$headers = Requests::flatten($headers);
if (!empty($data)) {
$data_format = $options['data_format'];
if ($data_format === 'query') {
$url = self::format_get($url, $data);
$data = '';
}
elseif (!is_string($data)) {
$data = http_build_query($data, null, '&');
}
}
switch ($options['type']) {
case Requests::POST:
curl_setopt($this->handle, CURLOPT_POST, true);
curl_setopt($this->handle, CURLOPT_POSTFIELDS, $data);
break;
case Requests::HEAD:
curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']);
curl_setopt($this->handle, CURLOPT_NOBODY, true);
break;
case Requests::TRACE:
curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']);
break;
case Requests::PATCH:
case Requests::PUT:
case Requests::DELETE:
case Requests::OPTIONS:
default:
curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']);
if (!empty($data)) {
curl_setopt($this->handle, CURLOPT_POSTFIELDS, $data);
}
}
// cURL requires a minimum timeout of 1 second when using the system
// DNS resolver, as it uses `alarm()`, which is second resolution only.
// There's no way to detect which DNS resolver is being used from our
// end, so we need to round up regardless of the supplied timeout.
//
// https://github.com/curl/curl/blob/4f45240bc84a9aa648c8f7243be7b79e9f9323a5/lib/hostip.c#L606-L609
$timeout = max($options['timeout'], 1);
if (is_int($timeout) || $this->version < self::CURL_7_16_2) {
curl_setopt($this->handle, CURLOPT_TIMEOUT, ceil($timeout));
}
else {
curl_setopt($this->handle, CURLOPT_TIMEOUT_MS, round($timeout * 1000));
}
if (is_int($options['connect_timeout']) || $this->version < self::CURL_7_16_2) {
curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT, ceil($options['connect_timeout']));
}
else {
curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT_MS, round($options['connect_timeout'] * 1000));
}
curl_setopt($this->handle, CURLOPT_URL, $url);
curl_setopt($this->handle, CURLOPT_REFERER, $url);
curl_setopt($this->handle, CURLOPT_USERAGENT, $options['useragent']);
if (!empty($headers)) {
curl_setopt($this->handle, CURLOPT_HTTPHEADER, $headers);
}
if ($options['protocol_version'] === 1.1) {
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
}
else {
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
}
if (true === $options['blocking']) {
curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, array(&$this, 'stream_headers'));
curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, array(&$this, 'stream_body'));
curl_setopt($this->handle, CURLOPT_BUFFERSIZE, Requests::BUFFER_SIZE);
}
}
/**
* Process a response
*
* @param string $response Response data from the body
* @param array $options Request options
* @return string HTTP response data including headers
*/
public function process_response($response, $options) {
if ($options['blocking'] === false) {
$fake_headers = '';
$options['hooks']->dispatch('curl.after_request', array(&$fake_headers));
return false;
}
if ($options['filename'] !== false) {
fclose($this->stream_handle);
$this->headers = trim($this->headers);
}
else {
$this->headers .= $response;
}
if (curl_errno($this->handle)) {
$error = sprintf(
'cURL error %s: %s',
curl_errno($this->handle),
curl_error($this->handle)
);
throw new Requests_Exception($error, 'curlerror', $this->handle);
}
$this->info = curl_getinfo($this->handle);
$options['hooks']->dispatch('curl.after_request', array(&$this->headers, &$this->info));
return $this->headers;
}
/**
* Collect the headers as they are received
*
* @param resource $handle cURL resource
* @param string $headers Header string
* @return integer Length of provided header
*/
public function stream_headers($handle, $headers) {
// Why do we do this? cURL will send both the final response and any
// interim responses, such as a 100 Continue. We don't need that.
// (We may want to keep this somewhere just in case)
if ($this->done_headers) {
$this->headers = '';
$this->done_headers = false;
}
$this->headers .= $headers;
if ($headers === "\r\n") {
$this->done_headers = true;
}
return strlen($headers);
}
/**
* Collect data as it's received
*
* @since 1.6.1
*
* @param resource $handle cURL resource
* @param string $data Body data
* @return integer Length of provided data
*/
public function stream_body($handle, $data) {
$this->hooks->dispatch('request.progress', array($data, $this->response_bytes, $this->response_byte_limit));
$data_length = strlen($data);
// Are we limiting the response size?
if ($this->response_byte_limit) {
if ($this->response_bytes === $this->response_byte_limit) {
// Already at maximum, move on
return $data_length;
}
if (($this->response_bytes + $data_length) > $this->response_byte_limit) {
// Limit the length
$limited_length = ($this->response_byte_limit - $this->response_bytes);
$data = substr($data, 0, $limited_length);
}
}
if ($this->stream_handle) {
fwrite($this->stream_handle, $data);
}
else {
$this->response_data .= $data;
}
$this->response_bytes += strlen($data);
return $data_length;
}
/**
* Format a URL given GET data
*
* @param string $url
* @param array|object $data Data to build query using, see {@see https://secure.php.net/http_build_query}
* @return string URL with data
*/
protected static function format_get($url, $data) {
if (!empty($data)) {
$url_parts = parse_url($url);
if (empty($url_parts['query'])) {
$query = $url_parts['query'] = '';
}
else {
$query = $url_parts['query'];
}
$query .= '&' . http_build_query($data, null, '&');
$query = trim($query, '&');
if (empty($url_parts['query'])) {
$url .= '?' . $query;
}
else {
$url = str_replace($url_parts['query'], $query, $url);
}
}
return $url;
}
/**
* Whether this transport is valid
*
* @codeCoverageIgnore
* @return boolean True if the transport is valid, false otherwise.
*/
public static function test($capabilities = array()) {
if (!function_exists('curl_init') || !function_exists('curl_exec')) {
return false;
}
// If needed, check that our installed curl version supports SSL
if (isset($capabilities['ssl']) && $capabilities['ssl']) {
$curl_version = curl_version();
if (!(CURL_VERSION_SSL & $curl_version['features'])) {
return false;
}
}
return true;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,444 @@
<?php
/**
* fsockopen HTTP transport
*
* @package Requests
* @subpackage Transport
*/
/**
* fsockopen HTTP transport
*
* @package Requests
* @subpackage Transport
*/
class Requests_Transport_fsockopen implements Requests_Transport {
/**
* Second to microsecond conversion
*
* @var integer
*/
const SECOND_IN_MICROSECONDS = 1000000;
/**
* Raw HTTP data
*
* @var string
*/
public $headers = '';
/**
* Stream metadata
*
* @var array Associative array of properties, see {@see https://secure.php.net/stream_get_meta_data}
*/
public $info;
/**
* What's the maximum number of bytes we should keep?
*
* @var int|bool Byte count, or false if no limit.
*/
protected $max_bytes = false;
protected $connect_error = '';
/**
* Perform a request
*
* @throws Requests_Exception On failure to connect to socket (`fsockopenerror`)
* @throws Requests_Exception On socket timeout (`timeout`)
*
* @param string $url URL to request
* @param array $headers Associative array of request headers
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
* @param array $options Request options, see {@see Requests::response()} for documentation
* @return string Raw HTTP result
*/
public function request($url, $headers = array(), $data = array(), $options = array()) {
$options['hooks']->dispatch('fsockopen.before_request');
$url_parts = parse_url($url);
if (empty($url_parts)) {
throw new Requests_Exception('Invalid URL.', 'invalidurl', $url);
}
$host = $url_parts['host'];
$context = stream_context_create();
$verifyname = false;
$case_insensitive_headers = new Requests_Utility_CaseInsensitiveDictionary($headers);
// HTTPS support
if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') {
$remote_socket = 'ssl://' . $host;
if (!isset($url_parts['port'])) {
$url_parts['port'] = 443;
}
$context_options = array(
'verify_peer' => true,
// 'CN_match' => $host,
'capture_peer_cert' => true
);
$verifyname = true;
// SNI, if enabled (OpenSSL >=0.9.8j)
if (defined('OPENSSL_TLSEXT_SERVER_NAME') && OPENSSL_TLSEXT_SERVER_NAME) {
$context_options['SNI_enabled'] = true;
if (isset($options['verifyname']) && $options['verifyname'] === false) {
$context_options['SNI_enabled'] = false;
}
}
if (isset($options['verify'])) {
if ($options['verify'] === false) {
$context_options['verify_peer'] = false;
}
elseif (is_string($options['verify'])) {
$context_options['cafile'] = $options['verify'];
}
}
if (isset($options['verifyname']) && $options['verifyname'] === false) {
$context_options['verify_peer_name'] = false;
$verifyname = false;
}
stream_context_set_option($context, array('ssl' => $context_options));
}
else {
$remote_socket = 'tcp://' . $host;
}
$this->max_bytes = $options['max_bytes'];
if (!isset($url_parts['port'])) {
$url_parts['port'] = 80;
}
$remote_socket .= ':' . $url_parts['port'];
set_error_handler(array($this, 'connect_error_handler'), E_WARNING | E_NOTICE);
$options['hooks']->dispatch('fsockopen.remote_socket', array(&$remote_socket));
$socket = stream_socket_client($remote_socket, $errno, $errstr, ceil($options['connect_timeout']), STREAM_CLIENT_CONNECT, $context);
restore_error_handler();
if ($verifyname && !$this->verify_certificate_from_context($host, $context)) {
throw new Requests_Exception('SSL certificate did not match the requested domain name', 'ssl.no_match');
}
if (!$socket) {
if ($errno === 0) {
// Connection issue
throw new Requests_Exception(rtrim($this->connect_error), 'fsockopen.connect_error');
}
throw new Requests_Exception($errstr, 'fsockopenerror', null, $errno);
}
$data_format = $options['data_format'];
if ($data_format === 'query') {
$path = self::format_get($url_parts, $data);
$data = '';
}
else {
$path = self::format_get($url_parts, array());
}
$options['hooks']->dispatch('fsockopen.remote_host_path', array(&$path, $url));
$request_body = '';
$out = sprintf("%s %s HTTP/%.1f\r\n", $options['type'], $path, $options['protocol_version']);
if ($options['type'] !== Requests::TRACE) {
if (is_array($data)) {
$request_body = http_build_query($data, null, '&');
}
else {
$request_body = $data;
}
if (!empty($data)) {
if (!isset($case_insensitive_headers['Content-Length'])) {
$headers['Content-Length'] = strlen($request_body);
}
if (!isset($case_insensitive_headers['Content-Type'])) {
$headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
}
}
}
if (!isset($case_insensitive_headers['Host'])) {
$out .= sprintf('Host: %s', $url_parts['host']);
if (( 'http' === strtolower($url_parts['scheme']) && $url_parts['port'] !== 80 ) || ( 'https' === strtolower($url_parts['scheme']) && $url_parts['port'] !== 443 )) {
$out .= ':' . $url_parts['port'];
}
$out .= "\r\n";
}
if (!isset($case_insensitive_headers['User-Agent'])) {
$out .= sprintf("User-Agent: %s\r\n", $options['useragent']);
}
$accept_encoding = $this->accept_encoding();
if (!isset($case_insensitive_headers['Accept-Encoding']) && !empty($accept_encoding)) {
$out .= sprintf("Accept-Encoding: %s\r\n", $accept_encoding);
}
$headers = Requests::flatten($headers);
if (!empty($headers)) {
$out .= implode($headers, "\r\n") . "\r\n";
}
$options['hooks']->dispatch('fsockopen.after_headers', array(&$out));
if (substr($out, -2) !== "\r\n") {
$out .= "\r\n";
}
if (!isset($case_insensitive_headers['Connection'])) {
$out .= "Connection: Close\r\n";
}
$out .= "\r\n" . $request_body;
$options['hooks']->dispatch('fsockopen.before_send', array(&$out));
fwrite($socket, $out);
$options['hooks']->dispatch('fsockopen.after_send', array($out));
if (!$options['blocking']) {
fclose($socket);
$fake_headers = '';
$options['hooks']->dispatch('fsockopen.after_request', array(&$fake_headers));
return '';
}
$timeout_sec = (int) floor($options['timeout']);
if ($timeout_sec == $options['timeout']) {
$timeout_msec = 0;
}
else {
$timeout_msec = self::SECOND_IN_MICROSECONDS * $options['timeout'] % self::SECOND_IN_MICROSECONDS;
}
stream_set_timeout($socket, $timeout_sec, $timeout_msec);
$response = $body = $headers = '';
$this->info = stream_get_meta_data($socket);
$size = 0;
$doingbody = false;
$download = false;
if ($options['filename']) {
$download = fopen($options['filename'], 'wb');
}
while (!feof($socket)) {
$this->info = stream_get_meta_data($socket);
if ($this->info['timed_out']) {
throw new Requests_Exception('fsocket timed out', 'timeout');
}
$block = fread($socket, Requests::BUFFER_SIZE);
if (!$doingbody) {
$response .= $block;
if (strpos($response, "\r\n\r\n")) {
list($headers, $block) = explode("\r\n\r\n", $response, 2);
$doingbody = true;
}
}
// Are we in body mode now?
if ($doingbody) {
$options['hooks']->dispatch('request.progress', array($block, $size, $this->max_bytes));
$data_length = strlen($block);
if ($this->max_bytes) {
// Have we already hit a limit?
if ($size === $this->max_bytes) {
continue;
}
if (($size + $data_length) > $this->max_bytes) {
// Limit the length
$limited_length = ($this->max_bytes - $size);
$block = substr($block, 0, $limited_length);
}
}
$size += strlen($block);
if ($download) {
fwrite($download, $block);
}
else {
$body .= $block;
}
}
}
$this->headers = $headers;
if ($download) {
fclose($download);
}
else {
$this->headers .= "\r\n\r\n" . $body;
}
fclose($socket);
$options['hooks']->dispatch('fsockopen.after_request', array(&$this->headers, &$this->info));
return $this->headers;
}
/**
* Send multiple requests simultaneously
*
* @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request}
* @param array $options Global options, see {@see Requests::response()} for documentation
* @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well)
*/
public function request_multiple($requests, $options) {
$responses = array();
$class = get_class($this);
foreach ($requests as $id => $request) {
try {
$handler = new $class();
$responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']);
$request['options']['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$id], $request));
}
catch (Requests_Exception $e) {
$responses[$id] = $e;
}
if (!is_string($responses[$id])) {
$request['options']['hooks']->dispatch('multiple.request.complete', array(&$responses[$id], $id));
}
}
return $responses;
}
/**
* Retrieve the encodings we can accept
*
* @return string Accept-Encoding header value
*/
protected static function accept_encoding() {
$type = array();
if (function_exists('gzinflate')) {
$type[] = 'deflate;q=1.0';
}
if (function_exists('gzuncompress')) {
$type[] = 'compress;q=0.5';
}
$type[] = 'gzip;q=0.5';
return implode(', ', $type);
}
/**
* Format a URL given GET data
*
* @param array $url_parts
* @param array|object $data Data to build query using, see {@see https://secure.php.net/http_build_query}
* @return string URL with data
*/
protected static function format_get($url_parts, $data) {
if (!empty($data)) {
if (empty($url_parts['query'])) {
$url_parts['query'] = '';
}
$url_parts['query'] .= '&' . http_build_query($data, null, '&');
$url_parts['query'] = trim($url_parts['query'], '&');
}
if (isset($url_parts['path'])) {
if (isset($url_parts['query'])) {
$get = $url_parts['path'] . '?' . $url_parts['query'];
}
else {
$get = $url_parts['path'];
}
}
else {
$get = '/';
}
return $get;
}
/**
* Error handler for stream_socket_client()
*
* @param int $errno Error number (e.g. E_WARNING)
* @param string $errstr Error message
*/
public function connect_error_handler($errno, $errstr) {
// Double-check we can handle it
if (($errno & E_WARNING) === 0 && ($errno & E_NOTICE) === 0) {
// Return false to indicate the default error handler should engage
return false;
}
$this->connect_error .= $errstr . "\n";
return true;
}
/**
* Verify the certificate against common name and subject alternative names
*
* Unfortunately, PHP doesn't check the certificate against the alternative
* names, leading things like 'https://www.github.com/' to be invalid.
* Instead
*
* @see https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
*
* @throws Requests_Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`)
* @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`)
* @param string $host Host name to verify against
* @param resource $context Stream context
* @return bool
*/
public function verify_certificate_from_context($host, $context) {
$meta = stream_context_get_options($context);
// If we don't have SSL options, then we couldn't make the connection at
// all
if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) {
throw new Requests_Exception(rtrim($this->connect_error), 'ssl.connect_error');
}
$cert = openssl_x509_parse($meta['ssl']['peer_certificate']);
return Requests_SSL::verify_certificate($host, $cert);
}
/**
* Whether this transport is valid
*
* @codeCoverageIgnore
* @return boolean True if the transport is valid, false otherwise.
*/
public static function test($capabilities = array()) {
if (!function_exists('fsockopen')) {
return false;
}
// If needed, check that streams support SSL
if (isset($capabilities['ssl']) && $capabilities['ssl']) {
if (!extension_loaded('openssl') || !function_exists('openssl_x509_parse')) {
return false;
}
// Currently broken, thanks to https://github.com/facebook/hhvm/issues/2156
if (defined('HHVM_VERSION')) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,103 @@
<?php
/**
* Case-insensitive dictionary, suitable for HTTP headers
*
* @package Requests
* @subpackage Utilities
*/
/**
* Case-insensitive dictionary, suitable for HTTP headers
*
* @package Requests
* @subpackage Utilities
*/
class Requests_Utility_CaseInsensitiveDictionary implements ArrayAccess, IteratorAggregate {
/**
* Actual item data
*
* @var array
*/
protected $data = array();
/**
* Creates a case insensitive dictionary.
*
* @param array $data Dictionary/map to convert to case-insensitive
*/
public function __construct(array $data = array()) {
foreach ($data as $key => $value) {
$this->offsetSet($key, $value);
}
}
/**
* Check if the given item exists
*
* @param string $key Item key
* @return boolean Does the item exist?
*/
public function offsetExists($key) {
$key = strtolower($key);
return isset($this->data[$key]);
}
/**
* Get the value for the item
*
* @param string $key Item key
* @return string Item value
*/
public function offsetGet($key) {
$key = strtolower($key);
if (!isset($this->data[$key])) {
return null;
}
return $this->data[$key];
}
/**
* Set the given item
*
* @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
*
* @param string $key Item name
* @param string $value Item value
*/
public function offsetSet($key, $value) {
if ($key === null) {
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
}
$key = strtolower($key);
$this->data[$key] = $value;
}
/**
* Unset the given header
*
* @param string $key
*/
public function offsetUnset($key) {
unset($this->data[strtolower($key)]);
}
/**
* Get an iterator for the data
*
* @return ArrayIterator
*/
public function getIterator() {
return new ArrayIterator($this->data);
}
/**
* Get the headers as an array
*
* @return array Header data
*/
public function getAll() {
return $this->data;
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Iterator for arrays requiring filtered values
*
* @package Requests
* @subpackage Utilities
*/
/**
* Iterator for arrays requiring filtered values
*
* @package Requests
* @subpackage Utilities
*/
class Requests_Utility_FilteredIterator extends ArrayIterator {
/**
* Callback to run as a filter
*
* @var callable
*/
protected $callback;
/**
* Create a new iterator
*
* @param array $data
* @param callable $callback Callback to be called on each value
*/
public function __construct($data, $callback) {
parent::__construct($data);
$this->callback = $callback;
}
/**
* Get the current item's value after filtering
*
* @return string
*/
public function current() {
$value = parent::current();
$value = call_user_func($this->callback, $value);
return $value;
}
}

View File

@@ -0,0 +1,41 @@
<?php
class TSDNS {
protected $url = null;
protected $apiKey = null;
function __construct($url, $apiKey) {
$this->url = $url;
$this->apiKey = $apiKey;
}
function getZones() {
$command = '/list';
return $this->sendRequest($command);
}
function addZone($zone, $target) {
$command = '/add/' . $zone . '/' . $target;
return $this->sendRequest($command);
}
function deleteZone($zone) {
$command = '/del/' . $zone;
return $this->sendRequest($command);
}
function getZone($zone) {
$command = '/get/' . $zone;
return $this->sendRequest($command);
}
function sendRequest($command) {
$url = $this->url . $command;
$headers = array('Accept' => 'application/json', 'Authorization' => $this->apiKey);
Requests::register_autoloader();
$request = Requests::get($url, $headers);
return $request;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,555 @@
<?php
declare(strict_types=1);
if (!defined("WHMCS")) {
die("This file cannot be accessed directly");
}
use Illuminate\Database\Capsule\Manager as Capsule;
/**
* Backup management
*/
function teamspeak_backups(array $params)
{
try {
$port = $params['customfields']['Port'] ?? null;
if (!$port) {
throw new Exception('Port does not exist in custom fields');
}
$backups = Capsule::table('modwhmcs_teamspeak_backups')
->where('port', $port)
->get();
$vars = [
'backups' => array_map(fn($backup) => get_object_vars($backup), iterator_to_array($backups)),
];
// Handle backup actions
if (!empty($_GET['custom'])) {
$action = $_GET['custom'];
$backupId = (int)($_GET['backupid'] ?? 0);
switch ($action) {
case 'create':
$tsAdmin = new TeamSpeak($params['serverip'], $params['serverport']);
if (!$tsAdmin->getElement('success', $tsAdmin->connect())) {
throw new Exception('Server unavailable. Please contact support.');
}
if (!$tsAdmin->getElement('success', $tsAdmin->login($params['serverusername'], $params['serverpassword']))) {
throw new Exception('Unable to connect to server. Please contact support.');
}
if (!$tsAdmin->getElement('success', $tsAdmin->selectServer($port))) {
throw new Exception('Server is offline or does not exist');
}
$getsid = $tsAdmin->serverIdGetByPort($port);
$snapshot = $tsAdmin->serverSnapshotCreate();
if (!$tsAdmin->getElement('success', $snapshot)) {
throw new Exception('Failed to create backup snapshot');
}
$snapshotData = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", ltrim($snapshot['data']));
Capsule::table('modwhmcs_teamspeak_backups')->insert([
'sid' => $getsid['data']['server_id'],
'port' => $port,
'data' => $snapshotData,
'date' => date("Y-m-d H:i:s"),
]);
break;
case 'download':
if (!$backupId) {
throw new Exception('Backup ID not specified');
}
$data = Capsule::table('modwhmcs_teamspeak_backups')
->where('id', $backupId)
->value('data');
if (!$data) {
throw new Exception('Backup not found');
}
header('Content-Type: text/plain; charset=utf-8');
header('Content-Disposition: attachment; filename="backup_' . date("Y-m-d_His") . '.txt"');
echo $data;
exit;
case 'restore':
if (!$backupId) {
throw new Exception('Backup ID not specified');
}
$backup = Capsule::table('modwhmcs_teamspeak_backups')
->where('id', $backupId)
->value('data');
if (!$backup) {
throw new Exception('Backup not found');
}
$tsAdmin = new TeamSpeak($params['serverip'], $params['serverport']);
if (!$tsAdmin->getElement('success', $tsAdmin->connect())) {
throw new Exception('Server unavailable. Please contact support.');
}
if (!$tsAdmin->getElement('success', $tsAdmin->login($params['serverusername'], $params['serverpassword']))) {
throw new Exception('Unable to connect to server. Please contact support.');
}
if (!$tsAdmin->getElement('success', $tsAdmin->selectServer($port))) {
throw new Exception('Server is offline or does not exist');
}
if (!$tsAdmin->getElement('success', $tsAdmin->serverSnapshotDeploy($backup))) {
throw new Exception('Backup could not be restored');
}
break;
case 'delete':
if (!$backupId) {
throw new Exception('Backup ID not specified');
}
Capsule::table('modwhmcs_teamspeak_backups')
->where('id', $backupId)
->delete();
break;
default:
throw new Exception('Invalid action');
}
return 'success';
}
return [
'templatefile' => 'templates/backups',
'vars' => $vars,
];
} catch (Exception $e) {
logModuleCall(
'teamspeak',
__FUNCTION__,
$params,
$e->getMessage(),
$e->getTraceAsString()
);
return $e->getMessage();
}
}
/**
* Token/Privileges management
*/
function teamspeak_tokens(array $params)
{
try {
$port = $params['customfields']['Port'] ?? null;
if (!$port) {
throw new Exception('Port does not exist in custom fields');
}
$tsAdmin = new TeamSpeak($params['serverip'], $params['serverport']);
if (!$tsAdmin->getElement('success', $tsAdmin->connect())) {
throw new Exception('Server unavailable. Please contact support.');
}
if (!$tsAdmin->getElement('success', $tsAdmin->login($params['serverusername'], $params['serverpassword']))) {
throw new Exception('Unable to connect to server. Please contact support.');
}
if (!$tsAdmin->getElement('success', $tsAdmin->selectServer($port))) {
throw new Exception('Server is offline or does not exist');
}
// Get token list
$response = $tsAdmin->tokenList();
$tokens = $response['data'] ?? [];
// Get server group list
$serverglist = $tsAdmin->serverGroupList();
$sglist = [];
if (isset($serverglist['data'])) {
foreach ($serverglist['data'] as $sg) {
if (($sg['type'] ?? 0) == 1) {
$sglist[] = $sg;
}
}
}
$vars = [
'tokens' => $tokens,
'sglist' => $sglist,
];
// Handle token actions
if (!empty($_GET['custom'])) {
$action = $_GET['custom'];
switch ($action) {
case 'create':
$groupId = (int)($_GET['groupid'] ?? 0);
$description = $_GET['desc'] ?? '';
if (!$groupId) {
throw new Exception('Group ID not specified');
}
if (!$tsAdmin->getElement('success', $tsAdmin->tokenAdd(0, $groupId, 0, $description))) {
throw new Exception('Unable to create token. Please contact support.');
}
break;
case 'delete':
$token = $_GET['token'] ?? '';
if (!$token) {
throw new Exception('Token not specified');
}
if (!$tsAdmin->getElement('success', $tsAdmin->tokenDelete($token))) {
throw new Exception('Unable to delete token. Please contact support.');
}
break;
default:
throw new Exception('Invalid action');
}
return 'success';
}
return [
'templatefile' => 'templates/tokens',
'vars' => $vars,
];
} catch (Exception $e) {
logModuleCall(
'teamspeak',
__FUNCTION__,
$params,
$e->getMessage(),
$e->getTraceAsString()
);
return $e->getMessage();
}
}
/**
* Ban management
*/
function teamspeak_bans(array $params)
{
try {
$port = $params['customfields']['Port'] ?? null;
if (!$port) {
throw new Exception('Port does not exist in custom fields');
}
$tsAdmin = new TeamSpeak($params['serverip'], $params['serverport']);
if (!$tsAdmin->getElement('success', $tsAdmin->connect())) {
throw new Exception('Server unavailable. Please contact support.');
}
if (!$tsAdmin->getElement('success', $tsAdmin->login($params['serverusername'], $params['serverpassword']))) {
throw new Exception('Unable to connect to server. Please contact support.');
}
if (!$tsAdmin->getElement('success', $tsAdmin->selectServer($port))) {
throw new Exception('Server is offline or does not exist');
}
// Get ban list
$response = $tsAdmin->banList();
$bans = $response['data'] ?? [];
$vars = [
'bans' => $bans,
];
// Handle ban actions
if (!empty($_GET['custom'])) {
$action = $_GET['custom'];
switch ($action) {
case 'create':
$banType = $_GET['bantype'] ?? '';
$input = $_GET['inu'] ?? '';
$reason = $_GET['reason'] ?? '';
if (!$banType || !$input) {
throw new Exception('Ban type and input are required');
}
switch ($banType) {
case 'ip':
if (filter_var($input, FILTER_VALIDATE_IP) === false) {
throw new Exception('Invalid IP address');
}
if (!$tsAdmin->getElement('success', $tsAdmin->banAddByIp($input, 0, $reason))) {
throw new Exception('Unable to create IP ban. Please contact support.');
}
break;
case 'name':
if (!$tsAdmin->getElement('success', $tsAdmin->banAddByName($input, 0, $reason))) {
throw new Exception('Unable to create name ban. Please contact support.');
}
break;
case 'uid':
if (!$tsAdmin->getElement('success', $tsAdmin->banAddByUid($input, 0, $reason))) {
throw new Exception('Unable to create UID ban. Please contact support.');
}
break;
default:
throw new Exception('Invalid ban type');
}
break;
case 'delete':
$banId = (int)($_GET['banid'] ?? 0);
if (!$banId) {
throw new Exception('Ban ID not specified');
}
if (!$tsAdmin->getElement('success', $tsAdmin->banDelete($banId))) {
throw new Exception('Unable to delete ban. Please contact support.');
}
break;
default:
throw new Exception('Invalid action');
}
return 'success';
}
return [
'templatefile' => 'templates/bans',
'vars' => $vars,
];
} catch (Exception $e) {
logModuleCall(
'teamspeak',
__FUNCTION__,
$params,
$e->getMessage(),
$e->getTraceAsString()
);
return $e->getMessage();
}
}
/**
* Server settings management
*/
function teamspeak_settings(array $params)
{
try {
$port = $params['customfields']['Port'] ?? null;
if (!$port) {
throw new Exception('Port does not exist in custom fields');
}
$tsAdmin = new TeamSpeak($params['serverip'], $params['serverport']);
if (!$tsAdmin->getElement('success', $tsAdmin->connect())) {
throw new Exception('Could not connect to the TeamSpeak server');
}
if (!$tsAdmin->getElement('success', $tsAdmin->login($params['serverusername'], $params['serverpassword']))) {
throw new Exception('TeamSpeak ServerQuery login failed');
}
if (!$tsAdmin->getElement('success', $tsAdmin->selectServer($port))) {
throw new Exception('Server not found on port: ' . $port);
}
$serverinfo = $tsAdmin->serverInfo();
if (!$tsAdmin->getElement('success', $serverinfo)) {
throw new Exception('Could not retrieve server information');
}
$vars = [
'serverinfo' => $serverinfo['data'] ?? [],
];
// Handle settings save
if (isset($_GET['custom']) && $_GET['custom'] === 'save') {
$password = $_GET['pw'] ?? '';
$confirmPassword = $_GET['confirmpw'] ?? '';
$hostname = $_GET['hostname'] ?? '';
$welcomeMessage = $_GET['welcomemessage'] ?? '';
if ($password !== $confirmPassword) {
throw new Exception('The passwords entered do not match');
}
$data = [
'virtualserver_name' => $hostname,
'virtualserver_welcomemessage' => $welcomeMessage,
];
// Only update password if provided
if (!empty($password)) {
$data['virtualserver_password'] = $password;
}
if (!$tsAdmin->getElement('success', $tsAdmin->serverEdit($data))) {
throw new Exception('Failed to update server settings. Please contact support.');
}
return 'success';
}
return [
'templatefile' => 'templates/settings',
'vars' => $vars,
];
} catch (Exception $e) {
logModuleCall(
'teamspeak',
__FUNCTION__,
$params,
$e->getMessage(),
$e->getTraceAsString()
);
return $e->getMessage();
}
}
/**
* TSDNS zone management continuation
*/
function teamspeak_tsdns(array $params)
{
try {
$settings = Capsule::table('modwhmcs_teamspeak_settings')
->select('enabletsdns', 'domaintsdns', 'urlapi', 'keyapi')
->first();
$vars = [
'settings' => $settings ? get_object_vars($settings) : [],
'customfields' => $params['customfields'],
];
// Handle zone edit
if (isset($_GET['ma']) && $_GET['ma'] === 'editzone') {
$newZone = $_GET['zone'] ?? '';
$oldZone = $_GET['oldzone'] ?? '';
if (empty($newZone)) {
throw new Exception('No zone was specified');
}
if ($newZone === $oldZone) {
throw new Exception('The new zone is the same as the old zone');
}
if (!$settings) {
throw new Exception('TSDNS settings not found');
}
$tsdnsClient = new TSDNS($settings->urlapi, $settings->keyapi);
// Check if new zone exists
$response = $tsdnsClient->getZone($newZone . '.' . $settings->domaintsdns);
$result = json_decode($response->body);
if (isset($result->message) && count($result->message) > 0) {
throw new Exception('The specified zone already exists');
}
// Delete old zone and add new one
$tsdnsClient->deleteZone($oldZone . '.' . $settings->domaintsdns);
$response = $tsdnsClient->addZone(
$newZone . '.' . $settings->domaintsdns,
$params['serverip'] . ':' . $params['customfields']['Port']
);
$result = json_decode($response->body);
if (isset($result->message) && count($result->message) > 0) {
throw new Exception('The zone could not be updated');
}
// Update custom field
Capsule::table('tblcustomfieldsvalues')
->where('value', $params['customfields']['Subdomain'])
->update(['value' => $newZone]);
return 'success';
}
return [
'templatefile' => 'templates/tsdns',
'vars' => $vars,
];
} catch (Exception $e) {
logModuleCall(
'teamspeak',
__FUNCTION__,
$params,
$e->getMessage(),
$e->getTraceAsString()
);
return $e->getMessage();
}
}
/**
* Helper function to find available port
*/
function _modwhmcs_findPort(array $params, int $startPort, int $endPort): int
{
$tsAdmin = new TeamSpeak($params['serverip'], $params['serverport']);
if (!$tsAdmin->getElement('success', $tsAdmin->connect())) {
return 0;
}
if (!$tsAdmin->getElement('success', $tsAdmin->login($params['serverusername'], $params['serverpassword']))) {
return 0;
}
$currentPort = $startPort;
while ($currentPort <= $endPort) {
if (!$tsAdmin->getElement('success', $tsAdmin->serverIdGetByPort($currentPort))) {
return $currentPort;
}
$currentPort++;
}
return 0;
}

View File

@@ -0,0 +1,56 @@
<a href="clientarea.php?action=productdetails&id={$serviceid}" class="btn btn-primary pull-right"><i
class="fa fa-arrow-left"></i>&nbsp;&nbsp;Back</a>
<div class="clearfix"></div>
<hr>
<div class="panel panel-default">
<div class="panel-heading">Backups</div>
<div class="panel-body">
<form method="get" action="clientarea.php">
<input type="hidden" name="action" value="productdetails"/>
<input type="hidden" name="id" value="{$serviceid}"/>
<input type="hidden" name="modop" value="custom"/>
<input type="hidden" name="a" value="backups"/>
<table class="table table-condensed">
<thead>
<tr>
<th>#</th>
<th>Date/Time</th>
<th><i class="fa fa-hand-o-down"></i></th>
</tr>
</thead>
<tbody>
{if !empty($backups)}
{assign var=var value=1}
{foreach $backups as $backup}
<tr>
<td>{$var}</td>
<td>{$backup.date|date_format:"%d/%m/%Y %H:%M:%S"}</td>
<td><input type="radio" name="backupid" id="{$backup.id}" value="{$backup.id}"
{if $var eq 1}checked{/if}/></td>
</tr>
{capture assign=var}{$var+1}{/capture}
{/foreach}
{else}
<tr>
<td colspan="2" class="text-center">There is no backup</td>
</tr>
{/if}
</tbody>
</table>
</br>
<div class="btn-group pull-right">
<button type="submit" class="btn btn-success" name="custom" value="create"><i class="fa fa-hdd-o"></i>&nbsp;&nbsp;Create
</button>
<button type="submit" class="btn btn-primary" name="custom" value="download"><i
class="fa fa-download"></i>&nbsp;&nbsp;Download
</button>
<button type="submit" class="btn btn-warning" name="custom" value="restore"><i class="fa fa-undo"></i>&nbsp;&nbsp;Restore
</button>
<button type="submit" class="btn btn-danger" name="custom" value="delete"><i class="fa fa-trash" aria-hidden="true"></i>&nbsp;&nbsp;Delete
</button>
</div>
</form>
</div>
</div>

View File

@@ -0,0 +1,92 @@
<a href="clientarea.php?action=productdetails&id={$serviceid}" class="btn btn-primary pull-right"><i
class="fa fa-arrow-left"></i>&nbsp;&nbsp;Back</a>
<div class="clearfix"></div>
<hr>
<div class="panel panel-default">
<div class="panel-heading">Bans</div>
<div class="panel-body">
<form method="get" action="clientarea.php">
<input type="hidden" name="action" value="productdetails"/>
<input type="hidden" name="id" value="{$serviceid}"/>
<input type="hidden" name="modop" value="custom"/>
<input type="hidden" name="a" value="bans"/>
<input type="hidden" name="custom" value="delete"/>
<div class="table-responsive">
<table class="table table-condensed">
<thead>
<tr>
<th>#</th>
<th>IP/Name/UID</th>
<th>Date/Time</th>
<th>Reason</th>
<th>Duration</th>
<th>Banned by</th>
<th>Options</th>
</tr>
</thead>
<tbody>
{if !empty($bans)}
{assign var=var value=1}
{foreach $bans as $ban}
<tr>
<td>{$var}</td>
<td>{if $ban.ip}{$ban.ip|replace:"\\":""}{elseif $ban.name} {$ban.name}{elseif $ban.uid} {$ban.uid}{/if}</td>
<td>{$ban.created|date_format:"%d-%m-%Y %H:%M:%S"}</td>
<td>{$ban.reason}</td>
{assign var="sumban" value="`$ban.created+$ban.duration`"}
<td>{if $ban.duration eq 0}Indeterminada{else}{$sumban|date_format:"%d-%m-%Y %H:%M:%S"}{/if}</td>
<td>{$ban.invokername}</td>
<td>
<button type="submit" class="btn btn-danger btn-xs" name="banid" id="{$ban.banid}"
value="{$ban.banid}">Excluir
</button>
</td>
</tr>
{capture assign=var}{$var+1}{/capture}
{/foreach}
{else}
<tr>
<td colspan="7" class="text-center">There is no Ban</td>
</tr>
{/if}
</tbody>
</table>
</div>
</form>
<hr>
<form method="get" action="clientarea.php" class="form-horizontal text-center">
<input type="hidden" name="action" value="productdetails"/>
<input type="hidden" name="id" value="{$serviceid}"/>
<input type="hidden" name="modop" value="custom"/>
<input type="hidden" name="a" value="bans"/>
<div class="form-group">
<label for="bantype" class="control-label col-sm-2">Select Type:</label>
<div class="col-sm-2">
<select name="bantype" id="bantype" class="form-control">
<option value="ip">IP</option>
<option value="name">Name</option>
<option value="uid">UID</option>
</select>
</div>
</div>
<div class="form-group">
<label for="inu" class="control-label col-sm-2">IP/Name/UID</label>
<div class="col-sm-5">
<input type="text" class="form-control" name="inu" id="inu" value="" required/>
</div>
</div>
<div class="form-group">
<label for="reason" class="control-label col-sm-2">Reason</label>
<div class="col-sm-5">
<div class="input-group">
<input type="text" class="form-control" name="reason" id="reason" value=""/>
<span class="input-group-btn">
<button type="submit" class="btn btn-warning" name="custom" value="create"><i class="fa fa-ban"></i>&nbsp;&nbsp;Create
</button>
</span>
</div>
</div>
</div>
</form>
</div>
</div>

View File

@@ -0,0 +1,4 @@
<div class="alert alert-danger">
<h5>$lang.warn}</h5>
<p>{$usefulErrorHelper}</p>
</div>

View File

@@ -0,0 +1,277 @@
<script>
function alertme(message) {
var x = confirm('Do you want to ' + message + '?');
if (x)
return true;
else
return false;
}
</script>
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{$lang.service}</h3>
</div>
<div class="panel-body text-center">
<center><img src="https://kundencenter.voice4you.org/img/teaspeak-logo.png"/></center>
<em>{$groupname}</em>
<h4 style="margin:0;">{$product}</h4>
{$serverdata.ipaddress}:{$customfields.Port}
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{$lang.dconnect}</h3>
</div>
<div class="panel-body text-center">
{if $settings.enabletsdns eq 1}
{$customfields.Subdomain}.{$settings.domaintsdns}
<p>
<a href="teaclient://{$customfields.Subdomain}.{$settings.domaintsdns}"
class="btn btn-info btn-sm" target="_top">{$lang.lconnect}</a></p>
{else}
<p>
<a href="teaclient://{$serverdata.ipaddress}?port={$customfields.Port}"
class="btn btn-info btn-sm" target="_top">{$lang.lconnect}</a></p>
<a target="_blank" href="https://web.teaspeak.de/?connect_default=1&connect_address={$serverdata.ipaddress}:{$customfields.Port}"
class="btn btn-info btn-sm" target="_top">{$lang.wconnect}</a></p>
{/if}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="panel panel-default" id="cPanelAdditionalInfoPanel">
<div class="panel-heading">
<h3 class="panel-title">{$lang.webpanel}</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-3 col-xs-3 text-right">
<strong>{$lang.hostname}</strong>
</div>
<div class="col-md-9 col-xs-9 text-left">
{$serverdata.name}
</div>
<div class="col-md-3 col-xs-3 text-right">
<strong>{$lang.management}</strong>
</div>
<div class="col-md-9 col-xs-9 text-left">
<a target="_blank" href="https://tsi.voice4you.org/">{$lang.webinterface}</a>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
{if $hostteamspeak.status eq true}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{$LANG.cPanel.quickShortcuts}
{if $hostteamspeak.vs.status eq true}
<span class="text-success pull-right"><strong>{$lang.online}</strong></span>
{else}
<span class="text-danger pull-right"><strong>{$lang.offline}</strong></span>
{/if}
</h3>
</div>
<div class="panel-body text-center">
{if $hostteamspeak.vs.status eq false}
<a onclick="return alertme('Start')" href="clientarea.php?action=productdetails&amp;id={$serviceid}&amp;modop=custom&amp;a=start_server"
class="btn btn-success">
<i class="fa fa-play"></i> {$lang.start}
</a>
{else}
<a onclick="return alertme('Stop')" href="clientarea.php?action=productdetails&amp;id={$serviceid}&amp;modop=custom&amp;a=stop_server"
class="btn btn-danger">
<i class="fa fa-stop"></i> {$lang.stop}
</a>
{/if}
<a onclick="return alertme('Reinstall')" href="clientarea.php?action=productdetails&amp;id={$serviceid}&amp;modop=custom&amp;a=reinstall_server"
class="btn btn-warning">
<i class="fa fa-repeat"></i> {$lang.reinstall}
</a>
<a onclick="return alertme('Reset Permissions')" href="clientarea.php?action=productdetails&amp;id={$serviceid}&amp;modop=custom&amp;a=perm_reset"
class="btn btn-primary">
<i class="fa fa-undo"></i> {$lang.reset_permissions}
</a>
</div>
</div>
{elseif $hostteamspeak.status eq false}
<div class="alert alert-danger text-center">Server is {$lang.offline}</div>
{else}
<div class="alert alert-warning text-center" role="alert">
{if $suspendreason}
<strong>{$suspendreason}</strong>
<br/>
{/if}
{$LANG.cPanel.packageNotActive} {$status}.<br/>
{if $systemStatus eq "Pending"}
{$LANG.cPanel.statusPendingNotice}
{elseif $systemStatus eq "Suspended"}
{$LANG.cPanel.statusSuspendedNotice}
{/if}
</div>
{/if}
</div>
<!--
{if $systemStatus == 'Active' && $hostteamspeak.status eq true}
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Log</h3>
</div>
<div class="table-responsive">
<table class="table table-condensed">
<thead>
<tr>
<th>Data</th>
<th>Status</th>
<th>Description</th>
</tr>
<tbody>
{foreach $logs as $log}
<tr>
<td>{$log.0}</td>
<td>{$log.1}</td>
<td>{$log.4|substr:0:80}...</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
</div>
</div>
{/if}
-->
</div>
<div class="row">
<div class="col-md-4">
{if $configurableoptions}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{$LANG.orderconfigpackage}</h3>
</div>
<div class="panel-body">
{foreach from=$configurableoptions item=configoption}
<div class="row">
<div class="col-md-5 col-xs-6 text-right">
<strong>{$configoption.optionname}</strong>
</div>
<div class="col-md-7 col-xs-6 text-left">
{if $configoption.optiontype eq 3}{if $configoption.selectedqty}{$LANG.yes}{else}{$LANG.no}{/if}{elseif $configoption.optiontype eq 4}{$configoption.selectedqty} x {$configoption.selectedoption}{else}{$configoption.selectedoption}{/if}
</div>
</div>
{/foreach}
</div>
</div>
{/if}
</div>
<div class="col-md-8">
{if $customfields}
<div class="panel panel-default" id="cPanelAdditionalInfoPanel">
<div class="panel-heading">
<h3 class="panel-title">{$LANG.additionalInfo}</h3>
</div>
<div class="panel-body">
{foreach from=$customfields key=key item=field}
{if $key neq "Subdomain"}
<div class="row">
<div class="col-md-3 col-xs-3 text-right">
<strong>{$key}</strong>
</div>
<div class="col-md-9 col-xs-9 text-left">
{if empty($field)}
{$LANG.blankCustomField}
{else}
{$field}
{/if}
</div>
</div>
{/if}
{/foreach}
</div>
</div>
{/if}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{$LANG.cPanel.billingOverview}</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-5">
{if $firstpaymentamount neq $recurringamount}
<div class="row" id="firstPaymentAmount">
<div class="col-xs-6 text-right">
{$LANG.firstpaymentamount}
</div>
<div class="col-xs-6">
{$firstpaymentamount}
</div>
</div>
{/if}
{if $billingcycle != $LANG.orderpaymenttermonetime && $billingcycle != $LANG.orderfree}
<div class="row" id="recurringAmount">
<div class="col-xs-6 text-right">
{$LANG.recurringamount}
</div>
<div class="col-xs-6">
{$recurringamount}
</div>
</div>
{/if}
<div class="row" id="billingCycle">
<div class="col-xs-6 text-right">
{$LANG.orderbillingcycle}
</div>
<div class="col-xs-6">
{$billingcycle}
</div>
</div>
<div class="row" id="paymentMethod">
<div class="col-xs-6 text-right">
{$LANG.orderpaymentmethod}
</div>
<div class="col-xs-6">
{$paymentmethod}
</div>
</div>
</div>
<div class="col-md-6">
<div class="row" id="registrationDate">
<div class="col-xs-6 col-md-5 text-right">
{$LANG.clientareahostingregdate}
</div>
<div class="col-xs-6 col-md-7">
{$regdate}
</div>
</div>
<div class="row" id="nextDueDate">
<div class="col-xs-6 col-md-5 text-right">
{$LANG.clientareahostingnextduedate}
</div>
<div class="col-xs-6 col-md-7">
{$nextduedate}
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,351 @@
<style>
.teaspeak-logo {
max-width: 200px;
height: auto;
margin: 1rem 0;
}
.status-badge {
font-size: 0.875rem;
padding: 0.375rem 0.75rem;
}
.server-info-card {
transition: box-shadow 0.3s ease;
}
.server-info-card:hover {
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
.action-btn {
min-width: 120px;
}
</style>
<script>
function confirmAction(message) {
return confirm('Do you want to ' + message + '?');
}
</script>
<div class="container-fluid py-3">
<!-- Server Info & Connection -->
<div class="row g-3 mb-4">
<!-- Server Details -->
<div class="col-lg-6">
<div class="card server-info-card h-100">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">
<i class="fas fa-server me-2"></i>{$lang.service|default:"Service"}
</h5>
</div>
<div class="card-body text-center">
<img src="https://teaspeak.de/img/logo.png" alt="TeaSpeak Logo" class="teaspeak-logo">
<p class="text-muted mb-2"><em>{$groupname}</em></p>
<h5 class="mb-3">{$product}</h5>
<div class="alert alert-info mb-0">
<i class="fas fa-network-wired me-2"></i>
<strong>{$serverdata.ipaddress}:{$customfields.Port}</strong>
</div>
</div>
</div>
</div>
<!-- Connection Info -->
<div class="col-lg-6">
<div class="card server-info-card h-100">
<div class="card-header bg-success text-white">
<h5 class="card-title mb-0">
<i class="fas fa-plug me-2"></i>{$lang.dconnect|default:"Connect"}
</h5>
</div>
<div class="card-body text-center">
{if $settings.enabletsdns eq 1}
<div class="mb-3">
<h6 class="text-muted mb-2">Connection Address:</h6>
<div class="alert alert-success">
<code class="fs-6">{$customfields.Subdomain}.{$settings.domaintsdns}</code>
</div>
</div>
<div class="d-grid gap-2">
<a href="teaclient://{$customfields.Subdomain}.{$settings.domaintsdns}"
class="btn btn-success btn-lg">
<i class="fas fa-desktop me-2"></i>{$lang.lconnect|default:"Launch Client"}
</a>
<a href="https://web.teaspeak.de/?connect_default=1&connect_address={$customfields.Subdomain}.{$settings.domaintsdns}"
target="_blank"
class="btn btn-outline-success">
<i class="fas fa-globe me-2"></i>{$lang.wconnect|default:"Web Client"}
</a>
</div>
{else}
<div class="mb-3">
<h6 class="text-muted mb-2">Connection Address:</h6>
<div class="alert alert-success">
<code class="fs-6">{$serverdata.ipaddress}:{$customfields.Port}</code>
</div>
</div>
<div class="d-grid gap-2">
<a href="teaclient://{$serverdata.ipaddress}?port={$customfields.Port}"
class="btn btn-success btn-lg">
<i class="fas fa-desktop me-2"></i>{$lang.lconnect|default:"Launch Client"}
</a>
<a href="https://web.teaspeak.de/?connect_default=1&connect_address={$serverdata.ipaddress}:{$customfields.Port}"
target="_blank"
class="btn btn-outline-success">
<i class="fas fa-globe me-2"></i>{$lang.wconnect|default:"Web Client"}
</a>
</div>
{/if}
</div>
</div>
</div>
</div>
<!-- Management & Controls -->
<div class="row g-3 mb-4">
<!-- Web Panel Info -->
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header bg-info text-white">
<h5 class="card-title mb-0">
<i class="fas fa-cog me-2"></i>{$lang.webpanel|default:"Web Panel"}
</h5>
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-4 text-end">
<strong>{$lang.hostname|default:"Hostname"}:</strong>
</div>
<div class="col-8">
{$serverdata.name}
</div>
</div>
<div class="row">
<div class="col-4 text-end">
<strong>{$lang.management|default:"Management"}:</strong>
</div>
<div class="col-8">
<a href="https://panel.teaspeak.de/" target="_blank" class="btn btn-sm btn-outline-info">
<i class="fas fa-external-link-alt me-1"></i>{$lang.webinterface|default:"Web Interface"}
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Server Controls -->
<div class="col-lg-6">
{if $hostteamspeak.status eq true}
<div class="card h-100">
<div class="card-header {if $hostteamspeak.vs.status eq true}bg-success{else}bg-danger{/if} text-white">
<h5 class="card-title mb-0">
<i class="fas fa-sliders-h me-2"></i>{$LANG.cPanel.quickShortcuts|default:"Quick Actions"}
<span class="badge bg-light text-dark float-end status-badge">
{if $hostteamspeak.vs.status eq true}
<i class="fas fa-check-circle text-success me-1"></i>{$lang.online|default:"Online"}
{else}
<i class="fas fa-times-circle text-danger me-1"></i>{$lang.offline|default:"Offline"}
{/if}
</span>
</h5>
</div>
<div class="card-body text-center">
<div class="d-grid gap-2">
{if $hostteamspeak.vs.status eq false}
<a onclick="return confirmAction('start the server')"
href="clientarea.php?action=productdetails&id={$serviceid}&modop=custom&a=start_server"
class="btn btn-success action-btn">
<i class="fas fa-play me-2"></i>{$lang.start|default:"Start"}
</a>
{else}
<a onclick="return confirmAction('stop the server')"
href="clientarea.php?action=productdetails&id={$serviceid}&modop=custom&a=stop_server"
class="btn btn-danger action-btn">
<i class="fas fa-stop me-2"></i>{$lang.stop|default:"Stop"}
</a>
{/if}
<a onclick="return confirmAction('reinstall the server')"
href="clientarea.php?action=productdetails&id={$serviceid}&modop=custom&a=reinstall_server"
class="btn btn-warning action-btn">
<i class="fas fa-sync-alt me-2"></i>{$lang.reinstall|default:"Reinstall"}
</a>
<a onclick="return confirmAction('reset permissions')"
href="clientarea.php?action=productdetails&id={$serviceid}&modop=custom&a=perm_reset"
class="btn btn-primary action-btn">
<i class="fas fa-undo me-2"></i>{$lang.reset_permissions|default:"Reset Permissions"}
</a>
</div>
</div>
</div>
{elseif $hostteamspeak.status eq false}
<div class="card h-100">
<div class="card-body">
<div class="alert alert-danger text-center mb-0" role="alert">
<i class="fas fa-exclamation-triangle fa-2x mb-2"></i>
<h5>Server is {$lang.offline|default:"Offline"}</h5>
<p class="mb-0">Unable to connect to the server. Please contact support.</p>
</div>
</div>
</div>
{else}
<div class="card h-100">
<div class="card-body">
<div class="alert alert-warning text-center mb-0" role="alert">
{if $suspendreason}
<strong>{$suspendreason}</strong>
<hr>
{/if}
<p class="mb-2">{$LANG.cPanel.packageNotActive|default:"Package not active"}: {$status}</p>
{if $systemStatus eq "Pending"}
<small>{$LANG.cPanel.statusPendingNotice|default:"Your order is pending activation."}</small>
{elseif $systemStatus eq "Suspended"}
<small>{$LANG.cPanel.statusSuspendedNotice|default:"Your service has been suspended."}</small>
{/if}
</div>
</div>
</div>
{/if}
</div>
</div>
<!-- Configuration & Custom Fields -->
<div class="row g-3 mb-4">
<!-- Configurable Options -->
{if $configurableoptions}
<div class="col-lg-4">
<div class="card h-100">
<div class="card-header bg-secondary text-white">
<h5 class="card-title mb-0">
<i class="fas fa-cogs me-2"></i>{$LANG.orderconfigpackage|default:"Package Configuration"}
</h5>
</div>
<div class="card-body">
{foreach from=$configurableoptions item=configoption}
<div class="row mb-2">
<div class="col-6 text-end">
<strong>{$configoption.optionname}:</strong>
</div>
<div class="col-6">
{if $configoption.optiontype eq 3}
<span class="badge bg-{if $configoption.selectedqty}success{else}secondary{/if}">
{if $configoption.selectedqty}{$LANG.yes|default:"Yes"}{else}{$LANG.no|default:"No"}{/if}
</span>
{elseif $configoption.optiontype eq 4}
{$configoption.selectedqty} x {$configoption.selectedoption}
{else}
{$configoption.selectedoption}
{/if}
</div>
</div>
{/foreach}
</div>
</div>
</div>
{/if}
<!-- Custom Fields -->
{if $customfields}
<div class="col-lg-8">
<div class="card h-100">
<div class="card-header bg-dark text-white">
<h5 class="card-title mb-0">
<i class="fas fa-info-circle me-2"></i>{$LANG.additionalInfo|default:"Additional Information"}
</h5>
</div>
<div class="card-body">
<div class="row">
{foreach from=$customfields key=key item=field}
{if $key neq "Subdomain"}
<div class="col-md-6 mb-3">
<div class="border-start border-primary border-4 ps-3">
<small class="text-muted text-uppercase">{$key}</small>
<div class="fw-bold">
{if empty($field)}
<span class="text-muted">{$LANG.blankCustomField|default:"Not Set"}</span>
{else}
{$field}
{/if}
</div>
</div>
</div>
{/if}
{/foreach}
</div>
</div>
</div>
</div>
{/if}
</div>
<!-- Billing Overview -->
<div class="row g-3">
<div class="col-12">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">
<i class="fas fa-file-invoice-dollar me-2"></i>{$LANG.cPanel.billingOverview|default:"Billing Overview"}
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
{if $firstpaymentamount neq $recurringamount}
<div class="row mb-2">
<div class="col-6 text-end">
<strong>{$LANG.firstpaymentamount|default:"First Payment"}:</strong>
</div>
<div class="col-6">
<span class="badge bg-info fs-6">{$firstpaymentamount}</span>
</div>
</div>
{/if}
{if $billingcycle != $LANG.orderpaymenttermonetime && $billingcycle != $LANG.orderfree}
<div class="row mb-2">
<div class="col-6 text-end">
<strong>{$LANG.recurringamount|default:"Recurring Amount"}:</strong>
</div>
<div class="col-6">
<span class="badge bg-primary fs-6">{$recurringamount}</span>
</div>
</div>
{/if}
<div class="row mb-2">
<div class="col-6 text-end">
<strong>{$LANG.orderbillingcycle|default:"Billing Cycle"}:</strong>
</div>
<div class="col-6">
{$billingcycle}
</div>
</div>
<div class="row">
<div class="col-6 text-end">
<strong>{$LANG.orderpaymentmethod|default:"Payment Method"}:</strong>
</div>
<div class="col-6">
{$paymentmethod}
</div>
</div>
</div>
<div class="col-md-6">
<div class="row mb-2">
<div class="col-6 text-end">
<strong>{$LANG.clientareahostingregdate|default:"Registration Date"}:</strong>
</div>
<div class="col-6">
<i class="far fa-calendar-alt me-1"></i>{$regdate}
</div>
</div>
<div class="row">
<div class="col-6 text-end">
<strong>{$LANG.clientareahostingnextduedate|default:"Next Due Date"}:</strong>
</div>
<div class="col-6">
<i class="far fa-calendar-check me-1"></i>{$nextduedate}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,43 @@
<a href="clientarea.php?action=productdetails&id={$serviceid}" class="btn btn-primary pull-right"><i
class="fa fa-arrow-left"></i>&nbsp;&nbsp;Back</a>
<div class="clearfix"></div>
<hr>
<div class="panel panel-default">
<div class="panel-heading">Configuration</div>
<div class="panel-body">
<form method="get" action="clientarea.php" class="form-horizontal text-center">
<input type="hidden" name="action" value="productdetails"/>
<input type="hidden" name="id" value="{$serviceid}"/>
<input type="hidden" name="modop" value="custom"/>
<input type="hidden" name="a" value="settings"/>
<div class="form-group">
<label for="hostname" class="control-label col-sm-2">Server Name</label>
<div class="col-sm-6">
<input type="text" size="100" class="form-control" name="hostname" id="hostname"
value="{$serverinfo.virtualserver_name}"/>
</div>
</div>
<div class="form-group">
<label for="welcomemessage" size="100" class="control-label col-sm-2">Welcome Message</label>
<div class="col-sm-6">
<textarea class="form-control" name="welcomemessage" id="welcomemessage">{$serverinfo.virtualserver_welcomemessage}</textarea>
</div>
</div>
<div class="form-group">
<label for="pw" class="control-label col-sm-2">Password:</label>
<div class="col-sm-6">
<input type="text" size="100" class="form-control" name="pw" id="pw" value="" placeholder="Password"/>
<input type="text" size="100" class="form-control" name="confirmpw" id="confirmpw" value=""
placeholder="Confirm Password"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-6">
<button type="submit" class="btn btn-warning" name="custom" value="save"><i
class="fa fa-floppy-o"></i>&nbsp;&nbsp;Save
</button>
</div>
</div>
</form>
</div>
</div>

View File

@@ -0,0 +1,80 @@
<a href="clientarea.php?action=productdetails&id={$serviceid}" class="btn btn-primary pull-right"><i
class="fa fa-arrow-left"></i>&nbsp;&nbsp;Back</a>
<div class="clearfix"></div>
<hr>
<div class="panel panel-default">
<div class="panel-heading">Privileges</div>
<div class="panel-body">
<form method="get" action="clientarea.php">
<input type="hidden" name="action" value="productdetails"/>
<input type="hidden" name="id" value="{$serviceid}"/>
<input type="hidden" name="modop" value="custom"/>
<input type="hidden" name="a" value="tokens"/>
<input type="hidden" name="custom" value="delete"/>
<div class="table-responsive">
<table class="table table-condensed">
<thead>
<tr>
<th>#</th>
<th>Group's name</th>
<th>Date / Time</th>
<th>Key</th>
<th>Options</th>
</tr>
</thead>
<tbody>
{if !empty($tokens)}
{assign var=var value=1}
{foreach $tokens as $token}
<tr>
<td>{$var}</td>
{foreach $sglist as $sg}
{if $sg.sgid eq $token.token_id1}
<td>{$sg.name}</td>
{/if}
{/foreach}
<td>{$token.token_created|date_format:"%d-%m-%Y %H:%M:%S"}</td>
<td>{$token.token}</td>
<td>
<button type="submit" class="btn btn-danger btn-xs" name="token" id="{$token.token}"
value="{$token.token}">Delete
</button>
</td>
</tr>
{capture assign=var}{$var+1}{/capture}
{/foreach}
{else}
<tr>
<td colspan="2" class="text-center">There is no token</td>
</tr>
{/if}
</tbody>
</table>
</div>
</form>
<hr>
<form method="get" action="clientarea.php" class="form-inline text-center">
<input type="hidden" name="action" value="productdetails"/>
<input type="hidden" name="id" value="{$serviceid}"/>
<input type="hidden" name="modop" value="custom"/>
<input type="hidden" name="a" value="tokens"/>
<div class="form-group">
<label for="groupid">Select the group:</label>
<select name="groupid" id="groupid" class="form-control">
{foreach $sglist as $sg}
<option value="{$sg.sgid}">{$sg.name}</option>
{/foreach}
</select>
<div class="form-group">
<label class="sr-only" for="desc">Descrição</label>
<div class="input-group">
<input type="text" class="form-control" name="desc" id="desc" value=""/>
<span class="input-group-btn">
<button type="submit" class="btn btn-success" name="custom" value="create"><i class="fa fa-key"></i>&nbsp;&nbsp;Create
</button>
</span>
</div>
</div>
</form>
</div>
</div>

View File

@@ -0,0 +1,44 @@
{if $systemStatus == 'Active' && $settings.enabletsdns eq 1}
<a href="clientarea.php?action=productdetails&id={$serviceid}" class="btn btn-primary pull-right"><i
class="fa fa-arrow-left"></i>&nbsp;&nbsp;Back</a>
<div class="clearfix"></div>
<hr>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Edit Subdomain</h3>
</div>
<div class="panel-body">
<form method="get" action="clientarea.php">
<input type="hidden" name="action" value="productdetails"/>
<input type="hidden" name="id" value="{$serviceid}"/>
<input type="hidden" name="modop" value="custom"/>
<input type="hidden" name="a" value="tsdns"/>
<input type="hidden" name="oldzone" value="{$customfields.Subdomain}">
<div class="row">
<div class="col-sm-9">
<div class="input-group">
<input type="text" name="zone" class="form-control" value="{$customfields.Subdomain}">
<span class="input-group-addon">.{$settings.domaintsdns}</span>
</div>
</div>
<div class="col-sm-3">
<button type="submit" name="ma" value="editzone" class="btn btn-primary btn-block"/>
<i class="fa fa-pencil-square-o"></i>
Edit
</button>
</div>
</div>
</form>
</div>
</div>
{else}
<div class="row">
<div class="col-md-offset-3 col-xs-offset-1 col-md-6 col-xs-10">
<div class="alert alert-warning"><a href='clientarea.php?action=productdetails&id={$serviceid}'
class='btn btn-warning pull-right'><i class='fa fa-arrow-left'></i>&nbsp;&nbsp;Back</a>
<p><strong><i class='fa fa-frown-o fa-3x pull-left'></i>Oops! Something happens!</strong></p>This option is disabled
</div>
</div>
</div>
{/if}