Improvements to SSL classes
This commit is contained in:
parent
6588de4f7f
commit
863bc1150a
@ -18,25 +18,6 @@ abstract class ORM extends Kohana_ORM {
|
|||||||
// Our filters used to display values in a friendly format
|
// Our filters used to display values in a friendly format
|
||||||
protected $_display_filters = array();
|
protected $_display_filters = array();
|
||||||
|
|
||||||
// Override check() so that it doesnt throw an exception.
|
|
||||||
// @todo Need to figure out how to show the items that fail validation
|
|
||||||
final public function check(Validation $extra_validation = NULL) {
|
|
||||||
// Determine if any external validation failed
|
|
||||||
$extra_errors = ($extra_validation AND ! $extra_validation->check());
|
|
||||||
|
|
||||||
// Always build a new validation object
|
|
||||||
$this->_validation();
|
|
||||||
|
|
||||||
$array = $this->_validation;
|
|
||||||
|
|
||||||
if (($this->_valid = $array->check()) === FALSE OR $extra_errors)
|
|
||||||
{
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add our OSB site_id to each SELECT query
|
// Add our OSB site_id to each SELECT query
|
||||||
final protected function _build($type) {
|
final protected function _build($type) {
|
||||||
// Exclude tables without site ID's
|
// Exclude tables without site ID's
|
||||||
|
@ -18,8 +18,11 @@ abstract class GoogleChart implements Iterator,Countable {
|
|||||||
protected $_max = array();
|
protected $_max = array();
|
||||||
// Chart title
|
// Chart title
|
||||||
protected $_title = '';
|
protected $_title = '';
|
||||||
|
protected $_dataurl = '';
|
||||||
|
protected $_divname = '';
|
||||||
// Default chart size.
|
// Default chart size.
|
||||||
protected $_size = '700x200';
|
protected $_height = '200';
|
||||||
|
protected $_width = '700';
|
||||||
|
|
||||||
// Colors to use for series
|
// Colors to use for series
|
||||||
private $series_colors = array('AAACCC','E0E0E0','CCC888','EEEBBB','666CCC','888888');
|
private $series_colors = array('AAACCC','E0E0E0','CCC888','EEEBBB','666CCC','888888');
|
||||||
@ -46,8 +49,16 @@ abstract class GoogleChart implements Iterator,Countable {
|
|||||||
|
|
||||||
public function __call($name,$args) {
|
public function __call($name,$args) {
|
||||||
switch ($name) {
|
switch ($name) {
|
||||||
|
case 'dataurl': $this->_dataurl = array_shift($args);
|
||||||
|
break;
|
||||||
|
case 'div': $this->_divname = array_shift($args);
|
||||||
|
break;
|
||||||
|
case 'height': $this->_height = array_shift($args);
|
||||||
|
break;
|
||||||
case 'title': $this->_title = array_shift($args);
|
case 'title': $this->_title = array_shift($args);
|
||||||
break;
|
break;
|
||||||
|
case 'width': $this->_width = array_shift($args);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Kohana_Exception('Unknown method :name',array(':name'=>$name));
|
throw new Kohana_Exception('Unknown method :name',array(':name'=>$name));
|
||||||
}
|
}
|
||||||
@ -79,6 +90,9 @@ abstract class GoogleChart implements Iterator,Countable {
|
|||||||
return new $c();
|
return new $c();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render the chart data in a json format
|
||||||
|
abstract public function json();
|
||||||
|
|
||||||
// Our child class should define how to render as a string
|
// Our child class should define how to render as a string
|
||||||
abstract public function render();
|
abstract public function render();
|
||||||
|
|
||||||
@ -90,7 +104,7 @@ abstract class GoogleChart implements Iterator,Countable {
|
|||||||
* Example:
|
* Example:
|
||||||
* $this->data('yl'=>'Base Down Peak',array('11-12'=>1,'11-11'=>2));
|
* $this->data('yl'=>'Base Down Peak',array('11-12'=>1,'11-11'=>2));
|
||||||
*/
|
*/
|
||||||
public function data(array $axis,array $data) {
|
public function sdata(array $axis,array $data) {
|
||||||
// Some sanity checking
|
// Some sanity checking
|
||||||
if (count($axis) != 1)
|
if (count($axis) != 1)
|
||||||
throw new Kohana_Exception('We can only take 1 series at time.');
|
throw new Kohana_Exception('We can only take 1 series at time.');
|
||||||
@ -115,6 +129,34 @@ abstract class GoogleChart implements Iterator,Countable {
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record on plot event
|
||||||
|
* @param $data Should contain an "X" with a "YL" and/or "YR"
|
||||||
|
*/
|
||||||
|
public function pdata($x,array $data) {
|
||||||
|
if (! is_string($x))
|
||||||
|
throw new Kohana_Exception('X should be a string');
|
||||||
|
|
||||||
|
foreach ($data as $key => $values) {
|
||||||
|
switch ($key) {
|
||||||
|
case 'yr':
|
||||||
|
case 'yl':
|
||||||
|
foreach ($values as $k=>$v) {
|
||||||
|
if (! in_array($k,$this->_axis))
|
||||||
|
$this->_axis[$k] = $key;
|
||||||
|
|
||||||
|
$this->_data[$k][$x] = $v;
|
||||||
|
$this->_plotdata[$x][$k] = $v;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Kohana_Exception('Unknown key :key',array(':key'=>$key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the colors that will be used for this series
|
* Return the colors that will be used for this series
|
||||||
*/
|
*/
|
||||||
|
156
modules/gchart/classes/GoogleChart/ComboChart.php
Normal file
156
modules/gchart/classes/GoogleChart/ComboChart.php
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides access to Google's Chart API
|
||||||
|
*
|
||||||
|
* @package lnApp
|
||||||
|
* @subpackage GoogleChart
|
||||||
|
* @category Helper
|
||||||
|
* @author Deon George
|
||||||
|
* @copyright (c) 2010 Deon George
|
||||||
|
* @license http://dev.leenooks.net/license.html
|
||||||
|
*/
|
||||||
|
class GoogleChart_ComboChart extends GoogleChart {
|
||||||
|
// Should the Y column range be a log() function
|
||||||
|
protected $_logy = FALSE;
|
||||||
|
// Should the bar values be stacked
|
||||||
|
protected $_stacked = FALSE;
|
||||||
|
// Default line type to use
|
||||||
|
protected $_type = 'bars';
|
||||||
|
|
||||||
|
public function logy($value) {
|
||||||
|
$this->_logy = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stacked($value) {
|
||||||
|
$this->_stacked = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the type of the chart
|
||||||
|
* @param $type Chart type as per $this->cht
|
||||||
|
*/
|
||||||
|
public function ltitle($side,$title) {
|
||||||
|
if (! in_array($side,array('yl','yr','x')))
|
||||||
|
throw new Kohana_Exception('Unknown side :side',array(':side'=>$side));
|
||||||
|
|
||||||
|
$this->_ltitle[$side] = $title;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function json() {
|
||||||
|
$return = array();
|
||||||
|
|
||||||
|
$return['cols'][] = array(
|
||||||
|
'id'=>'date',
|
||||||
|
'label'=>'date',
|
||||||
|
'type'=>'string',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Columns
|
||||||
|
foreach (array_keys($this->_axis) as $l) {
|
||||||
|
$return['cols'][] = array(
|
||||||
|
'id'=>$l,
|
||||||
|
'label'=>$l,
|
||||||
|
'type'=>'number',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values
|
||||||
|
foreach ($this as $k => $v) {
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
array_push($data,array('v'=>$k));
|
||||||
|
|
||||||
|
foreach ($this->_axis as $l => $axis)
|
||||||
|
array_push($data,array('v'=>isset($v[$l]) ? $v[$l] : 0));
|
||||||
|
|
||||||
|
$return['rows'][] = array('c'=>$data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$options = array(
|
||||||
|
'bar' => array('groupWidth'=>'75%'),
|
||||||
|
'vAxis' => array('logScale'=>$this->_logy ? 1:0),
|
||||||
|
'title' => $this->_title,
|
||||||
|
'isStacked' => $this->_stacked ? 1:0,
|
||||||
|
'seriesType' => $this->_type,
|
||||||
|
'series' => $this->series(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return json_encode(array('data'=>$return,'options'=>$options));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
Script::add(array(
|
||||||
|
'type'=>'src',
|
||||||
|
'data'=>'https://www.google.com/jsapi',
|
||||||
|
));
|
||||||
|
|
||||||
|
Script::add(array(
|
||||||
|
'type'=>'stdin',
|
||||||
|
'data'=>'google.load("visualization", "1", {packages: ["corechart"]});',
|
||||||
|
));
|
||||||
|
|
||||||
|
Script::add(array(
|
||||||
|
'type'=>'stdin',
|
||||||
|
'data'=>"
|
||||||
|
function drawChart".$this->_divname."() {
|
||||||
|
var jsonData = $.ajax({
|
||||||
|
url: '".$this->_dataurl."',
|
||||||
|
dataType:'json',
|
||||||
|
async: false,
|
||||||
|
}).responseText;
|
||||||
|
|
||||||
|
var x = JSON.parse(jsonData);
|
||||||
|
for(var key in x) {
|
||||||
|
if (key == 'data')
|
||||||
|
data = x[key];
|
||||||
|
else if (key == 'options')
|
||||||
|
options = x[key];
|
||||||
|
else
|
||||||
|
alert('UNKNOWN Key: '+key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create our data table out of JSON data loaded from server.
|
||||||
|
var data = new google.visualization.DataTable(data);
|
||||||
|
|
||||||
|
// Instantiate and draw our chart, passing in some options.
|
||||||
|
var chart = new google.visualization.ComboChart(document.getElementById('".$this->_divname."'));
|
||||||
|
chart.draw(data, options);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
));
|
||||||
|
|
||||||
|
Script::add(array(
|
||||||
|
'type'=>'stdin',
|
||||||
|
'data'=>'google.setOnLoadCallback(drawChart'.$this->_divname.');',
|
||||||
|
));
|
||||||
|
|
||||||
|
return sprintf('<div id="%s" style="width: %spx; height: %spx;"></div>',$this->_divname,$this->_width,$this->_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function series() {
|
||||||
|
$return = array();
|
||||||
|
$c = $this->seriescolors();
|
||||||
|
$j = count($c);
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
foreach ($this->_axis as $l => $axis) {
|
||||||
|
// @todo This shouldnt be hard coded
|
||||||
|
if ($axis == 'yl')
|
||||||
|
array_push($return,array('type'=>'bar','color'=>$c[$i%$j],'targetAxisIndex'=>0));
|
||||||
|
else
|
||||||
|
array_push($return,array('type'=>'line','color'=>$c[$i%$j],'targetAxisIndex'=>1));
|
||||||
|
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
@ -140,6 +140,8 @@ class GoogleChart_Legacy extends GoogleChart {
|
|||||||
return implode('|',$return);
|
return implode('|',$return);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function json() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return URL that renders the chart
|
* Return URL that renders the chart
|
||||||
*/
|
*/
|
||||||
@ -155,7 +157,7 @@ class GoogleChart_Legacy extends GoogleChart {
|
|||||||
return array(
|
return array(
|
||||||
'chf'=>'bg,s,FFFFFF00',
|
'chf'=>'bg,s,FFFFFF00',
|
||||||
'cht'=>$this->_type,
|
'cht'=>$this->_type,
|
||||||
'chs'=>$this->_size,
|
'chs'=>sprintf('%sx%s',$this->_width,$this->_height),
|
||||||
'chtt'=>$this->_title,
|
'chtt'=>$this->_title,
|
||||||
'chbh'=>'a', // @todo This might need to be calculated, valid options (a,r);
|
'chbh'=>'a', // @todo This might need to be calculated, valid options (a,r);
|
||||||
'chg'=>'7.7,12.5,1,5', // @todo This should be calculated
|
'chg'=>'7.7,12.5,1,5', // @todo This should be calculated
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
|
class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
|
||||||
// @todo This "module" menu items should belong in the module dir.
|
// @todo This "module" menu items should belong in the module dir.
|
||||||
protected $secure_actions = array(
|
protected $secure_actions = array(
|
||||||
'ajaxlist'=>FALSE, // @todo To Change
|
'ajaxlist'=>TRUE,
|
||||||
|
'ajaxjson_traffic'=>TRUE,
|
||||||
'adslstat'=>TRUE,
|
'adslstat'=>TRUE,
|
||||||
'list'=>TRUE,
|
'list'=>TRUE,
|
||||||
'listbycheckout'=>TRUE,
|
'listbycheckout'=>TRUE,
|
||||||
@ -45,6 +46,23 @@ class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
|
|||||||
$this->response->body(json_encode(array_values($return)));
|
$this->response->body(json_encode(array_values($return)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function action_ajaxjson_traffic() {
|
||||||
|
$return = array();
|
||||||
|
$svs = ORM::factory('service')->list_bylistgroup('ADSL');
|
||||||
|
$data = $this->consoltraffic($svs,time());
|
||||||
|
|
||||||
|
$google = GoogleChart::factory('ComboChart');
|
||||||
|
|
||||||
|
foreach ($data['data'] as $key => $values)
|
||||||
|
$google->data(array('yl'=>$key),array($key=>$values));
|
||||||
|
|
||||||
|
$google->data(array('yr'=>'services'),array('services'=>$data['svs']));
|
||||||
|
|
||||||
|
$this->auto_render = FALSE;
|
||||||
|
$this->response->headers('Content-Type','application/json');
|
||||||
|
$this->response->body($google->json());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a list of services
|
* Show a list of services
|
||||||
*/
|
*/
|
||||||
@ -179,8 +197,7 @@ class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
|
|||||||
$svs = ORM::factory('service')->list_bylistgroup('ADSL');
|
$svs = ORM::factory('service')->list_bylistgroup('ADSL');
|
||||||
$data = $this->consoltraffic($svs,time());
|
$data = $this->consoltraffic($svs,time());
|
||||||
|
|
||||||
$google = GoogleChart::factory('Legacy')
|
$google = GoogleChart::factory('ComboChart')
|
||||||
->type('vertical_bar')
|
|
||||||
->title(sprintf('ADSL traffic as at %s',date('Y-m-d',strtotime('yesterday'))));
|
->title(sprintf('ADSL traffic as at %s',date('Y-m-d',strtotime('yesterday'))));
|
||||||
|
|
||||||
foreach ($data['data'] as $key => $values)
|
foreach ($data['data'] as $key => $values)
|
||||||
@ -189,9 +206,6 @@ class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
|
|||||||
$google->data(array('yr'=>'services'),array('services'=>$data['svs']));
|
$google->data(array('yr'=>'services'),array('services'=>$data['svs']));
|
||||||
|
|
||||||
Block::add(array('body'=>(string)$google));
|
Block::add(array('body'=>(string)$google));
|
||||||
Block::add(array('body'=>$google->table(FALSE,array(
|
|
||||||
'table'=>'style="border: 1px solid #bebcb7; padding: 5px 5px; background: none repeat scroll 0% 0% #f8f7f5; font-size: 70%;"',
|
|
||||||
))));
|
|
||||||
|
|
||||||
Block::add(array(
|
Block::add(array(
|
||||||
'title'=>_('ADSL Services'),
|
'title'=>_('ADSL Services'),
|
||||||
|
@ -27,7 +27,7 @@ class Controller_Admin_SSL extends Controller_TemplateDefault_Admin {
|
|||||||
'id'=>array('label'=>'ID','url'=>'admin/ssl/view/'),
|
'id'=>array('label'=>'ID','url'=>'admin/ssl/view/'),
|
||||||
'sign_cert'=>array('label'=>'Cert'),
|
'sign_cert'=>array('label'=>'Cert'),
|
||||||
'issuer()'=>array('label'=>'Issuer'),
|
'issuer()'=>array('label'=>'Issuer'),
|
||||||
'expires(TRUE)'=>array('label'=>'Expires'),
|
'valid_to(TRUE)'=>array('label'=>'Expires'),
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'page'=>TRUE,
|
'page'=>TRUE,
|
||||||
@ -41,18 +41,32 @@ class Controller_Admin_SSL extends Controller_TemplateDefault_Admin {
|
|||||||
$so = ORM::factory('ssl_ca',$id);
|
$so = ORM::factory('ssl_ca',$id);
|
||||||
|
|
||||||
if ($_POST) {
|
if ($_POST) {
|
||||||
if ($so->values($_POST)->check() AND $so->save())
|
if ($so->values($_POST)->changed()) {
|
||||||
SystemMessage::add(array(
|
try {
|
||||||
'title'=>'SSL Certificate Saved',
|
$so->save();
|
||||||
'type'=>'info',
|
SystemMessage::add(array(
|
||||||
'body'=>'SSL Certificate successfully recorded.',
|
'title'=>'SSL Certificate Saved',
|
||||||
));
|
'type'=>'info',
|
||||||
|
'body'=>'SSL Certificate successfully recorded.',
|
||||||
|
));
|
||||||
|
|
||||||
|
} catch (ORM_Validation_Exception $e) {
|
||||||
|
$errors = $e->errors('models');
|
||||||
|
|
||||||
|
SystemMessage::add(array(
|
||||||
|
'title'=>'SSL Certificate NOT saved',
|
||||||
|
'type'=>'error',
|
||||||
|
'body'=>join("\n",array_values($errors)),
|
||||||
|
));
|
||||||
|
|
||||||
|
$so->reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$output .= Form::open();
|
$output .= Form::open();
|
||||||
$output .= View::factory('ssl/admin/add_view')
|
$output .= View::factory('ssl/admin/add_view')
|
||||||
->set('so',$so)
|
->set('o',$so);
|
||||||
->set('mediapath',Route::get('default/media'));
|
|
||||||
$output .= Form::submit('submit','submit',array('class'=>'form_button'));
|
$output .= Form::submit('submit','submit',array('class'=>'form_button'));
|
||||||
$output .= Form::close();
|
$output .= Form::close();
|
||||||
|
|
||||||
|
@ -35,68 +35,51 @@ class Model_Service_Plugin_SSL extends Model_Service_Plugin {
|
|||||||
public function username_value() {} // Not used
|
public function username_value() {} // Not used
|
||||||
public function password_value() {} // Not used
|
public function password_value() {} // Not used
|
||||||
|
|
||||||
public function service_view() {
|
private $_so = NULL;
|
||||||
return View::factory('service/user/plugin/ssl/view')
|
|
||||||
->set('so',$this);
|
/**
|
||||||
|
* Resolve any queries to certificate details
|
||||||
|
*/
|
||||||
|
public function __call($name,$args) {
|
||||||
|
$m = 'get_'.$name;
|
||||||
|
|
||||||
|
if (method_exists($this->_so,$m))
|
||||||
|
return $this->_so->{$m}($args);
|
||||||
|
else
|
||||||
|
throw new Kohana_Exception('Unknown method :method',array(':method'=>$name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to inject the SSL object into this Model
|
||||||
|
protected function _load_values(array $values) {
|
||||||
|
parent::_load_values($values);
|
||||||
|
|
||||||
|
if ($this->cert)
|
||||||
|
$this->_so = SSL::instance($this->cert);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we change the SSL certificate, we need to reload our SSL object
|
||||||
|
public function values(array $values, array $expected = NULL) {
|
||||||
|
parent::values($values,$expected);
|
||||||
|
|
||||||
|
if (array_key_exists('cert',$values))
|
||||||
|
$this->_so = SSL::instance($this->cert);
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function expire() {
|
public function expire() {
|
||||||
return $this->valid_to();
|
return $this->_so->get_valid_to();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function name() {
|
public function name() {
|
||||||
if ($this->cert) {
|
return ($this->cert) ? sprintf('%s:%s',$this->ssl_ca->subject(),$this->display('cert')) : $this->display('csr');
|
||||||
return sprintf('%s:%s',$this->ssl_ca->subject(),$this->display('cert'));
|
|
||||||
} else
|
|
||||||
return $this->display('csr');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function algorithm() {
|
public function service_view() {
|
||||||
return SSL::algorithm($this->cert);
|
return View::factory('service/user/plugin/ssl/view')
|
||||||
}
|
->set('so',$this);
|
||||||
|
|
||||||
public function dn() {
|
|
||||||
return SSL::dn($this->cert);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function dnissuer() {
|
|
||||||
return SSL::dnissuer($this->cert);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function issuer() {
|
|
||||||
return SSL::issuer($this->cert);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @todo This needs to be validated for this model
|
|
||||||
public function product() {
|
|
||||||
if ($this->provided_adsl_plan_id)
|
|
||||||
return $this->adsl_plan;
|
|
||||||
else
|
|
||||||
return $this->service->product->plugin();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function details() {
|
|
||||||
return SSL::details($this->cert);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function valid_from($format=FALSE) {
|
|
||||||
return SSL::from($this->cert,$format);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function valid_to($format=FALSE) {
|
|
||||||
return SSL::expire($this->cert,$format);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function serial_num() {
|
|
||||||
return SSL::serial($this->cert);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hash() {
|
|
||||||
return SSL::hash($this->cert);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function version() {
|
|
||||||
return SSL::version($this->cert);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,9 +14,6 @@ class Model_SSL_CA extends ORM_OSB {
|
|||||||
protected $_updated_column = FALSE;
|
protected $_updated_column = FALSE;
|
||||||
|
|
||||||
// Relationships
|
// Relationships
|
||||||
protected $_belongs_to = array(
|
|
||||||
);
|
|
||||||
|
|
||||||
protected $_has_many = array(
|
protected $_has_many = array(
|
||||||
'service'=>array('through'=>'service__ssl'),
|
'service'=>array('through'=>'service__ssl'),
|
||||||
);
|
);
|
||||||
@ -27,44 +24,70 @@ class Model_SSL_CA extends ORM_OSB {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
public function expires($format=FALSE) {
|
public function rules() {
|
||||||
return SSL::expire($this->sign_cert,$format);
|
return array(
|
||||||
|
'sign_cert'=>array(
|
||||||
|
array(array($this,'isCert')),
|
||||||
|
array(array($this,'isCA')),
|
||||||
|
),
|
||||||
|
'parent_ssl_ca_id'=>array(
|
||||||
|
array(array($this,'Rule_ParentExists')),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function issuer() {
|
public function filters() {
|
||||||
return SSL::issuer($this->sign_cert);
|
return array(
|
||||||
|
'parent_ssl_ca_id'=>array(
|
||||||
|
array(array($this,'Filter_GetParent')),
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subject() {
|
private $_so = NULL;
|
||||||
return SSL::subject($this->sign_cert);
|
|
||||||
|
/**
|
||||||
|
* Resolve any queries to certificate details
|
||||||
|
*/
|
||||||
|
public function __call($name,$args) {
|
||||||
|
$m = 'get_'.$name;
|
||||||
|
|
||||||
|
if (method_exists($this->_so,$m))
|
||||||
|
return $this->_so->{$m}($args);
|
||||||
|
else
|
||||||
|
throw new Kohana_Exception('Unknown method :method',array(':method'=>$name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save(Validation $validation = NULL) {
|
// We want to inject the SSL object into this Model
|
||||||
// If our parent_ssl_ca_id is null, we'll need to work it out
|
protected function _load_values(array $values) {
|
||||||
if (is_null($this->parent_ssl_ca_id)) {
|
parent::_load_values($values);
|
||||||
$i = SSL::issuer($this->sign_cert);
|
|
||||||
|
|
||||||
$po = NULL;
|
if ($this->sign_cert)
|
||||||
foreach (ORM::factory('ssl_ca')->find_all() as $sco)
|
$this->_so = SSL::instance($this->sign_cert);
|
||||||
if ($sco->subject() == $i) {
|
|
||||||
$po = $sco;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($po)) {
|
return $this;
|
||||||
SystemMessage::add(array(
|
}
|
||||||
'title'=>'Certificate NOT Recorded',
|
|
||||||
'type'=>'warning',
|
|
||||||
'body'=>sprintf('Parent Certificate is not available (%s)',$this->issuer()),
|
|
||||||
));
|
|
||||||
|
|
||||||
return FALSE;
|
// If we change the SSL certificate, we need to reload our SSL object
|
||||||
} else
|
public function values(array $values, array $expected = NULL) {
|
||||||
$this->parent_ssl_ca_id = $po->id;
|
parent::values($values,$expected);
|
||||||
}
|
|
||||||
|
|
||||||
// Save the record
|
if (array_key_exists('sign_cert',$values))
|
||||||
return parent::save($validation);
|
$this->_so = SSL::instance($this->sign_cert);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo This could require some optimisation, by storing the keyid in the database and then getting the DB just to return that parent
|
||||||
|
public function Filter_GetParent() {
|
||||||
|
foreach (ORM::factory($this->_object_name)->find_all() as $sco)
|
||||||
|
if ($sco->aki_keyid() == $this->aki_keyid())
|
||||||
|
return $sco->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Rule_ParentExists() {
|
||||||
|
// Our parent_ssl_ca_id should have been populated by Filter_GetParent().
|
||||||
|
return $this->parent_ssl_ca_id OR $this->isRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function list_issued() {
|
public function list_issued() {
|
||||||
|
@ -4,132 +4,214 @@
|
|||||||
* This class is for access to SSL information
|
* This class is for access to SSL information
|
||||||
*
|
*
|
||||||
* @package OSB
|
* @package OSB
|
||||||
* @subpackage System
|
* @subpackage SSL
|
||||||
* @category Helpers
|
* @category Helpers
|
||||||
* @author Deon George
|
* @author Deon George
|
||||||
* @copyright (c) 2010 Open Source Billing
|
* @copyright (c) 2010 Open Source Billing
|
||||||
* @license http://dev.osbill.net/license.html
|
* @license http://dev.osbill.net/license.html
|
||||||
*/
|
*/
|
||||||
class SSL {
|
class SSL {
|
||||||
public static function instance() {
|
private $cert = '';
|
||||||
return new SSL;
|
private $_details = array();
|
||||||
|
|
||||||
|
public function __construct($cert) {
|
||||||
|
$this->cert = $cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function details($cert,$key=NULL) {
|
public static function instance($cert) {
|
||||||
$k = openssl_x509_parse($cert);
|
return new SSL($cert);
|
||||||
|
|
||||||
return is_null($key) ? $k : $k[$key];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function algorithm($cert,$key=NULL) {
|
/**
|
||||||
if (! $cert)
|
* This function will convert a large decimal number into hex
|
||||||
|
* @param $number Large decimal number
|
||||||
|
*/
|
||||||
|
private static function _dec_to_hex($number) {
|
||||||
|
$hex = array();
|
||||||
|
|
||||||
|
if ($number == 0)
|
||||||
|
return '00';
|
||||||
|
|
||||||
|
while ($number > 0) {
|
||||||
|
if ($number == 0) {
|
||||||
|
array_push($hex, '0');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$x = (int) ($number/16);
|
||||||
|
array_push($hex,strtoupper(dechex((int)($number-($x*16)))));
|
||||||
|
$number = $x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return preg_replace('/^:/','',preg_replace('/(..)/',":$1",implode(array_reverse($hex))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse our AuthorityKeyIndentifier Extension to extract information
|
||||||
|
* @param $key Return just that index
|
||||||
|
*/
|
||||||
|
private function _aki($key=NULL) {
|
||||||
|
$return = array();
|
||||||
|
|
||||||
|
$aki = $this->_extensions('authorityKeyIdentifier');
|
||||||
|
if (! $aki)
|
||||||
return '';
|
return '';
|
||||||
|
|
||||||
$r = openssl_x509_read($cert);
|
foreach (explode("\n",preg_replace("/\n$/",'',$aki)) as $x) {
|
||||||
openssl_x509_export($r,$e,FALSE);
|
if (! $x)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strstr($x,':')) {
|
||||||
|
list($a,$b) = explode(':',$x,2);
|
||||||
|
$return[strtolower($a)] = $b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_null($key) ? $return : (isset($return[$key]) ? $return[$key] : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _bc() {
|
||||||
|
return $this->_extensions('basicConstraints');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse our Sign Certifcate to extract information
|
||||||
|
* @param $key Return just that index
|
||||||
|
*/
|
||||||
|
private function _details($key=NULL) {
|
||||||
|
if (! $this->cert)
|
||||||
|
return array();
|
||||||
|
|
||||||
|
if (! $this->_details)
|
||||||
|
$this->_details = openssl_x509_parse($this->cert);
|
||||||
|
|
||||||
|
return is_null($key) ? $this->_details : (isset($this->_details[$key]) ? $this->_details[$key] : array());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse our Sign Certifcate Extensions to extract information
|
||||||
|
* @param $key Return just that index
|
||||||
|
*/
|
||||||
|
private function _extensions($key=NULL) {
|
||||||
|
$return = $this->_details('extensions');
|
||||||
|
|
||||||
|
return is_null($key) ? $return : (isset($return[$key]) ? $return[$key] : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a DN array as a string
|
||||||
|
*/
|
||||||
|
private function _dn(array $array) {
|
||||||
|
$return = '';
|
||||||
|
$i = 0;
|
||||||
|
|
||||||
|
foreach ($array as $k=>$v) {
|
||||||
|
if ($i++)
|
||||||
|
$return .= ',';
|
||||||
|
|
||||||
|
$return .= sprintf('%s=%s',$k,$v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_aki_dirname() {
|
||||||
|
return $this->_aki('dirname');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_aki_keyid() {
|
||||||
|
return $this->_aki('keyid');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_aki_serial() {
|
||||||
|
return $this->_aki('serial');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_algorithm() {
|
||||||
|
$e = '';
|
||||||
|
openssl_x509_export(openssl_x509_read($this->cert),$e,FALSE);
|
||||||
|
|
||||||
// @todo There must be a nice way to get this?
|
// @todo There must be a nice way to get this?
|
||||||
if (preg_match('/^\s+Signature Algorithm:\s*(.*)\s*$/m',$e,$match))
|
return (preg_match('/^\s+Signature Algorithm:\s*(.*)\s*$/m',$e,$match)) ? $match[1] : _('Unknown');
|
||||||
return $match[1];
|
|
||||||
else
|
|
||||||
return _('Unknown');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function aki($cert,$key=NULL) {
|
public function get_ca_path_len() {
|
||||||
$k = array();
|
$m = array();
|
||||||
foreach (explode("\n",preg_replace("/\n$/",'',static::extensions($cert,'authorityKeyIdentifier'))) as $x) {
|
$x = preg_match('/.*pathlen:\s*([0-9]+).*$/',$this->_bc(),$m);
|
||||||
list($a,$b) = explode(":",$x,2);
|
|
||||||
$k[strtolower($a)] = $b;
|
|
||||||
}
|
|
||||||
|
|
||||||
return is_null($key) ? $k : $k[$key];
|
return isset($m[1]) ? (int)$m[1] : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function aki_keyid($key) {
|
public function get_dn() {
|
||||||
return static::aki($key,'keyid');
|
return $this->_dn($this->_details('subject'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function aki_dirname($key) {
|
public function get_hash() {
|
||||||
return static::aki($key,'dirname');
|
return $this->_details('hash');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function aki_serial($key) {
|
public function get_isCA() {
|
||||||
return static::aki($key,'serial');
|
return preg_match('/CA:TRUE/',$this->_bc());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function dn($cert) {
|
public function get_isCert() {
|
||||||
if (! $cert)
|
return is_array($this->_details());
|
||||||
return '';
|
|
||||||
|
|
||||||
$s = '';
|
|
||||||
|
|
||||||
$c = 0;
|
|
||||||
foreach (static::details($cert,'subject') as $k=>$v) {
|
|
||||||
if ($c++)
|
|
||||||
$s .= ',';
|
|
||||||
|
|
||||||
$s .= sprintf('%s=%s',$k,$v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function dnissuer($cert) {
|
public function get_isRoot() {
|
||||||
if (! $cert)
|
return $this->get_aki_keyid() == $this->get_ski();
|
||||||
return '';
|
|
||||||
|
|
||||||
$s = '';
|
|
||||||
|
|
||||||
$c = 0;
|
|
||||||
foreach (static::details($cert,'issuer') as $k=>$v) {
|
|
||||||
if ($c++)
|
|
||||||
$s .= ',';
|
|
||||||
|
|
||||||
$s .= sprintf('%s=%s',$k,$v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function issuer($cert) {
|
public function get_issuer() {
|
||||||
$k = static::details($cert,'issuer');
|
$k = $this->_details('issuer');
|
||||||
return $k['CN'];
|
|
||||||
|
return isset($k['CN']) ? $k['CN'] : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function from($cert,$format=FALSE) {
|
public function get_issuerdn() {
|
||||||
$k = static::details($cert,'validFrom_time_t');
|
return $this->_dn($this->_details('issuer'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_serial() {
|
||||||
|
return $this->_dec_to_hex($this->_details('serialNumber'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_subject() {
|
||||||
|
$k = $this->_details('subject');
|
||||||
|
|
||||||
|
return isset($k['CN']) ? $k['CN'] : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_ski() {
|
||||||
|
return $this->_extensions('subjectKeyIdentifier');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_valid_to($format=FALSE) {
|
||||||
|
$k = $this->_details('validTo_time_t');
|
||||||
|
|
||||||
return $format ? Config::date($k) : $k;
|
return $format ? Config::date($k) : $k;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function expire($key,$format=FALSE) {
|
public function get_valid_from($format=FALSE) {
|
||||||
$k = static::details($key,'validTo_time_t');
|
$k = $this->_details('validFrom_time_t');
|
||||||
|
|
||||||
return $format ? Config::date($k) : $k;
|
return $format ? Config::date($k) : $k;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function extensions($cert,$key=NULL) {
|
public function get_version() {
|
||||||
$k = static::details($cert,'extensions');
|
return $this->_details('version');
|
||||||
return is_null($key) ? $k : $k[$key];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function hash($key) {
|
public static function xdn($cert) {
|
||||||
return static::details($key,'hash');
|
return static::instance($cert)->get_dn();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function serial($key) {
|
public static function xexpire($cert,$format=FALSE) {
|
||||||
return static::dec_to_hex(static::details($key,'serialNumber'));
|
return static::instance($cert)->get_expire($format);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function subject($key) {
|
public static function subject($cert) {
|
||||||
$k = static::details($key,'subject');
|
return static::instance($cert)->get_subject();
|
||||||
return $k['CN'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function ski($key) {
|
|
||||||
return static::extensions($key,'subjectKeyIdentifier');
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function version($key) {
|
|
||||||
return static::details($key,'version');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function csrsubject($csr) {
|
public static function csrsubject($csr) {
|
||||||
@ -137,25 +219,5 @@ class SSL {
|
|||||||
|
|
||||||
return $c['CN'];
|
return $c['CN'];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function dec_to_hex($number) {
|
|
||||||
$hex = array();
|
|
||||||
|
|
||||||
if ($number == 0)
|
|
||||||
return '00';
|
|
||||||
|
|
||||||
while ($number > 0) {
|
|
||||||
if ($number == 0) {
|
|
||||||
array_push($hex, '0');
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$x = (int) ($number/16);
|
|
||||||
array_push($hex,strtoupper(dechex((int)($number-($x*16)))));
|
|
||||||
$number = $x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return preg_replace('/^:/','',preg_replace('/(..)/',":$1",implode(array_reverse($hex))));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
11
modules/ssl/messages/models/ssl_ca.php
Normal file
11
modules/ssl/messages/models/ssl_ca.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
return array(
|
||||||
|
'sign_cert'=>array(
|
||||||
|
'isCert'=>'This is not a valid certificate',
|
||||||
|
'isCA'=>'This is certificate is not a Certificate Authority certificate',
|
||||||
|
),
|
||||||
|
'parent_ssl_ca_id'=>array(
|
||||||
|
'Rule_ParentExists'=>'The parent certificate doesnt exist, please define it first',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
?>
|
@ -15,7 +15,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Issuer</td>
|
<td>Issuer</td>
|
||||||
<td class="data"><?php echo $so->dnissuer(); ?></td>
|
<td class="data"><?php echo $so->issuerdn(); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>CA</td>
|
<td>CA</td>
|
||||||
@ -31,7 +31,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Serial Number</td>
|
<td>Serial Number</td>
|
||||||
<td class="data"><?php echo $so->serial_num(); ?></td>
|
<td class="data"><?php echo $so->serial(); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Version</td>
|
<td>Version</td>
|
||||||
|
@ -1,46 +1,55 @@
|
|||||||
|
<!-- $o = ORM::factory('SSL_CA'); -->
|
||||||
<table border="0">
|
<table border="0">
|
||||||
|
<tr>
|
||||||
|
<td>DN</td>
|
||||||
|
<td class="data"><?php echo $o->dn(); ?></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Subject</td>
|
<td>Subject</td>
|
||||||
<td class="data"><?php printf('%s (%s)',SSL::subject($so->sign_cert),SSL::serial($so->sign_cert)); ?></td>
|
<td class="data"><?php printf('%s (%s)',$o->subject(),$o->serial()); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Subject Key ID</td>
|
<td>Subject Key ID</td>
|
||||||
<td class="data"><?php echo SSL::ski($so->sign_cert); ?></td>
|
<td class="data"><?php echo $o->ski(); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Issuer</td>
|
<td>Issuer</td>
|
||||||
<td class="data"><?php printf('%s (%s)',SSL::issuer($so->sign_cert),SSL::aki_serial($so->sign_cert)); ?></td>
|
<td class="data"><?php printf('%s (%s)',$o->issuer(),$o->aki_serial()); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Issuer Key ID</td>
|
<td>Issuer Key ID</td>
|
||||||
<td class="data"><?php echo SSL::aki_keyid($so->sign_cert); ?></td>
|
<td class="data"><?php echo $o->aki_keyid(); ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>CA</td>
|
||||||
|
<td class="data"><?php echo StaticList_YesNo::display($o->isCA()); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Valid From</td>
|
<td>Valid From</td>
|
||||||
<td class="data"><?php echo SSL::from($so->sign_cert,TRUE); ?></td>
|
<td class="data"><?php echo $o->valid_from(TRUE); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Valid To</td>
|
<td>Valid To</td>
|
||||||
<td class="data"><?php echo SSL::expire($so->sign_cert,TRUE); ?></td>
|
<td class="data"><?php echo $o->valid_to(TRUE); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Hash</td>
|
<td>Hash</td>
|
||||||
<td class="data"><?php echo SSL::hash($so->sign_cert); ?></td>
|
<td class="data"><?php echo $o->hash(); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Version</td>
|
<td>Version</td>
|
||||||
<td class="data"><?php echo SSL::version($so->sign_cert); ?></td>
|
<td class="data"><?php echo $o->version(); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Key Algorithm</td>
|
<td>Key Algorithm</td>
|
||||||
<td class="data"><?php echo SSL::algorithm($so->sign_cert); ?></td>
|
<td class="data"><?php echo $o->algorithm(); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width: 40%;">Private Key</td>
|
<td style="width: 40%;">Private Key</td>
|
||||||
<td style="width: 60%;"><?php echo FORM::textarea('sign_pk',$so->sign_pk,array('cols'=>64,'rows'=>13)); ?></td>
|
<td style="width: 60%;"><?php echo FORM::textarea('sign_pk',$o->sign_pk,array('cols'=>64,'rows'=>13)); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Certificate</td>
|
<td>Certificate</td>
|
||||||
<td><?php echo FORM::textarea('sign_cert',$so->sign_cert,array('cols'=>64,'rows'=>13)); ?></td>
|
<td><?php echo FORM::textarea('sign_cert',$o->sign_cert,array('cols'=>64,'rows'=>13)); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<table>
|
<table>
|
||||||
|
Reference in New Issue
Block a user