save
This commit is contained in:
commit
0efda6c02a
1
acme
Submodule
1
acme
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 452d755719e53231601212b60ebb2e0855a857c3
|
1
cacme/.gitignore
vendored
Normal file
1
cacme/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
./keys/*
|
21
cacme/LICENSE
Normal file
21
cacme/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 walkor<walkor@workerman.net> and contributors (see https://github.com/walkor/webman/contributors)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
4
cacme/README.md
Normal file
4
cacme/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# PHP ACME Cli in Webman
|
||||
|
||||
run:
|
||||
php webman acme
|
133
cacme/app/command/Acme.php
Normal file
133
cacme/app/command/Acme.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||
|
||||
|
||||
use AcmePhp\Core\Http\Base64SafeEncoder;
|
||||
use AcmePhp\Core\Http\SecureHttpClientFactory;
|
||||
use AcmePhp\Core\Http\ServerErrorHandler;
|
||||
use AcmePhp\Ssl\KeyPair;
|
||||
use AcmePhp\Ssl\PrivateKey;
|
||||
use AcmePhp\Ssl\PublicKey;
|
||||
use AcmePhp\Ssl\Parser\KeyParser;
|
||||
use AcmePhp\Ssl\Signer\DataSigner;
|
||||
use GuzzleHttp\Client as GuzzleHttpClient;
|
||||
use AcmePhp\Ssl\DistinguishedName;
|
||||
use AcmePhp\Ssl\CertificateRequest;
|
||||
use AcmePhp\Ssl\Generator\KeyPairGenerator;
|
||||
use AcmePhp\Core\Challenge\Dns\DnsDataExtractor;
|
||||
|
||||
use AcmePhp\Core\AcmeClient;
|
||||
|
||||
class Acme extends Command
|
||||
{
|
||||
protected static $defaultName = 'acme';
|
||||
protected static $defaultDescription = 'acme';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::OPTIONAL, 'Name description');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$EMAIL='youremail@yourdomain.com';
|
||||
$DOMAIN='yourdomain.com';
|
||||
#$CA='https://acme-v02.api.letsencrypt.org/directory';
|
||||
$CA='https://acme-staging-v02.api.letsencrypt.org/directory';
|
||||
$secureHttpClientFactory = new SecureHttpClientFactory(
|
||||
new GuzzleHttpClient(),
|
||||
new Base64SafeEncoder(),
|
||||
new KeyParser(),
|
||||
new DataSigner(),
|
||||
new ServerErrorHandler()
|
||||
);
|
||||
|
||||
$publicKeyPath = base_path().'/keys/account.pub.pem';
|
||||
$privateKeyPath = base_path().'/keys/account.pem';
|
||||
|
||||
if (!file_exists($privateKeyPath)) {
|
||||
$keyPairGenerator = new KeyPairGenerator();
|
||||
$keyPair = $keyPairGenerator->generateKeyPair();
|
||||
|
||||
file_put_contents($publicKeyPath, $keyPair->getPublicKey()->getPEM());
|
||||
file_put_contents($privateKeyPath, $keyPair->getPrivateKey()->getPEM());
|
||||
} else {
|
||||
$publicKey = new PublicKey(file_get_contents($publicKeyPath));
|
||||
$privateKey = new PrivateKey(file_get_contents($privateKeyPath));
|
||||
|
||||
$keyPair = new KeyPair($publicKey, $privateKey);
|
||||
}
|
||||
$secureHttpClient = $secureHttpClientFactory->createSecureHttpClient($keyPair);
|
||||
|
||||
#$acmeClient = new AcmeClient($secureHttpClient, 'https://acme-v02.api.letsencrypt.org/directory');
|
||||
$acmeClient = new AcmeClient($secureHttpClient, $CA);
|
||||
|
||||
$acmeClient->registerAccount($EMAIL,null);
|
||||
|
||||
$authorizationChallenges = $acmeClient->requestAuthorization($DOMAIN);
|
||||
|
||||
foreach($authorizationChallenges as $id=>$cha){
|
||||
|
||||
$cha->id=$id;
|
||||
$dde=new DnsDataExtractor;
|
||||
$cha->value=$dde->getRecordValue($cha);
|
||||
print_r($cha);
|
||||
}
|
||||
#print_r($authorizationChallenges);
|
||||
|
||||
|
||||
#print_r($dde->getRecordValue($authorizationChallenges[1]));
|
||||
|
||||
#$output->writeln($authorizationChallenges);
|
||||
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ChoiceQuestion(
|
||||
'选择对应的ID',
|
||||
array('0', '1', '2'),
|
||||
0
|
||||
);
|
||||
$way = $helper->ask($input, $output, $question);
|
||||
|
||||
$acmeClient->challengeAuthorization($authorizationChallenges[$way]);
|
||||
|
||||
$dn = new DistinguishedName($DOMAIN);
|
||||
|
||||
$keyPairGenerator = new KeyPairGenerator();
|
||||
|
||||
// Make a new key pair. We'll keep the private key as our cert key
|
||||
$domainKeyPair = $keyPairGenerator->generateKeyPair();
|
||||
|
||||
// This is the private key
|
||||
var_dump($domainKeyPair->getPrivateKey()->getPem());
|
||||
|
||||
// Generate CSR
|
||||
$csr = new CertificateRequest($dn, $domainKeyPair);
|
||||
|
||||
$certificateResponse = $acmeClient->requestCertificate($DOMAIN, $csr);
|
||||
|
||||
// This is the certificate (public key)
|
||||
var_dump($certificateResponse->getCertificate()->getPem());
|
||||
|
||||
// For Let's Encrypt, you will need the intermediate too
|
||||
var_dump($certificateResponse->getCertificate()->getIssuerCertificate()->getPEM());
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
27
cacme/app/controller/Apply.php
Normal file
27
cacme/app/controller/Apply.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use support\Request;
|
||||
use Respect\Validation\Validator as v;
|
||||
|
||||
class Apply
|
||||
{
|
||||
public function check(Request $request)
|
||||
{
|
||||
$email=$request->input('email',null);
|
||||
$domain=$request->input('domain',null);
|
||||
$cert=$request->input('cert',null);
|
||||
if(!$email||!$domain||!$cert){
|
||||
return json(['code'=>404,'msg'=>'缺少参数,请刷新重试']);
|
||||
}
|
||||
if(!in_array($cert,array('R3','Laysense','FWNET'))){
|
||||
return json(['code'=>404,'msg'=>'证书类型错误或不存在']);
|
||||
}
|
||||
if(in_array($cert,array('Laysense','FWNET'))){
|
||||
return json(['code'=>403,'msg'=>'当前证书类型'.$cert.'暂时无法颁发']);
|
||||
}
|
||||
if(!v::domain($innerDomain)->validate($domain))
|
||||
return view('index');
|
||||
}
|
||||
}
|
13
cacme/app/controller/IndexController.php
Normal file
13
cacme/app/controller/IndexController.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use support\Request;
|
||||
|
||||
class IndexController
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
return view('index');
|
||||
}
|
||||
}
|
4
cacme/app/functions.php
Normal file
4
cacme/app/functions.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
/**
|
||||
* Here is your custom functions.
|
||||
*/
|
42
cacme/app/middleware/StaticFile.php
Normal file
42
cacme/app/middleware/StaticFile.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use Webman\MiddlewareInterface;
|
||||
use Webman\Http\Response;
|
||||
use Webman\Http\Request;
|
||||
|
||||
/**
|
||||
* Class StaticFile
|
||||
* @package app\middleware
|
||||
*/
|
||||
class StaticFile implements MiddlewareInterface
|
||||
{
|
||||
public function process(Request $request, callable $next): Response
|
||||
{
|
||||
// Access to files beginning with. Is prohibited
|
||||
if (strpos($request->path(), '/.') !== false) {
|
||||
return response('<h1>403 forbidden</h1>', 403);
|
||||
}
|
||||
/** @var Response $response */
|
||||
$response = $next($request);
|
||||
// Add cross domain HTTP header
|
||||
/*$response->withHeaders([
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
]);*/
|
||||
return $response;
|
||||
}
|
||||
}
|
29
cacme/app/model/Test.php
Normal file
29
cacme/app/model/Test.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use support\Model;
|
||||
|
||||
class Test extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'test';
|
||||
|
||||
/**
|
||||
* The primary key associated with the table.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
/**
|
||||
* Indicates if the model should be timestamped.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $timestamps = false;
|
||||
}
|
183
cacme/app/view/index.html
Normal file
183
cacme/app/view/index.html
Normal file
@ -0,0 +1,183 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="Content-Style-Type" content="text/css" />
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<meta name="generator" content="pandoc" />
|
||||
<title>Center ACME Auto SSL</title>
|
||||
<style type="text/css">code{white-space: pre;}</style>
|
||||
<link rel="stylesheet" href="normal.css" type="text/css" />
|
||||
<link rel="stylesheet" href="sakura.css" type="text/css" />
|
||||
<style>
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
.wrapper{
|
||||
position:relative;
|
||||
box-sizing:border-box;
|
||||
min-height:100%;
|
||||
padding-bottom:35px;
|
||||
}
|
||||
.footer{
|
||||
position:absolute;
|
||||
bottom:0;
|
||||
left:0;
|
||||
right:0;
|
||||
height:35px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<h1 id="sakura-a-minimal-classless-css-framework-theme.">
|
||||
Center ACME Auto SSL(CAAS) by Laysense
|
||||
</h1>
|
||||
<p>[由 <a href="https://lab.laysense.cn/">来笙实验室</a> 出品]</p>
|
||||
<blockquote>
|
||||
<p>CAAS将打破你对ssl和acme的认识,现在只需要设置一次302重定向就可以直接申请到证书辣!</p>
|
||||
</blockquote>
|
||||
<button id="back" onclick="skipto('step1')">🔃重设</button>
|
||||
<div id="step1" style="display: none;">
|
||||
<center>
|
||||
<h3>Step1:输入你需要申请SSL的域名</h3>
|
||||
<input id="domain" type="url" /><br />
|
||||
<h3>以及你的邮箱</h3>
|
||||
<input id="email" type="email" />
|
||||
<button onclick="skipto('step2')">👉下一步</button>
|
||||
</div>
|
||||
<div id="step2" style="display: none;">
|
||||
<center>
|
||||
|
||||
<h3>Step2:选择一种证书类型</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h5>正式使用、公共网站:</h5>
|
||||
<button onclick="choose('R3')" style="padding:8px;">📃R3(3个月)</button><br>
|
||||
(即Let's Encrypt)
|
||||
</li><hr />
|
||||
<li>
|
||||
<h5>实验用途、私有网络:</h5>
|
||||
<button onclick="skipto('step99')" style="background: linear-gradient(135deg, #c850c0, #4158d0);padding:15px;">💎来笙豪华娱乐证书(10年)</button>
|
||||
</li>
|
||||
<li>
|
||||
<h5>5050net、fwnet内部使用</h5>
|
||||
<button onclick="skipto('step999')">🌐FWNET证书</button>
|
||||
</li>
|
||||
</ul>
|
||||
</center>
|
||||
</div>
|
||||
<div id="step3" style="display: none;">
|
||||
<div width="50%" style="background-color: beige;">
|
||||
当前证书品牌:<span class="showtype"></span> 域名:<span class="showdomain"></span>
|
||||
</div>
|
||||
<center>
|
||||
<h3>最后一步!</h3>
|
||||
<p>请将<pre>http://<span class="showdomain"></span>/.well-known/acme-challenge/</pre>重定向到 <pre>http://httpacme.lab.laysense.cn/acme/<span class="showdomain"></span></pre></p>
|
||||
<button id="checkbutton" onclick="check()">😀我搞定了!</button><br />
|
||||
<pre id="checknotice" style="background-color: antiquewhite;display: none;"></pre>
|
||||
<hr><a>🧐不会操作? 查看文档!</a>
|
||||
</center>
|
||||
</div>
|
||||
<div id="step4" style="display: none;">
|
||||
<div width="50%" style="background-color: beige;">
|
||||
当前证书品牌:<span class="showtype"></span> 域名:<span class="showdomain"></span>
|
||||
</div>
|
||||
<center>
|
||||
<h3>😍激动人心的时刻</h3>
|
||||
<button style="background: linear-gradient(135deg, #ff9a9e, #fad0c4);padding:12px;" onclick="skipto('step999')">获取证书</button>
|
||||
</center>
|
||||
</div>
|
||||
<div id="step99" style="display: none;">
|
||||
<center>
|
||||
<h3>😳诶呀,所选证书品牌暂时不可用</h3>
|
||||
<button id="back" onclick="skipto('step2')">🔃重选证书</button>
|
||||
</center>
|
||||
</div>
|
||||
<div id="step999" style="display: none;">
|
||||
<center>
|
||||
<h3>😳诶呀,该证书品牌拒绝新的申请</h3>
|
||||
<button id="back" onclick="skipto('step2')">🔃重选证书</button>
|
||||
</center>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="footer">
|
||||
<a>📥下载来笙根证书</a> <a href="https://5050net.cn/fwnet-ecdsa-root-ca-1.crt" target="_blank">📥下载FWNET根证书</a>
|
||||
<a style="float: right;">📄文档</a>
|
||||
<a style="float: right;">⏰自动续期</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="jquery-3.7.1.js"></script>
|
||||
<script>
|
||||
|
||||
changestep();
|
||||
window.onhashchange=function(){
|
||||
changestep();
|
||||
}
|
||||
function skipto(whichstep){
|
||||
location.hash=whichstep;
|
||||
changestep();
|
||||
}
|
||||
function choose(type){
|
||||
window.certtype=type;
|
||||
location.hash='step3';
|
||||
changestep();
|
||||
}
|
||||
function changestep(){
|
||||
var domain=$('#domain').val();
|
||||
var step=location.hash;
|
||||
if(typeof(step) == "undefined" || !step || !domain || typeof(domain) == "undefined"){
|
||||
step='#step1';
|
||||
}else if(step!='#step2'&& !window.certtype || typeof(window.certtype) == "undefined"){
|
||||
step='#step2';
|
||||
}else{
|
||||
$('.showdomain').html(domain);
|
||||
$('.showtype').html(window.certtype);
|
||||
}
|
||||
if(step=='#step4'&&(window.check!='pass'||!window.check)){
|
||||
step='#step3';
|
||||
}
|
||||
//console.log(step);
|
||||
$(window.laststep).hide();
|
||||
$(step).show();
|
||||
window.laststep=step;
|
||||
}
|
||||
function check(){
|
||||
var domain=$('#domain').val();
|
||||
var email=$('#email').val();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '/apply/check',
|
||||
data: {'domain':domain,'email':email,'cert':window.certtype},
|
||||
async: true,
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSend: function () {
|
||||
$('#checkbutton').attr("disabled","disabled");
|
||||
$('#checkbutton').hide();
|
||||
|
||||
$('#checknotice').show();
|
||||
$('#checknotice').html('📑检查中,请稍等');
|
||||
|
||||
},
|
||||
success: function (data) {
|
||||
if (data.code == 200) {
|
||||
$('#checknotice').html('已通过校验');
|
||||
changestep('step4');
|
||||
window.check='pass';
|
||||
} else {
|
||||
$('#checkbutton').show();
|
||||
$('#checknotice').html('校验未通过:'+data.msg);
|
||||
$('#checkbutton').removeAttr("disabled");
|
||||
}
|
||||
},
|
||||
clearForm: true,
|
||||
resetForm: false
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
60
cacme/composer.json
Normal file
60
cacme/composer.json
Normal file
@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "workerman/webman",
|
||||
"type": "project",
|
||||
"keywords": [
|
||||
"high performance",
|
||||
"http service"
|
||||
],
|
||||
"homepage": "https://www.workerman.net",
|
||||
"license": "MIT",
|
||||
"description": "High performance HTTP Service Framework.",
|
||||
"authors": [
|
||||
{
|
||||
"name": "walkor",
|
||||
"email": "walkor@workerman.net",
|
||||
"homepage": "https://www.workerman.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"email": "walkor@workerman.net",
|
||||
"issues": "https://github.com/walkor/webman/issues",
|
||||
"forum": "https://wenda.workerman.net/",
|
||||
"wiki": "https://workerman.net/doc/webman",
|
||||
"source": "https://github.com/walkor/webman"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"workerman/webman-framework": "^1.5.0",
|
||||
"monolog/monolog": "^2.0",
|
||||
"webman/console": "^1.3",
|
||||
"acmephp/core": "^2.1",
|
||||
"yzh52521/easyhttp": "^1.1",
|
||||
"workerman/validation": "^3.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-event": "For better performance. "
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"": "./",
|
||||
"app\\": "./app",
|
||||
"App\\": "./app",
|
||||
"app\\View\\Components\\": "./app/view/components"
|
||||
},
|
||||
"files": [
|
||||
"./support/helpers.php"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"post-package-install": [
|
||||
"support\\Plugin::install"
|
||||
],
|
||||
"post-package-update": [
|
||||
"support\\Plugin::install"
|
||||
],
|
||||
"pre-package-uninstall": [
|
||||
"support\\Plugin::uninstall"
|
||||
]
|
||||
}
|
||||
}
|
2516
cacme/composer.lock
generated
Normal file
2516
cacme/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
cacme/config/app.php
Normal file
26
cacme/config/app.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use support\Request;
|
||||
|
||||
return [
|
||||
'debug' => true,
|
||||
'error_reporting' => E_ALL,
|
||||
'default_timezone' => 'Asia/Shanghai',
|
||||
'request_class' => Request::class,
|
||||
'public_path' => base_path() . DIRECTORY_SEPARATOR . 'public',
|
||||
'runtime_path' => base_path(false) . DIRECTORY_SEPARATOR . 'runtime',
|
||||
'controller_suffix' => 'Controller',
|
||||
'controller_reuse' => false,
|
||||
];
|
21
cacme/config/autoload.php
Normal file
21
cacme/config/autoload.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
'files' => [
|
||||
base_path() . '/app/functions.php',
|
||||
base_path() . '/support/Request.php',
|
||||
base_path() . '/support/Response.php',
|
||||
]
|
||||
];
|
18
cacme/config/bootstrap.php
Normal file
18
cacme/config/bootstrap.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
support\bootstrap\Session::class,
|
||||
support\bootstrap\LaravelDb::class,
|
||||
];
|
15
cacme/config/container.php
Normal file
15
cacme/config/container.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return new Webman\Container;
|
15
cacme/config/database.php
Normal file
15
cacme/config/database.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [];
|
15
cacme/config/dependence.php
Normal file
15
cacme/config/dependence.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [];
|
17
cacme/config/exception.php
Normal file
17
cacme/config/exception.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
'' => support\exception\Handler::class,
|
||||
];
|
32
cacme/config/log.php
Normal file
32
cacme/config/log.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'handlers' => [
|
||||
[
|
||||
'class' => Monolog\Handler\RotatingFileHandler::class,
|
||||
'constructor' => [
|
||||
runtime_path() . '/logs/webman.log',
|
||||
7, //$maxFiles
|
||||
Monolog\Logger::DEBUG,
|
||||
],
|
||||
'formatter' => [
|
||||
'class' => Monolog\Formatter\LineFormatter::class,
|
||||
'constructor' => [null, 'Y-m-d H:i:s', true],
|
||||
],
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
15
cacme/config/middleware.php
Normal file
15
cacme/config/middleware.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [];
|
24
cacme/config/plugin/webman/console/app.php
Normal file
24
cacme/config/plugin/webman/console/app.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
return [
|
||||
'enable' => true,
|
||||
|
||||
'build_dir' => BASE_PATH . DIRECTORY_SEPARATOR . 'build',
|
||||
|
||||
'phar_filename' => 'webman.phar',
|
||||
|
||||
'bin_filename' => 'webman.bin',
|
||||
|
||||
'signature_algorithm'=> Phar::SHA256, //set the signature algorithm for a phar and apply it. The signature algorithm must be one of Phar::MD5, Phar::SHA1, Phar::SHA256, Phar::SHA512, or Phar::OPENSSL.
|
||||
|
||||
'private_key_file' => '', // The file path for certificate or OpenSSL private key file.
|
||||
|
||||
'exclude_pattern' => '#^(?!.*(composer.json|/.github/|/.idea/|/.git/|/.setting/|/runtime/|/vendor-bin/|/build/|/vendor/webman/admin/))(.*)$#',
|
||||
|
||||
'exclude_files' => [
|
||||
'.env', 'LICENSE', 'composer.json', 'composer.lock', 'start.php', 'webman.phar', 'webman.bin'
|
||||
],
|
||||
|
||||
'custom_ini' => '
|
||||
memory_limit = 256M
|
||||
',
|
||||
];
|
42
cacme/config/process.php
Normal file
42
cacme/config/process.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
global $argv;
|
||||
|
||||
return [
|
||||
// File update detection and automatic reload
|
||||
'monitor' => [
|
||||
'handler' => process\Monitor::class,
|
||||
'reloadable' => false,
|
||||
'constructor' => [
|
||||
// Monitor these directories
|
||||
'monitorDir' => array_merge([
|
||||
app_path(),
|
||||
config_path(),
|
||||
base_path() . '/process',
|
||||
base_path() . '/support',
|
||||
base_path() . '/resource',
|
||||
base_path() . '/.env',
|
||||
], glob(base_path() . '/plugin/*/app'), glob(base_path() . '/plugin/*/config'), glob(base_path() . '/plugin/*/api')),
|
||||
// Files with these suffixes will be monitored
|
||||
'monitorExtensions' => [
|
||||
'php', 'html', 'htm', 'env'
|
||||
],
|
||||
'options' => [
|
||||
'enable_file_monitor' => !in_array('-d', $argv) && DIRECTORY_SEPARATOR === '/',
|
||||
'enable_memory_monitor' => DIRECTORY_SEPARATOR === '/',
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
22
cacme/config/redis.php
Normal file
22
cacme/config/redis.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'host' => '127.0.0.1',
|
||||
'password' => null,
|
||||
'port' => 6379,
|
||||
'database' => 0,
|
||||
],
|
||||
];
|
22
cacme/config/route.php
Normal file
22
cacme/config/route.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use Webman\Route;
|
||||
Route::any('/apply/check', [app\controller\Apply::class, 'check']);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
31
cacme/config/server.php
Normal file
31
cacme/config/server.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
'listen' => 'http://0.0.0.0:9961',
|
||||
'transport' => 'tcp',
|
||||
'context' => [],
|
||||
'name' => 'webman',
|
||||
'count' => cpu_count() * 4,
|
||||
'user' => '',
|
||||
'group' => '',
|
||||
'reusePort' => false,
|
||||
'event_loop' => '',
|
||||
'stop_timeout' => 2,
|
||||
'pid_file' => runtime_path() . '/webman.pid',
|
||||
'status_file' => runtime_path() . '/webman.status',
|
||||
'stdout_file' => runtime_path() . '/logs/stdout.log',
|
||||
'log_file' => runtime_path() . '/logs/workerman.log',
|
||||
'max_package_size' => 10 * 1024 * 1024
|
||||
];
|
65
cacme/config/session.php
Normal file
65
cacme/config/session.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use Webman\Session\FileSessionHandler;
|
||||
use Webman\Session\RedisSessionHandler;
|
||||
use Webman\Session\RedisClusterSessionHandler;
|
||||
|
||||
return [
|
||||
|
||||
'type' => 'file', // or redis or redis_cluster
|
||||
|
||||
'handler' => FileSessionHandler::class,
|
||||
|
||||
'config' => [
|
||||
'file' => [
|
||||
'save_path' => runtime_path() . '/sessions',
|
||||
],
|
||||
'redis' => [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'auth' => '',
|
||||
'timeout' => 2,
|
||||
'database' => '',
|
||||
'prefix' => 'redis_session_',
|
||||
],
|
||||
'redis_cluster' => [
|
||||
'host' => ['127.0.0.1:7000', '127.0.0.1:7001', '127.0.0.1:7001'],
|
||||
'timeout' => 2,
|
||||
'auth' => '',
|
||||
'prefix' => 'redis_session_',
|
||||
]
|
||||
],
|
||||
|
||||
'session_name' => 'PHPSID',
|
||||
|
||||
'auto_update_timestamp' => false,
|
||||
|
||||
'lifetime' => 7*24*60*60,
|
||||
|
||||
'cookie_lifetime' => 365*24*60*60,
|
||||
|
||||
'cookie_path' => '/',
|
||||
|
||||
'domain' => '',
|
||||
|
||||
'http_only' => true,
|
||||
|
||||
'secure' => false,
|
||||
|
||||
'same_site' => '',
|
||||
|
||||
'gc_probability' => [1, 1000],
|
||||
|
||||
];
|
23
cacme/config/static.php
Normal file
23
cacme/config/static.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Static file settings
|
||||
*/
|
||||
return [
|
||||
'enable' => true,
|
||||
'middleware' => [ // Static file Middleware
|
||||
//app\middleware\StaticFile::class,
|
||||
],
|
||||
];
|
25
cacme/config/translation.php
Normal file
25
cacme/config/translation.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Multilingual configuration
|
||||
*/
|
||||
return [
|
||||
// Default language
|
||||
'locale' => 'zh_CN',
|
||||
// Fallback language
|
||||
'fallback_locale' => ['zh_CN', 'en'],
|
||||
// Folder where language files are stored
|
||||
'path' => base_path() . '/resource/translations',
|
||||
];
|
22
cacme/config/view.php
Normal file
22
cacme/config/view.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use support\view\Raw;
|
||||
use support\view\Twig;
|
||||
use support\view\Blade;
|
||||
use support\view\ThinkPHP;
|
||||
|
||||
return [
|
||||
'handler' => Raw::class
|
||||
];
|
243
cacme/process/Monitor.php
Normal file
243
cacme/process/Monitor.php
Normal file
@ -0,0 +1,243 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace process;
|
||||
|
||||
use FilesystemIterator;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use SplFileInfo;
|
||||
use Workerman\Timer;
|
||||
use Workerman\Worker;
|
||||
|
||||
/**
|
||||
* Class FileMonitor
|
||||
* @package process
|
||||
*/
|
||||
class Monitor
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $paths = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $extensions = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $lockFile = __DIR__ . '/../runtime/monitor.lock';
|
||||
|
||||
/**
|
||||
* Pause monitor
|
||||
* @return void
|
||||
*/
|
||||
public static function pause()
|
||||
{
|
||||
file_put_contents(static::$lockFile, time());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume monitor
|
||||
* @return void
|
||||
*/
|
||||
public static function resume(): void
|
||||
{
|
||||
clearstatcache();
|
||||
if (is_file(static::$lockFile)) {
|
||||
unlink(static::$lockFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether monitor is paused
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPaused(): bool
|
||||
{
|
||||
clearstatcache();
|
||||
return file_exists(static::$lockFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* FileMonitor constructor.
|
||||
* @param $monitorDir
|
||||
* @param $monitorExtensions
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($monitorDir, $monitorExtensions, array $options = [])
|
||||
{
|
||||
static::resume();
|
||||
$this->paths = (array)$monitorDir;
|
||||
$this->extensions = $monitorExtensions;
|
||||
if (!Worker::getAllWorkers()) {
|
||||
return;
|
||||
}
|
||||
$disableFunctions = explode(',', ini_get('disable_functions'));
|
||||
if (in_array('exec', $disableFunctions, true)) {
|
||||
echo "\nMonitor file change turned off because exec() has been disabled by disable_functions setting in " . PHP_CONFIG_FILE_PATH . "/php.ini\n";
|
||||
} else {
|
||||
if ($options['enable_file_monitor'] ?? true) {
|
||||
Timer::add(1, function () {
|
||||
$this->checkAllFilesChange();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$memoryLimit = $this->getMemoryLimit($options['memory_limit'] ?? null);
|
||||
if ($memoryLimit && ($options['enable_memory_monitor'] ?? true)) {
|
||||
Timer::add(60, [$this, 'checkMemory'], [$memoryLimit]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $monitorDir
|
||||
* @return bool
|
||||
*/
|
||||
public function checkFilesChange($monitorDir): bool
|
||||
{
|
||||
static $lastMtime, $tooManyFilesCheck;
|
||||
if (!$lastMtime) {
|
||||
$lastMtime = time();
|
||||
}
|
||||
clearstatcache();
|
||||
if (!is_dir($monitorDir)) {
|
||||
if (!is_file($monitorDir)) {
|
||||
return false;
|
||||
}
|
||||
$iterator = [new SplFileInfo($monitorDir)];
|
||||
} else {
|
||||
// recursive traversal directory
|
||||
$dirIterator = new RecursiveDirectoryIterator($monitorDir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS);
|
||||
$iterator = new RecursiveIteratorIterator($dirIterator);
|
||||
}
|
||||
$count = 0;
|
||||
foreach ($iterator as $file) {
|
||||
$count ++;
|
||||
/** var SplFileInfo $file */
|
||||
if (is_dir($file->getRealPath())) {
|
||||
continue;
|
||||
}
|
||||
// check mtime
|
||||
if (in_array($file->getExtension(), $this->extensions, true) && $lastMtime < $file->getMTime()) {
|
||||
$var = 0;
|
||||
exec('"'.PHP_BINARY . '" -l ' . $file, $out, $var);
|
||||
$lastMtime = $file->getMTime();
|
||||
if ($var) {
|
||||
continue;
|
||||
}
|
||||
echo $file . " update and reload\n";
|
||||
// send SIGUSR1 signal to master process for reload
|
||||
if (DIRECTORY_SEPARATOR === '/') {
|
||||
posix_kill(posix_getppid(), SIGUSR1);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$tooManyFilesCheck && $count > 1000) {
|
||||
echo "Monitor: There are too many files ($count files) in $monitorDir which makes file monitoring very slow\n";
|
||||
$tooManyFilesCheck = 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function checkAllFilesChange(): bool
|
||||
{
|
||||
if (static::isPaused()) {
|
||||
return false;
|
||||
}
|
||||
foreach ($this->paths as $path) {
|
||||
if ($this->checkFilesChange($path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $memoryLimit
|
||||
* @return void
|
||||
*/
|
||||
public function checkMemory($memoryLimit)
|
||||
{
|
||||
if (static::isPaused() || $memoryLimit <= 0) {
|
||||
return;
|
||||
}
|
||||
$ppid = posix_getppid();
|
||||
$childrenFile = "/proc/$ppid/task/$ppid/children";
|
||||
if (!is_file($childrenFile) || !($children = file_get_contents($childrenFile))) {
|
||||
return;
|
||||
}
|
||||
foreach (explode(' ', $children) as $pid) {
|
||||
$pid = (int)$pid;
|
||||
$statusFile = "/proc/$pid/status";
|
||||
if (!is_file($statusFile) || !($status = file_get_contents($statusFile))) {
|
||||
continue;
|
||||
}
|
||||
$mem = 0;
|
||||
if (preg_match('/VmRSS\s*?:\s*?(\d+?)\s*?kB/', $status, $match)) {
|
||||
$mem = $match[1];
|
||||
}
|
||||
$mem = (int)($mem / 1024);
|
||||
if ($mem >= $memoryLimit) {
|
||||
posix_kill($pid, SIGINT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory limit
|
||||
* @return float
|
||||
*/
|
||||
protected function getMemoryLimit($memoryLimit)
|
||||
{
|
||||
if ($memoryLimit === 0) {
|
||||
return 0;
|
||||
}
|
||||
$usePhpIni = false;
|
||||
if (!$memoryLimit) {
|
||||
$memoryLimit = ini_get('memory_limit');
|
||||
$usePhpIni = true;
|
||||
}
|
||||
|
||||
if ($memoryLimit == -1) {
|
||||
return 0;
|
||||
}
|
||||
$unit = strtolower($memoryLimit[strlen($memoryLimit) - 1]);
|
||||
if ($unit === 'g') {
|
||||
$memoryLimit = 1024 * (int)$memoryLimit;
|
||||
} else if ($unit === 'm') {
|
||||
$memoryLimit = (int)$memoryLimit;
|
||||
} else if ($unit === 'k') {
|
||||
$memoryLimit = ((int)$memoryLimit / 1024);
|
||||
} else {
|
||||
$memoryLimit = ((int)$memoryLimit / (1024 * 1024));
|
||||
}
|
||||
if ($memoryLimit < 30) {
|
||||
$memoryLimit = 30;
|
||||
}
|
||||
if ($usePhpIni) {
|
||||
$memoryLimit = (int)(0.8 * $memoryLimit);
|
||||
}
|
||||
return $memoryLimit;
|
||||
}
|
||||
}
|
12
cacme/public/404.html
Normal file
12
cacme/public/404.html
Normal file
@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>404 Not Found - webman</title>
|
||||
</head>
|
||||
<body>
|
||||
<center>
|
||||
<h1>404 Not Found</h1>
|
||||
</center>
|
||||
<hr>
|
||||
<center>webman</center>
|
||||
</body>
|
||||
</html>
|
BIN
cacme/public/favicon.ico
Normal file
BIN
cacme/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
10716
cacme/public/jquery-3.7.1.js
vendored
Normal file
10716
cacme/public/jquery-3.7.1.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
461
cacme/public/normal.css
Normal file
461
cacme/public/normal.css
Normal file
@ -0,0 +1,461 @@
|
||||
/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/**
|
||||
* 1. Change the default font family in all browsers (opinionated).
|
||||
* 2. Correct the line height in all browsers.
|
||||
* 3. Prevent adjustments of font size after orientation changes in
|
||||
* IE on Windows Phone and in iOS.
|
||||
*/
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
html {
|
||||
font-family: sans-serif; /* 1 */
|
||||
line-height: 1.15; /* 2 */
|
||||
-ms-text-size-adjust: 100%; /* 3 */
|
||||
-webkit-text-size-adjust: 100%; /* 3 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
footer,
|
||||
header,
|
||||
nav,
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in IE.
|
||||
*/
|
||||
|
||||
figcaption,
|
||||
figure,
|
||||
main { /* 1 */
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct margin in IE 8.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Remove the gray background on active links in IE 10.
|
||||
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent; /* 1 */
|
||||
-webkit-text-decoration-skip: objects; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the outline on focused links when they are also active or hovered
|
||||
* in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Firefox 39-.
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font style in Android 4.3-.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct background and color in IE 9-.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background-color: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
audio,
|
||||
video {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in iOS 4-7.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10-.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the overflow in IE.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers (opinionated).
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: sans-serif; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||
* controls in Android 4.
|
||||
* 2. Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
html [type="button"], /* 1 */
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the border, margin, and padding in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct display in IE 9-.
|
||||
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
display: inline-block; /* 1 */
|
||||
vertical-align: baseline; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10-.
|
||||
* 2. Remove the padding in IE 10-.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-cancel-button,
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in Edge, IE, and Firefox.
|
||||
*/
|
||||
|
||||
details, /* 1 */
|
||||
menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Scripting
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
canvas {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hidden
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10-.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
225
cacme/public/sakura.css
Normal file
225
cacme/public/sakura.css
Normal file
@ -0,0 +1,225 @@
|
||||
/* Sakura.css v1.5.0
|
||||
* ================
|
||||
* Minimal css theme.
|
||||
* Project: https://github.com/oxalorg/sakura/
|
||||
*/
|
||||
/* Body */
|
||||
html {
|
||||
font-size: 62.5%;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 1.8rem;
|
||||
line-height: 1.618;
|
||||
max-width: 38em;
|
||||
margin: auto;
|
||||
color: #49002d;
|
||||
padding: 13px;
|
||||
}
|
||||
|
||||
@media (max-width: 684px) {
|
||||
body {
|
||||
font-size: 1.53rem;
|
||||
}
|
||||
}
|
||||
@media (max-width: 382px) {
|
||||
body {
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
line-height: 1.1;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||
font-weight: 700;
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 1.5rem;
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
-ms-word-break: break-all;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.35em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.75em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
small, sub, sup {
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-color: #980255;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #980255;
|
||||
}
|
||||
a:visited {
|
||||
color: #660139;
|
||||
}
|
||||
a:hover {
|
||||
color: #753851;
|
||||
border-bottom: 2px solid #49002d;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 1.4em;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 0.4em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin-left: 0px;
|
||||
margin-right: 0px;
|
||||
padding-left: 1em;
|
||||
padding-top: 0.8em;
|
||||
padding-bottom: 0.8em;
|
||||
padding-right: 0.8em;
|
||||
border-left: 5px solid #980255;
|
||||
margin-bottom: 2.5rem;
|
||||
background-color: #f8d2e9;
|
||||
}
|
||||
|
||||
blockquote p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
img, video {
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
/* Pre and Code */
|
||||
pre {
|
||||
background-color: #f8d2e9;
|
||||
display: block;
|
||||
padding: 1em;
|
||||
overflow-x: auto;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 2.5rem;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
code, kbd, samp {
|
||||
font-size: 0.9em;
|
||||
padding: 0 0.5em;
|
||||
background-color: #f8d2e9;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
white-space: pre;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
table {
|
||||
text-align: justify;
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
td, th {
|
||||
padding: 0.5em;
|
||||
border-bottom: 1px solid #f8d2e9;
|
||||
}
|
||||
|
||||
/* Buttons, forms and input */
|
||||
input, textarea {
|
||||
border: 1px solid #49002d;
|
||||
}
|
||||
input:focus, textarea:focus {
|
||||
border: 1px solid #980255;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button, button, input[type=submit], input[type=reset], input[type=button], input[type=file]::file-selector-button {
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
background-color: #980255;
|
||||
color: #ffe4f5;
|
||||
border-radius: 1px;
|
||||
border: 1px solid #980255;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.button[disabled], button[disabled], input[type=submit][disabled], input[type=reset][disabled], input[type=button][disabled], input[type=file]::file-selector-button[disabled] {
|
||||
cursor: default;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.button:hover, button:hover, input[type=submit]:hover, input[type=reset]:hover, input[type=button]:hover, input[type=file]::file-selector-button:hover {
|
||||
background-color: #753851;
|
||||
color: #ffe4f5;
|
||||
outline: 0;
|
||||
}
|
||||
.button:focus-visible, button:focus-visible, input[type=submit]:focus-visible, input[type=reset]:focus-visible, input[type=button]:focus-visible, input[type=file]::file-selector-button:focus-visible {
|
||||
outline-style: solid;
|
||||
outline-width: 2px;
|
||||
}
|
||||
|
||||
textarea, select, input {
|
||||
color: #49002d;
|
||||
padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */
|
||||
margin-bottom: 10px;
|
||||
background-color: #f8d2e9;
|
||||
border: 1px solid #f8d2e9;
|
||||
border-radius: 4px;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
textarea:focus, select:focus, input:focus {
|
||||
border: 1px solid #980255;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input[type=checkbox]:focus {
|
||||
outline: 1px dotted #980255;
|
||||
}
|
||||
|
||||
label, legend, fieldset {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 600;
|
||||
}
|
4
cacme/runtime/.gitignore
vendored
Normal file
4
cacme/runtime/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!logs
|
||||
!views
|
||||
!.gitignore
|
2
cacme/runtime/logs/.gitignore
vendored
Normal file
2
cacme/runtime/logs/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
2
cacme/runtime/views/.gitignore
vendored
Normal file
2
cacme/runtime/views/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
4
cacme/start.php
Executable file
4
cacme/start.php
Executable file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
support\App::run();
|
24
cacme/support/Request.php
Normal file
24
cacme/support/Request.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace support;
|
||||
|
||||
/**
|
||||
* Class Request
|
||||
* @package support
|
||||
*/
|
||||
class Request extends \Webman\Http\Request
|
||||
{
|
||||
|
||||
}
|
24
cacme/support/Response.php
Normal file
24
cacme/support/Response.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace support;
|
||||
|
||||
/**
|
||||
* Class Response
|
||||
* @package support
|
||||
*/
|
||||
class Response extends \Webman\Http\Response
|
||||
{
|
||||
|
||||
}
|
133
cacme/support/bootstrap.php
Normal file
133
cacme/support/bootstrap.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
use support\Log;
|
||||
use Webman\Bootstrap;
|
||||
use Webman\Config;
|
||||
use Webman\Middleware;
|
||||
use Webman\Route;
|
||||
use Webman\Util;
|
||||
|
||||
$worker = $worker ?? null;
|
||||
|
||||
set_error_handler(function ($level, $message, $file = '', $line = 0) {
|
||||
if (error_reporting() & $level) {
|
||||
throw new ErrorException($message, 0, $level, $file, $line);
|
||||
}
|
||||
});
|
||||
|
||||
if ($worker) {
|
||||
register_shutdown_function(function ($startTime) {
|
||||
if (time() - $startTime <= 0.1) {
|
||||
sleep(1);
|
||||
}
|
||||
}, time());
|
||||
}
|
||||
|
||||
if (class_exists('Dotenv\Dotenv') && file_exists(base_path(false) . '/.env')) {
|
||||
if (method_exists('Dotenv\Dotenv', 'createUnsafeMutable')) {
|
||||
Dotenv::createUnsafeMutable(base_path(false))->load();
|
||||
} else {
|
||||
Dotenv::createMutable(base_path(false))->load();
|
||||
}
|
||||
}
|
||||
|
||||
Config::clear();
|
||||
support\App::loadAllConfig(['route']);
|
||||
if ($timezone = config('app.default_timezone')) {
|
||||
date_default_timezone_set($timezone);
|
||||
}
|
||||
|
||||
foreach (config('autoload.files', []) as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($project['autoload']['files'] ?? [] as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
}
|
||||
foreach ($projects['autoload']['files'] ?? [] as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
}
|
||||
|
||||
Middleware::load(config('middleware', []));
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project) || $name === 'static') {
|
||||
continue;
|
||||
}
|
||||
Middleware::load($project['middleware'] ?? []);
|
||||
}
|
||||
Middleware::load($projects['middleware'] ?? [], $firm);
|
||||
if ($staticMiddlewares = config("plugin.$firm.static.middleware")) {
|
||||
Middleware::load(['__static__' => $staticMiddlewares], $firm);
|
||||
}
|
||||
}
|
||||
Middleware::load(['__static__' => config('static.middleware', [])]);
|
||||
|
||||
foreach (config('bootstrap', []) as $className) {
|
||||
if (!class_exists($className)) {
|
||||
$log = "Warning: Class $className setting in config/bootstrap.php not found\r\n";
|
||||
echo $log;
|
||||
Log::error($log);
|
||||
continue;
|
||||
}
|
||||
/** @var Bootstrap $className */
|
||||
$className::start($worker);
|
||||
}
|
||||
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($project['bootstrap'] ?? [] as $className) {
|
||||
if (!class_exists($className)) {
|
||||
$log = "Warning: Class $className setting in config/plugin/$firm/$name/bootstrap.php not found\r\n";
|
||||
echo $log;
|
||||
Log::error($log);
|
||||
continue;
|
||||
}
|
||||
/** @var Bootstrap $className */
|
||||
$className::start($worker);
|
||||
}
|
||||
}
|
||||
foreach ($projects['bootstrap'] ?? [] as $className) {
|
||||
/** @var string $className */
|
||||
if (!class_exists($className)) {
|
||||
$log = "Warning: Class $className setting in plugin/$firm/config/bootstrap.php not found\r\n";
|
||||
echo $log;
|
||||
Log::error($log);
|
||||
continue;
|
||||
}
|
||||
/** @var Bootstrap $className */
|
||||
$className::start($worker);
|
||||
}
|
||||
}
|
||||
|
||||
$directory = base_path() . '/plugin';
|
||||
$paths = [config_path()];
|
||||
foreach (Util::scanDir($directory) as $path) {
|
||||
if (is_dir($path = "$path/config")) {
|
||||
$paths[] = $path;
|
||||
}
|
||||
}
|
||||
Route::load($paths);
|
||||
|
528
cacme/support/helpers.php
Normal file
528
cacme/support/helpers.php
Normal file
@ -0,0 +1,528 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use support\Translation;
|
||||
use support\view\Blade;
|
||||
use support\view\Raw;
|
||||
use support\view\ThinkPHP;
|
||||
use support\view\Twig;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Error\SyntaxError;
|
||||
use Webman\App;
|
||||
use Webman\Config;
|
||||
use Webman\Route;
|
||||
use Workerman\Protocols\Http\Session;
|
||||
use Workerman\Worker;
|
||||
|
||||
// Project base path
|
||||
define('BASE_PATH', dirname(__DIR__));
|
||||
|
||||
/**
|
||||
* return the program execute directory
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function run_path(string $path = ''): string
|
||||
{
|
||||
static $runPath = '';
|
||||
if (!$runPath) {
|
||||
$runPath = is_phar() ? dirname(Phar::running(false)) : BASE_PATH;
|
||||
}
|
||||
return path_combine($runPath, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* if the param $path equal false,will return this program current execute directory
|
||||
* @param string|false $path
|
||||
* @return string
|
||||
*/
|
||||
function base_path($path = ''): string
|
||||
{
|
||||
if (false === $path) {
|
||||
return run_path();
|
||||
}
|
||||
return path_combine(BASE_PATH, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* App path
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function app_path(string $path = ''): string
|
||||
{
|
||||
return path_combine(BASE_PATH . DIRECTORY_SEPARATOR . 'app', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public path
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function public_path(string $path = ''): string
|
||||
{
|
||||
static $publicPath = '';
|
||||
if (!$publicPath) {
|
||||
$publicPath = \config('app.public_path') ?: run_path('public');
|
||||
}
|
||||
return path_combine($publicPath, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Config path
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function config_path(string $path = ''): string
|
||||
{
|
||||
return path_combine(BASE_PATH . DIRECTORY_SEPARATOR . 'config', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime path
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function runtime_path(string $path = ''): string
|
||||
{
|
||||
static $runtimePath = '';
|
||||
if (!$runtimePath) {
|
||||
$runtimePath = \config('app.runtime_path') ?: run_path('runtime');
|
||||
}
|
||||
return path_combine($runtimePath, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate paths based on given information
|
||||
* @param string $front
|
||||
* @param string $back
|
||||
* @return string
|
||||
*/
|
||||
function path_combine(string $front, string $back): string
|
||||
{
|
||||
return $front . ($back ? (DIRECTORY_SEPARATOR . ltrim($back, DIRECTORY_SEPARATOR)) : $back);
|
||||
}
|
||||
|
||||
/**
|
||||
* Response
|
||||
* @param int $status
|
||||
* @param array $headers
|
||||
* @param string $body
|
||||
* @return Response
|
||||
*/
|
||||
function response(string $body = '', int $status = 200, array $headers = []): Response
|
||||
{
|
||||
return new Response($status, $headers, $body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Json response
|
||||
* @param $data
|
||||
* @param int $options
|
||||
* @return Response
|
||||
*/
|
||||
function json($data, int $options = JSON_UNESCAPED_UNICODE): Response
|
||||
{
|
||||
return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Xml response
|
||||
* @param $xml
|
||||
* @return Response
|
||||
*/
|
||||
function xml($xml): Response
|
||||
{
|
||||
if ($xml instanceof SimpleXMLElement) {
|
||||
$xml = $xml->asXML();
|
||||
}
|
||||
return new Response(200, ['Content-Type' => 'text/xml'], $xml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Jsonp response
|
||||
* @param $data
|
||||
* @param string $callbackName
|
||||
* @return Response
|
||||
*/
|
||||
function jsonp($data, string $callbackName = 'callback'): Response
|
||||
{
|
||||
if (!is_scalar($data) && null !== $data) {
|
||||
$data = json_encode($data);
|
||||
}
|
||||
return new Response(200, [], "$callbackName($data)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect response
|
||||
* @param string $location
|
||||
* @param int $status
|
||||
* @param array $headers
|
||||
* @return Response
|
||||
*/
|
||||
function redirect(string $location, int $status = 302, array $headers = []): Response
|
||||
{
|
||||
$response = new Response($status, ['Location' => $location]);
|
||||
if (!empty($headers)) {
|
||||
$response->withHeaders($headers);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* View response
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @param string|null $plugin
|
||||
* @return Response
|
||||
*/
|
||||
function view(string $template, array $vars = [], string $app = null, string $plugin = null): Response
|
||||
{
|
||||
$request = \request();
|
||||
$plugin = $plugin === null ? ($request->plugin ?? '') : $plugin;
|
||||
$handler = \config($plugin ? "plugin.$plugin.view.handler" : 'view.handler');
|
||||
return new Response(200, [], $handler::render($template, $vars, $app, $plugin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw view response
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
function raw_view(string $template, array $vars = [], string $app = null): Response
|
||||
{
|
||||
return new Response(200, [], Raw::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* Blade view response
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @return Response
|
||||
*/
|
||||
function blade_view(string $template, array $vars = [], string $app = null): Response
|
||||
{
|
||||
return new Response(200, [], Blade::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* Think view response
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @return Response
|
||||
*/
|
||||
function think_view(string $template, array $vars = [], string $app = null): Response
|
||||
{
|
||||
return new Response(200, [], ThinkPHP::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* Twig view response
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @return Response
|
||||
* @throws LoaderError
|
||||
* @throws RuntimeError
|
||||
* @throws SyntaxError
|
||||
*/
|
||||
function twig_view(string $template, array $vars = [], string $app = null): Response
|
||||
{
|
||||
return new Response(200, [], Twig::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request
|
||||
* @return \Webman\Http\Request|Request|null
|
||||
*/
|
||||
function request()
|
||||
{
|
||||
return App::request();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config
|
||||
* @param string|null $key
|
||||
* @param $default
|
||||
* @return array|mixed|null
|
||||
*/
|
||||
function config(string $key = null, $default = null)
|
||||
{
|
||||
return Config::get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create url
|
||||
* @param string $name
|
||||
* @param ...$parameters
|
||||
* @return string
|
||||
*/
|
||||
function route(string $name, ...$parameters): string
|
||||
{
|
||||
$route = Route::getByName($name);
|
||||
if (!$route) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!$parameters) {
|
||||
return $route->url();
|
||||
}
|
||||
|
||||
if (is_array(current($parameters))) {
|
||||
$parameters = current($parameters);
|
||||
}
|
||||
|
||||
return $route->url($parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Session
|
||||
* @param mixed $key
|
||||
* @param mixed $default
|
||||
* @return mixed|bool|Session
|
||||
*/
|
||||
function session($key = null, $default = null)
|
||||
{
|
||||
$session = \request()->session();
|
||||
if (null === $key) {
|
||||
return $session;
|
||||
}
|
||||
if (is_array($key)) {
|
||||
$session->put($key);
|
||||
return null;
|
||||
}
|
||||
if (strpos($key, '.')) {
|
||||
$keyArray = explode('.', $key);
|
||||
$value = $session->all();
|
||||
foreach ($keyArray as $index) {
|
||||
if (!isset($value[$index])) {
|
||||
return $default;
|
||||
}
|
||||
$value = $value[$index];
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
return $session->get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translation
|
||||
* @param string $id
|
||||
* @param array $parameters
|
||||
* @param string|null $domain
|
||||
* @param string|null $locale
|
||||
* @return string
|
||||
*/
|
||||
function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string
|
||||
{
|
||||
$res = Translation::trans($id, $parameters, $domain, $locale);
|
||||
return $res === '' ? $id : $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locale
|
||||
* @param string|null $locale
|
||||
* @return string
|
||||
*/
|
||||
function locale(string $locale = null): string
|
||||
{
|
||||
if (!$locale) {
|
||||
return Translation::getLocale();
|
||||
}
|
||||
Translation::setLocale($locale);
|
||||
return $locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* 404 not found
|
||||
* @return Response
|
||||
*/
|
||||
function not_found(): Response
|
||||
{
|
||||
return new Response(404, [], file_get_contents(public_path() . '/404.html'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy dir
|
||||
* @param string $source
|
||||
* @param string $dest
|
||||
* @param bool $overwrite
|
||||
* @return void
|
||||
*/
|
||||
function copy_dir(string $source, string $dest, bool $overwrite = false)
|
||||
{
|
||||
if (is_dir($source)) {
|
||||
if (!is_dir($dest)) {
|
||||
mkdir($dest);
|
||||
}
|
||||
$files = scandir($source);
|
||||
foreach ($files as $file) {
|
||||
if ($file !== "." && $file !== "..") {
|
||||
copy_dir("$source/$file", "$dest/$file", $overwrite);
|
||||
}
|
||||
}
|
||||
} else if (file_exists($source) && ($overwrite || !file_exists($dest))) {
|
||||
copy($source, $dest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove dir
|
||||
* @param string $dir
|
||||
* @return bool
|
||||
*/
|
||||
function remove_dir(string $dir): bool
|
||||
{
|
||||
if (is_link($dir) || is_file($dir)) {
|
||||
return unlink($dir);
|
||||
}
|
||||
$files = array_diff(scandir($dir), array('.', '..'));
|
||||
foreach ($files as $file) {
|
||||
(is_dir("$dir/$file") && !is_link($dir)) ? remove_dir("$dir/$file") : unlink("$dir/$file");
|
||||
}
|
||||
return rmdir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind worker
|
||||
* @param $worker
|
||||
* @param $class
|
||||
*/
|
||||
function worker_bind($worker, $class)
|
||||
{
|
||||
$callbackMap = [
|
||||
'onConnect',
|
||||
'onMessage',
|
||||
'onClose',
|
||||
'onError',
|
||||
'onBufferFull',
|
||||
'onBufferDrain',
|
||||
'onWorkerStop',
|
||||
'onWebSocketConnect',
|
||||
'onWorkerReload'
|
||||
];
|
||||
foreach ($callbackMap as $name) {
|
||||
if (method_exists($class, $name)) {
|
||||
$worker->$name = [$class, $name];
|
||||
}
|
||||
}
|
||||
if (method_exists($class, 'onWorkerStart')) {
|
||||
call_user_func([$class, 'onWorkerStart'], $worker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start worker
|
||||
* @param $processName
|
||||
* @param $config
|
||||
* @return void
|
||||
*/
|
||||
function worker_start($processName, $config)
|
||||
{
|
||||
$worker = new Worker($config['listen'] ?? null, $config['context'] ?? []);
|
||||
$propertyMap = [
|
||||
'count',
|
||||
'user',
|
||||
'group',
|
||||
'reloadable',
|
||||
'reusePort',
|
||||
'transport',
|
||||
'protocol',
|
||||
];
|
||||
$worker->name = $processName;
|
||||
foreach ($propertyMap as $property) {
|
||||
if (isset($config[$property])) {
|
||||
$worker->$property = $config[$property];
|
||||
}
|
||||
}
|
||||
|
||||
$worker->onWorkerStart = function ($worker) use ($config) {
|
||||
require_once base_path('/support/bootstrap.php');
|
||||
if (isset($config['handler'])) {
|
||||
if (!class_exists($config['handler'])) {
|
||||
echo "process error: class {$config['handler']} not exists\r\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$instance = Container::make($config['handler'], $config['constructor'] ?? []);
|
||||
worker_bind($worker, $instance);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get realpath
|
||||
* @param string $filePath
|
||||
* @return string
|
||||
*/
|
||||
function get_realpath(string $filePath): string
|
||||
{
|
||||
if (strpos($filePath, 'phar://') === 0) {
|
||||
return $filePath;
|
||||
} else {
|
||||
return realpath($filePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is phar
|
||||
* @return bool
|
||||
*/
|
||||
function is_phar(): bool
|
||||
{
|
||||
return class_exists(Phar::class, false) && Phar::running();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cpu count
|
||||
* @return int
|
||||
*/
|
||||
function cpu_count(): int
|
||||
{
|
||||
// Windows does not support the number of processes setting.
|
||||
if (DIRECTORY_SEPARATOR === '\\') {
|
||||
return 1;
|
||||
}
|
||||
$count = 4;
|
||||
if (is_callable('shell_exec')) {
|
||||
if (strtolower(PHP_OS) === 'darwin') {
|
||||
$count = (int)shell_exec('sysctl -n machdep.cpu.core_count');
|
||||
} else {
|
||||
$count = (int)shell_exec('nproc');
|
||||
}
|
||||
}
|
||||
return $count > 0 ? $count : 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request parameters, if no parameter name is passed, an array of all values is returned, default values is supported
|
||||
* @param string|null $param param's name
|
||||
* @param mixed|null $default default value
|
||||
* @return mixed|null
|
||||
*/
|
||||
function input(string $param = null, $default = null)
|
||||
{
|
||||
return is_null($param) ? request()->all() : request()->input($param, $default);
|
||||
}
|
2
cacme/vendor/acmephp/core/.gitignore
vendored
Normal file
2
cacme/vendor/acmephp/core/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
composer.lock
|
||||
vendor/
|
404
cacme/vendor/acmephp/core/AcmeClient.php
vendored
Normal file
404
cacme/vendor/acmephp/core/AcmeClient.php
vendored
Normal file
@ -0,0 +1,404 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreClientException;
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use AcmePhp\Core\Exception\Protocol\CertificateRequestFailedException;
|
||||
use AcmePhp\Core\Exception\Protocol\CertificateRevocationException;
|
||||
use AcmePhp\Core\Exception\Protocol\ChallengeFailedException;
|
||||
use AcmePhp\Core\Exception\Protocol\ChallengeNotSupportedException;
|
||||
use AcmePhp\Core\Exception\Protocol\ChallengeTimedOutException;
|
||||
use AcmePhp\Core\Http\SecureHttpClient;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
use AcmePhp\Core\Protocol\CertificateOrder;
|
||||
use AcmePhp\Core\Protocol\ExternalAccount;
|
||||
use AcmePhp\Core\Protocol\ResourcesDirectory;
|
||||
use AcmePhp\Core\Protocol\RevocationReason;
|
||||
use AcmePhp\Ssl\Certificate;
|
||||
use AcmePhp\Ssl\CertificateRequest;
|
||||
use AcmePhp\Ssl\CertificateResponse;
|
||||
use AcmePhp\Ssl\Signer\CertificateRequestSigner;
|
||||
use GuzzleHttp\Psr7\Utils;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/**
|
||||
* ACME protocol client implementation.
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class AcmeClient implements AcmeClientInterface
|
||||
{
|
||||
/**
|
||||
* @var SecureHttpClient
|
||||
*/
|
||||
private $uninitializedHttpClient;
|
||||
|
||||
/**
|
||||
* @var SecureHttpClient
|
||||
*/
|
||||
private $initializedHttpClient;
|
||||
|
||||
/**
|
||||
* @var CertificateRequestSigner
|
||||
*/
|
||||
private $csrSigner;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $directoryUrl;
|
||||
|
||||
/**
|
||||
* @var ResourcesDirectory
|
||||
*/
|
||||
private $directory;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $account;
|
||||
|
||||
public function __construct(SecureHttpClient $httpClient, string $directoryUrl, CertificateRequestSigner $csrSigner = null)
|
||||
{
|
||||
$this->uninitializedHttpClient = $httpClient;
|
||||
$this->directoryUrl = $directoryUrl;
|
||||
$this->csrSigner = $csrSigner ?: new CertificateRequestSigner();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function registerAccount(string $email = null, ExternalAccount $externalAccount = null): array
|
||||
{
|
||||
$client = $this->getHttpClient();
|
||||
|
||||
$payload = [
|
||||
'termsOfServiceAgreed' => true,
|
||||
'contact' => [],
|
||||
];
|
||||
|
||||
if ($email) {
|
||||
$payload['contact'][] = 'mailto:'.$email;
|
||||
}
|
||||
|
||||
if ($externalAccount) {
|
||||
$payload['externalAccountBinding'] = $client->createExternalAccountPayload(
|
||||
$externalAccount,
|
||||
$this->getResourceUrl(ResourcesDirectory::NEW_ACCOUNT)
|
||||
);
|
||||
}
|
||||
|
||||
$this->requestResource('POST', ResourcesDirectory::NEW_ACCOUNT, $payload);
|
||||
$account = $this->getResourceAccount();
|
||||
|
||||
return $client->request('POST', $account, $client->signKidPayload($account, $account, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function requestOrder(array $domains): CertificateOrder
|
||||
{
|
||||
Assert::allStringNotEmpty($domains, 'requestOrder::$domains expected a list of strings. Got: %s');
|
||||
|
||||
$payload = [
|
||||
'identifiers' => array_map(
|
||||
static function ($domain) {
|
||||
return [
|
||||
'type' => 'dns',
|
||||
'value' => $domain,
|
||||
];
|
||||
},
|
||||
array_values($domains)
|
||||
),
|
||||
];
|
||||
|
||||
$client = $this->getHttpClient();
|
||||
$resourceUrl = $this->getResourceUrl(ResourcesDirectory::NEW_ORDER);
|
||||
$response = $client->request('POST', $resourceUrl, $client->signKidPayload($resourceUrl, $this->getResourceAccount(), $payload));
|
||||
if (!isset($response['authorizations']) || !$response['authorizations']) {
|
||||
throw new ChallengeNotSupportedException();
|
||||
}
|
||||
|
||||
$authorizationsChallenges = [];
|
||||
$orderEndpoint = $client->getLastLocation();
|
||||
foreach ($response['authorizations'] as $authorizationEndpoint) {
|
||||
$authorizationsResponse = $client->request('POST', $authorizationEndpoint, $client->signKidPayload($authorizationEndpoint, $this->getResourceAccount(), null));
|
||||
$domain = (empty($authorizationsResponse['wildcard']) ? '' : '*.').$authorizationsResponse['identifier']['value'];
|
||||
foreach ($authorizationsResponse['challenges'] as $challenge) {
|
||||
$authorizationsChallenges[$domain][] = $this->createAuthorizationChallenge($authorizationsResponse['identifier']['value'], $challenge);
|
||||
}
|
||||
}
|
||||
|
||||
return new CertificateOrder($authorizationsChallenges, $orderEndpoint, $response['status']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reloadOrder(CertificateOrder $order): CertificateOrder
|
||||
{
|
||||
$client = $this->getHttpClient();
|
||||
$orderEndpoint = $order->getOrderEndpoint();
|
||||
$response = $client->request('POST', $orderEndpoint, $client->signKidPayload($orderEndpoint, $this->getResourceAccount(), null));
|
||||
|
||||
if (!isset($response['authorizations']) || !$response['authorizations']) {
|
||||
throw new ChallengeNotSupportedException();
|
||||
}
|
||||
|
||||
$authorizationsChallenges = [];
|
||||
foreach ($response['authorizations'] as $authorizationEndpoint) {
|
||||
$authorizationsResponse = $client->request('POST', $authorizationEndpoint, $client->signKidPayload($authorizationEndpoint, $this->getResourceAccount(), null));
|
||||
$domain = (empty($authorizationsResponse['wildcard']) ? '' : '*.').$authorizationsResponse['identifier']['value'];
|
||||
foreach ($authorizationsResponse['challenges'] as $challenge) {
|
||||
$authorizationsChallenges[$domain][] = $this->createAuthorizationChallenge($authorizationsResponse['identifier']['value'], $challenge);
|
||||
}
|
||||
}
|
||||
|
||||
return new CertificateOrder($authorizationsChallenges, $orderEndpoint, $response['status']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, int $timeout = 180, bool $returnAlternateCertificateIfAvailable = false): CertificateResponse
|
||||
{
|
||||
$endTime = time() + $timeout;
|
||||
$client = $this->getHttpClient();
|
||||
$orderEndpoint = $order->getOrderEndpoint();
|
||||
$response = $client->request('POST', $orderEndpoint, $client->signKidPayload($orderEndpoint, $this->getResourceAccount(), null));
|
||||
if (\in_array($response['status'], ['pending', 'processing', 'ready'])) {
|
||||
$humanText = ['-----BEGIN CERTIFICATE REQUEST-----', '-----END CERTIFICATE REQUEST-----'];
|
||||
|
||||
$csrContent = $this->csrSigner->signCertificateRequest($csr);
|
||||
$csrContent = trim(str_replace($humanText, '', $csrContent));
|
||||
$csrContent = trim($client->getBase64Encoder()->encode(base64_decode($csrContent)));
|
||||
|
||||
$response = $client->request('POST', $response['finalize'], $client->signKidPayload($response['finalize'], $this->getResourceAccount(), ['csr' => $csrContent]));
|
||||
}
|
||||
|
||||
// Waiting loop
|
||||
while (time() <= $endTime && (!isset($response['status']) || \in_array($response['status'], ['pending', 'processing', 'ready']))) {
|
||||
sleep(1);
|
||||
$response = $client->request('POST', $orderEndpoint, $client->signKidPayload($orderEndpoint, $this->getResourceAccount(), null));
|
||||
}
|
||||
|
||||
if ('valid' !== $response['status']) {
|
||||
throw new CertificateRequestFailedException('The order has not been validated');
|
||||
}
|
||||
|
||||
$response = $client->rawRequest('POST', $response['certificate'], $client->signKidPayload($response['certificate'], $this->getResourceAccount(), null));
|
||||
$responseHeaders = $response->getHeaders();
|
||||
|
||||
if ($returnAlternateCertificateIfAvailable && isset($responseHeaders['Link'][1])) {
|
||||
$matches = [];
|
||||
preg_match('/<(http.*)>;rel="alternate"/', $responseHeaders['Link'][1], $matches);
|
||||
|
||||
// If response headers include a valid alternate certificate link, return that certificate instead
|
||||
if (isset($matches[1])) {
|
||||
return $this->createCertificateResponse(
|
||||
$csr,
|
||||
$client->request('POST', $matches[1], $client->signKidPayload($matches[1], $this->getResourceAccount(), null), false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createCertificateResponse($csr, Utils::copyToString($response->getBody()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function requestAuthorization(string $domain): array
|
||||
{
|
||||
$order = $this->requestOrder([$domain]);
|
||||
|
||||
try {
|
||||
return $order->getAuthorizationChallenges($domain);
|
||||
} catch (AcmeCoreClientException $e) {
|
||||
throw new ChallengeNotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reloadAuthorization(AuthorizationChallenge $challenge): AuthorizationChallenge
|
||||
{
|
||||
$client = $this->getHttpClient();
|
||||
$challengeUrl = $challenge->getUrl();
|
||||
$response = (array) $client->request('POST', $challengeUrl, $client->signKidPayload($challengeUrl, $this->getResourceAccount(), null));
|
||||
|
||||
return $this->createAuthorizationChallenge($challenge->getDomain(), $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function challengeAuthorization(AuthorizationChallenge $challenge, int $timeout = 180): array
|
||||
{
|
||||
$endTime = time() + $timeout;
|
||||
$client = $this->getHttpClient();
|
||||
$challengeUrl = $challenge->getUrl();
|
||||
$response = (array) $client->request('POST', $challengeUrl, $client->signKidPayload($challengeUrl, $this->getResourceAccount(), null));
|
||||
if ('pending' === $response['status'] || 'processing' === $response['status']) {
|
||||
$response = (array) $client->request('POST', $challengeUrl, $client->signKidPayload($challengeUrl, $this->getResourceAccount(), []));
|
||||
}
|
||||
|
||||
// Waiting loop
|
||||
while (time() <= $endTime && (!isset($response['status']) || 'pending' === $response['status'] || 'processing' === $response['status'])) {
|
||||
sleep(1);
|
||||
$response = (array) $client->request('POST', $challengeUrl, $client->signKidPayload($challengeUrl, $this->getResourceAccount(), null));
|
||||
}
|
||||
|
||||
if (isset($response['status']) && ('pending' === $response['status'] || 'processing' === $response['status'])) {
|
||||
throw new ChallengeTimedOutException($response);
|
||||
}
|
||||
if (!isset($response['status']) || 'valid' !== $response['status']) {
|
||||
throw new ChallengeFailedException($response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function requestCertificate(string $domain, CertificateRequest $csr, int $timeout = 180, bool $returnAlternateCertificateIfAvailable = false): CertificateResponse
|
||||
{
|
||||
$order = $this->requestOrder(array_unique(array_merge([$domain], $csr->getDistinguishedName()->getSubjectAlternativeNames())));
|
||||
|
||||
return $this->finalizeOrder($order, $csr, $timeout, $returnAlternateCertificateIfAvailable);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function revokeCertificate(Certificate $certificate, RevocationReason $revocationReason = null)
|
||||
{
|
||||
if (!$endpoint = $this->getResourceUrl(ResourcesDirectory::REVOKE_CERT)) {
|
||||
throw new CertificateRevocationException('This ACME server does not support certificate revocation.');
|
||||
}
|
||||
|
||||
if (null === $revocationReason) {
|
||||
$revocationReason = RevocationReason::createDefaultReason();
|
||||
}
|
||||
|
||||
openssl_x509_export(openssl_x509_read($certificate->getPEM()), $formattedPem);
|
||||
|
||||
$formattedPem = str_ireplace('-----BEGIN CERTIFICATE-----', '', $formattedPem);
|
||||
$formattedPem = str_ireplace('-----END CERTIFICATE-----', '', $formattedPem);
|
||||
$client = $this->getHttpClient();
|
||||
$formattedPem = $client->getBase64Encoder()->encode(base64_decode(trim($formattedPem)));
|
||||
|
||||
try {
|
||||
$client->request(
|
||||
'POST',
|
||||
$endpoint,
|
||||
$client->signKidPayload($endpoint, $this->getResourceAccount(), ['certificate' => $formattedPem, 'reason' => $revocationReason->getReasonType()]),
|
||||
false
|
||||
);
|
||||
} catch (AcmeCoreServerException $e) {
|
||||
throw new CertificateRevocationException($e->getMessage(), $e);
|
||||
} catch (AcmeCoreClientException $e) {
|
||||
throw new CertificateRevocationException($e->getMessage(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a resource URL from the Certificate Authority.
|
||||
*/
|
||||
public function getResourceUrl(string $resource): string
|
||||
{
|
||||
if (!$this->directory) {
|
||||
$this->directory = new ResourcesDirectory(
|
||||
$this->getHttpClient()->request('GET', $this->directoryUrl)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->directory->getResourceUrl($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a resource (URL is found using ACME server directory).
|
||||
*
|
||||
* @throws AcmeCoreServerException when the ACME server returns an error HTTP status code
|
||||
* @throws AcmeCoreClientException when an error occured during response parsing
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
protected function requestResource(string $method, string $resource, array $payload, bool $returnJson = true)
|
||||
{
|
||||
$client = $this->getHttpClient();
|
||||
$endpoint = $this->getResourceUrl($resource);
|
||||
|
||||
return $client->request(
|
||||
$method,
|
||||
$endpoint,
|
||||
$client->signJwkPayload($endpoint, $payload),
|
||||
$returnJson
|
||||
);
|
||||
}
|
||||
|
||||
private function createCertificateResponse(CertificateRequest $csr, string $certificate): CertificateResponse
|
||||
{
|
||||
$certificateHeader = '-----BEGIN CERTIFICATE-----';
|
||||
$certificatesChain = null;
|
||||
|
||||
foreach (array_reverse(explode($certificateHeader, $certificate)) as $pem) {
|
||||
if ('' !== \trim($pem)) {
|
||||
$certificatesChain = new Certificate($certificateHeader.$pem, $certificatesChain);
|
||||
}
|
||||
}
|
||||
|
||||
return new CertificateResponse($csr, $certificatesChain);
|
||||
}
|
||||
|
||||
private function getResourceAccount(): string
|
||||
{
|
||||
if (!$this->account) {
|
||||
$payload = [
|
||||
'onlyReturnExisting' => true,
|
||||
];
|
||||
|
||||
$this->requestResource('POST', ResourcesDirectory::NEW_ACCOUNT, $payload);
|
||||
$this->account = $this->getHttpClient()->getLastLocation();
|
||||
}
|
||||
|
||||
return $this->account;
|
||||
}
|
||||
|
||||
private function createAuthorizationChallenge($domain, array $response): AuthorizationChallenge
|
||||
{
|
||||
$base64encoder = $this->getHttpClient()->getBase64Encoder();
|
||||
|
||||
return new AuthorizationChallenge(
|
||||
$domain,
|
||||
$response['status'],
|
||||
$response['type'],
|
||||
$response['url'],
|
||||
$response['token'],
|
||||
$response['token'].'.'.$base64encoder->encode($this->getHttpClient()->getJWKThumbprint())
|
||||
);
|
||||
}
|
||||
|
||||
private function getHttpClient(): SecureHttpClient
|
||||
{
|
||||
if (!$this->initializedHttpClient) {
|
||||
$this->initializedHttpClient = $this->uninitializedHttpClient;
|
||||
$this->initializedHttpClient->setNonceEndpoint($this->getResourceUrl(ResourcesDirectory::NEW_NONCE));
|
||||
}
|
||||
|
||||
return $this->initializedHttpClient;
|
||||
}
|
||||
}
|
187
cacme/vendor/acmephp/core/AcmeClientInterface.php
vendored
Normal file
187
cacme/vendor/acmephp/core/AcmeClientInterface.php
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreClientException;
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use AcmePhp\Core\Exception\Protocol\CertificateRequestFailedException;
|
||||
use AcmePhp\Core\Exception\Protocol\CertificateRequestTimedOutException;
|
||||
use AcmePhp\Core\Exception\Protocol\CertificateRevocationException;
|
||||
use AcmePhp\Core\Exception\Protocol\ChallengeFailedException;
|
||||
use AcmePhp\Core\Exception\Protocol\ChallengeNotSupportedException;
|
||||
use AcmePhp\Core\Exception\Protocol\ChallengeTimedOutException;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
use AcmePhp\Core\Protocol\CertificateOrder;
|
||||
use AcmePhp\Core\Protocol\ExternalAccount;
|
||||
use AcmePhp\Core\Protocol\RevocationReason;
|
||||
use AcmePhp\Ssl\Certificate;
|
||||
use AcmePhp\Ssl\CertificateRequest;
|
||||
use AcmePhp\Ssl\CertificateResponse;
|
||||
|
||||
/**
|
||||
* ACME protocol client interface.
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
interface AcmeClientInterface
|
||||
{
|
||||
/**
|
||||
* Register the local account KeyPair in the Certificate Authority.
|
||||
*
|
||||
* @param string|null $email an optionnal e-mail to associate with the account
|
||||
* @param ExternalAccount|null $externalAccount an optionnal External Account to use for External Account Binding
|
||||
*
|
||||
* @throws AcmeCoreServerException when the ACME server returns an error HTTP status code
|
||||
* (the exception will be more specific if detail is provided)
|
||||
* @throws AcmeCoreClientException when an error occured during response parsing
|
||||
*
|
||||
* @return array the Certificate Authority response decoded from JSON into an array
|
||||
*/
|
||||
public function registerAccount(string $email = null, ExternalAccount $externalAccount = null): array;
|
||||
|
||||
/**
|
||||
* Request authorization challenge data for a list of domains.
|
||||
*
|
||||
* An AuthorizationChallenge is an association between a URI, a token and a payload.
|
||||
* The Certificate Authority will create this challenge data and you will then have
|
||||
* to expose the payload for the verification (see challengeAuthorization).
|
||||
*
|
||||
* @param string[] $domains the domains to challenge
|
||||
*
|
||||
* @throws AcmeCoreServerException when the ACME server returns an error HTTP status code
|
||||
* (the exception will be more specific if detail is provided)
|
||||
* @throws AcmeCoreClientException when an error occured during response parsing
|
||||
* @throws ChallengeNotSupportedException when the HTTP challenge is not supported by the server
|
||||
*
|
||||
* @return CertificateOrder the Order returned by the Certificate Authority
|
||||
*/
|
||||
public function requestOrder(array $domains): CertificateOrder;
|
||||
|
||||
/**
|
||||
* Request the current status of a certificate order.
|
||||
*/
|
||||
public function reloadOrder(CertificateOrder $order): CertificateOrder;
|
||||
|
||||
/**
|
||||
* Request a certificate for the given domain.
|
||||
*
|
||||
* This method should be called only if a previous authorization challenge has
|
||||
* been successful for the asked domain.
|
||||
*
|
||||
* WARNING : This method SHOULD NOT BE USED in a web action. It will
|
||||
* wait for the Certificate Authority to validate the certificate and
|
||||
* this operation could be long.
|
||||
*
|
||||
* @param CertificateOrder $order the Order returned by the Certificate Authority
|
||||
* @param CertificateRequest $csr the Certificate Signing Request (informations for the certificate)
|
||||
* @param int $timeout the timeout period
|
||||
* @param bool $returnAlternateCertificateIfAvailable whether the alternate certificate provided by
|
||||
* the CA should be returned instead of the main one.
|
||||
* This is especially useful following
|
||||
* https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html.
|
||||
*
|
||||
* @throws AcmeCoreServerException when the ACME server returns an error HTTP status code
|
||||
* (the exception will be more specific if detail is provided)
|
||||
* @throws AcmeCoreClientException when an error occured during response parsing
|
||||
* @throws CertificateRequestFailedException when the certificate request failed
|
||||
* @throws CertificateRequestTimedOutException when the certificate request timed out
|
||||
*
|
||||
* @return CertificateResponse the certificate data to save it somewhere you want
|
||||
*/
|
||||
public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, int $timeout = 180, bool $returnAlternateCertificateIfAvailable = false): CertificateResponse;
|
||||
|
||||
/**
|
||||
* Request authorization challenge data for a given domain.
|
||||
*
|
||||
* An AuthorizationChallenge is an association between a URI, a token and a payload.
|
||||
* The Certificate Authority will create this challenge data and you will then have
|
||||
* to expose the payload for the verification (see challengeAuthorization).
|
||||
*
|
||||
* @param string $domain the domain to challenge
|
||||
*
|
||||
* @throws AcmeCoreServerException when the ACME server returns an error HTTP status code
|
||||
* (the exception will be more specific if detail is provided)
|
||||
* @throws AcmeCoreClientException when an error occured during response parsing
|
||||
* @throws ChallengeNotSupportedException when the HTTP challenge is not supported by the server
|
||||
*
|
||||
* @return AuthorizationChallenge[] the list of challenges data returned by the Certificate Authority
|
||||
*/
|
||||
public function requestAuthorization(string $domain): array;
|
||||
|
||||
/**
|
||||
* Request the current status of an authorization challenge.
|
||||
*
|
||||
* @param AuthorizationChallenge $challenge The challenge to request
|
||||
*
|
||||
* @return AuthorizationChallenge A new instance of the challenge
|
||||
*/
|
||||
public function reloadAuthorization(AuthorizationChallenge $challenge): AuthorizationChallenge;
|
||||
|
||||
/**
|
||||
* Ask the Certificate Authority to challenge a given authorization.
|
||||
*
|
||||
* This check will generally consists of requesting over HTTP the domain
|
||||
* at a specific URL. This URL should return the raw payload generated
|
||||
* by requestAuthorization.
|
||||
*
|
||||
* WARNING : This method SHOULD NOT BE USED in a web action. It will
|
||||
* wait for the Certificate Authority to validate the challenge and this
|
||||
* operation could be long.
|
||||
*
|
||||
* @param AuthorizationChallenge $challenge the challenge data to check
|
||||
* @param int $timeout the timeout period
|
||||
*
|
||||
* @throws AcmeCoreServerException when the ACME server returns an error HTTP status code
|
||||
* (the exception will be more specific if detail is provided)
|
||||
* @throws AcmeCoreClientException when an error occured during response parsing
|
||||
* @throws ChallengeTimedOutException when the challenge timed out
|
||||
* @throws ChallengeFailedException when the challenge failed
|
||||
*
|
||||
* @return array the validate challenge response
|
||||
*/
|
||||
public function challengeAuthorization(AuthorizationChallenge $challenge, int $timeout = 180): array;
|
||||
|
||||
/**
|
||||
* Request a certificate for the given domain.
|
||||
*
|
||||
* This method should be called only if a previous authorization challenge has
|
||||
* been successful for the asked domain.
|
||||
*
|
||||
* WARNING : This method SHOULD NOT BE USED in a web action. It will
|
||||
* wait for the Certificate Authority to validate the certificate and
|
||||
* this operation could be long.
|
||||
*
|
||||
* @param string $domain the domain to request a certificate for
|
||||
* @param CertificateRequest $csr the Certificate Signing Request (informations for the certificate)
|
||||
* @param int $timeout the timeout period
|
||||
* @param bool $returnAlternateCertificateIfAvailable whether the alternate certificate provided by
|
||||
* the CA should be returned instead of the main one.
|
||||
* This is especially useful following
|
||||
* https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html.
|
||||
*
|
||||
* @throws AcmeCoreServerException when the ACME server returns an error HTTP status code
|
||||
* (the exception will be more specific if detail is provided)
|
||||
* @throws AcmeCoreClientException when an error occured during response parsing
|
||||
* @throws CertificateRequestFailedException when the certificate request failed
|
||||
* @throws CertificateRequestTimedOutException when the certificate request timed out
|
||||
*
|
||||
* @return CertificateResponse the certificate data to save it somewhere you want
|
||||
*/
|
||||
public function requestCertificate(string $domain, CertificateRequest $csr, int $timeout = 180, bool $returnAlternateCertificateIfAvailable = false): CertificateResponse;
|
||||
|
||||
/**
|
||||
* Revoke a given certificate from the Certificate Authority.
|
||||
*
|
||||
* @throws CertificateRevocationException
|
||||
*/
|
||||
public function revokeCertificate(Certificate $certificate, RevocationReason $revocationReason = null);
|
||||
}
|
62
cacme/vendor/acmephp/core/Challenge/ChainValidator.php
vendored
Normal file
62
cacme/vendor/acmephp/core/Challenge/ChainValidator.php
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge;
|
||||
|
||||
use AcmePhp\Core\Exception\Protocol\ChallengeNotSupportedException;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
|
||||
/**
|
||||
* A strategy ACME validator.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class ChainValidator implements ValidatorInterface
|
||||
{
|
||||
/** @var ValidatorInterface[] */
|
||||
private $validators;
|
||||
|
||||
/**
|
||||
* @param ValidatorInterface[] $validators
|
||||
*/
|
||||
public function __construct(array $validators)
|
||||
{
|
||||
$this->validators = $validators;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool
|
||||
{
|
||||
foreach ($this->validators as $validator) {
|
||||
if ($validator->supports($authorizationChallenge, $solver)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool
|
||||
{
|
||||
foreach ($this->validators as $validator) {
|
||||
if ($validator->supports($authorizationChallenge, $solver)) {
|
||||
return $validator->isValid($authorizationChallenge, $solver);
|
||||
}
|
||||
}
|
||||
|
||||
throw new ChallengeNotSupportedException();
|
||||
}
|
||||
}
|
25
cacme/vendor/acmephp/core/Challenge/ConfigurableServiceInterface.php
vendored
Normal file
25
cacme/vendor/acmephp/core/Challenge/ConfigurableServiceInterface.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge;
|
||||
|
||||
/**
|
||||
* ACME challenge solver.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
interface ConfigurableServiceInterface
|
||||
{
|
||||
/**
|
||||
* Configure the service with a set of configuration.
|
||||
*/
|
||||
public function configure(array $config);
|
||||
}
|
47
cacme/vendor/acmephp/core/Challenge/Dns/DnsDataExtractor.php
vendored
Normal file
47
cacme/vendor/acmephp/core/Challenge/Dns/DnsDataExtractor.php
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Dns;
|
||||
|
||||
use AcmePhp\Core\Http\Base64SafeEncoder;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
|
||||
/**
|
||||
* Extract data needed to solve DNS challenges.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class DnsDataExtractor
|
||||
{
|
||||
/** @var Base64SafeEncoder */
|
||||
private $encoder;
|
||||
|
||||
public function __construct(Base64SafeEncoder $encoder = null)
|
||||
{
|
||||
$this->encoder = $encoder ?: new Base64SafeEncoder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the name of the TXT record to register.
|
||||
*/
|
||||
public function getRecordName(AuthorizationChallenge $authorizationChallenge): string
|
||||
{
|
||||
return sprintf('_acme-challenge.%s.', $authorizationChallenge->getDomain());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of the TXT record to register.
|
||||
*/
|
||||
public function getRecordValue(AuthorizationChallenge $authorizationChallenge): string
|
||||
{
|
||||
return $this->encoder->encode(hash('sha256', $authorizationChallenge->getPayload(), true));
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Challenge/Dns/DnsResolverInterface.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Challenge/Dns/DnsResolverInterface.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Dns;
|
||||
|
||||
/**
|
||||
* Resolves DNS.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
interface DnsResolverInterface
|
||||
{
|
||||
/**
|
||||
* Return whether or not the Resolver is supported.
|
||||
*/
|
||||
public static function isSupported(): bool;
|
||||
|
||||
/**
|
||||
* Retrieves the list of TXT entries for the given domain.
|
||||
*/
|
||||
public function getTxtEntries(string $domain): array;
|
||||
}
|
68
cacme/vendor/acmephp/core/Challenge/Dns/DnsValidator.php
vendored
Normal file
68
cacme/vendor/acmephp/core/Challenge/Dns/DnsValidator.php
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Dns;
|
||||
|
||||
use AcmePhp\Core\Challenge\SolverInterface;
|
||||
use AcmePhp\Core\Challenge\ValidatorInterface;
|
||||
use AcmePhp\Core\Exception\AcmeDnsResolutionException;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
|
||||
/**
|
||||
* Validator for DNS challenges.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class DnsValidator implements ValidatorInterface
|
||||
{
|
||||
/**
|
||||
* @var DnsDataExtractor
|
||||
*/
|
||||
private $extractor;
|
||||
|
||||
/**
|
||||
* @var DnsResolverInterface
|
||||
*/
|
||||
private $dnsResolver;
|
||||
|
||||
public function __construct(DnsDataExtractor $extractor = null, DnsResolverInterface $dnsResolver = null)
|
||||
{
|
||||
$this->extractor = $extractor ?: new DnsDataExtractor();
|
||||
|
||||
$this->dnsResolver = $dnsResolver;
|
||||
if (!$this->dnsResolver) {
|
||||
$this->dnsResolver = LibDnsResolver::isSupported() ? new LibDnsResolver() : new SimpleDnsResolver();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool
|
||||
{
|
||||
return 'dns-01' === $authorizationChallenge->getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool
|
||||
{
|
||||
$recordName = $this->extractor->getRecordName($authorizationChallenge);
|
||||
$recordValue = $this->extractor->getRecordValue($authorizationChallenge);
|
||||
|
||||
try {
|
||||
return \in_array($recordValue, $this->dnsResolver->getTxtEntries($recordName), false);
|
||||
} catch (AcmeDnsResolutionException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
152
cacme/vendor/acmephp/core/Challenge/Dns/GandiSolver.php
vendored
Normal file
152
cacme/vendor/acmephp/core/Challenge/Dns/GandiSolver.php
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Dns;
|
||||
|
||||
use AcmePhp\Core\Challenge\ConfigurableServiceInterface;
|
||||
use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use Psr\Log\NullLogger;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/**
|
||||
* ACME DNS solver with automate configuration of a Gandi.Net.
|
||||
*
|
||||
* @author Alexander Obuhovich <aik.bold@gmail.com>
|
||||
*/
|
||||
class GandiSolver implements MultipleChallengesSolverInterface, ConfigurableServiceInterface
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var DnsDataExtractor
|
||||
*/
|
||||
private $extractor;
|
||||
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $cacheZones;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $apiKey;
|
||||
|
||||
public function __construct(DnsDataExtractor $extractor = null, ClientInterface $client = null)
|
||||
{
|
||||
$this->extractor = $extractor ?: new DnsDataExtractor();
|
||||
$this->client = $client ?: new Client();
|
||||
$this->logger = new NullLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the service with a set of configuration.
|
||||
*/
|
||||
public function configure(array $config)
|
||||
{
|
||||
$this->apiKey = $config['api_key'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge): bool
|
||||
{
|
||||
return 'dns-01' === $authorizationChallenge->getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function solve(AuthorizationChallenge $authorizationChallenge)
|
||||
{
|
||||
return $this->solveAll([$authorizationChallenge]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function solveAll(array $authorizationChallenges)
|
||||
{
|
||||
Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class);
|
||||
|
||||
foreach ($authorizationChallenges as $authorizationChallenge) {
|
||||
$topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain());
|
||||
$recordName = $this->extractor->getRecordName($authorizationChallenge);
|
||||
$recordValue = $this->extractor->getRecordValue($authorizationChallenge);
|
||||
|
||||
$subDomain = \str_replace('.'.$topLevelDomain.'.', '', $recordName);
|
||||
|
||||
$this->client->request(
|
||||
'PUT',
|
||||
'https://dns.api.gandi.net/api/v5/domains/'.$topLevelDomain.'/records/'.$subDomain.'/TXT',
|
||||
[
|
||||
'headers' => [
|
||||
'X-Api-Key' => $this->apiKey,
|
||||
],
|
||||
'json' => [
|
||||
'rrset_type' => 'TXT',
|
||||
'rrset_ttl' => 600,
|
||||
'rrset_name' => $subDomain,
|
||||
'rrset_values' => [$recordValue],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanup(AuthorizationChallenge $authorizationChallenge)
|
||||
{
|
||||
return $this->cleanupAll([$authorizationChallenge]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanupAll(array $authorizationChallenges)
|
||||
{
|
||||
Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class);
|
||||
|
||||
foreach ($authorizationChallenges as $authorizationChallenge) {
|
||||
$topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain());
|
||||
$recordName = $this->extractor->getRecordName($authorizationChallenge);
|
||||
|
||||
$subDomain = \str_replace('.'.$topLevelDomain.'.', '', $recordName);
|
||||
|
||||
$this->client->request(
|
||||
'DELETE',
|
||||
'https://dns.api.gandi.net/api/v5/domains/'.$topLevelDomain.'/records/'.$subDomain.'/TXT',
|
||||
[
|
||||
'headers' => [
|
||||
'X-Api-Key' => $this->apiKey,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getTopLevelDomain(string $domain): string
|
||||
{
|
||||
return \implode('.', \array_slice(\explode('.', $domain), -2));
|
||||
}
|
||||
}
|
200
cacme/vendor/acmephp/core/Challenge/Dns/LibDnsResolver.php
vendored
Normal file
200
cacme/vendor/acmephp/core/Challenge/Dns/LibDnsResolver.php
vendored
Normal file
@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Dns;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeDnsResolutionException;
|
||||
use LibDNS\Decoder\Decoder;
|
||||
use LibDNS\Decoder\DecoderFactory;
|
||||
use LibDNS\Encoder\Encoder;
|
||||
use LibDNS\Encoder\EncoderFactory;
|
||||
use LibDNS\Messages\MessageFactory;
|
||||
use LibDNS\Messages\MessageTypes;
|
||||
use LibDNS\Records\QuestionFactory;
|
||||
use LibDNS\Records\ResourceTypes;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
/**
|
||||
* Resolves DNS with LibDNS (pass over internal DNS cache and check several nameservers).
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class LibDnsResolver implements DnsResolverInterface
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var QuestionFactory
|
||||
*/
|
||||
private $questionFactory;
|
||||
|
||||
/**
|
||||
* @var MessageFactory
|
||||
*/
|
||||
private $messageFactory;
|
||||
|
||||
/**
|
||||
* @var Encoder
|
||||
*/
|
||||
private $encoder;
|
||||
|
||||
/**
|
||||
* @var Decoder
|
||||
*/
|
||||
private $decoder;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $nameServer;
|
||||
|
||||
public function __construct(
|
||||
QuestionFactory $questionFactory = null,
|
||||
MessageFactory $messageFactory = null,
|
||||
Encoder $encoder = null,
|
||||
Decoder $decoder = null,
|
||||
$nameServer = '8.8.8.8'
|
||||
) {
|
||||
$this->questionFactory = $questionFactory ?: new QuestionFactory();
|
||||
$this->messageFactory = $messageFactory ?: new MessageFactory();
|
||||
$this->encoder = $encoder ?: (new EncoderFactory())->create();
|
||||
$this->decoder = $decoder ?: (new DecoderFactory())->create();
|
||||
$this->nameServer = $nameServer;
|
||||
$this->logger = new NullLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* @{@inheritdoc}
|
||||
*/
|
||||
public static function isSupported(): bool
|
||||
{
|
||||
return class_exists(ResourceTypes::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @{@inheritdoc}
|
||||
*/
|
||||
public function getTxtEntries($domain): array
|
||||
{
|
||||
$domain = rtrim($domain, '.');
|
||||
$nameServers = $this->getNameServers($domain);
|
||||
$this->logger->debug('Fetched TXT records for domain', ['nsDomain' => $domain, 'servers' => $nameServers]);
|
||||
$identicalEntries = [];
|
||||
foreach ($nameServers as $nameServer) {
|
||||
$ipNameServer = gethostbynamel($nameServer);
|
||||
if (empty($ipNameServer)) {
|
||||
throw new AcmeDnsResolutionException(sprintf('Unable to find domain %s on nameserver %s', $domain, $nameServer));
|
||||
}
|
||||
try {
|
||||
$response = $this->request($domain, ResourceTypes::TXT, $ipNameServer[0]);
|
||||
} catch (\Exception $e) {
|
||||
throw new AcmeDnsResolutionException(sprintf('Unable to find domain %s on nameserver %s', $domain, $nameServer));
|
||||
}
|
||||
$entries = [];
|
||||
foreach ($response->getAnswerRecords() as $record) {
|
||||
foreach ($record->getData() as $recordData) {
|
||||
$entries[] = (string) $recordData;
|
||||
}
|
||||
}
|
||||
|
||||
$identicalEntries[json_encode($entries)][] = $nameServer;
|
||||
}
|
||||
|
||||
$this->logger->info('DNS records fetched', ['mapping' => $identicalEntries]);
|
||||
if (1 !== \count($identicalEntries)) {
|
||||
throw new AcmeDnsResolutionException('Dns not fully propagated');
|
||||
}
|
||||
|
||||
return json_decode(key($identicalEntries));
|
||||
}
|
||||
|
||||
private function getNameServers($domain)
|
||||
{
|
||||
if ('' === $domain) {
|
||||
return [$this->nameServer];
|
||||
}
|
||||
|
||||
$parentNameServers = $this->getNameServers(implode('.', \array_slice(explode('.', $domain), 1)));
|
||||
$itemNameServers = [];
|
||||
$this->logger->debug('Fetched NS in charge of domain', ['nsDomain' => $domain, 'servers' => $parentNameServers]);
|
||||
foreach ($parentNameServers as $parentNameServer) {
|
||||
$ipNameServer = gethostbynamel($parentNameServer);
|
||||
if (empty($ipNameServer)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$response = $this->request(
|
||||
$domain,
|
||||
ResourceTypes::NS,
|
||||
$ipNameServer[0]
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
// ignore errors
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($response->getAnswerRecords() as $record) {
|
||||
try {
|
||||
$field = $record->getData()->getFieldByName('nsdname');
|
||||
$itemNameServers[] = $field->getValue();
|
||||
} catch (\OutOfBoundsException $e) {
|
||||
}
|
||||
}
|
||||
foreach ($response->getAuthorityRecords() as $record) {
|
||||
try {
|
||||
$field = $record->getData()->getFieldByName('nsdname');
|
||||
$itemNameServers[] = $field->getValue();
|
||||
} catch (\OutOfBoundsException $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
$itemNameServers = array_unique($itemNameServers);
|
||||
if (empty($itemNameServers)) {
|
||||
return $parentNameServers;
|
||||
}
|
||||
|
||||
return $itemNameServers;
|
||||
}
|
||||
|
||||
private function request($domain, $type, $nameServer)
|
||||
{
|
||||
$question = $this->questionFactory->create($type);
|
||||
$question->setName($domain);
|
||||
|
||||
// Create request message
|
||||
$request = $this->messageFactory->create(MessageTypes::QUERY);
|
||||
$request->getQuestionRecords()->add($question);
|
||||
$request->isRecursionDesired(true);
|
||||
|
||||
// Send request
|
||||
$socket = stream_socket_client(sprintf('udp://'.$nameServer.':53'));
|
||||
stream_socket_sendto($socket, $this->encoder->encode($request));
|
||||
|
||||
$r = [$socket];
|
||||
$w = $e = [];
|
||||
if (!stream_select($r, $w, $e, 3)) {
|
||||
throw new AcmeDnsResolutionException(sprintf('Timeout reached when requesting ServerName %s', $nameServer));
|
||||
}
|
||||
|
||||
// Decode response message
|
||||
try {
|
||||
$response = $this->decoder->decode(fread($socket, 1 << 20));
|
||||
} catch (\Exception $e) {
|
||||
throw new AcmeDnsResolutionException('Failed to decode server response', $e);
|
||||
}
|
||||
if (0 !== $response->getResponseCode()) {
|
||||
throw new AcmeDnsResolutionException(sprintf('ServerName respond with error code "%d"', $response->getResponseCode()));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
295
cacme/vendor/acmephp/core/Challenge/Dns/Route53Solver.php
vendored
Normal file
295
cacme/vendor/acmephp/core/Challenge/Dns/Route53Solver.php
vendored
Normal file
@ -0,0 +1,295 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Dns;
|
||||
|
||||
use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface;
|
||||
use AcmePhp\Core\Exception\Protocol\ChallengeFailedException;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
use Aws\Route53\Route53Client;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use Psr\Log\NullLogger;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/**
|
||||
* ACME DNS solver with automate configuration of a AWS route53.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class Route53Solver implements MultipleChallengesSolverInterface
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var DnsDataExtractor
|
||||
*/
|
||||
private $extractor;
|
||||
|
||||
/**
|
||||
* @var Route53Client
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $cacheZones;
|
||||
|
||||
public function __construct(DnsDataExtractor $extractor = null, Route53Client $client = null)
|
||||
{
|
||||
$this->extractor = $extractor ?: new DnsDataExtractor();
|
||||
$this->client = $client ?: new Route53Client([]);
|
||||
$this->logger = new NullLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge): bool
|
||||
{
|
||||
return 'dns-01' === $authorizationChallenge->getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function solve(AuthorizationChallenge $authorizationChallenge)
|
||||
{
|
||||
return $this->solveAll([$authorizationChallenge]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function solveAll(array $authorizationChallenges)
|
||||
{
|
||||
Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class);
|
||||
|
||||
$changesPerZone = [];
|
||||
$authorizationChallengesPerDomain = $this->groupAuthorizationChallengesPerDomain($authorizationChallenges);
|
||||
foreach ($authorizationChallengesPerDomain as $domain => $authorizationChallengesForDomain) {
|
||||
$zone = $this->getZone($authorizationChallengesForDomain[0]->getDomain());
|
||||
|
||||
$authorizationChallengesPerRecordName = $this->groupAuthorizationChallengesPerRecordName($authorizationChallengesForDomain);
|
||||
foreach ($authorizationChallengesPerRecordName as $recordName => $authorizationChallengesForRecordName) {
|
||||
$challengeValues = array_unique(array_map([$this->extractor, 'getRecordValue'], $authorizationChallengesForRecordName));
|
||||
$recordIndex = $this->getPreviousRecordIndex($zone['Id'], $recordName);
|
||||
|
||||
if (0 === \count(array_diff($challengeValues, array_keys($recordIndex)))) {
|
||||
$this->logger->debug('Record already defined', ['recordName' => $recordName]);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($challengeValues as $recordValue) {
|
||||
$recordIndex[$recordValue] = time();
|
||||
}
|
||||
|
||||
$changesPerZone[$zone['Id']][] = $this->getSaveRecordQuery($recordName, $recordIndex);
|
||||
}
|
||||
}
|
||||
|
||||
$records = [];
|
||||
foreach ($changesPerZone as $zoneId => $changes) {
|
||||
$this->logger->info('Updating route 53 DNS', ['zone' => $zoneId]);
|
||||
$records[$zoneId] = $this->client->changeResourceRecordSets(
|
||||
[
|
||||
'ChangeBatch' => [
|
||||
'Changes' => $changes,
|
||||
],
|
||||
'HostedZoneId' => $zoneId,
|
||||
]
|
||||
);
|
||||
}
|
||||
foreach ($records as $zoneId => $record) {
|
||||
$this->logger->info('Waiting for Route 53 changes', ['zone' => $zoneId]);
|
||||
$this->client->waitUntil('ResourceRecordSetsChanged', ['Id' => $record['ChangeInfo']['Id']]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanup(AuthorizationChallenge $authorizationChallenge)
|
||||
{
|
||||
return $this->cleanupAll([$authorizationChallenge]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanupAll(array $authorizationChallenges)
|
||||
{
|
||||
Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class);
|
||||
|
||||
$changesPerZone = [];
|
||||
$authorizationChallengesPerDomain = $this->groupAuthorizationChallengesPerDomain($authorizationChallenges);
|
||||
foreach ($authorizationChallengesPerDomain as $domain => $authorizationChallengesForDomain) {
|
||||
$zone = $this->getZone($authorizationChallengesForDomain[0]->getDomain());
|
||||
|
||||
$authorizationChallengesPerRecordName = $this->groupAuthorizationChallengesPerRecordName($authorizationChallengesForDomain);
|
||||
foreach ($authorizationChallengesPerRecordName as $recordName => $authorizationChallengesForRecordName) {
|
||||
$challengeValues = array_unique(array_map([$this->extractor, 'getRecordValue'], $authorizationChallengesForRecordName));
|
||||
$recordIndex = $this->getPreviousRecordIndex($zone['Id'], $recordName);
|
||||
|
||||
foreach ($challengeValues as $recordValue) {
|
||||
unset($recordIndex[$recordValue]);
|
||||
}
|
||||
$changesPerZone[$zone['Id']][] = $this->getSaveRecordQuery($recordName, $recordIndex);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($changesPerZone as $zoneId => $changes) {
|
||||
$this->logger->info('Updating route 53 DNS', ['zone' => $zoneId]);
|
||||
$this->client->changeResourceRecordSets(
|
||||
[
|
||||
'ChangeBatch' => [
|
||||
'Changes' => $changes,
|
||||
],
|
||||
'HostedZoneId' => $zoneId,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function getPreviousRecordIndex($zoneId, $recordName)
|
||||
{
|
||||
$previousRecordSets = $this->client->listResourceRecordSets([
|
||||
'HostedZoneId' => $zoneId,
|
||||
'StartRecordName' => $recordName,
|
||||
'StartRecordType' => 'TXT',
|
||||
]);
|
||||
$recordSets = array_filter(
|
||||
$previousRecordSets['ResourceRecordSets'],
|
||||
function ($recordSet) use ($recordName) {
|
||||
return $recordSet['Name'] === $recordName && 'TXT' === $recordSet['Type'];
|
||||
}
|
||||
);
|
||||
$recordIndex = [];
|
||||
foreach ($recordSets as $previousRecordSet) {
|
||||
$previousTxt = array_map(function ($resourceRecord) {
|
||||
return stripslashes(trim($resourceRecord['Value'], '"'));
|
||||
}, $previousRecordSet['ResourceRecords']);
|
||||
// Search the special Index
|
||||
foreach ($previousTxt as $index => $recordValue) {
|
||||
if (null !== $previousIndex = json_decode($recordValue, true)) {
|
||||
$recordIndex = $previousIndex;
|
||||
unset($previousTxt[$index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Set default value
|
||||
foreach ($previousTxt as $recordValue) {
|
||||
if (!isset($recordIndex[$recordValue])) {
|
||||
$recordIndex[$recordValue] = time();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $recordIndex;
|
||||
}
|
||||
|
||||
private function getSaveRecordQuery($recordName, array $recordIndex)
|
||||
{
|
||||
//remove old indexes
|
||||
$limitTime = time() - 86400;
|
||||
foreach ($recordIndex as $recordValue => $time) {
|
||||
if ($time < $limitTime) {
|
||||
unset($recordIndex[$recordValue]);
|
||||
}
|
||||
}
|
||||
|
||||
$recordValues = array_keys($recordIndex);
|
||||
$recordValues[] = json_encode($recordIndex);
|
||||
|
||||
return [
|
||||
'Action' => 'UPSERT',
|
||||
'ResourceRecordSet' => [
|
||||
'Name' => $recordName,
|
||||
'ResourceRecords' => array_map(function ($recordValue) {
|
||||
return [
|
||||
'Value' => sprintf('"%s"', addslashes($recordValue)),
|
||||
];
|
||||
}, $recordValues),
|
||||
'TTL' => 5,
|
||||
'Type' => 'TXT',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AuthorizationChallenge[] $authorizationChallenges
|
||||
*
|
||||
* @return AuthorizationChallenge[][]
|
||||
*/
|
||||
private function groupAuthorizationChallengesPerDomain(array $authorizationChallenges)
|
||||
{
|
||||
$groups = [];
|
||||
foreach ($authorizationChallenges as $authorizationChallenge) {
|
||||
$groups[$authorizationChallenge->getDomain()][] = $authorizationChallenge;
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AuthorizationChallenge[] $authorizationChallenges
|
||||
*
|
||||
* @return AuthorizationChallenge[][]
|
||||
*/
|
||||
private function groupAuthorizationChallengesPerRecordName(array $authorizationChallenges)
|
||||
{
|
||||
$groups = [];
|
||||
foreach ($authorizationChallenges as $authorizationChallenge) {
|
||||
$groups[$this->extractor->getRecordName($authorizationChallenge)][] = $authorizationChallenge;
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
private function getZone($domain)
|
||||
{
|
||||
$domainParts = explode('.', $domain);
|
||||
$domains = array_reverse(array_map(
|
||||
function ($index) use ($domainParts) {
|
||||
return implode('.', \array_slice($domainParts, \count($domainParts) - $index));
|
||||
},
|
||||
range(0, \count($domainParts))
|
||||
));
|
||||
|
||||
$zones = $this->getZones();
|
||||
foreach ($domains as $cursorDomain) {
|
||||
if (isset($zones[$cursorDomain.'.'])) {
|
||||
return $zones[$cursorDomain.'.'];
|
||||
}
|
||||
}
|
||||
|
||||
throw new ChallengeFailedException(sprintf('Unable to find a zone for the domain "%s"', $domain));
|
||||
}
|
||||
|
||||
private function getZones()
|
||||
{
|
||||
if (null !== $this->cacheZones) {
|
||||
return $this->cacheZones;
|
||||
}
|
||||
|
||||
$zones = [];
|
||||
$args = [];
|
||||
do {
|
||||
$resp = $this->client->listHostedZones($args);
|
||||
$zones = array_merge($zones, $resp['HostedZones']);
|
||||
$args = ['Marker' => $resp['NextMarker']];
|
||||
} while ($resp['IsTruncated']);
|
||||
|
||||
$this->cacheZones = array_column($zones, null, 'Name');
|
||||
|
||||
return $this->cacheZones;
|
||||
}
|
||||
}
|
43
cacme/vendor/acmephp/core/Challenge/Dns/SimpleDnsResolver.php
vendored
Normal file
43
cacme/vendor/acmephp/core/Challenge/Dns/SimpleDnsResolver.php
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Dns;
|
||||
|
||||
/**
|
||||
* Resolves DNS through dns_get_record.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class SimpleDnsResolver implements DnsResolverInterface
|
||||
{
|
||||
/**
|
||||
* @{@inheritdoc}
|
||||
*/
|
||||
public static function isSupported(): bool
|
||||
{
|
||||
return \function_exists('dns_get_record');
|
||||
}
|
||||
|
||||
/**
|
||||
* @{@inheritdoc}
|
||||
*/
|
||||
public function getTxtEntries($domain): array
|
||||
{
|
||||
$entries = [];
|
||||
foreach (dns_get_record($domain, DNS_TXT) as $record) {
|
||||
$entries = array_merge($entries, $record['entries']);
|
||||
}
|
||||
|
||||
sort($entries);
|
||||
|
||||
return array_unique($entries);
|
||||
}
|
||||
}
|
100
cacme/vendor/acmephp/core/Challenge/Dns/SimpleDnsSolver.php
vendored
Normal file
100
cacme/vendor/acmephp/core/Challenge/Dns/SimpleDnsSolver.php
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Dns;
|
||||
|
||||
use AcmePhp\Core\Challenge\SolverInterface;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* ACME DNS solver with manual intervention.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class SimpleDnsSolver implements SolverInterface
|
||||
{
|
||||
/**
|
||||
* @var DnsDataExtractor
|
||||
*/
|
||||
private $extractor;
|
||||
|
||||
/**
|
||||
* @var OutputInterface
|
||||
*/
|
||||
protected $output;
|
||||
|
||||
/**
|
||||
* @param DnsDataExtractor $extractor
|
||||
* @param OutputInterface $output
|
||||
*/
|
||||
public function __construct(DnsDataExtractor $extractor = null, OutputInterface $output = null)
|
||||
{
|
||||
$this->extractor = $extractor ?: new DnsDataExtractor();
|
||||
$this->output = $output ?: new NullOutput();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge): bool
|
||||
{
|
||||
return 'dns-01' === $authorizationChallenge->getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function solve(AuthorizationChallenge $authorizationChallenge)
|
||||
{
|
||||
$recordName = $this->extractor->getRecordName($authorizationChallenge);
|
||||
$recordValue = $this->extractor->getRecordValue($authorizationChallenge);
|
||||
|
||||
$this->output->writeln(
|
||||
sprintf(
|
||||
<<<'EOF'
|
||||
Add the following TXT record to your DNS zone
|
||||
Domain: %s
|
||||
TXT value: %s
|
||||
|
||||
<comment>Wait for the propagation before moving to the next step</comment>
|
||||
Tips: Use the following command to check the propagation
|
||||
|
||||
host -t TXT %s
|
||||
|
||||
EOF
|
||||
,
|
||||
$recordName,
|
||||
$recordValue,
|
||||
$recordName
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanup(AuthorizationChallenge $authorizationChallenge)
|
||||
{
|
||||
$recordName = $this->extractor->getRecordName($authorizationChallenge);
|
||||
|
||||
$this->output->writeln(
|
||||
sprintf(
|
||||
<<<'EOF'
|
||||
You can now cleanup your DNS by removing the domain <comment>_acme-challenge.%s.</comment>
|
||||
EOF
|
||||
,
|
||||
$recordName
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
90
cacme/vendor/acmephp/core/Challenge/Http/FilesystemSolver.php
vendored
Normal file
90
cacme/vendor/acmephp/core/Challenge/Http/FilesystemSolver.php
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Http;
|
||||
|
||||
use AcmePhp\Core\Challenge\ConfigurableServiceInterface;
|
||||
use AcmePhp\Core\Challenge\SolverInterface;
|
||||
use AcmePhp\Core\Filesystem\Adapter\NullAdapter;
|
||||
use AcmePhp\Core\Filesystem\FilesystemFactoryInterface;
|
||||
use AcmePhp\Core\Filesystem\FilesystemInterface;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/**
|
||||
* ACME HTTP solver through ftp upload.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class FilesystemSolver implements SolverInterface, ConfigurableServiceInterface
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
private $filesystemFactoryLocator;
|
||||
|
||||
/**
|
||||
* @var FilesystemInterface
|
||||
*/
|
||||
private $filesystem;
|
||||
|
||||
/**
|
||||
* @var HttpDataExtractor
|
||||
*/
|
||||
private $extractor;
|
||||
|
||||
public function __construct(ContainerInterface $filesystemFactoryLocator = null, HttpDataExtractor $extractor = null)
|
||||
{
|
||||
$this->filesystemFactoryLocator = $filesystemFactoryLocator ?: new ServiceLocator([]);
|
||||
$this->extractor = $extractor ?: new HttpDataExtractor();
|
||||
$this->filesystem = new NullAdapter();
|
||||
}
|
||||
|
||||
public function configure(array $config)
|
||||
{
|
||||
Assert::keyExists($config, 'adapter', 'configure::$config expected an array with the key %s.');
|
||||
|
||||
/** @var FilesystemFactoryInterface $factory */
|
||||
$factory = $this->filesystemFactoryLocator->get($config['adapter']);
|
||||
$this->filesystem = $factory->create($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge): bool
|
||||
{
|
||||
return 'http-01' === $authorizationChallenge->getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function solve(AuthorizationChallenge $authorizationChallenge)
|
||||
{
|
||||
$checkPath = $this->extractor->getCheckPath($authorizationChallenge);
|
||||
$checkContent = $this->extractor->getCheckContent($authorizationChallenge);
|
||||
|
||||
$this->filesystem->write($checkPath, $checkContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanup(AuthorizationChallenge $authorizationChallenge)
|
||||
{
|
||||
$checkPath = $this->extractor->getCheckPath($authorizationChallenge);
|
||||
|
||||
$this->filesystem->delete($checkPath);
|
||||
}
|
||||
}
|
53
cacme/vendor/acmephp/core/Challenge/Http/HttpDataExtractor.php
vendored
Normal file
53
cacme/vendor/acmephp/core/Challenge/Http/HttpDataExtractor.php
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Http;
|
||||
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
|
||||
/**
|
||||
* Extract data needed to solve HTTP challenges.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class HttpDataExtractor
|
||||
{
|
||||
/**
|
||||
* Retrieves the absolute URL called by the CA.
|
||||
*/
|
||||
public function getCheckUrl(AuthorizationChallenge $authorizationChallenge): string
|
||||
{
|
||||
return sprintf(
|
||||
'http://%s%s',
|
||||
$authorizationChallenge->getDomain(),
|
||||
$this->getCheckPath($authorizationChallenge)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the absolute path called by the CA.
|
||||
*/
|
||||
public function getCheckPath(AuthorizationChallenge $authorizationChallenge): string
|
||||
{
|
||||
return sprintf(
|
||||
'/.well-known/acme-challenge/%s',
|
||||
$authorizationChallenge->getToken()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the content that should be returned in the response.
|
||||
*/
|
||||
public function getCheckContent(AuthorizationChallenge $authorizationChallenge): string
|
||||
{
|
||||
return $authorizationChallenge->getPayload();
|
||||
}
|
||||
}
|
65
cacme/vendor/acmephp/core/Challenge/Http/HttpValidator.php
vendored
Normal file
65
cacme/vendor/acmephp/core/Challenge/Http/HttpValidator.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Http;
|
||||
|
||||
use AcmePhp\Core\Challenge\SolverInterface;
|
||||
use AcmePhp\Core\Challenge\ValidatorInterface;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
|
||||
/**
|
||||
* Validator for HTTP challenges.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class HttpValidator implements ValidatorInterface
|
||||
{
|
||||
/**
|
||||
* @var HttpDataExtractor
|
||||
*/
|
||||
private $extractor;
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
private $client;
|
||||
|
||||
public function __construct(HttpDataExtractor $extractor = null, Client $client = null)
|
||||
{
|
||||
$this->extractor = $extractor ?: new HttpDataExtractor();
|
||||
$this->client = $client ?: new Client();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool
|
||||
{
|
||||
return 'http-01' === $authorizationChallenge->getType() && !$solver instanceof MockServerHttpSolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool
|
||||
{
|
||||
$checkUrl = $this->extractor->getCheckUrl($authorizationChallenge);
|
||||
$checkContent = $this->extractor->getCheckContent($authorizationChallenge);
|
||||
|
||||
try {
|
||||
return $checkContent === trim($this->client->get($checkUrl, ['verify' => false])->getBody()->getContents());
|
||||
} catch (ClientException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
40
cacme/vendor/acmephp/core/Challenge/Http/MockHttpValidator.php
vendored
Normal file
40
cacme/vendor/acmephp/core/Challenge/Http/MockHttpValidator.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Http;
|
||||
|
||||
use AcmePhp\Core\Challenge\SolverInterface;
|
||||
use AcmePhp\Core\Challenge\ValidatorInterface;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
|
||||
/**
|
||||
* Validator for pebble-challtestsrv.
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class MockHttpValidator implements ValidatorInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool
|
||||
{
|
||||
return 'http-01' === $authorizationChallenge->getType() && $solver instanceof MockServerHttpSolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
58
cacme/vendor/acmephp/core/Challenge/Http/MockServerHttpSolver.php
vendored
Normal file
58
cacme/vendor/acmephp/core/Challenge/Http/MockServerHttpSolver.php
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Http;
|
||||
|
||||
use AcmePhp\Core\Challenge\SolverInterface;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
/**
|
||||
* ACME HTTP solver talking to pebble-challtestsrv.
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class MockServerHttpSolver implements SolverInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge): bool
|
||||
{
|
||||
return 'http-01' === $authorizationChallenge->getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function solve(AuthorizationChallenge $authorizationChallenge)
|
||||
{
|
||||
(new Client())->post('http://localhost:8055/add-http01', [
|
||||
RequestOptions::JSON => [
|
||||
'token' => $authorizationChallenge->getToken(),
|
||||
'content' => $authorizationChallenge->getPayload(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanup(AuthorizationChallenge $authorizationChallenge)
|
||||
{
|
||||
(new Client())->post('http://localhost:8055/del-http01', [
|
||||
RequestOptions::JSON => [
|
||||
'token' => $authorizationChallenge->getToken(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
96
cacme/vendor/acmephp/core/Challenge/Http/SimpleHttpSolver.php
vendored
Normal file
96
cacme/vendor/acmephp/core/Challenge/Http/SimpleHttpSolver.php
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge\Http;
|
||||
|
||||
use AcmePhp\Core\Challenge\SolverInterface;
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* ACME HTTP solver with manual intervention.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class SimpleHttpSolver implements SolverInterface
|
||||
{
|
||||
/**
|
||||
* @var HttpDataExtractor
|
||||
*/
|
||||
private $extractor;
|
||||
|
||||
/**
|
||||
* @var OutputInterface
|
||||
*/
|
||||
private $output;
|
||||
|
||||
public function __construct(HttpDataExtractor $extractor = null, OutputInterface $output = null)
|
||||
{
|
||||
$this->extractor = $extractor ?: new HttpDataExtractor();
|
||||
$this->output = $output ?: new NullOutput();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge): bool
|
||||
{
|
||||
return 'http-01' === $authorizationChallenge->getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function solve(AuthorizationChallenge $authorizationChallenge)
|
||||
{
|
||||
$checkUrl = $this->extractor->getCheckUrl($authorizationChallenge);
|
||||
$checkContent = $this->extractor->getCheckContent($authorizationChallenge);
|
||||
|
||||
$this->output->writeln(
|
||||
sprintf(
|
||||
<<<'EOF'
|
||||
Create a text file accessible on URL %s
|
||||
containing the following content:
|
||||
|
||||
%s
|
||||
|
||||
Check in your browser that the URL %s returns
|
||||
the authorization token above.
|
||||
|
||||
EOF
|
||||
,
|
||||
$checkUrl,
|
||||
$checkContent,
|
||||
$checkContent
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanup(AuthorizationChallenge $authorizationChallenge)
|
||||
{
|
||||
$checkUrl = $this->extractor->getCheckUrl($authorizationChallenge);
|
||||
|
||||
$this->output->writeln(
|
||||
sprintf(
|
||||
<<<'EOF'
|
||||
You can now safely remove the challenge's file at %s
|
||||
|
||||
EOF
|
||||
,
|
||||
$checkUrl
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
36
cacme/vendor/acmephp/core/Challenge/MultipleChallengesSolverInterface.php
vendored
Normal file
36
cacme/vendor/acmephp/core/Challenge/MultipleChallengesSolverInterface.php
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge;
|
||||
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
|
||||
/**
|
||||
* ACME challenge solver able to solve several challenges at once.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
interface MultipleChallengesSolverInterface extends SolverInterface
|
||||
{
|
||||
/**
|
||||
* Solve the given list of authorization challenge.
|
||||
*
|
||||
* @param AuthorizationChallenge[] $authorizationChallenges
|
||||
*/
|
||||
public function solveAll(array $authorizationChallenges);
|
||||
|
||||
/**
|
||||
* Cleanup the environments after all challenges.
|
||||
*
|
||||
* @param AuthorizationChallenge[] $authorizationChallenges
|
||||
*/
|
||||
public function cleanupAll(array $authorizationChallenges);
|
||||
}
|
39
cacme/vendor/acmephp/core/Challenge/SolverInterface.php
vendored
Normal file
39
cacme/vendor/acmephp/core/Challenge/SolverInterface.php
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge;
|
||||
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
|
||||
/**
|
||||
* ACME challenge solver.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
interface SolverInterface
|
||||
{
|
||||
/**
|
||||
* Determines whether or not the solver supports a given Challenge.
|
||||
*
|
||||
* @return bool The solver supports the given challenge's type
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge): bool;
|
||||
|
||||
/**
|
||||
* Solve the given authorization challenge.
|
||||
*/
|
||||
public function solve(AuthorizationChallenge $authorizationChallenge);
|
||||
|
||||
/**
|
||||
* Cleanup the environments after a successful challenge.
|
||||
*/
|
||||
public function cleanup(AuthorizationChallenge $authorizationChallenge);
|
||||
}
|
36
cacme/vendor/acmephp/core/Challenge/ValidatorInterface.php
vendored
Normal file
36
cacme/vendor/acmephp/core/Challenge/ValidatorInterface.php
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge;
|
||||
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
|
||||
/**
|
||||
* ACME challenge pre validator.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
interface ValidatorInterface
|
||||
{
|
||||
/**
|
||||
* Determines whether or not the validator supports a given Challenge.
|
||||
*
|
||||
* @return bool The validator supports the given challenge's type
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool;
|
||||
|
||||
/**
|
||||
* Internally validate the challenge by performing the same kind of test than the CA.
|
||||
*
|
||||
* @return bool The challenge is valid
|
||||
*/
|
||||
public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool;
|
||||
}
|
60
cacme/vendor/acmephp/core/Challenge/WaitingValidator.php
vendored
Normal file
60
cacme/vendor/acmephp/core/Challenge/WaitingValidator.php
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Challenge;
|
||||
|
||||
use AcmePhp\Core\Protocol\AuthorizationChallenge;
|
||||
|
||||
/**
|
||||
* ACME Challenge validator who implement a retry strategy till the decorated validator successfully validate the
|
||||
* challenge or the timeout is reached.
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class WaitingValidator implements ValidatorInterface
|
||||
{
|
||||
/** @var ValidatorInterface */
|
||||
private $validator;
|
||||
|
||||
/** @var int */
|
||||
private $timeout;
|
||||
|
||||
public function __construct(ValidatorInterface $validator, int $timeout = 180)
|
||||
{
|
||||
$this->validator = $validator;
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool
|
||||
{
|
||||
return $this->validator->supports($authorizationChallenge, $solver);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool
|
||||
{
|
||||
$limitEndTime = time() + $this->timeout;
|
||||
|
||||
do {
|
||||
if ($this->validator->isValid($authorizationChallenge, $solver)) {
|
||||
return true;
|
||||
}
|
||||
sleep(3);
|
||||
} while ($limitEndTime > time());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
25
cacme/vendor/acmephp/core/Exception/AcmeCoreClientException.php
vendored
Normal file
25
cacme/vendor/acmephp/core/Exception/AcmeCoreClientException.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception;
|
||||
|
||||
/**
|
||||
* Error reported by the client.
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class AcmeCoreClientException extends AcmeCoreException
|
||||
{
|
||||
public function __construct($message, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, 0, $previous);
|
||||
}
|
||||
}
|
19
cacme/vendor/acmephp/core/Exception/AcmeCoreException.php
vendored
Normal file
19
cacme/vendor/acmephp/core/Exception/AcmeCoreException.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class AcmeCoreException extends \RuntimeException
|
||||
{
|
||||
}
|
27
cacme/vendor/acmephp/core/Exception/AcmeCoreServerException.php
vendored
Normal file
27
cacme/vendor/acmephp/core/Exception/AcmeCoreServerException.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Error reported by the server.
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class AcmeCoreServerException extends AcmeCoreException
|
||||
{
|
||||
public function __construct(RequestInterface $request, $message, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $previous ? $previous->getCode() : 0, $previous);
|
||||
}
|
||||
}
|
23
cacme/vendor/acmephp/core/Exception/AcmeDnsResolutionException.php
vendored
Normal file
23
cacme/vendor/acmephp/core/Exception/AcmeDnsResolutionException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class AcmeDnsResolutionException extends AcmeCoreException
|
||||
{
|
||||
public function __construct($message, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(null === $message ? 'An exception was thrown during resolution of DNS' : $message, 0, $previous);
|
||||
}
|
||||
}
|
23
cacme/vendor/acmephp/core/Exception/Protocol/CertificateRequestFailedException.php
vendored
Normal file
23
cacme/vendor/acmephp/core/Exception/Protocol/CertificateRequestFailedException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Protocol;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class CertificateRequestFailedException extends ProtocolException
|
||||
{
|
||||
public function __construct(string $response)
|
||||
{
|
||||
parent::__construct(sprintf('Certificate request failed (response: %s)', $response));
|
||||
}
|
||||
}
|
23
cacme/vendor/acmephp/core/Exception/Protocol/CertificateRequestTimedOutException.php
vendored
Normal file
23
cacme/vendor/acmephp/core/Exception/Protocol/CertificateRequestTimedOutException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Protocol;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class CertificateRequestTimedOutException extends ProtocolException
|
||||
{
|
||||
public function __construct(string $response)
|
||||
{
|
||||
parent::__construct(sprintf('Certificate request timed out (response: %s)', $response));
|
||||
}
|
||||
}
|
18
cacme/vendor/acmephp/core/Exception/Protocol/CertificateRevocationException.php
vendored
Normal file
18
cacme/vendor/acmephp/core/Exception/Protocol/CertificateRevocationException.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Protocol;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreClientException;
|
||||
|
||||
class CertificateRevocationException extends AcmeCoreClientException
|
||||
{
|
||||
}
|
38
cacme/vendor/acmephp/core/Exception/Protocol/ChallengeFailedException.php
vendored
Normal file
38
cacme/vendor/acmephp/core/Exception/Protocol/ChallengeFailedException.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Protocol;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class ChallengeFailedException extends ProtocolException
|
||||
{
|
||||
private $response;
|
||||
|
||||
public function __construct($response, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
sprintf('Challenge failed (response: %s).', json_encode($response)),
|
||||
$previous
|
||||
);
|
||||
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
}
|
23
cacme/vendor/acmephp/core/Exception/Protocol/ChallengeNotSupportedException.php
vendored
Normal file
23
cacme/vendor/acmephp/core/Exception/Protocol/ChallengeNotSupportedException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Protocol;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class ChallengeNotSupportedException extends ProtocolException
|
||||
{
|
||||
public function __construct(\Exception $previous = null)
|
||||
{
|
||||
parent::__construct('This ACME server does not expose supported challenge.', $previous);
|
||||
}
|
||||
}
|
38
cacme/vendor/acmephp/core/Exception/Protocol/ChallengeTimedOutException.php
vendored
Normal file
38
cacme/vendor/acmephp/core/Exception/Protocol/ChallengeTimedOutException.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Protocol;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class ChallengeTimedOutException extends ProtocolException
|
||||
{
|
||||
private $response;
|
||||
|
||||
public function __construct($response, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
sprintf('Challenge timed out (response: %s).', json_encode($response)),
|
||||
$previous
|
||||
);
|
||||
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
}
|
19
cacme/vendor/acmephp/core/Exception/Protocol/ExpectedJsonException.php
vendored
Normal file
19
cacme/vendor/acmephp/core/Exception/Protocol/ExpectedJsonException.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Protocol;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class ExpectedJsonException extends ProtocolException
|
||||
{
|
||||
}
|
23
cacme/vendor/acmephp/core/Exception/Protocol/ProtocolException.php
vendored
Normal file
23
cacme/vendor/acmephp/core/Exception/Protocol/ProtocolException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Protocol;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreClientException;
|
||||
|
||||
/**
|
||||
* Error because the protocol was not respected.
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class ProtocolException extends AcmeCoreClientException
|
||||
{
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/BadCsrServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/BadCsrServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class BadCsrServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[badCSR] The CSR is unacceptable (e.g., due to a short key): '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/BadNonceServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/BadNonceServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class BadNonceServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[badNonce] The client sent an unacceptable anti-replay nonce: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/CaaServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/CaaServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Alex Plekhanov <alex@plekhanov.dev>
|
||||
*/
|
||||
class CaaServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[caa] Certification Authority Authorization (CAA) records forbid the CA from issuing a certificate: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/ConnectionServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/ConnectionServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class ConnectionServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[connection] The server could not connect to the client for DV: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/DnsServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/DnsServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Alex Plekhanov <alex@plekhanov.dev>
|
||||
*/
|
||||
class DnsServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[dns] There was a problem with a DNS query during identifier validation: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/IncorrectResponseServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/IncorrectResponseServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Alex Plekhanov <alex@plekhanov.dev>
|
||||
*/
|
||||
class IncorrectResponseServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
"[incorrectResponse] Response received didn’t match the challenge's requirements: ".$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/InternalServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/InternalServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class InternalServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[serverInternal] The server experienced an internal error: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/InvalidContactServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/InvalidContactServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Alex Plekhanov <alex@plekhanov.dev>
|
||||
*/
|
||||
class InvalidContactServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[invalidContact] A contact URL for an account was invalid: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/InvalidEmailServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/InvalidEmailServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class InvalidEmailServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[invalidEmail] This email is unacceptable (e.g., it is invalid): '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/MalformedServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/MalformedServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class MalformedServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[malformed] The request message was malformed: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
27
cacme/vendor/acmephp/core/Exception/Server/OrderNotReadyServerException.php
vendored
Normal file
27
cacme/vendor/acmephp/core/Exception/Server/OrderNotReadyServerException.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
class OrderNotReadyServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[orderNotReady] Order could not be finalized: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/RateLimitedServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/RateLimitedServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class RateLimitedServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[rateLimited] This client reached the rate limit of the server: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/RejectedIdentifierServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/RejectedIdentifierServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Alex Plekhanov <alex@plekhanov.dev>
|
||||
*/
|
||||
class RejectedIdentifierServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[rejectedIdentifier] The server will not issue certificates for the identifier: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/TlsServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/TlsServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class TlsServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[tls] The server experienced a TLS error during DV: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/UnauthorizedServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/UnauthorizedServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class UnauthorizedServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[unauthorized] The client lacks sufficient authorization: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/UnknownHostServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/UnknownHostServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class UnknownHostServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[unknownHost] The server could not resolve a domain name: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/UnsupportedContactServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/UnsupportedContactServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Alex Plekhanov <alex@plekhanov.dev>
|
||||
*/
|
||||
class UnsupportedContactServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[unsupportedContact] A contact URL for an account used an unsupported protocol scheme: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
30
cacme/vendor/acmephp/core/Exception/Server/UnsupportedIdentifierServerException.php
vendored
Normal file
30
cacme/vendor/acmephp/core/Exception/Server/UnsupportedIdentifierServerException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Acme PHP project.
|
||||
*
|
||||
* (c) Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AcmePhp\Core\Exception\Server;
|
||||
|
||||
use AcmePhp\Core\Exception\AcmeCoreServerException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @author Alex Plekhanov <alex@plekhanov.dev>
|
||||
*/
|
||||
class UnsupportedIdentifierServerException extends AcmeCoreServerException
|
||||
{
|
||||
public function __construct(RequestInterface $request, string $detail, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
$request,
|
||||
'[unsupportedIdentifier] An identifier is of an unsupported type: '.$detail,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user