From 561bfbf475a46604d856d277a60cf165a1061829 Mon Sep 17 00:00:00 2001
From: Mathieu Sanchez
Date: Tue, 27 Nov 2018 13:59:21 +0900
Subject: [PATCH] Add a lot
---
.htaccess | 9 +-
index.php | 2 +-
src/API/API.php | 32 +-
src/API/APIContact.php | 201 +
src/API/APIError.php | 159 +-
src/API/APIRouter.php | 214 +-
src/Autoloader.php | 32 +-
src/Config.php | 12 +-
src/Controller/Controller.php | 48 +-
src/Controller/ControllerSite.php | 98 +-
src/Controller/Error.php | 174 +-
src/Controller/site/Index.php | 26 +-
src/Controller/site/SiteError.php | 184 +-
src/Controller/site/SiteRouter.php | 26 +-
src/Model/BDD.php | 56 +-
src/Model/BDTables.php | 7 +-
src/Model/Contact.php | 220 +
src/Model/FPDF.php | 3758 +++++++-------
src/Model/Logs.php | 303 +-
src/Model/Model.php | 88 +-
src/View/Site/Index.php | 42 +-
src/View/Site/tpl/footer.php | 31 +-
src/View/Site/tpl/head.php | 61 +-
src/lib/functions.php | 460 +-
src/lib/mail/PHPMailerAutoload.php | 38 +-
src/lib/mail/class.phpmailer.php | 7719 ++++++++++++++--------------
src/lib/mail/class.smtp.php | 2372 ++++-----
src/lib/mail/mail_default.html | 2 +-
static/css/materialize.min.css | 13 +
static/css/style.css | 68 +-
static/img/add-contacts.png | Bin 0 -> 1274 bytes
static/img/favicon.ico | Bin 0 -> 4286 bytes
static/img/logo.png | Bin 0 -> 9871 bytes
static/js/javascript.js | 74 +
static/js/jquery-3.2.1.min.js | 4 -
static/js/jquery-3.3.1.min.js | 2 +
static/js/materialize.min.js | 6 +
37 files changed, 8589 insertions(+), 7952 deletions(-)
create mode 100644 src/API/APIContact.php
create mode 100644 src/Model/Contact.php
create mode 100644 static/css/materialize.min.css
create mode 100644 static/img/add-contacts.png
create mode 100644 static/img/favicon.ico
create mode 100644 static/img/logo.png
delete mode 100644 static/js/jquery-3.2.1.min.js
create mode 100644 static/js/jquery-3.3.1.min.js
create mode 100644 static/js/materialize.min.js
diff --git a/.htaccess b/.htaccess
index 55341e9..013cda8 100644
--- a/.htaccess
+++ b/.htaccess
@@ -10,12 +10,15 @@ RewriteRule ^ - [L]
# RewriteCond %{HTTP_HOST} ^eldotravo.fr$
# RewriteRule ^(.*)$ http://www.eldotravo.fr/$1 [R=301,L,E=END:1]
-# Redirection des fichiers static pour contact.sanchez-mathieu.dev
-RewriteCond %{HTTP_HOST} ^contact.sanchez-mathieu.dev$
+# Redirection des fichiers static pour contact.sanchez-mathieu.test
+RewriteCond %{HTTP_HOST} ^contact.sanchez-mathieu.test
RewriteCond %{DOCUMENT_ROOT}/static%{REQUEST_URI} -f
RewriteRule ^(.*)$ static/$1 [L,E=END:1]
-# RewriteRule ^haute-garonne/toulouse/installation-entretien-climatisation /haute-garonne/toulouse/climatisation.php [L]
+# Redirection des fichiers static pour contact.sanchez-mathieu.fr
+RewriteCond %{HTTP_HOST} ^contact.sanchez-mathieu.fr
+RewriteCond %{DOCUMENT_ROOT}/static%{REQUEST_URI} -f
+RewriteRule ^(.*)$ static/$1 [L,E=END:1]
# Redirection de toutes les requêtes vers Index.php
RewriteRule ^ index.php [L,E=END:1]
\ No newline at end of file
diff --git a/index.php b/index.php
index fd92929..f87b67c 100644
--- a/index.php
+++ b/index.php
@@ -35,7 +35,7 @@ if ( count( $pages ) > 1 && $pages[ count( $pages ) - 1 ] == '' ) {
if ( $pages[0] == 'api' && isset( $pages[1] ) && preg_match( '#^([a-z]+)$#', $pages[1], $api1 ) && isset( $pages[2] ) && preg_match( '#^([a-z-]+)$#', $pages[2], $api2 ) ) {
new APIRouter( $api1[0], $api2[0] );
-} else if ( preg_match( '#^test\.dev$#', $_SERVER['SERVER_NAME'] ) ) {
+} else if ( preg_match( '#^contact.sanchez-mathieu\.test$#', $_SERVER[ 'SERVER_NAME' ] ) || preg_match( '#^contact.sanchez-mathieu\.fr#', $_SERVER[ 'SERVER_NAME' ] ) ) {
new SiteRouter( $pages );
} else {
new Error( 404 );
diff --git a/src/API/API.php b/src/API/API.php
index 7f03283..69361d8 100644
--- a/src/API/API.php
+++ b/src/API/API.php
@@ -6,24 +6,24 @@ use CAUProject3Contact\Controller\Controller;
class API extends Controller {
- private $declaredFunctions = [];
+ private $declaredFunctions = [];
- /**
- * API constructor.
- *
- * @param array $declaredFunctions
- */
- public function __construct( array $declaredFunctions ) {
- parent::__construct();
- $this->declaredFunctions = $declaredFunctions;
- }
+ /**
+ * API constructor.
+ *
+ * @param array $declaredFunctions
+ */
+ public function __construct( array $declaredFunctions ) {
+ parent::__construct();
+ $this->declaredFunctions = $declaredFunctions;
+ }
- /**
- * @return array
- */
- public function getDeclaredFunctions() {
- return $this->declaredFunctions;
- }
+ /**
+ * @return array
+ */
+ public function getDeclaredFunctions() {
+ return $this->declaredFunctions;
+ }
}
?>
\ No newline at end of file
diff --git a/src/API/APIContact.php b/src/API/APIContact.php
new file mode 100644
index 0000000..4722bbd
--- /dev/null
+++ b/src/API/APIContact.php
@@ -0,0 +1,201 @@
+ [
+ 'method' => 'POST',
+ 'params' => [
+ 'firstName' => [
+ 'required' => true,
+ 'type' => 'string'
+ ],
+ 'lastName' => [
+ 'required' => true,
+ 'type' => 'string'
+ ],
+ 'surname' => [
+ 'required' => false,
+ 'type' => 'string'
+ ],
+ 'email' => [
+ 'required' => false,
+ 'type' => 'string'
+ ],
+ 'address' => [
+ 'required' => false,
+ 'type' => 'string'
+ ],
+ 'phoneNumber' => [
+ 'required' => false,
+ 'type' => 'string'
+ ],
+ 'birthday' => [
+ 'required' => false,
+ 'type' => 'string'
+ ],
+ ]
+ ],
+ 'delete' => [
+ 'method' => 'POST',
+ 'params' => [
+ 'id' => [
+ 'required' => true,
+ 'type' => 'int'
+ ]
+ ]
+ ],
+ 'get-contacts' => [
+ 'method' => 'GET',
+ 'params' => []
+ ],
+ 'update' => [
+ 'method' => 'POST',
+ 'params' => [
+ 'id' => [
+ 'required' => true,
+ 'type' => 'int'
+ ],
+ 'firstName' => [
+ 'required' => false,
+ 'type' => 'string'
+ ],
+ 'lastName' => [
+ 'required' => false,
+ 'type' => 'string'
+ ],
+ 'surname' => [
+ 'required' => false,
+ 'type' => 'string'
+ ],
+ 'email' => [
+ 'required' => false,
+ 'type' => 'string'
+ ],
+ 'address' => [
+ 'required' => false,
+ 'type' => 'string'
+ ],
+ 'phoneNumber' => [
+ 'required' => false,
+ 'type' => 'string'
+ ],
+ 'birthday' => [
+ 'required' => false,
+ 'type' => 'string'
+ ]
+ ]
+ ],
+ 'search' => [
+ 'method' => 'POST',
+ 'params' => [
+ 'query' => [
+ 'required' => true,
+ 'type' => 'string'
+ ]
+ ]
+ ]
+ ];
+
+ /**
+ * APIContact constructor.
+ *
+ * @param array $declaredFunctions
+ */
+ public function __construct() {
+ parent::__construct( $this->declaredFunctions );
+ }
+
+ /**
+ * @return array
+ */
+ public function getDeclaredFunctions() {
+ return $this->declaredFunctions;
+ }
+
+ public function insert( array $data ) {
+
+ $id = Contact::insertNewContact( $data[ "firstName" ], $data[ "lastName" ], $data[ "surname" ],
+ $data[ "email" ], $data[ "address" ], $data[ "phoneNumber" ], $data[ "birthday" ] );
+
+ $this->returnJson( json_encode( [
+ "status" => "success",
+ "data" => [
+ "id" => $id
+ ]
+ ] ) );
+ }
+
+ public function delete( array $data ) {
+ Contact::deleteContact( $data[ "id" ] );
+ }
+
+ public function getContacts() {
+ $this->returnJson( json_encode( [
+ "contacts" => Contact::getAllContact()
+ ] ) );
+ }
+
+ public function update( array $data ) {
+ $contact = Contact::getById( $data[ "id" ] );
+
+ $newData = [];
+
+ if ( $data[ "firstName" ] !== null && $data[ "firstName" ] !== "" ) {
+ $newData[ "first_name" ] = $data[ "firstName" ];
+ }
+ if ( $data[ "lastName" ] !== null && $data[ "lastName" ] !== "" ) {
+ $newData[ "last_name" ] = $data[ "lastName" ];
+ }
+ if ( $data[ "surname" ] !== null && $data[ "surname" ] !== "" ) {
+ $newData[ "surname" ] = $data[ "surname" ];
+ }
+ if ( $data[ "email" ] !== null && $data[ "email" ] !== "" ) {
+ $newData[ "email" ] = $data[ "email" ];
+ }
+ if ( $data[ "address" ] !== null && $data[ "address" ] !== "" ) {
+ $newData[ "address" ] = $data[ "address" ];
+ }
+ if ( $data[ "phoneNumber" ] !== null && $data[ "phoneNumber" ] !== "" ) {
+ $newData[ "phone_number" ] = $data[ "phoneNumber" ];
+ }
+ if ( $data[ "birthday" ] !== null && $data[ "birthday" ] !== "" ) {
+ $newData[ "birthday" ] = date( "Y-m-d", strtotime( $data[ "birthday" ] ) );
+ }
+
+ $contact->updateContact( $newData );
+ $this->returnJson( json_encode( $contact ) );
+ }
+
+ public function search( array $data ) {
+ if ( count_chars( $data[ "query" ] ) >= 3 ) {
+ $result = Contact::search( $data[ "query" ] );
+ if ( $result !== null ) {
+ $this->returnJson( [
+ "status" => "success",
+ "code" => 200,
+ "result" => $result,
+ ] );
+ } else {
+ $this->returnJson( [
+ "status" => "error",
+ "code" => 404,
+ "message" => "Nothing find",
+ ] );
+ }
+ } else {
+ $this->returnJson( [
+ "status" => "error",
+ "code" => 400,
+ "message" => "Need at least 3 chars",
+ ] );
+ }
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/src/API/APIError.php b/src/API/APIError.php
index fbd88b1..891de31 100644
--- a/src/API/APIError.php
+++ b/src/API/APIError.php
@@ -6,87 +6,88 @@ use CAUProject3Contact\Controller\Controller;
class APIError extends Controller {
- /**
- * APIError constructor.
- *
- * @param int $ErrCode
- * @param string $devMessage
- * @param string $publicMessage
- * @param string $code
- */
- public function __construct( int $ErrCode = 500, string $devMessage = 'Erreur inconnue', string $publicMessage = 'Une erreur inconnue s\'est produite', string $code = '' ) {
- parent::__construct();
+ /**
+ * APIError constructor.
+ *
+ * @param int $ErrCode
+ * @param string $devMessage
+ * @param string $publicMessage
+ * @param string $code
+ */
+ public function __construct( int $ErrCode = 500, string $devMessage = 'Erreur inconnue',
+ string $publicMessage = 'Une erreur inconnue s\'est produite', string $code = '' ) {
+ parent::__construct();
- $tabCode = [
- //Informational 1xx
- 100 => [ 'label' => '100 Continue', 'msg' => '100 Continue' ],
- 101 => [ 'label' => '101 Switching Protocols', 'msg' => '101 Switching Protocols' ],
- //Successful 2xx
- 200 => [ 'label' => '200 OK', 'msg' => '200 OK' ],
- 201 => [ 'label' => '201 Created', 'msg' => '201 Created' ],
- 202 => [ 'label' => '202 Accepted', 'msg' => '202 Accepted' ],
- 203 => [ 'label' => '203 Non-Authoritative Information', 'msg' => '203 Non-Authoritative Information' ],
- 204 => [ 'label' => '204 No Content', 'msg' => '204 No Content' ],
- 205 => [ 'label' => '205 Reset Content', 'msg' => '205 Reset Content' ],
- 206 => [ 'label' => '206 Partial Content', 'msg' => '206 Partial Content' ],
- 226 => [ 'label' => '226 IM Used', 'msg' => '226 IM Used' ],
- //Redirection 3xx
- 300 => [ 'label' => '300 Multiple Choices', 'msg' => '300 Multiple Choices' ],
- 301 => [ 'label' => '301 Moved Permanently', 'msg' => '301 Moved Permanently' ],
- 302 => [ 'label' => '302 Found', 'msg' => '302 Found' ],
- 303 => [ 'label' => '303 See Other', 'msg' => '303 See Other' ],
- 304 => [ 'label' => '304 Not Modified', 'msg' => '304 Not Modified' ],
- 305 => [ 'label' => '305 Use Proxy', 'msg' => '305 Use Proxy' ],
- 306 => [ 'label' => '306 (Unused)', 'msg' => '306 (Unused)' ],
- 307 => [ 'label' => '307 Temporary Redirect', 'msg' => '307 Temporary Redirect' ],
- //Client Error 4xx
- 400 => [ 'label' => '400 Bad Request', 'msg' => '400 Bad Request' ],
- 401 => [ 'label' => '401 Unauthorized', 'msg' => 'Vous n\'êtes pas autorisé à accéder à cette page' ],
- 402 => [ 'label' => '402 Payment Required', 'msg' => '402 Payment Required' ],
- 403 => [ 'label' => '403 Forbidden', 'msg' => '403 Forbidden' ],
- 404 => [ 'label' => '404 Not Found', 'msg' => 'Page non trouvée' ],
- 405 => [ 'label' => '405 Method Not Allowed', 'msg' => '405 Method Not Allowed' ],
- 406 => [ 'label' => '406 Not Acceptable', 'msg' => '406 Not Acceptable' ],
- 407 => [ 'label' => '407 Proxy Authentication Required', 'msg' => '407 Proxy Authentication Required' ],
- 408 => [ 'label' => '408 Request Timeout', 'msg' => '408 Request Timeout' ],
- 409 => [ 'label' => '409 Conflict', 'msg' => '409 Conflict' ],
- 410 => [ 'label' => '410 Gone', 'msg' => '410 Gone' ],
- 411 => [ 'label' => '411 Length Required', 'msg' => '411 Length Required' ],
- 412 => [ 'label' => '412 Precondition Failed', 'msg' => '412 Precondition Failed' ],
- 413 => [ 'label' => '413 Request Entity Too Large', 'msg' => '413 Request Entity Too Large' ],
- 414 => [ 'label' => '414 Request-URI Too Long', 'msg' => '414 Request-URI Too Long' ],
- 415 => [ 'label' => '415 Unsupported Media Type', 'msg' => '415 Unsupported Media Type' ],
- 416 => [ 'label' => '416 Requested Range Not Satisfiable', 'msg' => '416 Requested Range Not Satisfiable' ],
- 417 => [ 'label' => '417 Expectation Failed', 'msg' => '417 Expectation Failed' ],
- 418 => [ 'label' => '418 I\'m a teapot', 'msg' => '418 I\'m a teapot' ],
- 422 => [ 'label' => '422 Unprocessable Entity', 'msg' => '422 Unprocessable Entity' ],
- 423 => [ 'label' => '423 Locked', 'msg' => '423 Locked' ],
- 426 => [ 'label' => '426 Upgrade Required', 'msg' => '426 Upgrade Required' ],
- 428 => [ 'label' => '428 Precondition Required', 'msg' => '428 Precondition Required' ],
- 429 => [ 'label' => '429 Too Many Requests', 'msg' => '429 Too Many Requests' ],
- 431 => [ 'label' => '431 Request Header Fields Too Large', 'msg' => '431 Request Header Fields Too Large' ],
- //Server Error 5xx
- 500 => [ 'label' => '500 Internal Server Error', 'msg' => 'Une erreur est survenue' ],
- 501 => [ 'label' => '501 Not Implemented', 'msg' => '501 Not Implemented' ],
- 502 => [ 'label' => '502 Bad Gateway', 'msg' => '502 Bad Gateway' ],
- 503 => [ 'label' => '503 Service Unavailable', 'msg' => '503 Service Unavailable' ],
- 504 => [ 'label' => '504 Gateway Timeout', 'msg' => '504 Gateway Timeout' ],
- 505 => [ 'label' => '505 HTTP Version Not Supported', 'msg' => '505 HTTP Version Not Supported' ],
- 506 => [ 'label' => '506 Variant Also Negotiates', 'msg' => '506 Variant Also Negotiates' ],
- 510 => [ 'label' => '510 Not Extended', 'msg' => '510 Not Extended' ],
- 511 => [ 'label' => '511 Network Authentication Required', 'msg' => '511 Network Authentication Required' ]
- ];
+ $tabCode = [
+ //Informational 1xx
+ 100 => [ 'label' => '100 Continue', 'msg' => '100 Continue' ],
+ 101 => [ 'label' => '101 Switching Protocols', 'msg' => '101 Switching Protocols' ],
+ //Successful 2xx
+ 200 => [ 'label' => '200 OK', 'msg' => '200 OK' ],
+ 201 => [ 'label' => '201 Created', 'msg' => '201 Created' ],
+ 202 => [ 'label' => '202 Accepted', 'msg' => '202 Accepted' ],
+ 203 => [ 'label' => '203 Non-Authoritative Information', 'msg' => '203 Non-Authoritative Information' ],
+ 204 => [ 'label' => '204 No Content', 'msg' => '204 No Content' ],
+ 205 => [ 'label' => '205 Reset Content', 'msg' => '205 Reset Content' ],
+ 206 => [ 'label' => '206 Partial Content', 'msg' => '206 Partial Content' ],
+ 226 => [ 'label' => '226 IM Used', 'msg' => '226 IM Used' ],
+ //Redirection 3xx
+ 300 => [ 'label' => '300 Multiple Choices', 'msg' => '300 Multiple Choices' ],
+ 301 => [ 'label' => '301 Moved Permanently', 'msg' => '301 Moved Permanently' ],
+ 302 => [ 'label' => '302 Found', 'msg' => '302 Found' ],
+ 303 => [ 'label' => '303 See Other', 'msg' => '303 See Other' ],
+ 304 => [ 'label' => '304 Not Modified', 'msg' => '304 Not Modified' ],
+ 305 => [ 'label' => '305 Use Proxy', 'msg' => '305 Use Proxy' ],
+ 306 => [ 'label' => '306 (Unused)', 'msg' => '306 (Unused)' ],
+ 307 => [ 'label' => '307 Temporary Redirect', 'msg' => '307 Temporary Redirect' ],
+ //Client Error 4xx
+ 400 => [ 'label' => '400 Bad Request', 'msg' => '400 Bad Request' ],
+ 401 => [ 'label' => '401 Unauthorized', 'msg' => 'Vous n\'êtes pas autorisé à accéder à cette page' ],
+ 402 => [ 'label' => '402 Payment Required', 'msg' => '402 Payment Required' ],
+ 403 => [ 'label' => '403 Forbidden', 'msg' => '403 Forbidden' ],
+ 404 => [ 'label' => '404 Not Found', 'msg' => 'Page non trouvée' ],
+ 405 => [ 'label' => '405 Method Not Allowed', 'msg' => '405 Method Not Allowed' ],
+ 406 => [ 'label' => '406 Not Acceptable', 'msg' => '406 Not Acceptable' ],
+ 407 => [ 'label' => '407 Proxy Authentication Required', 'msg' => '407 Proxy Authentication Required' ],
+ 408 => [ 'label' => '408 Request Timeout', 'msg' => '408 Request Timeout' ],
+ 409 => [ 'label' => '409 Conflict', 'msg' => '409 Conflict' ],
+ 410 => [ 'label' => '410 Gone', 'msg' => '410 Gone' ],
+ 411 => [ 'label' => '411 Length Required', 'msg' => '411 Length Required' ],
+ 412 => [ 'label' => '412 Precondition Failed', 'msg' => '412 Precondition Failed' ],
+ 413 => [ 'label' => '413 Request Entity Too Large', 'msg' => '413 Request Entity Too Large' ],
+ 414 => [ 'label' => '414 Request-URI Too Long', 'msg' => '414 Request-URI Too Long' ],
+ 415 => [ 'label' => '415 Unsupported Media Type', 'msg' => '415 Unsupported Media Type' ],
+ 416 => [ 'label' => '416 Requested Range Not Satisfiable', 'msg' => '416 Requested Range Not Satisfiable' ],
+ 417 => [ 'label' => '417 Expectation Failed', 'msg' => '417 Expectation Failed' ],
+ 418 => [ 'label' => '418 I\'m a teapot', 'msg' => '418 I\'m a teapot' ],
+ 422 => [ 'label' => '422 Unprocessable Entity', 'msg' => '422 Unprocessable Entity' ],
+ 423 => [ 'label' => '423 Locked', 'msg' => '423 Locked' ],
+ 426 => [ 'label' => '426 Upgrade Required', 'msg' => '426 Upgrade Required' ],
+ 428 => [ 'label' => '428 Precondition Required', 'msg' => '428 Precondition Required' ],
+ 429 => [ 'label' => '429 Too Many Requests', 'msg' => '429 Too Many Requests' ],
+ 431 => [ 'label' => '431 Request Header Fields Too Large', 'msg' => '431 Request Header Fields Too Large' ],
+ //Server Error 5xx
+ 500 => [ 'label' => '500 Internal Server Error', 'msg' => 'Une erreur est survenue' ],
+ 501 => [ 'label' => '501 Not Implemented', 'msg' => '501 Not Implemented' ],
+ 502 => [ 'label' => '502 Bad Gateway', 'msg' => '502 Bad Gateway' ],
+ 503 => [ 'label' => '503 Service Unavailable', 'msg' => '503 Service Unavailable' ],
+ 504 => [ 'label' => '504 Gateway Timeout', 'msg' => '504 Gateway Timeout' ],
+ 505 => [ 'label' => '505 HTTP Version Not Supported', 'msg' => '505 HTTP Version Not Supported' ],
+ 506 => [ 'label' => '506 Variant Also Negotiates', 'msg' => '506 Variant Also Negotiates' ],
+ 510 => [ 'label' => '510 Not Extended', 'msg' => '510 Not Extended' ],
+ 511 => [ 'label' => '511 Network Authentication Required', 'msg' => '511 Network Authentication Required' ]
+ ];
- header( 'Content-Type: application/json' );
- header( $_SERVER['SERVER_PROTOCOL'] . ' ' . $tabCode[ $ErrCode ]['label'] );
- echo json_encode( [
- 'status' => 'echec',
- 'msg' => $publicMessage,
- 'devMsg' => $devMessage,
- 'code' => $code
- ], JSON_PRETTY_PRINT );
- exit();
- }
+ header( 'Content-Type: application/json' );
+ header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' ' . $tabCode[ $ErrCode ][ 'label' ] );
+ echo json_encode( [
+ 'status' => 'echec',
+ 'msg' => $publicMessage,
+ 'devMsg' => $devMessage,
+ 'code' => $code
+ ], JSON_PRETTY_PRINT );
+ exit();
+ }
}
?>
\ No newline at end of file
diff --git a/src/API/APIRouter.php b/src/API/APIRouter.php
index 375dfb5..9b443cd 100644
--- a/src/API/APIRouter.php
+++ b/src/API/APIRouter.php
@@ -6,127 +6,127 @@ use CAUProject3Contact\Config;
class APIRouter {
- //Les méthodes HTTP gérée par l'API
- const HTTP_METHODS = [ 'GET', 'POST', 'DELETE', 'PUT' ];
+ //Les méthodes HTTP gérée par l'API
+ const HTTP_METHODS = [ 'GET', 'POST', 'DELETE', 'PUT' ];
- /**
- * APIRouter constructor.
- *
- * @param string $file
- * @param string $action
- */
- public function __construct( string $file, string $action ) {
+ /**
+ * APIRouter constructor.
+ *
+ * @param string $file
+ * @param string $action
+ */
+ public function __construct( string $file, string $action ) {
- //On vérifie que les paramètres ne sont pas vides
- if ( empty( $file ) || empty( $action ) ) {
- new APIError();
- }
+ //On vérifie que les paramètres ne sont pas vides
+ if ( empty( $file ) || empty( $action ) ) {
+ new APIError();
+ }
- //On vérifie que la classe appelée existe
- $fileName = 'API' . ucfirst( $file );
- if ( ! file_exists( 'src/API/' . $fileName . '.php' ) ) {
- new APIError( 404 );
- }
+ //On vérifie que la classe appelée existe
+ $fileName = 'API' . ucfirst( $file );
+ if ( !file_exists( 'src/API/' . $fileName . '.php' ) ) {
+ new APIError( 404 );
+ }
- //On instancie la classe.
- $class = '\\' . Config::NAMESPACE . '\\API\\' . $fileName;
- $class = new $class( $action );
+ //On instancie la classe.
+ $class = '\\' . Config::NAMESPACE . '\\API\\' . $fileName;
+ $class = new $class( $action );
- //On vérifie que l'action demandé est déclarée
- if ( ! array_key_exists( $action, $class->getDeclaredFunctions() ) ) {
- new APIError( 404 );
- }
+ //On vérifie que l'action demandé est déclarée
+ if ( !array_key_exists( $action, $class->getDeclaredFunctions() ) ) {
+ new APIError( 404 );
+ }
- //On reconstruit le nom de la fonction
- $array = explode( '-', $action );
- foreach ( $array as $key => $value ) {
- if ( $key == 0 ) {
- $array[ $key ] = $value;
- } else {
- $array[ $key ] = ucfirst( $value );
- }
- }
- $function = implode( '', $array ); //le nom de la fonction
+ //On reconstruit le nom de la fonction
+ $array = explode( '-', $action );
+ foreach ( $array as $key => $value ) {
+ if ( $key == 0 ) {
+ $array[ $key ] = $value;
+ } else {
+ $array[ $key ] = ucfirst( $value );
+ }
+ }
+ $function = implode( '', $array ); //le nom de la fonction
- //On vérifie que la fonction existe dans la classe
- if ( ! method_exists( $class, $function ) ) {
- new APIError( 500, 'La fonction ' . $function . ' n\'existe pas dans la classe ' . get_class( $class ) );
- }
+ //On vérifie que la fonction existe dans la classe
+ if ( !method_exists( $class, $function ) ) {
+ new APIError( 500, 'La fonction ' . $function . ' n\'existe pas dans la classe ' . get_class( $class ) );
+ }
- //On vérifie que la méthode d'envoie est référencée
- $method = $class->getDeclaredFunctions()[ $action ]['method'];
- if ( ! in_array( $method, self::HTTP_METHODS ) ) {
- new APIError( 500, 'méthode http inconnue' );
- }
+ //On vérifie que la méthode d'envoie est référencée
+ $method = $class->getDeclaredFunctions()[ $action ][ 'method' ];
+ if ( !in_array( $method, self::HTTP_METHODS ) ) {
+ new APIError( 500, 'méthode http inconnue' );
+ }
- //On vérifie que la méthode requise et la mathode obtenue sont les même
- if ( $method != $_SERVER['REQUEST_METHOD'] ) {
- new APIError( 400, 'La méthode HTTP ne correspond pas à la méthode prévue' );
- }
+ //On vérifie que la méthode requise et la mathode obtenue sont les même
+ if ( $method != $_SERVER[ 'REQUEST_METHOD' ] ) {
+ new APIError( 400, 'La méthode HTTP ne correspond pas à la méthode prévue' );
+ }
- //On met les paramètres dans le tableau $data
- $params = $class->getDeclaredFunctions()[ $action ]['params'];
- $data = [];
- if ( ! empty( $params ) ) {
- if ( $method == 'DELETE' || $method == 'PUT' ) {
- parse_str( file_get_contents( 'php://input' ), $data );
- } else if ( $method == 'POST' ) {
- $data = $_POST;
- } else if ( $method == 'GET' ) {
- $data = $_GET;
- }
+ //On met les paramètres dans le tableau $data
+ $params = $class->getDeclaredFunctions()[ $action ][ 'params' ];
+ $data = [];
+ if ( !empty( $params ) ) {
+ if ( $method == 'DELETE' || $method == 'PUT' ) {
+ parse_str( file_get_contents( 'php://input' ), $data );
+ } else if ( $method == 'POST' ) {
+ $data = $_POST;
+ } else if ( $method == 'GET' ) {
+ $data = $_GET;
+ }
- //On boucle sur les paramètres de la doc de la fonction
- foreach ( $params as $p => $options ) {
- if ( ! isset( $options['required'] ) ) {
- $options['required'] = false;
- }
- //Si le paramètre est obligatoire et qu'il est vide ou non fourni on lève une erreur 400 BAD REQUEST
- if ( $options['required'] && ( ! array_key_exists( $p, $data ) || ( empty( $data[ $p ] ) && $data[ $p ] != '0' ) ) ) {
- $devMsg = 'Paramètre ' . $p . ' manquant';
- if ( isset( $options['devMsg'] ) ) {
- $devMsg = $options['devMsg'];
- }
- $publicMsg = 'Des paramètres obligatoires ne sont pas envoyés ou sont vides';
- if ( isset( $options['publicMsg'] ) ) {
- $publicMsg = $options['publicMsg'];
- }
- $code = '';
- if ( isset( $options['code'] ) ) {
- $code = $options['code'];
- }
- new APIError( 400, $devMsg, $publicMsg, $code );
- }
+ //On boucle sur les paramètres de la doc de la fonction
+ foreach ( $params as $p => $options ) {
+ if ( !isset( $options[ 'required' ] ) ) {
+ $options[ 'required' ] = false;
+ }
+ //Si le paramètre est obligatoire et qu'il est vide ou non fourni on lève une erreur 400 BAD REQUEST
+ if ( $options[ 'required' ] && ( !array_key_exists( $p, $data ) || ( empty( $data[ $p ] ) && $data[ $p ] != '0' ) ) ) {
+ $devMsg = 'Paramètre ' . $p . ' manquant';
+ if ( isset( $options[ 'devMsg' ] ) ) {
+ $devMsg = $options[ 'devMsg' ];
+ }
+ $publicMsg = 'Des paramètres obligatoires ne sont pas envoyés ou sont vides';
+ if ( isset( $options[ 'publicMsg' ] ) ) {
+ $publicMsg = $options[ 'publicMsg' ];
+ }
+ $code = '';
+ if ( isset( $options[ 'code' ] ) ) {
+ $code = $options[ 'code' ];
+ }
+ new APIError( 400, $devMsg, $publicMsg, $code );
+ }
- //On vérifie que le type donné correspond au typage requis
- if ( isset( $options['type'] ) ) {
- if ( $options['type'] == 'int' ) {
- if ( ctype_digit( $data[ $p ] ) ) {
- $data[ $p ] = (int) $data[ $p ];
- } else {
- new APIError( 400, 'Le type donné ne correspond pas au type demandé pour le paramètre ' . $p . ' : string donné, ' . $options['type'] . ' requis' );
- }
- } else if ( $options['type'] == 'bool' ) {
- if ( $data[ $p ] == 'true' || $data[ $p ] == '1' ) {
- $data[ $p ] = true;
- } else if ( $data[ $p ] == 'false' || $data[ $p ] == '0' ) {
- $data[ $p ] = false;
- } else {
- new APIError( 400, 'Le type donné ne correspond pas au type demandé pour le paramètre ' . $p . ' : string donné, ' . $options['type'] . ' requis' );
- }
- }
- }
+ //On vérifie que le type donné correspond au typage requis
+ if ( isset( $options[ 'type' ] ) ) {
+ if ( $options[ 'type' ] == 'int' ) {
+ if ( ctype_digit( $data[ $p ] ) ) {
+ $data[ $p ] = (int)$data[ $p ];
+ } else {
+ new APIError( 400, 'Le type donné ne correspond pas au type demandé pour le paramètre ' . $p . ' : string donné, ' . $options[ 'type' ] . ' requis' );
+ }
+ } else if ( $options[ 'type' ] == 'bool' ) {
+ if ( $data[ $p ] == 'true' || $data[ $p ] == '1' ) {
+ $data[ $p ] = true;
+ } else if ( $data[ $p ] == 'false' || $data[ $p ] == '0' ) {
+ $data[ $p ] = false;
+ } else {
+ new APIError( 400, 'Le type donné ne correspond pas au type demandé pour le paramètre ' . $p . ' : string donné, ' . $options[ 'type' ] . ' requis' );
+ }
+ }
+ }
- //Si un paramètre non obligatoire n'est pas donné par l'utilisateur on lui donne la valeur par défaut d'une chaine vide
- if ( ! array_key_exists( $p, $data ) ) {
- $data[ $p ] = '';
- }
- }
- }
+ //Si un paramètre non obligatoire n'est pas donné par l'utilisateur on lui donne la valeur par défaut d'une chaine vide
+ if ( !array_key_exists( $p, $data ) ) {
+ $data[ $p ] = '';
+ }
+ }
+ }
- //On appelle la fonction correspondante pour le traitement
- $class->$function( $data );
- }
+ //On appelle la fonction correspondante pour le traitement
+ $class->$function( $data );
+ }
}
?>
\ No newline at end of file
diff --git a/src/Autoloader.php b/src/Autoloader.php
index 659e755..4be7172 100644
--- a/src/Autoloader.php
+++ b/src/Autoloader.php
@@ -4,21 +4,21 @@ namespace CAUProject3Contact;
class Autoloader {
- /**
- * Enregistre notre autoloader
- */
- static function register() {
- spl_autoload_register( array( __CLASS__, 'autoload' ) );
- }
+ /**
+ * Enregistre notre autoloader
+ */
+ static function register() {
+ spl_autoload_register( array( __CLASS__, 'autoload' ) );
+ }
- /**
- * Inclue le fichier correspondant à notre classe
- *
- * @param $class string Le nom de la classe à charger
- */
- static function autoload( $class ) {
- if ( preg_match( '#^' . Config::NAMESPACE . '\\\(.+)$#', $class, $matches ) ) {
- require 'src/' . str_replace( '\\', '/', $matches[1] ) . '.php';
- }
- }
+ /**
+ * Inclue le fichier correspondant à notre classe
+ *
+ * @param $class string Le nom de la classe à charger
+ */
+ static function autoload( $class ) {
+ if ( preg_match( '#^' . Config::NAMESPACE . '\\\(.+)$#', $class, $matches ) ) {
+ require 'src/' . str_replace( '\\', '/', $matches[ 1 ] ) . '.php';
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Config.php b/src/Config.php
index 82430bc..a7137e7 100644
--- a/src/Config.php
+++ b/src/Config.php
@@ -3,12 +3,12 @@
namespace CAUProject3Contact;
class Config {
- const SITE_JS_VERSION = '1.00';
- const SITE_CSS_VERSION = '1.00';
+ const SITE_JS_VERSION = '0.01';
+ const SITE_CSS_VERSION = '0.01';
- const TITLE_HEADER = 'Your contact';
- const DESCRIPTION_HEADER = 'Manage your contact easly';
- const NAMESPACE = 'CAUProject3Contact';
+ const TITLE_HEADER = 'Your contact';
+ const DESCRIPTION_HEADER = 'Manage your contact easly';
+ const NAMESPACE = 'CAUProject3Contact';
- const FAVICON_PATH = '/img/favicon.png';
+ const FAVICON_PATH = '/img/favicon.ico';
}
\ No newline at end of file
diff --git a/src/Controller/Controller.php b/src/Controller/Controller.php
index 0c7d2b6..c1a7984 100644
--- a/src/Controller/Controller.php
+++ b/src/Controller/Controller.php
@@ -6,35 +6,35 @@ use CAUProject3Contact\Config;
class Controller {
- private $data;
+ private $data;
- public function __construct() {
- $this->data = [];
- }
+ public function __construct() {
+ $this->data = [];
+ }
- protected function view() {
- ob_start();
- extract( $this->data );
- require 'src/View/' . str_replace( '\\', '/', preg_replace( '#^' . Config::NAMESPACE . '\\\Controller\\\#', '', get_class( $this ) ) ) . '.php';
- ob_end_flush();
- exit();
- }
+ protected function view() {
+ ob_start();
+ extract( $this->data );
+ require 'src/View/' . str_replace( '\\', '/', preg_replace( '#^' . Config::NAMESPACE . '\\\Controller\\\#', '', get_class( $this ) ) ) . '.php';
+ ob_end_flush();
+ exit();
+ }
- protected function addData( $data ) {
- $this->data += $data;
- }
+ protected function addData( $data ) {
+ $this->data += $data;
+ }
- protected function returnJson( $data ) {
- header( 'Content-Type: application/json' );
- echo json_encode( $data );
- exit();
- }
+ protected function returnJson( $data ) {
+ header( 'Content-Type: application/json' );
+ echo json_encode( $data );
+ exit();
+ }
- protected function throwError( $msg, $code = '' ) {
- header( 'Content-Type: application/json' );
- echo json_encode( [ 'status' => 'echec', 'msg' => $msg, 'code' => $code ], JSON_PRETTY_PRINT );
- exit();
- }
+ protected function throwError( $msg, $code = '' ) {
+ header( 'Content-Type: application/json' );
+ echo json_encode( [ 'status' => 'echec', 'msg' => $msg, 'code' => $code ], JSON_PRETTY_PRINT );
+ exit();
+ }
}
?>
\ No newline at end of file
diff --git a/src/Controller/ControllerSite.php b/src/Controller/ControllerSite.php
index 1a3f798..be653ce 100644
--- a/src/Controller/ControllerSite.php
+++ b/src/Controller/ControllerSite.php
@@ -6,67 +6,67 @@ use CAUProject3Contact\Config;
class ControllerSite {
- private $data;
- private $head;
- private $footer;
+ private $data;
+ private $head;
+ private $footer;
- public function __construct() {
- $this->data = [];
- $this->head = [];
- $this->footer = [];
- }
+ public function __construct() {
+ $this->data = [];
+ $this->head = [];
+ $this->footer = [];
+ }
- protected function view( $header = true, $footer = true ) {
- ob_start();
- extract( $this->data );
+ protected function view( $header = true, $footer = true ) {
+ ob_start();
+ extract( $this->data );
- if ( empty( $this->head['title'] ) ) {
- $this->head['title'] = Config::TITLE_HEADER;
- }
- if ( empty( $this->head['description'] ) ) {
- $this->head['description'] = Config::DESCRIPTION_HEADER;
- }
- if ( $header ) {
- require 'src/View/Site/tpl/head.php';
- }
+ if ( empty( $this->head[ 'title' ] ) ) {
+ $this->head[ 'title' ] = Config::TITLE_HEADER;
+ }
+ if ( empty( $this->head[ 'description' ] ) ) {
+ $this->head[ 'description' ] = Config::DESCRIPTION_HEADER;
+ }
+ if ( $header ) {
+ require 'src/View/Site/tpl/head.php';
+ }
- require 'src/View/' . str_replace( '\\', '/', preg_replace( '#^' . Config::NAMESPACE . '\\\Controller\\\#', '', get_class( $this ) ) ) . '.php';
+ require 'src/View/' . str_replace( '\\', '/', preg_replace( '#^' . Config::NAMESPACE . '\\\Controller\\\#', '', get_class( $this ) ) ) . '.php';
- if ( $footer ) {
- require 'src/View/Site/tpl/footer.php';
- }
+ if ( $footer ) {
+ require 'src/View/Site/tpl/footer.php';
+ }
- ob_end_flush();
- exit();
- }
+ ob_end_flush();
+ exit();
+ }
- protected function addHead( $head ) {
- $this->head += $head;
- }
+ protected function addHead( $head ) {
+ $this->head += $head;
+ }
- protected function addData( $data ) {
- $this->data += $data;
- }
+ protected function addData( $data ) {
+ $this->data += $data;
+ }
- protected function addFooter( $footer ) {
- $this->footer += $footer;
- }
+ protected function addFooter( $footer ) {
+ $this->footer += $footer;
+ }
- /**
- * @param $data
- */
- protected function returnJson( $data ) {
- header( 'Content-Type: application/json' );
- echo json_encode( $data );
- exit();
- }
+ /**
+ * @param $data
+ */
+ protected function returnJson( $data ) {
+ header( 'Content-Type: application/json' );
+ echo json_encode( $data );
+ exit();
+ }
- protected function throwError( $msg, $code = '' ) {
- header( 'Content-Type: application/json' );
- echo json_encode( [ 'status' => 'echec', 'msg' => $msg, 'code' => $code ], JSON_PRETTY_PRINT );
- exit();
- }
+ protected function throwError( $msg, $code = '' ) {
+ header( 'Content-Type: application/json' );
+ echo json_encode( [ 'status' => 'echec', 'msg' => $msg, 'code' => $code ], JSON_PRETTY_PRINT );
+ exit();
+ }
}
?>
\ No newline at end of file
diff --git a/src/Controller/Error.php b/src/Controller/Error.php
index 2ed3cbd..089d27a 100644
--- a/src/Controller/Error.php
+++ b/src/Controller/Error.php
@@ -4,96 +4,96 @@ namespace CAUProject3Contact\Controller;
class Error extends Controller {
- /**
- * Error constructor.
- *
- * @param int $ErrCode
- * @param string $message
- */
- public function __construct( $ErrCode = 500, $message = '' ) {
+ /**
+ * Error constructor.
+ *
+ * @param int $ErrCode
+ * @param string $message
+ */
+ public function __construct( $ErrCode = 500, $message = '' ) {
- parent::__construct();
+ parent::__construct();
- $tabCode = [
- //Informational 1xx
- 100 => [ 'label' => '100 Continue', 'msg' => '100 Continue' ],
- 101 => [ 'label' => '101 Switching Protocols', 'msg' => '101 Switching Protocols' ],
- //Successful 2xx
- 200 => [ 'label' => '200 OK', 'msg' => '200 OK' ],
- 201 => [ 'label' => '201 Created', 'msg' => '201 Created' ],
- 202 => [ 'label' => '202 Accepted', 'msg' => '202 Accepted' ],
- 203 => [ 'label' => '203 Non-Authoritative Information', 'msg' => '203 Non-Authoritative Information' ],
- 204 => [ 'label' => '204 No Content', 'msg' => '204 No Content' ],
- 205 => [ 'label' => '205 Reset Content', 'msg' => '205 Reset Content' ],
- 206 => [ 'label' => '206 Partial Content', 'msg' => '206 Partial Content' ],
- 226 => [ 'label' => '226 IM Used', 'msg' => '226 IM Used' ],
- //Redirection 3xx
- 300 => [ 'label' => '300 Multiple Choices', 'msg' => '300 Multiple Choices' ],
- 301 => [ 'label' => '301 Moved Permanently', 'msg' => '301 Moved Permanently' ],
- 302 => [ 'label' => '302 Found', 'msg' => '302 Found' ],
- 303 => [ 'label' => '303 See Other', 'msg' => '303 See Other' ],
- 304 => [ 'label' => '304 Not Modified', 'msg' => '304 Not Modified' ],
- 305 => [ 'label' => '305 Use Proxy', 'msg' => '305 Use Proxy' ],
- 306 => [ 'label' => '306 (Unused)', 'msg' => '306 (Unused)' ],
- 307 => [ 'label' => '307 Temporary Redirect', 'msg' => '307 Temporary Redirect' ],
- //Client Error 4xx
- 400 => [ 'label' => '400 Bad Request', 'msg' => '400 Bad Request' ],
- 401 => [ 'label' => '401 Unauthorized', 'msg' => 'Vous n\'êtes pas autorisé à accéder à cette page' ],
- 402 => [ 'label' => '402 Payment Required', 'msg' => '402 Payment Required' ],
- 403 => [ 'label' => '403 Forbidden', 'msg' => '403 Forbidden' ],
- 404 => [ 'label' => '404 Not Found', 'msg' => 'Page non trouvée' ],
- 405 => [ 'label' => '405 Method Not Allowed', 'msg' => '405 Method Not Allowed' ],
- 406 => [ 'label' => '406 Not Acceptable', 'msg' => '406 Not Acceptable' ],
- 407 => [ 'label' => '407 Proxy Authentication Required', 'msg' => '407 Proxy Authentication Required' ],
- 408 => [ 'label' => '408 Request Timeout', 'msg' => '408 Request Timeout' ],
- 409 => [ 'label' => '409 Conflict', 'msg' => '409 Conflict' ],
- 410 => [ 'label' => '410 Gone', 'msg' => '410 Gone' ],
- 411 => [ 'label' => '411 Length Required', 'msg' => '411 Length Required' ],
- 412 => [ 'label' => '412 Precondition Failed', 'msg' => '412 Precondition Failed' ],
- 413 => [ 'label' => '413 Request Entity Too Large', 'msg' => '413 Request Entity Too Large' ],
- 414 => [ 'label' => '414 Request-URI Too Long', 'msg' => '414 Request-URI Too Long' ],
- 415 => [ 'label' => '415 Unsupported Media Type', 'msg' => '415 Unsupported Media Type' ],
- 416 => [ 'label' => '416 Requested Range Not Satisfiable', 'msg' => '416 Requested Range Not Satisfiable' ],
- 417 => [ 'label' => '417 Expectation Failed', 'msg' => '417 Expectation Failed' ],
- 418 => [ 'label' => '418 I\'m a teapot', 'msg' => '418 I\'m a teapot' ],
- 422 => [ 'label' => '422 Unprocessable Entity', 'msg' => '422 Unprocessable Entity' ],
- 423 => [ 'label' => '423 Locked', 'msg' => '423 Locked' ],
- 426 => [ 'label' => '426 Upgrade Required', 'msg' => '426 Upgrade Required' ],
- 428 => [ 'label' => '428 Precondition Required', 'msg' => '428 Precondition Required' ],
- 429 => [ 'label' => '429 Too Many Requests', 'msg' => '429 Too Many Requests' ],
- 431 => [ 'label' => '431 Request Header Fields Too Large', 'msg' => '431 Request Header Fields Too Large' ],
- //Server Error 5xx
- 500 => [ 'label' => '500 Internal Server Error', 'msg' => 'Une erreur est survenue' ],
- 501 => [ 'label' => '501 Not Implemented', 'msg' => '501 Not Implemented' ],
- 502 => [ 'label' => '502 Bad Gateway', 'msg' => '502 Bad Gateway' ],
- 503 => [ 'label' => '503 Service Unavailable', 'msg' => '503 Service Unavailable' ],
- 504 => [ 'label' => '504 Gateway Timeout', 'msg' => '504 Gateway Timeout' ],
- 505 => [ 'label' => '505 HTTP Version Not Supported', 'msg' => '505 HTTP Version Not Supported' ],
- 506 => [ 'label' => '506 Variant Also Negotiates', 'msg' => '506 Variant Also Negotiates' ],
- 510 => [ 'label' => '510 Not Extended', 'msg' => '510 Not Extended' ],
- 511 => [ 'label' => '511 Network Authentication Required', 'msg' => '511 Network Authentication Required' ]
- ];
+ $tabCode = [
+ //Informational 1xx
+ 100 => [ 'label' => '100 Continue', 'msg' => '100 Continue' ],
+ 101 => [ 'label' => '101 Switching Protocols', 'msg' => '101 Switching Protocols' ],
+ //Successful 2xx
+ 200 => [ 'label' => '200 OK', 'msg' => '200 OK' ],
+ 201 => [ 'label' => '201 Created', 'msg' => '201 Created' ],
+ 202 => [ 'label' => '202 Accepted', 'msg' => '202 Accepted' ],
+ 203 => [ 'label' => '203 Non-Authoritative Information', 'msg' => '203 Non-Authoritative Information' ],
+ 204 => [ 'label' => '204 No Content', 'msg' => '204 No Content' ],
+ 205 => [ 'label' => '205 Reset Content', 'msg' => '205 Reset Content' ],
+ 206 => [ 'label' => '206 Partial Content', 'msg' => '206 Partial Content' ],
+ 226 => [ 'label' => '226 IM Used', 'msg' => '226 IM Used' ],
+ //Redirection 3xx
+ 300 => [ 'label' => '300 Multiple Choices', 'msg' => '300 Multiple Choices' ],
+ 301 => [ 'label' => '301 Moved Permanently', 'msg' => '301 Moved Permanently' ],
+ 302 => [ 'label' => '302 Found', 'msg' => '302 Found' ],
+ 303 => [ 'label' => '303 See Other', 'msg' => '303 See Other' ],
+ 304 => [ 'label' => '304 Not Modified', 'msg' => '304 Not Modified' ],
+ 305 => [ 'label' => '305 Use Proxy', 'msg' => '305 Use Proxy' ],
+ 306 => [ 'label' => '306 (Unused)', 'msg' => '306 (Unused)' ],
+ 307 => [ 'label' => '307 Temporary Redirect', 'msg' => '307 Temporary Redirect' ],
+ //Client Error 4xx
+ 400 => [ 'label' => '400 Bad Request', 'msg' => '400 Bad Request' ],
+ 401 => [ 'label' => '401 Unauthorized', 'msg' => 'Vous n\'êtes pas autorisé à accéder à cette page' ],
+ 402 => [ 'label' => '402 Payment Required', 'msg' => '402 Payment Required' ],
+ 403 => [ 'label' => '403 Forbidden', 'msg' => '403 Forbidden' ],
+ 404 => [ 'label' => '404 Not Found', 'msg' => 'Page non trouvée' ],
+ 405 => [ 'label' => '405 Method Not Allowed', 'msg' => '405 Method Not Allowed' ],
+ 406 => [ 'label' => '406 Not Acceptable', 'msg' => '406 Not Acceptable' ],
+ 407 => [ 'label' => '407 Proxy Authentication Required', 'msg' => '407 Proxy Authentication Required' ],
+ 408 => [ 'label' => '408 Request Timeout', 'msg' => '408 Request Timeout' ],
+ 409 => [ 'label' => '409 Conflict', 'msg' => '409 Conflict' ],
+ 410 => [ 'label' => '410 Gone', 'msg' => '410 Gone' ],
+ 411 => [ 'label' => '411 Length Required', 'msg' => '411 Length Required' ],
+ 412 => [ 'label' => '412 Precondition Failed', 'msg' => '412 Precondition Failed' ],
+ 413 => [ 'label' => '413 Request Entity Too Large', 'msg' => '413 Request Entity Too Large' ],
+ 414 => [ 'label' => '414 Request-URI Too Long', 'msg' => '414 Request-URI Too Long' ],
+ 415 => [ 'label' => '415 Unsupported Media Type', 'msg' => '415 Unsupported Media Type' ],
+ 416 => [ 'label' => '416 Requested Range Not Satisfiable', 'msg' => '416 Requested Range Not Satisfiable' ],
+ 417 => [ 'label' => '417 Expectation Failed', 'msg' => '417 Expectation Failed' ],
+ 418 => [ 'label' => '418 I\'m a teapot', 'msg' => '418 I\'m a teapot' ],
+ 422 => [ 'label' => '422 Unprocessable Entity', 'msg' => '422 Unprocessable Entity' ],
+ 423 => [ 'label' => '423 Locked', 'msg' => '423 Locked' ],
+ 426 => [ 'label' => '426 Upgrade Required', 'msg' => '426 Upgrade Required' ],
+ 428 => [ 'label' => '428 Precondition Required', 'msg' => '428 Precondition Required' ],
+ 429 => [ 'label' => '429 Too Many Requests', 'msg' => '429 Too Many Requests' ],
+ 431 => [ 'label' => '431 Request Header Fields Too Large', 'msg' => '431 Request Header Fields Too Large' ],
+ //Server Error 5xx
+ 500 => [ 'label' => '500 Internal Server Error', 'msg' => 'Une erreur est survenue' ],
+ 501 => [ 'label' => '501 Not Implemented', 'msg' => '501 Not Implemented' ],
+ 502 => [ 'label' => '502 Bad Gateway', 'msg' => '502 Bad Gateway' ],
+ 503 => [ 'label' => '503 Service Unavailable', 'msg' => '503 Service Unavailable' ],
+ 504 => [ 'label' => '504 Gateway Timeout', 'msg' => '504 Gateway Timeout' ],
+ 505 => [ 'label' => '505 HTTP Version Not Supported', 'msg' => '505 HTTP Version Not Supported' ],
+ 506 => [ 'label' => '506 Variant Also Negotiates', 'msg' => '506 Variant Also Negotiates' ],
+ 510 => [ 'label' => '510 Not Extended', 'msg' => '510 Not Extended' ],
+ 511 => [ 'label' => '511 Network Authentication Required', 'msg' => '511 Network Authentication Required' ]
+ ];
- if ( isset( $tabCode[ $ErrCode ] ) ) {
- header( $_SERVER['SERVER_PROTOCOL'] . ' ' . $tabCode[ $ErrCode ]['label'] );
- if ( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) == 'xmlhttprequest' ) {
- $this->throwError( $tabCode[ $ErrCode ]['msg'], 'Erreur_' . $ErrCode );
- }
- $erreur = $tabCode[ $ErrCode ]['msg'];
- $this->addData( [ 'erreur' => $erreur ] );
- } else {
- header( $_SERVER['SERVER_PROTOCOL'] . ' ' . $tabCode[500]['label'] );
- if ( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) == 'xmlhttprequest' ) {
- $this->throwError( $tabCode[500]['msg'], 'Erreur_500' );
- }
- $erreur = $tabCode[500]['msg'];
- $this->addData( [ 'erreur' => $erreur ] );
- }
+ if ( isset( $tabCode[ $ErrCode ] ) ) {
+ header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' ' . $tabCode[ $ErrCode ][ 'label' ] );
+ if ( isset( $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] ) && strtolower( $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] ) == 'xmlhttprequest' ) {
+ $this->throwError( $tabCode[ $ErrCode ][ 'msg' ], 'Erreur_' . $ErrCode );
+ }
+ $erreur = $tabCode[ $ErrCode ][ 'msg' ];
+ $this->addData( [ 'erreur' => $erreur ] );
+ } else {
+ header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' ' . $tabCode[ 500 ][ 'label' ] );
+ if ( isset( $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] ) && strtolower( $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] ) == 'xmlhttprequest' ) {
+ $this->throwError( $tabCode[ 500 ][ 'msg' ], 'Erreur_500' );
+ }
+ $erreur = $tabCode[ 500 ][ 'msg' ];
+ $this->addData( [ 'erreur' => $erreur ] );
+ }
- if ( ! empty( $message ) ) {
- $this->addData( [ 'message' => $message ] );
- }
+ if ( !empty( $message ) ) {
+ $this->addData( [ 'message' => $message ] );
+ }
- $this->view();
- }
+ $this->view();
+ }
}
\ No newline at end of file
diff --git a/src/Controller/site/Index.php b/src/Controller/site/Index.php
index f56c0ff..5dd9ecd 100644
--- a/src/Controller/site/Index.php
+++ b/src/Controller/site/Index.php
@@ -3,25 +3,27 @@
namespace CAUProject3Contact\Controller\Site;
use CAUProject3Contact\Controller\ControllerSite;
+use CAUProject3Contact\Model\Contact;
class Index extends ControllerSite {
- /**
- * Index constructor.
- */
- public function __construct() {
- parent::__construct();
+ /**
+ * Index constructor.
+ */
+ public function __construct() {
+ parent::__construct();
- $this->addHead( [
- ] );
+ $this->addHead( [] );
- $this->addFooter( [
+ $this->addFooter( [] );
- ] );
+ $contacts = Contact::getAllContact();
- $this->addData( [] );
- $this->view();
- }
+ $this->addData( [
+ "contacts" => $contacts
+ ] );
+ $this->view();
+ }
}
?>
\ No newline at end of file
diff --git a/src/Controller/site/SiteError.php b/src/Controller/site/SiteError.php
index b1b8130..7f53931 100644
--- a/src/Controller/site/SiteError.php
+++ b/src/Controller/site/SiteError.php
@@ -6,101 +6,101 @@ use CAUProject3Contact\Controller\ControllerSite;
class SiteError extends ControllerSite {
- /**
- * SiteError constructor.
- *
- * @param int $ErrCode
- * @param string $message
- */
- public function __construct( $ErrCode = 500, $message = '' ) {
- parent::__construct();
+ /**
+ * SiteError constructor.
+ *
+ * @param int $ErrCode
+ * @param string $message
+ */
+ public function __construct( $ErrCode = 500, $message = '' ) {
+ parent::__construct();
- $tabCode = [
- //Informational 1xx
- 100 => [ 'label' => '100 Continue', 'msg' => '100 Continue' ],
- 101 => [ 'label' => '101 Switching Protocols', 'msg' => '101 Switching Protocols' ],
- //Successful 2xx
- 200 => [ 'label' => '200 OK', 'msg' => '200 OK' ],
- 201 => [ 'label' => '201 Created', 'msg' => '201 Created' ],
- 202 => [ 'label' => '202 Accepted', 'msg' => '202 Accepted' ],
- 203 => [ 'label' => '203 Non-Authoritative Information', 'msg' => '203 Non-Authoritative Information' ],
- 204 => [ 'label' => '204 No Content', 'msg' => '204 No Content' ],
- 205 => [ 'label' => '205 Reset Content', 'msg' => '205 Reset Content' ],
- 206 => [ 'label' => '206 Partial Content', 'msg' => '206 Partial Content' ],
- 226 => [ 'label' => '226 IM Used', 'msg' => '226 IM Used' ],
- //Redirection 3xx
- 300 => [ 'label' => '300 Multiple Choices', 'msg' => '300 Multiple Choices' ],
- 301 => [ 'label' => '301 Moved Permanently', 'msg' => '301 Moved Permanently' ],
- 302 => [ 'label' => '302 Found', 'msg' => '302 Found' ],
- 303 => [ 'label' => '303 See Other', 'msg' => '303 See Other' ],
- 304 => [ 'label' => '304 Not Modified', 'msg' => '304 Not Modified' ],
- 305 => [ 'label' => '305 Use Proxy', 'msg' => '305 Use Proxy' ],
- 306 => [ 'label' => '306 (Unused)', 'msg' => '306 (Unused)' ],
- 307 => [ 'label' => '307 Temporary Redirect', 'msg' => '307 Temporary Redirect' ],
- //Client Error 4xx
- 400 => [ 'label' => '400 Bad Request', 'msg' => '400 Bad Request' ],
- 401 => [ 'label' => '401 Unauthorized', 'msg' => '401 Unauthorized' ],
- 402 => [ 'label' => '402 Payment Required', 'msg' => '402 Payment Required' ],
- 403 => [ 'label' => '403 Forbidden', 'msg' => '403 Forbidden' ],
- 404 => [ 'label' => '404 Not Found', 'msg' => 'Page non trouvée' ],
- 405 => [ 'label' => '405 Method Not Allowed', 'msg' => '405 Method Not Allowed' ],
- 406 => [ 'label' => '406 Not Acceptable', 'msg' => '406 Not Acceptable' ],
- 407 => [ 'label' => '407 Proxy Authentication Required', 'msg' => '407 Proxy Authentication Required' ],
- 408 => [ 'label' => '408 Request Timeout', 'msg' => '408 Request Timeout' ],
- 409 => [ 'label' => '409 Conflict', 'msg' => '409 Conflict' ],
- 410 => [ 'label' => '410 Gone', 'msg' => 'Cet artisan n\'est plus plus référencé sur Eldotravo' ],
- 411 => [ 'label' => '411 Length Required', 'msg' => '411 Length Required' ],
- 412 => [ 'label' => '412 Precondition Failed', 'msg' => '412 Precondition Failed' ],
- 413 => [ 'label' => '413 Request Entity Too Large', 'msg' => '413 Request Entity Too Large' ],
- 414 => [ 'label' => '414 Request-URI Too Long', 'msg' => '414 Request-URI Too Long' ],
- 415 => [ 'label' => '415 Unsupported Media Type', 'msg' => '415 Unsupported Media Type' ],
- 416 => [ 'label' => '416 Requested Range Not Satisfiable', 'msg' => '416 Requested Range Not Satisfiable' ],
- 417 => [ 'label' => '417 Expectation Failed', 'msg' => '417 Expectation Failed' ],
- 418 => [ 'label' => '418 I\'m a teapot', 'msg' => '418 I\'m a teapot' ],
- 422 => [ 'label' => '422 Unprocessable Entity', 'msg' => '422 Unprocessable Entity' ],
- 423 => [ 'label' => '423 Locked', 'msg' => '423 Locked' ],
- 426 => [ 'label' => '426 Upgrade Required', 'msg' => '426 Upgrade Required' ],
- 428 => [ 'label' => '428 Precondition Required', 'msg' => '428 Precondition Required' ],
- 429 => [ 'label' => '429 Too Many Requests', 'msg' => '429 Too Many Requests' ],
- 431 => [ 'label' => '431 Request Header Fields Too Large', 'msg' => '431 Request Header Fields Too Large' ],
- //Server Error 5xx
- 500 => [ 'label' => '500 Internal Server Error', 'msg' => 'Une erreur est survenue' ],
- 501 => [ 'label' => '501 Not Implemented', 'msg' => '501 Not Implemented' ],
- 502 => [ 'label' => '502 Bad Gateway', 'msg' => '502 Bad Gateway' ],
- 503 => [ 'label' => '503 Service Unavailable', 'msg' => '503 Service Unavailable' ],
- 504 => [ 'label' => '504 Gateway Timeout', 'msg' => '504 Gateway Timeout' ],
- 505 => [ 'label' => '505 HTTP Version Not Supported', 'msg' => '505 HTTP Version Not Supported' ],
- 506 => [ 'label' => '506 Variant Also Negotiates', 'msg' => '506 Variant Also Negotiates' ],
- 510 => [ 'label' => '510 Not Extended', 'msg' => '510 Not Extended' ],
- 511 => [ 'label' => '511 Network Authentication Required', 'msg' => '511 Network Authentication Required' ]
- ];
+ $tabCode = [
+ //Informational 1xx
+ 100 => [ 'label' => '100 Continue', 'msg' => '100 Continue' ],
+ 101 => [ 'label' => '101 Switching Protocols', 'msg' => '101 Switching Protocols' ],
+ //Successful 2xx
+ 200 => [ 'label' => '200 OK', 'msg' => '200 OK' ],
+ 201 => [ 'label' => '201 Created', 'msg' => '201 Created' ],
+ 202 => [ 'label' => '202 Accepted', 'msg' => '202 Accepted' ],
+ 203 => [ 'label' => '203 Non-Authoritative Information', 'msg' => '203 Non-Authoritative Information' ],
+ 204 => [ 'label' => '204 No Content', 'msg' => '204 No Content' ],
+ 205 => [ 'label' => '205 Reset Content', 'msg' => '205 Reset Content' ],
+ 206 => [ 'label' => '206 Partial Content', 'msg' => '206 Partial Content' ],
+ 226 => [ 'label' => '226 IM Used', 'msg' => '226 IM Used' ],
+ //Redirection 3xx
+ 300 => [ 'label' => '300 Multiple Choices', 'msg' => '300 Multiple Choices' ],
+ 301 => [ 'label' => '301 Moved Permanently', 'msg' => '301 Moved Permanently' ],
+ 302 => [ 'label' => '302 Found', 'msg' => '302 Found' ],
+ 303 => [ 'label' => '303 See Other', 'msg' => '303 See Other' ],
+ 304 => [ 'label' => '304 Not Modified', 'msg' => '304 Not Modified' ],
+ 305 => [ 'label' => '305 Use Proxy', 'msg' => '305 Use Proxy' ],
+ 306 => [ 'label' => '306 (Unused)', 'msg' => '306 (Unused)' ],
+ 307 => [ 'label' => '307 Temporary Redirect', 'msg' => '307 Temporary Redirect' ],
+ //Client Error 4xx
+ 400 => [ 'label' => '400 Bad Request', 'msg' => '400 Bad Request' ],
+ 401 => [ 'label' => '401 Unauthorized', 'msg' => '401 Unauthorized' ],
+ 402 => [ 'label' => '402 Payment Required', 'msg' => '402 Payment Required' ],
+ 403 => [ 'label' => '403 Forbidden', 'msg' => '403 Forbidden' ],
+ 404 => [ 'label' => '404 Not Found', 'msg' => 'Page non trouvée' ],
+ 405 => [ 'label' => '405 Method Not Allowed', 'msg' => '405 Method Not Allowed' ],
+ 406 => [ 'label' => '406 Not Acceptable', 'msg' => '406 Not Acceptable' ],
+ 407 => [ 'label' => '407 Proxy Authentication Required', 'msg' => '407 Proxy Authentication Required' ],
+ 408 => [ 'label' => '408 Request Timeout', 'msg' => '408 Request Timeout' ],
+ 409 => [ 'label' => '409 Conflict', 'msg' => '409 Conflict' ],
+ 410 => [ 'label' => '410 Gone', 'msg' => 'Cet artisan n\'est plus plus référencé sur Eldotravo' ],
+ 411 => [ 'label' => '411 Length Required', 'msg' => '411 Length Required' ],
+ 412 => [ 'label' => '412 Precondition Failed', 'msg' => '412 Precondition Failed' ],
+ 413 => [ 'label' => '413 Request Entity Too Large', 'msg' => '413 Request Entity Too Large' ],
+ 414 => [ 'label' => '414 Request-URI Too Long', 'msg' => '414 Request-URI Too Long' ],
+ 415 => [ 'label' => '415 Unsupported Media Type', 'msg' => '415 Unsupported Media Type' ],
+ 416 => [ 'label' => '416 Requested Range Not Satisfiable', 'msg' => '416 Requested Range Not Satisfiable' ],
+ 417 => [ 'label' => '417 Expectation Failed', 'msg' => '417 Expectation Failed' ],
+ 418 => [ 'label' => '418 I\'m a teapot', 'msg' => '418 I\'m a teapot' ],
+ 422 => [ 'label' => '422 Unprocessable Entity', 'msg' => '422 Unprocessable Entity' ],
+ 423 => [ 'label' => '423 Locked', 'msg' => '423 Locked' ],
+ 426 => [ 'label' => '426 Upgrade Required', 'msg' => '426 Upgrade Required' ],
+ 428 => [ 'label' => '428 Precondition Required', 'msg' => '428 Precondition Required' ],
+ 429 => [ 'label' => '429 Too Many Requests', 'msg' => '429 Too Many Requests' ],
+ 431 => [ 'label' => '431 Request Header Fields Too Large', 'msg' => '431 Request Header Fields Too Large' ],
+ //Server Error 5xx
+ 500 => [ 'label' => '500 Internal Server Error', 'msg' => 'Une erreur est survenue' ],
+ 501 => [ 'label' => '501 Not Implemented', 'msg' => '501 Not Implemented' ],
+ 502 => [ 'label' => '502 Bad Gateway', 'msg' => '502 Bad Gateway' ],
+ 503 => [ 'label' => '503 Service Unavailable', 'msg' => '503 Service Unavailable' ],
+ 504 => [ 'label' => '504 Gateway Timeout', 'msg' => '504 Gateway Timeout' ],
+ 505 => [ 'label' => '505 HTTP Version Not Supported', 'msg' => '505 HTTP Version Not Supported' ],
+ 506 => [ 'label' => '506 Variant Also Negotiates', 'msg' => '506 Variant Also Negotiates' ],
+ 510 => [ 'label' => '510 Not Extended', 'msg' => '510 Not Extended' ],
+ 511 => [ 'label' => '511 Network Authentication Required', 'msg' => '511 Network Authentication Required' ]
+ ];
- if ( isset( $tabCode[ $ErrCode ] ) ) {
- header( $_SERVER['SERVER_PROTOCOL'] . ' ' . $tabCode[ $ErrCode ]['label'] );
- if ( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) == 'xmlhttprequest' ) {
- $this->throwError( 'Erreur ' . $tabCode[ $ErrCode ]['label'], 'Erreur_' . $ErrCode );
- }
- $erreur = $tabCode[ $ErrCode ]['msg'];
- $this->addData( [ 'erreur' => $erreur ] );
- } else {
- header( $_SERVER['SERVER_PROTOCOL'] . ' ' . $tabCode[500]['label'] );
- if ( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) == 'xmlhttprequest' ) {
- $this->throwError( 'Erreur ' . $tabCode[500]['label'], 'Erreur_500' );
- }
- $erreur = $tabCode[500]['msg'];
- $this->addData( [ 'erreur' => $erreur ] );
- }
+ if ( isset( $tabCode[ $ErrCode ] ) ) {
+ header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' ' . $tabCode[ $ErrCode ][ 'label' ] );
+ if ( isset( $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] ) && strtolower( $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] ) == 'xmlhttprequest' ) {
+ $this->throwError( 'Erreur ' . $tabCode[ $ErrCode ][ 'label' ], 'Erreur_' . $ErrCode );
+ }
+ $erreur = $tabCode[ $ErrCode ][ 'msg' ];
+ $this->addData( [ 'erreur' => $erreur ] );
+ } else {
+ header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' ' . $tabCode[ 500 ][ 'label' ] );
+ if ( isset( $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] ) && strtolower( $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] ) == 'xmlhttprequest' ) {
+ $this->throwError( 'Erreur ' . $tabCode[ 500 ][ 'label' ], 'Erreur_500' );
+ }
+ $erreur = $tabCode[ 500 ][ 'msg' ];
+ $this->addData( [ 'erreur' => $erreur ] );
+ }
- if ( ! empty( $message ) ) {
- $this->addData( [ 'message' => $message ] );
- }
+ if ( !empty( $message ) ) {
+ $this->addData( [ 'message' => $message ] );
+ }
- $this->addHead( [
- 'title' => 'Un erreur est survenue',
- 'description' => 'Oops une erreur est survenue',
- 'robotNoIndex' => true
- ] );
+ $this->addHead( [
+ 'title' => 'Un erreur est survenue',
+ 'description' => 'Oops une erreur est survenue',
+ 'robotNoIndex' => true
+ ] );
- $this->view();
- }
+ $this->view();
+ }
}
\ No newline at end of file
diff --git a/src/Controller/site/SiteRouter.php b/src/Controller/site/SiteRouter.php
index f10df0b..8b1836a 100644
--- a/src/Controller/site/SiteRouter.php
+++ b/src/Controller/site/SiteRouter.php
@@ -4,21 +4,21 @@ namespace CAUProject3Contact\Controller\Site;
class SiteRouter {
- /**
- * SiteRouter constructor.
- *
- * @param $pages
- */
- public function __construct( $pages ) {
+ /**
+ * SiteRouter constructor.
+ *
+ * @param $pages
+ */
+ public function __construct( $pages ) {
- set_error_handler( 'errorHandler' );
+ set_error_handler( 'errorHandler' );
- if ( $pages[0] == '' ) {
- new Index();
- } else {
- new SiteError( 404 );
- }
- }
+ if ( $pages[ 0 ] == '' ) {
+ new Index();
+ } else {
+ new SiteError( 404 );
+ }
+ }
}
?>
\ No newline at end of file
diff --git a/src/Model/BDD.php b/src/Model/BDD.php
index 532830a..27fa205 100644
--- a/src/Model/BDD.php
+++ b/src/Model/BDD.php
@@ -6,35 +6,43 @@ use Exception;
use PDO;
class BDD {
- const SQL_SERVER = 'sql.sanchez-mathieu.fr'; // BDD Server
- const SQL_LOGIN = 'why7n0_contact'; // BDD Login
- const SQL_PASSWORD = 'fC3c87Gy'; // BDD Password
- const SQL_DB = 'why7n0_contact'; // BDD Name
- private static $bdd;
+ // Server BDD
+// const SQL_SERVER = 'sql.sanchez-mathieu.fr'; // BDD Server
+// const SQL_LOGIN = 'why7n0_contact'; // BDD Login
+// const SQL_PASSWORD = 'fC3c87Gy'; // BDD Password
+// const SQL_DB = 'why7n0_contact'; // BDD Name
- public function __construct() {
- try {
- $pdo_options = [
- PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING,
- PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4',
- PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
- ];
+ // Local BDD
+ const SQL_SERVER = 'localhost'; // BDD Server
+ const SQL_LOGIN = 'root'; // BDD Login
+ const SQL_PASSWORD = ''; // BDD Password
+ const SQL_DB = 'contact'; // BDD Name
- self::$bdd = new PDO( 'mysql:host=' . self::SQL_SERVER . ';dbname=' . self::SQL_DB . ';charset=utf8',
- self::SQL_LOGIN, self::SQL_PASSWORD, $pdo_options );
- } catch ( Exception $e ) {
- die( 'Erreur : ' . $e->getMessage() );
- }
- }
+ private static $bdd;
- public static function instance() {
- return self::$bdd;
- }
+ public function __construct() {
+ try {
+ $pdo_options = [
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING,
+ PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4',
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
+ ];
- public static function lastInsertId() {
- return self::$bdd->lastInsertId();
- }
+ self::$bdd = new PDO( 'mysql:host=' . self::SQL_SERVER . ';dbname=' . self::SQL_DB . ';charset=utf8',
+ self::SQL_LOGIN, self::SQL_PASSWORD, $pdo_options );
+ } catch ( Exception $e ) {
+ die( 'Erreur : ' . $e->getMessage() );
+ }
+ }
+
+ public static function instance() {
+ return self::$bdd;
+ }
+
+ public static function lastInsertId() {
+ return self::$bdd->lastInsertId();
+ }
}
?>
\ No newline at end of file
diff --git a/src/Model/BDTables.php b/src/Model/BDTables.php
index aa46dc4..c3f66e8 100644
--- a/src/Model/BDTables.php
+++ b/src/Model/BDTables.php
@@ -4,10 +4,11 @@ namespace CAUProject3Contact\Model;
abstract class BDTables {
- // const NOM = 'bd_nom'
- // Ex : const ABONNEMENT = 'abonnement';
+ // const NOM = 'bd_nom'
+ // Ex : const ABONNEMENT = 'abonnement';
- const LOGS = "logs";
+ const LOGS = "logs";
+ const CONTACT = "contact";
}
?>
\ No newline at end of file
diff --git a/src/Model/Contact.php b/src/Model/Contact.php
new file mode 100644
index 0000000..3bffba0
--- /dev/null
+++ b/src/Model/Contact.php
@@ -0,0 +1,220 @@
+id = $id;
+ $this->firstName = $firstName;
+ $this->lastName = $lastName;
+ $this->surname = $surname;
+ $this->email = $email;
+ $this->address = $address;
+ $this->phoneNumber = $phoneNumber;
+ $this->birthday = $birthday;
+ }
+
+ public static function getById( int $id ) {
+ $req = BDD::instance()->prepare( "SELECT * FROM " . BDTables::CONTACT .
+ " WHERE `id` = :id" );
+ $req->execute( [ "id" => $id ] );
+ $d = $req->fetch();
+ return new Contact( $d[ "id" ], $d[ "first_name" ], $d[ "last_name" ], $d[ "surname" ],
+ $d[ "email" ], $d[ "address" ], $d[ "phone_number" ], $d[ "birthday" ] );
+ }
+
+ // Getters
+
+ public static function insertNewContact( string $firstName, string $lastName, string $surname = null,
+ string $email = null,
+ string $address = null, string $phoneNumber = null,
+ string $birthday = null ) {
+ $data = [
+ "first_name" => $firstName,
+ "last_name" => $lastName,
+ ];
+ if ( $surname !== null && $surname !== "" ) {
+ $data[ "surname" ] = $surname;
+ }
+ if ( $email !== null && $email !== "" ) {
+ $data[ "email" ] = $email;
+ }
+ if ( $address !== null && $address !== "" ) {
+ $data[ "address" ] = $address;
+ }
+ if ( $phoneNumber !== null && $phoneNumber !== "" ) {
+ $data[ "phone_number" ] = $phoneNumber;
+ }
+ if ( $birthday !== null && $birthday !== "" ) {
+ $data[ "birthday" ] = date( "Y-m-d", strtotime( $birthday ) );
+ }
+ return Model::insert( BDTables::CONTACT, $data );
+ }
+
+ public static function deleteContact( int $id ) {
+ Model::delete( BDTables::CONTACT, [ "id" => $id ] );
+ }
+
+ public static function getAllContact(): array {
+ $contacts = [];
+ $req = BDD::instance()->prepare( "SELECT * FROM " . BDTables::CONTACT );
+ $req->execute();
+
+ foreach ( $req->fetchAll() as $c ) {
+ $contacts[] = new Contact( $c[ "id" ], $c[ "first_name" ], $c[ "last_name" ], $c[ "surname" ],
+ $c[ "email" ], $c[ "address" ], $c[ "phone_number" ], $c[ "birthday" ] );
+ }
+ return ( count( $contacts ) > 0 ? $contacts : null );
+ }
+
+ public static function search( string $query ) {
+ $result = [];
+ $words = explode( " ", cleanString( $query ) );
+
+ $q1 = $q2 = $q3 = $q4 = "SELECT * FROM `" . BDTables::CONTACT . "` WHERE ";
+
+ $lastKey = endKey( $words );
+ foreach ( $words as $key => $word ) {
+ $normal = self::getQuerySearch( $word, [ "first_name", "last_name", "surname" ] );;
+ $hard = self::getQuerySearch( $word, [ "email", "address", "phone_number" ] );
+ $q1 .= $normal;
+ $q2 .= $normal;
+ $q3 .= $hard;
+ $q4 .= $hard;
+ if ( $key != $lastKey ) {
+ $q1 .= " AND ";
+ $q2 .= " OR ";
+ $q3 .= " AND ";
+ $q4 .= " OR ";
+ }
+ }
+
+ $req1 = BDD::instance()->prepare( $q1 );
+ $req2 = BDD::instance()->prepare( $q2 );
+ $req3 = BDD::instance()->prepare( $q3 );
+ $req4 = BDD::instance()->prepare( $q4 );
+
+ $req1->execute();
+ $req2->execute();
+ $req3->execute();
+ $req4->execute();
+
+ $tmp1 = $req1->fetchAll();
+ $tmp2 = filterArrays( $tmp1, $req2->fetchAll() );
+ $tmp3 = filterArrays( $tmp1, filterArrays( $tmp2, $req3->fetchAll() ) );
+ $tmp4 = filterArrays( $tmp1, filterArrays( $tmp2, filterArrays( $tmp3, $req4->fetchAll() ) ) );
+
+ if ( count( $tmp1 ) > 0 || count( $tmp2 ) > 0 || count( $tmp3 ) > 0 || count( $tmp4 ) > 0 ) {
+ $result[ "1" ] = ( count( $tmp1 ) > 0 ? $tmp1 : null );
+ $result[ "2" ] = ( count( $tmp2 ) > 0 ? $tmp2 : null );
+ $result[ "3" ] = ( count( $tmp3 ) > 0 ? $tmp3 : null );
+ $result[ "4" ] = ( count( $tmp4 ) > 0 ? $tmp4 : null );
+ return $result;
+ }
+ return null;
+ }
+
+ private static function getQuerySearch( string $word, array $fields ): string {
+ $str = '';
+ $i = 0;
+
+ foreach ( $fields as $field ) {
+ if ( $i === 0 ) {
+ $str .= "(";
+ } else {
+ $str .= " OR ";
+ }
+ $str .= "`" . $field . "` LIKE '%" . $word . "%'";
+ $i++;
+ }
+ $str .= ')';
+ return $str;
+ }
+
+ /**
+ * @return string
+ */
+ public function getFirstName(): string {
+ return $this->firstName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLastName(): string {
+ return $this->lastName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSurname() {
+ return $this->surname;
+ }
+
+ // Method
+
+ /**
+ * @return string
+ */
+ public function getEmail() {
+ return $this->email;
+ }
+
+ // Static functions
+
+ /**
+ * @return string
+ */
+ public function getAddress() {
+ return $this->address;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPhoneNumber() {
+ return $this->phoneNumber;
+ }
+
+ /**
+ * @return string
+ */
+ public function getBirthday() {
+ return $this->birthday;
+ }
+
+ public function updateContact( array $data ) {
+ foreach ( $data as $key => $value ) {
+ $this->{$key} = $value;
+ }
+ Model::update( BDTables::CONTACT, $data, "id", $this->getId() );
+ }
+
+ /**
+ * @return int
+ */
+ public function getId(): int {
+ return $this->id;
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/src/Model/FPDF.php b/src/Model/FPDF.php
index 95c9a20..cef14e7 100644
--- a/src/Model/FPDF.php
+++ b/src/Model/FPDF.php
@@ -11,1903 +11,1903 @@ namespace CAUProject3Contact\Model;
define( 'FPDF_VERSION', '1.81' );
class FPDF {
- protected $page; // current page number
- protected $n; // current object number
- protected $offsets; // array of object offsets
- protected $buffer; // buffer holding in-memory PDF
- protected $pages; // array containing pages
- protected $state; // current document state
- protected $compress; // compression flag
- protected $k; // scale factor (number of points in user unit)
- protected $DefOrientation; // default orientation
- protected $CurOrientation; // current orientation
- protected $StdPageSizes; // standard page sizes
- protected $DefPageSize; // default page size
- protected $CurPageSize; // current page size
- protected $CurRotation; // current page rotation
- protected $PageInfo; // page-related data
- protected $wPt, $hPt; // dimensions of current page in points
- protected $w, $h; // dimensions of current page in user unit
- protected $lMargin; // left margin
- protected $tMargin; // top margin
- protected $rMargin; // right margin
- protected $bMargin; // page break margin
- protected $cMargin; // cell margin
- protected $x, $y; // current position in user unit
- protected $lasth; // height of last printed cell
- protected $LineWidth; // line width in user unit
- protected $fontpath; // path containing fonts
- protected $CoreFonts; // array of core font names
- protected $fonts; // array of used fonts
- protected $FontFiles; // array of font files
- protected $encodings; // array of encodings
- protected $cmaps; // array of ToUnicode CMaps
- protected $FontFamily; // current font family
- protected $FontStyle; // current font style
- protected $underline; // underlining flag
- protected $CurrentFont; // current font info
- protected $FontSizePt; // current font size in points
- protected $FontSize; // current font size in user unit
- protected $DrawColor; // commands for drawing color
- protected $FillColor; // commands for filling color
- protected $TextColor; // commands for text color
- protected $ColorFlag; // indicates whether fill and text colors are different
- protected $WithAlpha; // indicates whether alpha channel is used
- protected $ws; // word spacing
- protected $images; // array of used images
- protected $PageLinks; // array of links in pages
- protected $links; // array of internal links
- protected $AutoPageBreak; // automatic page breaking
- protected $PageBreakTrigger; // threshold used to trigger page breaks
- protected $InHeader; // flag set when processing header
- protected $InFooter; // flag set when processing footer
- protected $AliasNbPages; // alias for total number of pages
- protected $ZoomMode; // zoom display mode
- protected $LayoutMode; // layout display mode
- protected $metadata; // document properties
- protected $PDFVersion; // PDF version number
-
- /*******************************************************************************
- * Public methods *
- *******************************************************************************/
-
- function __construct( $orientation = 'P', $unit = 'mm', $size = 'A4' ) {
- // Some checks
- $this->_dochecks();
- // Initialization of properties
- $this->state = 0;
- $this->page = 0;
- $this->n = 2;
- $this->buffer = '';
- $this->pages = array();
- $this->PageInfo = array();
- $this->fonts = array();
- $this->FontFiles = array();
- $this->encodings = array();
- $this->cmaps = array();
- $this->images = array();
- $this->links = array();
- $this->InHeader = false;
- $this->InFooter = false;
- $this->lasth = 0;
- $this->FontFamily = '';
- $this->FontStyle = '';
- $this->FontSizePt = 12;
- $this->underline = false;
- $this->DrawColor = '0 G';
- $this->FillColor = '0 g';
- $this->TextColor = '0 g';
- $this->ColorFlag = false;
- $this->WithAlpha = false;
- $this->ws = 0;
- // Font path
- if ( defined( 'FPDF_FONTPATH' ) ) {
- $this->fontpath = FPDF_FONTPATH;
- if ( substr( $this->fontpath, - 1 ) != '/' && substr( $this->fontpath, - 1 ) != '\\' ) {
- $this->fontpath .= '/';
- }
- } elseif ( is_dir( dirname( __FILE__ ) . '/font' ) ) {
- $this->fontpath = dirname( __FILE__ ) . '/font/';
- } else {
- $this->fontpath = '';
- }
- // Core fonts
- $this->CoreFonts = array( 'courier', 'helvetica', 'times', 'symbol', 'zapfdingbats' );
- // Scale factor
- if ( $unit == 'pt' ) {
- $this->k = 1;
- } elseif ( $unit == 'mm' ) {
- $this->k = 72 / 25.4;
- } elseif ( $unit == 'cm' ) {
- $this->k = 72 / 2.54;
- } elseif ( $unit == 'in' ) {
- $this->k = 72;
- } else {
- $this->Error( 'Incorrect unit: ' . $unit );
- }
- // Page sizes
- $this->StdPageSizes = array(
- 'a3' => array( 841.89, 1190.55 ),
- 'a4' => array( 595.28, 841.89 ),
- 'a5' => array( 420.94, 595.28 ),
- 'letter' => array( 612, 792 ),
- 'legal' => array( 612, 1008 )
- );
- $size = $this->_getpagesize( $size );
- $this->DefPageSize = $size;
- $this->CurPageSize = $size;
- // Page orientation
- $orientation = strtolower( $orientation );
- if ( $orientation == 'p' || $orientation == 'portrait' ) {
- $this->DefOrientation = 'P';
- $this->w = $size[0];
- $this->h = $size[1];
- } elseif ( $orientation == 'l' || $orientation == 'landscape' ) {
- $this->DefOrientation = 'L';
- $this->w = $size[1];
- $this->h = $size[0];
- } else {
- $this->Error( 'Incorrect orientation: ' . $orientation );
- }
- $this->CurOrientation = $this->DefOrientation;
- $this->wPt = $this->w * $this->k;
- $this->hPt = $this->h * $this->k;
- // Page rotation
- $this->CurRotation = 0;
- // Page margins (1 cm)
- $margin = 28.35 / $this->k;
- $this->SetMargins( $margin, $margin );
- // Interior cell margin (1 mm)
- $this->cMargin = $margin / 10;
- // Line width (0.2 mm)
- $this->LineWidth = .567 / $this->k;
- // Automatic page break
- $this->SetAutoPageBreak( true, 2 * $margin );
- // Default display mode
- $this->SetDisplayMode( 'default' );
- // Enable compression
- $this->SetCompression( true );
- // Set default PDF version number
- $this->PDFVersion = '1.3';
- }
-
- /*******************************************************************************
- * Protected methods *
- *******************************************************************************/
-
- protected function _dochecks() {
- // Check mbstring overloading
- if ( ini_get( 'mbstring.func_overload' ) & 2 ) {
- $this->Error( 'mbstring overloading must be disabled' );
- }
- // Ensure runtime magic quotes are disabled
- if ( get_magic_quotes_runtime() ) {
- @set_magic_quotes_runtime( 0 );
- }
- }
-
- function Error( $msg ) {
- // Fatal error
- throw new Exception( 'FPDF error: ' . $msg );
- }
-
- protected function _getpagesize( $size ) {
- if ( is_string( $size ) ) {
- $size = strtolower( $size );
- if ( ! isset( $this->StdPageSizes[ $size ] ) ) {
- $this->Error( 'Unknown page size: ' . $size );
- }
- $a = $this->StdPageSizes[ $size ];
-
- return array( $a[0] / $this->k, $a[1] / $this->k );
- } else {
- if ( $size[0] > $size[1] ) {
- return array( $size[1], $size[0] );
- } else {
- return $size;
- }
- }
- }
-
- function SetMargins( $left, $top, $right = null ) {
- // Set left, top and right margins
- $this->lMargin = $left;
- $this->tMargin = $top;
- if ( $right === null ) {
- $right = $left;
- }
- $this->rMargin = $right;
- }
-
- function SetAutoPageBreak( $auto, $margin = 0 ) {
- // Set auto page break mode and triggering margin
- $this->AutoPageBreak = $auto;
- $this->bMargin = $margin;
- $this->PageBreakTrigger = $this->h - $margin;
- }
-
- function SetDisplayMode( $zoom, $layout = 'default' ) {
- // Set display mode in viewer
- if ( $zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real' || $zoom == 'default' || ! is_string( $zoom ) ) {
- $this->ZoomMode = $zoom;
- } else {
- $this->Error( 'Incorrect zoom display mode: ' . $zoom );
- }
- if ( $layout == 'single' || $layout == 'continuous' || $layout == 'two' || $layout == 'default' ) {
- $this->LayoutMode = $layout;
- } else {
- $this->Error( 'Incorrect layout display mode: ' . $layout );
- }
- }
-
- function SetCompression( $compress ) {
- // Set page compression
- if ( function_exists( 'gzcompress' ) ) {
- $this->compress = $compress;
- } else {
- $this->compress = false;
- }
- }
-
- function SetLeftMargin( $margin ) {
- // Set left margin
- $this->lMargin = $margin;
- if ( $this->page > 0 && $this->x < $margin ) {
- $this->x = $margin;
- }
- }
-
- function SetTopMargin( $margin ) {
- // Set top margin
- $this->tMargin = $margin;
- }
-
- function SetRightMargin( $margin ) {
- // Set right margin
- $this->rMargin = $margin;
- }
-
- function SetTitle( $title, $isUTF8 = false ) {
- // Title of document
- $this->metadata['Title'] = $isUTF8 ? $title : utf8_encode( $title );
- }
-
- function SetAuthor( $author, $isUTF8 = false ) {
- // Author of document
- $this->metadata['Author'] = $isUTF8 ? $author : utf8_encode( $author );
- }
-
- function SetSubject( $subject, $isUTF8 = false ) {
- // Subject of document
- $this->metadata['Subject'] = $isUTF8 ? $subject : utf8_encode( $subject );
- }
-
- function SetKeywords( $keywords, $isUTF8 = false ) {
- // Keywords of document
- $this->metadata['Keywords'] = $isUTF8 ? $keywords : utf8_encode( $keywords );
- }
-
- function SetCreator( $creator, $isUTF8 = false ) {
- // Creator of document
- $this->metadata['Creator'] = $isUTF8 ? $creator : utf8_encode( $creator );
- }
-
- function AliasNbPages( $alias = '{nb}' ) {
- // Define an alias for total number of pages
- $this->AliasNbPages = $alias;
- }
-
- function PageNo() {
- // Get current page number
- return $this->page;
- }
-
- function SetDrawColor( $r, $g = null, $b = null ) {
- // Set color for all stroking operations
- if ( ( $r == 0 && $g == 0 && $b == 0 ) || $g === null ) {
- $this->DrawColor = sprintf( '%.3F G', $r / 255 );
- } else {
- $this->DrawColor = sprintf( '%.3F %.3F %.3F RG', $r / 255, $g / 255, $b / 255 );
- }
- if ( $this->page > 0 ) {
- $this->_out( $this->DrawColor );
- }
- }
-
- protected function _out( $s ) {
- // Add a line to the document
- if ( $this->state == 2 ) {
- $this->pages[ $this->page ] .= $s . "\n";
- } elseif ( $this->state == 1 ) {
- $this->_put( $s );
- } elseif ( $this->state == 0 ) {
- $this->Error( 'No page has been added yet' );
- } elseif ( $this->state == 3 ) {
- $this->Error( 'The document is closed' );
- }
- }
-
- protected function _put( $s ) {
- $this->buffer .= $s . "\n";
- }
-
- function SetFillColor( $r, $g = null, $b = null ) {
- // Set color for all filling operations
- if ( ( $r == 0 && $g == 0 && $b == 0 ) || $g === null ) {
- $this->FillColor = sprintf( '%.3F g', $r / 255 );
- } else {
- $this->FillColor = sprintf( '%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255 );
- }
- $this->ColorFlag = ( $this->FillColor != $this->TextColor );
- if ( $this->page > 0 ) {
- $this->_out( $this->FillColor );
- }
- }
-
- function SetTextColor( $r, $g = null, $b = null ) {
- // Set color for text
- if ( ( $r == 0 && $g == 0 && $b == 0 ) || $g === null ) {
- $this->TextColor = sprintf( '%.3F g', $r / 255 );
- } else {
- $this->TextColor = sprintf( '%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255 );
- }
- $this->ColorFlag = ( $this->FillColor != $this->TextColor );
- }
-
- function SetLineWidth( $width ) {
- // Set line width
- $this->LineWidth = $width;
- if ( $this->page > 0 ) {
- $this->_out( sprintf( '%.2F w', $width * $this->k ) );
- }
- }
-
- function Line( $x1, $y1, $x2, $y2 ) {
- // Draw a line
- $this->_out( sprintf( '%.2F %.2F m %.2F %.2F l S', $x1 * $this->k, ( $this->h - $y1 ) * $this->k, $x2 * $this->k, ( $this->h - $y2 ) * $this->k ) );
- }
-
- function Rect( $x, $y, $w, $h, $style = '' ) {
- // Draw a rectangle
- if ( $style == 'F' ) {
- $op = 'f';
- } elseif ( $style == 'FD' || $style == 'DF' ) {
- $op = 'B';
- } else {
- $op = 'S';
- }
- $this->_out( sprintf( '%.2F %.2F %.2F %.2F re %s', $x * $this->k, ( $this->h - $y ) * $this->k, $w * $this->k, - $h * $this->k, $op ) );
- }
-
- function SetFontSize( $size ) {
- // Set font size in points
- if ( $this->FontSizePt == $size ) {
- return;
- }
- $this->FontSizePt = $size;
- $this->FontSize = $size / $this->k;
- if ( $this->page > 0 ) {
- $this->_out( sprintf( 'BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt ) );
- }
- }
-
- function AddLink() {
- // Create a new internal link
- $n = count( $this->links ) + 1;
- $this->links[ $n ] = array( 0, 0 );
-
- return $n;
- }
-
- function SetLink( $link, $y = 0, $page = - 1 ) {
- // Set destination of internal link
- if ( $y == - 1 ) {
- $y = $this->y;
- }
- if ( $page == - 1 ) {
- $page = $this->page;
- }
- $this->links[ $link ] = array( $page, $y );
- }
-
- function Text( $x, $y, $txt ) {
- // Output a string
- if ( ! isset( $this->CurrentFont ) ) {
- $this->Error( 'No font has been set' );
- }
- $s = sprintf( 'BT %.2F %.2F Td (%s) Tj ET', $x * $this->k, ( $this->h - $y ) * $this->k, $this->_escape( $txt ) );
- if ( $this->underline && $txt != '' ) {
- $s .= ' ' . $this->_dounderline( $x, $y, $txt );
- }
- if ( $this->ColorFlag ) {
- $s = 'q ' . $this->TextColor . ' ' . $s . ' Q';
- }
- $this->_out( $s );
- }
-
- protected function _escape( $s ) {
- // Escape special characters
- if ( strpos( $s, '(' ) !== false || strpos( $s, ')' ) !== false || strpos( $s, '\\' ) !== false || strpos( $s, "\r" ) !== false ) {
- return str_replace( array( '\\', '(', ')', "\r" ), array( '\\\\', '\\(', '\\)', '\\r' ), $s );
- } else {
- return $s;
- }
- }
-
- protected function _dounderline( $x, $y, $txt ) {
- // Underline text
- $up = $this->CurrentFont['up'];
- $ut = $this->CurrentFont['ut'];
- $w = $this->GetStringWidth( $txt ) + $this->ws * substr_count( $txt, ' ' );
-
- return sprintf( '%.2F %.2F %.2F %.2F re f', $x * $this->k, ( $this->h - ( $y - $up / 1000 * $this->FontSize ) ) * $this->k, $w * $this->k, - $ut / 1000 * $this->FontSizePt );
- }
-
- function GetStringWidth( $s ) {
- // Get width of a string in the current font
- $s = (string) $s;
- $cw = &$this->CurrentFont['cw'];
- $w = 0;
- $l = strlen( $s );
- for ( $i = 0; $i < $l; $i ++ ) {
- $w += $cw[ $s[ $i ] ];
- }
-
- return $w * $this->FontSize / 1000;
- }
-
- function MultiCell( $w, $h, $txt, $border = 0, $align = 'J', $fill = false ) {
- // Output text with automatic or explicit line breaks
- if ( ! isset( $this->CurrentFont ) ) {
- $this->Error( 'No font has been set' );
- }
- $cw = &$this->CurrentFont['cw'];
- if ( $w == 0 ) {
- $w = $this->w - $this->rMargin - $this->x;
- }
- $wmax = ( $w - 2 * $this->cMargin ) * 1000 / $this->FontSize;
- $s = str_replace( "\r", '', $txt );
- $nb = strlen( $s );
- if ( $nb > 0 && $s[ $nb - 1 ] == "\n" ) {
- $nb --;
- }
- $b = 0;
- if ( $border ) {
- if ( $border == 1 ) {
- $border = 'LTRB';
- $b = 'LRT';
- $b2 = 'LR';
- } else {
- $b2 = '';
- if ( strpos( $border, 'L' ) !== false ) {
- $b2 .= 'L';
- }
- if ( strpos( $border, 'R' ) !== false ) {
- $b2 .= 'R';
- }
- $b = ( strpos( $border, 'T' ) !== false ) ? $b2 . 'T' : $b2;
- }
- }
- $sep = - 1;
- $i = 0;
- $j = 0;
- $l = 0;
- $ns = 0;
- $nl = 1;
- while ( $i < $nb ) {
- // Get next character
- $c = $s[ $i ];
- if ( $c == "\n" ) {
- // Explicit line break
- if ( $this->ws > 0 ) {
- $this->ws = 0;
- $this->_out( '0 Tw' );
- }
- $this->Cell( $w, $h, substr( $s, $j, $i - $j ), $b, 2, $align, $fill );
- $i ++;
- $sep = - 1;
- $j = $i;
- $l = 0;
- $ns = 0;
- $nl ++;
- if ( $border && $nl == 2 ) {
- $b = $b2;
- }
- continue;
- }
- if ( $c == ' ' ) {
- $sep = $i;
- $ls = $l;
- $ns ++;
- }
- $l += $cw[ $c ];
- if ( $l > $wmax ) {
- // Automatic line break
- if ( $sep == - 1 ) {
- if ( $i == $j ) {
- $i ++;
- }
- if ( $this->ws > 0 ) {
- $this->ws = 0;
- $this->_out( '0 Tw' );
- }
- $this->Cell( $w, $h, substr( $s, $j, $i - $j ), $b, 2, $align, $fill );
- } else {
- if ( $align == 'J' ) {
- $this->ws = ( $ns > 1 ) ? ( $wmax - $ls ) / 1000 * $this->FontSize / ( $ns - 1 ) : 0;
- $this->_out( sprintf( '%.3F Tw', $this->ws * $this->k ) );
- }
- $this->Cell( $w, $h, substr( $s, $j, $sep - $j ), $b, 2, $align, $fill );
- $i = $sep + 1;
- }
- $sep = - 1;
- $j = $i;
- $l = 0;
- $ns = 0;
- $nl ++;
- if ( $border && $nl == 2 ) {
- $b = $b2;
- }
- } else {
- $i ++;
- }
- }
- // Last chunk
- if ( $this->ws > 0 ) {
- $this->ws = 0;
- $this->_out( '0 Tw' );
- }
- if ( $border && strpos( $border, 'B' ) !== false ) {
- $b .= 'B';
- }
- $this->Cell( $w, $h, substr( $s, $j, $i - $j ), $b, 2, $align, $fill );
- $this->x = $this->lMargin;
- }
-
- function Cell( $w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = false, $link = '' ) {
- // Output a cell
- $k = $this->k;
- if ( $this->y + $h > $this->PageBreakTrigger && ! $this->InHeader && ! $this->InFooter && $this->AcceptPageBreak() ) {
- // Automatic page break
- $x = $this->x;
- $ws = $this->ws;
- if ( $ws > 0 ) {
- $this->ws = 0;
- $this->_out( '0 Tw' );
- }
- $this->AddPage( $this->CurOrientation, $this->CurPageSize, $this->CurRotation );
- $this->x = $x;
- if ( $ws > 0 ) {
- $this->ws = $ws;
- $this->_out( sprintf( '%.3F Tw', $ws * $k ) );
- }
- }
- if ( $w == 0 ) {
- $w = $this->w - $this->rMargin - $this->x;
- }
- $s = '';
- if ( $fill || $border == 1 ) {
- if ( $fill ) {
- $op = ( $border == 1 ) ? 'B' : 'f';
- } else {
- $op = 'S';
- }
- $s = sprintf( '%.2F %.2F %.2F %.2F re %s ', $this->x * $k, ( $this->h - $this->y ) * $k, $w * $k, - $h * $k, $op );
- }
- if ( is_string( $border ) ) {
- $x = $this->x;
- $y = $this->y;
- if ( strpos( $border, 'L' ) !== false ) {
- $s .= sprintf( '%.2F %.2F m %.2F %.2F l S ', $x * $k, ( $this->h - $y ) * $k, $x * $k, ( $this->h - ( $y + $h ) ) * $k );
- }
- if ( strpos( $border, 'T' ) !== false ) {
- $s .= sprintf( '%.2F %.2F m %.2F %.2F l S ', $x * $k, ( $this->h - $y ) * $k, ( $x + $w ) * $k, ( $this->h - $y ) * $k );
- }
- if ( strpos( $border, 'R' ) !== false ) {
- $s .= sprintf( '%.2F %.2F m %.2F %.2F l S ', ( $x + $w ) * $k, ( $this->h - $y ) * $k, ( $x + $w ) * $k, ( $this->h - ( $y + $h ) ) * $k );
- }
- if ( strpos( $border, 'B' ) !== false ) {
- $s .= sprintf( '%.2F %.2F m %.2F %.2F l S ', $x * $k, ( $this->h - ( $y + $h ) ) * $k, ( $x + $w ) * $k, ( $this->h - ( $y + $h ) ) * $k );
- }
- }
- if ( $txt !== '' ) {
- if ( ! isset( $this->CurrentFont ) ) {
- $this->Error( 'No font has been set' );
- }
- if ( $align == 'R' ) {
- $dx = $w - $this->cMargin - $this->GetStringWidth( $txt );
- } elseif ( $align == 'C' ) {
- $dx = ( $w - $this->GetStringWidth( $txt ) ) / 2;
- } else {
- $dx = $this->cMargin;
- }
- if ( $this->ColorFlag ) {
- $s .= 'q ' . $this->TextColor . ' ';
- }
- $s .= sprintf( 'BT %.2F %.2F Td (%s) Tj ET', ( $this->x + $dx ) * $k, ( $this->h - ( $this->y + .5 * $h + .3 * $this->FontSize ) ) * $k, $this->_escape( $txt ) );
- if ( $this->underline ) {
- $s .= ' ' . $this->_dounderline( $this->x + $dx, $this->y + .5 * $h + .3 * $this->FontSize, $txt );
- }
- if ( $this->ColorFlag ) {
- $s .= ' Q';
- }
- if ( $link ) {
- $this->Link( $this->x + $dx, $this->y + .5 * $h - .5 * $this->FontSize, $this->GetStringWidth( $txt ), $this->FontSize, $link );
- }
- }
- if ( $s ) {
- $this->_out( $s );
- }
- $this->lasth = $h;
- if ( $ln > 0 ) {
- // Go to next line
- $this->y += $h;
- if ( $ln == 1 ) {
- $this->x = $this->lMargin;
- }
- } else {
- $this->x += $w;
- }
- }
-
- function AcceptPageBreak() {
- // Accept automatic page break or not
- return $this->AutoPageBreak;
- }
-
- function AddPage( $orientation = '', $size = '', $rotation = 0 ) {
- // Start a new page
- if ( $this->state == 3 ) {
- $this->Error( 'The document is closed' );
- }
- $family = $this->FontFamily;
- $style = $this->FontStyle . ( $this->underline ? 'U' : '' );
- $fontsize = $this->FontSizePt;
- $lw = $this->LineWidth;
- $dc = $this->DrawColor;
- $fc = $this->FillColor;
- $tc = $this->TextColor;
- $cf = $this->ColorFlag;
- if ( $this->page > 0 ) {
- // Page footer
- $this->InFooter = true;
- $this->Footer();
- $this->InFooter = false;
- // Close page
- $this->_endpage();
- }
- // Start new page
- $this->_beginpage( $orientation, $size, $rotation );
- // Set line cap style to square
- $this->_out( '2 J' );
- // Set line width
- $this->LineWidth = $lw;
- $this->_out( sprintf( '%.2F w', $lw * $this->k ) );
- // Set font
- if ( $family ) {
- $this->SetFont( $family, $style, $fontsize );
- }
- // Set colors
- $this->DrawColor = $dc;
- if ( $dc != '0 G' ) {
- $this->_out( $dc );
- }
- $this->FillColor = $fc;
- if ( $fc != '0 g' ) {
- $this->_out( $fc );
- }
- $this->TextColor = $tc;
- $this->ColorFlag = $cf;
- // Page header
- $this->InHeader = true;
- $this->Header();
- $this->InHeader = false;
- // Restore line width
- if ( $this->LineWidth != $lw ) {
- $this->LineWidth = $lw;
- $this->_out( sprintf( '%.2F w', $lw * $this->k ) );
- }
- // Restore font
- if ( $family ) {
- $this->SetFont( $family, $style, $fontsize );
- }
- // Restore colors
- if ( $this->DrawColor != $dc ) {
- $this->DrawColor = $dc;
- $this->_out( $dc );
- }
- if ( $this->FillColor != $fc ) {
- $this->FillColor = $fc;
- $this->_out( $fc );
- }
- $this->TextColor = $tc;
- $this->ColorFlag = $cf;
- }
-
- function Footer() {
- // To be implemented in your own inherited class
- }
-
- protected function _endpage() {
- $this->state = 1;
- }
-
- protected function _beginpage( $orientation, $size, $rotation ) {
- $this->page ++;
- $this->pages[ $this->page ] = '';
- $this->state = 2;
- $this->x = $this->lMargin;
- $this->y = $this->tMargin;
- $this->FontFamily = '';
- // Check page size and orientation
- if ( $orientation == '' ) {
- $orientation = $this->DefOrientation;
- } else {
- $orientation = strtoupper( $orientation[0] );
- }
- if ( $size == '' ) {
- $size = $this->DefPageSize;
- } else {
- $size = $this->_getpagesize( $size );
- }
- if ( $orientation != $this->CurOrientation || $size[0] != $this->CurPageSize[0] || $size[1] != $this->CurPageSize[1] ) {
- // New size or orientation
- if ( $orientation == 'P' ) {
- $this->w = $size[0];
- $this->h = $size[1];
- } else {
- $this->w = $size[1];
- $this->h = $size[0];
- }
- $this->wPt = $this->w * $this->k;
- $this->hPt = $this->h * $this->k;
- $this->PageBreakTrigger = $this->h - $this->bMargin;
- $this->CurOrientation = $orientation;
- $this->CurPageSize = $size;
- }
- if ( $orientation != $this->DefOrientation || $size[0] != $this->DefPageSize[0] || $size[1] != $this->DefPageSize[1] ) {
- $this->PageInfo[ $this->page ]['size'] = array( $this->wPt, $this->hPt );
- }
- if ( $rotation != 0 ) {
- if ( $rotation % 90 != 0 ) {
- $this->Error( 'Incorrect rotation value: ' . $rotation );
- }
- $this->CurRotation = $rotation;
- $this->PageInfo[ $this->page ]['rotation'] = $rotation;
- }
- }
-
- function SetFont( $family, $style = '', $size = 0 ) {
- // Select a font; size given in points
- if ( $family == '' ) {
- $family = $this->FontFamily;
- } else {
- $family = strtolower( $family );
- }
- $style = strtoupper( $style );
- if ( strpos( $style, 'U' ) !== false ) {
- $this->underline = true;
- $style = str_replace( 'U', '', $style );
- } else {
- $this->underline = false;
- }
- if ( $style == 'IB' ) {
- $style = 'BI';
- }
- if ( $size == 0 ) {
- $size = $this->FontSizePt;
- }
- // Test if font is already selected
- if ( $this->FontFamily == $family && $this->FontStyle == $style && $this->FontSizePt == $size ) {
- return;
- }
- // Test if font is already loaded
- $fontkey = $family . $style;
- if ( ! isset( $this->fonts[ $fontkey ] ) ) {
- // Test if one of the core fonts
- if ( $family == 'arial' ) {
- $family = 'helvetica';
- }
- if ( in_array( $family, $this->CoreFonts ) ) {
- if ( $family == 'symbol' || $family == 'zapfdingbats' ) {
- $style = '';
- }
- $fontkey = $family . $style;
- if ( ! isset( $this->fonts[ $fontkey ] ) ) {
- $this->AddFont( $family, $style );
- }
- } else {
- $this->Error( 'Undefined font: ' . $family . ' ' . $style );
- }
- }
- // Select it
- $this->FontFamily = $family;
- $this->FontStyle = $style;
- $this->FontSizePt = $size;
- $this->FontSize = $size / $this->k;
- $this->CurrentFont = &$this->fonts[ $fontkey ];
- if ( $this->page > 0 ) {
- $this->_out( sprintf( 'BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt ) );
- }
- }
-
- function AddFont( $family, $style = '', $file = '' ) {
- // Add a TrueType, OpenType or Type1 font
- $family = strtolower( $family );
- if ( $file == '' ) {
- $file = str_replace( ' ', '', $family ) . strtolower( $style ) . '.php';
- }
- $style = strtoupper( $style );
- if ( $style == 'IB' ) {
- $style = 'BI';
- }
- $fontkey = $family . $style;
- if ( isset( $this->fonts[ $fontkey ] ) ) {
- return;
- }
- $info = $this->_loadfont( $file );
- $info['i'] = count( $this->fonts ) + 1;
- if ( ! empty( $info['file'] ) ) {
- // Embedded font
- if ( $info['type'] == 'TrueType' ) {
- $this->FontFiles[ $info['file'] ] = array( 'length1' => $info['originalsize'] );
- } else {
- $this->FontFiles[ $info['file'] ] = array( 'length1' => $info['size1'], 'length2' => $info['size2'] );
- }
- }
- $this->fonts[ $fontkey ] = $info;
- }
-
- protected function _loadfont( $font ) {
- // Load a font definition file from the font directory
- if ( strpos( $font, '/' ) !== false || strpos( $font, "\\" ) !== false ) {
- $this->Error( 'Incorrect font definition file name: ' . $font );
- }
- include( $this->fontpath . $font );
- if ( ! isset( $name ) ) {
- $this->Error( 'Could not include font definition file' );
- }
- if ( isset( $enc ) ) {
- $enc = strtolower( $enc );
- }
- if ( ! isset( $subsetted ) ) {
- $subsetted = false;
- }
-
- return get_defined_vars();
- }
-
- function Header() {
- // To be implemented in your own inherited class
- }
-
- function Link( $x, $y, $w, $h, $link ) {
- // Put a link on the page
- $this->PageLinks[ $this->page ][] = array(
- $x * $this->k,
- $this->hPt - $y * $this->k,
- $w * $this->k,
- $h * $this->k,
- $link
- );
- }
-
- function Write( $h, $txt, $link = '' ) {
- // Output text in flowing mode
- if ( ! isset( $this->CurrentFont ) ) {
- $this->Error( 'No font has been set' );
- }
- $cw = &$this->CurrentFont['cw'];
- $w = $this->w - $this->rMargin - $this->x;
- $wmax = ( $w - 2 * $this->cMargin ) * 1000 / $this->FontSize;
- $s = str_replace( "\r", '', $txt );
- $nb = strlen( $s );
- $sep = - 1;
- $i = 0;
- $j = 0;
- $l = 0;
- $nl = 1;
- while ( $i < $nb ) {
- // Get next character
- $c = $s[ $i ];
- if ( $c == "\n" ) {
- // Explicit line break
- $this->Cell( $w, $h, substr( $s, $j, $i - $j ), 0, 2, '', false, $link );
- $i ++;
- $sep = - 1;
- $j = $i;
- $l = 0;
- if ( $nl == 1 ) {
- $this->x = $this->lMargin;
- $w = $this->w - $this->rMargin - $this->x;
- $wmax = ( $w - 2 * $this->cMargin ) * 1000 / $this->FontSize;
- }
- $nl ++;
- continue;
- }
- if ( $c == ' ' ) {
- $sep = $i;
- }
- $l += $cw[ $c ];
- if ( $l > $wmax ) {
- // Automatic line break
- if ( $sep == - 1 ) {
- if ( $this->x > $this->lMargin ) {
- // Move to next line
- $this->x = $this->lMargin;
- $this->y += $h;
- $w = $this->w - $this->rMargin - $this->x;
- $wmax = ( $w - 2 * $this->cMargin ) * 1000 / $this->FontSize;
- $i ++;
- $nl ++;
- continue;
- }
- if ( $i == $j ) {
- $i ++;
- }
- $this->Cell( $w, $h, substr( $s, $j, $i - $j ), 0, 2, '', false, $link );
- } else {
- $this->Cell( $w, $h, substr( $s, $j, $sep - $j ), 0, 2, '', false, $link );
- $i = $sep + 1;
- }
- $sep = - 1;
- $j = $i;
- $l = 0;
- if ( $nl == 1 ) {
- $this->x = $this->lMargin;
- $w = $this->w - $this->rMargin - $this->x;
- $wmax = ( $w - 2 * $this->cMargin ) * 1000 / $this->FontSize;
- }
- $nl ++;
- } else {
- $i ++;
- }
- }
- // Last chunk
- if ( $i != $j ) {
- $this->Cell( $l / 1000 * $this->FontSize, $h, substr( $s, $j ), 0, 0, '', false, $link );
- }
- }
-
- function Ln( $h = null ) {
- // Line feed; default value is the last cell height
- $this->x = $this->lMargin;
- if ( $h === null ) {
- $this->y += $this->lasth;
- } else {
- $this->y += $h;
- }
- }
-
- function Image( $file, $x = null, $y = null, $w = 0, $h = 0, $type = '', $link = '' ) {
- // Put an image on the page
- if ( $file == '' ) {
- $this->Error( 'Image file name is empty' );
- }
- if ( ! isset( $this->images[ $file ] ) ) {
- // First use of this image, get info
- if ( $type == '' ) {
- $pos = strrpos( $file, '.' );
- if ( ! $pos ) {
- $this->Error( 'Image file has no extension and no type was specified: ' . $file );
- }
- $type = substr( $file, $pos + 1 );
- }
- $type = strtolower( $type );
- if ( $type == 'jpeg' ) {
- $type = 'jpg';
- }
- $mtd = '_parse' . $type;
- if ( ! method_exists( $this, $mtd ) ) {
- $this->Error( 'Unsupported image type: ' . $type );
- }
- $info = $this->$mtd( $file );
- $info['i'] = count( $this->images ) + 1;
- $this->images[ $file ] = $info;
- } else {
- $info = $this->images[ $file ];
- }
-
- // Automatic width and height calculation if needed
- if ( $w == 0 && $h == 0 ) {
- // Put image at 96 dpi
- $w = - 96;
- $h = - 96;
- }
- if ( $w < 0 ) {
- $w = - $info['w'] * 72 / $w / $this->k;
- }
- if ( $h < 0 ) {
- $h = - $info['h'] * 72 / $h / $this->k;
- }
- if ( $w == 0 ) {
- $w = $h * $info['w'] / $info['h'];
- }
- if ( $h == 0 ) {
- $h = $w * $info['h'] / $info['w'];
- }
-
- // Flowing mode
- if ( $y === null ) {
- if ( $this->y + $h > $this->PageBreakTrigger && ! $this->InHeader && ! $this->InFooter && $this->AcceptPageBreak() ) {
- // Automatic page break
- $x2 = $this->x;
- $this->AddPage( $this->CurOrientation, $this->CurPageSize, $this->CurRotation );
- $this->x = $x2;
- }
- $y = $this->y;
- $this->y += $h;
- }
-
- if ( $x === null ) {
- $x = $this->x;
- }
- $this->_out( sprintf( 'q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q', $w * $this->k, $h * $this->k, $x * $this->k, ( $this->h - ( $y + $h ) ) * $this->k, $info['i'] ) );
- if ( $link ) {
- $this->Link( $x, $y, $w, $h, $link );
- }
- }
-
- function GetPageWidth() {
- // Get current page width
- return $this->w;
- }
-
- function GetPageHeight() {
- // Get current page height
- return $this->h;
- }
-
- function GetX() {
- // Get x position
- return $this->x;
- }
-
- function SetX( $x ) {
- // Set x position
- if ( $x >= 0 ) {
- $this->x = $x;
- } else {
- $this->x = $this->w + $x;
- }
- }
-
- function GetY() {
- // Get y position
- return $this->y;
- }
-
- function SetXY( $x, $y ) {
- // Set x and y positions
- $this->SetX( $x );
- $this->SetY( $y, false );
- }
-
- function SetY( $y, $resetX = true ) {
- // Set y position and optionally reset x
- if ( $y >= 0 ) {
- $this->y = $y;
- } else {
- $this->y = $this->h + $y;
- }
- if ( $resetX ) {
- $this->x = $this->lMargin;
- }
- }
-
- function Output( $dest = '', $name = '', $isUTF8 = false ) {
- // Output PDF to some destination
- $this->Close();
- if ( strlen( $name ) == 1 && strlen( $dest ) != 1 ) {
- // Fix parameter order
- $tmp = $dest;
- $dest = $name;
- $name = $tmp;
- }
- if ( $dest == '' ) {
- $dest = 'I';
- }
- if ( $name == '' ) {
- $name = 'doc.pdf';
- }
- switch ( strtoupper( $dest ) ) {
- case 'I':
- // Send to standard output
- $this->_checkoutput();
- if ( PHP_SAPI != 'cli' ) {
- // We send to a browser
- header( 'Content-Type: application/pdf' );
- header( 'Content-Disposition: inline; ' . $this->_httpencode( 'filename', $name, $isUTF8 ) );
- header( 'Cache-Control: private, max-age=0, must-revalidate' );
- header( 'Pragma: public' );
- }
- echo $this->buffer;
- break;
- case 'D':
- // Download file
- $this->_checkoutput();
- header( 'Content-Type: application/x-download' );
- header( 'Content-Disposition: attachment; ' . $this->_httpencode( 'filename', $name, $isUTF8 ) );
- header( 'Cache-Control: private, max-age=0, must-revalidate' );
- header( 'Pragma: public' );
- echo $this->buffer;
- break;
- case 'F':
- // Save to local file
- if ( ! file_put_contents( $name, $this->buffer ) ) {
- $this->Error( 'Unable to create output file: ' . $name );
- }
- break;
- case 'S':
- // Return as a string
- return $this->buffer;
- default:
- $this->Error( 'Incorrect output destination: ' . $dest );
- }
-
- return '';
- }
-
- function Close() {
- // Terminate document
- if ( $this->state == 3 ) {
- return;
- }
- if ( $this->page == 0 ) {
- $this->AddPage();
- }
- // Page footer
- $this->InFooter = true;
- $this->Footer();
- $this->InFooter = false;
- // Close page
- $this->_endpage();
- // Close document
- $this->_enddoc();
- }
-
- protected function _enddoc() {
- $this->_putheader();
- $this->_putpages();
- $this->_putresources();
- // Info
- $this->_newobj();
- $this->_put( '<<' );
- $this->_putinfo();
- $this->_put( '>>' );
- $this->_put( 'endobj' );
- // Catalog
- $this->_newobj();
- $this->_put( '<<' );
- $this->_putcatalog();
- $this->_put( '>>' );
- $this->_put( 'endobj' );
- // Cross-ref
- $offset = $this->_getoffset();
- $this->_put( 'xref' );
- $this->_put( '0 ' . ( $this->n + 1 ) );
- $this->_put( '0000000000 65535 f ' );
- for ( $i = 1; $i <= $this->n; $i ++ ) {
- $this->_put( sprintf( '%010d 00000 n ', $this->offsets[ $i ] ) );
- }
- // Trailer
- $this->_put( 'trailer' );
- $this->_put( '<<' );
- $this->_puttrailer();
- $this->_put( '>>' );
- $this->_put( 'startxref' );
- $this->_put( $offset );
- $this->_put( '%%EOF' );
- $this->state = 3;
- }
-
- protected function _putheader() {
- $this->_put( '%PDF-' . $this->PDFVersion );
- }
-
- protected function _putpages() {
- $nb = $this->page;
- for ( $n = 1; $n <= $nb; $n ++ ) {
- $this->PageInfo[ $n ]['n'] = $this->n + 1 + 2 * ( $n - 1 );
- }
- for ( $n = 1; $n <= $nb; $n ++ ) {
- $this->_putpage( $n );
- }
- // Pages root
- $this->_newobj( 1 );
- $this->_put( '<PageInfo[ $n ]['n'] . ' 0 R ';
- }
- $this->_put( $kids . ']' );
- $this->_put( '/Count ' . $nb );
- if ( $this->DefOrientation == 'P' ) {
- $w = $this->DefPageSize[0];
- $h = $this->DefPageSize[1];
- } else {
- $w = $this->DefPageSize[1];
- $h = $this->DefPageSize[0];
- }
- $this->_put( sprintf( '/MediaBox [0 0 %.2F %.2F]', $w * $this->k, $h * $this->k ) );
- $this->_put( '>>' );
- $this->_put( 'endobj' );
- }
-
- protected function _putpage( $n ) {
- $this->_newobj();
- $this->_put( '<_put( '/Parent 1 0 R' );
- if ( isset( $this->PageInfo[ $n ]['size'] ) ) {
- $this->_put( sprintf( '/MediaBox [0 0 %.2F %.2F]', $this->PageInfo[ $n ]['size'][0], $this->PageInfo[ $n ]['size'][1] ) );
- }
- if ( isset( $this->PageInfo[ $n ]['rotation'] ) ) {
- $this->_put( '/Rotate ' . $this->PageInfo[ $n ]['rotation'] );
- }
- $this->_put( '/Resources 2 0 R' );
- if ( isset( $this->PageLinks[ $n ] ) ) {
- // Links
- $annots = '/Annots [';
- foreach ( $this->PageLinks[ $n ] as $pl ) {
- $rect = sprintf( '%.2F %.2F %.2F %.2F', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3] );
- $annots .= '<_textstring( $pl[4] ) . '>>>>';
- } else {
- $l = $this->links[ $pl[4] ];
- if ( isset( $this->PageInfo[ $l[0] ]['size'] ) ) {
- $h = $this->PageInfo[ $l[0] ]['size'][1];
- } else {
- $h = ( $this->DefOrientation == 'P' ) ? $this->DefPageSize[1] * $this->k : $this->DefPageSize[0] * $this->k;
- }
- $annots .= sprintf( '/Dest [%d 0 R /XYZ 0 %.2F null]>>', $this->PageInfo[ $l[0] ]['n'], $h - $l[1] * $this->k );
- }
- }
- $this->_put( $annots . ']' );
- }
- if ( $this->WithAlpha ) {
- $this->_put( '/Group <>' );
- }
- $this->_put( '/Contents ' . ( $this->n + 1 ) . ' 0 R>>' );
- $this->_put( 'endobj' );
- // Page content
- if ( ! empty( $this->AliasNbPages ) ) {
- $this->pages[ $n ] = str_replace( $this->AliasNbPages, $this->page, $this->pages[ $n ] );
- }
- $this->_putstreamobject( $this->pages[ $n ] );
- }
-
- protected function _newobj( $n = null ) {
- // Begin a new object
- if ( $n === null ) {
- $n = ++ $this->n;
- }
- $this->offsets[ $n ] = $this->_getoffset();
- $this->_put( $n . ' 0 obj' );
- }
-
- protected function _getoffset() {
- return strlen( $this->buffer );
- }
-
- protected function _textstring( $s ) {
- // Format a text string
- if ( ! $this->_isascii( $s ) ) {
- $s = $this->_UTF8toUTF16( $s );
- }
-
- return '(' . $this->_escape( $s ) . ')';
- }
-
- protected function _isascii( $s ) {
- // Test if string is ASCII
- $nb = strlen( $s );
- for ( $i = 0; $i < $nb; $i ++ ) {
- if ( ord( $s[ $i ] ) > 127 ) {
- return false;
- }
- }
-
- return true;
- }
-
- protected function _UTF8toUTF16( $s ) {
- // Convert UTF-8 to UTF-16BE with BOM
- $res = "\xFE\xFF";
- $nb = strlen( $s );
- $i = 0;
- while ( $i < $nb ) {
- $c1 = ord( $s[ $i ++ ] );
- if ( $c1 >= 224 ) {
- // 3-byte character
- $c2 = ord( $s[ $i ++ ] );
- $c3 = ord( $s[ $i ++ ] );
- $res .= chr( ( ( $c1 & 0x0F ) << 4 ) + ( ( $c2 & 0x3C ) >> 2 ) );
- $res .= chr( ( ( $c2 & 0x03 ) << 6 ) + ( $c3 & 0x3F ) );
- } elseif ( $c1 >= 192 ) {
- // 2-byte character
- $c2 = ord( $s[ $i ++ ] );
- $res .= chr( ( $c1 & 0x1C ) >> 2 );
- $res .= chr( ( ( $c1 & 0x03 ) << 6 ) + ( $c2 & 0x3F ) );
- } else {
- // Single-byte character
- $res .= "\0" . chr( $c1 );
- }
- }
-
- return $res;
- }
-
- protected function _putstreamobject( $data ) {
- if ( $this->compress ) {
- $entries = '/Filter /FlateDecode ';
- $data = gzcompress( $data );
- } else {
- $entries = '';
- }
- $entries .= '/Length ' . strlen( $data );
- $this->_newobj();
- $this->_put( '<<' . $entries . '>>' );
- $this->_putstream( $data );
- $this->_put( 'endobj' );
- }
-
- protected function _putstream( $data ) {
- $this->_put( 'stream' );
- $this->_put( $data );
- $this->_put( 'endstream' );
- }
-
- protected function _putresources() {
- $this->_putfonts();
- $this->_putimages();
- // Resource dictionary
- $this->_newobj( 2 );
- $this->_put( '<<' );
- $this->_putresourcedict();
- $this->_put( '>>' );
- $this->_put( 'endobj' );
- }
-
- protected function _putfonts() {
- foreach ( $this->FontFiles as $file => $info ) {
- // Font file embedding
- $this->_newobj();
- $this->FontFiles[ $file ]['n'] = $this->n;
- $font = file_get_contents( $this->fontpath . $file, true );
- if ( ! $font ) {
- $this->Error( 'Font file not found: ' . $file );
- }
- $compressed = ( substr( $file, - 2 ) == '.z' );
- if ( ! $compressed && isset( $info['length2'] ) ) {
- $font = substr( $font, 6, $info['length1'] ) . substr( $font, 6 + $info['length1'] + 6, $info['length2'] );
- }
- $this->_put( '<_put( '/Filter /FlateDecode' );
- }
- $this->_put( '/Length1 ' . $info['length1'] );
- if ( isset( $info['length2'] ) ) {
- $this->_put( '/Length2 ' . $info['length2'] . ' /Length3 0' );
- }
- $this->_put( '>>' );
- $this->_putstream( $font );
- $this->_put( 'endobj' );
- }
- foreach ( $this->fonts as $k => $font ) {
- // Encoding
- if ( isset( $font['diff'] ) ) {
- if ( ! isset( $this->encodings[ $font['enc'] ] ) ) {
- $this->_newobj();
- $this->_put( '<>' );
- $this->_put( 'endobj' );
- $this->encodings[ $font['enc'] ] = $this->n;
- }
- }
- // ToUnicode CMap
- if ( isset( $font['uv'] ) ) {
- if ( isset( $font['enc'] ) ) {
- $cmapkey = $font['enc'];
- } else {
- $cmapkey = $font['name'];
- }
- if ( ! isset( $this->cmaps[ $cmapkey ] ) ) {
- $cmap = $this->_tounicodecmap( $font['uv'] );
- $this->_putstreamobject( $cmap );
- $this->cmaps[ $cmapkey ] = $this->n;
- }
- }
- // Font object
- $this->fonts[ $k ]['n'] = $this->n + 1;
- $type = $font['type'];
- $name = $font['name'];
- if ( $font['subsetted'] ) {
- $name = 'AAAAAA+' . $name;
- }
- if ( $type == 'Core' ) {
- // Core font
- $this->_newobj();
- $this->_put( '<_put( '/BaseFont /' . $name );
- $this->_put( '/Subtype /Type1' );
- if ( $name != 'Symbol' && $name != 'ZapfDingbats' ) {
- $this->_put( '/Encoding /WinAnsiEncoding' );
- }
- if ( isset( $font['uv'] ) ) {
- $this->_put( '/ToUnicode ' . $this->cmaps[ $cmapkey ] . ' 0 R' );
- }
- $this->_put( '>>' );
- $this->_put( 'endobj' );
- } elseif ( $type == 'Type1' || $type == 'TrueType' ) {
- // Additional Type1 or TrueType/OpenType font
- $this->_newobj();
- $this->_put( '<_put( '/BaseFont /' . $name );
- $this->_put( '/Subtype /' . $type );
- $this->_put( '/FirstChar 32 /LastChar 255' );
- $this->_put( '/Widths ' . ( $this->n + 1 ) . ' 0 R' );
- $this->_put( '/FontDescriptor ' . ( $this->n + 2 ) . ' 0 R' );
- if ( isset( $font['diff'] ) ) {
- $this->_put( '/Encoding ' . $this->encodings[ $font['enc'] ] . ' 0 R' );
- } else {
- $this->_put( '/Encoding /WinAnsiEncoding' );
- }
- if ( isset( $font['uv'] ) ) {
- $this->_put( '/ToUnicode ' . $this->cmaps[ $cmapkey ] . ' 0 R' );
- }
- $this->_put( '>>' );
- $this->_put( 'endobj' );
- // Widths
- $this->_newobj();
- $cw = &$font['cw'];
- $s = '[';
- for ( $i = 32; $i <= 255; $i ++ ) {
- $s .= $cw[ chr( $i ) ] . ' ';
- }
- $this->_put( $s . ']' );
- $this->_put( 'endobj' );
- // Descriptor
- $this->_newobj();
- $s = '< $v ) {
- $s .= ' /' . $k . ' ' . $v;
- }
- if ( ! empty( $font['file'] ) ) {
- $s .= ' /FontFile' . ( $type == 'Type1' ? '' : '2' ) . ' ' . $this->FontFiles[ $font['file'] ]['n'] . ' 0 R';
- }
- $this->_put( $s . '>>' );
- $this->_put( 'endobj' );
- } else {
- // Allow for additional types
- $mtd = '_put' . strtolower( $type );
- if ( ! method_exists( $this, $mtd ) ) {
- $this->Error( 'Unsupported font type: ' . $type );
- }
- $this->$mtd( $font );
- }
- }
- }
-
- protected function _tounicodecmap( $uv ) {
- $ranges = '';
- $nbr = 0;
- $chars = '';
- $nbc = 0;
- foreach ( $uv as $c => $v ) {
- if ( is_array( $v ) ) {
- $ranges .= sprintf( "<%02X> <%02X> <%04X>\n", $c, $c + $v[1] - 1, $v[0] );
- $nbr ++;
- } else {
- $chars .= sprintf( "<%02X> <%04X>\n", $c, $v );
- $nbc ++;
- }
- }
- $s = "/CIDInit /ProcSet findresource begin\n";
- $s .= "12 dict begin\n";
- $s .= "begincmap\n";
- $s .= "/CIDSystemInfo\n";
- $s .= "<> def\n";
- $s .= "/CMapName /Adobe-Identity-UCS def\n";
- $s .= "/CMapType 2 def\n";
- $s .= "1 begincodespacerange\n";
- $s .= "<00> \n";
- $s .= "endcodespacerange\n";
- if ( $nbr > 0 ) {
- $s .= "$nbr beginbfrange\n";
- $s .= $ranges;
- $s .= "endbfrange\n";
- }
- if ( $nbc > 0 ) {
- $s .= "$nbc beginbfchar\n";
- $s .= $chars;
- $s .= "endbfchar\n";
- }
- $s .= "endcmap\n";
- $s .= "CMapName currentdict /CMap defineresource pop\n";
- $s .= "end\n";
- $s .= "end";
-
- return $s;
- }
-
- protected function _putimages() {
- foreach ( array_keys( $this->images ) as $file ) {
- $this->_putimage( $this->images[ $file ] );
- unset( $this->images[ $file ]['data'] );
- unset( $this->images[ $file ]['smask'] );
- }
- }
-
- protected function _putimage( &$info ) {
- $this->_newobj();
- $info['n'] = $this->n;
- $this->_put( '<_put( '/Subtype /Image' );
- $this->_put( '/Width ' . $info['w'] );
- $this->_put( '/Height ' . $info['h'] );
- if ( $info['cs'] == 'Indexed' ) {
- $this->_put( '/ColorSpace [/Indexed /DeviceRGB ' . ( strlen( $info['pal'] ) / 3 - 1 ) . ' ' . ( $this->n + 1 ) . ' 0 R]' );
- } else {
- $this->_put( '/ColorSpace /' . $info['cs'] );
- if ( $info['cs'] == 'DeviceCMYK' ) {
- $this->_put( '/Decode [1 0 1 0 1 0 1 0]' );
- }
- }
- $this->_put( '/BitsPerComponent ' . $info['bpc'] );
- if ( isset( $info['f'] ) ) {
- $this->_put( '/Filter /' . $info['f'] );
- }
- if ( isset( $info['dp'] ) ) {
- $this->_put( '/DecodeParms <<' . $info['dp'] . '>>' );
- }
- if ( isset( $info['trns'] ) && is_array( $info['trns'] ) ) {
- $trns = '';
- for ( $i = 0; $i < count( $info['trns'] ); $i ++ ) {
- $trns .= $info['trns'][ $i ] . ' ' . $info['trns'][ $i ] . ' ';
- }
- $this->_put( '/Mask [' . $trns . ']' );
- }
- if ( isset( $info['smask'] ) ) {
- $this->_put( '/SMask ' . ( $this->n + 1 ) . ' 0 R' );
- }
- $this->_put( '/Length ' . strlen( $info['data'] ) . '>>' );
- $this->_putstream( $info['data'] );
- $this->_put( 'endobj' );
- // Soft mask
- if ( isset( $info['smask'] ) ) {
- $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns ' . $info['w'];
- $smask = array(
- 'w' => $info['w'],
- 'h' => $info['h'],
- 'cs' => 'DeviceGray',
- 'bpc' => 8,
- 'f' => $info['f'],
- 'dp' => $dp,
- 'data' => $info['smask']
- );
- $this->_putimage( $smask );
- }
- // Palette
- if ( $info['cs'] == 'Indexed' ) {
- $this->_putstreamobject( $info['pal'] );
- }
- }
-
- protected function _putresourcedict() {
- $this->_put( '/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]' );
- $this->_put( '/Font <<' );
- foreach ( $this->fonts as $font ) {
- $this->_put( '/F' . $font['i'] . ' ' . $font['n'] . ' 0 R' );
- }
- $this->_put( '>>' );
- $this->_put( '/XObject <<' );
- $this->_putxobjectdict();
- $this->_put( '>>' );
- }
-
- protected function _putxobjectdict() {
- foreach ( $this->images as $image ) {
- $this->_put( '/I' . $image['i'] . ' ' . $image['n'] . ' 0 R' );
- }
- }
-
- protected function _putinfo() {
- $this->metadata['Producer'] = 'FPDF ' . FPDF_VERSION;
- $this->metadata['CreationDate'] = 'D:' . @date( 'YmdHis' );
- foreach ( $this->metadata as $key => $value ) {
- $this->_put( '/' . $key . ' ' . $this->_textstring( $value ) );
- }
- }
-
- protected function _putcatalog() {
- $n = $this->PageInfo[1]['n'];
- $this->_put( '/Type /Catalog' );
- $this->_put( '/Pages 1 0 R' );
- if ( $this->ZoomMode == 'fullpage' ) {
- $this->_put( '/OpenAction [' . $n . ' 0 R /Fit]' );
- } elseif ( $this->ZoomMode == 'fullwidth' ) {
- $this->_put( '/OpenAction [' . $n . ' 0 R /FitH null]' );
- } elseif ( $this->ZoomMode == 'real' ) {
- $this->_put( '/OpenAction [' . $n . ' 0 R /XYZ null null 1]' );
- } elseif ( ! is_string( $this->ZoomMode ) ) {
- $this->_put( '/OpenAction [' . $n . ' 0 R /XYZ null null ' . sprintf( '%.2F', $this->ZoomMode / 100 ) . ']' );
- }
- if ( $this->LayoutMode == 'single' ) {
- $this->_put( '/PageLayout /SinglePage' );
- } elseif ( $this->LayoutMode == 'continuous' ) {
- $this->_put( '/PageLayout /OneColumn' );
- } elseif ( $this->LayoutMode == 'two' ) {
- $this->_put( '/PageLayout /TwoColumnLeft' );
- }
- }
-
- protected function _puttrailer() {
- $this->_put( '/Size ' . ( $this->n + 1 ) );
- $this->_put( '/Root ' . $this->n . ' 0 R' );
- $this->_put( '/Info ' . ( $this->n - 1 ) . ' 0 R' );
- }
-
- protected function _checkoutput() {
- if ( PHP_SAPI != 'cli' ) {
- if ( headers_sent( $file, $line ) ) {
- $this->Error( "Some data has already been output, can't send PDF file (output started at $file:$line)" );
- }
- }
- if ( ob_get_length() ) {
- // The output buffer is not empty
- if ( preg_match( '/^(\xEF\xBB\xBF)?\s*$/', ob_get_contents() ) ) {
- // It contains only a UTF-8 BOM and/or whitespace, let's clean it
- ob_clean();
- }
+ protected $page; // current page number
+ protected $n; // current object number
+ protected $offsets; // array of object offsets
+ protected $buffer; // buffer holding in-memory PDF
+ protected $pages; // array containing pages
+ protected $state; // current document state
+ protected $compress; // compression flag
+ protected $k; // scale factor (number of points in user unit)
+ protected $DefOrientation; // default orientation
+ protected $CurOrientation; // current orientation
+ protected $StdPageSizes; // standard page sizes
+ protected $DefPageSize; // default page size
+ protected $CurPageSize; // current page size
+ protected $CurRotation; // current page rotation
+ protected $PageInfo; // page-related data
+ protected $wPt, $hPt; // dimensions of current page in points
+ protected $w, $h; // dimensions of current page in user unit
+ protected $lMargin; // left margin
+ protected $tMargin; // top margin
+ protected $rMargin; // right margin
+ protected $bMargin; // page break margin
+ protected $cMargin; // cell margin
+ protected $x, $y; // current position in user unit
+ protected $lasth; // height of last printed cell
+ protected $LineWidth; // line width in user unit
+ protected $fontpath; // path containing fonts
+ protected $CoreFonts; // array of core font names
+ protected $fonts; // array of used fonts
+ protected $FontFiles; // array of font files
+ protected $encodings; // array of encodings
+ protected $cmaps; // array of ToUnicode CMaps
+ protected $FontFamily; // current font family
+ protected $FontStyle; // current font style
+ protected $underline; // underlining flag
+ protected $CurrentFont; // current font info
+ protected $FontSizePt; // current font size in points
+ protected $FontSize; // current font size in user unit
+ protected $DrawColor; // commands for drawing color
+ protected $FillColor; // commands for filling color
+ protected $TextColor; // commands for text color
+ protected $ColorFlag; // indicates whether fill and text colors are different
+ protected $WithAlpha; // indicates whether alpha channel is used
+ protected $ws; // word spacing
+ protected $images; // array of used images
+ protected $PageLinks; // array of links in pages
+ protected $links; // array of internal links
+ protected $AutoPageBreak; // automatic page breaking
+ protected $PageBreakTrigger; // threshold used to trigger page breaks
+ protected $InHeader; // flag set when processing header
+ protected $InFooter; // flag set when processing footer
+ protected $AliasNbPages; // alias for total number of pages
+ protected $ZoomMode; // zoom display mode
+ protected $LayoutMode; // layout display mode
+ protected $metadata; // document properties
+ protected $PDFVersion; // PDF version number
+
+ /*******************************************************************************
+ * Public methods *
+ *******************************************************************************/
+
+ function __construct( $orientation = 'P', $unit = 'mm', $size = 'A4' ) {
+ // Some checks
+ $this->_dochecks();
+ // Initialization of properties
+ $this->state = 0;
+ $this->page = 0;
+ $this->n = 2;
+ $this->buffer = '';
+ $this->pages = array();
+ $this->PageInfo = array();
+ $this->fonts = array();
+ $this->FontFiles = array();
+ $this->encodings = array();
+ $this->cmaps = array();
+ $this->images = array();
+ $this->links = array();
+ $this->InHeader = false;
+ $this->InFooter = false;
+ $this->lasth = 0;
+ $this->FontFamily = '';
+ $this->FontStyle = '';
+ $this->FontSizePt = 12;
+ $this->underline = false;
+ $this->DrawColor = '0 G';
+ $this->FillColor = '0 g';
+ $this->TextColor = '0 g';
+ $this->ColorFlag = false;
+ $this->WithAlpha = false;
+ $this->ws = 0;
+ // Font path
+ if ( defined( 'FPDF_FONTPATH' ) ) {
+ $this->fontpath = FPDF_FONTPATH;
+ if ( substr( $this->fontpath, -1 ) != '/' && substr( $this->fontpath, -1 ) != '\\' ) {
+ $this->fontpath .= '/';
+ }
+ } elseif ( is_dir( dirname( __FILE__ ) . '/font' ) ) {
+ $this->fontpath = dirname( __FILE__ ) . '/font/';
+ } else {
+ $this->fontpath = '';
+ }
+ // Core fonts
+ $this->CoreFonts = array( 'courier', 'helvetica', 'times', 'symbol', 'zapfdingbats' );
+ // Scale factor
+ if ( $unit == 'pt' ) {
+ $this->k = 1;
+ } elseif ( $unit == 'mm' ) {
+ $this->k = 72 / 25.4;
+ } elseif ( $unit == 'cm' ) {
+ $this->k = 72 / 2.54;
+ } elseif ( $unit == 'in' ) {
+ $this->k = 72;
+ } else {
+ $this->Error( 'Incorrect unit: ' . $unit );
+ }
+ // Page sizes
+ $this->StdPageSizes = array(
+ 'a3' => array( 841.89, 1190.55 ),
+ 'a4' => array( 595.28, 841.89 ),
+ 'a5' => array( 420.94, 595.28 ),
+ 'letter' => array( 612, 792 ),
+ 'legal' => array( 612, 1008 )
+ );
+ $size = $this->_getpagesize( $size );
+ $this->DefPageSize = $size;
+ $this->CurPageSize = $size;
+ // Page orientation
+ $orientation = strtolower( $orientation );
+ if ( $orientation == 'p' || $orientation == 'portrait' ) {
+ $this->DefOrientation = 'P';
+ $this->w = $size[ 0 ];
+ $this->h = $size[ 1 ];
+ } elseif ( $orientation == 'l' || $orientation == 'landscape' ) {
+ $this->DefOrientation = 'L';
+ $this->w = $size[ 1 ];
+ $this->h = $size[ 0 ];
+ } else {
+ $this->Error( 'Incorrect orientation: ' . $orientation );
+ }
+ $this->CurOrientation = $this->DefOrientation;
+ $this->wPt = $this->w * $this->k;
+ $this->hPt = $this->h * $this->k;
+ // Page rotation
+ $this->CurRotation = 0;
+ // Page margins (1 cm)
+ $margin = 28.35 / $this->k;
+ $this->SetMargins( $margin, $margin );
+ // Interior cell margin (1 mm)
+ $this->cMargin = $margin / 10;
+ // Line width (0.2 mm)
+ $this->LineWidth = .567 / $this->k;
+ // Automatic page break
+ $this->SetAutoPageBreak( true, 2 * $margin );
+ // Default display mode
+ $this->SetDisplayMode( 'default' );
+ // Enable compression
+ $this->SetCompression( true );
+ // Set default PDF version number
+ $this->PDFVersion = '1.3';
+ }
+
+ /*******************************************************************************
+ * Protected methods *
+ *******************************************************************************/
+
+ protected function _dochecks() {
+ // Check mbstring overloading
+ if ( ini_get( 'mbstring.func_overload' ) & 2 ) {
+ $this->Error( 'mbstring overloading must be disabled' );
+ }
+ // Ensure runtime magic quotes are disabled
+ if ( get_magic_quotes_runtime() ) {
+ @set_magic_quotes_runtime( 0 );
+ }
+ }
+
+ function Error( $msg ) {
+ // Fatal error
+ throw new Exception( 'FPDF error: ' . $msg );
+ }
+
+ protected function _getpagesize( $size ) {
+ if ( is_string( $size ) ) {
+ $size = strtolower( $size );
+ if ( !isset( $this->StdPageSizes[ $size ] ) ) {
+ $this->Error( 'Unknown page size: ' . $size );
+ }
+ $a = $this->StdPageSizes[ $size ];
+
+ return array( $a[ 0 ] / $this->k, $a[ 1 ] / $this->k );
+ } else {
+ if ( $size[ 0 ] > $size[ 1 ] ) {
+ return array( $size[ 1 ], $size[ 0 ] );
+ } else {
+ return $size;
+ }
+ }
+ }
+
+ function SetMargins( $left, $top, $right = null ) {
+ // Set left, top and right margins
+ $this->lMargin = $left;
+ $this->tMargin = $top;
+ if ( $right === null ) {
+ $right = $left;
+ }
+ $this->rMargin = $right;
+ }
+
+ function SetAutoPageBreak( $auto, $margin = 0 ) {
+ // Set auto page break mode and triggering margin
+ $this->AutoPageBreak = $auto;
+ $this->bMargin = $margin;
+ $this->PageBreakTrigger = $this->h - $margin;
+ }
+
+ function SetDisplayMode( $zoom, $layout = 'default' ) {
+ // Set display mode in viewer
+ if ( $zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real' || $zoom == 'default' || !is_string( $zoom ) ) {
+ $this->ZoomMode = $zoom;
+ } else {
+ $this->Error( 'Incorrect zoom display mode: ' . $zoom );
+ }
+ if ( $layout == 'single' || $layout == 'continuous' || $layout == 'two' || $layout == 'default' ) {
+ $this->LayoutMode = $layout;
+ } else {
+ $this->Error( 'Incorrect layout display mode: ' . $layout );
+ }
+ }
+
+ function SetCompression( $compress ) {
+ // Set page compression
+ if ( function_exists( 'gzcompress' ) ) {
+ $this->compress = $compress;
+ } else {
+ $this->compress = false;
+ }
+ }
+
+ function SetLeftMargin( $margin ) {
+ // Set left margin
+ $this->lMargin = $margin;
+ if ( $this->page > 0 && $this->x < $margin ) {
+ $this->x = $margin;
+ }
+ }
+
+ function SetTopMargin( $margin ) {
+ // Set top margin
+ $this->tMargin = $margin;
+ }
+
+ function SetRightMargin( $margin ) {
+ // Set right margin
+ $this->rMargin = $margin;
+ }
+
+ function SetTitle( $title, $isUTF8 = false ) {
+ // Title of document
+ $this->metadata[ 'Title' ] = $isUTF8 ? $title : utf8_encode( $title );
+ }
+
+ function SetAuthor( $author, $isUTF8 = false ) {
+ // Author of document
+ $this->metadata[ 'Author' ] = $isUTF8 ? $author : utf8_encode( $author );
+ }
+
+ function SetSubject( $subject, $isUTF8 = false ) {
+ // Subject of document
+ $this->metadata[ 'Subject' ] = $isUTF8 ? $subject : utf8_encode( $subject );
+ }
+
+ function SetKeywords( $keywords, $isUTF8 = false ) {
+ // Keywords of document
+ $this->metadata[ 'Keywords' ] = $isUTF8 ? $keywords : utf8_encode( $keywords );
+ }
+
+ function SetCreator( $creator, $isUTF8 = false ) {
+ // Creator of document
+ $this->metadata[ 'Creator' ] = $isUTF8 ? $creator : utf8_encode( $creator );
+ }
+
+ function AliasNbPages( $alias = '{nb}' ) {
+ // Define an alias for total number of pages
+ $this->AliasNbPages = $alias;
+ }
+
+ function PageNo() {
+ // Get current page number
+ return $this->page;
+ }
+
+ function SetDrawColor( $r, $g = null, $b = null ) {
+ // Set color for all stroking operations
+ if ( ( $r == 0 && $g == 0 && $b == 0 ) || $g === null ) {
+ $this->DrawColor = sprintf( '%.3F G', $r / 255 );
+ } else {
+ $this->DrawColor = sprintf( '%.3F %.3F %.3F RG', $r / 255, $g / 255, $b / 255 );
+ }
+ if ( $this->page > 0 ) {
+ $this->_out( $this->DrawColor );
+ }
+ }
+
+ protected function _out( $s ) {
+ // Add a line to the document
+ if ( $this->state == 2 ) {
+ $this->pages[ $this->page ] .= $s . "\n";
+ } elseif ( $this->state == 1 ) {
+ $this->_put( $s );
+ } elseif ( $this->state == 0 ) {
+ $this->Error( 'No page has been added yet' );
+ } elseif ( $this->state == 3 ) {
+ $this->Error( 'The document is closed' );
+ }
+ }
+
+ protected function _put( $s ) {
+ $this->buffer .= $s . "\n";
+ }
+
+ function SetFillColor( $r, $g = null, $b = null ) {
+ // Set color for all filling operations
+ if ( ( $r == 0 && $g == 0 && $b == 0 ) || $g === null ) {
+ $this->FillColor = sprintf( '%.3F g', $r / 255 );
+ } else {
+ $this->FillColor = sprintf( '%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255 );
+ }
+ $this->ColorFlag = ( $this->FillColor != $this->TextColor );
+ if ( $this->page > 0 ) {
+ $this->_out( $this->FillColor );
+ }
+ }
+
+ function SetTextColor( $r, $g = null, $b = null ) {
+ // Set color for text
+ if ( ( $r == 0 && $g == 0 && $b == 0 ) || $g === null ) {
+ $this->TextColor = sprintf( '%.3F g', $r / 255 );
+ } else {
+ $this->TextColor = sprintf( '%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255 );
+ }
+ $this->ColorFlag = ( $this->FillColor != $this->TextColor );
+ }
+
+ function SetLineWidth( $width ) {
+ // Set line width
+ $this->LineWidth = $width;
+ if ( $this->page > 0 ) {
+ $this->_out( sprintf( '%.2F w', $width * $this->k ) );
+ }
+ }
+
+ function Line( $x1, $y1, $x2, $y2 ) {
+ // Draw a line
+ $this->_out( sprintf( '%.2F %.2F m %.2F %.2F l S', $x1 * $this->k, ( $this->h - $y1 ) * $this->k, $x2 * $this->k, ( $this->h - $y2 ) * $this->k ) );
+ }
+
+ function Rect( $x, $y, $w, $h, $style = '' ) {
+ // Draw a rectangle
+ if ( $style == 'F' ) {
+ $op = 'f';
+ } elseif ( $style == 'FD' || $style == 'DF' ) {
+ $op = 'B';
+ } else {
+ $op = 'S';
+ }
+ $this->_out( sprintf( '%.2F %.2F %.2F %.2F re %s', $x * $this->k, ( $this->h - $y ) * $this->k, $w * $this->k, -$h * $this->k, $op ) );
+ }
+
+ function SetFontSize( $size ) {
+ // Set font size in points
+ if ( $this->FontSizePt == $size ) {
+ return;
+ }
+ $this->FontSizePt = $size;
+ $this->FontSize = $size / $this->k;
+ if ( $this->page > 0 ) {
+ $this->_out( sprintf( 'BT /F%d %.2F Tf ET', $this->CurrentFont[ 'i' ], $this->FontSizePt ) );
+ }
+ }
+
+ function AddLink() {
+ // Create a new internal link
+ $n = count( $this->links ) + 1;
+ $this->links[ $n ] = array( 0, 0 );
+
+ return $n;
+ }
+
+ function SetLink( $link, $y = 0, $page = -1 ) {
+ // Set destination of internal link
+ if ( $y == -1 ) {
+ $y = $this->y;
+ }
+ if ( $page == -1 ) {
+ $page = $this->page;
+ }
+ $this->links[ $link ] = array( $page, $y );
+ }
+
+ function Text( $x, $y, $txt ) {
+ // Output a string
+ if ( !isset( $this->CurrentFont ) ) {
+ $this->Error( 'No font has been set' );
+ }
+ $s = sprintf( 'BT %.2F %.2F Td (%s) Tj ET', $x * $this->k, ( $this->h - $y ) * $this->k, $this->_escape( $txt ) );
+ if ( $this->underline && $txt != '' ) {
+ $s .= ' ' . $this->_dounderline( $x, $y, $txt );
+ }
+ if ( $this->ColorFlag ) {
+ $s = 'q ' . $this->TextColor . ' ' . $s . ' Q';
+ }
+ $this->_out( $s );
+ }
+
+ protected function _escape( $s ) {
+ // Escape special characters
+ if ( strpos( $s, '(' ) !== false || strpos( $s, ')' ) !== false || strpos( $s, '\\' ) !== false || strpos( $s, "\r" ) !== false ) {
+ return str_replace( array( '\\', '(', ')', "\r" ), array( '\\\\', '\\(', '\\)', '\\r' ), $s );
+ } else {
+ return $s;
+ }
+ }
+
+ protected function _dounderline( $x, $y, $txt ) {
+ // Underline text
+ $up = $this->CurrentFont[ 'up' ];
+ $ut = $this->CurrentFont[ 'ut' ];
+ $w = $this->GetStringWidth( $txt ) + $this->ws * substr_count( $txt, ' ' );
+
+ return sprintf( '%.2F %.2F %.2F %.2F re f', $x * $this->k, ( $this->h - ( $y - $up / 1000 * $this->FontSize ) ) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt );
+ }
+
+ function GetStringWidth( $s ) {
+ // Get width of a string in the current font
+ $s = (string)$s;
+ $cw = &$this->CurrentFont[ 'cw' ];
+ $w = 0;
+ $l = strlen( $s );
+ for ( $i = 0; $i < $l; $i++ ) {
+ $w += $cw[ $s[ $i ] ];
+ }
+
+ return $w * $this->FontSize / 1000;
+ }
+
+ function MultiCell( $w, $h, $txt, $border = 0, $align = 'J', $fill = false ) {
+ // Output text with automatic or explicit line breaks
+ if ( !isset( $this->CurrentFont ) ) {
+ $this->Error( 'No font has been set' );
+ }
+ $cw = &$this->CurrentFont[ 'cw' ];
+ if ( $w == 0 ) {
+ $w = $this->w - $this->rMargin - $this->x;
+ }
+ $wmax = ( $w - 2 * $this->cMargin ) * 1000 / $this->FontSize;
+ $s = str_replace( "\r", '', $txt );
+ $nb = strlen( $s );
+ if ( $nb > 0 && $s[ $nb - 1 ] == "\n" ) {
+ $nb--;
+ }
+ $b = 0;
+ if ( $border ) {
+ if ( $border == 1 ) {
+ $border = 'LTRB';
+ $b = 'LRT';
+ $b2 = 'LR';
+ } else {
+ $b2 = '';
+ if ( strpos( $border, 'L' ) !== false ) {
+ $b2 .= 'L';
+ }
+ if ( strpos( $border, 'R' ) !== false ) {
+ $b2 .= 'R';
+ }
+ $b = ( strpos( $border, 'T' ) !== false ) ? $b2 . 'T' : $b2;
+ }
+ }
+ $sep = -1;
+ $i = 0;
+ $j = 0;
+ $l = 0;
+ $ns = 0;
+ $nl = 1;
+ while ( $i < $nb ) {
+ // Get next character
+ $c = $s[ $i ];
+ if ( $c == "\n" ) {
+ // Explicit line break
+ if ( $this->ws > 0 ) {
+ $this->ws = 0;
+ $this->_out( '0 Tw' );
+ }
+ $this->Cell( $w, $h, substr( $s, $j, $i - $j ), $b, 2, $align, $fill );
+ $i++;
+ $sep = -1;
+ $j = $i;
+ $l = 0;
+ $ns = 0;
+ $nl++;
+ if ( $border && $nl == 2 ) {
+ $b = $b2;
+ }
+ continue;
+ }
+ if ( $c == ' ' ) {
+ $sep = $i;
+ $ls = $l;
+ $ns++;
+ }
+ $l += $cw[ $c ];
+ if ( $l > $wmax ) {
+ // Automatic line break
+ if ( $sep == -1 ) {
+ if ( $i == $j ) {
+ $i++;
+ }
+ if ( $this->ws > 0 ) {
+ $this->ws = 0;
+ $this->_out( '0 Tw' );
+ }
+ $this->Cell( $w, $h, substr( $s, $j, $i - $j ), $b, 2, $align, $fill );
+ } else {
+ if ( $align == 'J' ) {
+ $this->ws = ( $ns > 1 ) ? ( $wmax - $ls ) / 1000 * $this->FontSize / ( $ns - 1 ) : 0;
+ $this->_out( sprintf( '%.3F Tw', $this->ws * $this->k ) );
+ }
+ $this->Cell( $w, $h, substr( $s, $j, $sep - $j ), $b, 2, $align, $fill );
+ $i = $sep + 1;
+ }
+ $sep = -1;
+ $j = $i;
+ $l = 0;
+ $ns = 0;
+ $nl++;
+ if ( $border && $nl == 2 ) {
+ $b = $b2;
+ }
+ } else {
+ $i++;
+ }
+ }
+ // Last chunk
+ if ( $this->ws > 0 ) {
+ $this->ws = 0;
+ $this->_out( '0 Tw' );
+ }
+ if ( $border && strpos( $border, 'B' ) !== false ) {
+ $b .= 'B';
+ }
+ $this->Cell( $w, $h, substr( $s, $j, $i - $j ), $b, 2, $align, $fill );
+ $this->x = $this->lMargin;
+ }
+
+ function Cell( $w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = false, $link = '' ) {
+ // Output a cell
+ $k = $this->k;
+ if ( $this->y + $h > $this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak() ) {
+ // Automatic page break
+ $x = $this->x;
+ $ws = $this->ws;
+ if ( $ws > 0 ) {
+ $this->ws = 0;
+ $this->_out( '0 Tw' );
+ }
+ $this->AddPage( $this->CurOrientation, $this->CurPageSize, $this->CurRotation );
+ $this->x = $x;
+ if ( $ws > 0 ) {
+ $this->ws = $ws;
+ $this->_out( sprintf( '%.3F Tw', $ws * $k ) );
+ }
+ }
+ if ( $w == 0 ) {
+ $w = $this->w - $this->rMargin - $this->x;
+ }
+ $s = '';
+ if ( $fill || $border == 1 ) {
+ if ( $fill ) {
+ $op = ( $border == 1 ) ? 'B' : 'f';
+ } else {
+ $op = 'S';
+ }
+ $s = sprintf( '%.2F %.2F %.2F %.2F re %s ', $this->x * $k, ( $this->h - $this->y ) * $k, $w * $k, -$h * $k, $op );
+ }
+ if ( is_string( $border ) ) {
+ $x = $this->x;
+ $y = $this->y;
+ if ( strpos( $border, 'L' ) !== false ) {
+ $s .= sprintf( '%.2F %.2F m %.2F %.2F l S ', $x * $k, ( $this->h - $y ) * $k, $x * $k, ( $this->h - ( $y + $h ) ) * $k );
+ }
+ if ( strpos( $border, 'T' ) !== false ) {
+ $s .= sprintf( '%.2F %.2F m %.2F %.2F l S ', $x * $k, ( $this->h - $y ) * $k, ( $x + $w ) * $k, ( $this->h - $y ) * $k );
+ }
+ if ( strpos( $border, 'R' ) !== false ) {
+ $s .= sprintf( '%.2F %.2F m %.2F %.2F l S ', ( $x + $w ) * $k, ( $this->h - $y ) * $k, ( $x + $w ) * $k, ( $this->h - ( $y + $h ) ) * $k );
+ }
+ if ( strpos( $border, 'B' ) !== false ) {
+ $s .= sprintf( '%.2F %.2F m %.2F %.2F l S ', $x * $k, ( $this->h - ( $y + $h ) ) * $k, ( $x + $w ) * $k, ( $this->h - ( $y + $h ) ) * $k );
+ }
+ }
+ if ( $txt !== '' ) {
+ if ( !isset( $this->CurrentFont ) ) {
+ $this->Error( 'No font has been set' );
+ }
+ if ( $align == 'R' ) {
+ $dx = $w - $this->cMargin - $this->GetStringWidth( $txt );
+ } elseif ( $align == 'C' ) {
+ $dx = ( $w - $this->GetStringWidth( $txt ) ) / 2;
+ } else {
+ $dx = $this->cMargin;
+ }
+ if ( $this->ColorFlag ) {
+ $s .= 'q ' . $this->TextColor . ' ';
+ }
+ $s .= sprintf( 'BT %.2F %.2F Td (%s) Tj ET', ( $this->x + $dx ) * $k, ( $this->h - ( $this->y + .5 * $h + .3 * $this->FontSize ) ) * $k, $this->_escape( $txt ) );
+ if ( $this->underline ) {
+ $s .= ' ' . $this->_dounderline( $this->x + $dx, $this->y + .5 * $h + .3 * $this->FontSize, $txt );
+ }
+ if ( $this->ColorFlag ) {
+ $s .= ' Q';
+ }
+ if ( $link ) {
+ $this->Link( $this->x + $dx, $this->y + .5 * $h - .5 * $this->FontSize, $this->GetStringWidth( $txt ), $this->FontSize, $link );
+ }
+ }
+ if ( $s ) {
+ $this->_out( $s );
+ }
+ $this->lasth = $h;
+ if ( $ln > 0 ) {
+ // Go to next line
+ $this->y += $h;
+ if ( $ln == 1 ) {
+ $this->x = $this->lMargin;
+ }
+ } else {
+ $this->x += $w;
+ }
+ }
+
+ function AcceptPageBreak() {
+ // Accept automatic page break or not
+ return $this->AutoPageBreak;
+ }
+
+ function AddPage( $orientation = '', $size = '', $rotation = 0 ) {
+ // Start a new page
+ if ( $this->state == 3 ) {
+ $this->Error( 'The document is closed' );
+ }
+ $family = $this->FontFamily;
+ $style = $this->FontStyle . ( $this->underline ? 'U' : '' );
+ $fontsize = $this->FontSizePt;
+ $lw = $this->LineWidth;
+ $dc = $this->DrawColor;
+ $fc = $this->FillColor;
+ $tc = $this->TextColor;
+ $cf = $this->ColorFlag;
+ if ( $this->page > 0 ) {
+ // Page footer
+ $this->InFooter = true;
+ $this->Footer();
+ $this->InFooter = false;
+ // Close page
+ $this->_endpage();
+ }
+ // Start new page
+ $this->_beginpage( $orientation, $size, $rotation );
+ // Set line cap style to square
+ $this->_out( '2 J' );
+ // Set line width
+ $this->LineWidth = $lw;
+ $this->_out( sprintf( '%.2F w', $lw * $this->k ) );
+ // Set font
+ if ( $family ) {
+ $this->SetFont( $family, $style, $fontsize );
+ }
+ // Set colors
+ $this->DrawColor = $dc;
+ if ( $dc != '0 G' ) {
+ $this->_out( $dc );
+ }
+ $this->FillColor = $fc;
+ if ( $fc != '0 g' ) {
+ $this->_out( $fc );
+ }
+ $this->TextColor = $tc;
+ $this->ColorFlag = $cf;
+ // Page header
+ $this->InHeader = true;
+ $this->Header();
+ $this->InHeader = false;
+ // Restore line width
+ if ( $this->LineWidth != $lw ) {
+ $this->LineWidth = $lw;
+ $this->_out( sprintf( '%.2F w', $lw * $this->k ) );
+ }
+ // Restore font
+ if ( $family ) {
+ $this->SetFont( $family, $style, $fontsize );
+ }
+ // Restore colors
+ if ( $this->DrawColor != $dc ) {
+ $this->DrawColor = $dc;
+ $this->_out( $dc );
+ }
+ if ( $this->FillColor != $fc ) {
+ $this->FillColor = $fc;
+ $this->_out( $fc );
+ }
+ $this->TextColor = $tc;
+ $this->ColorFlag = $cf;
+ }
+
+ function Footer() {
+ // To be implemented in your own inherited class
+ }
+
+ protected function _endpage() {
+ $this->state = 1;
+ }
+
+ protected function _beginpage( $orientation, $size, $rotation ) {
+ $this->page++;
+ $this->pages[ $this->page ] = '';
+ $this->state = 2;
+ $this->x = $this->lMargin;
+ $this->y = $this->tMargin;
+ $this->FontFamily = '';
+ // Check page size and orientation
+ if ( $orientation == '' ) {
+ $orientation = $this->DefOrientation;
+ } else {
+ $orientation = strtoupper( $orientation[ 0 ] );
+ }
+ if ( $size == '' ) {
+ $size = $this->DefPageSize;
+ } else {
+ $size = $this->_getpagesize( $size );
+ }
+ if ( $orientation != $this->CurOrientation || $size[ 0 ] != $this->CurPageSize[ 0 ] || $size[ 1 ] != $this->CurPageSize[ 1 ] ) {
+ // New size or orientation
+ if ( $orientation == 'P' ) {
+ $this->w = $size[ 0 ];
+ $this->h = $size[ 1 ];
+ } else {
+ $this->w = $size[ 1 ];
+ $this->h = $size[ 0 ];
+ }
+ $this->wPt = $this->w * $this->k;
+ $this->hPt = $this->h * $this->k;
+ $this->PageBreakTrigger = $this->h - $this->bMargin;
+ $this->CurOrientation = $orientation;
+ $this->CurPageSize = $size;
+ }
+ if ( $orientation != $this->DefOrientation || $size[ 0 ] != $this->DefPageSize[ 0 ] || $size[ 1 ] != $this->DefPageSize[ 1 ] ) {
+ $this->PageInfo[ $this->page ][ 'size' ] = array( $this->wPt, $this->hPt );
+ }
+ if ( $rotation != 0 ) {
+ if ( $rotation % 90 != 0 ) {
+ $this->Error( 'Incorrect rotation value: ' . $rotation );
+ }
+ $this->CurRotation = $rotation;
+ $this->PageInfo[ $this->page ][ 'rotation' ] = $rotation;
+ }
+ }
+
+ function SetFont( $family, $style = '', $size = 0 ) {
+ // Select a font; size given in points
+ if ( $family == '' ) {
+ $family = $this->FontFamily;
+ } else {
+ $family = strtolower( $family );
+ }
+ $style = strtoupper( $style );
+ if ( strpos( $style, 'U' ) !== false ) {
+ $this->underline = true;
+ $style = str_replace( 'U', '', $style );
+ } else {
+ $this->underline = false;
+ }
+ if ( $style == 'IB' ) {
+ $style = 'BI';
+ }
+ if ( $size == 0 ) {
+ $size = $this->FontSizePt;
+ }
+ // Test if font is already selected
+ if ( $this->FontFamily == $family && $this->FontStyle == $style && $this->FontSizePt == $size ) {
+ return;
+ }
+ // Test if font is already loaded
+ $fontkey = $family . $style;
+ if ( !isset( $this->fonts[ $fontkey ] ) ) {
+ // Test if one of the core fonts
+ if ( $family == 'arial' ) {
+ $family = 'helvetica';
+ }
+ if ( in_array( $family, $this->CoreFonts ) ) {
+ if ( $family == 'symbol' || $family == 'zapfdingbats' ) {
+ $style = '';
+ }
+ $fontkey = $family . $style;
+ if ( !isset( $this->fonts[ $fontkey ] ) ) {
+ $this->AddFont( $family, $style );
+ }
+ } else {
+ $this->Error( 'Undefined font: ' . $family . ' ' . $style );
+ }
+ }
+ // Select it
+ $this->FontFamily = $family;
+ $this->FontStyle = $style;
+ $this->FontSizePt = $size;
+ $this->FontSize = $size / $this->k;
+ $this->CurrentFont = &$this->fonts[ $fontkey ];
+ if ( $this->page > 0 ) {
+ $this->_out( sprintf( 'BT /F%d %.2F Tf ET', $this->CurrentFont[ 'i' ], $this->FontSizePt ) );
+ }
+ }
+
+ function AddFont( $family, $style = '', $file = '' ) {
+ // Add a TrueType, OpenType or Type1 font
+ $family = strtolower( $family );
+ if ( $file == '' ) {
+ $file = str_replace( ' ', '', $family ) . strtolower( $style ) . '.php';
+ }
+ $style = strtoupper( $style );
+ if ( $style == 'IB' ) {
+ $style = 'BI';
+ }
+ $fontkey = $family . $style;
+ if ( isset( $this->fonts[ $fontkey ] ) ) {
+ return;
+ }
+ $info = $this->_loadfont( $file );
+ $info[ 'i' ] = count( $this->fonts ) + 1;
+ if ( !empty( $info[ 'file' ] ) ) {
+ // Embedded font
+ if ( $info[ 'type' ] == 'TrueType' ) {
+ $this->FontFiles[ $info[ 'file' ] ] = array( 'length1' => $info[ 'originalsize' ] );
+ } else {
+ $this->FontFiles[ $info[ 'file' ] ] = array( 'length1' => $info[ 'size1' ], 'length2' => $info[ 'size2' ] );
+ }
+ }
+ $this->fonts[ $fontkey ] = $info;
+ }
+
+ protected function _loadfont( $font ) {
+ // Load a font definition file from the font directory
+ if ( strpos( $font, '/' ) !== false || strpos( $font, "\\" ) !== false ) {
+ $this->Error( 'Incorrect font definition file name: ' . $font );
+ }
+ include( $this->fontpath . $font );
+ if ( !isset( $name ) ) {
+ $this->Error( 'Could not include font definition file' );
+ }
+ if ( isset( $enc ) ) {
+ $enc = strtolower( $enc );
+ }
+ if ( !isset( $subsetted ) ) {
+ $subsetted = false;
+ }
+
+ return get_defined_vars();
+ }
+
+ function Header() {
+ // To be implemented in your own inherited class
+ }
+
+ function Link( $x, $y, $w, $h, $link ) {
+ // Put a link on the page
+ $this->PageLinks[ $this->page ][] = array(
+ $x * $this->k,
+ $this->hPt - $y * $this->k,
+ $w * $this->k,
+ $h * $this->k,
+ $link
+ );
+ }
+
+ function Write( $h, $txt, $link = '' ) {
+ // Output text in flowing mode
+ if ( !isset( $this->CurrentFont ) ) {
+ $this->Error( 'No font has been set' );
+ }
+ $cw = &$this->CurrentFont[ 'cw' ];
+ $w = $this->w - $this->rMargin - $this->x;
+ $wmax = ( $w - 2 * $this->cMargin ) * 1000 / $this->FontSize;
+ $s = str_replace( "\r", '', $txt );
+ $nb = strlen( $s );
+ $sep = -1;
+ $i = 0;
+ $j = 0;
+ $l = 0;
+ $nl = 1;
+ while ( $i < $nb ) {
+ // Get next character
+ $c = $s[ $i ];
+ if ( $c == "\n" ) {
+ // Explicit line break
+ $this->Cell( $w, $h, substr( $s, $j, $i - $j ), 0, 2, '', false, $link );
+ $i++;
+ $sep = -1;
+ $j = $i;
+ $l = 0;
+ if ( $nl == 1 ) {
+ $this->x = $this->lMargin;
+ $w = $this->w - $this->rMargin - $this->x;
+ $wmax = ( $w - 2 * $this->cMargin ) * 1000 / $this->FontSize;
+ }
+ $nl++;
+ continue;
+ }
+ if ( $c == ' ' ) {
+ $sep = $i;
+ }
+ $l += $cw[ $c ];
+ if ( $l > $wmax ) {
+ // Automatic line break
+ if ( $sep == -1 ) {
+ if ( $this->x > $this->lMargin ) {
+ // Move to next line
+ $this->x = $this->lMargin;
+ $this->y += $h;
+ $w = $this->w - $this->rMargin - $this->x;
+ $wmax = ( $w - 2 * $this->cMargin ) * 1000 / $this->FontSize;
+ $i++;
+ $nl++;
+ continue;
+ }
+ if ( $i == $j ) {
+ $i++;
+ }
+ $this->Cell( $w, $h, substr( $s, $j, $i - $j ), 0, 2, '', false, $link );
+ } else {
+ $this->Cell( $w, $h, substr( $s, $j, $sep - $j ), 0, 2, '', false, $link );
+ $i = $sep + 1;
+ }
+ $sep = -1;
+ $j = $i;
+ $l = 0;
+ if ( $nl == 1 ) {
+ $this->x = $this->lMargin;
+ $w = $this->w - $this->rMargin - $this->x;
+ $wmax = ( $w - 2 * $this->cMargin ) * 1000 / $this->FontSize;
+ }
+ $nl++;
+ } else {
+ $i++;
+ }
+ }
+ // Last chunk
+ if ( $i != $j ) {
+ $this->Cell( $l / 1000 * $this->FontSize, $h, substr( $s, $j ), 0, 0, '', false, $link );
+ }
+ }
+
+ function Ln( $h = null ) {
+ // Line feed; default value is the last cell height
+ $this->x = $this->lMargin;
+ if ( $h === null ) {
+ $this->y += $this->lasth;
+ } else {
+ $this->y += $h;
+ }
+ }
+
+ function Image( $file, $x = null, $y = null, $w = 0, $h = 0, $type = '', $link = '' ) {
+ // Put an image on the page
+ if ( $file == '' ) {
+ $this->Error( 'Image file name is empty' );
+ }
+ if ( !isset( $this->images[ $file ] ) ) {
+ // First use of this image, get info
+ if ( $type == '' ) {
+ $pos = strrpos( $file, '.' );
+ if ( !$pos ) {
+ $this->Error( 'Image file has no extension and no type was specified: ' . $file );
+ }
+ $type = substr( $file, $pos + 1 );
+ }
+ $type = strtolower( $type );
+ if ( $type == 'jpeg' ) {
+ $type = 'jpg';
+ }
+ $mtd = '_parse' . $type;
+ if ( !method_exists( $this, $mtd ) ) {
+ $this->Error( 'Unsupported image type: ' . $type );
+ }
+ $info = $this->$mtd( $file );
+ $info[ 'i' ] = count( $this->images ) + 1;
+ $this->images[ $file ] = $info;
+ } else {
+ $info = $this->images[ $file ];
+ }
+
+ // Automatic width and height calculation if needed
+ if ( $w == 0 && $h == 0 ) {
+ // Put image at 96 dpi
+ $w = -96;
+ $h = -96;
+ }
+ if ( $w < 0 ) {
+ $w = -$info[ 'w' ] * 72 / $w / $this->k;
+ }
+ if ( $h < 0 ) {
+ $h = -$info[ 'h' ] * 72 / $h / $this->k;
+ }
+ if ( $w == 0 ) {
+ $w = $h * $info[ 'w' ] / $info[ 'h' ];
+ }
+ if ( $h == 0 ) {
+ $h = $w * $info[ 'h' ] / $info[ 'w' ];
+ }
+
+ // Flowing mode
+ if ( $y === null ) {
+ if ( $this->y + $h > $this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak() ) {
+ // Automatic page break
+ $x2 = $this->x;
+ $this->AddPage( $this->CurOrientation, $this->CurPageSize, $this->CurRotation );
+ $this->x = $x2;
+ }
+ $y = $this->y;
+ $this->y += $h;
+ }
+
+ if ( $x === null ) {
+ $x = $this->x;
+ }
+ $this->_out( sprintf( 'q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q', $w * $this->k, $h * $this->k, $x * $this->k, ( $this->h - ( $y + $h ) ) * $this->k, $info[ 'i' ] ) );
+ if ( $link ) {
+ $this->Link( $x, $y, $w, $h, $link );
+ }
+ }
+
+ function GetPageWidth() {
+ // Get current page width
+ return $this->w;
+ }
+
+ function GetPageHeight() {
+ // Get current page height
+ return $this->h;
+ }
+
+ function GetX() {
+ // Get x position
+ return $this->x;
+ }
+
+ function SetX( $x ) {
+ // Set x position
+ if ( $x >= 0 ) {
+ $this->x = $x;
+ } else {
+ $this->x = $this->w + $x;
+ }
+ }
+
+ function GetY() {
+ // Get y position
+ return $this->y;
+ }
+
+ function SetXY( $x, $y ) {
+ // Set x and y positions
+ $this->SetX( $x );
+ $this->SetY( $y, false );
+ }
+
+ function SetY( $y, $resetX = true ) {
+ // Set y position and optionally reset x
+ if ( $y >= 0 ) {
+ $this->y = $y;
+ } else {
+ $this->y = $this->h + $y;
+ }
+ if ( $resetX ) {
+ $this->x = $this->lMargin;
+ }
+ }
+
+ function Output( $dest = '', $name = '', $isUTF8 = false ) {
+ // Output PDF to some destination
+ $this->Close();
+ if ( strlen( $name ) == 1 && strlen( $dest ) != 1 ) {
+ // Fix parameter order
+ $tmp = $dest;
+ $dest = $name;
+ $name = $tmp;
+ }
+ if ( $dest == '' ) {
+ $dest = 'I';
+ }
+ if ( $name == '' ) {
+ $name = 'doc.pdf';
+ }
+ switch ( strtoupper( $dest ) ) {
+ case 'I':
+ // Send to standard output
+ $this->_checkoutput();
+ if ( PHP_SAPI != 'cli' ) {
+ // We send to a browser
+ header( 'Content-Type: application/pdf' );
+ header( 'Content-Disposition: inline; ' . $this->_httpencode( 'filename', $name, $isUTF8 ) );
+ header( 'Cache-Control: private, max-age=0, must-revalidate' );
+ header( 'Pragma: public' );
+ }
+ echo $this->buffer;
+ break;
+ case 'D':
+ // Download file
+ $this->_checkoutput();
+ header( 'Content-Type: application/x-download' );
+ header( 'Content-Disposition: attachment; ' . $this->_httpencode( 'filename', $name, $isUTF8 ) );
+ header( 'Cache-Control: private, max-age=0, must-revalidate' );
+ header( 'Pragma: public' );
+ echo $this->buffer;
+ break;
+ case 'F':
+ // Save to local file
+ if ( !file_put_contents( $name, $this->buffer ) ) {
+ $this->Error( 'Unable to create output file: ' . $name );
+ }
+ break;
+ case 'S':
+ // Return as a string
+ return $this->buffer;
+ default:
+ $this->Error( 'Incorrect output destination: ' . $dest );
+ }
+
+ return '';
+ }
+
+ function Close() {
+ // Terminate document
+ if ( $this->state == 3 ) {
+ return;
+ }
+ if ( $this->page == 0 ) {
+ $this->AddPage();
+ }
+ // Page footer
+ $this->InFooter = true;
+ $this->Footer();
+ $this->InFooter = false;
+ // Close page
+ $this->_endpage();
+ // Close document
+ $this->_enddoc();
+ }
+
+ protected function _enddoc() {
+ $this->_putheader();
+ $this->_putpages();
+ $this->_putresources();
+ // Info
+ $this->_newobj();
+ $this->_put( '<<' );
+ $this->_putinfo();
+ $this->_put( '>>' );
+ $this->_put( 'endobj' );
+ // Catalog
+ $this->_newobj();
+ $this->_put( '<<' );
+ $this->_putcatalog();
+ $this->_put( '>>' );
+ $this->_put( 'endobj' );
+ // Cross-ref
+ $offset = $this->_getoffset();
+ $this->_put( 'xref' );
+ $this->_put( '0 ' . ( $this->n + 1 ) );
+ $this->_put( '0000000000 65535 f ' );
+ for ( $i = 1; $i <= $this->n; $i++ ) {
+ $this->_put( sprintf( '%010d 00000 n ', $this->offsets[ $i ] ) );
+ }
+ // Trailer
+ $this->_put( 'trailer' );
+ $this->_put( '<<' );
+ $this->_puttrailer();
+ $this->_put( '>>' );
+ $this->_put( 'startxref' );
+ $this->_put( $offset );
+ $this->_put( '%%EOF' );
+ $this->state = 3;
+ }
+
+ protected function _putheader() {
+ $this->_put( '%PDF-' . $this->PDFVersion );
+ }
+
+ protected function _putpages() {
+ $nb = $this->page;
+ for ( $n = 1; $n <= $nb; $n++ ) {
+ $this->PageInfo[ $n ][ 'n' ] = $this->n + 1 + 2 * ( $n - 1 );
+ }
+ for ( $n = 1; $n <= $nb; $n++ ) {
+ $this->_putpage( $n );
+ }
+ // Pages root
+ $this->_newobj( 1 );
+ $this->_put( '<PageInfo[ $n ][ 'n' ] . ' 0 R ';
+ }
+ $this->_put( $kids . ']' );
+ $this->_put( '/Count ' . $nb );
+ if ( $this->DefOrientation == 'P' ) {
+ $w = $this->DefPageSize[ 0 ];
+ $h = $this->DefPageSize[ 1 ];
+ } else {
+ $w = $this->DefPageSize[ 1 ];
+ $h = $this->DefPageSize[ 0 ];
+ }
+ $this->_put( sprintf( '/MediaBox [0 0 %.2F %.2F]', $w * $this->k, $h * $this->k ) );
+ $this->_put( '>>' );
+ $this->_put( 'endobj' );
+ }
+
+ protected function _putpage( $n ) {
+ $this->_newobj();
+ $this->_put( '<_put( '/Parent 1 0 R' );
+ if ( isset( $this->PageInfo[ $n ][ 'size' ] ) ) {
+ $this->_put( sprintf( '/MediaBox [0 0 %.2F %.2F]', $this->PageInfo[ $n ][ 'size' ][ 0 ], $this->PageInfo[ $n ][ 'size' ][ 1 ] ) );
+ }
+ if ( isset( $this->PageInfo[ $n ][ 'rotation' ] ) ) {
+ $this->_put( '/Rotate ' . $this->PageInfo[ $n ][ 'rotation' ] );
+ }
+ $this->_put( '/Resources 2 0 R' );
+ if ( isset( $this->PageLinks[ $n ] ) ) {
+ // Links
+ $annots = '/Annots [';
+ foreach ( $this->PageLinks[ $n ] as $pl ) {
+ $rect = sprintf( '%.2F %.2F %.2F %.2F', $pl[ 0 ], $pl[ 1 ], $pl[ 0 ] + $pl[ 2 ], $pl[ 1 ] - $pl[ 3 ] );
+ $annots .= '<_textstring( $pl[ 4 ] ) . '>>>>';
+ } else {
+ $l = $this->links[ $pl[ 4 ] ];
+ if ( isset( $this->PageInfo[ $l[ 0 ] ][ 'size' ] ) ) {
+ $h = $this->PageInfo[ $l[ 0 ] ][ 'size' ][ 1 ];
+ } else {
+ $h = ( $this->DefOrientation == 'P' ) ? $this->DefPageSize[ 1 ] * $this->k : $this->DefPageSize[ 0 ] * $this->k;
+ }
+ $annots .= sprintf( '/Dest [%d 0 R /XYZ 0 %.2F null]>>', $this->PageInfo[ $l[ 0 ] ][ 'n' ], $h - $l[ 1 ] * $this->k );
+ }
+ }
+ $this->_put( $annots . ']' );
+ }
+ if ( $this->WithAlpha ) {
+ $this->_put( '/Group <>' );
+ }
+ $this->_put( '/Contents ' . ( $this->n + 1 ) . ' 0 R>>' );
+ $this->_put( 'endobj' );
+ // Page content
+ if ( !empty( $this->AliasNbPages ) ) {
+ $this->pages[ $n ] = str_replace( $this->AliasNbPages, $this->page, $this->pages[ $n ] );
+ }
+ $this->_putstreamobject( $this->pages[ $n ] );
+ }
+
+ protected function _newobj( $n = null ) {
+ // Begin a new object
+ if ( $n === null ) {
+ $n = ++$this->n;
+ }
+ $this->offsets[ $n ] = $this->_getoffset();
+ $this->_put( $n . ' 0 obj' );
+ }
+
+ protected function _getoffset() {
+ return strlen( $this->buffer );
+ }
+
+ protected function _textstring( $s ) {
+ // Format a text string
+ if ( !$this->_isascii( $s ) ) {
+ $s = $this->_UTF8toUTF16( $s );
+ }
+
+ return '(' . $this->_escape( $s ) . ')';
+ }
+
+ protected function _isascii( $s ) {
+ // Test if string is ASCII
+ $nb = strlen( $s );
+ for ( $i = 0; $i < $nb; $i++ ) {
+ if ( ord( $s[ $i ] ) > 127 ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected function _UTF8toUTF16( $s ) {
+ // Convert UTF-8 to UTF-16BE with BOM
+ $res = "\xFE\xFF";
+ $nb = strlen( $s );
+ $i = 0;
+ while ( $i < $nb ) {
+ $c1 = ord( $s[ $i++ ] );
+ if ( $c1 >= 224 ) {
+ // 3-byte character
+ $c2 = ord( $s[ $i++ ] );
+ $c3 = ord( $s[ $i++ ] );
+ $res .= chr( ( ( $c1 & 0x0F ) << 4 ) + ( ( $c2 & 0x3C ) >> 2 ) );
+ $res .= chr( ( ( $c2 & 0x03 ) << 6 ) + ( $c3 & 0x3F ) );
+ } elseif ( $c1 >= 192 ) {
+ // 2-byte character
+ $c2 = ord( $s[ $i++ ] );
+ $res .= chr( ( $c1 & 0x1C ) >> 2 );
+ $res .= chr( ( ( $c1 & 0x03 ) << 6 ) + ( $c2 & 0x3F ) );
+ } else {
+ // Single-byte character
+ $res .= "\0" . chr( $c1 );
+ }
+ }
+
+ return $res;
+ }
+
+ protected function _putstreamobject( $data ) {
+ if ( $this->compress ) {
+ $entries = '/Filter /FlateDecode ';
+ $data = gzcompress( $data );
+ } else {
+ $entries = '';
+ }
+ $entries .= '/Length ' . strlen( $data );
+ $this->_newobj();
+ $this->_put( '<<' . $entries . '>>' );
+ $this->_putstream( $data );
+ $this->_put( 'endobj' );
+ }
+
+ protected function _putstream( $data ) {
+ $this->_put( 'stream' );
+ $this->_put( $data );
+ $this->_put( 'endstream' );
+ }
+
+ protected function _putresources() {
+ $this->_putfonts();
+ $this->_putimages();
+ // Resource dictionary
+ $this->_newobj( 2 );
+ $this->_put( '<<' );
+ $this->_putresourcedict();
+ $this->_put( '>>' );
+ $this->_put( 'endobj' );
+ }
+
+ protected function _putfonts() {
+ foreach ( $this->FontFiles as $file => $info ) {
+ // Font file embedding
+ $this->_newobj();
+ $this->FontFiles[ $file ][ 'n' ] = $this->n;
+ $font = file_get_contents( $this->fontpath . $file, true );
+ if ( !$font ) {
+ $this->Error( 'Font file not found: ' . $file );
+ }
+ $compressed = ( substr( $file, -2 ) == '.z' );
+ if ( !$compressed && isset( $info[ 'length2' ] ) ) {
+ $font = substr( $font, 6, $info[ 'length1' ] ) . substr( $font, 6 + $info[ 'length1' ] + 6, $info[ 'length2' ] );
+ }
+ $this->_put( '<_put( '/Filter /FlateDecode' );
+ }
+ $this->_put( '/Length1 ' . $info[ 'length1' ] );
+ if ( isset( $info[ 'length2' ] ) ) {
+ $this->_put( '/Length2 ' . $info[ 'length2' ] . ' /Length3 0' );
+ }
+ $this->_put( '>>' );
+ $this->_putstream( $font );
+ $this->_put( 'endobj' );
+ }
+ foreach ( $this->fonts as $k => $font ) {
+ // Encoding
+ if ( isset( $font[ 'diff' ] ) ) {
+ if ( !isset( $this->encodings[ $font[ 'enc' ] ] ) ) {
+ $this->_newobj();
+ $this->_put( '<>' );
+ $this->_put( 'endobj' );
+ $this->encodings[ $font[ 'enc' ] ] = $this->n;
+ }
+ }
+ // ToUnicode CMap
+ if ( isset( $font[ 'uv' ] ) ) {
+ if ( isset( $font[ 'enc' ] ) ) {
+ $cmapkey = $font[ 'enc' ];
+ } else {
+ $cmapkey = $font[ 'name' ];
+ }
+ if ( !isset( $this->cmaps[ $cmapkey ] ) ) {
+ $cmap = $this->_tounicodecmap( $font[ 'uv' ] );
+ $this->_putstreamobject( $cmap );
+ $this->cmaps[ $cmapkey ] = $this->n;
+ }
+ }
+ // Font object
+ $this->fonts[ $k ][ 'n' ] = $this->n + 1;
+ $type = $font[ 'type' ];
+ $name = $font[ 'name' ];
+ if ( $font[ 'subsetted' ] ) {
+ $name = 'AAAAAA+' . $name;
+ }
+ if ( $type == 'Core' ) {
+ // Core font
+ $this->_newobj();
+ $this->_put( '<_put( '/BaseFont /' . $name );
+ $this->_put( '/Subtype /Type1' );
+ if ( $name != 'Symbol' && $name != 'ZapfDingbats' ) {
+ $this->_put( '/Encoding /WinAnsiEncoding' );
+ }
+ if ( isset( $font[ 'uv' ] ) ) {
+ $this->_put( '/ToUnicode ' . $this->cmaps[ $cmapkey ] . ' 0 R' );
+ }
+ $this->_put( '>>' );
+ $this->_put( 'endobj' );
+ } elseif ( $type == 'Type1' || $type == 'TrueType' ) {
+ // Additional Type1 or TrueType/OpenType font
+ $this->_newobj();
+ $this->_put( '<_put( '/BaseFont /' . $name );
+ $this->_put( '/Subtype /' . $type );
+ $this->_put( '/FirstChar 32 /LastChar 255' );
+ $this->_put( '/Widths ' . ( $this->n + 1 ) . ' 0 R' );
+ $this->_put( '/FontDescriptor ' . ( $this->n + 2 ) . ' 0 R' );
+ if ( isset( $font[ 'diff' ] ) ) {
+ $this->_put( '/Encoding ' . $this->encodings[ $font[ 'enc' ] ] . ' 0 R' );
+ } else {
+ $this->_put( '/Encoding /WinAnsiEncoding' );
+ }
+ if ( isset( $font[ 'uv' ] ) ) {
+ $this->_put( '/ToUnicode ' . $this->cmaps[ $cmapkey ] . ' 0 R' );
+ }
+ $this->_put( '>>' );
+ $this->_put( 'endobj' );
+ // Widths
+ $this->_newobj();
+ $cw = &$font[ 'cw' ];
+ $s = '[';
+ for ( $i = 32; $i <= 255; $i++ ) {
+ $s .= $cw[ chr( $i ) ] . ' ';
+ }
+ $this->_put( $s . ']' );
+ $this->_put( 'endobj' );
+ // Descriptor
+ $this->_newobj();
+ $s = '< $v ) {
+ $s .= ' /' . $k . ' ' . $v;
+ }
+ if ( !empty( $font[ 'file' ] ) ) {
+ $s .= ' /FontFile' . ( $type == 'Type1' ? '' : '2' ) . ' ' . $this->FontFiles[ $font[ 'file' ] ][ 'n' ] . ' 0 R';
+ }
+ $this->_put( $s . '>>' );
+ $this->_put( 'endobj' );
+ } else {
+ // Allow for additional types
+ $mtd = '_put' . strtolower( $type );
+ if ( !method_exists( $this, $mtd ) ) {
+ $this->Error( 'Unsupported font type: ' . $type );
+ }
+ $this->$mtd( $font );
+ }
+ }
+ }
+
+ protected function _tounicodecmap( $uv ) {
+ $ranges = '';
+ $nbr = 0;
+ $chars = '';
+ $nbc = 0;
+ foreach ( $uv as $c => $v ) {
+ if ( is_array( $v ) ) {
+ $ranges .= sprintf( "<%02X> <%02X> <%04X>\n", $c, $c + $v[ 1 ] - 1, $v[ 0 ] );
+ $nbr++;
+ } else {
+ $chars .= sprintf( "<%02X> <%04X>\n", $c, $v );
+ $nbc++;
+ }
+ }
+ $s = "/CIDInit /ProcSet findresource begin\n";
+ $s .= "12 dict begin\n";
+ $s .= "begincmap\n";
+ $s .= "/CIDSystemInfo\n";
+ $s .= "<> def\n";
+ $s .= "/CMapName /Adobe-Identity-UCS def\n";
+ $s .= "/CMapType 2 def\n";
+ $s .= "1 begincodespacerange\n";
+ $s .= "<00> \n";
+ $s .= "endcodespacerange\n";
+ if ( $nbr > 0 ) {
+ $s .= "$nbr beginbfrange\n";
+ $s .= $ranges;
+ $s .= "endbfrange\n";
+ }
+ if ( $nbc > 0 ) {
+ $s .= "$nbc beginbfchar\n";
+ $s .= $chars;
+ $s .= "endbfchar\n";
+ }
+ $s .= "endcmap\n";
+ $s .= "CMapName currentdict /CMap defineresource pop\n";
+ $s .= "end\n";
+ $s .= "end";
+
+ return $s;
+ }
+
+ protected function _putimages() {
+ foreach ( array_keys( $this->images ) as $file ) {
+ $this->_putimage( $this->images[ $file ] );
+ unset( $this->images[ $file ][ 'data' ] );
+ unset( $this->images[ $file ][ 'smask' ] );
+ }
+ }
+
+ protected function _putimage( &$info ) {
+ $this->_newobj();
+ $info[ 'n' ] = $this->n;
+ $this->_put( '<_put( '/Subtype /Image' );
+ $this->_put( '/Width ' . $info[ 'w' ] );
+ $this->_put( '/Height ' . $info[ 'h' ] );
+ if ( $info[ 'cs' ] == 'Indexed' ) {
+ $this->_put( '/ColorSpace [/Indexed /DeviceRGB ' . ( strlen( $info[ 'pal' ] ) / 3 - 1 ) . ' ' . ( $this->n + 1 ) . ' 0 R]' );
+ } else {
+ $this->_put( '/ColorSpace /' . $info[ 'cs' ] );
+ if ( $info[ 'cs' ] == 'DeviceCMYK' ) {
+ $this->_put( '/Decode [1 0 1 0 1 0 1 0]' );
+ }
+ }
+ $this->_put( '/BitsPerComponent ' . $info[ 'bpc' ] );
+ if ( isset( $info[ 'f' ] ) ) {
+ $this->_put( '/Filter /' . $info[ 'f' ] );
+ }
+ if ( isset( $info[ 'dp' ] ) ) {
+ $this->_put( '/DecodeParms <<' . $info[ 'dp' ] . '>>' );
+ }
+ if ( isset( $info[ 'trns' ] ) && is_array( $info[ 'trns' ] ) ) {
+ $trns = '';
+ for ( $i = 0; $i < count( $info[ 'trns' ] ); $i++ ) {
+ $trns .= $info[ 'trns' ][ $i ] . ' ' . $info[ 'trns' ][ $i ] . ' ';
+ }
+ $this->_put( '/Mask [' . $trns . ']' );
+ }
+ if ( isset( $info[ 'smask' ] ) ) {
+ $this->_put( '/SMask ' . ( $this->n + 1 ) . ' 0 R' );
+ }
+ $this->_put( '/Length ' . strlen( $info[ 'data' ] ) . '>>' );
+ $this->_putstream( $info[ 'data' ] );
+ $this->_put( 'endobj' );
+ // Soft mask
+ if ( isset( $info[ 'smask' ] ) ) {
+ $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns ' . $info[ 'w' ];
+ $smask = array(
+ 'w' => $info[ 'w' ],
+ 'h' => $info[ 'h' ],
+ 'cs' => 'DeviceGray',
+ 'bpc' => 8,
+ 'f' => $info[ 'f' ],
+ 'dp' => $dp,
+ 'data' => $info[ 'smask' ]
+ );
+ $this->_putimage( $smask );
+ }
+ // Palette
+ if ( $info[ 'cs' ] == 'Indexed' ) {
+ $this->_putstreamobject( $info[ 'pal' ] );
+ }
+ }
+
+ protected function _putresourcedict() {
+ $this->_put( '/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]' );
+ $this->_put( '/Font <<' );
+ foreach ( $this->fonts as $font ) {
+ $this->_put( '/F' . $font[ 'i' ] . ' ' . $font[ 'n' ] . ' 0 R' );
+ }
+ $this->_put( '>>' );
+ $this->_put( '/XObject <<' );
+ $this->_putxobjectdict();
+ $this->_put( '>>' );
+ }
+
+ protected function _putxobjectdict() {
+ foreach ( $this->images as $image ) {
+ $this->_put( '/I' . $image[ 'i' ] . ' ' . $image[ 'n' ] . ' 0 R' );
+ }
+ }
+
+ protected function _putinfo() {
+ $this->metadata[ 'Producer' ] = 'FPDF ' . FPDF_VERSION;
+ $this->metadata[ 'CreationDate' ] = 'D:' . @date( 'YmdHis' );
+ foreach ( $this->metadata as $key => $value ) {
+ $this->_put( '/' . $key . ' ' . $this->_textstring( $value ) );
+ }
+ }
+
+ protected function _putcatalog() {
+ $n = $this->PageInfo[ 1 ][ 'n' ];
+ $this->_put( '/Type /Catalog' );
+ $this->_put( '/Pages 1 0 R' );
+ if ( $this->ZoomMode == 'fullpage' ) {
+ $this->_put( '/OpenAction [' . $n . ' 0 R /Fit]' );
+ } elseif ( $this->ZoomMode == 'fullwidth' ) {
+ $this->_put( '/OpenAction [' . $n . ' 0 R /FitH null]' );
+ } elseif ( $this->ZoomMode == 'real' ) {
+ $this->_put( '/OpenAction [' . $n . ' 0 R /XYZ null null 1]' );
+ } elseif ( !is_string( $this->ZoomMode ) ) {
+ $this->_put( '/OpenAction [' . $n . ' 0 R /XYZ null null ' . sprintf( '%.2F', $this->ZoomMode / 100 ) . ']' );
+ }
+ if ( $this->LayoutMode == 'single' ) {
+ $this->_put( '/PageLayout /SinglePage' );
+ } elseif ( $this->LayoutMode == 'continuous' ) {
+ $this->_put( '/PageLayout /OneColumn' );
+ } elseif ( $this->LayoutMode == 'two' ) {
+ $this->_put( '/PageLayout /TwoColumnLeft' );
+ }
+ }
+
+ protected function _puttrailer() {
+ $this->_put( '/Size ' . ( $this->n + 1 ) );
+ $this->_put( '/Root ' . $this->n . ' 0 R' );
+ $this->_put( '/Info ' . ( $this->n - 1 ) . ' 0 R' );
+ }
+
+ protected function _checkoutput() {
+ if ( PHP_SAPI != 'cli' ) {
+ if ( headers_sent( $file, $line ) ) {
+ $this->Error( "Some data has already been output, can't send PDF file (output started at $file:$line)" );
+ }
+ }
+ if ( ob_get_length() ) {
+ // The output buffer is not empty
+ if ( preg_match( '/^(\xEF\xBB\xBF)?\s*$/', ob_get_contents() ) ) {
+ // It contains only a UTF-8 BOM and/or whitespace, let's clean it
+ ob_clean();
+ }
// else
// $this->Error("Some data has already been output, can't send PDF file");
- }
- }
+ }
+ }
- protected function _httpencode( $param, $value, $isUTF8 ) {
- // Encode HTTP header field parameter
- if ( $this->_isascii( $value ) ) {
- return $param . '="' . $value . '"';
- }
- if ( ! $isUTF8 ) {
- $value = utf8_encode( $value );
- }
- if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) !== false ) {
- return $param . '="' . rawurlencode( $value ) . '"';
- } else {
- return $param . "*=UTF-8''" . rawurlencode( $value );
- }
- }
+ protected function _httpencode( $param, $value, $isUTF8 ) {
+ // Encode HTTP header field parameter
+ if ( $this->_isascii( $value ) ) {
+ return $param . '="' . $value . '"';
+ }
+ if ( !$isUTF8 ) {
+ $value = utf8_encode( $value );
+ }
+ if ( strpos( $_SERVER[ 'HTTP_USER_AGENT' ], 'MSIE' ) !== false ) {
+ return $param . '="' . rawurlencode( $value ) . '"';
+ } else {
+ return $param . "*=UTF-8''" . rawurlencode( $value );
+ }
+ }
- protected function _parsejpg( $file ) {
- // Extract info from a JPEG file
- $a = getimagesize( $file );
- if ( ! $a ) {
- $this->Error( 'Missing or incorrect image file: ' . $file );
- }
- if ( $a[2] != 2 ) {
- $this->Error( 'Not a JPEG file: ' . $file );
- }
- if ( ! isset( $a['channels'] ) || $a['channels'] == 3 ) {
- $colspace = 'DeviceRGB';
- } elseif ( $a['channels'] == 4 ) {
- $colspace = 'DeviceCMYK';
- } else {
- $colspace = 'DeviceGray';
- }
- $bpc = isset( $a['bits'] ) ? $a['bits'] : 8;
- $data = file_get_contents( $file );
+ protected function _parsejpg( $file ) {
+ // Extract info from a JPEG file
+ $a = getimagesize( $file );
+ if ( !$a ) {
+ $this->Error( 'Missing or incorrect image file: ' . $file );
+ }
+ if ( $a[ 2 ] != 2 ) {
+ $this->Error( 'Not a JPEG file: ' . $file );
+ }
+ if ( !isset( $a[ 'channels' ] ) || $a[ 'channels' ] == 3 ) {
+ $colspace = 'DeviceRGB';
+ } elseif ( $a[ 'channels' ] == 4 ) {
+ $colspace = 'DeviceCMYK';
+ } else {
+ $colspace = 'DeviceGray';
+ }
+ $bpc = isset( $a[ 'bits' ] ) ? $a[ 'bits' ] : 8;
+ $data = file_get_contents( $file );
- return array(
- 'w' => $a[0],
- 'h' => $a[1],
- 'cs' => $colspace,
- 'bpc' => $bpc,
- 'f' => 'DCTDecode',
- 'data' => $data
- );
- }
+ return array(
+ 'w' => $a[ 0 ],
+ 'h' => $a[ 1 ],
+ 'cs' => $colspace,
+ 'bpc' => $bpc,
+ 'f' => 'DCTDecode',
+ 'data' => $data
+ );
+ }
- protected function _parsepng( $file ) {
- // Extract info from a PNG file
- $f = fopen( $file, 'rb' );
- if ( ! $f ) {
- $this->Error( 'Can\'t open image file: ' . $file );
- }
- $info = $this->_parsepngstream( $f, $file );
- fclose( $f );
+ protected function _parsepng( $file ) {
+ // Extract info from a PNG file
+ $f = fopen( $file, 'rb' );
+ if ( !$f ) {
+ $this->Error( 'Can\'t open image file: ' . $file );
+ }
+ $info = $this->_parsepngstream( $f, $file );
+ fclose( $f );
- return $info;
- }
+ return $info;
+ }
- protected function _parsepngstream( $f, $file ) {
- // Check signature
- if ( $this->_readstream( $f, 8 ) != chr( 137 ) . 'PNG' . chr( 13 ) . chr( 10 ) . chr( 26 ) . chr( 10 ) ) {
- $this->Error( 'Not a PNG file: ' . $file );
- }
+ protected function _parsepngstream( $f, $file ) {
+ // Check signature
+ if ( $this->_readstream( $f, 8 ) != chr( 137 ) . 'PNG' . chr( 13 ) . chr( 10 ) . chr( 26 ) . chr( 10 ) ) {
+ $this->Error( 'Not a PNG file: ' . $file );
+ }
- // Read header chunk
- $this->_readstream( $f, 4 );
- if ( $this->_readstream( $f, 4 ) != 'IHDR' ) {
- $this->Error( 'Incorrect PNG file: ' . $file );
- }
- $w = $this->_readint( $f );
- $h = $this->_readint( $f );
- $bpc = ord( $this->_readstream( $f, 1 ) );
- if ( $bpc > 8 ) {
- $this->Error( '16-bit depth not supported: ' . $file );
- }
- $ct = ord( $this->_readstream( $f, 1 ) );
- if ( $ct == 0 || $ct == 4 ) {
- $colspace = 'DeviceGray';
- } elseif ( $ct == 2 || $ct == 6 ) {
- $colspace = 'DeviceRGB';
- } elseif ( $ct == 3 ) {
- $colspace = 'Indexed';
- } else {
- $this->Error( 'Unknown color type: ' . $file );
- }
- if ( ord( $this->_readstream( $f, 1 ) ) != 0 ) {
- $this->Error( 'Unknown compression method: ' . $file );
- }
- if ( ord( $this->_readstream( $f, 1 ) ) != 0 ) {
- $this->Error( 'Unknown filter method: ' . $file );
- }
- if ( ord( $this->_readstream( $f, 1 ) ) != 0 ) {
- $this->Error( 'Interlacing not supported: ' . $file );
- }
- $this->_readstream( $f, 4 );
- $dp = '/Predictor 15 /Colors ' . ( $colspace == 'DeviceRGB' ? 3 : 1 ) . ' /BitsPerComponent ' . $bpc . ' /Columns ' . $w;
+ // Read header chunk
+ $this->_readstream( $f, 4 );
+ if ( $this->_readstream( $f, 4 ) != 'IHDR' ) {
+ $this->Error( 'Incorrect PNG file: ' . $file );
+ }
+ $w = $this->_readint( $f );
+ $h = $this->_readint( $f );
+ $bpc = ord( $this->_readstream( $f, 1 ) );
+ if ( $bpc > 8 ) {
+ $this->Error( '16-bit depth not supported: ' . $file );
+ }
+ $ct = ord( $this->_readstream( $f, 1 ) );
+ if ( $ct == 0 || $ct == 4 ) {
+ $colspace = 'DeviceGray';
+ } elseif ( $ct == 2 || $ct == 6 ) {
+ $colspace = 'DeviceRGB';
+ } elseif ( $ct == 3 ) {
+ $colspace = 'Indexed';
+ } else {
+ $this->Error( 'Unknown color type: ' . $file );
+ }
+ if ( ord( $this->_readstream( $f, 1 ) ) != 0 ) {
+ $this->Error( 'Unknown compression method: ' . $file );
+ }
+ if ( ord( $this->_readstream( $f, 1 ) ) != 0 ) {
+ $this->Error( 'Unknown filter method: ' . $file );
+ }
+ if ( ord( $this->_readstream( $f, 1 ) ) != 0 ) {
+ $this->Error( 'Interlacing not supported: ' . $file );
+ }
+ $this->_readstream( $f, 4 );
+ $dp = '/Predictor 15 /Colors ' . ( $colspace == 'DeviceRGB' ? 3 : 1 ) . ' /BitsPerComponent ' . $bpc . ' /Columns ' . $w;
- // Scan chunks looking for palette, transparency and image data
- $pal = '';
- $trns = '';
- $data = '';
- do {
- $n = $this->_readint( $f );
- $type = $this->_readstream( $f, 4 );
- if ( $type == 'PLTE' ) {
- // Read palette
- $pal = $this->_readstream( $f, $n );
- $this->_readstream( $f, 4 );
- } elseif ( $type == 'tRNS' ) {
- // Read transparency info
- $t = $this->_readstream( $f, $n );
- if ( $ct == 0 ) {
- $trns = array( ord( substr( $t, 1, 1 ) ) );
- } elseif ( $ct == 2 ) {
- $trns = array( ord( substr( $t, 1, 1 ) ), ord( substr( $t, 3, 1 ) ), ord( substr( $t, 5, 1 ) ) );
- } else {
- $pos = strpos( $t, chr( 0 ) );
- if ( $pos !== false ) {
- $trns = array( $pos );
- }
- }
- $this->_readstream( $f, 4 );
- } elseif ( $type == 'IDAT' ) {
- // Read image data block
- $data .= $this->_readstream( $f, $n );
- $this->_readstream( $f, 4 );
- } elseif ( $type == 'IEND' ) {
- break;
- } else {
- $this->_readstream( $f, $n + 4 );
- }
- } while ( $n );
+ // Scan chunks looking for palette, transparency and image data
+ $pal = '';
+ $trns = '';
+ $data = '';
+ do {
+ $n = $this->_readint( $f );
+ $type = $this->_readstream( $f, 4 );
+ if ( $type == 'PLTE' ) {
+ // Read palette
+ $pal = $this->_readstream( $f, $n );
+ $this->_readstream( $f, 4 );
+ } elseif ( $type == 'tRNS' ) {
+ // Read transparency info
+ $t = $this->_readstream( $f, $n );
+ if ( $ct == 0 ) {
+ $trns = array( ord( substr( $t, 1, 1 ) ) );
+ } elseif ( $ct == 2 ) {
+ $trns = array( ord( substr( $t, 1, 1 ) ), ord( substr( $t, 3, 1 ) ), ord( substr( $t, 5, 1 ) ) );
+ } else {
+ $pos = strpos( $t, chr( 0 ) );
+ if ( $pos !== false ) {
+ $trns = array( $pos );
+ }
+ }
+ $this->_readstream( $f, 4 );
+ } elseif ( $type == 'IDAT' ) {
+ // Read image data block
+ $data .= $this->_readstream( $f, $n );
+ $this->_readstream( $f, 4 );
+ } elseif ( $type == 'IEND' ) {
+ break;
+ } else {
+ $this->_readstream( $f, $n + 4 );
+ }
+ } while ( $n );
- if ( $colspace == 'Indexed' && empty( $pal ) ) {
- $this->Error( 'Missing palette in ' . $file );
- }
- $info = array(
- 'w' => $w,
- 'h' => $h,
- 'cs' => $colspace,
- 'bpc' => $bpc,
- 'f' => 'FlateDecode',
- 'dp' => $dp,
- 'pal' => $pal,
- 'trns' => $trns
- );
- if ( $ct >= 4 ) {
- // Extract alpha channel
- if ( ! function_exists( 'gzuncompress' ) ) {
- $this->Error( 'Zlib not available, can\'t handle alpha channel: ' . $file );
- }
- $data = gzuncompress( $data );
- $color = '';
- $alpha = '';
- if ( $ct == 4 ) {
- // Gray image
- $len = 2 * $w;
- for ( $i = 0; $i < $h; $i ++ ) {
- $pos = ( 1 + $len ) * $i;
- $color .= $data[ $pos ];
- $alpha .= $data[ $pos ];
- $line = substr( $data, $pos + 1, $len );
- $color .= preg_replace( '/(.)./s', '$1', $line );
- $alpha .= preg_replace( '/.(.)/s', '$1', $line );
- }
- } else {
- // RGB image
- $len = 4 * $w;
- for ( $i = 0; $i < $h; $i ++ ) {
- $pos = ( 1 + $len ) * $i;
- $color .= $data[ $pos ];
- $alpha .= $data[ $pos ];
- $line = substr( $data, $pos + 1, $len );
- $color .= preg_replace( '/(.{3})./s', '$1', $line );
- $alpha .= preg_replace( '/.{3}(.)/s', '$1', $line );
- }
- }
- unset( $data );
- $data = gzcompress( $color );
- $info['smask'] = gzcompress( $alpha );
- $this->WithAlpha = true;
- if ( $this->PDFVersion < '1.4' ) {
- $this->PDFVersion = '1.4';
- }
- }
- $info['data'] = $data;
+ if ( $colspace == 'Indexed' && empty( $pal ) ) {
+ $this->Error( 'Missing palette in ' . $file );
+ }
+ $info = array(
+ 'w' => $w,
+ 'h' => $h,
+ 'cs' => $colspace,
+ 'bpc' => $bpc,
+ 'f' => 'FlateDecode',
+ 'dp' => $dp,
+ 'pal' => $pal,
+ 'trns' => $trns
+ );
+ if ( $ct >= 4 ) {
+ // Extract alpha channel
+ if ( !function_exists( 'gzuncompress' ) ) {
+ $this->Error( 'Zlib not available, can\'t handle alpha channel: ' . $file );
+ }
+ $data = gzuncompress( $data );
+ $color = '';
+ $alpha = '';
+ if ( $ct == 4 ) {
+ // Gray image
+ $len = 2 * $w;
+ for ( $i = 0; $i < $h; $i++ ) {
+ $pos = ( 1 + $len ) * $i;
+ $color .= $data[ $pos ];
+ $alpha .= $data[ $pos ];
+ $line = substr( $data, $pos + 1, $len );
+ $color .= preg_replace( '/(.)./s', '$1', $line );
+ $alpha .= preg_replace( '/.(.)/s', '$1', $line );
+ }
+ } else {
+ // RGB image
+ $len = 4 * $w;
+ for ( $i = 0; $i < $h; $i++ ) {
+ $pos = ( 1 + $len ) * $i;
+ $color .= $data[ $pos ];
+ $alpha .= $data[ $pos ];
+ $line = substr( $data, $pos + 1, $len );
+ $color .= preg_replace( '/(.{3})./s', '$1', $line );
+ $alpha .= preg_replace( '/.{3}(.)/s', '$1', $line );
+ }
+ }
+ unset( $data );
+ $data = gzcompress( $color );
+ $info[ 'smask' ] = gzcompress( $alpha );
+ $this->WithAlpha = true;
+ if ( $this->PDFVersion < '1.4' ) {
+ $this->PDFVersion = '1.4';
+ }
+ }
+ $info[ 'data' ] = $data;
- return $info;
- }
+ return $info;
+ }
- protected function _readstream( $f, $n ) {
- // Read n bytes from stream
- $res = '';
- while ( $n > 0 && ! feof( $f ) ) {
- $s = fread( $f, $n );
- if ( $s === false ) {
- $this->Error( 'Error while reading stream' );
- }
- $n -= strlen( $s );
- $res .= $s;
- }
- if ( $n > 0 ) {
- $this->Error( 'Unexpected end of stream' );
- }
+ protected function _readstream( $f, $n ) {
+ // Read n bytes from stream
+ $res = '';
+ while ( $n > 0 && !feof( $f ) ) {
+ $s = fread( $f, $n );
+ if ( $s === false ) {
+ $this->Error( 'Error while reading stream' );
+ }
+ $n -= strlen( $s );
+ $res .= $s;
+ }
+ if ( $n > 0 ) {
+ $this->Error( 'Unexpected end of stream' );
+ }
- return $res;
- }
+ return $res;
+ }
- protected function _readint( $f ) {
- // Read a 4-byte integer from stream
- $a = unpack( 'Ni', $this->_readstream( $f, 4 ) );
+ protected function _readint( $f ) {
+ // Read a 4-byte integer from stream
+ $a = unpack( 'Ni', $this->_readstream( $f, 4 ) );
- return $a['i'];
- }
+ return $a[ 'i' ];
+ }
- protected function _parsegif( $file ) {
- // Extract info from a GIF file (via PNG conversion)
- if ( ! function_exists( 'imagepng' ) ) {
- $this->Error( 'GD extension is required for GIF support' );
- }
- if ( ! function_exists( 'imagecreatefromgif' ) ) {
- $this->Error( 'GD has no GIF read support' );
- }
- $im = imagecreatefromgif( $file );
- if ( ! $im ) {
- $this->Error( 'Missing or incorrect image file: ' . $file );
- }
- imageinterlace( $im, 0 );
- ob_start();
- imagepng( $im );
- $data = ob_get_clean();
- imagedestroy( $im );
- $f = fopen( 'php://temp', 'rb+' );
- if ( ! $f ) {
- $this->Error( 'Unable to create memory stream' );
- }
- fwrite( $f, $data );
- rewind( $f );
- $info = $this->_parsepngstream( $f, $file );
- fclose( $f );
+ protected function _parsegif( $file ) {
+ // Extract info from a GIF file (via PNG conversion)
+ if ( !function_exists( 'imagepng' ) ) {
+ $this->Error( 'GD extension is required for GIF support' );
+ }
+ if ( !function_exists( 'imagecreatefromgif' ) ) {
+ $this->Error( 'GD has no GIF read support' );
+ }
+ $im = imagecreatefromgif( $file );
+ if ( !$im ) {
+ $this->Error( 'Missing or incorrect image file: ' . $file );
+ }
+ imageinterlace( $im, 0 );
+ ob_start();
+ imagepng( $im );
+ $data = ob_get_clean();
+ imagedestroy( $im );
+ $f = fopen( 'php://temp', 'rb+' );
+ if ( !$f ) {
+ $this->Error( 'Unable to create memory stream' );
+ }
+ fwrite( $f, $data );
+ rewind( $f );
+ $info = $this->_parsepngstream( $f, $file );
+ fclose( $f );
- return $info;
- }
+ return $info;
+ }
}
?>
diff --git a/src/Model/Logs.php b/src/Model/Logs.php
index 731ee89..5dc1495 100644
--- a/src/Model/Logs.php
+++ b/src/Model/Logs.php
@@ -6,179 +6,180 @@ use PDO;
class Logs {
- const ERROR_LEVEL = [
- 1 => 'E_ERROR',
- 2 => 'E_WARNING',
- 4 => 'E_PARSE',
- 8 => 'E_NOTICE',
- 16 => 'E_CORE_ERROR',
- 32 => 'E_CORE_WARNING',
- 64 => 'E_COMPILE_ERROR',
- 128 => 'E_COMPILE_WARNING',
- 256 => 'E_USER_ERROR',
- 512 => 'E_USER_WARNING',
- 1024 => 'E_USER_NOTICE',
- 2048 => 'E_STRICT',
- 4096 => 'E_RECOVERABLE_ERROR',
- 8192 => 'E_DEPRECATED',
- 16384 => 'E_USER_DEPRECATED',
- 32767 => 'E_ALL'
- ];
- public $id;
- public $level;
- public $message;
- public $file;
- public $line;
- public $date;
+ const ERROR_LEVEL = [
+ 1 => 'E_ERROR',
+ 2 => 'E_WARNING',
+ 4 => 'E_PARSE',
+ 8 => 'E_NOTICE',
+ 16 => 'E_CORE_ERROR',
+ 32 => 'E_CORE_WARNING',
+ 64 => 'E_COMPILE_ERROR',
+ 128 => 'E_COMPILE_WARNING',
+ 256 => 'E_USER_ERROR',
+ 512 => 'E_USER_WARNING',
+ 1024 => 'E_USER_NOTICE',
+ 2048 => 'E_STRICT',
+ 4096 => 'E_RECOVERABLE_ERROR',
+ 8192 => 'E_DEPRECATED',
+ 16384 => 'E_USER_DEPRECATED',
+ 32767 => 'E_ALL'
+ ];
+ public $id;
+ public $level;
+ public $message;
+ public $file;
+ public $line;
+ public $date;
- /**
- * Logs constructor.
- *
- * @param int|null $id
- * @param string|null $level
- * @param string|null $message
- * @param string|null $file
- * @param string|null $line
- * @param string|null $date
- */
- public function __construct( int $id = null, string $level = null, string $message = null, string $file = null, string $line = null, string $date = null ) {
- if ( $id === null ) {
- return;
- }
- $this->id = $id;
- $this->level = $level;
- $this->message = $message;
- $this->file = $file;
- $this->line = $line;
- $this->date = $date;
- }
+ /**
+ * Logs constructor.
+ *
+ * @param int|null $id
+ * @param string|null $level
+ * @param string|null $message
+ * @param string|null $file
+ * @param string|null $line
+ * @param string|null $date
+ */
+ public function __construct( int $id = null, string $level = null, string $message = null, string $file = null,
+ string $line = null, string $date = null ) {
+ if ( $id === null ) {
+ return;
+ }
+ $this->id = $id;
+ $this->level = $level;
+ $this->message = $message;
+ $this->file = $file;
+ $this->line = $line;
+ $this->date = $date;
+ }
- public static function insert( $level, $message, $file, $line, $date ) {
- Model::insert( BDTables::LOGS, [
- 'level' => $level,
- 'message' => $message,
- 'file' => $file,
- 'line' => $line,
- 'date' => date( "Y-m-d H:i:s", strtotime( $date ) )
- ] );
- }
+ public static function insert( $level, $message, $file, $line, $date ) {
+ Model::insert( BDTables::LOGS, [
+ 'level' => $level,
+ 'message' => $message,
+ 'file' => $file,
+ 'line' => $line,
+ 'date' => date( "Y-m-d H:i:s", strtotime( $date ) )
+ ] );
+ }
- /**
- * Retourne un tableau des derniers logs (limite en param)
- *
- * @param int $limit
- *
- * @return array
- */
- public static function getLastLogs( int $limit ) {
- $req = BDD::instance()->prepare( 'SELECT *
+ /**
+ * Retourne un tableau des derniers logs (limite en param)
+ *
+ * @param int $limit
+ *
+ * @return array
+ */
+ public static function getLastLogs( int $limit ) {
+ $req = BDD::instance()->prepare( 'SELECT *
FROM ' . BDTables::LOGS . '
ORDER BY date DESC
LIMIT :limit' );
- $req->bindValue( 'limit', $limit, PDO::PARAM_INT );
- $req->execute();
- $return = [];
+ $req->bindValue( 'limit', $limit, PDO::PARAM_INT );
+ $req->execute();
+ $return = [];
- foreach ( $req->fetchAll() as $l ) {
- $log = new Logs( $l['id'], $l['level'], $l['message'], $l['file'], $l['line'], $l['date'] );
- $return[] = $log;
- }
+ foreach ( $req->fetchAll() as $l ) {
+ $log = new Logs( $l[ 'id' ], $l[ 'level' ], $l[ 'message' ], $l[ 'file' ], $l[ 'line' ], $l[ 'date' ] );
+ $return[] = $log;
+ }
- return $return;
- }
+ return $return;
+ }
- /**
- * @return int
- */
- public function getId() {
- return $this->id;
- }
+ /**
+ * @return int
+ */
+ public function getId() {
+ return $this->id;
+ }
- /**
- * @param int $id
- */
- public function setId( $id ) {
- $this->id = $id;
- }
+ /**
+ * @param int $id
+ */
+ public function setId( $id ) {
+ $this->id = $id;
+ }
- /**
- * @return string
- */
- public function getLevel() {
- return $this->level;
- }
+ /**
+ * @return string
+ */
+ public function getLevel() {
+ return $this->level;
+ }
- /**
- * @param string $level
- */
- public function setLevel( $level ) {
- $this->level = $level;
- }
+ /**
+ * @param string $level
+ */
+ public function setLevel( $level ) {
+ $this->level = $level;
+ }
- /**
- * @return string
- */
- public function getMessage() {
- return htmlspecialchars( $this->message );
- }
+ /**
+ * @return string
+ */
+ public function getMessage() {
+ return htmlspecialchars( $this->message );
+ }
- /**
- * @param string $message
- */
- public function setMessage( $message ) {
- $this->message = $message;
- }
+ /**
+ * @param string $message
+ */
+ public function setMessage( $message ) {
+ $this->message = $message;
+ }
- /**
- * @return string
- */
- public function getFile() {
- return htmlspecialchars( $this->file );
- }
+ /**
+ * @return string
+ */
+ public function getFile() {
+ return htmlspecialchars( $this->file );
+ }
- /**
- * @param string $file
- */
- public function setFile( $file ) {
- $this->file = $file;
- }
+ /**
+ * @param string $file
+ */
+ public function setFile( $file ) {
+ $this->file = $file;
+ }
- /**
- * @return string
- */
- public function getLine() {
- return htmlspecialchars( $this->line );
- }
+ /**
+ * @return string
+ */
+ public function getLine() {
+ return htmlspecialchars( $this->line );
+ }
- /**
- * @param string $line
- */
- public function setLine( $line ) {
- $this->line = $line;
- }
+ /**
+ * @param string $line
+ */
+ public function setLine( $line ) {
+ $this->line = $line;
+ }
- /**
- * @return string
- */
- public function getDate() {
- return $this->date;
- }
+ /**
+ * @return string
+ */
+ public function getDate() {
+ return $this->date;
+ }
- /**
- * @param string $date
- */
- public function setDate( $date ) {
- $this->date = $date;
- }
+ /**
+ * @param string $date
+ */
+ public function setDate( $date ) {
+ $this->date = $date;
+ }
- /**
- * Retourne le type d'erreur en string (label)
- * @return string
- */
- public function getErrorLabel() {
- return htmlspecialchars( self::ERROR_LEVEL[ $this->level ] );
- }
+ /**
+ * Retourne le type d'erreur en string (label)
+ * @return string
+ */
+ public function getErrorLabel() {
+ return htmlspecialchars( self::ERROR_LEVEL[ $this->level ] );
+ }
}
?>
\ No newline at end of file
diff --git a/src/Model/Model.php b/src/Model/Model.php
index ad4937d..c7d10d7 100644
--- a/src/Model/Model.php
+++ b/src/Model/Model.php
@@ -4,47 +4,63 @@ namespace CAUProject3Contact\Model;
class Model {
- /**
- * Crée une reqette d'insertion en base àpartir du nom de la table et d'un tableau associatif et l'exécute
- *
- * @param string $tableName
- * @param array $data
- *
- * @return int lastInsertId
- */
- public static function insert( string $tableName, array $data ) {
- $req = BDD::instance()->prepare( 'INSERT INTO ' . $tableName . ' (' . implode( ', ', array_keys( $data ) ) . ')
+ /**
+ * Crée une reqette d'insertion en base àpartir du nom de la table et d'un tableau associatif et l'exécute
+ *
+ * @param string $tableName
+ * @param array $data
+ *
+ * @return int lastInsertId
+ */
+ public static function insert( string $tableName, array $data ) {
+ $req = BDD::instance()->prepare( 'INSERT INTO ' . $tableName . ' (' . implode( ', ', array_keys( $data ) ) . ')
VALUES (' . ':' . implode( ', :', array_keys( $data ) ) . ')' );
- $req->execute( $data );
+ $req->execute( $data );
- return BDD::lastInsertId();
- }
+ return BDD::lastInsertId();
+ }
- /**
- * Met à jour les données d'une ligne d'un table données
- *
- * @param string $tableName
- * @param array $data
- * @param string $idColumn
- * @param int $idValue
- */
- public static function update( string $tableName, array $data, string $idColumn, int $idValue ) {
- $reqStr = 'UPDATE ' . $tableName . ' SET ';
- $lastKey = endKey( $data );
- foreach ( $data as $key => $value ) {
- $reqStr .= $key . ' = :' . $key;
- if ( $key != $lastKey ) {
- $reqStr .= ', ';
- }
- }
- $reqStr .= ' WHERE ' . $idColumn . ' = :' . $idColumn;
- $data[ $idColumn ] = $idValue;
+ /**
+ * Met à jour les données d'une ligne d'un table données
+ *
+ * @param string $tableName
+ * @param array $data
+ * @param string $idColumn
+ * @param int $idValue
+ */
+ public static function update( string $tableName, array $data, string $idColumn, int $idValue ) {
+ $reqStr = 'UPDATE ' . $tableName . ' SET ';
+ $lastKey = endKey( $data );
+ foreach ( $data as $key => $value ) {
+ $reqStr .= $key . ' = :' . $key;
+ if ( $key != $lastKey ) {
+ $reqStr .= ', ';
+ }
+ }
+ $reqStr .= ' WHERE ' . $idColumn . ' = :' . $idColumn;
+ $data[ $idColumn ] = $idValue;
- //echo $reqStr; exit();
+ //echo $reqStr; exit();
+
+ $req = BDD::instance()->prepare( $reqStr );
+ $req->execute( $data );
+ }
+
+ public static function delete( string $tableName, array $conditions ) {
+ $reqStr = 'DELETE FROM ' . $tableName . ' WHERE ';
+ $lastKey = endKey( $conditions );
+
+ foreach ( $conditions as $key => $value ) {
+ $reqStr .= $key . ' = :' . $key;
+ if ( $key != $lastKey ) {
+ $reqStr .= ' AND ';
+ }
+ }
+
+ $req = BDD::instance()->prepare( $reqStr );
+ $req->execute( $conditions );
+ }
- $req = BDD::instance()->prepare( $reqStr );
- $req->execute( $data );
- }
}
?>
\ No newline at end of file
diff --git a/src/View/Site/Index.php b/src/View/Site/Index.php
index 0ccb255..891b5a4 100644
--- a/src/View/Site/Index.php
+++ b/src/View/Site/Index.php
@@ -1,3 +1,39 @@
-
- Bonjour 2
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/src/View/Site/tpl/footer.php b/src/View/Site/tpl/footer.php
index e9063ac..0fbdf1b 100644
--- a/src/View/Site/tpl/footer.php
+++ b/src/View/Site/tpl/footer.php
@@ -1,5 +1,32 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib/functions.php b/src/lib/functions.php
index 8b4414e..fd84d40 100644
--- a/src/lib/functions.php
+++ b/src/lib/functions.php
@@ -12,36 +12,36 @@ include( 'src/lib/mail/PHPMailerAutoload.php' );
*/
function formatURL( string $str, $encoding = 'utf-8' ) {
- $str = str_replace( "+", "_plus_", $str );
- $str = str_replace( "%", "_pourcent_", $str );
- $str = str_replace( "&", "_et_", $str );
+ $str = str_replace( "+", "_plus_", $str );
+ $str = str_replace( "%", "_pourcent_", $str );
+ $str = str_replace( "&", "_et_", $str );
- //on remplace les apotrophes et espaces par des underscore
- $str = str_replace( array( "'", " ", "," ), "_", $str );
+ //on remplace les apotrophes et espaces par des underscore
+ $str = str_replace( array( "'", " ", "," ), "_", $str );
- $str = str_replace( "__", "_", $str );
+ $str = str_replace( "__", "_", $str );
- // transformer les caractères accentués en entités HTML
- $str = htmlentities( $str, ENT_NOQUOTES, $encoding );
- // remplacer les entités HTML pour avoir juste le premier caractères non accentués
- // Exemple : "&ecute;" => "e", "&Ecute;" => "E", "Ã " => "a" ...
- $str = preg_replace( '#&([A-za-z])(?:acute|grave|cedil|circ|orn|ring|slash|th|tilde|uml);#', '\1', $str );
+ // transformer les caractères accentués en entités HTML
+ $str = htmlentities( $str, ENT_NOQUOTES, $encoding );
+ // remplacer les entités HTML pour avoir juste le premier caractères non accentués
+ // Exemple : "&ecute;" => "e", "&Ecute;" => "E", "Ã " => "a" ...
+ $str = preg_replace( '#&([A-za-z])(?:acute|grave|cedil|circ|orn|ring|slash|th|tilde|uml);#', '\1', $str );
- // Remplacer les ligatures tel que : Œ, Æ ...
- // Exemple "Å“" => "oe"
- $str = preg_replace( '#&([A-za-z]{2})(?:lig);#', '\1', $str );
- // Supprimer tout le reste
- //$str = preg_replace('#&[^;]+;#', '', $str);
- $str = str_replace( array( "#", "&", "[", "^", ";", "]" ), '', $str );
+ // Remplacer les ligatures tel que : Œ, Æ ...
+ // Exemple "Å“" => "oe"
+ $str = preg_replace( '#&([A-za-z]{2})(?:lig);#', '\1', $str );
+ // Supprimer tout le reste
+ //$str = preg_replace('#&[^;]+;#', '', $str);
+ $str = str_replace( array( "#", "&", "[", "^", ";", "]" ), '', $str );
- //on passe tout en minuscule
- $str = strtolower( $str );
+ //on passe tout en minuscule
+ $str = strtolower( $str );
- if ( substr( $str, - 1 ) == '_' ) {
- $str = substr( $str, 0, - 1 );
- }
+ if ( substr( $str, -1 ) == '_' ) {
+ $str = substr( $str, 0, -1 );
+ }
- return $str;
+ return $str;
}
/**
@@ -57,55 +57,55 @@ function formatURL( string $str, $encoding = 'utf-8' ) {
*/
function darkroom( $img, $to, $width = 0, $height = 0, $quality = 100, $useGD = true ) {
- $dimensions = getimagesize( $img );
- $ratio = $dimensions[0] / $dimensions[1];
+ $dimensions = getimagesize( $img );
+ $ratio = $dimensions[ 0 ] / $dimensions[ 1 ];
- // Calcul des dimensions si 0 passé en paramètre
- if ( $width == 0 && $height == 0 ) {
- $width = $dimensions[0];
- $height = $dimensions[1];
- } else if ( $height == 0 ) {
- $height = round( $width / $ratio );
- } else if ( $width == 0 ) {
- $width = round( $height * $ratio );
- }
+ // Calcul des dimensions si 0 passé en paramètre
+ if ( $width == 0 && $height == 0 ) {
+ $width = $dimensions[ 0 ];
+ $height = $dimensions[ 1 ];
+ } else if ( $height == 0 ) {
+ $height = round( $width / $ratio );
+ } else if ( $width == 0 ) {
+ $width = round( $height * $ratio );
+ }
- if ( $dimensions[0] > ( $width / $height ) * $dimensions[1] ) {
- $dimY = $height;
- $dimX = round( $height * $dimensions[0] / $dimensions[1] );
- }
- if ( $dimensions[0] < ( $width / $height ) * $dimensions[1] ) {
- $dimX = $width;
- $dimY = round( $width * $dimensions[1] / $dimensions[0] );
- }
- if ( $dimensions[0] == ( $width / $height ) * $dimensions[1] ) {
- $dimX = $width;
- $dimY = $height;
- }
+ if ( $dimensions[ 0 ] > ( $width / $height ) * $dimensions[ 1 ] ) {
+ $dimY = $height;
+ $dimX = round( $height * $dimensions[ 0 ] / $dimensions[ 1 ] );
+ }
+ if ( $dimensions[ 0 ] < ( $width / $height ) * $dimensions[ 1 ] ) {
+ $dimX = $width;
+ $dimY = round( $width * $dimensions[ 1 ] / $dimensions[ 0 ] );
+ }
+ if ( $dimensions[ 0 ] == ( $width / $height ) * $dimensions[ 1 ] ) {
+ $dimX = $width;
+ $dimY = $height;
+ }
- // Création de l'image avec la librairie GD
- if ( $useGD ) {
- $pattern = imagecreatetruecolor( $width, $height );
- $type = mime_content_type( $img );
- switch ( substr( $type, 6 ) ) {
- case 'jpeg':
- $image = imagecreatefromjpeg( $img );
- break;
- case 'gif':
- $image = imagecreatefromgif( $img );
- break;
- case 'png':
- $image = imagecreatefrompng( $img );
- break;
- }
- imagecopyresampled( $pattern, $image, 0, 0, 0, 0, $dimX, $dimY, $dimensions[0], $dimensions[1] );
- imagedestroy( $image );
- imagejpeg( $pattern, $to, $quality );
+ // Création de l'image avec la librairie GD
+ if ( $useGD ) {
+ $pattern = imagecreatetruecolor( $width, $height );
+ $type = mime_content_type( $img );
+ switch ( substr( $type, 6 ) ) {
+ case 'jpeg':
+ $image = imagecreatefromjpeg( $img );
+ break;
+ case 'gif':
+ $image = imagecreatefromgif( $img );
+ break;
+ case 'png':
+ $image = imagecreatefrompng( $img );
+ break;
+ }
+ imagecopyresampled( $pattern, $image, 0, 0, 0, 0, $dimX, $dimY, $dimensions[ 0 ], $dimensions[ 1 ] );
+ imagedestroy( $image );
+ imagejpeg( $pattern, $to, $quality );
- return true;
- }
+ return true;
+ }
- return true;
+ return true;
}
/**
@@ -119,20 +119,20 @@ function darkroom( $img, $to, $width = 0, $height = 0, $quality = 100, $useGD =
* @return bool|void
*/
function errorHandler( $errno, $errstr, $errfile, $errline ) {
- if ( ! ( error_reporting() & $errno ) ) {
- // Ce code d'erreur n'est pas inclus dans error_reporting()
- return;
- }
+ if ( !( error_reporting() & $errno ) ) {
+ // Ce code d'erreur n'est pas inclus dans error_reporting()
+ return;
+ }
- // Insertion des logs
- \CAUProject3Contact\Model\Logs::insert( $errno, $errstr, $errfile, $errline, date( 'Y-m-d H:i:s' ) );
+ // Insertion des logs
+ \CAUProject3Contact\Model\Logs::insert( $errno, $errstr, $errfile, $errline, date( 'Y-m-d H:i:s' ) );
- ob_clean();
- new \CAUProject3Contact\Controller\Site\SiteError( 500 );
+ ob_clean();
+ new \CAUProject3Contact\Controller\Site\SiteError( 500 );
- /* Ne pas exécuter le gestionnaire interne de PHP */
+ /* Ne pas exécuter le gestionnaire interne de PHP */
- return;
+ return;
}
/**
@@ -141,87 +141,87 @@ function errorHandler( $errno, $errstr, $errfile, $errline ) {
*/
function getBrowser() {
- $u_agent = $_SERVER['HTTP_USER_AGENT'];
- $bname = 'Unknown';
- $platform = 'Unknown';
- $ub = "";
+ $u_agent = $_SERVER[ 'HTTP_USER_AGENT' ];
+ $bname = 'Unknown';
+ $platform = 'Unknown';
+ $ub = "";
- //First get the platform?
- if ( preg_match( '/android/i', $u_agent ) || preg_match( '/Android/i', $u_agent ) ) {
- $platform = 'android';
- } else if ( preg_match( '/linux/i', $u_agent ) ) {
- $platform = 'linux';
- } else if ( preg_match( '/macintosh|mac os x/i', $u_agent ) ) {
- $platform = 'mac';
- } else if ( preg_match( '/windows|win32/i', $u_agent ) ) {
- $platform = 'windows';
- }
+ //First get the platform?
+ if ( preg_match( '/android/i', $u_agent ) || preg_match( '/Android/i', $u_agent ) ) {
+ $platform = 'android';
+ } else if ( preg_match( '/linux/i', $u_agent ) ) {
+ $platform = 'linux';
+ } else if ( preg_match( '/macintosh|mac os x/i', $u_agent ) ) {
+ $platform = 'mac';
+ } else if ( preg_match( '/windows|win32/i', $u_agent ) ) {
+ $platform = 'windows';
+ }
- if ( strstr( $u_agent, 'mobile' ) || strstr( $u_agent, 'Mobile' ) ) {
- $platform .= ' mobile';
- }
+ if ( strstr( $u_agent, 'mobile' ) || strstr( $u_agent, 'Mobile' ) ) {
+ $platform .= ' mobile';
+ }
- // Next get the name of the useragent yes seperately and for good reason
- if ( preg_match( '/MSIE/i', $u_agent ) && ! preg_match( '/Opera/i', $u_agent ) ) {
- $bname = 'Internet Explorer';
- $ub = "MSIE";
- } else if ( preg_match( '/Edge/i', $u_agent ) ) {
- $bname = 'Microsoft Edge';
- $ub = "Edge";
- } else if ( preg_match( '/Trident/i', $u_agent ) ) {
- $bname = 'Internet Explorer';
- $ub = "rv";
- } else if ( preg_match( '/Firefox/i', $u_agent ) ) {
- $bname = 'Mozilla Firefox';
- $ub = "Firefox";
- } else if ( preg_match( '/Chrome/i', $u_agent ) ) {
- $bname = 'Google Chrome';
- $ub = "Chrome";
- } else if ( preg_match( '/Safari/i', $u_agent ) ) {
- $bname = 'Apple Safari';
- $ub = "Safari";
- } else if ( preg_match( '/Opera/i', $u_agent ) ) {
- $bname = 'Opera';
- $ub = "Opera";
- } else if ( preg_match( '/Netscape/i', $u_agent ) ) {
- $bname = 'Netscape';
- $ub = "Netscape";
- }
+ // Next get the name of the useragent yes seperately and for good reason
+ if ( preg_match( '/MSIE/i', $u_agent ) && !preg_match( '/Opera/i', $u_agent ) ) {
+ $bname = 'Internet Explorer';
+ $ub = "MSIE";
+ } else if ( preg_match( '/Edge/i', $u_agent ) ) {
+ $bname = 'Microsoft Edge';
+ $ub = "Edge";
+ } else if ( preg_match( '/Trident/i', $u_agent ) ) {
+ $bname = 'Internet Explorer';
+ $ub = "rv";
+ } else if ( preg_match( '/Firefox/i', $u_agent ) ) {
+ $bname = 'Mozilla Firefox';
+ $ub = "Firefox";
+ } else if ( preg_match( '/Chrome/i', $u_agent ) ) {
+ $bname = 'Google Chrome';
+ $ub = "Chrome";
+ } else if ( preg_match( '/Safari/i', $u_agent ) ) {
+ $bname = 'Apple Safari';
+ $ub = "Safari";
+ } else if ( preg_match( '/Opera/i', $u_agent ) ) {
+ $bname = 'Opera';
+ $ub = "Opera";
+ } else if ( preg_match( '/Netscape/i', $u_agent ) ) {
+ $bname = 'Netscape';
+ $ub = "Netscape";
+ }
- // finally get the correct version number
- // Added "|:"
- $known = array( 'Version', $ub, 'other' );
- $pattern = '#(?
' . join( '|', $known ) . ')[/|: ]+(?[0-9.|a-zA-Z.]*)#';
- if ( ! preg_match_all( $pattern, $u_agent, $matches ) ) {
- // we have no matching number just continue
- }
+ // finally get the correct version number
+ // Added "|:"
+ $known = array( 'Version', $ub, 'other' );
+ $pattern = '#(?' . join( '|', $known ) . ')[/|: ]+(?[0-9.|a-zA-Z.]*)#';
+ if ( !preg_match_all( $pattern, $u_agent, $matches ) ) {
+ // we have no matching number just continue
+ }
- // see how many we have
- $i = count( $matches['browser'] );
- if ( $i != 1 ) {
- //we will have two since we are not using 'other' argument yet
- //see if version is before or after the name
- if ( strripos( $u_agent, "Version" ) < strripos( $u_agent, $ub ) ) {
- $version = $matches['version'][0];
- } else {
- $version = $matches['version'][1];
- }
- } else {
- $version = $matches['version'][0];
- }
+ // see how many we have
+ $i = count( $matches[ 'browser' ] );
+ if ( $i != 1 ) {
+ //we will have two since we are not using 'other' argument yet
+ //see if version is before or after the name
+ if ( strripos( $u_agent, "Version" ) < strripos( $u_agent, $ub ) ) {
+ $version = $matches[ 'version' ][ 0 ];
+ } else {
+ $version = $matches[ 'version' ][ 1 ];
+ }
+ } else {
+ $version = $matches[ 'version' ][ 0 ];
+ }
- // check if we have a number
- if ( $version == null || $version == "" ) {
- $version = "?";
- }
+ // check if we have a number
+ if ( $version == null || $version == "" ) {
+ $version = "?";
+ }
- return array(
- 'userAgent' => $u_agent,
- 'platform' => $platform,
- 'version' => $version,
- 'pattern' => $pattern,
- 'name' => $bname
- );
+ return array(
+ 'userAgent' => $u_agent,
+ 'platform' => $platform,
+ 'version' => $version,
+ 'pattern' => $pattern,
+ 'name' => $bname
+ );
}
/**
@@ -231,15 +231,15 @@ function getBrowser() {
* @return int
*/
function getLimitWord( $string, $limit ) {
- $i = $limit;
- if ( ! isset( $string ) || empty( $string ) ) {
- return 0;
- }
- while ( $i > 0 && $string[ $i ] != ' ' ) {
- $i --;
- }
+ $i = $limit;
+ if ( !isset( $string ) || empty( $string ) ) {
+ return 0;
+ }
+ while ( $i > 0 && $string[ $i ] != ' ' ) {
+ $i--;
+ }
- return $i;
+ return $i;
}
/**
@@ -329,38 +329,38 @@ function email(array $destinataires, string $subject, string $body, string $aute
* @return bool
*/
function rotateImage( string $file, int $angle, string $newName ) {
- // Initialisation variable pou test futur
- $image = null;
- $type = mime_content_type( $file );
- // Création ressources en fonction de l'image
- switch ( substr( $type, 6 ) ) {
- case 'jpeg':
- $image = imagecreatefromjpeg( $file );
- break;
- case 'png':
- $image = imagecreatefrompng( $file );
- break;
- }
- // Si format image non prit en charge
- if ( $image == null ) {
- return false;
- }
- // Rotation de l'image
- $rotate = imagerotate( $image, $angle, 0 );
- // On recrée l'image au format de base
- switch ( substr( $type, 6 ) ) {
- case 'jpeg':
- imagejpeg( $rotate, $file );
- break;
- case 'png':
- imagepng( $rotate, $file );
- break;
- }
- imagedestroy( $image );
- imagedestroy( $rotate );
- rename( $file, $newName );
+ // Initialisation variable pou test futur
+ $image = null;
+ $type = mime_content_type( $file );
+ // Création ressources en fonction de l'image
+ switch ( substr( $type, 6 ) ) {
+ case 'jpeg':
+ $image = imagecreatefromjpeg( $file );
+ break;
+ case 'png':
+ $image = imagecreatefrompng( $file );
+ break;
+ }
+ // Si format image non prit en charge
+ if ( $image == null ) {
+ return false;
+ }
+ // Rotation de l'image
+ $rotate = imagerotate( $image, $angle, 0 );
+ // On recrée l'image au format de base
+ switch ( substr( $type, 6 ) ) {
+ case 'jpeg':
+ imagejpeg( $rotate, $file );
+ break;
+ case 'png':
+ imagepng( $rotate, $file );
+ break;
+ }
+ imagedestroy( $image );
+ imagedestroy( $rotate );
+ rename( $file, $newName );
- return true;
+ return true;
}
/**
@@ -369,34 +369,38 @@ function rotateImage( string $file, int $angle, string $newName ) {
* @return array
* Clean toutes les strings dans array en récursif, et filtre pour n'avoir qu'un espaces entre chaque mot
*/
-function cleanArray( array $data ) {
- if ( ! empty( $data ) ) {
- foreach ( $data as $key => $donnée ) {
- switch ( gettype( $donnée ) ) {
- case 'string':
- if ( ! empty( $donnée ) ) {
- $new_string = '';
- foreach ( explode( ' ', trim( $donnée ) ) as $str ) {
- if ( ! empty( $str ) ) {
- if ( $new_string != '' ) {
- $new_string .= ' ';
- }
- $new_string .= $str;
- }
- }
- $data[ $key ] = $new_string;
- }
- break;
- case 'array':
- if ( ! empty( $donnée ) ) {
- $data[ $key ] = cleanArray( $donnée );
- }
- break;
- }
- }
- }
+function cleanArray( array $data ): array {
+ if ( !empty( $data ) ) {
+ foreach ( $data as $key => $value ) {
+ switch ( gettype( $value ) ) {
+ case 'string':
+ if ( !empty( $str ) ) {
+ $data[ $key ] = cleanString( $value );
+ }
+ break;
+ case 'array':
+ if ( !empty( $value ) ) {
+ $data[ $key ] = cleanArray( $value );
+ }
+ break;
+ }
+ }
+ }
- return $data;
+ return $data;
+}
+
+function cleanString( string $str ): string {
+ $newStr = '';
+ foreach ( explode( ' ', trim( $str ) ) as $word ) {
+ if ( !empty( $word ) && $word != '' ) {
+ if ( $newStr != '' ) {
+ $newStr .= ' ';
+ }
+ $newStr .= $word;
+ }
+ }
+ return $newStr;
}
/**
@@ -405,9 +409,27 @@ function cleanArray( array $data ) {
* @return mixed
*/
function endKey( $array ) {
- end( $array );
+ end( $array );
- return key( $array );
+ return key( $array );
+}
+
+function filterArrays( $array1, $array2 ): array {
+ $newArray = [];
+
+ foreach ( $array2 as $item2 ) {
+ $add = true;
+ foreach ( $array1 as $item1 ) {
+ if ( $item2[ "id" ] == $item1[ "id" ] ) {
+ $add = false;
+ }
+ }
+ if ( $add ) {
+ $newArray[] = $item2;
+ }
+ }
+
+ return $newArray;
}
?>
\ No newline at end of file
diff --git a/src/lib/mail/PHPMailerAutoload.php b/src/lib/mail/PHPMailerAutoload.php
index 2a82750..d2bdae6 100644
--- a/src/lib/mail/PHPMailerAutoload.php
+++ b/src/lib/mail/PHPMailerAutoload.php
@@ -23,27 +23,27 @@
* @param string $classname The name of the class to load
*/
function PHPMailerAutoload( $classname ) {
- //Can't use __DIR__ as it's only in PHP 5.3+
- $filename = dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'class.' . strtolower( $classname ) . '.php';
- if ( is_readable( $filename ) ) {
- require $filename;
- }
+ //Can't use __DIR__ as it's only in PHP 5.3+
+ $filename = dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'class.' . strtolower( $classname ) . '.php';
+ if ( is_readable( $filename ) ) {
+ require $filename;
+ }
}
if ( version_compare( PHP_VERSION, '5.1.2', '>=' ) ) {
- //SPL autoloading was introduced in PHP 5.1.2
- if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) {
- spl_autoload_register( 'PHPMailerAutoload', true, true );
- } else {
- spl_autoload_register( 'PHPMailerAutoload' );
- }
+ //SPL autoloading was introduced in PHP 5.1.2
+ if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) {
+ spl_autoload_register( 'PHPMailerAutoload', true, true );
+ } else {
+ spl_autoload_register( 'PHPMailerAutoload' );
+ }
} else {
- /**
- * Fall back to traditional autoload for old PHP versions
- *
- * @param string $classname The name of the class to load
- */
- function __autoload( $classname ) {
- PHPMailerAutoload( $classname );
- }
+ /**
+ * Fall back to traditional autoload for old PHP versions
+ *
+ * @param string $classname The name of the class to load
+ */
+ function __autoload( $classname ) {
+ PHPMailerAutoload( $classname );
+ }
}
diff --git a/src/lib/mail/class.phpmailer.php b/src/lib/mail/class.phpmailer.php
index 4278591..603b45d 100644
--- a/src/lib/mail/class.phpmailer.php
+++ b/src/lib/mail/class.phpmailer.php
@@ -26,3857 +26,3858 @@
* @author Brent R. Matzelle (original founder)
*/
class PHPMailer {
- /**
- * Error severity: message only, continue processing.
- */
- const STOP_MESSAGE = 0;
- /**
- * Error severity: message, likely ok to continue processing.
- */
- const STOP_CONTINUE = 1;
- /**
- * Error severity: message, plus full stop, critical error reached.
- */
- const STOP_CRITICAL = 2;
- /**
- * SMTP RFC standard line ending.
- */
- const CRLF = "\r\n";
- /**
- * The maximum line length allowed by RFC 2822 section 2.1.1
- * @var integer
- */
- const MAX_LINE_LENGTH = 998;
- /**
- * The PHPMailer Version number.
- * @var string
- */
- public $Version = '5.2.14';
- /**
- * Email priority.
- * Options: null (default), 1 = High, 3 = Normal, 5 = low.
- * When null, the header is not set at all.
- * @var integer
- */
- public $Priority = null;
- /**
- * The character set of the message.
- * @var string
- */
- public $CharSet = 'utf-8';
- /**
- * The MIME Content-type of the message.
- * @var string
- */
- public $ContentType = 'text/plain';
- /**
- * The message encoding.
- * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
- * @var string
- */
- public $Encoding = '8bit';
- /**
- * Holds the most recent mailer error message.
- * @var string
- */
- public $ErrorInfo = '';
- /**
- * The From email address for the message.
- * @var string
- */
- public $From = 'root@localhost';
- /**
- * The From name of the message.
- * @var string
- */
- public $FromName = 'Root User';
- /**
- * The Sender email (Return-Path) of the message.
- * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
- * @var string
- */
- public $Sender = '';
- /**
- * The Return-Path of the message.
- * If empty, it will be set to either From or Sender.
- * @var string
- * @deprecated Email senders should never set a return-path header;
- * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
- * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
- */
- public $ReturnPath = '';
- /**
- * The Subject of the message.
- * @var string
- */
- public $Subject = '';
- /**
- * An HTML or plain text message body.
- * If HTML then call isHTML(true).
- * @var string
- */
- public $Body = '';
- /**
- * The plain-text message body.
- * This body can be read by mail clients that do not have HTML email
- * capability such as mutt & Eudora.
- * Clients that can read HTML will view the normal Body.
- * @var string
- */
- public $AltBody = '';
- /**
- * An iCal message part body.
- * Only supported in simple alt or alt_inline message types
- * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
- * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
- * @link http://kigkonsult.se/iCalcreator/
- * @var string
- */
- public $Ical = '';
- /**
- * Word-wrap the message body to this number of chars.
- * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
- * @var integer
- */
- public $WordWrap = 0;
- /**
- * Which method to use to send mail.
- * Options: "mail", "sendmail", or "smtp".
- * @var string
- */
- public $Mailer = 'mail';
- /**
- * The path to the sendmail program.
- * @var string
- */
- public $Sendmail = '/usr/sbin/sendmail';
- /**
- * Whether mail() uses a fully sendmail-compatible MTA.
- * One which supports sendmail's "-oi -f" options.
- * @var boolean
- */
- public $UseSendmailOptions = true;
- /**
- * Path to PHPMailer plugins.
- * Useful if the SMTP class is not in the PHP include path.
- * @var string
- * @deprecated Should not be needed now there is an autoloader.
- */
- public $PluginDir = '';
- /**
- * The email address that a reading confirmation should be sent to, also known as read receipt.
- * @var string
- */
- public $ConfirmReadingTo = '';
- /**
- * The hostname to use in the Message-ID header and as default HELO string.
- * If empty, PHPMailer attempts to find one with, in order,
- * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
- * 'localhost.localdomain'.
- * @var string
- */
- public $Hostname = '';
- /**
- * An ID to be used in the Message-ID header.
- * If empty, a unique id will be generated.
- * @var string
- */
- public $MessageID = '';
- /**
- * The message Date to be used in the Date header.
- * If empty, the current date will be added.
- * @var string
- */
- public $MessageDate = '';
- /**
- * SMTP hosts.
- * Either a single hostname or multiple semicolon-delimited hostnames.
- * You can also specify a different port
- * for each host by using this format: [hostname:port]
- * (e.g. "smtp1.example.com:25;smtp2.example.com").
- * You can also specify encryption type, for example:
- * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
- * Hosts will be tried in order.
- * @var string
- */
- public $Host = 'localhost';
- /**
- * The default SMTP server port.
- * @var integer
- */
- public $Port = 25;
- /**
- * The SMTP HELO of the message.
- * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
- * one with the same method described above for $Hostname.
- * @var string
- * @see PHPMailer::$Hostname
- */
- public $Helo = '';
- /**
- * What kind of encryption to use on the SMTP connection.
- * Options: '', 'ssl' or 'tls'
- * @var string
- */
- public $SMTPSecure = '';
- /**
- * Whether to enable TLS encryption automatically if a server supports it,
- * even if `SMTPSecure` is not set to 'tls'.
- * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
- * @var boolean
- */
- public $SMTPAutoTLS = true;
- /**
- * Whether to use SMTP authentication.
- * Uses the Username and Password properties.
- * @var boolean
- * @see PHPMailer::$Username
- * @see PHPMailer::$Password
- */
- public $SMTPAuth = false;
- /**
- * Options array passed to stream_context_create when connecting via SMTP.
- * @var array
- */
- public $SMTPOptions = array();
- /**
- * SMTP username.
- * @var string
- */
- public $Username = '';
- /**
- * SMTP password.
- * @var string
- */
- public $Password = '';
- /**
- * SMTP auth type.
- * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
- * @var string
- */
- public $AuthType = '';
- /**
- * SMTP realm.
- * Used for NTLM auth
- * @var string
- */
- public $Realm = '';
- /**
- * SMTP workstation.
- * Used for NTLM auth
- * @var string
- */
- public $Workstation = '';
- /**
- * The SMTP server timeout in seconds.
- * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
- * @var integer
- */
- public $Timeout = 300;
- /**
- * SMTP class debug output mode.
- * Debug output level.
- * Options:
- * * `0` No output
- * * `1` Commands
- * * `2` Data and commands
- * * `3` As 2 plus connection status
- * * `4` Low-level data output
- * @var integer
- * @see SMTP::$do_debug
- */
- public $SMTPDebug = 0;
- /**
- * How to handle debug output.
- * Options:
- * * `echo` Output plain-text as-is, appropriate for CLI
- * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output
- * * `error_log` Output to error log as configured in php.ini
- *
- * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
- *
- * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
- *
- * @var string|callable
- * @see SMTP::$Debugoutput
- */
- public $Debugoutput = 'echo';
- /**
- * Whether to keep SMTP connection open after each message.
- * If this is set to true then to close the connection
- * requires an explicit call to smtpClose().
- * @var boolean
- */
- public $SMTPKeepAlive = false;
- /**
- * Whether to split multiple to addresses into multiple messages
- * or send them all in one message.
- * Only supported in `mail` and `sendmail` transports, not in SMTP.
- * @var boolean
- */
- public $SingleTo = false;
- /**
- * Storage for addresses when SingleTo is enabled.
- * @var array
- */
- public $SingleToArray = array();
- /**
- * Whether to generate VERP addresses on send.
- * Only applicable when sending via SMTP.
- * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
- * @link http://www.postfix.org/VERP_README.html Postfix VERP info
- * @var boolean
- */
- public $do_verp = false;
- /**
- * Whether to allow sending messages with an empty body.
- * @var boolean
- */
- public $AllowEmpty = false;
- /**
- * The default line ending.
- * @note The default remains "\n". We force CRLF where we know
- * it must be used via self::CRLF.
- * @var string
- */
- public $LE = "\n";
- /**
- * DKIM selector.
- * @var string
- */
- public $DKIM_selector = '';
- /**
- * DKIM Identity.
- * Usually the email address used as the source of the email
- * @var string
- */
- public $DKIM_identity = '';
- /**
- * DKIM passphrase.
- * Used if your key is encrypted.
- * @var string
- */
- public $DKIM_passphrase = '';
- /**
- * DKIM signing domain name.
- * @example 'example.com'
- * @var string
- */
- public $DKIM_domain = '';
- /**
- * DKIM private key file path.
- * @var string
- */
- public $DKIM_private = '';
- /**
- * Callback Action function name.
- *
- * The function that handles the result of the send email action.
- * It is called out by send() for each email sent.
- *
- * Value can be any php callable: http://www.php.net/is_callable
- *
- * Parameters:
- * boolean $result result of the send action
- * string $to email address of the recipient
- * string $cc cc email addresses
- * string $bcc bcc email addresses
- * string $subject the subject
- * string $body the email body
- * string $from email address of sender
- * @var string
- */
- public $action_function = '';
- /**
- * What to put in the X-Mailer header.
- * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
- * @var string
- */
- public $XMailer = '';
- /**
- * The complete compiled MIME message body.
- * @access protected
- * @var string
- */
- protected $MIMEBody = '';
- /**
- * The complete compiled MIME message headers.
- * @var string
- * @access protected
- */
- protected $MIMEHeader = '';
- /**
- * Extra headers that createHeader() doesn't fold in.
- * @var string
- * @access protected
- */
- protected $mailHeader = '';
- /**
- * An instance of the SMTP sender class.
- * @var SMTP
- * @access protected
- */
- protected $smtp = null;
- /**
- * The array of 'to' names and addresses.
- * @var array
- * @access protected
- */
- protected $to = array();
- /**
- * The array of 'cc' names and addresses.
- * @var array
- * @access protected
- */
- protected $cc = array();
- /**
- * The array of 'bcc' names and addresses.
- * @var array
- * @access protected
- */
- protected $bcc = array();
- /**
- * The array of reply-to names and addresses.
- * @var array
- * @access protected
- */
- protected $ReplyTo = array();
- /**
- * An array of all kinds of addresses.
- * Includes all of $to, $cc, $bcc
- * @var array
- * @access protected
- * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
- */
- protected $all_recipients = array();
- /**
- * An array of names and addresses queued for validation.
- * In send(), valid and non duplicate entries are moved to $all_recipients
- * and one of $to, $cc, or $bcc.
- * This array is used only for addresses with IDN.
- * @var array
- * @access protected
- * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
- * @see PHPMailer::$all_recipients
- */
- protected $RecipientsQueue = array();
- /**
- * An array of reply-to names and addresses queued for validation.
- * In send(), valid and non duplicate entries are moved to $ReplyTo.
- * This array is used only for addresses with IDN.
- * @var array
- * @access protected
- * @see PHPMailer::$ReplyTo
- */
- protected $ReplyToQueue = array();
- /**
- * The array of attachments.
- * @var array
- * @access protected
- */
- protected $attachment = array();
- /**
- * The array of custom headers.
- * @var array
- * @access protected
- */
- protected $CustomHeader = array();
- /**
- * The most recent Message-ID (including angular brackets).
- * @var string
- * @access protected
- */
- protected $lastMessageID = '';
- /**
- * The message's MIME type.
- * @var string
- * @access protected
- */
- protected $message_type = '';
- /**
- * The array of MIME boundary strings.
- * @var array
- * @access protected
- */
- protected $boundary = array();
- /**
- * The array of available languages.
- * @var array
- * @access protected
- */
- protected $language = array();
- /**
- * The number of errors encountered.
- * @var integer
- * @access protected
- */
- protected $error_count = 0;
- /**
- * The S/MIME certificate file path.
- * @var string
- * @access protected
- */
- protected $sign_cert_file = '';
- /**
- * The S/MIME key file path.
- * @var string
- * @access protected
- */
- protected $sign_key_file = '';
- /**
- * The optional S/MIME extra certificates ("CA Chain") file path.
- * @var string
- * @access protected
- */
- protected $sign_extracerts_file = '';
- /**
- * The S/MIME password for the key.
- * Used only if the key is encrypted.
- * @var string
- * @access protected
- */
- protected $sign_key_pass = '';
- /**
- * Whether to throw exceptions for errors.
- * @var boolean
- * @access protected
- */
- protected $exceptions = false;
- /**
- * Unique ID used for message ID and boundaries.
- * @var string
- * @access protected
- */
- protected $uniqueid = '';
-
- /**
- * Constructor.
- *
- * @param boolean $exceptions Should we throw external exceptions?
- */
- public function __construct( $exceptions = false ) {
- $this->exceptions = (boolean) $exceptions;
- }
-
- /**
- * Destructor.
- */
- public function __destruct() {
- //Close any open SMTP connection nicely
- $this->smtpClose();
- }
-
- /**
- * Close the active SMTP session if one exists.
- * @return void
- */
- public function smtpClose() {
- if ( is_a( $this->smtp, 'SMTP' ) ) {
- if ( $this->smtp->connected() ) {
- $this->smtp->quit();
- $this->smtp->close();
- }
- }
- }
-
- /**
- * Send messages using SMTP.
- * @return void
- */
- public function isSMTP() {
- $this->Mailer = 'smtp';
- }
-
- /**
- * Send messages using PHP's mail() function.
- * @return void
- */
- public function isMail() {
- $this->Mailer = 'mail';
- }
-
- /**
- * Send messages using $Sendmail.
- * @return void
- */
- public function isSendmail() {
- $ini_sendmail_path = ini_get( 'sendmail_path' );
-
- if ( ! stristr( $ini_sendmail_path, 'sendmail' ) ) {
- $this->Sendmail = '/usr/sbin/sendmail';
- } else {
- $this->Sendmail = $ini_sendmail_path;
- }
- $this->Mailer = 'sendmail';
- }
-
- /**
- * Send messages using qmail.
- * @return void
- */
- public function isQmail() {
- $ini_sendmail_path = ini_get( 'sendmail_path' );
-
- if ( ! stristr( $ini_sendmail_path, 'qmail' ) ) {
- $this->Sendmail = '/var/qmail/bin/qmail-inject';
- } else {
- $this->Sendmail = $ini_sendmail_path;
- }
- $this->Mailer = 'qmail';
- }
-
- /**
- * Add a "To" address.
- *
- * @param string $address The email address to send to
- * @param string $name
- *
- * @return boolean true on success, false if address already used or invalid in some way
- */
- public function addAddress( $address, $name = '' ) {
- return $this->addOrEnqueueAnAddress( 'to', $address, $name );
- }
-
- /**
- * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
- * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
- * be modified after calling this function), addition of such addresses is delayed until send().
- * Addresses that have been added already return false, but do not throw exceptions.
- *
- * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
- * @param string $address The email address to send, resp. to reply to
- * @param string $name
- *
- * @throws phpmailerException
- * @return boolean true on success, false if address already used or invalid in some way
- * @access protected
- */
- protected function addOrEnqueueAnAddress( $kind, $address, $name ) {
- $address = trim( $address );
- $name = trim( preg_replace( '/[\r\n]+/', '', $name ) ); //Strip breaks and trim
- if ( ( $pos = strrpos( $address, '@' ) ) === false ) {
- // At-sign is misssing.
- $error_message = $this->lang( 'invalid_address' ) . " (addAnAddress $kind): $address";
- $this->setError( $error_message );
- $this->edebug( $error_message );
- if ( $this->exceptions ) {
- throw new phpmailerException( $error_message );
- }
-
- return false;
- }
- $params = array( $kind, $address, $name );
- // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
- if ( $this->has8bitChars( substr( $address, ++ $pos ) ) and $this->idnSupported() ) {
- if ( $kind != 'Reply-To' ) {
- if ( ! array_key_exists( $address, $this->RecipientsQueue ) ) {
- $this->RecipientsQueue[ $address ] = $params;
-
- return true;
- }
- } else {
- if ( ! array_key_exists( $address, $this->ReplyToQueue ) ) {
- $this->ReplyToQueue[ $address ] = $params;
-
- return true;
- }
- }
-
- return false;
- }
-
- // Immediately add standard addresses without IDN.
- return call_user_func_array( array( $this, 'addAnAddress' ), $params );
- }
-
- /**
- * Get an error message in the current language.
- * @access protected
- *
- * @param string $key
- *
- * @return string
- */
- protected function lang( $key ) {
- if ( count( $this->language ) < 1 ) {
- $this->setLanguage( 'en' ); // set the default language
- }
-
- if ( array_key_exists( $key, $this->language ) ) {
- if ( $key == 'smtp_connect_failed' ) {
- //Include a link to troubleshooting docs on SMTP connection failure
- //this is by far the biggest cause of support questions
- //but it's usually not PHPMailer's fault.
- return $this->language[ $key ] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
- }
-
- return $this->language[ $key ];
- } else {
- //Return the key as a fallback
- return $key;
- }
- }
-
- /**
- * Set the language for error messages.
- * Returns false if it cannot load the language file.
- * The default language is English.
- *
- * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
- * @param string $lang_path Path to the language file directory, with trailing separator (slash)
- *
- * @return boolean
- * @access public
- */
- public function setLanguage( $langcode = 'en', $lang_path = '' ) {
- // Define full set of translatable strings in English
- $PHPMAILER_LANG = array(
- 'authenticate' => 'SMTP Error: Could not authenticate.',
- 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
- 'data_not_accepted' => 'SMTP Error: data not accepted.',
- 'empty_message' => 'Message body empty',
- 'encoding' => 'Unknown encoding: ',
- 'execute' => 'Could not execute: ',
- 'file_access' => 'Could not access file: ',
- 'file_open' => 'File Error: Could not open file: ',
- 'from_failed' => 'The following From address failed: ',
- 'instantiate' => 'Could not instantiate mail function.',
- 'invalid_address' => 'Invalid address: ',
- 'mailer_not_supported' => ' mailer is not supported.',
- 'provide_address' => 'You must provide at least one recipient email address.',
- 'recipients_failed' => 'SMTP Error: The following recipients failed: ',
- 'signing' => 'Signing Error: ',
- 'smtp_connect_failed' => 'SMTP connect() failed.',
- 'smtp_error' => 'SMTP server error: ',
- 'variable_set' => 'Cannot set or reset variable: ',
- 'extension_missing' => 'Extension missing: '
- );
- if ( empty( $lang_path ) ) {
- // Calculate an absolute path so it can work if CWD is not here
- $lang_path = dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
- }
- $foundlang = true;
- $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
- // There is no English translation file
- if ( $langcode != 'en' ) {
- // Make sure language file path is readable
- if ( ! is_readable( $lang_file ) ) {
- $foundlang = false;
- } else {
- // Overwrite language-specific strings.
- // This way we'll never have missing translation keys.
- $foundlang = include $lang_file;
- }
- }
- $this->language = $PHPMAILER_LANG;
-
- return (boolean) $foundlang; // Returns false if language not found
- }
-
- /**
- * Add an error message to the error container.
- * @access protected
- *
- * @param string $msg
- *
- * @return void
- */
- protected function setError( $msg ) {
- $this->error_count ++;
- if ( $this->Mailer == 'smtp' and ! is_null( $this->smtp ) ) {
- $lasterror = $this->smtp->getError();
- if ( ! empty( $lasterror['error'] ) ) {
- $msg .= $this->lang( 'smtp_error' ) . $lasterror['error'];
- if ( ! empty( $lasterror['detail'] ) ) {
- $msg .= ' Detail: ' . $lasterror['detail'];
- }
- if ( ! empty( $lasterror['smtp_code'] ) ) {
- $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
- }
- if ( ! empty( $lasterror['smtp_code_ex'] ) ) {
- $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
- }
- }
- }
- $this->ErrorInfo = $msg;
- }
-
- /**
- * Output debugging info via user-defined method.
- * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
- * @see PHPMailer::$Debugoutput
- * @see PHPMailer::$SMTPDebug
- *
- * @param string $str
- */
- protected function edebug( $str ) {
- if ( $this->SMTPDebug <= 0 ) {
- return;
- }
- //Avoid clash with built-in function names
- if ( ! in_array( $this->Debugoutput, array(
- 'error_log',
- 'html',
- 'echo'
- ) ) and is_callable( $this->Debugoutput ) ) {
- call_user_func( $this->Debugoutput, $str, $this->SMTPDebug );
-
- return;
- }
- switch ( $this->Debugoutput ) {
- case 'error_log':
- //Don't output, just log
- error_log( $str );
- break;
- case 'html':
- //Cleans up output a bit for a better looking, HTML-safe output
- echo htmlentities(
- preg_replace( '/[\r\n]+/', '', $str ),
- ENT_QUOTES,
- 'UTF-8'
- )
- . "
\n";
- break;
- case 'echo':
- default:
- //Normalize line breaks
- $str = preg_replace( '/\r\n?/ms', "\n", $str );
- echo gmdate( 'Y-m-d H:i:s' ) . "\t" . str_replace(
- "\n",
- "\n \t ",
- trim( $str )
- ) . "\n";
- }
- }
-
- /**
- * Does a string contain any 8-bit chars (in any charset)?
- *
- * @param string $text
- *
- * @return boolean
- */
- public function has8bitChars( $text ) {
- return (boolean) preg_match( '/[\x80-\xFF]/', $text );
- }
-
- /**
- * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
- * "intl" and "mbstring" PHP extensions.
- * @return bool "true" if required functions for IDN support are present
- */
- public function idnSupported() {
- return function_exists( 'idn_to_ascii' ) and function_exists( 'mb_convert_encoding' );
- }
-
- /**
- * Add a "CC" address.
- * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
- *
- * @param string $address The email address to send to
- * @param string $name
- *
- * @return boolean true on success, false if address already used or invalid in some way
- */
- public function addCC( $address, $name = '' ) {
- return $this->addOrEnqueueAnAddress( 'cc', $address, $name );
- }
-
- /**
- * Add a "BCC" address.
- * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
- *
- * @param string $address The email address to send to
- * @param string $name
- *
- * @return boolean true on success, false if address already used or invalid in some way
- */
- public function addBCC( $address, $name = '' ) {
- return $this->addOrEnqueueAnAddress( 'bcc', $address, $name );
- }
-
- /**
- * Add a "Reply-To" address.
- *
- * @param string $address The email address to reply to
- * @param string $name
- *
- * @return boolean true on success, false if address already used or invalid in some way
- */
- public function addReplyTo( $address, $name = '' ) {
- return $this->addOrEnqueueAnAddress( 'Reply-To', $address, $name );
- }
-
- /**
- * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
- * of the form "display name " into an array of name/address pairs.
- * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
- * Note that quotes in the name part are removed.
- *
- * @param string $addrstr The address list string
- * @param bool $useimap Whether to use the IMAP extension to parse the list
- *
- * @return array
- * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
- */
- public function parseAddresses( $addrstr, $useimap = true ) {
- $addresses = array();
- if ( $useimap and function_exists( 'imap_rfc822_parse_adrlist' ) ) {
- //Use this built-in parser if it's available
- $list = imap_rfc822_parse_adrlist( $addrstr, '' );
- foreach ( $list as $address ) {
- if ( $address->host != '.SYNTAX-ERROR.' ) {
- if ( $this->validateAddress( $address->mailbox . '@' . $address->host ) ) {
- $addresses[] = array(
- 'name' => ( property_exists( $address, 'personal' ) ? $address->personal : '' ),
- 'address' => $address->mailbox . '@' . $address->host
- );
- }
- }
- }
- } else {
- //Use this simpler parser
- $list = explode( ',', $addrstr );
- foreach ( $list as $address ) {
- $address = trim( $address );
- //Is there a separate name part?
- if ( strpos( $address, '<' ) === false ) {
- //No separate name, just use the whole thing
- if ( $this->validateAddress( $address ) ) {
- $addresses[] = array(
- 'name' => '',
- 'address' => $address
- );
- }
- } else {
- list( $name, $email ) = explode( '<', $address );
- $email = trim( str_replace( '>', '', $email ) );
- if ( $this->validateAddress( $email ) ) {
- $addresses[] = array(
- 'name' => trim( str_replace( array( '"', "'" ), '', $name ) ),
- 'address' => $email
- );
- }
- }
- }
- }
-
- return $addresses;
- }
-
- /**
- * Set the From and FromName properties.
- *
- * @param string $address
- * @param string $name
- * @param boolean $auto Whether to also set the Sender address, defaults to true
- *
- * @throws phpmailerException
- * @return boolean
- */
- public function setFrom( $address, $name = '', $auto = true ) {
- $address = trim( $address );
- $name = trim( preg_replace( '/[\r\n]+/', '', $name ) ); //Strip breaks and trim
- // Don't validate now addresses with IDN. Will be done in send().
- if ( ( $pos = strrpos( $address, '@' ) ) === false or
- ( ! $this->has8bitChars( substr( $address, ++ $pos ) ) or ! $this->idnSupported() ) and
- ! $this->validateAddress( $address ) ) {
- $error_message = $this->lang( 'invalid_address' ) . " (setFrom) $address";
- $this->setError( $error_message );
- $this->edebug( $error_message );
- if ( $this->exceptions ) {
- throw new phpmailerException( $error_message );
- }
-
- return false;
- }
- $this->From = $address;
- $this->FromName = $name;
- if ( $auto ) {
- if ( empty( $this->Sender ) ) {
- $this->Sender = $address;
- }
- }
-
- return true;
- }
-
- /**
- * Return the Message-ID header of the last email.
- * Technically this is the value from the last time the headers were created,
- * but it's also the message ID of the last sent message except in
- * pathological cases.
- * @return string
- */
- public function getLastMessageID() {
- return $this->lastMessageID;
- }
-
- /**
- * Create a message and send it.
- * Uses the sending method specified by $Mailer.
- * @throws phpmailerException
- * @return boolean false on error - See the ErrorInfo property for details of the error.
- */
- public function send() {
- try {
- if ( ! $this->preSend() ) {
- return false;
- }
-
- return $this->postSend();
- } catch ( phpmailerException $exc ) {
- $this->mailHeader = '';
- $this->setError( $exc->getMessage() );
- if ( $this->exceptions ) {
- throw $exc;
- }
-
- return false;
- }
- }
-
- /**
- * Prepare a message for sending.
- * @throws phpmailerException
- * @return boolean
- */
- public function preSend() {
- try {
- $this->error_count = 0; // Reset errors
- $this->mailHeader = '';
-
- // Dequeue recipient and Reply-To addresses with IDN
- foreach ( array_merge( $this->RecipientsQueue, $this->ReplyToQueue ) as $params ) {
- $params[1] = $this->punyencodeAddress( $params[1] );
- call_user_func_array( array( $this, 'addAnAddress' ), $params );
- }
- if ( ( count( $this->to ) + count( $this->cc ) + count( $this->bcc ) ) < 1 ) {
- throw new phpmailerException( $this->lang( 'provide_address' ), self::STOP_CRITICAL );
- }
-
- // Validate From, Sender, and ConfirmReadingTo addresses
- foreach ( array( 'From', 'Sender', 'ConfirmReadingTo' ) as $address_kind ) {
- $this->$address_kind = trim( $this->$address_kind );
- if ( empty( $this->$address_kind ) ) {
- continue;
- }
- $this->$address_kind = $this->punyencodeAddress( $this->$address_kind );
- if ( ! $this->validateAddress( $this->$address_kind ) ) {
- $error_message = $this->lang( 'invalid_address' ) . ' (punyEncode) ' . $this->$address_kind;
- $this->setError( $error_message );
- $this->edebug( $error_message );
- if ( $this->exceptions ) {
- throw new phpmailerException( $error_message );
- }
-
- return false;
- }
- }
-
- // Set whether the message is multipart/alternative
- if ( $this->alternativeExists() ) {
- $this->ContentType = 'multipart/alternative';
- }
-
- $this->setMessageType();
- // Refuse to send an empty message unless we are specifically allowing it
- if ( ! $this->AllowEmpty and empty( $this->Body ) ) {
- throw new phpmailerException( $this->lang( 'empty_message' ), self::STOP_CRITICAL );
- }
-
- // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
- $this->MIMEHeader = '';
- $this->MIMEBody = $this->createBody();
- // createBody may have added some headers, so retain them
- $tempheaders = $this->MIMEHeader;
- $this->MIMEHeader = $this->createHeader();
- $this->MIMEHeader .= $tempheaders;
-
- // To capture the complete message when using mail(), create
- // an extra header list which createHeader() doesn't fold in
- if ( $this->Mailer == 'mail' ) {
- if ( count( $this->to ) > 0 ) {
- $this->mailHeader .= $this->addrAppend( 'To', $this->to );
- } else {
- $this->mailHeader .= $this->headerLine( 'To', 'undisclosed-recipients:;' );
- }
- $this->mailHeader .= $this->headerLine(
- 'Subject',
- $this->encodeHeader( $this->secureHeader( trim( $this->Subject ) ) )
- );
- }
-
- // Sign with DKIM if enabled
- if ( ! empty( $this->DKIM_domain )
- && ! empty( $this->DKIM_private )
- && ! empty( $this->DKIM_selector )
- && file_exists( $this->DKIM_private ) ) {
- $header_dkim = $this->DKIM_Add(
- $this->MIMEHeader . $this->mailHeader,
- $this->encodeHeader( $this->secureHeader( $this->Subject ) ),
- $this->MIMEBody
- );
- $this->MIMEHeader = rtrim( $this->MIMEHeader, "\r\n " ) . self::CRLF .
- str_replace( "\r\n", "\n", $header_dkim ) . self::CRLF;
- }
-
- return true;
- } catch ( phpmailerException $exc ) {
- $this->setError( $exc->getMessage() );
- if ( $this->exceptions ) {
- throw $exc;
- }
-
- return false;
- }
- }
-
- /**
- * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
- * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
- * This function silently returns unmodified address if:
- * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
- * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
- * or fails for any reason (e.g. domain has characters not allowed in an IDN)
- * @see PHPMailer::$CharSet
- *
- * @param string $address The email address to convert
- *
- * @return string The encoded address in ASCII form
- */
- public function punyencodeAddress( $address ) {
- // Verify we have required functions, CharSet, and at-sign.
- if ( $this->idnSupported() and
- ! empty( $this->CharSet ) and
- ( $pos = strrpos( $address, '@' ) ) !== false ) {
- $domain = substr( $address, ++ $pos );
- // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
- if ( $this->has8bitChars( $domain ) and @mb_check_encoding( $domain, $this->CharSet ) ) {
- $domain = mb_convert_encoding( $domain, 'UTF-8', $this->CharSet );
- if ( ( $punycode = defined( 'INTL_IDNA_VARIANT_UTS46' ) ?
- idn_to_ascii( $domain, 0, INTL_IDNA_VARIANT_UTS46 ) :
- idn_to_ascii( $domain ) ) !== false ) {
- return substr( $address, 0, $pos ) . $punycode;
- }
- }
- }
-
- return $address;
- }
-
- /**
- * Check if this message has an alternative body set.
- * @return boolean
- */
- public function alternativeExists() {
- return ! empty( $this->AltBody );
- }
-
- /**
- * Set the message type.
- * PHPMailer only supports some preset message types,
- * not arbitrary MIME structures.
- * @access protected
- * @return void
- */
- protected function setMessageType() {
- $type = array();
- if ( $this->alternativeExists() ) {
- $type[] = 'alt';
- }
- if ( $this->inlineImageExists() ) {
- $type[] = 'inline';
- }
- if ( $this->attachmentExists() ) {
- $type[] = 'attach';
- }
- $this->message_type = implode( '_', $type );
- if ( $this->message_type == '' ) {
- $this->message_type = 'plain';
- }
- }
-
- /**
- * Check if an inline attachment is present.
- * @access public
- * @return boolean
- */
- public function inlineImageExists() {
- foreach ( $this->attachment as $attachment ) {
- if ( $attachment[6] == 'inline' ) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Check if an attachment (non-inline) is present.
- * @return boolean
- */
- public function attachmentExists() {
- foreach ( $this->attachment as $attachment ) {
- if ( $attachment[6] == 'attachment' ) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Assemble the message body.
- * Returns an empty string on failure.
- * @access public
- * @throws phpmailerException
- * @return string The assembled message body
- */
- public function createBody() {
- $body = '';
- //Create unique IDs and preset boundaries
- $this->uniqueid = md5( uniqid( time() ) );
- $this->boundary[1] = 'b1_' . $this->uniqueid;
- $this->boundary[2] = 'b2_' . $this->uniqueid;
- $this->boundary[3] = 'b3_' . $this->uniqueid;
-
- if ( $this->sign_key_file ) {
- $body .= $this->getMailMIME() . $this->LE;
- }
-
- $this->setWordWrap();
-
- $bodyEncoding = $this->Encoding;
- $bodyCharSet = $this->CharSet;
- //Can we do a 7-bit downgrade?
- if ( $bodyEncoding == '8bit' and ! $this->has8bitChars( $this->Body ) ) {
- $bodyEncoding = '7bit';
- $bodyCharSet = 'us-ascii';
- }
- //If lines are too long, and we're not already using an encoding that will shorten them,
- //change to quoted-printable transfer encoding
- if ( 'base64' != $this->Encoding and self::hasLineLongerThanMax( $this->Body ) ) {
- $this->Encoding = 'quoted-printable';
- $bodyEncoding = 'quoted-printable';
- }
-
- $altBodyEncoding = $this->Encoding;
- $altBodyCharSet = $this->CharSet;
- //Can we do a 7-bit downgrade?
- if ( $altBodyEncoding == '8bit' and ! $this->has8bitChars( $this->AltBody ) ) {
- $altBodyEncoding = '7bit';
- $altBodyCharSet = 'us-ascii';
- }
- //If lines are too long, and we're not already using an encoding that will shorten them,
- //change to quoted-printable transfer encoding
- if ( 'base64' != $altBodyEncoding and self::hasLineLongerThanMax( $this->AltBody ) ) {
- $altBodyEncoding = 'quoted-printable';
- }
- //Use this as a preamble in all multipart message types
- $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
- switch ( $this->message_type ) {
- case 'inline':
- $body .= $mimepre;
- $body .= $this->getBoundary( $this->boundary[1], $bodyCharSet, '', $bodyEncoding );
- $body .= $this->encodeString( $this->Body, $bodyEncoding );
- $body .= $this->LE . $this->LE;
- $body .= $this->attachAll( 'inline', $this->boundary[1] );
- break;
- case 'attach':
- $body .= $mimepre;
- $body .= $this->getBoundary( $this->boundary[1], $bodyCharSet, '', $bodyEncoding );
- $body .= $this->encodeString( $this->Body, $bodyEncoding );
- $body .= $this->LE . $this->LE;
- $body .= $this->attachAll( 'attachment', $this->boundary[1] );
- break;
- case 'inline_attach':
- $body .= $mimepre;
- $body .= $this->textLine( '--' . $this->boundary[1] );
- $body .= $this->headerLine( 'Content-Type', 'multipart/related;' );
- $body .= $this->textLine( "\tboundary=\"" . $this->boundary[2] . '"' );
- $body .= $this->LE;
- $body .= $this->getBoundary( $this->boundary[2], $bodyCharSet, '', $bodyEncoding );
- $body .= $this->encodeString( $this->Body, $bodyEncoding );
- $body .= $this->LE . $this->LE;
- $body .= $this->attachAll( 'inline', $this->boundary[2] );
- $body .= $this->LE;
- $body .= $this->attachAll( 'attachment', $this->boundary[1] );
- break;
- case 'alt':
- $body .= $mimepre;
- $body .= $this->getBoundary( $this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding );
- $body .= $this->encodeString( $this->AltBody, $altBodyEncoding );
- $body .= $this->LE . $this->LE;
- $body .= $this->getBoundary( $this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding );
- $body .= $this->encodeString( $this->Body, $bodyEncoding );
- $body .= $this->LE . $this->LE;
- if ( ! empty( $this->Ical ) ) {
- $body .= $this->getBoundary( $this->boundary[1], '', 'text/calendar; method=REQUEST', '' );
- $body .= $this->encodeString( $this->Ical, $this->Encoding );
- $body .= $this->LE . $this->LE;
- }
- $body .= $this->endBoundary( $this->boundary[1] );
- break;
- case 'alt_inline':
- $body .= $mimepre;
- $body .= $this->getBoundary( $this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding );
- $body .= $this->encodeString( $this->AltBody, $altBodyEncoding );
- $body .= $this->LE . $this->LE;
- $body .= $this->textLine( '--' . $this->boundary[1] );
- $body .= $this->headerLine( 'Content-Type', 'multipart/related;' );
- $body .= $this->textLine( "\tboundary=\"" . $this->boundary[2] . '"' );
- $body .= $this->LE;
- $body .= $this->getBoundary( $this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding );
- $body .= $this->encodeString( $this->Body, $bodyEncoding );
- $body .= $this->LE . $this->LE;
- $body .= $this->attachAll( 'inline', $this->boundary[2] );
- $body .= $this->LE;
- $body .= $this->endBoundary( $this->boundary[1] );
- break;
- case 'alt_attach':
- $body .= $mimepre;
- $body .= $this->textLine( '--' . $this->boundary[1] );
- $body .= $this->headerLine( 'Content-Type', 'multipart/alternative;' );
- $body .= $this->textLine( "\tboundary=\"" . $this->boundary[2] . '"' );
- $body .= $this->LE;
- $body .= $this->getBoundary( $this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding );
- $body .= $this->encodeString( $this->AltBody, $altBodyEncoding );
- $body .= $this->LE . $this->LE;
- $body .= $this->getBoundary( $this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding );
- $body .= $this->encodeString( $this->Body, $bodyEncoding );
- $body .= $this->LE . $this->LE;
- $body .= $this->endBoundary( $this->boundary[2] );
- $body .= $this->LE;
- $body .= $this->attachAll( 'attachment', $this->boundary[1] );
- break;
- case 'alt_inline_attach':
- $body .= $mimepre;
- $body .= $this->textLine( '--' . $this->boundary[1] );
- $body .= $this->headerLine( 'Content-Type', 'multipart/alternative;' );
- $body .= $this->textLine( "\tboundary=\"" . $this->boundary[2] . '"' );
- $body .= $this->LE;
- $body .= $this->getBoundary( $this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding );
- $body .= $this->encodeString( $this->AltBody, $altBodyEncoding );
- $body .= $this->LE . $this->LE;
- $body .= $this->textLine( '--' . $this->boundary[2] );
- $body .= $this->headerLine( 'Content-Type', 'multipart/related;' );
- $body .= $this->textLine( "\tboundary=\"" . $this->boundary[3] . '"' );
- $body .= $this->LE;
- $body .= $this->getBoundary( $this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding );
- $body .= $this->encodeString( $this->Body, $bodyEncoding );
- $body .= $this->LE . $this->LE;
- $body .= $this->attachAll( 'inline', $this->boundary[3] );
- $body .= $this->LE;
- $body .= $this->endBoundary( $this->boundary[2] );
- $body .= $this->LE;
- $body .= $this->attachAll( 'attachment', $this->boundary[1] );
- break;
- default:
- // catch case 'plain' and case ''
- $body .= $this->encodeString( $this->Body, $bodyEncoding );
- break;
- }
-
- if ( $this->isError() ) {
- $body = '';
- } elseif ( $this->sign_key_file ) {
- try {
- if ( ! defined( 'PKCS7_TEXT' ) ) {
- throw new phpmailerException( $this->lang( 'extension_missing' ) . 'openssl' );
- }
- $file = tempnam( sys_get_temp_dir(), 'mail' );
- if ( false === file_put_contents( $file, $body ) ) {
- throw new phpmailerException( $this->lang( 'signing' ) . ' Could not write temp file' );
- }
- $signed = tempnam( sys_get_temp_dir(), 'signed' );
- //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
- if ( empty( $this->sign_extracerts_file ) ) {
- $sign = @openssl_pkcs7_sign(
- $file,
- $signed,
- 'file://' . realpath( $this->sign_cert_file ),
- array( 'file://' . realpath( $this->sign_key_file ), $this->sign_key_pass ),
- null
- );
- } else {
- $sign = @openssl_pkcs7_sign(
- $file,
- $signed,
- 'file://' . realpath( $this->sign_cert_file ),
- array( 'file://' . realpath( $this->sign_key_file ), $this->sign_key_pass ),
- null,
- PKCS7_DETACHED,
- $this->sign_extracerts_file
- );
- }
- if ( $sign ) {
- @unlink( $file );
- $body = file_get_contents( $signed );
- @unlink( $signed );
- //The message returned by openssl contains both headers and body, so need to split them up
- $parts = explode( "\n\n", $body, 2 );
- $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
- $body = $parts[1];
- } else {
- @unlink( $file );
- @unlink( $signed );
- throw new phpmailerException( $this->lang( 'signing' ) . openssl_error_string() );
- }
- } catch ( phpmailerException $exc ) {
- $body = '';
- if ( $this->exceptions ) {
- throw $exc;
- }
- }
- }
-
- return $body;
- }
-
- /**
- * Get the message MIME type headers.
- * @access public
- * @return string
- */
- public function getMailMIME() {
- $result = '';
- $ismultipart = true;
- switch ( $this->message_type ) {
- case 'inline':
- $result .= $this->headerLine( 'Content-Type', 'multipart/related;' );
- $result .= $this->textLine( "\tboundary=\"" . $this->boundary[1] . '"' );
- break;
- case 'attach':
- case 'inline_attach':
- case 'alt_attach':
- case 'alt_inline_attach':
- $result .= $this->headerLine( 'Content-Type', 'multipart/mixed;' );
- $result .= $this->textLine( "\tboundary=\"" . $this->boundary[1] . '"' );
- break;
- case 'alt':
- case 'alt_inline':
- $result .= $this->headerLine( 'Content-Type', 'multipart/alternative;' );
- $result .= $this->textLine( "\tboundary=\"" . $this->boundary[1] . '"' );
- break;
- default:
- // Catches case 'plain': and case '':
- $result .= $this->textLine( 'Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet );
- $ismultipart = false;
- break;
- }
- // RFC1341 part 5 says 7bit is assumed if not specified
- if ( $this->Encoding != '7bit' ) {
- // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
- if ( $ismultipart ) {
- if ( $this->Encoding == '8bit' ) {
- $result .= $this->headerLine( 'Content-Transfer-Encoding', '8bit' );
- }
- // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
- } else {
- $result .= $this->headerLine( 'Content-Transfer-Encoding', $this->Encoding );
- }
- }
-
- if ( $this->Mailer != 'mail' ) {
- $result .= $this->LE;
- }
-
- return $result;
- }
-
- /**
- * Format a header line.
- * @access public
- *
- * @param string $name
- * @param string $value
- *
- * @return string
- */
- public function headerLine( $name, $value ) {
- return $name . ': ' . $value . $this->LE;
- }
-
- /**
- * Return a formatted mail line.
- * @access public
- *
- * @param string $value
- *
- * @return string
- */
- public function textLine( $value ) {
- return $value . $this->LE;
- }
-
- /**
- * Apply word wrapping to the message body.
- * Wraps the message body to the number of chars set in the WordWrap property.
- * You should only do this to plain-text bodies as wrapping HTML tags may break them.
- * This is called automatically by createBody(), so you don't need to call it yourself.
- * @access public
- * @return void
- */
- public function setWordWrap() {
- if ( $this->WordWrap < 1 ) {
- return;
- }
-
- switch ( $this->message_type ) {
- case 'alt':
- case 'alt_inline':
- case 'alt_attach':
- case 'alt_inline_attach':
- $this->AltBody = $this->wrapText( $this->AltBody, $this->WordWrap );
- break;
- default:
- $this->Body = $this->wrapText( $this->Body, $this->WordWrap );
- break;
- }
- }
-
- /**
- * Detect if a string contains a line longer than the maximum line length allowed.
- *
- * @param string $str
- *
- * @return boolean
- * @static
- */
- public static function hasLineLongerThanMax( $str ) {
- //+2 to include CRLF line break for a 1000 total
- return (boolean) preg_match( '/^(.{' . ( self::MAX_LINE_LENGTH + 2 ) . ',})/m', $str );
- }
-
- /**
- * Return the start of a message boundary.
- * @access protected
- *
- * @param string $boundary
- * @param string $charSet
- * @param string $contentType
- * @param string $encoding
- *
- * @return string
- */
- protected function getBoundary( $boundary, $charSet, $contentType, $encoding ) {
- $result = '';
- if ( $charSet == '' ) {
- $charSet = $this->CharSet;
- }
- if ( $contentType == '' ) {
- $contentType = $this->ContentType;
- }
- if ( $encoding == '' ) {
- $encoding = $this->Encoding;
- }
- $result .= $this->textLine( '--' . $boundary );
- $result .= sprintf( 'Content-Type: %s; charset=%s', $contentType, $charSet );
- $result .= $this->LE;
- // RFC1341 part 5 says 7bit is assumed if not specified
- if ( $encoding != '7bit' ) {
- $result .= $this->headerLine( 'Content-Transfer-Encoding', $encoding );
- }
- $result .= $this->LE;
-
- return $result;
- }
-
- /**
- * Encode a string in requested format.
- * Returns an empty string on failure.
- *
- * @param string $str The text to encode
- * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
- *
- * @access public
- * @return string
- */
- public function encodeString( $str, $encoding = 'base64' ) {
- $encoded = '';
- switch ( strtolower( $encoding ) ) {
- case 'base64':
- $encoded = chunk_split( base64_encode( $str ), 76, $this->LE );
- break;
- case '7bit':
- case '8bit':
- $encoded = $this->fixEOL( $str );
- // Make sure it ends with a line break
- if ( substr( $encoded, - ( strlen( $this->LE ) ) ) != $this->LE ) {
- $encoded .= $this->LE;
- }
- break;
- case 'binary':
- $encoded = $str;
- break;
- case 'quoted-printable':
- $encoded = $this->encodeQP( $str );
- break;
- default:
- $this->setError( $this->lang( 'encoding' ) . $encoding );
- break;
- }
-
- return $encoded;
- }
-
- /**
- * Encode a string in quoted-printable format.
- * According to RFC2045 section 6.7.
- * @access public
- *
- * @param string $string The text to encode
- * @param integer $line_max Number of chars allowed on a line before wrapping
- *
- * @return string
- * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
- */
- public function encodeQP( $string, $line_max = 76 ) {
- // Use native function if it's available (>= PHP5.3)
- if ( function_exists( 'quoted_printable_encode' ) ) {
- return quoted_printable_encode( $string );
- }
- // Fall back to a pure PHP implementation
- $string = str_replace(
- array( '%20', '%0D%0A.', '%0D%0A', '%' ),
- array( ' ', "\r\n=2E", "\r\n", '=' ),
- rawurlencode( $string )
- );
-
- return preg_replace( '/[^\r\n]{' . ( $line_max - 3 ) . '}[^=\r\n]{2}/', "$0=\r\n", $string );
- }
-
- /**
- * Attach all file, string, and binary attachments to the message.
- * Returns an empty string on failure.
- * @access protected
- *
- * @param string $disposition_type
- * @param string $boundary
- *
- * @return string
- */
- protected function attachAll( $disposition_type, $boundary ) {
- // Return text of body
- $mime = array();
- $cidUniq = array();
- $incl = array();
-
- // Add all attachments
- foreach ( $this->attachment as $attachment ) {
- // Check if it is a valid disposition_filter
- if ( $attachment[6] == $disposition_type ) {
- // Check for string attachment
- $string = '';
- $path = '';
- $bString = $attachment[5];
- if ( $bString ) {
- $string = $attachment[0];
- } else {
- $path = $attachment[0];
- }
-
- $inclhash = md5( serialize( $attachment ) );
- if ( in_array( $inclhash, $incl ) ) {
- continue;
- }
- $incl[] = $inclhash;
- $name = $attachment[2];
- $encoding = $attachment[3];
- $type = $attachment[4];
- $disposition = $attachment[6];
- $cid = $attachment[7];
- if ( $disposition == 'inline' && array_key_exists( $cid, $cidUniq ) ) {
- continue;
- }
- $cidUniq[ $cid ] = true;
-
- $mime[] = sprintf( '--%s%s', $boundary, $this->LE );
- //Only include a filename property if we have one
- if ( ! empty( $name ) ) {
- $mime[] = sprintf(
- 'Content-Type: %s; name="%s"%s',
- $type,
- $this->encodeHeader( $this->secureHeader( $name ) ),
- $this->LE
- );
- } else {
- $mime[] = sprintf(
- 'Content-Type: %s%s',
- $type,
- $this->LE
- );
- }
- // RFC1341 part 5 says 7bit is assumed if not specified
- if ( $encoding != '7bit' ) {
- $mime[] = sprintf( 'Content-Transfer-Encoding: %s%s', $encoding, $this->LE );
- }
-
- if ( $disposition == 'inline' ) {
- $mime[] = sprintf( 'Content-ID: <%s>%s', $cid, $this->LE );
- }
-
- // If a filename contains any of these chars, it should be quoted,
- // but not otherwise: RFC2183 & RFC2045 5.1
- // Fixes a warning in IETF's msglint MIME checker
- // Allow for bypassing the Content-Disposition header totally
- if ( ! ( empty( $disposition ) ) ) {
- $encoded_name = $this->encodeHeader( $this->secureHeader( $name ) );
- if ( preg_match( '/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name ) ) {
- $mime[] = sprintf(
- 'Content-Disposition: %s; filename="%s"%s',
- $disposition,
- $encoded_name,
- $this->LE . $this->LE
- );
- } else {
- if ( ! empty( $encoded_name ) ) {
- $mime[] = sprintf(
- 'Content-Disposition: %s; filename=%s%s',
- $disposition,
- $encoded_name,
- $this->LE . $this->LE
- );
- } else {
- $mime[] = sprintf(
- 'Content-Disposition: %s%s',
- $disposition,
- $this->LE . $this->LE
- );
- }
- }
- } else {
- $mime[] = $this->LE;
- }
-
- // Encode as string attachment
- if ( $bString ) {
- $mime[] = $this->encodeString( $string, $encoding );
- if ( $this->isError() ) {
- return '';
- }
- $mime[] = $this->LE . $this->LE;
- } else {
- $mime[] = $this->encodeFile( $path, $encoding );
- if ( $this->isError() ) {
- return '';
- }
- $mime[] = $this->LE . $this->LE;
- }
- }
- }
-
- $mime[] = sprintf( '--%s--%s', $boundary, $this->LE );
-
- return implode( '', $mime );
- }
-
- /**
- * Check if an error occurred.
- * @access public
- * @return boolean True if an error did occur.
- */
- public function isError() {
- return ( $this->error_count > 0 );
- }
-
- /**
- * Encode a file attachment in requested format.
- * Returns an empty string on failure.
- *
- * @param string $path The full path to the file
- * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
- *
- * @throws phpmailerException
- * @access protected
- * @return string
- */
- protected function encodeFile( $path, $encoding = 'base64' ) {
- try {
- if ( ! is_readable( $path ) ) {
- throw new phpmailerException( $this->lang( 'file_open' ) . $path, self::STOP_CONTINUE );
- }
- $magic_quotes = get_magic_quotes_runtime();
- if ( $magic_quotes ) {
- if ( version_compare( PHP_VERSION, '5.3.0', '<' ) ) {
- set_magic_quotes_runtime( false );
- } else {
- //Doesn't exist in PHP 5.4, but we don't need to check because
- //get_magic_quotes_runtime always returns false in 5.4+
- //so it will never get here
- ini_set( 'magic_quotes_runtime', false );
- }
- }
- $file_buffer = file_get_contents( $path );
- $file_buffer = $this->encodeString( $file_buffer, $encoding );
- if ( $magic_quotes ) {
- if ( version_compare( PHP_VERSION, '5.3.0', '<' ) ) {
- set_magic_quotes_runtime( $magic_quotes );
- } else {
- ini_set( 'magic_quotes_runtime', $magic_quotes );
- }
- }
-
- return $file_buffer;
- } catch ( Exception $exc ) {
- $this->setError( $exc->getMessage() );
-
- return '';
- }
- }
-
- /**
- * Return the end of a message boundary.
- * @access protected
- *
- * @param string $boundary
- *
- * @return string
- */
- protected function endBoundary( $boundary ) {
- return $this->LE . '--' . $boundary . '--' . $this->LE;
- }
-
- /**
- * Assemble message headers.
- * @access public
- * @return string The assembled headers
- */
- public function createHeader() {
- $result = '';
-
- if ( $this->MessageDate == '' ) {
- $this->MessageDate = self::rfcDate();
- }
- $result .= $this->headerLine( 'Date', $this->MessageDate );
-
- // To be created automatically by mail()
- if ( $this->SingleTo ) {
- if ( $this->Mailer != 'mail' ) {
- foreach ( $this->to as $toaddr ) {
- $this->SingleToArray[] = $this->addrFormat( $toaddr );
- }
- }
- } else {
- if ( count( $this->to ) > 0 ) {
- if ( $this->Mailer != 'mail' ) {
- $result .= $this->addrAppend( 'To', $this->to );
- }
- } elseif ( count( $this->cc ) == 0 ) {
- $result .= $this->headerLine( 'To', 'undisclosed-recipients:;' );
- }
- }
-
- $result .= $this->addrAppend( 'From', array( array( trim( $this->From ), $this->FromName ) ) );
-
- // sendmail and mail() extract Cc from the header before sending
- if ( count( $this->cc ) > 0 ) {
- $result .= $this->addrAppend( 'Cc', $this->cc );
- }
-
- // sendmail and mail() extract Bcc from the header before sending
- if ( (
- $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
- )
- and count( $this->bcc ) > 0
- ) {
- $result .= $this->addrAppend( 'Bcc', $this->bcc );
- }
-
- if ( count( $this->ReplyTo ) > 0 ) {
- $result .= $this->addrAppend( 'Reply-To', $this->ReplyTo );
- }
-
- // mail() sets the subject itself
- if ( $this->Mailer != 'mail' ) {
- $result .= $this->headerLine( 'Subject', $this->encodeHeader( $this->secureHeader( $this->Subject ) ) );
- }
-
- if ( '' != $this->MessageID and preg_match( '/^<.*@.*>$/', $this->MessageID ) ) {
- $this->lastMessageID = $this->MessageID;
- } else {
- $this->lastMessageID = sprintf( '<%s@%s>', $this->uniqueid, $this->serverHostname() );
- }
- $result .= $this->headerLine( 'Message-ID', $this->lastMessageID );
- if ( ! is_null( $this->Priority ) ) {
- $result .= $this->headerLine( 'X-Priority', $this->Priority );
- }
- if ( $this->XMailer == '' ) {
- $result .= $this->headerLine(
- 'X-Mailer',
- 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
- );
- } else {
- $myXmailer = trim( $this->XMailer );
- if ( $myXmailer ) {
- $result .= $this->headerLine( 'X-Mailer', $myXmailer );
- }
- }
-
- if ( $this->ConfirmReadingTo != '' ) {
- $result .= $this->headerLine( 'Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>' );
- }
-
- // Add custom headers
- foreach ( $this->CustomHeader as $header ) {
- $result .= $this->headerLine(
- trim( $header[0] ),
- $this->encodeHeader( trim( $header[1] ) )
- );
- }
- if ( ! $this->sign_key_file ) {
- $result .= $this->headerLine( 'MIME-Version', '1.0' );
- $result .= $this->getMailMIME();
- }
-
- return $result;
- }
-
- /**
- * Return an RFC 822 formatted date.
- * @access public
- * @return string
- * @static
- */
- public static function rfcDate() {
- // Set the time zone to whatever the default is to avoid 500 errors
- // Will default to UTC if it's not set properly in php.ini
- date_default_timezone_set( @date_default_timezone_get() );
-
- return date( 'D, j M Y H:i:s O' );
- }
-
- /**
- * Format an address for use in a message header.
- * @access public
- *
- * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
- * like array('joe@example.com', 'Joe User')
- *
- * @return string
- */
- public function addrFormat( $addr ) {
- if ( empty( $addr[1] ) ) { // No name provided
- return $this->secureHeader( $addr[0] );
- } else {
- return $this->encodeHeader( $this->secureHeader( $addr[1] ), 'phrase' ) . ' <' . $this->secureHeader(
- $addr[0]
- ) . '>';
- }
- }
-
- /**
- * Create recipient headers.
- * @access public
- *
- * @param string $type
- * @param array $addr An array of recipient,
- * where each recipient is a 2-element indexed array with element 0 containing an address
- * and element 1 containing a name, like:
- * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
- *
- * @return string
- */
- public function addrAppend( $type, $addr ) {
- $addresses = array();
- foreach ( $addr as $address ) {
- $addresses[] = $this->addrFormat( $address );
- }
-
- return $type . ': ' . implode( ', ', $addresses ) . $this->LE;
- }
-
- /**
- * Get the server hostname.
- * Returns 'localhost.localdomain' if unknown.
- * @access protected
- * @return string
- */
- protected function serverHostname() {
- $result = 'localhost.localdomain';
- if ( ! empty( $this->Hostname ) ) {
- $result = $this->Hostname;
- } elseif ( isset( $_SERVER ) and array_key_exists( 'SERVER_NAME', $_SERVER ) and ! empty( $_SERVER['SERVER_NAME'] ) ) {
- $result = $_SERVER['SERVER_NAME'];
- } elseif ( function_exists( 'gethostname' ) && gethostname() !== false ) {
- $result = gethostname();
- } elseif ( php_uname( 'n' ) !== false ) {
- $result = php_uname( 'n' );
- }
-
- return $result;
- }
-
- /**
- * Create the DKIM header and body in a new message header.
- * @access public
- *
- * @param string $headers_line Header lines
- * @param string $subject Subject
- * @param string $body Body
- *
- * @return string
- */
- public function DKIM_Add( $headers_line, $subject, $body ) {
- $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
- $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
- $DKIMquery = 'dns/txt'; // Query method
- $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
- $subject_header = "Subject: $subject";
- $headers = explode( $this->LE, $headers_line );
- $from_header = '';
- $to_header = '';
- $date_header = '';
- $current = '';
- foreach ( $headers as $header ) {
- if ( strpos( $header, 'From:' ) === 0 ) {
- $from_header = $header;
- $current = 'from_header';
- } elseif ( strpos( $header, 'To:' ) === 0 ) {
- $to_header = $header;
- $current = 'to_header';
- } elseif ( strpos( $header, 'Date:' ) === 0 ) {
- $date_header = $header;
- $current = 'date_header';
- } else {
- if ( ! empty( $$current ) && strpos( $header, ' =?' ) === 0 ) {
- $$current .= $header;
- } else {
- $current = '';
- }
- }
- }
- $from = str_replace( '|', '=7C', $this->DKIM_QP( $from_header ) );
- $to = str_replace( '|', '=7C', $this->DKIM_QP( $to_header ) );
- $date = str_replace( '|', '=7C', $this->DKIM_QP( $date_header ) );
- $subject = str_replace(
- '|',
- '=7C',
- $this->DKIM_QP( $subject_header )
- ); // Copied header fields (dkim-quoted-printable)
- $body = $this->DKIM_BodyC( $body );
- $DKIMlen = strlen( $body ); // Length of body
- $DKIMb64 = base64_encode( pack( 'H*', hash( 'sha256', $body ) ) ); // Base64 of packed binary SHA-256 hash of body
- if ( '' == $this->DKIM_identity ) {
- $ident = '';
- } else {
- $ident = ' i=' . $this->DKIM_identity . ';';
- }
- $dkimhdrs = 'DKIM-Signature: v=1; a=' .
- $DKIMsignatureType . '; q=' .
- $DKIMquery . '; l=' .
- $DKIMlen . '; s=' .
- $this->DKIM_selector .
- ";\r\n" .
- "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
- "\th=From:To:Date:Subject;\r\n" .
- "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
- "\tz=$from\r\n" .
- "\t|$to\r\n" .
- "\t|$date\r\n" .
- "\t|$subject;\r\n" .
- "\tbh=" . $DKIMb64 . ";\r\n" .
- "\tb=";
- $toSign = $this->DKIM_HeaderC(
- $from_header . "\r\n" .
- $to_header . "\r\n" .
- $date_header . "\r\n" .
- $subject_header . "\r\n" .
- $dkimhdrs
- );
- $signed = $this->DKIM_Sign( $toSign );
-
- return $dkimhdrs . $signed . "\r\n";
- }
-
- /**
- * Quoted-Printable-encode a DKIM header.
- * @access public
- *
- * @param string $txt
- *
- * @return string
- */
- public function DKIM_QP( $txt ) {
- $line = '';
- for ( $i = 0; $i < strlen( $txt ); $i ++ ) {
- $ord = ord( $txt[ $i ] );
- if ( ( ( 0x21 <= $ord ) && ( $ord <= 0x3A ) ) || $ord == 0x3C || ( ( 0x3E <= $ord ) && ( $ord <= 0x7E ) ) ) {
- $line .= $txt[ $i ];
- } else {
- $line .= '=' . sprintf( '%02X', $ord );
- }
- }
-
- return $line;
- }
-
- /**
- * Generate a DKIM canonicalization body.
- * @access public
- *
- * @param string $body Message Body
- *
- * @return string
- */
- public function DKIM_BodyC( $body ) {
- if ( $body == '' ) {
- return "\r\n";
- }
- // stabilize line endings
- $body = str_replace( "\r\n", "\n", $body );
- $body = str_replace( "\n", "\r\n", $body );
- // END stabilize line endings
- while ( substr( $body, strlen( $body ) - 4, 4 ) == "\r\n\r\n" ) {
- $body = substr( $body, 0, strlen( $body ) - 2 );
- }
-
- return $body;
- }
-
- /**
- * Generate a DKIM canonicalization header.
- * @access public
- *
- * @param string $signHeader Header
- *
- * @return string
- */
- public function DKIM_HeaderC( $signHeader ) {
- $signHeader = preg_replace( '/\r\n\s+/', ' ', $signHeader );
- $lines = explode( "\r\n", $signHeader );
- foreach ( $lines as $key => $line ) {
- list( $heading, $value ) = explode( ':', $line, 2 );
- $heading = strtolower( $heading );
- $value = preg_replace( '/\s{2,}/', ' ', $value ); // Compress useless spaces
- $lines[ $key ] = $heading . ':' . trim( $value ); // Don't forget to remove WSP around the value
- }
- $signHeader = implode( "\r\n", $lines );
-
- return $signHeader;
- }
-
- /**
- * Generate a DKIM signature.
- * @access public
- *
- * @param string $signHeader
- *
- * @throws phpmailerException
- * @return string
- */
- public function DKIM_Sign( $signHeader ) {
- if ( ! defined( 'PKCS7_TEXT' ) ) {
- if ( $this->exceptions ) {
- throw new phpmailerException( $this->lang( 'extension_missing' ) . 'openssl' );
- }
-
- return '';
- }
- $privKeyStr = file_get_contents( $this->DKIM_private );
- if ( $this->DKIM_passphrase != '' ) {
- $privKey = openssl_pkey_get_private( $privKeyStr, $this->DKIM_passphrase );
- } else {
- $privKey = openssl_pkey_get_private( $privKeyStr );
- }
- if ( openssl_sign( $signHeader, $signature, $privKey, 'sha256WithRSAEncryption' ) ) { //sha1WithRSAEncryption
- openssl_pkey_free( $privKey );
-
- return base64_encode( $signature );
- }
- openssl_pkey_free( $privKey );
-
- return '';
- }
-
- /**
- * Actually send a message.
- * Send the email via the selected mechanism
- * @throws phpmailerException
- * @return boolean
- */
- public function postSend() {
- try {
- // Choose the mailer and send through it
- switch ( $this->Mailer ) {
- case 'sendmail':
- case 'qmail':
- return $this->sendmailSend( $this->MIMEHeader, $this->MIMEBody );
- case 'smtp':
- return $this->smtpSend( $this->MIMEHeader, $this->MIMEBody );
- case 'mail':
- return $this->mailSend( $this->MIMEHeader, $this->MIMEBody );
- default:
- $sendMethod = $this->Mailer . 'Send';
- if ( method_exists( $this, $sendMethod ) ) {
- return $this->$sendMethod( $this->MIMEHeader, $this->MIMEBody );
- }
-
- return $this->mailSend( $this->MIMEHeader, $this->MIMEBody );
- }
- } catch ( phpmailerException $exc ) {
- $this->setError( $exc->getMessage() );
- $this->edebug( $exc->getMessage() );
- if ( $this->exceptions ) {
- throw $exc;
- }
- }
-
- return false;
- }
-
- /**
- * Send mail using the $Sendmail program.
- *
- * @param string $header The message headers
- * @param string $body The message body
- *
- * @see PHPMailer::$Sendmail
- * @throws phpmailerException
- * @access protected
- * @return boolean
- */
- protected function sendmailSend( $header, $body ) {
- if ( $this->Sender != '' ) {
- if ( $this->Mailer == 'qmail' ) {
- $sendmail = sprintf( '%s -f%s', escapeshellcmd( $this->Sendmail ), escapeshellarg( $this->Sender ) );
- } else {
- $sendmail = sprintf( '%s -oi -f%s -t', escapeshellcmd( $this->Sendmail ), escapeshellarg( $this->Sender ) );
- }
- } else {
- if ( $this->Mailer == 'qmail' ) {
- $sendmail = sprintf( '%s', escapeshellcmd( $this->Sendmail ) );
- } else {
- $sendmail = sprintf( '%s -oi -t', escapeshellcmd( $this->Sendmail ) );
- }
- }
- if ( $this->SingleTo ) {
- foreach ( $this->SingleToArray as $toAddr ) {
- if ( ! @$mail = popen( $sendmail, 'w' ) ) {
- throw new phpmailerException( $this->lang( 'execute' ) . $this->Sendmail, self::STOP_CRITICAL );
- }
- fputs( $mail, 'To: ' . $toAddr . "\n" );
- fputs( $mail, $header );
- fputs( $mail, $body );
- $result = pclose( $mail );
- $this->doCallback(
- ( $result == 0 ),
- array( $toAddr ),
- $this->cc,
- $this->bcc,
- $this->Subject,
- $body,
- $this->From
- );
- if ( $result != 0 ) {
- throw new phpmailerException( $this->lang( 'execute' ) . $this->Sendmail, self::STOP_CRITICAL );
- }
- }
- } else {
- if ( ! @$mail = popen( $sendmail, 'w' ) ) {
- throw new phpmailerException( $this->lang( 'execute' ) . $this->Sendmail, self::STOP_CRITICAL );
- }
- fputs( $mail, $header );
- fputs( $mail, $body );
- $result = pclose( $mail );
- $this->doCallback(
- ( $result == 0 ),
- $this->to,
- $this->cc,
- $this->bcc,
- $this->Subject,
- $body,
- $this->From
- );
- if ( $result != 0 ) {
- throw new phpmailerException( $this->lang( 'execute' ) . $this->Sendmail, self::STOP_CRITICAL );
- }
- }
-
- return true;
- }
-
- /**
- * Perform a callback.
- *
- * @param boolean $isSent
- * @param array $to
- * @param array $cc
- * @param array $bcc
- * @param string $subject
- * @param string $body
- * @param string $from
- */
- protected function doCallback( $isSent, $to, $cc, $bcc, $subject, $body, $from ) {
- if ( ! empty( $this->action_function ) && is_callable( $this->action_function ) ) {
- $params = array( $isSent, $to, $cc, $bcc, $subject, $body, $from );
- call_user_func_array( $this->action_function, $params );
- }
- }
-
- /**
- * Send mail via SMTP.
- * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
- * Uses the PHPMailerSMTP class by default.
- * @see PHPMailer::getSMTPInstance() to use a different class.
- *
- * @param string $header The message headers
- * @param string $body The message body
- *
- * @throws phpmailerException
- * @uses SMTP
- * @access protected
- * @return boolean
- */
- protected function smtpSend( $header, $body ) {
- $bad_rcpt = array();
- if ( ! $this->smtpConnect( $this->SMTPOptions ) ) {
- throw new phpmailerException( $this->lang( 'smtp_connect_failed' ), self::STOP_CRITICAL );
- }
- if ( '' == $this->Sender ) {
- $smtp_from = $this->From;
- } else {
- $smtp_from = $this->Sender;
- }
- if ( ! $this->smtp->mail( $smtp_from ) ) {
- $this->setError( $this->lang( 'from_failed' ) . $smtp_from . ' : ' . implode( ',', $this->smtp->getError() ) );
- throw new phpmailerException( $this->ErrorInfo, self::STOP_CRITICAL );
- }
-
- // Attempt to send to all recipients
- foreach ( array( $this->to, $this->cc, $this->bcc ) as $togroup ) {
- foreach ( $togroup as $to ) {
- if ( ! $this->smtp->recipient( $to[0] ) ) {
- $error = $this->smtp->getError();
- $bad_rcpt[] = array( 'to' => $to[0], 'error' => $error['detail'] );
- $isSent = false;
- } else {
- $isSent = true;
- }
- $this->doCallback( $isSent, array( $to[0] ), array(), array(), $this->Subject, $body, $this->From );
- }
- }
-
- // Only send the DATA command if we have viable recipients
- if ( ( count( $this->all_recipients ) > count( $bad_rcpt ) ) and ! $this->smtp->data( $header . $body ) ) {
- throw new phpmailerException( $this->lang( 'data_not_accepted' ), self::STOP_CRITICAL );
- }
- if ( $this->SMTPKeepAlive ) {
- $this->smtp->reset();
- } else {
- $this->smtp->quit();
- $this->smtp->close();
- }
- //Create error message for any bad addresses
- if ( count( $bad_rcpt ) > 0 ) {
- $errstr = '';
- foreach ( $bad_rcpt as $bad ) {
- $errstr .= $bad['to'] . ': ' . $bad['error'];
- }
- throw new phpmailerException(
- $this->lang( 'recipients_failed' ) . $errstr,
- self::STOP_CONTINUE
- );
- }
-
- return true;
- }
-
- /**
- * Initiate a connection to an SMTP server.
- * Returns false if the operation failed.
- *
- * @param array $options An array of options compatible with stream_context_create()
- *
- * @return bool
- * @throws null
- * @throws phpmailerException
- * @uses SMTP
- * @access public
- */
- public function smtpConnect( $options = array() ) {
- if ( is_null( $this->smtp ) ) {
- $this->smtp = $this->getSMTPInstance();
- }
-
- // Already connected?
- if ( $this->smtp->connected() ) {
- return true;
- }
-
- $this->smtp->setTimeout( $this->Timeout );
- $this->smtp->setDebugLevel( $this->SMTPDebug );
- $this->smtp->setDebugOutput( $this->Debugoutput );
- $this->smtp->setVerp( $this->do_verp );
- $hosts = explode( ';', $this->Host );
- $lastexception = null;
-
- foreach ( $hosts as $hostentry ) {
- $hostinfo = array();
- if ( ! preg_match( '/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim( $hostentry ), $hostinfo ) ) {
- // Not a valid host entry
- continue;
- }
- // $hostinfo[2]: optional ssl or tls prefix
- // $hostinfo[3]: the hostname
- // $hostinfo[4]: optional port number
- // The host string prefix can temporarily override the current setting for SMTPSecure
- // If it's not specified, the default value is used
- $prefix = '';
- $secure = $this->SMTPSecure;
- $tls = ( $this->SMTPSecure == 'tls' );
- if ( 'ssl' == $hostinfo[2] or ( '' == $hostinfo[2] and 'ssl' == $this->SMTPSecure ) ) {
- $prefix = 'ssl://';
- $tls = false; // Can't have SSL and TLS at the same time
- $secure = 'ssl';
- } elseif ( $hostinfo[2] == 'tls' ) {
- $tls = true;
- // tls doesn't use a prefix
- $secure = 'tls';
- }
- //Do we need the OpenSSL extension?
- $sslext = defined( 'OPENSSL_ALGO_SHA1' );
- if ( 'tls' === $secure or 'ssl' === $secure ) {
- //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
- if ( ! $sslext ) {
- throw new phpmailerException( $this->lang( 'extension_missing' ) . 'openssl', self::STOP_CRITICAL );
- }
- }
- $host = $hostinfo[3];
- $port = $this->Port;
- $tport = (integer) $hostinfo[4];
- if ( $tport > 0 and $tport < 65536 ) {
- $port = $tport;
- }
- if ( $this->smtp->connect( $prefix . $host, $port, $this->Timeout, $options ) ) {
- try {
- if ( $this->Helo ) {
- $hello = $this->Helo;
- } else {
- $hello = $this->serverHostname();
- }
- $this->smtp->hello( $hello );
- //Automatically enable TLS encryption if:
- // * it's not disabled
- // * we have openssl extension
- // * we are not already using SSL
- // * the server offers STARTTLS
- if ( $this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt( 'STARTTLS' ) ) {
- $tls = true;
- }
- if ( $tls ) {
- if ( ! $this->smtp->startTLS() ) {
- throw new phpmailerException( $this->lang( 'connect_host' ) );
- }
- // We must resend HELO after tls negotiation
- $this->smtp->hello( $hello );
- }
- if ( $this->SMTPAuth ) {
- if ( ! $this->smtp->authenticate(
- $this->Username,
- $this->Password,
- $this->AuthType,
- $this->Realm,
- $this->Workstation
- )
- ) {
- throw new phpmailerException( $this->lang( 'authenticate' ) );
- }
- }
-
- return true;
- } catch ( phpmailerException $exc ) {
- $lastexception = $exc;
- $this->edebug( $exc->getMessage() );
- // We must have connected, but then failed TLS or Auth, so close connection nicely
- $this->smtp->quit();
- }
- }
- }
- // If we get here, all connection attempts have failed, so close connection hard
- $this->smtp->close();
- // As we've caught all exceptions, just report whatever the last one was
- if ( $this->exceptions and ! is_null( $lastexception ) ) {
- throw $lastexception;
- }
-
- return false;
- }
-
- /**
- * Get an instance to use for SMTP operations.
- * Override this function to load your own SMTP implementation
- * @return SMTP
- */
- public function getSMTPInstance() {
- if ( ! is_object( $this->smtp ) ) {
- $this->smtp = new SMTP;
- }
-
- return $this->smtp;
- }
-
- /**
- * Send mail using the PHP mail() function.
- *
- * @param string $header The message headers
- * @param string $body The message body
- *
- * @link http://www.php.net/manual/en/book.mail.php
- * @throws phpmailerException
- * @access protected
- * @return boolean
- */
- protected function mailSend( $header, $body ) {
- $toArr = array();
- foreach ( $this->to as $toaddr ) {
- $toArr[] = $this->addrFormat( $toaddr );
- }
- $to = implode( ', ', $toArr );
-
- if ( empty( $this->Sender ) ) {
- $params = ' ';
- } else {
- $params = sprintf( '-f%s', $this->Sender );
- }
- if ( $this->Sender != '' and ! ini_get( 'safe_mode' ) ) {
- $old_from = ini_get( 'sendmail_from' );
- ini_set( 'sendmail_from', $this->Sender );
- }
- $result = false;
- if ( $this->SingleTo && count( $toArr ) > 1 ) {
- foreach ( $toArr as $toAddr ) {
- $result = $this->mailPassthru( $toAddr, $this->Subject, $body, $header, $params );
- $this->doCallback( $result, array( $toAddr ), $this->cc, $this->bcc, $this->Subject, $body, $this->From );
- }
- } else {
- $result = $this->mailPassthru( $to, $this->Subject, $body, $header, $params );
- $this->doCallback( $result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From );
- }
- if ( isset( $old_from ) ) {
- ini_set( 'sendmail_from', $old_from );
- }
- if ( ! $result ) {
- throw new phpmailerException( $this->lang( 'instantiate' ), self::STOP_CRITICAL );
- }
-
- return true;
- }
-
- /**
- * Call mail() in a safe_mode-aware fashion.
- * Also, unless sendmail_path points to sendmail (or something that
- * claims to be sendmail), don't pass params (not a perfect fix,
- * but it will do)
- *
- * @param string $to To
- * @param string $subject Subject
- * @param string $body Message Body
- * @param string $header Additional Header(s)
- * @param string $params Params
- *
- * @access private
- * @return boolean
- */
- private function mailPassthru( $to, $subject, $body, $header, $params ) {
- //Check overloading of mail function to avoid double-encoding
- if ( ini_get( 'mbstring.func_overload' ) & 1 ) {
- $subject = $this->secureHeader( $subject );
- } else {
- $subject = $this->encodeHeader( $this->secureHeader( $subject ) );
- }
- if ( ini_get( 'safe_mode' ) || ! ( $this->UseSendmailOptions ) ) {
- $result = @mail( $to, $subject, $body, $header );
- } else {
- $result = @mail( $to, $subject, $body, $header, $params );
- }
-
- return $result;
- }
-
- /**
- * Strip newlines to prevent header injection.
- * @access public
- *
- * @param string $str
- *
- * @return string
- */
- public function secureHeader( $str ) {
- return trim( str_replace( array( "\r", "\n" ), '', $str ) );
- }
-
- /**
- * Encode a header string optimally.
- * Picks shortest of Q, B, quoted-printable or none.
- * @access public
- *
- * @param string $str
- * @param string $position
- *
- * @return string
- */
- public function encodeHeader( $str, $position = 'text' ) {
- $matchcount = 0;
- switch ( strtolower( $position ) ) {
- case 'phrase':
- if ( ! preg_match( '/[\200-\377]/', $str ) ) {
- // Can't use addslashes as we don't know the value of magic_quotes_sybase
- $encoded = addcslashes( $str, "\0..\37\177\\\"" );
- if ( ( $str == $encoded ) && ! preg_match( '/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str ) ) {
- return ( $encoded );
- } else {
- return ( "\"$encoded\"" );
- }
- }
- $matchcount = preg_match_all( '/[^\040\041\043-\133\135-\176]/', $str, $matches );
- break;
- /** @noinspection PhpMissingBreakStatementInspection */
- case 'comment':
- $matchcount = preg_match_all( '/[()"]/', $str, $matches );
- // Intentional fall-through
- case 'text':
- default:
- $matchcount += preg_match_all( '/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches );
- break;
- }
-
- //There are no chars that need encoding
- if ( $matchcount == 0 ) {
- return ( $str );
- }
-
- $maxlen = 75 - 7 - strlen( $this->CharSet );
- // Try to select the encoding which should produce the shortest output
- if ( $matchcount > strlen( $str ) / 3 ) {
- // More than a third of the content will need encoding, so B encoding will be most efficient
- $encoding = 'B';
- if ( function_exists( 'mb_strlen' ) && $this->hasMultiBytes( $str ) ) {
- // Use a custom function which correctly encodes and wraps long
- // multibyte strings without breaking lines within a character
- $encoded = $this->base64EncodeWrapMB( $str, "\n" );
- } else {
- $encoded = base64_encode( $str );
- $maxlen -= $maxlen % 4;
- $encoded = trim( chunk_split( $encoded, $maxlen, "\n" ) );
- }
- } else {
- $encoding = 'Q';
- $encoded = $this->encodeQ( $str, $position );
- $encoded = $this->wrapText( $encoded, $maxlen, true );
- $encoded = str_replace( '=' . self::CRLF, "\n", trim( $encoded ) );
- }
-
- $encoded = preg_replace( '/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded );
- $encoded = trim( str_replace( "\n", $this->LE, $encoded ) );
-
- return $encoded;
- }
-
- /**
- * Check if a string contains multi-byte characters.
- * @access public
- *
- * @param string $str multi-byte text to wrap encode
- *
- * @return boolean
- */
- public function hasMultiBytes( $str ) {
- if ( function_exists( 'mb_strlen' ) ) {
- return ( strlen( $str ) > mb_strlen( $str, $this->CharSet ) );
- } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
- return false;
- }
- }
-
- /**
- * Encode and wrap long multibyte strings for mail headers
- * without breaking lines within a character.
- * Adapted from a function by paravoid
- * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
- * @access public
- *
- * @param string $str multi-byte text to wrap encode
- * @param string $linebreak string to use as linefeed/end-of-line
- *
- * @return string
- */
- public function base64EncodeWrapMB( $str, $linebreak = null ) {
- $start = '=?' . $this->CharSet . '?B?';
- $end = '?=';
- $encoded = '';
- if ( $linebreak === null ) {
- $linebreak = $this->LE;
- }
-
- $mb_length = mb_strlen( $str, $this->CharSet );
- // Each line must have length <= 75, including $start and $end
- $length = 75 - strlen( $start ) - strlen( $end );
- // Average multi-byte ratio
- $ratio = $mb_length / strlen( $str );
- // Base64 has a 4:3 ratio
- $avgLength = floor( $length * $ratio * .75 );
-
- for ( $i = 0; $i < $mb_length; $i += $offset ) {
- $lookBack = 0;
- do {
- $offset = $avgLength - $lookBack;
- $chunk = mb_substr( $str, $i, $offset, $this->CharSet );
- $chunk = base64_encode( $chunk );
- $lookBack ++;
- } while ( strlen( $chunk ) > $length );
- $encoded .= $chunk . $linebreak;
- }
-
- // Chomp the last linefeed
- $encoded = substr( $encoded, 0, - strlen( $linebreak ) );
-
- return $encoded;
- }
-
- /**
- * Encode a string using Q encoding.
- * @link http://tools.ietf.org/html/rfc2047
- *
- * @param string $str the text to encode
- * @param string $position Where the text is going to be used, see the RFC for what that means
- *
- * @access public
- * @return string
- */
- public function encodeQ( $str, $position = 'text' ) {
- // There should not be any EOL in the string
- $pattern = '';
- $encoded = str_replace( array( "\r", "\n" ), '', $str );
- switch ( strtolower( $position ) ) {
- case 'phrase':
- // RFC 2047 section 5.3
- $pattern = '^A-Za-z0-9!*+\/ -';
- break;
- /** @noinspection PhpMissingBreakStatementInspection */
- case 'comment':
- // RFC 2047 section 5.2
- $pattern = '\(\)"';
- // intentional fall-through
- // for this reason we build the $pattern without including delimiters and []
- case 'text':
- default:
- // RFC 2047 section 5.1
- // Replace every high ascii, control, =, ? and _ characters
- $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
- break;
- }
- $matches = array();
- if ( preg_match_all( "/[{$pattern}]/", $encoded, $matches ) ) {
- // If the string contains an '=', make sure it's the first thing we replace
- // so as to avoid double-encoding
- $eqkey = array_search( '=', $matches[0] );
- if ( false !== $eqkey ) {
- unset( $matches[0][ $eqkey ] );
- array_unshift( $matches[0], '=' );
- }
- foreach ( array_unique( $matches[0] ) as $char ) {
- $encoded = str_replace( $char, '=' . sprintf( '%02X', ord( $char ) ), $encoded );
- }
- }
-
- // Replace every spaces to _ (more readable than =20)
- return str_replace( ' ', '_', $encoded );
- }
-
- /**
- * Word-wrap message.
- * For use with mailers that do not automatically perform wrapping
- * and for quoted-printable encoded messages.
- * Original written by philippe.
- *
- * @param string $message The message to wrap
- * @param integer $length The line length to wrap to
- * @param boolean $qp_mode Whether to run in Quoted-Printable mode
- *
- * @access public
- * @return string
- */
- public function wrapText( $message, $length, $qp_mode = false ) {
- if ( $qp_mode ) {
- $soft_break = sprintf( ' =%s', $this->LE );
- } else {
- $soft_break = $this->LE;
- }
- // If utf-8 encoding is used, we will need to make sure we don't
- // split multibyte characters when we wrap
- $is_utf8 = ( strtolower( $this->CharSet ) == 'utf-8' );
- $lelen = strlen( $this->LE );
- $crlflen = strlen( self::CRLF );
-
- $message = $this->fixEOL( $message );
- //Remove a trailing line break
- if ( substr( $message, - $lelen ) == $this->LE ) {
- $message = substr( $message, 0, - $lelen );
- }
-
- //Split message into lines
- $lines = explode( $this->LE, $message );
- //Message will be rebuilt in here
- $message = '';
- foreach ( $lines as $line ) {
- $words = explode( ' ', $line );
- $buf = '';
- $firstword = true;
- foreach ( $words as $word ) {
- if ( $qp_mode and ( strlen( $word ) > $length ) ) {
- $space_left = $length - strlen( $buf ) - $crlflen;
- if ( ! $firstword ) {
- if ( $space_left > 20 ) {
- $len = $space_left;
- if ( $is_utf8 ) {
- $len = $this->utf8CharBoundary( $word, $len );
- } elseif ( substr( $word, $len - 1, 1 ) == '=' ) {
- $len --;
- } elseif ( substr( $word, $len - 2, 1 ) == '=' ) {
- $len -= 2;
- }
- $part = substr( $word, 0, $len );
- $word = substr( $word, $len );
- $buf .= ' ' . $part;
- $message .= $buf . sprintf( '=%s', self::CRLF );
- } else {
- $message .= $buf . $soft_break;
- }
- $buf = '';
- }
- while ( strlen( $word ) > 0 ) {
- if ( $length <= 0 ) {
- break;
- }
- $len = $length;
- if ( $is_utf8 ) {
- $len = $this->utf8CharBoundary( $word, $len );
- } elseif ( substr( $word, $len - 1, 1 ) == '=' ) {
- $len --;
- } elseif ( substr( $word, $len - 2, 1 ) == '=' ) {
- $len -= 2;
- }
- $part = substr( $word, 0, $len );
- $word = substr( $word, $len );
-
- if ( strlen( $word ) > 0 ) {
- $message .= $part . sprintf( '=%s', self::CRLF );
- } else {
- $buf = $part;
- }
- }
- } else {
- $buf_o = $buf;
- if ( ! $firstword ) {
- $buf .= ' ';
- }
- $buf .= $word;
-
- if ( strlen( $buf ) > $length and $buf_o != '' ) {
- $message .= $buf_o . $soft_break;
- $buf = $word;
- }
- }
- $firstword = false;
- }
- $message .= $buf . self::CRLF;
- }
-
- return $message;
- }
-
- /**
- * Ensure consistent line endings in a string.
- * Changes every end of line from CRLF, CR or LF to $this->LE.
- * @access public
- *
- * @param string $str String to fixEOL
- *
- * @return string
- */
- public function fixEOL( $str ) {
- // Normalise to \n
- $nstr = str_replace( array( "\r\n", "\r" ), "\n", $str );
- // Now convert LE as needed
- if ( $this->LE !== "\n" ) {
- $nstr = str_replace( "\n", $this->LE, $nstr );
- }
-
- return $nstr;
- }
-
- /**
- * Find the last character boundary prior to $maxLength in a utf-8
- * quoted-printable encoded string.
- * Original written by Colin Brown.
- * @access public
- *
- * @param string $encodedText utf-8 QP text
- * @param integer $maxLength Find the last character boundary prior to this length
- *
- * @return integer
- */
- public function utf8CharBoundary( $encodedText, $maxLength ) {
- $foundSplitPos = false;
- $lookBack = 3;
- while ( ! $foundSplitPos ) {
- $lastChunk = substr( $encodedText, $maxLength - $lookBack, $lookBack );
- $encodedCharPos = strpos( $lastChunk, '=' );
- if ( false !== $encodedCharPos ) {
- // Found start of encoded character byte within $lookBack block.
- // Check the encoded byte value (the 2 chars after the '=')
- $hex = substr( $encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2 );
- $dec = hexdec( $hex );
- if ( $dec < 128 ) {
- // Single byte character.
- // If the encoded char was found at pos 0, it will fit
- // otherwise reduce maxLength to start of the encoded char
- if ( $encodedCharPos > 0 ) {
- $maxLength = $maxLength - ( $lookBack - $encodedCharPos );
- }
- $foundSplitPos = true;
- } elseif ( $dec >= 192 ) {
- // First byte of a multi byte character
- // Reduce maxLength to split at start of character
- $maxLength = $maxLength - ( $lookBack - $encodedCharPos );
- $foundSplitPos = true;
- } elseif ( $dec < 192 ) {
- // Middle byte of a multi byte character, look further back
- $lookBack += 3;
- }
- } else {
- // No encoded character found
- $foundSplitPos = true;
- }
- }
-
- return $maxLength;
- }
-
- /**
- * Get the array of strings for the current language.
- * @return array
- */
- public function getTranslations() {
- return $this->language;
- }
-
- /**
- * Returns the whole MIME message.
- * Includes complete headers and body.
- * Only valid post preSend().
- * @see PHPMailer::preSend()
- * @access public
- * @return string
- */
- public function getSentMIMEMessage() {
- return rtrim( $this->MIMEHeader . $this->mailHeader, "\n\r" ) . self::CRLF . self::CRLF . $this->MIMEBody;
- }
-
- /**
- * Add an attachment from a path on the filesystem.
- * Returns false if the file could not be found or read.
- *
- * @param string $path Path to the attachment.
- * @param string $name Overrides the attachment name.
- * @param string $encoding File encoding (see $Encoding).
- * @param string $type File extension (MIME) type.
- * @param string $disposition Disposition to use
- *
- * @throws phpmailerException
- * @return boolean
- */
- public function addAttachment( $path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment' ) {
- try {
- if ( ! @is_file( $path ) ) {
- throw new phpmailerException( $this->lang( 'file_access' ) . $path, self::STOP_CONTINUE );
- }
-
- // If a MIME type is not specified, try to work it out from the file name
- if ( $type == '' ) {
- $type = self::filenameToType( $path );
- }
-
- $filename = basename( $path );
- if ( $name == '' ) {
- $name = $filename;
- }
-
- $this->attachment[] = array(
- 0 => $path,
- 1 => $filename,
- 2 => $name,
- 3 => $encoding,
- 4 => $type,
- 5 => false, // isStringAttachment
- 6 => $disposition,
- 7 => 0
- );
-
- } catch ( phpmailerException $exc ) {
- $this->setError( $exc->getMessage() );
- $this->edebug( $exc->getMessage() );
- if ( $this->exceptions ) {
- throw $exc;
- }
-
- return false;
- }
-
- return true;
- }
-
- /**
- * Map a file name to a MIME type.
- * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
- *
- * @param string $filename A file name or full path, does not need to exist as a file
- *
- * @return string
- * @static
- */
- public static function filenameToType( $filename ) {
- // In case the path is a URL, strip any query string before getting extension
- $qpos = strpos( $filename, '?' );
- if ( false !== $qpos ) {
- $filename = substr( $filename, 0, $qpos );
- }
- $pathinfo = self::mb_pathinfo( $filename );
-
- return self::_mime_types( $pathinfo['extension'] );
- }
-
- /**
- * Multi-byte-safe pathinfo replacement.
- * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
- * Works similarly to the one in PHP >= 5.2.0
- * @link http://www.php.net/manual/en/function.pathinfo.php#107461
- *
- * @param string $path A filename or path, does not need to exist as a file
- * @param integer|string $options Either a PATHINFO_* constant,
- * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
- *
- * @return string|array
- * @static
- */
- public static function mb_pathinfo( $path, $options = null ) {
- $ret = array( 'dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '' );
- $pathinfo = array();
- if ( preg_match( '%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo ) ) {
- if ( array_key_exists( 1, $pathinfo ) ) {
- $ret['dirname'] = $pathinfo[1];
- }
- if ( array_key_exists( 2, $pathinfo ) ) {
- $ret['basename'] = $pathinfo[2];
- }
- if ( array_key_exists( 5, $pathinfo ) ) {
- $ret['extension'] = $pathinfo[5];
- }
- if ( array_key_exists( 3, $pathinfo ) ) {
- $ret['filename'] = $pathinfo[3];
- }
- }
- switch ( $options ) {
- case PATHINFO_DIRNAME:
- case 'dirname':
- return $ret['dirname'];
- case PATHINFO_BASENAME:
- case 'basename':
- return $ret['basename'];
- case PATHINFO_EXTENSION:
- case 'extension':
- return $ret['extension'];
- case PATHINFO_FILENAME:
- case 'filename':
- return $ret['filename'];
- default:
- return $ret;
- }
- }
-
- /**
- * Get the MIME type for a file extension.
- *
- * @param string $ext File extension
- *
- * @access public
- * @return string MIME type of file.
- * @static
- */
- public static function _mime_types( $ext = '' ) {
- $mimes = array(
- 'xl' => 'application/excel',
- 'js' => 'application/javascript',
- 'hqx' => 'application/mac-binhex40',
- 'cpt' => 'application/mac-compactpro',
- 'bin' => 'application/macbinary',
- 'doc' => 'application/msword',
- 'word' => 'application/msword',
- 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
- 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
- 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
- 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
- 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
- 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
- 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
- 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
- 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
- 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
- 'class' => 'application/octet-stream',
- 'dll' => 'application/octet-stream',
- 'dms' => 'application/octet-stream',
- 'exe' => 'application/octet-stream',
- 'lha' => 'application/octet-stream',
- 'lzh' => 'application/octet-stream',
- 'psd' => 'application/octet-stream',
- 'sea' => 'application/octet-stream',
- 'so' => 'application/octet-stream',
- 'oda' => 'application/oda',
- 'pdf' => 'application/pdf',
- 'ai' => 'application/postscript',
- 'eps' => 'application/postscript',
- 'ps' => 'application/postscript',
- 'smi' => 'application/smil',
- 'smil' => 'application/smil',
- 'mif' => 'application/vnd.mif',
- 'xls' => 'application/vnd.ms-excel',
- 'ppt' => 'application/vnd.ms-powerpoint',
- 'wbxml' => 'application/vnd.wap.wbxml',
- 'wmlc' => 'application/vnd.wap.wmlc',
- 'dcr' => 'application/x-director',
- 'dir' => 'application/x-director',
- 'dxr' => 'application/x-director',
- 'dvi' => 'application/x-dvi',
- 'gtar' => 'application/x-gtar',
- 'php3' => 'application/x-httpd-php',
- 'php4' => 'application/x-httpd-php',
- 'php' => 'application/x-httpd-php',
- 'phtml' => 'application/x-httpd-php',
- 'phps' => 'application/x-httpd-php-source',
- 'swf' => 'application/x-shockwave-flash',
- 'sit' => 'application/x-stuffit',
- 'tar' => 'application/x-tar',
- 'tgz' => 'application/x-tar',
- 'xht' => 'application/xhtml+xml',
- 'xhtml' => 'application/xhtml+xml',
- 'zip' => 'application/zip',
- 'mid' => 'audio/midi',
- 'midi' => 'audio/midi',
- 'mp2' => 'audio/mpeg',
- 'mp3' => 'audio/mpeg',
- 'mpga' => 'audio/mpeg',
- 'aif' => 'audio/x-aiff',
- 'aifc' => 'audio/x-aiff',
- 'aiff' => 'audio/x-aiff',
- 'ram' => 'audio/x-pn-realaudio',
- 'rm' => 'audio/x-pn-realaudio',
- 'rpm' => 'audio/x-pn-realaudio-plugin',
- 'ra' => 'audio/x-realaudio',
- 'wav' => 'audio/x-wav',
- 'bmp' => 'image/bmp',
- 'gif' => 'image/gif',
- 'jpeg' => 'image/jpeg',
- 'jpe' => 'image/jpeg',
- 'jpg' => 'image/jpeg',
- 'png' => 'image/png',
- 'tiff' => 'image/tiff',
- 'tif' => 'image/tiff',
- 'eml' => 'message/rfc822',
- 'css' => 'text/css',
- 'html' => 'text/html',
- 'htm' => 'text/html',
- 'shtml' => 'text/html',
- 'log' => 'text/plain',
- 'text' => 'text/plain',
- 'txt' => 'text/plain',
- 'rtx' => 'text/richtext',
- 'rtf' => 'text/rtf',
- 'vcf' => 'text/vcard',
- 'vcard' => 'text/vcard',
- 'xml' => 'text/xml',
- 'xsl' => 'text/xml',
- 'mpeg' => 'video/mpeg',
- 'mpe' => 'video/mpeg',
- 'mpg' => 'video/mpeg',
- 'mov' => 'video/quicktime',
- 'qt' => 'video/quicktime',
- 'rv' => 'video/vnd.rn-realvideo',
- 'avi' => 'video/x-msvideo',
- 'movie' => 'video/x-sgi-movie'
- );
- if ( array_key_exists( strtolower( $ext ), $mimes ) ) {
- return $mimes[ strtolower( $ext ) ];
- }
-
- return 'application/octet-stream';
- }
-
- /**
- * Return the array of attachments.
- * @return array
- */
- public function getAttachments() {
- return $this->attachment;
- }
-
- /**
- * Backward compatibility wrapper for an old QP encoding function that was removed.
- * @see PHPMailer::encodeQP()
- * @access public
- *
- * @param string $string
- * @param integer $line_max
- * @param boolean $space_conv
- *
- * @return string
- * @deprecated Use encodeQP instead.
- */
- public function encodeQPphp(
- $string,
- $line_max = 76,
- /** @noinspection PhpUnusedParameterInspection */
- $space_conv = false
- ) {
- return $this->encodeQP( $string, $line_max );
- }
-
- /**
- * Add a string or binary attachment (non-filesystem).
- * This method can be used to attach ascii or binary data,
- * such as a BLOB record from a database.
- *
- * @param string $string String attachment data.
- * @param string $filename Name of the attachment.
- * @param string $encoding File encoding (see $Encoding).
- * @param string $type File extension (MIME) type.
- * @param string $disposition Disposition to use
- *
- * @return void
- */
- public function addStringAttachment(
- $string,
- $filename,
- $encoding = 'base64',
- $type = '',
- $disposition = 'attachment'
- ) {
- // If a MIME type is not specified, try to work it out from the file name
- if ( $type == '' ) {
- $type = self::filenameToType( $filename );
- }
- // Append to $attachment array
- $this->attachment[] = array(
- 0 => $string,
- 1 => $filename,
- 2 => basename( $filename ),
- 3 => $encoding,
- 4 => $type,
- 5 => true, // isStringAttachment
- 6 => $disposition,
- 7 => 0
- );
- }
-
- /**
- * Clear all To recipients.
- * @return void
- */
- public function clearAddresses() {
- foreach ( $this->to as $to ) {
- unset( $this->all_recipients[ strtolower( $to[0] ) ] );
- }
- $this->to = array();
- $this->clearQueuedAddresses( 'to' );
- }
-
- /**
- * Clear queued addresses of given kind.
- * @access protected
- *
- * @param string $kind 'to', 'cc', or 'bcc'
- *
- * @return void
- */
- public function clearQueuedAddresses( $kind ) {
- $RecipientsQueue = $this->RecipientsQueue;
- foreach ( $RecipientsQueue as $address => $params ) {
- if ( $params[0] == $kind ) {
- unset( $this->RecipientsQueue[ $address ] );
- }
- }
- }
-
- /**
- * Clear all CC recipients.
- * @return void
- */
- public function clearCCs() {
- foreach ( $this->cc as $cc ) {
- unset( $this->all_recipients[ strtolower( $cc[0] ) ] );
- }
- $this->cc = array();
- $this->clearQueuedAddresses( 'cc' );
- }
-
- /**
- * Clear all BCC recipients.
- * @return void
- */
- public function clearBCCs() {
- foreach ( $this->bcc as $bcc ) {
- unset( $this->all_recipients[ strtolower( $bcc[0] ) ] );
- }
- $this->bcc = array();
- $this->clearQueuedAddresses( 'bcc' );
- }
-
- /**
- * Clear all ReplyTo recipients.
- * @return void
- */
- public function clearReplyTos() {
- $this->ReplyTo = array();
- $this->ReplyToQueue = array();
- }
-
- /**
- * Clear all recipient types.
- * @return void
- */
- public function clearAllRecipients() {
- $this->to = array();
- $this->cc = array();
- $this->bcc = array();
- $this->all_recipients = array();
- $this->RecipientsQueue = array();
- }
-
- /**
- * Clear all filesystem, string, and binary attachments.
- * @return void
- */
- public function clearAttachments() {
- $this->attachment = array();
- }
-
- /**
- * Clear all custom headers.
- * @return void
- */
- public function clearCustomHeaders() {
- $this->CustomHeader = array();
- }
-
- /**
- * Add a custom header.
- * $name value can be overloaded to contain
- * both header name and value (name:value)
- * @access public
- *
- * @param string $name Custom header name
- * @param string $value Header value
- *
- * @return void
- */
- public function addCustomHeader( $name, $value = null ) {
- if ( $value === null ) {
- // Value passed in as name:value
- $this->CustomHeader[] = explode( ':', $name, 2 );
- } else {
- $this->CustomHeader[] = array( $name, $value );
- }
- }
-
- /**
- * Returns all custom headers.
- * @return array
- */
- public function getCustomHeaders() {
- return $this->CustomHeader;
- }
-
- /**
- * Create a message from an HTML string.
- * Automatically makes modifications for inline images and backgrounds
- * and creates a plain-text version by converting the HTML.
- * Overwrites any existing values in $this->Body and $this->AltBody
- * @access public
- *
- * @param string $message HTML message string
- * @param string $basedir baseline directory for path
- * @param boolean|callable $advanced Whether to use the internal HTML to text converter
- * or your own custom converter @see PHPMailer::html2text()
- *
- * @return string $message
- */
- public function msgHTML( $message, $basedir = '', $advanced = false ) {
- preg_match_all( '/(src|background)=["\'](.*)["\']/Ui', $message, $images );
- if ( array_key_exists( 2, $images ) ) {
- foreach ( $images[2] as $imgindex => $url ) {
- // Convert data URIs into embedded images
- if ( preg_match( '#^data:(image[^;,]*)(;base64)?,#', $url, $match ) ) {
- $data = substr( $url, strpos( $url, ',' ) );
- if ( $match[2] ) {
- $data = base64_decode( $data );
- } else {
- $data = rawurldecode( $data );
- }
- $cid = md5( $url ) . '@phpmailer.0'; // RFC2392 S 2
- if ( $this->addStringEmbeddedImage( $data, $cid, 'embed' . $imgindex, 'base64', $match[1] ) ) {
- $message = str_replace(
- $images[0][ $imgindex ],
- $images[1][ $imgindex ] . '="cid:' . $cid . '"',
- $message
- );
- }
- } elseif ( substr( $url, 0, 4 ) !== 'cid:' && ! preg_match( '#^[a-z][a-z0-9+.-]*://#i', $url ) ) {
- // Do not change urls for absolute images (thanks to corvuscorax)
- // Do not change urls that are already inline images
- $filename = basename( $url );
- $directory = dirname( $url );
- if ( $directory == '.' ) {
- $directory = '';
- }
- $cid = md5( $url ) . '@phpmailer.0'; // RFC2392 S 2
- if ( strlen( $basedir ) > 1 && substr( $basedir, - 1 ) != '/' ) {
- $basedir .= '/';
- }
- if ( strlen( $directory ) > 1 && substr( $directory, - 1 ) != '/' ) {
- $directory .= '/';
- }
- if ( $this->addEmbeddedImage(
- $basedir . $directory . $filename,
- $cid,
- $filename,
- 'base64',
- self::_mime_types( (string) self::mb_pathinfo( $filename, PATHINFO_EXTENSION ) )
- )
- ) {
- $message = preg_replace(
- '/' . $images[1][ $imgindex ] . '=["\']' . preg_quote( $url, '/' ) . '["\']/Ui',
- $images[1][ $imgindex ] . '="cid:' . $cid . '"',
- $message
- );
- }
- }
- }
- }
- $this->isHTML( true );
- // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
- $this->Body = $this->normalizeBreaks( $message );
- $this->AltBody = $this->normalizeBreaks( $this->html2text( $message, $advanced ) );
- if ( ! $this->alternativeExists() ) {
- $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
- self::CRLF . self::CRLF;
- }
-
- return $this->Body;
- }
-
- /**
- * Add an embedded stringified attachment.
- * This can include images, sounds, and just about any other document type.
- * Be sure to set the $type to an image type for images:
- * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
- *
- * @param string $string The attachment binary data.
- * @param string $cid Content ID of the attachment; Use this to reference
- * the content when using an embedded image in HTML.
- * @param string $name
- * @param string $encoding File encoding (see $Encoding).
- * @param string $type MIME type.
- * @param string $disposition Disposition to use
- *
- * @return boolean True on successfully adding an attachment
- */
- public function addStringEmbeddedImage(
- $string,
- $cid,
- $name = '',
- $encoding = 'base64',
- $type = '',
- $disposition = 'inline'
- ) {
- // If a MIME type is not specified, try to work it out from the name
- if ( $type == '' and ! empty( $name ) ) {
- $type = self::filenameToType( $name );
- }
-
- // Append to $attachment array
- $this->attachment[] = array(
- 0 => $string,
- 1 => $name,
- 2 => $name,
- 3 => $encoding,
- 4 => $type,
- 5 => true, // isStringAttachment
- 6 => $disposition,
- 7 => $cid
- );
-
- return true;
- }
-
- /**
- * Add an embedded (inline) attachment from a file.
- * This can include images, sounds, and just about any other document type.
- * These differ from 'regular' attachments in that they are intended to be
- * displayed inline with the message, not just attached for download.
- * This is used in HTML messages that embed the images
- * the HTML refers to using the $cid value.
- *
- * @param string $path Path to the attachment.
- * @param string $cid Content ID of the attachment; Use this to reference
- * the content when using an embedded image in HTML.
- * @param string $name Overrides the attachment name.
- * @param string $encoding File encoding (see $Encoding).
- * @param string $type File MIME type.
- * @param string $disposition Disposition to use
- *
- * @return boolean True on successfully adding an attachment
- */
- public function addEmbeddedImage( $path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline' ) {
- if ( ! @is_file( $path ) ) {
- $this->setError( $this->lang( 'file_access' ) . $path );
-
- return false;
- }
-
- // If a MIME type is not specified, try to work it out from the file name
- if ( $type == '' ) {
- $type = self::filenameToType( $path );
- }
-
- $filename = basename( $path );
- if ( $name == '' ) {
- $name = $filename;
- }
-
- // Append to $attachment array
- $this->attachment[] = array(
- 0 => $path,
- 1 => $filename,
- 2 => $name,
- 3 => $encoding,
- 4 => $type,
- 5 => false, // isStringAttachment
- 6 => $disposition,
- 7 => $cid
- );
-
- return true;
- }
-
- /**
- * Sets message type to HTML or plain.
- *
- * @param boolean $isHtml True for HTML mode.
- *
- * @return void
- */
- public function isHTML( $isHtml = true ) {
- if ( $isHtml ) {
- $this->ContentType = 'text/html';
- } else {
- $this->ContentType = 'text/plain';
- }
- }
-
- /**
- * Normalize line breaks in a string.
- * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
- * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
- *
- * @param string $text
- * @param string $breaktype What kind of line break to use, defaults to CRLF
- *
- * @return string
- * @access public
- * @static
- */
- public static function normalizeBreaks( $text, $breaktype = "\r\n" ) {
- return preg_replace( '/(\r\n|\r|\n)/ms', $breaktype, $text );
- }
-
- /**
- * Convert an HTML string into plain text.
- * This is used by msgHTML().
- * Note - older versions of this function used a bundled advanced converter
- * which was been removed for license reasons in #232
- * Example usage:
- *
- * // Use default conversion
- * $plain = $mail->html2text($html);
- * // Use your own custom converter
- * $plain = $mail->html2text($html, function($html) {
- * $converter = new MyHtml2text($html);
- * return $converter->get_text();
- * });
- *
- *
- * @param string $html The HTML text to convert
- * @param boolean|callable $advanced Any boolean value to use the internal converter,
- * or provide your own callable for custom conversion.
- *
- * @return string
- */
- public function html2text( $html, $advanced = false ) {
- if ( is_callable( $advanced ) ) {
- return call_user_func( $advanced, $html );
- }
-
- return html_entity_decode(
- trim( strip_tags( preg_replace( '/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html ) ) ),
- ENT_QUOTES,
- $this->CharSet
- );
- }
-
- /**
- * Set or reset instance properties.
- * You should avoid this function - it's more verbose, less efficient, more error-prone and
- * harder to debug than setting properties directly.
- * Usage Example:
- * `$mail->set('SMTPSecure', 'tls');`
- * is the same as:
- * `$mail->SMTPSecure = 'tls';`
- * @access public
- *
- * @param string $name The property name to set
- * @param mixed $value The value to set the property to
- *
- * @return boolean
- */
- public function set( $name, $value = '' ) {
- if ( property_exists( $this, $name ) ) {
- $this->$name = $value;
-
- return true;
- } else {
- $this->setError( $this->lang( 'variable_set' ) . $name );
-
- return false;
- }
- }
-
- /**
- * Set the public and private key files and password for S/MIME signing.
- * @access public
- *
- * @param string $cert_filename
- * @param string $key_filename
- * @param string $key_pass Password for private key
- * @param string $extracerts_filename Optional path to chain certificate
- */
- public function sign( $cert_filename, $key_filename, $key_pass, $extracerts_filename = '' ) {
- $this->sign_cert_file = $cert_filename;
- $this->sign_key_file = $key_filename;
- $this->sign_key_pass = $key_pass;
- $this->sign_extracerts_file = $extracerts_filename;
- }
-
- /**
- * Allows for public read access to 'to' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getToAddresses() {
- return $this->to;
- }
-
- /**
- * Allows for public read access to 'cc' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getCcAddresses() {
- return $this->cc;
- }
-
- /**
- * Allows for public read access to 'bcc' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getBccAddresses() {
- return $this->bcc;
- }
-
- /**
- * Allows for public read access to 'ReplyTo' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getReplyToAddresses() {
- return $this->ReplyTo;
- }
-
- /**
- * Allows for public read access to 'all_recipients' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getAllRecipientAddresses() {
- return $this->all_recipients;
- }
-
- /**
- * Add an address to one of the recipient arrays or to the ReplyTo array.
- * Addresses that have been added already return false, but do not throw exceptions.
- *
- * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
- * @param string $address The email address to send, resp. to reply to
- * @param string $name
- *
- * @throws phpmailerException
- * @return boolean true on success, false if address already used or invalid in some way
- * @access protected
- */
- protected function addAnAddress( $kind, $address, $name = '' ) {
- if ( ! in_array( $kind, array( 'to', 'cc', 'bcc', 'Reply-To' ) ) ) {
- $error_message = $this->lang( 'Invalid recipient kind: ' ) . $kind;
- $this->setError( $error_message );
- $this->edebug( $error_message );
- if ( $this->exceptions ) {
- throw new phpmailerException( $error_message );
- }
-
- return false;
- }
- if ( ! $this->validateAddress( $address ) ) {
- $error_message = $this->lang( 'invalid_address' ) . " (addAnAddress $kind): $address";
- $this->setError( $error_message );
- $this->edebug( $error_message );
- if ( $this->exceptions ) {
- throw new phpmailerException( $error_message );
- }
-
- return false;
- }
- if ( $kind != 'Reply-To' ) {
- if ( ! array_key_exists( strtolower( $address ), $this->all_recipients ) ) {
- array_push( $this->$kind, array( $address, $name ) );
- $this->all_recipients[ strtolower( $address ) ] = true;
-
- return true;
- }
- } else {
- if ( ! array_key_exists( strtolower( $address ), $this->ReplyTo ) ) {
- $this->ReplyTo[ strtolower( $address ) ] = array( $address, $name );
-
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Check that a string looks like an email address.
- *
- * @param string $address The email address to check
- * @param string $patternselect A selector for the validation pattern to use :
- * * `auto` Pick best pattern automatically;
- * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
- * * `pcre` Use old PCRE implementation;
- * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
- * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
- * * `noregex` Don't use a regex: super fast, really dumb.
- *
- * @return boolean
- * @static
- * @access public
- */
- public static function validateAddress( $address, $patternselect = 'auto' ) {
- //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
- if ( strpos( $address, "\n" ) !== false or strpos( $address, "\r" ) !== false ) {
- return false;
- }
- if ( ! $patternselect or $patternselect == 'auto' ) {
- //Check this constant first so it works when extension_loaded() is disabled by safe mode
- //Constant was added in PHP 5.2.4
- if ( defined( 'PCRE_VERSION' ) ) {
- //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
- if ( version_compare( PCRE_VERSION, '8.0.3' ) >= 0 ) {
- $patternselect = 'pcre8';
- } else {
- $patternselect = 'pcre';
- }
- } elseif ( function_exists( 'extension_loaded' ) and extension_loaded( 'pcre' ) ) {
- //Fall back to older PCRE
- $patternselect = 'pcre';
- } else {
- //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
- if ( version_compare( PHP_VERSION, '5.2.0' ) >= 0 ) {
- $patternselect = 'php';
- } else {
- $patternselect = 'noregex';
- }
- }
- }
- switch ( $patternselect ) {
- case 'pcre8':
- /**
- * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
- * @link http://squiloople.com/2009/12/20/email-address-validation/
- * @copyright 2009-2010 Michael Rushton
- * Feel free to use and redistribute this code. But please keep this copyright notice.
- */
- return (boolean) preg_match(
- '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
- '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
- '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
- '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
- '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
- '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
- '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
- '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
- '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
- $address
- );
- case 'pcre':
- //An older regex that doesn't need a recent PCRE
- return (boolean) preg_match(
- '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
- '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
- '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
- '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
- '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
- '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
- '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
- '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
- '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
- '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
- $address
- );
- case 'html5':
- /**
- * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
- * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
- */
- return (boolean) preg_match(
- '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
- '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
- $address
- );
- case 'noregex':
- //No PCRE! Do something _very_ approximate!
- //Check the address is 3 chars or longer and contains an @ that's not the first or last char
- return ( strlen( $address ) >= 3
- and strpos( $address, '@' ) >= 1
- and strpos( $address, '@' ) != strlen( $address ) - 1 );
- case 'php':
- default:
- return (boolean) filter_var( $address, FILTER_VALIDATE_EMAIL );
- }
- }
+ /**
+ * Error severity: message only, continue processing.
+ */
+ const STOP_MESSAGE = 0;
+ /**
+ * Error severity: message, likely ok to continue processing.
+ */
+ const STOP_CONTINUE = 1;
+ /**
+ * Error severity: message, plus full stop, critical error reached.
+ */
+ const STOP_CRITICAL = 2;
+ /**
+ * SMTP RFC standard line ending.
+ */
+ const CRLF = "\r\n";
+ /**
+ * The maximum line length allowed by RFC 2822 section 2.1.1
+ * @var integer
+ */
+ const MAX_LINE_LENGTH = 998;
+ /**
+ * The PHPMailer Version number.
+ * @var string
+ */
+ public $Version = '5.2.14';
+ /**
+ * Email priority.
+ * Options: null (default), 1 = High, 3 = Normal, 5 = low.
+ * When null, the header is not set at all.
+ * @var integer
+ */
+ public $Priority = null;
+ /**
+ * The character set of the message.
+ * @var string
+ */
+ public $CharSet = 'utf-8';
+ /**
+ * The MIME Content-type of the message.
+ * @var string
+ */
+ public $ContentType = 'text/plain';
+ /**
+ * The message encoding.
+ * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
+ * @var string
+ */
+ public $Encoding = '8bit';
+ /**
+ * Holds the most recent mailer error message.
+ * @var string
+ */
+ public $ErrorInfo = '';
+ /**
+ * The From email address for the message.
+ * @var string
+ */
+ public $From = 'root@localhost';
+ /**
+ * The From name of the message.
+ * @var string
+ */
+ public $FromName = 'Root User';
+ /**
+ * The Sender email (Return-Path) of the message.
+ * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
+ * @var string
+ */
+ public $Sender = '';
+ /**
+ * The Return-Path of the message.
+ * If empty, it will be set to either From or Sender.
+ * @var string
+ * @deprecated Email senders should never set a return-path header;
+ * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
+ * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
+ */
+ public $ReturnPath = '';
+ /**
+ * The Subject of the message.
+ * @var string
+ */
+ public $Subject = '';
+ /**
+ * An HTML or plain text message body.
+ * If HTML then call isHTML(true).
+ * @var string
+ */
+ public $Body = '';
+ /**
+ * The plain-text message body.
+ * This body can be read by mail clients that do not have HTML email
+ * capability such as mutt & Eudora.
+ * Clients that can read HTML will view the normal Body.
+ * @var string
+ */
+ public $AltBody = '';
+ /**
+ * An iCal message part body.
+ * Only supported in simple alt or alt_inline message types
+ * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
+ * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
+ * @link http://kigkonsult.se/iCalcreator/
+ * @var string
+ */
+ public $Ical = '';
+ /**
+ * Word-wrap the message body to this number of chars.
+ * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
+ * @var integer
+ */
+ public $WordWrap = 0;
+ /**
+ * Which method to use to send mail.
+ * Options: "mail", "sendmail", or "smtp".
+ * @var string
+ */
+ public $Mailer = 'mail';
+ /**
+ * The path to the sendmail program.
+ * @var string
+ */
+ public $Sendmail = '/usr/sbin/sendmail';
+ /**
+ * Whether mail() uses a fully sendmail-compatible MTA.
+ * One which supports sendmail's "-oi -f" options.
+ * @var boolean
+ */
+ public $UseSendmailOptions = true;
+ /**
+ * Path to PHPMailer plugins.
+ * Useful if the SMTP class is not in the PHP include path.
+ * @var string
+ * @deprecated Should not be needed now there is an autoloader.
+ */
+ public $PluginDir = '';
+ /**
+ * The email address that a reading confirmation should be sent to, also known as read receipt.
+ * @var string
+ */
+ public $ConfirmReadingTo = '';
+ /**
+ * The hostname to use in the Message-ID header and as default HELO string.
+ * If empty, PHPMailer attempts to find one with, in order,
+ * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
+ * 'localhost.localdomain'.
+ * @var string
+ */
+ public $Hostname = '';
+ /**
+ * An ID to be used in the Message-ID header.
+ * If empty, a unique id will be generated.
+ * @var string
+ */
+ public $MessageID = '';
+ /**
+ * The message Date to be used in the Date header.
+ * If empty, the current date will be added.
+ * @var string
+ */
+ public $MessageDate = '';
+ /**
+ * SMTP hosts.
+ * Either a single hostname or multiple semicolon-delimited hostnames.
+ * You can also specify a different port
+ * for each host by using this format: [hostname:port]
+ * (e.g. "smtp1.example.com:25;smtp2.example.com").
+ * You can also specify encryption type, for example:
+ * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
+ * Hosts will be tried in order.
+ * @var string
+ */
+ public $Host = 'localhost';
+ /**
+ * The default SMTP server port.
+ * @var integer
+ */
+ public $Port = 25;
+ /**
+ * The SMTP HELO of the message.
+ * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
+ * one with the same method described above for $Hostname.
+ * @var string
+ * @see PHPMailer::$Hostname
+ */
+ public $Helo = '';
+ /**
+ * What kind of encryption to use on the SMTP connection.
+ * Options: '', 'ssl' or 'tls'
+ * @var string
+ */
+ public $SMTPSecure = '';
+ /**
+ * Whether to enable TLS encryption automatically if a server supports it,
+ * even if `SMTPSecure` is not set to 'tls'.
+ * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
+ * @var boolean
+ */
+ public $SMTPAutoTLS = true;
+ /**
+ * Whether to use SMTP authentication.
+ * Uses the Username and Password properties.
+ * @var boolean
+ * @see PHPMailer::$Username
+ * @see PHPMailer::$Password
+ */
+ public $SMTPAuth = false;
+ /**
+ * Options array passed to stream_context_create when connecting via SMTP.
+ * @var array
+ */
+ public $SMTPOptions = array();
+ /**
+ * SMTP username.
+ * @var string
+ */
+ public $Username = '';
+ /**
+ * SMTP password.
+ * @var string
+ */
+ public $Password = '';
+ /**
+ * SMTP auth type.
+ * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
+ * @var string
+ */
+ public $AuthType = '';
+ /**
+ * SMTP realm.
+ * Used for NTLM auth
+ * @var string
+ */
+ public $Realm = '';
+ /**
+ * SMTP workstation.
+ * Used for NTLM auth
+ * @var string
+ */
+ public $Workstation = '';
+ /**
+ * The SMTP server timeout in seconds.
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+ * @var integer
+ */
+ public $Timeout = 300;
+ /**
+ * SMTP class debug output mode.
+ * Debug output level.
+ * Options:
+ * * `0` No output
+ * * `1` Commands
+ * * `2` Data and commands
+ * * `3` As 2 plus connection status
+ * * `4` Low-level data output
+ * @var integer
+ * @see SMTP::$do_debug
+ */
+ public $SMTPDebug = 0;
+ /**
+ * How to handle debug output.
+ * Options:
+ * * `echo` Output plain-text as-is, appropriate for CLI
+ * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output
+ * * `error_log` Output to error log as configured in php.ini
+ *
+ * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
+ *
+ * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
+ *
+ * @var string|callable
+ * @see SMTP::$Debugoutput
+ */
+ public $Debugoutput = 'echo';
+ /**
+ * Whether to keep SMTP connection open after each message.
+ * If this is set to true then to close the connection
+ * requires an explicit call to smtpClose().
+ * @var boolean
+ */
+ public $SMTPKeepAlive = false;
+ /**
+ * Whether to split multiple to addresses into multiple messages
+ * or send them all in one message.
+ * Only supported in `mail` and `sendmail` transports, not in SMTP.
+ * @var boolean
+ */
+ public $SingleTo = false;
+ /**
+ * Storage for addresses when SingleTo is enabled.
+ * @var array
+ */
+ public $SingleToArray = array();
+ /**
+ * Whether to generate VERP addresses on send.
+ * Only applicable when sending via SMTP.
+ * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
+ * @link http://www.postfix.org/VERP_README.html Postfix VERP info
+ * @var boolean
+ */
+ public $do_verp = false;
+ /**
+ * Whether to allow sending messages with an empty body.
+ * @var boolean
+ */
+ public $AllowEmpty = false;
+ /**
+ * The default line ending.
+ * @note The default remains "\n". We force CRLF where we know
+ * it must be used via self::CRLF.
+ * @var string
+ */
+ public $LE = "\n";
+ /**
+ * DKIM selector.
+ * @var string
+ */
+ public $DKIM_selector = '';
+ /**
+ * DKIM Identity.
+ * Usually the email address used as the source of the email
+ * @var string
+ */
+ public $DKIM_identity = '';
+ /**
+ * DKIM passphrase.
+ * Used if your key is encrypted.
+ * @var string
+ */
+ public $DKIM_passphrase = '';
+ /**
+ * DKIM signing domain name.
+ * @example 'example.com'
+ * @var string
+ */
+ public $DKIM_domain = '';
+ /**
+ * DKIM private key file path.
+ * @var string
+ */
+ public $DKIM_private = '';
+ /**
+ * Callback Action function name.
+ *
+ * The function that handles the result of the send email action.
+ * It is called out by send() for each email sent.
+ *
+ * Value can be any php callable: http://www.php.net/is_callable
+ *
+ * Parameters:
+ * boolean $result result of the send action
+ * string $to email address of the recipient
+ * string $cc cc email addresses
+ * string $bcc bcc email addresses
+ * string $subject the subject
+ * string $body the email body
+ * string $from email address of sender
+ * @var string
+ */
+ public $action_function = '';
+ /**
+ * What to put in the X-Mailer header.
+ * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
+ * @var string
+ */
+ public $XMailer = '';
+ /**
+ * The complete compiled MIME message body.
+ * @access protected
+ * @var string
+ */
+ protected $MIMEBody = '';
+ /**
+ * The complete compiled MIME message headers.
+ * @var string
+ * @access protected
+ */
+ protected $MIMEHeader = '';
+ /**
+ * Extra headers that createHeader() doesn't fold in.
+ * @var string
+ * @access protected
+ */
+ protected $mailHeader = '';
+ /**
+ * An instance of the SMTP sender class.
+ * @var SMTP
+ * @access protected
+ */
+ protected $smtp = null;
+ /**
+ * The array of 'to' names and addresses.
+ * @var array
+ * @access protected
+ */
+ protected $to = array();
+ /**
+ * The array of 'cc' names and addresses.
+ * @var array
+ * @access protected
+ */
+ protected $cc = array();
+ /**
+ * The array of 'bcc' names and addresses.
+ * @var array
+ * @access protected
+ */
+ protected $bcc = array();
+ /**
+ * The array of reply-to names and addresses.
+ * @var array
+ * @access protected
+ */
+ protected $ReplyTo = array();
+ /**
+ * An array of all kinds of addresses.
+ * Includes all of $to, $cc, $bcc
+ * @var array
+ * @access protected
+ * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
+ */
+ protected $all_recipients = array();
+ /**
+ * An array of names and addresses queued for validation.
+ * In send(), valid and non duplicate entries are moved to $all_recipients
+ * and one of $to, $cc, or $bcc.
+ * This array is used only for addresses with IDN.
+ * @var array
+ * @access protected
+ * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
+ * @see PHPMailer::$all_recipients
+ */
+ protected $RecipientsQueue = array();
+ /**
+ * An array of reply-to names and addresses queued for validation.
+ * In send(), valid and non duplicate entries are moved to $ReplyTo.
+ * This array is used only for addresses with IDN.
+ * @var array
+ * @access protected
+ * @see PHPMailer::$ReplyTo
+ */
+ protected $ReplyToQueue = array();
+ /**
+ * The array of attachments.
+ * @var array
+ * @access protected
+ */
+ protected $attachment = array();
+ /**
+ * The array of custom headers.
+ * @var array
+ * @access protected
+ */
+ protected $CustomHeader = array();
+ /**
+ * The most recent Message-ID (including angular brackets).
+ * @var string
+ * @access protected
+ */
+ protected $lastMessageID = '';
+ /**
+ * The message's MIME type.
+ * @var string
+ * @access protected
+ */
+ protected $message_type = '';
+ /**
+ * The array of MIME boundary strings.
+ * @var array
+ * @access protected
+ */
+ protected $boundary = array();
+ /**
+ * The array of available languages.
+ * @var array
+ * @access protected
+ */
+ protected $language = array();
+ /**
+ * The number of errors encountered.
+ * @var integer
+ * @access protected
+ */
+ protected $error_count = 0;
+ /**
+ * The S/MIME certificate file path.
+ * @var string
+ * @access protected
+ */
+ protected $sign_cert_file = '';
+ /**
+ * The S/MIME key file path.
+ * @var string
+ * @access protected
+ */
+ protected $sign_key_file = '';
+ /**
+ * The optional S/MIME extra certificates ("CA Chain") file path.
+ * @var string
+ * @access protected
+ */
+ protected $sign_extracerts_file = '';
+ /**
+ * The S/MIME password for the key.
+ * Used only if the key is encrypted.
+ * @var string
+ * @access protected
+ */
+ protected $sign_key_pass = '';
+ /**
+ * Whether to throw exceptions for errors.
+ * @var boolean
+ * @access protected
+ */
+ protected $exceptions = false;
+ /**
+ * Unique ID used for message ID and boundaries.
+ * @var string
+ * @access protected
+ */
+ protected $uniqueid = '';
+
+ /**
+ * Constructor.
+ *
+ * @param boolean $exceptions Should we throw external exceptions?
+ */
+ public function __construct( $exceptions = false ) {
+ $this->exceptions = (boolean)$exceptions;
+ }
+
+ /**
+ * Destructor.
+ */
+ public function __destruct() {
+ //Close any open SMTP connection nicely
+ $this->smtpClose();
+ }
+
+ /**
+ * Close the active SMTP session if one exists.
+ * @return void
+ */
+ public function smtpClose() {
+ if ( is_a( $this->smtp, 'SMTP' ) ) {
+ if ( $this->smtp->connected() ) {
+ $this->smtp->quit();
+ $this->smtp->close();
+ }
+ }
+ }
+
+ /**
+ * Send messages using SMTP.
+ * @return void
+ */
+ public function isSMTP() {
+ $this->Mailer = 'smtp';
+ }
+
+ /**
+ * Send messages using PHP's mail() function.
+ * @return void
+ */
+ public function isMail() {
+ $this->Mailer = 'mail';
+ }
+
+ /**
+ * Send messages using $Sendmail.
+ * @return void
+ */
+ public function isSendmail() {
+ $ini_sendmail_path = ini_get( 'sendmail_path' );
+
+ if ( !stristr( $ini_sendmail_path, 'sendmail' ) ) {
+ $this->Sendmail = '/usr/sbin/sendmail';
+ } else {
+ $this->Sendmail = $ini_sendmail_path;
+ }
+ $this->Mailer = 'sendmail';
+ }
+
+ /**
+ * Send messages using qmail.
+ * @return void
+ */
+ public function isQmail() {
+ $ini_sendmail_path = ini_get( 'sendmail_path' );
+
+ if ( !stristr( $ini_sendmail_path, 'qmail' ) ) {
+ $this->Sendmail = '/var/qmail/bin/qmail-inject';
+ } else {
+ $this->Sendmail = $ini_sendmail_path;
+ }
+ $this->Mailer = 'qmail';
+ }
+
+ /**
+ * Add a "To" address.
+ *
+ * @param string $address The email address to send to
+ * @param string $name
+ *
+ * @return boolean true on success, false if address already used or invalid in some way
+ */
+ public function addAddress( $address, $name = '' ) {
+ return $this->addOrEnqueueAnAddress( 'to', $address, $name );
+ }
+
+ /**
+ * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
+ * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
+ * be modified after calling this function), addition of such addresses is delayed until send().
+ * Addresses that have been added already return false, but do not throw exceptions.
+ *
+ * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
+ * @param string $address The email address to send, resp. to reply to
+ * @param string $name
+ *
+ * @throws phpmailerException
+ * @return boolean true on success, false if address already used or invalid in some way
+ * @access protected
+ */
+ protected function addOrEnqueueAnAddress( $kind, $address, $name ) {
+ $address = trim( $address );
+ $name = trim( preg_replace( '/[\r\n]+/', '', $name ) ); //Strip breaks and trim
+ if ( ( $pos = strrpos( $address, '@' ) ) === false ) {
+ // At-sign is misssing.
+ $error_message = $this->lang( 'invalid_address' ) . " (addAnAddress $kind): $address";
+ $this->setError( $error_message );
+ $this->edebug( $error_message );
+ if ( $this->exceptions ) {
+ throw new phpmailerException( $error_message );
+ }
+
+ return false;
+ }
+ $params = array( $kind, $address, $name );
+ // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
+ if ( $this->has8bitChars( substr( $address, ++$pos ) ) and $this->idnSupported() ) {
+ if ( $kind != 'Reply-To' ) {
+ if ( !array_key_exists( $address, $this->RecipientsQueue ) ) {
+ $this->RecipientsQueue[ $address ] = $params;
+
+ return true;
+ }
+ } else {
+ if ( !array_key_exists( $address, $this->ReplyToQueue ) ) {
+ $this->ReplyToQueue[ $address ] = $params;
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Immediately add standard addresses without IDN.
+ return call_user_func_array( array( $this, 'addAnAddress' ), $params );
+ }
+
+ /**
+ * Get an error message in the current language.
+ * @access protected
+ *
+ * @param string $key
+ *
+ * @return string
+ */
+ protected function lang( $key ) {
+ if ( count( $this->language ) < 1 ) {
+ $this->setLanguage( 'en' ); // set the default language
+ }
+
+ if ( array_key_exists( $key, $this->language ) ) {
+ if ( $key == 'smtp_connect_failed' ) {
+ //Include a link to troubleshooting docs on SMTP connection failure
+ //this is by far the biggest cause of support questions
+ //but it's usually not PHPMailer's fault.
+ return $this->language[ $key ] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
+ }
+
+ return $this->language[ $key ];
+ } else {
+ //Return the key as a fallback
+ return $key;
+ }
+ }
+
+ /**
+ * Set the language for error messages.
+ * Returns false if it cannot load the language file.
+ * The default language is English.
+ *
+ * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
+ * @param string $lang_path Path to the language file directory, with trailing separator (slash)
+ *
+ * @return boolean
+ * @access public
+ */
+ public function setLanguage( $langcode = 'en', $lang_path = '' ) {
+ // Define full set of translatable strings in English
+ $PHPMAILER_LANG = array(
+ 'authenticate' => 'SMTP Error: Could not authenticate.',
+ 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
+ 'data_not_accepted' => 'SMTP Error: data not accepted.',
+ 'empty_message' => 'Message body empty',
+ 'encoding' => 'Unknown encoding: ',
+ 'execute' => 'Could not execute: ',
+ 'file_access' => 'Could not access file: ',
+ 'file_open' => 'File Error: Could not open file: ',
+ 'from_failed' => 'The following From address failed: ',
+ 'instantiate' => 'Could not instantiate mail function.',
+ 'invalid_address' => 'Invalid address: ',
+ 'mailer_not_supported' => ' mailer is not supported.',
+ 'provide_address' => 'You must provide at least one recipient email address.',
+ 'recipients_failed' => 'SMTP Error: The following recipients failed: ',
+ 'signing' => 'Signing Error: ',
+ 'smtp_connect_failed' => 'SMTP connect() failed.',
+ 'smtp_error' => 'SMTP server error: ',
+ 'variable_set' => 'Cannot set or reset variable: ',
+ 'extension_missing' => 'Extension missing: '
+ );
+ if ( empty( $lang_path ) ) {
+ // Calculate an absolute path so it can work if CWD is not here
+ $lang_path = dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
+ }
+ $foundlang = true;
+ $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
+ // There is no English translation file
+ if ( $langcode != 'en' ) {
+ // Make sure language file path is readable
+ if ( !is_readable( $lang_file ) ) {
+ $foundlang = false;
+ } else {
+ // Overwrite language-specific strings.
+ // This way we'll never have missing translation keys.
+ $foundlang = include $lang_file;
+ }
+ }
+ $this->language = $PHPMAILER_LANG;
+
+ return (boolean)$foundlang; // Returns false if language not found
+ }
+
+ /**
+ * Add an error message to the error container.
+ * @access protected
+ *
+ * @param string $msg
+ *
+ * @return void
+ */
+ protected function setError( $msg ) {
+ $this->error_count++;
+ if ( $this->Mailer == 'smtp' and !is_null( $this->smtp ) ) {
+ $lasterror = $this->smtp->getError();
+ if ( !empty( $lasterror[ 'error' ] ) ) {
+ $msg .= $this->lang( 'smtp_error' ) . $lasterror[ 'error' ];
+ if ( !empty( $lasterror[ 'detail' ] ) ) {
+ $msg .= ' Detail: ' . $lasterror[ 'detail' ];
+ }
+ if ( !empty( $lasterror[ 'smtp_code' ] ) ) {
+ $msg .= ' SMTP code: ' . $lasterror[ 'smtp_code' ];
+ }
+ if ( !empty( $lasterror[ 'smtp_code_ex' ] ) ) {
+ $msg .= ' Additional SMTP info: ' . $lasterror[ 'smtp_code_ex' ];
+ }
+ }
+ }
+ $this->ErrorInfo = $msg;
+ }
+
+ /**
+ * Output debugging info via user-defined method.
+ * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
+ * @see PHPMailer::$Debugoutput
+ * @see PHPMailer::$SMTPDebug
+ *
+ * @param string $str
+ */
+ protected function edebug( $str ) {
+ if ( $this->SMTPDebug <= 0 ) {
+ return;
+ }
+ //Avoid clash with built-in function names
+ if ( !in_array( $this->Debugoutput, array(
+ 'error_log',
+ 'html',
+ 'echo'
+ ) ) and is_callable( $this->Debugoutput ) ) {
+ call_user_func( $this->Debugoutput, $str, $this->SMTPDebug );
+
+ return;
+ }
+ switch ( $this->Debugoutput ) {
+ case 'error_log':
+ //Don't output, just log
+ error_log( $str );
+ break;
+ case 'html':
+ //Cleans up output a bit for a better looking, HTML-safe output
+ echo htmlentities(
+ preg_replace( '/[\r\n]+/', '', $str ),
+ ENT_QUOTES,
+ 'UTF-8'
+ )
+ . "
\n";
+ break;
+ case 'echo':
+ default:
+ //Normalize line breaks
+ $str = preg_replace( '/\r\n?/ms', "\n", $str );
+ echo gmdate( 'Y-m-d H:i:s' ) . "\t" . str_replace(
+ "\n",
+ "\n \t ",
+ trim( $str )
+ ) . "\n";
+ }
+ }
+
+ /**
+ * Does a string contain any 8-bit chars (in any charset)?
+ *
+ * @param string $text
+ *
+ * @return boolean
+ */
+ public function has8bitChars( $text ) {
+ return (boolean)preg_match( '/[\x80-\xFF]/', $text );
+ }
+
+ /**
+ * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
+ * "intl" and "mbstring" PHP extensions.
+ * @return bool "true" if required functions for IDN support are present
+ */
+ public function idnSupported() {
+ return function_exists( 'idn_to_ascii' ) and function_exists( 'mb_convert_encoding' );
+ }
+
+ /**
+ * Add a "CC" address.
+ * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
+ *
+ * @param string $address The email address to send to
+ * @param string $name
+ *
+ * @return boolean true on success, false if address already used or invalid in some way
+ */
+ public function addCC( $address, $name = '' ) {
+ return $this->addOrEnqueueAnAddress( 'cc', $address, $name );
+ }
+
+ /**
+ * Add a "BCC" address.
+ * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
+ *
+ * @param string $address The email address to send to
+ * @param string $name
+ *
+ * @return boolean true on success, false if address already used or invalid in some way
+ */
+ public function addBCC( $address, $name = '' ) {
+ return $this->addOrEnqueueAnAddress( 'bcc', $address, $name );
+ }
+
+ /**
+ * Add a "Reply-To" address.
+ *
+ * @param string $address The email address to reply to
+ * @param string $name
+ *
+ * @return boolean true on success, false if address already used or invalid in some way
+ */
+ public function addReplyTo( $address, $name = '' ) {
+ return $this->addOrEnqueueAnAddress( 'Reply-To', $address, $name );
+ }
+
+ /**
+ * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
+ * of the form "display name " into an array of name/address pairs.
+ * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
+ * Note that quotes in the name part are removed.
+ *
+ * @param string $addrstr The address list string
+ * @param bool $useimap Whether to use the IMAP extension to parse the list
+ *
+ * @return array
+ * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
+ */
+ public function parseAddresses( $addrstr, $useimap = true ) {
+ $addresses = array();
+ if ( $useimap and function_exists( 'imap_rfc822_parse_adrlist' ) ) {
+ //Use this built-in parser if it's available
+ $list = imap_rfc822_parse_adrlist( $addrstr, '' );
+ foreach ( $list as $address ) {
+ if ( $address->host != '.SYNTAX-ERROR.' ) {
+ if ( $this->validateAddress( $address->mailbox . '@' . $address->host ) ) {
+ $addresses[] = array(
+ 'name' => ( property_exists( $address, 'personal' ) ? $address->personal : '' ),
+ 'address' => $address->mailbox . '@' . $address->host
+ );
+ }
+ }
+ }
+ } else {
+ //Use this simpler parser
+ $list = explode( ',', $addrstr );
+ foreach ( $list as $address ) {
+ $address = trim( $address );
+ //Is there a separate name part?
+ if ( strpos( $address, '<' ) === false ) {
+ //No separate name, just use the whole thing
+ if ( $this->validateAddress( $address ) ) {
+ $addresses[] = array(
+ 'name' => '',
+ 'address' => $address
+ );
+ }
+ } else {
+ list( $name, $email ) = explode( '<', $address );
+ $email = trim( str_replace( '>', '', $email ) );
+ if ( $this->validateAddress( $email ) ) {
+ $addresses[] = array(
+ 'name' => trim( str_replace( array( '"', "'" ), '', $name ) ),
+ 'address' => $email
+ );
+ }
+ }
+ }
+ }
+
+ return $addresses;
+ }
+
+ /**
+ * Check that a string looks like an email address.
+ *
+ * @param string $address The email address to check
+ * @param string $patternselect A selector for the validation pattern to use :
+ * * `auto` Pick best pattern automatically;
+ * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
+ * * `pcre` Use old PCRE implementation;
+ * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
+ * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
+ * * `noregex` Don't use a regex: super fast, really dumb.
+ *
+ * @return boolean
+ * @static
+ * @access public
+ */
+ public static function validateAddress( $address, $patternselect = 'auto' ) {
+ //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
+ if ( strpos( $address, "\n" ) !== false or strpos( $address, "\r" ) !== false ) {
+ return false;
+ }
+ if ( !$patternselect or $patternselect == 'auto' ) {
+ //Check this constant first so it works when extension_loaded() is disabled by safe mode
+ //Constant was added in PHP 5.2.4
+ if ( defined( 'PCRE_VERSION' ) ) {
+ //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
+ if ( version_compare( PCRE_VERSION, '8.0.3' ) >= 0 ) {
+ $patternselect = 'pcre8';
+ } else {
+ $patternselect = 'pcre';
+ }
+ } elseif ( function_exists( 'extension_loaded' ) and extension_loaded( 'pcre' ) ) {
+ //Fall back to older PCRE
+ $patternselect = 'pcre';
+ } else {
+ //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
+ if ( version_compare( PHP_VERSION, '5.2.0' ) >= 0 ) {
+ $patternselect = 'php';
+ } else {
+ $patternselect = 'noregex';
+ }
+ }
+ }
+ switch ( $patternselect ) {
+ case 'pcre8':
+ /**
+ * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
+ * @link http://squiloople.com/2009/12/20/email-address-validation/
+ * @copyright 2009-2010 Michael Rushton
+ * Feel free to use and redistribute this code. But please keep this copyright notice.
+ */
+ return (boolean)preg_match(
+ '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
+ '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
+ '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
+ '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
+ '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
+ '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
+ '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
+ '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
+ '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
+ $address
+ );
+ case 'pcre':
+ //An older regex that doesn't need a recent PCRE
+ return (boolean)preg_match(
+ '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
+ '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
+ '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
+ '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
+ '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
+ '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
+ '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
+ '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
+ '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
+ '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
+ $address
+ );
+ case 'html5':
+ /**
+ * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
+ * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
+ */
+ return (boolean)preg_match(
+ '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
+ '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
+ $address
+ );
+ case 'noregex':
+ //No PCRE! Do something _very_ approximate!
+ //Check the address is 3 chars or longer and contains an @ that's not the first or last char
+ return ( strlen( $address ) >= 3
+ and strpos( $address, '@' ) >= 1
+ and strpos( $address, '@' ) != strlen( $address ) - 1 );
+ case 'php':
+ default:
+ return (boolean)filter_var( $address, FILTER_VALIDATE_EMAIL );
+ }
+ }
+
+ /**
+ * Set the From and FromName properties.
+ *
+ * @param string $address
+ * @param string $name
+ * @param boolean $auto Whether to also set the Sender address, defaults to true
+ *
+ * @throws phpmailerException
+ * @return boolean
+ */
+ public function setFrom( $address, $name = '', $auto = true ) {
+ $address = trim( $address );
+ $name = trim( preg_replace( '/[\r\n]+/', '', $name ) ); //Strip breaks and trim
+ // Don't validate now addresses with IDN. Will be done in send().
+ if ( ( $pos = strrpos( $address, '@' ) ) === false or
+ ( !$this->has8bitChars( substr( $address, ++$pos ) ) or !$this->idnSupported() ) and
+ !$this->validateAddress( $address ) ) {
+ $error_message = $this->lang( 'invalid_address' ) . " (setFrom) $address";
+ $this->setError( $error_message );
+ $this->edebug( $error_message );
+ if ( $this->exceptions ) {
+ throw new phpmailerException( $error_message );
+ }
+
+ return false;
+ }
+ $this->From = $address;
+ $this->FromName = $name;
+ if ( $auto ) {
+ if ( empty( $this->Sender ) ) {
+ $this->Sender = $address;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Return the Message-ID header of the last email.
+ * Technically this is the value from the last time the headers were created,
+ * but it's also the message ID of the last sent message except in
+ * pathological cases.
+ * @return string
+ */
+ public function getLastMessageID() {
+ return $this->lastMessageID;
+ }
+
+ /**
+ * Create a message and send it.
+ * Uses the sending method specified by $Mailer.
+ * @throws phpmailerException
+ * @return boolean false on error - See the ErrorInfo property for details of the error.
+ */
+ public function send() {
+ try {
+ if ( !$this->preSend() ) {
+ return false;
+ }
+
+ return $this->postSend();
+ } catch ( phpmailerException $exc ) {
+ $this->mailHeader = '';
+ $this->setError( $exc->getMessage() );
+ if ( $this->exceptions ) {
+ throw $exc;
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Prepare a message for sending.
+ * @throws phpmailerException
+ * @return boolean
+ */
+ public function preSend() {
+ try {
+ $this->error_count = 0; // Reset errors
+ $this->mailHeader = '';
+
+ // Dequeue recipient and Reply-To addresses with IDN
+ foreach ( array_merge( $this->RecipientsQueue, $this->ReplyToQueue ) as $params ) {
+ $params[ 1 ] = $this->punyencodeAddress( $params[ 1 ] );
+ call_user_func_array( array( $this, 'addAnAddress' ), $params );
+ }
+ if ( ( count( $this->to ) + count( $this->cc ) + count( $this->bcc ) ) < 1 ) {
+ throw new phpmailerException( $this->lang( 'provide_address' ), self::STOP_CRITICAL );
+ }
+
+ // Validate From, Sender, and ConfirmReadingTo addresses
+ foreach ( array( 'From', 'Sender', 'ConfirmReadingTo' ) as $address_kind ) {
+ $this->$address_kind = trim( $this->$address_kind );
+ if ( empty( $this->$address_kind ) ) {
+ continue;
+ }
+ $this->$address_kind = $this->punyencodeAddress( $this->$address_kind );
+ if ( !$this->validateAddress( $this->$address_kind ) ) {
+ $error_message = $this->lang( 'invalid_address' ) . ' (punyEncode) ' . $this->$address_kind;
+ $this->setError( $error_message );
+ $this->edebug( $error_message );
+ if ( $this->exceptions ) {
+ throw new phpmailerException( $error_message );
+ }
+
+ return false;
+ }
+ }
+
+ // Set whether the message is multipart/alternative
+ if ( $this->alternativeExists() ) {
+ $this->ContentType = 'multipart/alternative';
+ }
+
+ $this->setMessageType();
+ // Refuse to send an empty message unless we are specifically allowing it
+ if ( !$this->AllowEmpty and empty( $this->Body ) ) {
+ throw new phpmailerException( $this->lang( 'empty_message' ), self::STOP_CRITICAL );
+ }
+
+ // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
+ $this->MIMEHeader = '';
+ $this->MIMEBody = $this->createBody();
+ // createBody may have added some headers, so retain them
+ $tempheaders = $this->MIMEHeader;
+ $this->MIMEHeader = $this->createHeader();
+ $this->MIMEHeader .= $tempheaders;
+
+ // To capture the complete message when using mail(), create
+ // an extra header list which createHeader() doesn't fold in
+ if ( $this->Mailer == 'mail' ) {
+ if ( count( $this->to ) > 0 ) {
+ $this->mailHeader .= $this->addrAppend( 'To', $this->to );
+ } else {
+ $this->mailHeader .= $this->headerLine( 'To', 'undisclosed-recipients:;' );
+ }
+ $this->mailHeader .= $this->headerLine(
+ 'Subject',
+ $this->encodeHeader( $this->secureHeader( trim( $this->Subject ) ) )
+ );
+ }
+
+ // Sign with DKIM if enabled
+ if ( !empty( $this->DKIM_domain )
+ && !empty( $this->DKIM_private )
+ && !empty( $this->DKIM_selector )
+ && file_exists( $this->DKIM_private ) ) {
+ $header_dkim = $this->DKIM_Add(
+ $this->MIMEHeader . $this->mailHeader,
+ $this->encodeHeader( $this->secureHeader( $this->Subject ) ),
+ $this->MIMEBody
+ );
+ $this->MIMEHeader = rtrim( $this->MIMEHeader, "\r\n " ) . self::CRLF .
+ str_replace( "\r\n", "\n", $header_dkim ) . self::CRLF;
+ }
+
+ return true;
+ } catch ( phpmailerException $exc ) {
+ $this->setError( $exc->getMessage() );
+ if ( $this->exceptions ) {
+ throw $exc;
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
+ * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
+ * This function silently returns unmodified address if:
+ * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
+ * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
+ * or fails for any reason (e.g. domain has characters not allowed in an IDN)
+ * @see PHPMailer::$CharSet
+ *
+ * @param string $address The email address to convert
+ *
+ * @return string The encoded address in ASCII form
+ */
+ public function punyencodeAddress( $address ) {
+ // Verify we have required functions, CharSet, and at-sign.
+ if ( $this->idnSupported() and
+ !empty( $this->CharSet ) and
+ ( $pos = strrpos( $address, '@' ) ) !== false ) {
+ $domain = substr( $address, ++$pos );
+ // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
+ if ( $this->has8bitChars( $domain ) and @mb_check_encoding( $domain, $this->CharSet ) ) {
+ $domain = mb_convert_encoding( $domain, 'UTF-8', $this->CharSet );
+ if ( ( $punycode = defined( 'INTL_IDNA_VARIANT_UTS46' ) ?
+ idn_to_ascii( $domain, 0, INTL_IDNA_VARIANT_UTS46 ) :
+ idn_to_ascii( $domain ) ) !== false ) {
+ return substr( $address, 0, $pos ) . $punycode;
+ }
+ }
+ }
+
+ return $address;
+ }
+
+ /**
+ * Check if this message has an alternative body set.
+ * @return boolean
+ */
+ public function alternativeExists() {
+ return !empty( $this->AltBody );
+ }
+
+ /**
+ * Set the message type.
+ * PHPMailer only supports some preset message types,
+ * not arbitrary MIME structures.
+ * @access protected
+ * @return void
+ */
+ protected function setMessageType() {
+ $type = array();
+ if ( $this->alternativeExists() ) {
+ $type[] = 'alt';
+ }
+ if ( $this->inlineImageExists() ) {
+ $type[] = 'inline';
+ }
+ if ( $this->attachmentExists() ) {
+ $type[] = 'attach';
+ }
+ $this->message_type = implode( '_', $type );
+ if ( $this->message_type == '' ) {
+ $this->message_type = 'plain';
+ }
+ }
+
+ /**
+ * Check if an inline attachment is present.
+ * @access public
+ * @return boolean
+ */
+ public function inlineImageExists() {
+ foreach ( $this->attachment as $attachment ) {
+ if ( $attachment[ 6 ] == 'inline' ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if an attachment (non-inline) is present.
+ * @return boolean
+ */
+ public function attachmentExists() {
+ foreach ( $this->attachment as $attachment ) {
+ if ( $attachment[ 6 ] == 'attachment' ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Assemble the message body.
+ * Returns an empty string on failure.
+ * @access public
+ * @throws phpmailerException
+ * @return string The assembled message body
+ */
+ public function createBody() {
+ $body = '';
+ //Create unique IDs and preset boundaries
+ $this->uniqueid = md5( uniqid( time() ) );
+ $this->boundary[ 1 ] = 'b1_' . $this->uniqueid;
+ $this->boundary[ 2 ] = 'b2_' . $this->uniqueid;
+ $this->boundary[ 3 ] = 'b3_' . $this->uniqueid;
+
+ if ( $this->sign_key_file ) {
+ $body .= $this->getMailMIME() . $this->LE;
+ }
+
+ $this->setWordWrap();
+
+ $bodyEncoding = $this->Encoding;
+ $bodyCharSet = $this->CharSet;
+ //Can we do a 7-bit downgrade?
+ if ( $bodyEncoding == '8bit' and !$this->has8bitChars( $this->Body ) ) {
+ $bodyEncoding = '7bit';
+ $bodyCharSet = 'us-ascii';
+ }
+ //If lines are too long, and we're not already using an encoding that will shorten them,
+ //change to quoted-printable transfer encoding
+ if ( 'base64' != $this->Encoding and self::hasLineLongerThanMax( $this->Body ) ) {
+ $this->Encoding = 'quoted-printable';
+ $bodyEncoding = 'quoted-printable';
+ }
+
+ $altBodyEncoding = $this->Encoding;
+ $altBodyCharSet = $this->CharSet;
+ //Can we do a 7-bit downgrade?
+ if ( $altBodyEncoding == '8bit' and !$this->has8bitChars( $this->AltBody ) ) {
+ $altBodyEncoding = '7bit';
+ $altBodyCharSet = 'us-ascii';
+ }
+ //If lines are too long, and we're not already using an encoding that will shorten them,
+ //change to quoted-printable transfer encoding
+ if ( 'base64' != $altBodyEncoding and self::hasLineLongerThanMax( $this->AltBody ) ) {
+ $altBodyEncoding = 'quoted-printable';
+ }
+ //Use this as a preamble in all multipart message types
+ $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
+ switch ( $this->message_type ) {
+ case 'inline':
+ $body .= $mimepre;
+ $body .= $this->getBoundary( $this->boundary[ 1 ], $bodyCharSet, '', $bodyEncoding );
+ $body .= $this->encodeString( $this->Body, $bodyEncoding );
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll( 'inline', $this->boundary[ 1 ] );
+ break;
+ case 'attach':
+ $body .= $mimepre;
+ $body .= $this->getBoundary( $this->boundary[ 1 ], $bodyCharSet, '', $bodyEncoding );
+ $body .= $this->encodeString( $this->Body, $bodyEncoding );
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll( 'attachment', $this->boundary[ 1 ] );
+ break;
+ case 'inline_attach':
+ $body .= $mimepre;
+ $body .= $this->textLine( '--' . $this->boundary[ 1 ] );
+ $body .= $this->headerLine( 'Content-Type', 'multipart/related;' );
+ $body .= $this->textLine( "\tboundary=\"" . $this->boundary[ 2 ] . '"' );
+ $body .= $this->LE;
+ $body .= $this->getBoundary( $this->boundary[ 2 ], $bodyCharSet, '', $bodyEncoding );
+ $body .= $this->encodeString( $this->Body, $bodyEncoding );
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll( 'inline', $this->boundary[ 2 ] );
+ $body .= $this->LE;
+ $body .= $this->attachAll( 'attachment', $this->boundary[ 1 ] );
+ break;
+ case 'alt':
+ $body .= $mimepre;
+ $body .= $this->getBoundary( $this->boundary[ 1 ], $altBodyCharSet, 'text/plain', $altBodyEncoding );
+ $body .= $this->encodeString( $this->AltBody, $altBodyEncoding );
+ $body .= $this->LE . $this->LE;
+ $body .= $this->getBoundary( $this->boundary[ 1 ], $bodyCharSet, 'text/html', $bodyEncoding );
+ $body .= $this->encodeString( $this->Body, $bodyEncoding );
+ $body .= $this->LE . $this->LE;
+ if ( !empty( $this->Ical ) ) {
+ $body .= $this->getBoundary( $this->boundary[ 1 ], '', 'text/calendar; method=REQUEST', '' );
+ $body .= $this->encodeString( $this->Ical, $this->Encoding );
+ $body .= $this->LE . $this->LE;
+ }
+ $body .= $this->endBoundary( $this->boundary[ 1 ] );
+ break;
+ case 'alt_inline':
+ $body .= $mimepre;
+ $body .= $this->getBoundary( $this->boundary[ 1 ], $altBodyCharSet, 'text/plain', $altBodyEncoding );
+ $body .= $this->encodeString( $this->AltBody, $altBodyEncoding );
+ $body .= $this->LE . $this->LE;
+ $body .= $this->textLine( '--' . $this->boundary[ 1 ] );
+ $body .= $this->headerLine( 'Content-Type', 'multipart/related;' );
+ $body .= $this->textLine( "\tboundary=\"" . $this->boundary[ 2 ] . '"' );
+ $body .= $this->LE;
+ $body .= $this->getBoundary( $this->boundary[ 2 ], $bodyCharSet, 'text/html', $bodyEncoding );
+ $body .= $this->encodeString( $this->Body, $bodyEncoding );
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll( 'inline', $this->boundary[ 2 ] );
+ $body .= $this->LE;
+ $body .= $this->endBoundary( $this->boundary[ 1 ] );
+ break;
+ case 'alt_attach':
+ $body .= $mimepre;
+ $body .= $this->textLine( '--' . $this->boundary[ 1 ] );
+ $body .= $this->headerLine( 'Content-Type', 'multipart/alternative;' );
+ $body .= $this->textLine( "\tboundary=\"" . $this->boundary[ 2 ] . '"' );
+ $body .= $this->LE;
+ $body .= $this->getBoundary( $this->boundary[ 2 ], $altBodyCharSet, 'text/plain', $altBodyEncoding );
+ $body .= $this->encodeString( $this->AltBody, $altBodyEncoding );
+ $body .= $this->LE . $this->LE;
+ $body .= $this->getBoundary( $this->boundary[ 2 ], $bodyCharSet, 'text/html', $bodyEncoding );
+ $body .= $this->encodeString( $this->Body, $bodyEncoding );
+ $body .= $this->LE . $this->LE;
+ $body .= $this->endBoundary( $this->boundary[ 2 ] );
+ $body .= $this->LE;
+ $body .= $this->attachAll( 'attachment', $this->boundary[ 1 ] );
+ break;
+ case 'alt_inline_attach':
+ $body .= $mimepre;
+ $body .= $this->textLine( '--' . $this->boundary[ 1 ] );
+ $body .= $this->headerLine( 'Content-Type', 'multipart/alternative;' );
+ $body .= $this->textLine( "\tboundary=\"" . $this->boundary[ 2 ] . '"' );
+ $body .= $this->LE;
+ $body .= $this->getBoundary( $this->boundary[ 2 ], $altBodyCharSet, 'text/plain', $altBodyEncoding );
+ $body .= $this->encodeString( $this->AltBody, $altBodyEncoding );
+ $body .= $this->LE . $this->LE;
+ $body .= $this->textLine( '--' . $this->boundary[ 2 ] );
+ $body .= $this->headerLine( 'Content-Type', 'multipart/related;' );
+ $body .= $this->textLine( "\tboundary=\"" . $this->boundary[ 3 ] . '"' );
+ $body .= $this->LE;
+ $body .= $this->getBoundary( $this->boundary[ 3 ], $bodyCharSet, 'text/html', $bodyEncoding );
+ $body .= $this->encodeString( $this->Body, $bodyEncoding );
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll( 'inline', $this->boundary[ 3 ] );
+ $body .= $this->LE;
+ $body .= $this->endBoundary( $this->boundary[ 2 ] );
+ $body .= $this->LE;
+ $body .= $this->attachAll( 'attachment', $this->boundary[ 1 ] );
+ break;
+ default:
+ // catch case 'plain' and case ''
+ $body .= $this->encodeString( $this->Body, $bodyEncoding );
+ break;
+ }
+
+ if ( $this->isError() ) {
+ $body = '';
+ } elseif ( $this->sign_key_file ) {
+ try {
+ if ( !defined( 'PKCS7_TEXT' ) ) {
+ throw new phpmailerException( $this->lang( 'extension_missing' ) . 'openssl' );
+ }
+ $file = tempnam( sys_get_temp_dir(), 'mail' );
+ if ( false === file_put_contents( $file, $body ) ) {
+ throw new phpmailerException( $this->lang( 'signing' ) . ' Could not write temp file' );
+ }
+ $signed = tempnam( sys_get_temp_dir(), 'signed' );
+ //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
+ if ( empty( $this->sign_extracerts_file ) ) {
+ $sign = @openssl_pkcs7_sign(
+ $file,
+ $signed,
+ 'file://' . realpath( $this->sign_cert_file ),
+ array( 'file://' . realpath( $this->sign_key_file ), $this->sign_key_pass ),
+ null
+ );
+ } else {
+ $sign = @openssl_pkcs7_sign(
+ $file,
+ $signed,
+ 'file://' . realpath( $this->sign_cert_file ),
+ array( 'file://' . realpath( $this->sign_key_file ), $this->sign_key_pass ),
+ null,
+ PKCS7_DETACHED,
+ $this->sign_extracerts_file
+ );
+ }
+ if ( $sign ) {
+ @unlink( $file );
+ $body = file_get_contents( $signed );
+ @unlink( $signed );
+ //The message returned by openssl contains both headers and body, so need to split them up
+ $parts = explode( "\n\n", $body, 2 );
+ $this->MIMEHeader .= $parts[ 0 ] . $this->LE . $this->LE;
+ $body = $parts[ 1 ];
+ } else {
+ @unlink( $file );
+ @unlink( $signed );
+ throw new phpmailerException( $this->lang( 'signing' ) . openssl_error_string() );
+ }
+ } catch ( phpmailerException $exc ) {
+ $body = '';
+ if ( $this->exceptions ) {
+ throw $exc;
+ }
+ }
+ }
+
+ return $body;
+ }
+
+ /**
+ * Get the message MIME type headers.
+ * @access public
+ * @return string
+ */
+ public function getMailMIME() {
+ $result = '';
+ $ismultipart = true;
+ switch ( $this->message_type ) {
+ case 'inline':
+ $result .= $this->headerLine( 'Content-Type', 'multipart/related;' );
+ $result .= $this->textLine( "\tboundary=\"" . $this->boundary[ 1 ] . '"' );
+ break;
+ case 'attach':
+ case 'inline_attach':
+ case 'alt_attach':
+ case 'alt_inline_attach':
+ $result .= $this->headerLine( 'Content-Type', 'multipart/mixed;' );
+ $result .= $this->textLine( "\tboundary=\"" . $this->boundary[ 1 ] . '"' );
+ break;
+ case 'alt':
+ case 'alt_inline':
+ $result .= $this->headerLine( 'Content-Type', 'multipart/alternative;' );
+ $result .= $this->textLine( "\tboundary=\"" . $this->boundary[ 1 ] . '"' );
+ break;
+ default:
+ // Catches case 'plain': and case '':
+ $result .= $this->textLine( 'Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet );
+ $ismultipart = false;
+ break;
+ }
+ // RFC1341 part 5 says 7bit is assumed if not specified
+ if ( $this->Encoding != '7bit' ) {
+ // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
+ if ( $ismultipart ) {
+ if ( $this->Encoding == '8bit' ) {
+ $result .= $this->headerLine( 'Content-Transfer-Encoding', '8bit' );
+ }
+ // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
+ } else {
+ $result .= $this->headerLine( 'Content-Transfer-Encoding', $this->Encoding );
+ }
+ }
+
+ if ( $this->Mailer != 'mail' ) {
+ $result .= $this->LE;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Format a header line.
+ * @access public
+ *
+ * @param string $name
+ * @param string $value
+ *
+ * @return string
+ */
+ public function headerLine( $name, $value ) {
+ return $name . ': ' . $value . $this->LE;
+ }
+
+ /**
+ * Return a formatted mail line.
+ * @access public
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ public function textLine( $value ) {
+ return $value . $this->LE;
+ }
+
+ /**
+ * Apply word wrapping to the message body.
+ * Wraps the message body to the number of chars set in the WordWrap property.
+ * You should only do this to plain-text bodies as wrapping HTML tags may break them.
+ * This is called automatically by createBody(), so you don't need to call it yourself.
+ * @access public
+ * @return void
+ */
+ public function setWordWrap() {
+ if ( $this->WordWrap < 1 ) {
+ return;
+ }
+
+ switch ( $this->message_type ) {
+ case 'alt':
+ case 'alt_inline':
+ case 'alt_attach':
+ case 'alt_inline_attach':
+ $this->AltBody = $this->wrapText( $this->AltBody, $this->WordWrap );
+ break;
+ default:
+ $this->Body = $this->wrapText( $this->Body, $this->WordWrap );
+ break;
+ }
+ }
+
+ /**
+ * Word-wrap message.
+ * For use with mailers that do not automatically perform wrapping
+ * and for quoted-printable encoded messages.
+ * Original written by philippe.
+ *
+ * @param string $message The message to wrap
+ * @param integer $length The line length to wrap to
+ * @param boolean $qp_mode Whether to run in Quoted-Printable mode
+ *
+ * @access public
+ * @return string
+ */
+ public function wrapText( $message, $length, $qp_mode = false ) {
+ if ( $qp_mode ) {
+ $soft_break = sprintf( ' =%s', $this->LE );
+ } else {
+ $soft_break = $this->LE;
+ }
+ // If utf-8 encoding is used, we will need to make sure we don't
+ // split multibyte characters when we wrap
+ $is_utf8 = ( strtolower( $this->CharSet ) == 'utf-8' );
+ $lelen = strlen( $this->LE );
+ $crlflen = strlen( self::CRLF );
+
+ $message = $this->fixEOL( $message );
+ //Remove a trailing line break
+ if ( substr( $message, -$lelen ) == $this->LE ) {
+ $message = substr( $message, 0, -$lelen );
+ }
+
+ //Split message into lines
+ $lines = explode( $this->LE, $message );
+ //Message will be rebuilt in here
+ $message = '';
+ foreach ( $lines as $line ) {
+ $words = explode( ' ', $line );
+ $buf = '';
+ $firstword = true;
+ foreach ( $words as $word ) {
+ if ( $qp_mode and ( strlen( $word ) > $length ) ) {
+ $space_left = $length - strlen( $buf ) - $crlflen;
+ if ( !$firstword ) {
+ if ( $space_left > 20 ) {
+ $len = $space_left;
+ if ( $is_utf8 ) {
+ $len = $this->utf8CharBoundary( $word, $len );
+ } elseif ( substr( $word, $len - 1, 1 ) == '=' ) {
+ $len--;
+ } elseif ( substr( $word, $len - 2, 1 ) == '=' ) {
+ $len -= 2;
+ }
+ $part = substr( $word, 0, $len );
+ $word = substr( $word, $len );
+ $buf .= ' ' . $part;
+ $message .= $buf . sprintf( '=%s', self::CRLF );
+ } else {
+ $message .= $buf . $soft_break;
+ }
+ $buf = '';
+ }
+ while ( strlen( $word ) > 0 ) {
+ if ( $length <= 0 ) {
+ break;
+ }
+ $len = $length;
+ if ( $is_utf8 ) {
+ $len = $this->utf8CharBoundary( $word, $len );
+ } elseif ( substr( $word, $len - 1, 1 ) == '=' ) {
+ $len--;
+ } elseif ( substr( $word, $len - 2, 1 ) == '=' ) {
+ $len -= 2;
+ }
+ $part = substr( $word, 0, $len );
+ $word = substr( $word, $len );
+
+ if ( strlen( $word ) > 0 ) {
+ $message .= $part . sprintf( '=%s', self::CRLF );
+ } else {
+ $buf = $part;
+ }
+ }
+ } else {
+ $buf_o = $buf;
+ if ( !$firstword ) {
+ $buf .= ' ';
+ }
+ $buf .= $word;
+
+ if ( strlen( $buf ) > $length and $buf_o != '' ) {
+ $message .= $buf_o . $soft_break;
+ $buf = $word;
+ }
+ }
+ $firstword = false;
+ }
+ $message .= $buf . self::CRLF;
+ }
+
+ return $message;
+ }
+
+ /**
+ * Ensure consistent line endings in a string.
+ * Changes every end of line from CRLF, CR or LF to $this->LE.
+ * @access public
+ *
+ * @param string $str String to fixEOL
+ *
+ * @return string
+ */
+ public function fixEOL( $str ) {
+ // Normalise to \n
+ $nstr = str_replace( array( "\r\n", "\r" ), "\n", $str );
+ // Now convert LE as needed
+ if ( $this->LE !== "\n" ) {
+ $nstr = str_replace( "\n", $this->LE, $nstr );
+ }
+
+ return $nstr;
+ }
+
+ /**
+ * Find the last character boundary prior to $maxLength in a utf-8
+ * quoted-printable encoded string.
+ * Original written by Colin Brown.
+ * @access public
+ *
+ * @param string $encodedText utf-8 QP text
+ * @param integer $maxLength Find the last character boundary prior to this length
+ *
+ * @return integer
+ */
+ public function utf8CharBoundary( $encodedText, $maxLength ) {
+ $foundSplitPos = false;
+ $lookBack = 3;
+ while ( !$foundSplitPos ) {
+ $lastChunk = substr( $encodedText, $maxLength - $lookBack, $lookBack );
+ $encodedCharPos = strpos( $lastChunk, '=' );
+ if ( false !== $encodedCharPos ) {
+ // Found start of encoded character byte within $lookBack block.
+ // Check the encoded byte value (the 2 chars after the '=')
+ $hex = substr( $encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2 );
+ $dec = hexdec( $hex );
+ if ( $dec < 128 ) {
+ // Single byte character.
+ // If the encoded char was found at pos 0, it will fit
+ // otherwise reduce maxLength to start of the encoded char
+ if ( $encodedCharPos > 0 ) {
+ $maxLength = $maxLength - ( $lookBack - $encodedCharPos );
+ }
+ $foundSplitPos = true;
+ } elseif ( $dec >= 192 ) {
+ // First byte of a multi byte character
+ // Reduce maxLength to split at start of character
+ $maxLength = $maxLength - ( $lookBack - $encodedCharPos );
+ $foundSplitPos = true;
+ } elseif ( $dec < 192 ) {
+ // Middle byte of a multi byte character, look further back
+ $lookBack += 3;
+ }
+ } else {
+ // No encoded character found
+ $foundSplitPos = true;
+ }
+ }
+
+ return $maxLength;
+ }
+
+ /**
+ * Detect if a string contains a line longer than the maximum line length allowed.
+ *
+ * @param string $str
+ *
+ * @return boolean
+ * @static
+ */
+ public static function hasLineLongerThanMax( $str ) {
+ //+2 to include CRLF line break for a 1000 total
+ return (boolean)preg_match( '/^(.{' . ( self::MAX_LINE_LENGTH + 2 ) . ',})/m', $str );
+ }
+
+ /**
+ * Return the start of a message boundary.
+ * @access protected
+ *
+ * @param string $boundary
+ * @param string $charSet
+ * @param string $contentType
+ * @param string $encoding
+ *
+ * @return string
+ */
+ protected function getBoundary( $boundary, $charSet, $contentType, $encoding ) {
+ $result = '';
+ if ( $charSet == '' ) {
+ $charSet = $this->CharSet;
+ }
+ if ( $contentType == '' ) {
+ $contentType = $this->ContentType;
+ }
+ if ( $encoding == '' ) {
+ $encoding = $this->Encoding;
+ }
+ $result .= $this->textLine( '--' . $boundary );
+ $result .= sprintf( 'Content-Type: %s; charset=%s', $contentType, $charSet );
+ $result .= $this->LE;
+ // RFC1341 part 5 says 7bit is assumed if not specified
+ if ( $encoding != '7bit' ) {
+ $result .= $this->headerLine( 'Content-Transfer-Encoding', $encoding );
+ }
+ $result .= $this->LE;
+
+ return $result;
+ }
+
+ /**
+ * Encode a string in requested format.
+ * Returns an empty string on failure.
+ *
+ * @param string $str The text to encode
+ * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
+ *
+ * @access public
+ * @return string
+ */
+ public function encodeString( $str, $encoding = 'base64' ) {
+ $encoded = '';
+ switch ( strtolower( $encoding ) ) {
+ case 'base64':
+ $encoded = chunk_split( base64_encode( $str ), 76, $this->LE );
+ break;
+ case '7bit':
+ case '8bit':
+ $encoded = $this->fixEOL( $str );
+ // Make sure it ends with a line break
+ if ( substr( $encoded, -( strlen( $this->LE ) ) ) != $this->LE ) {
+ $encoded .= $this->LE;
+ }
+ break;
+ case 'binary':
+ $encoded = $str;
+ break;
+ case 'quoted-printable':
+ $encoded = $this->encodeQP( $str );
+ break;
+ default:
+ $this->setError( $this->lang( 'encoding' ) . $encoding );
+ break;
+ }
+
+ return $encoded;
+ }
+
+ /**
+ * Encode a string in quoted-printable format.
+ * According to RFC2045 section 6.7.
+ * @access public
+ *
+ * @param string $string The text to encode
+ * @param integer $line_max Number of chars allowed on a line before wrapping
+ *
+ * @return string
+ * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
+ */
+ public function encodeQP( $string, $line_max = 76 ) {
+ // Use native function if it's available (>= PHP5.3)
+ if ( function_exists( 'quoted_printable_encode' ) ) {
+ return quoted_printable_encode( $string );
+ }
+ // Fall back to a pure PHP implementation
+ $string = str_replace(
+ array( '%20', '%0D%0A.', '%0D%0A', '%' ),
+ array( ' ', "\r\n=2E", "\r\n", '=' ),
+ rawurlencode( $string )
+ );
+
+ return preg_replace( '/[^\r\n]{' . ( $line_max - 3 ) . '}[^=\r\n]{2}/', "$0=\r\n", $string );
+ }
+
+ /**
+ * Attach all file, string, and binary attachments to the message.
+ * Returns an empty string on failure.
+ * @access protected
+ *
+ * @param string $disposition_type
+ * @param string $boundary
+ *
+ * @return string
+ */
+ protected function attachAll( $disposition_type, $boundary ) {
+ // Return text of body
+ $mime = array();
+ $cidUniq = array();
+ $incl = array();
+
+ // Add all attachments
+ foreach ( $this->attachment as $attachment ) {
+ // Check if it is a valid disposition_filter
+ if ( $attachment[ 6 ] == $disposition_type ) {
+ // Check for string attachment
+ $string = '';
+ $path = '';
+ $bString = $attachment[ 5 ];
+ if ( $bString ) {
+ $string = $attachment[ 0 ];
+ } else {
+ $path = $attachment[ 0 ];
+ }
+
+ $inclhash = md5( serialize( $attachment ) );
+ if ( in_array( $inclhash, $incl ) ) {
+ continue;
+ }
+ $incl[] = $inclhash;
+ $name = $attachment[ 2 ];
+ $encoding = $attachment[ 3 ];
+ $type = $attachment[ 4 ];
+ $disposition = $attachment[ 6 ];
+ $cid = $attachment[ 7 ];
+ if ( $disposition == 'inline' && array_key_exists( $cid, $cidUniq ) ) {
+ continue;
+ }
+ $cidUniq[ $cid ] = true;
+
+ $mime[] = sprintf( '--%s%s', $boundary, $this->LE );
+ //Only include a filename property if we have one
+ if ( !empty( $name ) ) {
+ $mime[] = sprintf(
+ 'Content-Type: %s; name="%s"%s',
+ $type,
+ $this->encodeHeader( $this->secureHeader( $name ) ),
+ $this->LE
+ );
+ } else {
+ $mime[] = sprintf(
+ 'Content-Type: %s%s',
+ $type,
+ $this->LE
+ );
+ }
+ // RFC1341 part 5 says 7bit is assumed if not specified
+ if ( $encoding != '7bit' ) {
+ $mime[] = sprintf( 'Content-Transfer-Encoding: %s%s', $encoding, $this->LE );
+ }
+
+ if ( $disposition == 'inline' ) {
+ $mime[] = sprintf( 'Content-ID: <%s>%s', $cid, $this->LE );
+ }
+
+ // If a filename contains any of these chars, it should be quoted,
+ // but not otherwise: RFC2183 & RFC2045 5.1
+ // Fixes a warning in IETF's msglint MIME checker
+ // Allow for bypassing the Content-Disposition header totally
+ if ( !( empty( $disposition ) ) ) {
+ $encoded_name = $this->encodeHeader( $this->secureHeader( $name ) );
+ if ( preg_match( '/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name ) ) {
+ $mime[] = sprintf(
+ 'Content-Disposition: %s; filename="%s"%s',
+ $disposition,
+ $encoded_name,
+ $this->LE . $this->LE
+ );
+ } else {
+ if ( !empty( $encoded_name ) ) {
+ $mime[] = sprintf(
+ 'Content-Disposition: %s; filename=%s%s',
+ $disposition,
+ $encoded_name,
+ $this->LE . $this->LE
+ );
+ } else {
+ $mime[] = sprintf(
+ 'Content-Disposition: %s%s',
+ $disposition,
+ $this->LE . $this->LE
+ );
+ }
+ }
+ } else {
+ $mime[] = $this->LE;
+ }
+
+ // Encode as string attachment
+ if ( $bString ) {
+ $mime[] = $this->encodeString( $string, $encoding );
+ if ( $this->isError() ) {
+ return '';
+ }
+ $mime[] = $this->LE . $this->LE;
+ } else {
+ $mime[] = $this->encodeFile( $path, $encoding );
+ if ( $this->isError() ) {
+ return '';
+ }
+ $mime[] = $this->LE . $this->LE;
+ }
+ }
+ }
+
+ $mime[] = sprintf( '--%s--%s', $boundary, $this->LE );
+
+ return implode( '', $mime );
+ }
+
+ /**
+ * Encode a header string optimally.
+ * Picks shortest of Q, B, quoted-printable or none.
+ * @access public
+ *
+ * @param string $str
+ * @param string $position
+ *
+ * @return string
+ */
+ public function encodeHeader( $str, $position = 'text' ) {
+ $matchcount = 0;
+ switch ( strtolower( $position ) ) {
+ case 'phrase':
+ if ( !preg_match( '/[\200-\377]/', $str ) ) {
+ // Can't use addslashes as we don't know the value of magic_quotes_sybase
+ $encoded = addcslashes( $str, "\0..\37\177\\\"" );
+ if ( ( $str == $encoded ) && !preg_match( '/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str ) ) {
+ return ( $encoded );
+ } else {
+ return ( "\"$encoded\"" );
+ }
+ }
+ $matchcount = preg_match_all( '/[^\040\041\043-\133\135-\176]/', $str, $matches );
+ break;
+ /** @noinspection PhpMissingBreakStatementInspection */
+ case 'comment':
+ $matchcount = preg_match_all( '/[()"]/', $str, $matches );
+ // Intentional fall-through
+ case 'text':
+ default:
+ $matchcount += preg_match_all( '/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches );
+ break;
+ }
+
+ //There are no chars that need encoding
+ if ( $matchcount == 0 ) {
+ return ( $str );
+ }
+
+ $maxlen = 75 - 7 - strlen( $this->CharSet );
+ // Try to select the encoding which should produce the shortest output
+ if ( $matchcount > strlen( $str ) / 3 ) {
+ // More than a third of the content will need encoding, so B encoding will be most efficient
+ $encoding = 'B';
+ if ( function_exists( 'mb_strlen' ) && $this->hasMultiBytes( $str ) ) {
+ // Use a custom function which correctly encodes and wraps long
+ // multibyte strings without breaking lines within a character
+ $encoded = $this->base64EncodeWrapMB( $str, "\n" );
+ } else {
+ $encoded = base64_encode( $str );
+ $maxlen -= $maxlen % 4;
+ $encoded = trim( chunk_split( $encoded, $maxlen, "\n" ) );
+ }
+ } else {
+ $encoding = 'Q';
+ $encoded = $this->encodeQ( $str, $position );
+ $encoded = $this->wrapText( $encoded, $maxlen, true );
+ $encoded = str_replace( '=' . self::CRLF, "\n", trim( $encoded ) );
+ }
+
+ $encoded = preg_replace( '/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded );
+ $encoded = trim( str_replace( "\n", $this->LE, $encoded ) );
+
+ return $encoded;
+ }
+
+ /**
+ * Check if a string contains multi-byte characters.
+ * @access public
+ *
+ * @param string $str multi-byte text to wrap encode
+ *
+ * @return boolean
+ */
+ public function hasMultiBytes( $str ) {
+ if ( function_exists( 'mb_strlen' ) ) {
+ return ( strlen( $str ) > mb_strlen( $str, $this->CharSet ) );
+ } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
+ return false;
+ }
+ }
+
+ /**
+ * Encode and wrap long multibyte strings for mail headers
+ * without breaking lines within a character.
+ * Adapted from a function by paravoid
+ * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
+ * @access public
+ *
+ * @param string $str multi-byte text to wrap encode
+ * @param string $linebreak string to use as linefeed/end-of-line
+ *
+ * @return string
+ */
+ public function base64EncodeWrapMB( $str, $linebreak = null ) {
+ $start = '=?' . $this->CharSet . '?B?';
+ $end = '?=';
+ $encoded = '';
+ if ( $linebreak === null ) {
+ $linebreak = $this->LE;
+ }
+
+ $mb_length = mb_strlen( $str, $this->CharSet );
+ // Each line must have length <= 75, including $start and $end
+ $length = 75 - strlen( $start ) - strlen( $end );
+ // Average multi-byte ratio
+ $ratio = $mb_length / strlen( $str );
+ // Base64 has a 4:3 ratio
+ $avgLength = floor( $length * $ratio * .75 );
+
+ for ( $i = 0; $i < $mb_length; $i += $offset ) {
+ $lookBack = 0;
+ do {
+ $offset = $avgLength - $lookBack;
+ $chunk = mb_substr( $str, $i, $offset, $this->CharSet );
+ $chunk = base64_encode( $chunk );
+ $lookBack++;
+ } while ( strlen( $chunk ) > $length );
+ $encoded .= $chunk . $linebreak;
+ }
+
+ // Chomp the last linefeed
+ $encoded = substr( $encoded, 0, -strlen( $linebreak ) );
+
+ return $encoded;
+ }
+
+ /**
+ * Encode a string using Q encoding.
+ * @link http://tools.ietf.org/html/rfc2047
+ *
+ * @param string $str the text to encode
+ * @param string $position Where the text is going to be used, see the RFC for what that means
+ *
+ * @access public
+ * @return string
+ */
+ public function encodeQ( $str, $position = 'text' ) {
+ // There should not be any EOL in the string
+ $pattern = '';
+ $encoded = str_replace( array( "\r", "\n" ), '', $str );
+ switch ( strtolower( $position ) ) {
+ case 'phrase':
+ // RFC 2047 section 5.3
+ $pattern = '^A-Za-z0-9!*+\/ -';
+ break;
+ /** @noinspection PhpMissingBreakStatementInspection */
+ case 'comment':
+ // RFC 2047 section 5.2
+ $pattern = '\(\)"';
+ // intentional fall-through
+ // for this reason we build the $pattern without including delimiters and []
+ case 'text':
+ default:
+ // RFC 2047 section 5.1
+ // Replace every high ascii, control, =, ? and _ characters
+ $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
+ break;
+ }
+ $matches = array();
+ if ( preg_match_all( "/[{$pattern}]/", $encoded, $matches ) ) {
+ // If the string contains an '=', make sure it's the first thing we replace
+ // so as to avoid double-encoding
+ $eqkey = array_search( '=', $matches[ 0 ] );
+ if ( false !== $eqkey ) {
+ unset( $matches[ 0 ][ $eqkey ] );
+ array_unshift( $matches[ 0 ], '=' );
+ }
+ foreach ( array_unique( $matches[ 0 ] ) as $char ) {
+ $encoded = str_replace( $char, '=' . sprintf( '%02X', ord( $char ) ), $encoded );
+ }
+ }
+
+ // Replace every spaces to _ (more readable than =20)
+ return str_replace( ' ', '_', $encoded );
+ }
+
+ /**
+ * Strip newlines to prevent header injection.
+ * @access public
+ *
+ * @param string $str
+ *
+ * @return string
+ */
+ public function secureHeader( $str ) {
+ return trim( str_replace( array( "\r", "\n" ), '', $str ) );
+ }
+
+ /**
+ * Check if an error occurred.
+ * @access public
+ * @return boolean True if an error did occur.
+ */
+ public function isError() {
+ return ( $this->error_count > 0 );
+ }
+
+ /**
+ * Encode a file attachment in requested format.
+ * Returns an empty string on failure.
+ *
+ * @param string $path The full path to the file
+ * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
+ *
+ * @throws phpmailerException
+ * @access protected
+ * @return string
+ */
+ protected function encodeFile( $path, $encoding = 'base64' ) {
+ try {
+ if ( !is_readable( $path ) ) {
+ throw new phpmailerException( $this->lang( 'file_open' ) . $path, self::STOP_CONTINUE );
+ }
+ $magic_quotes = get_magic_quotes_runtime();
+ if ( $magic_quotes ) {
+ if ( version_compare( PHP_VERSION, '5.3.0', '<' ) ) {
+ set_magic_quotes_runtime( false );
+ } else {
+ //Doesn't exist in PHP 5.4, but we don't need to check because
+ //get_magic_quotes_runtime always returns false in 5.4+
+ //so it will never get here
+ ini_set( 'magic_quotes_runtime', false );
+ }
+ }
+ $file_buffer = file_get_contents( $path );
+ $file_buffer = $this->encodeString( $file_buffer, $encoding );
+ if ( $magic_quotes ) {
+ if ( version_compare( PHP_VERSION, '5.3.0', '<' ) ) {
+ set_magic_quotes_runtime( $magic_quotes );
+ } else {
+ ini_set( 'magic_quotes_runtime', $magic_quotes );
+ }
+ }
+
+ return $file_buffer;
+ } catch ( Exception $exc ) {
+ $this->setError( $exc->getMessage() );
+
+ return '';
+ }
+ }
+
+ /**
+ * Return the end of a message boundary.
+ * @access protected
+ *
+ * @param string $boundary
+ *
+ * @return string
+ */
+ protected function endBoundary( $boundary ) {
+ return $this->LE . '--' . $boundary . '--' . $this->LE;
+ }
+
+ /**
+ * Assemble message headers.
+ * @access public
+ * @return string The assembled headers
+ */
+ public function createHeader() {
+ $result = '';
+
+ if ( $this->MessageDate == '' ) {
+ $this->MessageDate = self::rfcDate();
+ }
+ $result .= $this->headerLine( 'Date', $this->MessageDate );
+
+ // To be created automatically by mail()
+ if ( $this->SingleTo ) {
+ if ( $this->Mailer != 'mail' ) {
+ foreach ( $this->to as $toaddr ) {
+ $this->SingleToArray[] = $this->addrFormat( $toaddr );
+ }
+ }
+ } else {
+ if ( count( $this->to ) > 0 ) {
+ if ( $this->Mailer != 'mail' ) {
+ $result .= $this->addrAppend( 'To', $this->to );
+ }
+ } elseif ( count( $this->cc ) == 0 ) {
+ $result .= $this->headerLine( 'To', 'undisclosed-recipients:;' );
+ }
+ }
+
+ $result .= $this->addrAppend( 'From', array( array( trim( $this->From ), $this->FromName ) ) );
+
+ // sendmail and mail() extract Cc from the header before sending
+ if ( count( $this->cc ) > 0 ) {
+ $result .= $this->addrAppend( 'Cc', $this->cc );
+ }
+
+ // sendmail and mail() extract Bcc from the header before sending
+ if ( (
+ $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
+ )
+ and count( $this->bcc ) > 0
+ ) {
+ $result .= $this->addrAppend( 'Bcc', $this->bcc );
+ }
+
+ if ( count( $this->ReplyTo ) > 0 ) {
+ $result .= $this->addrAppend( 'Reply-To', $this->ReplyTo );
+ }
+
+ // mail() sets the subject itself
+ if ( $this->Mailer != 'mail' ) {
+ $result .= $this->headerLine( 'Subject', $this->encodeHeader( $this->secureHeader( $this->Subject ) ) );
+ }
+
+ if ( '' != $this->MessageID and preg_match( '/^<.*@.*>$/', $this->MessageID ) ) {
+ $this->lastMessageID = $this->MessageID;
+ } else {
+ $this->lastMessageID = sprintf( '<%s@%s>', $this->uniqueid, $this->serverHostname() );
+ }
+ $result .= $this->headerLine( 'Message-ID', $this->lastMessageID );
+ if ( !is_null( $this->Priority ) ) {
+ $result .= $this->headerLine( 'X-Priority', $this->Priority );
+ }
+ if ( $this->XMailer == '' ) {
+ $result .= $this->headerLine(
+ 'X-Mailer',
+ 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
+ );
+ } else {
+ $myXmailer = trim( $this->XMailer );
+ if ( $myXmailer ) {
+ $result .= $this->headerLine( 'X-Mailer', $myXmailer );
+ }
+ }
+
+ if ( $this->ConfirmReadingTo != '' ) {
+ $result .= $this->headerLine( 'Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>' );
+ }
+
+ // Add custom headers
+ foreach ( $this->CustomHeader as $header ) {
+ $result .= $this->headerLine(
+ trim( $header[ 0 ] ),
+ $this->encodeHeader( trim( $header[ 1 ] ) )
+ );
+ }
+ if ( !$this->sign_key_file ) {
+ $result .= $this->headerLine( 'MIME-Version', '1.0' );
+ $result .= $this->getMailMIME();
+ }
+
+ return $result;
+ }
+
+ /**
+ * Return an RFC 822 formatted date.
+ * @access public
+ * @return string
+ * @static
+ */
+ public static function rfcDate() {
+ // Set the time zone to whatever the default is to avoid 500 errors
+ // Will default to UTC if it's not set properly in php.ini
+ date_default_timezone_set( @date_default_timezone_get() );
+
+ return date( 'D, j M Y H:i:s O' );
+ }
+
+ /**
+ * Format an address for use in a message header.
+ * @access public
+ *
+ * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
+ * like array('joe@example.com', 'Joe User')
+ *
+ * @return string
+ */
+ public function addrFormat( $addr ) {
+ if ( empty( $addr[ 1 ] ) ) { // No name provided
+ return $this->secureHeader( $addr[ 0 ] );
+ } else {
+ return $this->encodeHeader( $this->secureHeader( $addr[ 1 ] ), 'phrase' ) . ' <' . $this->secureHeader(
+ $addr[ 0 ]
+ ) . '>';
+ }
+ }
+
+ /**
+ * Create recipient headers.
+ * @access public
+ *
+ * @param string $type
+ * @param array $addr An array of recipient,
+ * where each recipient is a 2-element indexed array with element 0 containing an address
+ * and element 1 containing a name, like:
+ * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
+ *
+ * @return string
+ */
+ public function addrAppend( $type, $addr ) {
+ $addresses = array();
+ foreach ( $addr as $address ) {
+ $addresses[] = $this->addrFormat( $address );
+ }
+
+ return $type . ': ' . implode( ', ', $addresses ) . $this->LE;
+ }
+
+ /**
+ * Get the server hostname.
+ * Returns 'localhost.localdomain' if unknown.
+ * @access protected
+ * @return string
+ */
+ protected function serverHostname() {
+ $result = 'localhost.localdomain';
+ if ( !empty( $this->Hostname ) ) {
+ $result = $this->Hostname;
+ } elseif ( isset( $_SERVER ) and array_key_exists( 'SERVER_NAME', $_SERVER ) and !empty( $_SERVER[ 'SERVER_NAME' ] ) ) {
+ $result = $_SERVER[ 'SERVER_NAME' ];
+ } elseif ( function_exists( 'gethostname' ) && gethostname() !== false ) {
+ $result = gethostname();
+ } elseif ( php_uname( 'n' ) !== false ) {
+ $result = php_uname( 'n' );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Create the DKIM header and body in a new message header.
+ * @access public
+ *
+ * @param string $headers_line Header lines
+ * @param string $subject Subject
+ * @param string $body Body
+ *
+ * @return string
+ */
+ public function DKIM_Add( $headers_line, $subject, $body ) {
+ $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
+ $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
+ $DKIMquery = 'dns/txt'; // Query method
+ $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
+ $subject_header = "Subject: $subject";
+ $headers = explode( $this->LE, $headers_line );
+ $from_header = '';
+ $to_header = '';
+ $date_header = '';
+ $current = '';
+ foreach ( $headers as $header ) {
+ if ( strpos( $header, 'From:' ) === 0 ) {
+ $from_header = $header;
+ $current = 'from_header';
+ } elseif ( strpos( $header, 'To:' ) === 0 ) {
+ $to_header = $header;
+ $current = 'to_header';
+ } elseif ( strpos( $header, 'Date:' ) === 0 ) {
+ $date_header = $header;
+ $current = 'date_header';
+ } else {
+ if ( !empty( $$current ) && strpos( $header, ' =?' ) === 0 ) {
+ $$current .= $header;
+ } else {
+ $current = '';
+ }
+ }
+ }
+ $from = str_replace( '|', '=7C', $this->DKIM_QP( $from_header ) );
+ $to = str_replace( '|', '=7C', $this->DKIM_QP( $to_header ) );
+ $date = str_replace( '|', '=7C', $this->DKIM_QP( $date_header ) );
+ $subject = str_replace(
+ '|',
+ '=7C',
+ $this->DKIM_QP( $subject_header )
+ ); // Copied header fields (dkim-quoted-printable)
+ $body = $this->DKIM_BodyC( $body );
+ $DKIMlen = strlen( $body ); // Length of body
+ $DKIMb64 = base64_encode( pack( 'H*', hash( 'sha256', $body ) ) ); // Base64 of packed binary SHA-256 hash of body
+ if ( '' == $this->DKIM_identity ) {
+ $ident = '';
+ } else {
+ $ident = ' i=' . $this->DKIM_identity . ';';
+ }
+ $dkimhdrs = 'DKIM-Signature: v=1; a=' .
+ $DKIMsignatureType . '; q=' .
+ $DKIMquery . '; l=' .
+ $DKIMlen . '; s=' .
+ $this->DKIM_selector .
+ ";\r\n" .
+ "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
+ "\th=From:To:Date:Subject;\r\n" .
+ "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
+ "\tz=$from\r\n" .
+ "\t|$to\r\n" .
+ "\t|$date\r\n" .
+ "\t|$subject;\r\n" .
+ "\tbh=" . $DKIMb64 . ";\r\n" .
+ "\tb=";
+ $toSign = $this->DKIM_HeaderC(
+ $from_header . "\r\n" .
+ $to_header . "\r\n" .
+ $date_header . "\r\n" .
+ $subject_header . "\r\n" .
+ $dkimhdrs
+ );
+ $signed = $this->DKIM_Sign( $toSign );
+
+ return $dkimhdrs . $signed . "\r\n";
+ }
+
+ /**
+ * Quoted-Printable-encode a DKIM header.
+ * @access public
+ *
+ * @param string $txt
+ *
+ * @return string
+ */
+ public function DKIM_QP( $txt ) {
+ $line = '';
+ for ( $i = 0; $i < strlen( $txt ); $i++ ) {
+ $ord = ord( $txt[ $i ] );
+ if ( ( ( 0x21 <= $ord ) && ( $ord <= 0x3A ) ) || $ord == 0x3C || ( ( 0x3E <= $ord ) && ( $ord <= 0x7E ) ) ) {
+ $line .= $txt[ $i ];
+ } else {
+ $line .= '=' . sprintf( '%02X', $ord );
+ }
+ }
+
+ return $line;
+ }
+
+ /**
+ * Generate a DKIM canonicalization body.
+ * @access public
+ *
+ * @param string $body Message Body
+ *
+ * @return string
+ */
+ public function DKIM_BodyC( $body ) {
+ if ( $body == '' ) {
+ return "\r\n";
+ }
+ // stabilize line endings
+ $body = str_replace( "\r\n", "\n", $body );
+ $body = str_replace( "\n", "\r\n", $body );
+ // END stabilize line endings
+ while ( substr( $body, strlen( $body ) - 4, 4 ) == "\r\n\r\n" ) {
+ $body = substr( $body, 0, strlen( $body ) - 2 );
+ }
+
+ return $body;
+ }
+
+ /**
+ * Generate a DKIM canonicalization header.
+ * @access public
+ *
+ * @param string $signHeader Header
+ *
+ * @return string
+ */
+ public function DKIM_HeaderC( $signHeader ) {
+ $signHeader = preg_replace( '/\r\n\s+/', ' ', $signHeader );
+ $lines = explode( "\r\n", $signHeader );
+ foreach ( $lines as $key => $line ) {
+ list( $heading, $value ) = explode( ':', $line, 2 );
+ $heading = strtolower( $heading );
+ $value = preg_replace( '/\s{2,}/', ' ', $value ); // Compress useless spaces
+ $lines[ $key ] = $heading . ':' . trim( $value ); // Don't forget to remove WSP around the value
+ }
+ $signHeader = implode( "\r\n", $lines );
+
+ return $signHeader;
+ }
+
+ /**
+ * Generate a DKIM signature.
+ * @access public
+ *
+ * @param string $signHeader
+ *
+ * @throws phpmailerException
+ * @return string
+ */
+ public function DKIM_Sign( $signHeader ) {
+ if ( !defined( 'PKCS7_TEXT' ) ) {
+ if ( $this->exceptions ) {
+ throw new phpmailerException( $this->lang( 'extension_missing' ) . 'openssl' );
+ }
+
+ return '';
+ }
+ $privKeyStr = file_get_contents( $this->DKIM_private );
+ if ( $this->DKIM_passphrase != '' ) {
+ $privKey = openssl_pkey_get_private( $privKeyStr, $this->DKIM_passphrase );
+ } else {
+ $privKey = openssl_pkey_get_private( $privKeyStr );
+ }
+ if ( openssl_sign( $signHeader, $signature, $privKey, 'sha256WithRSAEncryption' ) ) { //sha1WithRSAEncryption
+ openssl_pkey_free( $privKey );
+
+ return base64_encode( $signature );
+ }
+ openssl_pkey_free( $privKey );
+
+ return '';
+ }
+
+ /**
+ * Actually send a message.
+ * Send the email via the selected mechanism
+ * @throws phpmailerException
+ * @return boolean
+ */
+ public function postSend() {
+ try {
+ // Choose the mailer and send through it
+ switch ( $this->Mailer ) {
+ case 'sendmail':
+ case 'qmail':
+ return $this->sendmailSend( $this->MIMEHeader, $this->MIMEBody );
+ case 'smtp':
+ return $this->smtpSend( $this->MIMEHeader, $this->MIMEBody );
+ case 'mail':
+ return $this->mailSend( $this->MIMEHeader, $this->MIMEBody );
+ default:
+ $sendMethod = $this->Mailer . 'Send';
+ if ( method_exists( $this, $sendMethod ) ) {
+ return $this->$sendMethod( $this->MIMEHeader, $this->MIMEBody );
+ }
+
+ return $this->mailSend( $this->MIMEHeader, $this->MIMEBody );
+ }
+ } catch ( phpmailerException $exc ) {
+ $this->setError( $exc->getMessage() );
+ $this->edebug( $exc->getMessage() );
+ if ( $this->exceptions ) {
+ throw $exc;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Send mail using the $Sendmail program.
+ *
+ * @param string $header The message headers
+ * @param string $body The message body
+ *
+ * @see PHPMailer::$Sendmail
+ * @throws phpmailerException
+ * @access protected
+ * @return boolean
+ */
+ protected function sendmailSend( $header, $body ) {
+ if ( $this->Sender != '' ) {
+ if ( $this->Mailer == 'qmail' ) {
+ $sendmail = sprintf( '%s -f%s', escapeshellcmd( $this->Sendmail ), escapeshellarg( $this->Sender ) );
+ } else {
+ $sendmail = sprintf( '%s -oi -f%s -t', escapeshellcmd( $this->Sendmail ), escapeshellarg( $this->Sender ) );
+ }
+ } else {
+ if ( $this->Mailer == 'qmail' ) {
+ $sendmail = sprintf( '%s', escapeshellcmd( $this->Sendmail ) );
+ } else {
+ $sendmail = sprintf( '%s -oi -t', escapeshellcmd( $this->Sendmail ) );
+ }
+ }
+ if ( $this->SingleTo ) {
+ foreach ( $this->SingleToArray as $toAddr ) {
+ if ( !@$mail = popen( $sendmail, 'w' ) ) {
+ throw new phpmailerException( $this->lang( 'execute' ) . $this->Sendmail, self::STOP_CRITICAL );
+ }
+ fputs( $mail, 'To: ' . $toAddr . "\n" );
+ fputs( $mail, $header );
+ fputs( $mail, $body );
+ $result = pclose( $mail );
+ $this->doCallback(
+ ( $result == 0 ),
+ array( $toAddr ),
+ $this->cc,
+ $this->bcc,
+ $this->Subject,
+ $body,
+ $this->From
+ );
+ if ( $result != 0 ) {
+ throw new phpmailerException( $this->lang( 'execute' ) . $this->Sendmail, self::STOP_CRITICAL );
+ }
+ }
+ } else {
+ if ( !@$mail = popen( $sendmail, 'w' ) ) {
+ throw new phpmailerException( $this->lang( 'execute' ) . $this->Sendmail, self::STOP_CRITICAL );
+ }
+ fputs( $mail, $header );
+ fputs( $mail, $body );
+ $result = pclose( $mail );
+ $this->doCallback(
+ ( $result == 0 ),
+ $this->to,
+ $this->cc,
+ $this->bcc,
+ $this->Subject,
+ $body,
+ $this->From
+ );
+ if ( $result != 0 ) {
+ throw new phpmailerException( $this->lang( 'execute' ) . $this->Sendmail, self::STOP_CRITICAL );
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Perform a callback.
+ *
+ * @param boolean $isSent
+ * @param array $to
+ * @param array $cc
+ * @param array $bcc
+ * @param string $subject
+ * @param string $body
+ * @param string $from
+ */
+ protected function doCallback( $isSent, $to, $cc, $bcc, $subject, $body, $from ) {
+ if ( !empty( $this->action_function ) && is_callable( $this->action_function ) ) {
+ $params = array( $isSent, $to, $cc, $bcc, $subject, $body, $from );
+ call_user_func_array( $this->action_function, $params );
+ }
+ }
+
+ /**
+ * Send mail via SMTP.
+ * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
+ * Uses the PHPMailerSMTP class by default.
+ * @see PHPMailer::getSMTPInstance() to use a different class.
+ *
+ * @param string $header The message headers
+ * @param string $body The message body
+ *
+ * @throws phpmailerException
+ * @uses SMTP
+ * @access protected
+ * @return boolean
+ */
+ protected function smtpSend( $header, $body ) {
+ $bad_rcpt = array();
+ if ( !$this->smtpConnect( $this->SMTPOptions ) ) {
+ throw new phpmailerException( $this->lang( 'smtp_connect_failed' ), self::STOP_CRITICAL );
+ }
+ if ( '' == $this->Sender ) {
+ $smtp_from = $this->From;
+ } else {
+ $smtp_from = $this->Sender;
+ }
+ if ( !$this->smtp->mail( $smtp_from ) ) {
+ $this->setError( $this->lang( 'from_failed' ) . $smtp_from . ' : ' . implode( ',', $this->smtp->getError() ) );
+ throw new phpmailerException( $this->ErrorInfo, self::STOP_CRITICAL );
+ }
+
+ // Attempt to send to all recipients
+ foreach ( array( $this->to, $this->cc, $this->bcc ) as $togroup ) {
+ foreach ( $togroup as $to ) {
+ if ( !$this->smtp->recipient( $to[ 0 ] ) ) {
+ $error = $this->smtp->getError();
+ $bad_rcpt[] = array( 'to' => $to[ 0 ], 'error' => $error[ 'detail' ] );
+ $isSent = false;
+ } else {
+ $isSent = true;
+ }
+ $this->doCallback( $isSent, array( $to[ 0 ] ), array(), array(), $this->Subject, $body, $this->From );
+ }
+ }
+
+ // Only send the DATA command if we have viable recipients
+ if ( ( count( $this->all_recipients ) > count( $bad_rcpt ) ) and !$this->smtp->data( $header . $body ) ) {
+ throw new phpmailerException( $this->lang( 'data_not_accepted' ), self::STOP_CRITICAL );
+ }
+ if ( $this->SMTPKeepAlive ) {
+ $this->smtp->reset();
+ } else {
+ $this->smtp->quit();
+ $this->smtp->close();
+ }
+ //Create error message for any bad addresses
+ if ( count( $bad_rcpt ) > 0 ) {
+ $errstr = '';
+ foreach ( $bad_rcpt as $bad ) {
+ $errstr .= $bad[ 'to' ] . ': ' . $bad[ 'error' ];
+ }
+ throw new phpmailerException(
+ $this->lang( 'recipients_failed' ) . $errstr,
+ self::STOP_CONTINUE
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Initiate a connection to an SMTP server.
+ * Returns false if the operation failed.
+ *
+ * @param array $options An array of options compatible with stream_context_create()
+ *
+ * @return bool
+ * @throws null
+ * @throws phpmailerException
+ * @uses SMTP
+ * @access public
+ */
+ public function smtpConnect( $options = array() ) {
+ if ( is_null( $this->smtp ) ) {
+ $this->smtp = $this->getSMTPInstance();
+ }
+
+ // Already connected?
+ if ( $this->smtp->connected() ) {
+ return true;
+ }
+
+ $this->smtp->setTimeout( $this->Timeout );
+ $this->smtp->setDebugLevel( $this->SMTPDebug );
+ $this->smtp->setDebugOutput( $this->Debugoutput );
+ $this->smtp->setVerp( $this->do_verp );
+ $hosts = explode( ';', $this->Host );
+ $lastexception = null;
+
+ foreach ( $hosts as $hostentry ) {
+ $hostinfo = array();
+ if ( !preg_match( '/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim( $hostentry ), $hostinfo ) ) {
+ // Not a valid host entry
+ continue;
+ }
+ // $hostinfo[2]: optional ssl or tls prefix
+ // $hostinfo[3]: the hostname
+ // $hostinfo[4]: optional port number
+ // The host string prefix can temporarily override the current setting for SMTPSecure
+ // If it's not specified, the default value is used
+ $prefix = '';
+ $secure = $this->SMTPSecure;
+ $tls = ( $this->SMTPSecure == 'tls' );
+ if ( 'ssl' == $hostinfo[ 2 ] or ( '' == $hostinfo[ 2 ] and 'ssl' == $this->SMTPSecure ) ) {
+ $prefix = 'ssl://';
+ $tls = false; // Can't have SSL and TLS at the same time
+ $secure = 'ssl';
+ } elseif ( $hostinfo[ 2 ] == 'tls' ) {
+ $tls = true;
+ // tls doesn't use a prefix
+ $secure = 'tls';
+ }
+ //Do we need the OpenSSL extension?
+ $sslext = defined( 'OPENSSL_ALGO_SHA1' );
+ if ( 'tls' === $secure or 'ssl' === $secure ) {
+ //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
+ if ( !$sslext ) {
+ throw new phpmailerException( $this->lang( 'extension_missing' ) . 'openssl', self::STOP_CRITICAL );
+ }
+ }
+ $host = $hostinfo[ 3 ];
+ $port = $this->Port;
+ $tport = (integer)$hostinfo[ 4 ];
+ if ( $tport > 0 and $tport < 65536 ) {
+ $port = $tport;
+ }
+ if ( $this->smtp->connect( $prefix . $host, $port, $this->Timeout, $options ) ) {
+ try {
+ if ( $this->Helo ) {
+ $hello = $this->Helo;
+ } else {
+ $hello = $this->serverHostname();
+ }
+ $this->smtp->hello( $hello );
+ //Automatically enable TLS encryption if:
+ // * it's not disabled
+ // * we have openssl extension
+ // * we are not already using SSL
+ // * the server offers STARTTLS
+ if ( $this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt( 'STARTTLS' ) ) {
+ $tls = true;
+ }
+ if ( $tls ) {
+ if ( !$this->smtp->startTLS() ) {
+ throw new phpmailerException( $this->lang( 'connect_host' ) );
+ }
+ // We must resend HELO after tls negotiation
+ $this->smtp->hello( $hello );
+ }
+ if ( $this->SMTPAuth ) {
+ if ( !$this->smtp->authenticate(
+ $this->Username,
+ $this->Password,
+ $this->AuthType,
+ $this->Realm,
+ $this->Workstation
+ )
+ ) {
+ throw new phpmailerException( $this->lang( 'authenticate' ) );
+ }
+ }
+
+ return true;
+ } catch ( phpmailerException $exc ) {
+ $lastexception = $exc;
+ $this->edebug( $exc->getMessage() );
+ // We must have connected, but then failed TLS or Auth, so close connection nicely
+ $this->smtp->quit();
+ }
+ }
+ }
+ // If we get here, all connection attempts have failed, so close connection hard
+ $this->smtp->close();
+ // As we've caught all exceptions, just report whatever the last one was
+ if ( $this->exceptions and !is_null( $lastexception ) ) {
+ throw $lastexception;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get an instance to use for SMTP operations.
+ * Override this function to load your own SMTP implementation
+ * @return SMTP
+ */
+ public function getSMTPInstance() {
+ if ( !is_object( $this->smtp ) ) {
+ $this->smtp = new SMTP;
+ }
+
+ return $this->smtp;
+ }
+
+ /**
+ * Send mail using the PHP mail() function.
+ *
+ * @param string $header The message headers
+ * @param string $body The message body
+ *
+ * @link http://www.php.net/manual/en/book.mail.php
+ * @throws phpmailerException
+ * @access protected
+ * @return boolean
+ */
+ protected function mailSend( $header, $body ) {
+ $toArr = array();
+ foreach ( $this->to as $toaddr ) {
+ $toArr[] = $this->addrFormat( $toaddr );
+ }
+ $to = implode( ', ', $toArr );
+
+ if ( empty( $this->Sender ) ) {
+ $params = ' ';
+ } else {
+ $params = sprintf( '-f%s', $this->Sender );
+ }
+ if ( $this->Sender != '' and !ini_get( 'safe_mode' ) ) {
+ $old_from = ini_get( 'sendmail_from' );
+ ini_set( 'sendmail_from', $this->Sender );
+ }
+ $result = false;
+ if ( $this->SingleTo && count( $toArr ) > 1 ) {
+ foreach ( $toArr as $toAddr ) {
+ $result = $this->mailPassthru( $toAddr, $this->Subject, $body, $header, $params );
+ $this->doCallback( $result, array( $toAddr ), $this->cc, $this->bcc, $this->Subject, $body, $this->From );
+ }
+ } else {
+ $result = $this->mailPassthru( $to, $this->Subject, $body, $header, $params );
+ $this->doCallback( $result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From );
+ }
+ if ( isset( $old_from ) ) {
+ ini_set( 'sendmail_from', $old_from );
+ }
+ if ( !$result ) {
+ throw new phpmailerException( $this->lang( 'instantiate' ), self::STOP_CRITICAL );
+ }
+
+ return true;
+ }
+
+ /**
+ * Call mail() in a safe_mode-aware fashion.
+ * Also, unless sendmail_path points to sendmail (or something that
+ * claims to be sendmail), don't pass params (not a perfect fix,
+ * but it will do)
+ *
+ * @param string $to To
+ * @param string $subject Subject
+ * @param string $body Message Body
+ * @param string $header Additional Header(s)
+ * @param string $params Params
+ *
+ * @access private
+ * @return boolean
+ */
+ private function mailPassthru( $to, $subject, $body, $header, $params ) {
+ //Check overloading of mail function to avoid double-encoding
+ if ( ini_get( 'mbstring.func_overload' ) & 1 ) {
+ $subject = $this->secureHeader( $subject );
+ } else {
+ $subject = $this->encodeHeader( $this->secureHeader( $subject ) );
+ }
+ if ( ini_get( 'safe_mode' ) || !( $this->UseSendmailOptions ) ) {
+ $result = @mail( $to, $subject, $body, $header );
+ } else {
+ $result = @mail( $to, $subject, $body, $header, $params );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get the array of strings for the current language.
+ * @return array
+ */
+ public function getTranslations() {
+ return $this->language;
+ }
+
+ /**
+ * Returns the whole MIME message.
+ * Includes complete headers and body.
+ * Only valid post preSend().
+ * @see PHPMailer::preSend()
+ * @access public
+ * @return string
+ */
+ public function getSentMIMEMessage() {
+ return rtrim( $this->MIMEHeader . $this->mailHeader, "\n\r" ) . self::CRLF . self::CRLF . $this->MIMEBody;
+ }
+
+ /**
+ * Add an attachment from a path on the filesystem.
+ * Returns false if the file could not be found or read.
+ *
+ * @param string $path Path to the attachment.
+ * @param string $name Overrides the attachment name.
+ * @param string $encoding File encoding (see $Encoding).
+ * @param string $type File extension (MIME) type.
+ * @param string $disposition Disposition to use
+ *
+ * @throws phpmailerException
+ * @return boolean
+ */
+ public function addAttachment( $path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment' ) {
+ try {
+ if ( !@is_file( $path ) ) {
+ throw new phpmailerException( $this->lang( 'file_access' ) . $path, self::STOP_CONTINUE );
+ }
+
+ // If a MIME type is not specified, try to work it out from the file name
+ if ( $type == '' ) {
+ $type = self::filenameToType( $path );
+ }
+
+ $filename = basename( $path );
+ if ( $name == '' ) {
+ $name = $filename;
+ }
+
+ $this->attachment[] = array(
+ 0 => $path,
+ 1 => $filename,
+ 2 => $name,
+ 3 => $encoding,
+ 4 => $type,
+ 5 => false, // isStringAttachment
+ 6 => $disposition,
+ 7 => 0
+ );
+
+ } catch ( phpmailerException $exc ) {
+ $this->setError( $exc->getMessage() );
+ $this->edebug( $exc->getMessage() );
+ if ( $this->exceptions ) {
+ throw $exc;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Map a file name to a MIME type.
+ * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
+ *
+ * @param string $filename A file name or full path, does not need to exist as a file
+ *
+ * @return string
+ * @static
+ */
+ public static function filenameToType( $filename ) {
+ // In case the path is a URL, strip any query string before getting extension
+ $qpos = strpos( $filename, '?' );
+ if ( false !== $qpos ) {
+ $filename = substr( $filename, 0, $qpos );
+ }
+ $pathinfo = self::mb_pathinfo( $filename );
+
+ return self::_mime_types( $pathinfo[ 'extension' ] );
+ }
+
+ /**
+ * Multi-byte-safe pathinfo replacement.
+ * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
+ * Works similarly to the one in PHP >= 5.2.0
+ * @link http://www.php.net/manual/en/function.pathinfo.php#107461
+ *
+ * @param string $path A filename or path, does not need to exist as a file
+ * @param integer|string $options Either a PATHINFO_* constant,
+ * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
+ *
+ * @return string|array
+ * @static
+ */
+ public static function mb_pathinfo( $path, $options = null ) {
+ $ret = array( 'dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '' );
+ $pathinfo = array();
+ if ( preg_match( '%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo ) ) {
+ if ( array_key_exists( 1, $pathinfo ) ) {
+ $ret[ 'dirname' ] = $pathinfo[ 1 ];
+ }
+ if ( array_key_exists( 2, $pathinfo ) ) {
+ $ret[ 'basename' ] = $pathinfo[ 2 ];
+ }
+ if ( array_key_exists( 5, $pathinfo ) ) {
+ $ret[ 'extension' ] = $pathinfo[ 5 ];
+ }
+ if ( array_key_exists( 3, $pathinfo ) ) {
+ $ret[ 'filename' ] = $pathinfo[ 3 ];
+ }
+ }
+ switch ( $options ) {
+ case PATHINFO_DIRNAME:
+ case 'dirname':
+ return $ret[ 'dirname' ];
+ case PATHINFO_BASENAME:
+ case 'basename':
+ return $ret[ 'basename' ];
+ case PATHINFO_EXTENSION:
+ case 'extension':
+ return $ret[ 'extension' ];
+ case PATHINFO_FILENAME:
+ case 'filename':
+ return $ret[ 'filename' ];
+ default:
+ return $ret;
+ }
+ }
+
+ /**
+ * Get the MIME type for a file extension.
+ *
+ * @param string $ext File extension
+ *
+ * @access public
+ * @return string MIME type of file.
+ * @static
+ */
+ public static function _mime_types( $ext = '' ) {
+ $mimes = array(
+ 'xl' => 'application/excel',
+ 'js' => 'application/javascript',
+ 'hqx' => 'application/mac-binhex40',
+ 'cpt' => 'application/mac-compactpro',
+ 'bin' => 'application/macbinary',
+ 'doc' => 'application/msword',
+ 'word' => 'application/msword',
+ 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+ 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
+ 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+ 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+ 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
+ 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
+ 'class' => 'application/octet-stream',
+ 'dll' => 'application/octet-stream',
+ 'dms' => 'application/octet-stream',
+ 'exe' => 'application/octet-stream',
+ 'lha' => 'application/octet-stream',
+ 'lzh' => 'application/octet-stream',
+ 'psd' => 'application/octet-stream',
+ 'sea' => 'application/octet-stream',
+ 'so' => 'application/octet-stream',
+ 'oda' => 'application/oda',
+ 'pdf' => 'application/pdf',
+ 'ai' => 'application/postscript',
+ 'eps' => 'application/postscript',
+ 'ps' => 'application/postscript',
+ 'smi' => 'application/smil',
+ 'smil' => 'application/smil',
+ 'mif' => 'application/vnd.mif',
+ 'xls' => 'application/vnd.ms-excel',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+ 'wbxml' => 'application/vnd.wap.wbxml',
+ 'wmlc' => 'application/vnd.wap.wmlc',
+ 'dcr' => 'application/x-director',
+ 'dir' => 'application/x-director',
+ 'dxr' => 'application/x-director',
+ 'dvi' => 'application/x-dvi',
+ 'gtar' => 'application/x-gtar',
+ 'php3' => 'application/x-httpd-php',
+ 'php4' => 'application/x-httpd-php',
+ 'php' => 'application/x-httpd-php',
+ 'phtml' => 'application/x-httpd-php',
+ 'phps' => 'application/x-httpd-php-source',
+ 'swf' => 'application/x-shockwave-flash',
+ 'sit' => 'application/x-stuffit',
+ 'tar' => 'application/x-tar',
+ 'tgz' => 'application/x-tar',
+ 'xht' => 'application/xhtml+xml',
+ 'xhtml' => 'application/xhtml+xml',
+ 'zip' => 'application/zip',
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mp2' => 'audio/mpeg',
+ 'mp3' => 'audio/mpeg',
+ 'mpga' => 'audio/mpeg',
+ 'aif' => 'audio/x-aiff',
+ 'aifc' => 'audio/x-aiff',
+ 'aiff' => 'audio/x-aiff',
+ 'ram' => 'audio/x-pn-realaudio',
+ 'rm' => 'audio/x-pn-realaudio',
+ 'rpm' => 'audio/x-pn-realaudio-plugin',
+ 'ra' => 'audio/x-realaudio',
+ 'wav' => 'audio/x-wav',
+ 'bmp' => 'image/bmp',
+ 'gif' => 'image/gif',
+ 'jpeg' => 'image/jpeg',
+ 'jpe' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'png' => 'image/png',
+ 'tiff' => 'image/tiff',
+ 'tif' => 'image/tiff',
+ 'eml' => 'message/rfc822',
+ 'css' => 'text/css',
+ 'html' => 'text/html',
+ 'htm' => 'text/html',
+ 'shtml' => 'text/html',
+ 'log' => 'text/plain',
+ 'text' => 'text/plain',
+ 'txt' => 'text/plain',
+ 'rtx' => 'text/richtext',
+ 'rtf' => 'text/rtf',
+ 'vcf' => 'text/vcard',
+ 'vcard' => 'text/vcard',
+ 'xml' => 'text/xml',
+ 'xsl' => 'text/xml',
+ 'mpeg' => 'video/mpeg',
+ 'mpe' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'mov' => 'video/quicktime',
+ 'qt' => 'video/quicktime',
+ 'rv' => 'video/vnd.rn-realvideo',
+ 'avi' => 'video/x-msvideo',
+ 'movie' => 'video/x-sgi-movie'
+ );
+ if ( array_key_exists( strtolower( $ext ), $mimes ) ) {
+ return $mimes[ strtolower( $ext ) ];
+ }
+
+ return 'application/octet-stream';
+ }
+
+ /**
+ * Return the array of attachments.
+ * @return array
+ */
+ public function getAttachments() {
+ return $this->attachment;
+ }
+
+ /**
+ * Backward compatibility wrapper for an old QP encoding function that was removed.
+ * @see PHPMailer::encodeQP()
+ * @access public
+ *
+ * @param string $string
+ * @param integer $line_max
+ * @param boolean $space_conv
+ *
+ * @return string
+ * @deprecated Use encodeQP instead.
+ */
+ public function encodeQPphp(
+ $string,
+ $line_max = 76,
+ /** @noinspection PhpUnusedParameterInspection */
+ $space_conv = false
+ ) {
+ return $this->encodeQP( $string, $line_max );
+ }
+
+ /**
+ * Add a string or binary attachment (non-filesystem).
+ * This method can be used to attach ascii or binary data,
+ * such as a BLOB record from a database.
+ *
+ * @param string $string String attachment data.
+ * @param string $filename Name of the attachment.
+ * @param string $encoding File encoding (see $Encoding).
+ * @param string $type File extension (MIME) type.
+ * @param string $disposition Disposition to use
+ *
+ * @return void
+ */
+ public function addStringAttachment(
+ $string,
+ $filename,
+ $encoding = 'base64',
+ $type = '',
+ $disposition = 'attachment'
+ ) {
+ // If a MIME type is not specified, try to work it out from the file name
+ if ( $type == '' ) {
+ $type = self::filenameToType( $filename );
+ }
+ // Append to $attachment array
+ $this->attachment[] = array(
+ 0 => $string,
+ 1 => $filename,
+ 2 => basename( $filename ),
+ 3 => $encoding,
+ 4 => $type,
+ 5 => true, // isStringAttachment
+ 6 => $disposition,
+ 7 => 0
+ );
+ }
+
+ /**
+ * Clear all To recipients.
+ * @return void
+ */
+ public function clearAddresses() {
+ foreach ( $this->to as $to ) {
+ unset( $this->all_recipients[ strtolower( $to[ 0 ] ) ] );
+ }
+ $this->to = array();
+ $this->clearQueuedAddresses( 'to' );
+ }
+
+ /**
+ * Clear queued addresses of given kind.
+ * @access protected
+ *
+ * @param string $kind 'to', 'cc', or 'bcc'
+ *
+ * @return void
+ */
+ public function clearQueuedAddresses( $kind ) {
+ $RecipientsQueue = $this->RecipientsQueue;
+ foreach ( $RecipientsQueue as $address => $params ) {
+ if ( $params[ 0 ] == $kind ) {
+ unset( $this->RecipientsQueue[ $address ] );
+ }
+ }
+ }
+
+ /**
+ * Clear all CC recipients.
+ * @return void
+ */
+ public function clearCCs() {
+ foreach ( $this->cc as $cc ) {
+ unset( $this->all_recipients[ strtolower( $cc[ 0 ] ) ] );
+ }
+ $this->cc = array();
+ $this->clearQueuedAddresses( 'cc' );
+ }
+
+ /**
+ * Clear all BCC recipients.
+ * @return void
+ */
+ public function clearBCCs() {
+ foreach ( $this->bcc as $bcc ) {
+ unset( $this->all_recipients[ strtolower( $bcc[ 0 ] ) ] );
+ }
+ $this->bcc = array();
+ $this->clearQueuedAddresses( 'bcc' );
+ }
+
+ /**
+ * Clear all ReplyTo recipients.
+ * @return void
+ */
+ public function clearReplyTos() {
+ $this->ReplyTo = array();
+ $this->ReplyToQueue = array();
+ }
+
+ /**
+ * Clear all recipient types.
+ * @return void
+ */
+ public function clearAllRecipients() {
+ $this->to = array();
+ $this->cc = array();
+ $this->bcc = array();
+ $this->all_recipients = array();
+ $this->RecipientsQueue = array();
+ }
+
+ /**
+ * Clear all filesystem, string, and binary attachments.
+ * @return void
+ */
+ public function clearAttachments() {
+ $this->attachment = array();
+ }
+
+ /**
+ * Clear all custom headers.
+ * @return void
+ */
+ public function clearCustomHeaders() {
+ $this->CustomHeader = array();
+ }
+
+ /**
+ * Add a custom header.
+ * $name value can be overloaded to contain
+ * both header name and value (name:value)
+ * @access public
+ *
+ * @param string $name Custom header name
+ * @param string $value Header value
+ *
+ * @return void
+ */
+ public function addCustomHeader( $name, $value = null ) {
+ if ( $value === null ) {
+ // Value passed in as name:value
+ $this->CustomHeader[] = explode( ':', $name, 2 );
+ } else {
+ $this->CustomHeader[] = array( $name, $value );
+ }
+ }
+
+ /**
+ * Returns all custom headers.
+ * @return array
+ */
+ public function getCustomHeaders() {
+ return $this->CustomHeader;
+ }
+
+ /**
+ * Create a message from an HTML string.
+ * Automatically makes modifications for inline images and backgrounds
+ * and creates a plain-text version by converting the HTML.
+ * Overwrites any existing values in $this->Body and $this->AltBody
+ * @access public
+ *
+ * @param string $message HTML message string
+ * @param string $basedir baseline directory for path
+ * @param boolean|callable $advanced Whether to use the internal HTML to text converter
+ * or your own custom converter @see PHPMailer::html2text()
+ *
+ * @return string $message
+ */
+ public function msgHTML( $message, $basedir = '', $advanced = false ) {
+ preg_match_all( '/(src|background)=["\'](.*)["\']/Ui', $message, $images );
+ if ( array_key_exists( 2, $images ) ) {
+ foreach ( $images[ 2 ] as $imgindex => $url ) {
+ // Convert data URIs into embedded images
+ if ( preg_match( '#^data:(image[^;,]*)(;base64)?,#', $url, $match ) ) {
+ $data = substr( $url, strpos( $url, ',' ) );
+ if ( $match[ 2 ] ) {
+ $data = base64_decode( $data );
+ } else {
+ $data = rawurldecode( $data );
+ }
+ $cid = md5( $url ) . '@phpmailer.0'; // RFC2392 S 2
+ if ( $this->addStringEmbeddedImage( $data, $cid, 'embed' . $imgindex, 'base64', $match[ 1 ] ) ) {
+ $message = str_replace(
+ $images[ 0 ][ $imgindex ],
+ $images[ 1 ][ $imgindex ] . '="cid:' . $cid . '"',
+ $message
+ );
+ }
+ } elseif ( substr( $url, 0, 4 ) !== 'cid:' && !preg_match( '#^[a-z][a-z0-9+.-]*://#i', $url ) ) {
+ // Do not change urls for absolute images (thanks to corvuscorax)
+ // Do not change urls that are already inline images
+ $filename = basename( $url );
+ $directory = dirname( $url );
+ if ( $directory == '.' ) {
+ $directory = '';
+ }
+ $cid = md5( $url ) . '@phpmailer.0'; // RFC2392 S 2
+ if ( strlen( $basedir ) > 1 && substr( $basedir, -1 ) != '/' ) {
+ $basedir .= '/';
+ }
+ if ( strlen( $directory ) > 1 && substr( $directory, -1 ) != '/' ) {
+ $directory .= '/';
+ }
+ if ( $this->addEmbeddedImage(
+ $basedir . $directory . $filename,
+ $cid,
+ $filename,
+ 'base64',
+ self::_mime_types( (string)self::mb_pathinfo( $filename, PATHINFO_EXTENSION ) )
+ )
+ ) {
+ $message = preg_replace(
+ '/' . $images[ 1 ][ $imgindex ] . '=["\']' . preg_quote( $url, '/' ) . '["\']/Ui',
+ $images[ 1 ][ $imgindex ] . '="cid:' . $cid . '"',
+ $message
+ );
+ }
+ }
+ }
+ }
+ $this->isHTML( true );
+ // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
+ $this->Body = $this->normalizeBreaks( $message );
+ $this->AltBody = $this->normalizeBreaks( $this->html2text( $message, $advanced ) );
+ if ( !$this->alternativeExists() ) {
+ $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
+ self::CRLF . self::CRLF;
+ }
+
+ return $this->Body;
+ }
+
+ /**
+ * Add an embedded stringified attachment.
+ * This can include images, sounds, and just about any other document type.
+ * Be sure to set the $type to an image type for images:
+ * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
+ *
+ * @param string $string The attachment binary data.
+ * @param string $cid Content ID of the attachment; Use this to reference
+ * the content when using an embedded image in HTML.
+ * @param string $name
+ * @param string $encoding File encoding (see $Encoding).
+ * @param string $type MIME type.
+ * @param string $disposition Disposition to use
+ *
+ * @return boolean True on successfully adding an attachment
+ */
+ public function addStringEmbeddedImage(
+ $string,
+ $cid,
+ $name = '',
+ $encoding = 'base64',
+ $type = '',
+ $disposition = 'inline'
+ ) {
+ // If a MIME type is not specified, try to work it out from the name
+ if ( $type == '' and !empty( $name ) ) {
+ $type = self::filenameToType( $name );
+ }
+
+ // Append to $attachment array
+ $this->attachment[] = array(
+ 0 => $string,
+ 1 => $name,
+ 2 => $name,
+ 3 => $encoding,
+ 4 => $type,
+ 5 => true, // isStringAttachment
+ 6 => $disposition,
+ 7 => $cid
+ );
+
+ return true;
+ }
+
+ /**
+ * Add an embedded (inline) attachment from a file.
+ * This can include images, sounds, and just about any other document type.
+ * These differ from 'regular' attachments in that they are intended to be
+ * displayed inline with the message, not just attached for download.
+ * This is used in HTML messages that embed the images
+ * the HTML refers to using the $cid value.
+ *
+ * @param string $path Path to the attachment.
+ * @param string $cid Content ID of the attachment; Use this to reference
+ * the content when using an embedded image in HTML.
+ * @param string $name Overrides the attachment name.
+ * @param string $encoding File encoding (see $Encoding).
+ * @param string $type File MIME type.
+ * @param string $disposition Disposition to use
+ *
+ * @return boolean True on successfully adding an attachment
+ */
+ public function addEmbeddedImage( $path, $cid, $name = '', $encoding = 'base64', $type = '',
+ $disposition = 'inline' ) {
+ if ( !@is_file( $path ) ) {
+ $this->setError( $this->lang( 'file_access' ) . $path );
+
+ return false;
+ }
+
+ // If a MIME type is not specified, try to work it out from the file name
+ if ( $type == '' ) {
+ $type = self::filenameToType( $path );
+ }
+
+ $filename = basename( $path );
+ if ( $name == '' ) {
+ $name = $filename;
+ }
+
+ // Append to $attachment array
+ $this->attachment[] = array(
+ 0 => $path,
+ 1 => $filename,
+ 2 => $name,
+ 3 => $encoding,
+ 4 => $type,
+ 5 => false, // isStringAttachment
+ 6 => $disposition,
+ 7 => $cid
+ );
+
+ return true;
+ }
+
+ /**
+ * Sets message type to HTML or plain.
+ *
+ * @param boolean $isHtml True for HTML mode.
+ *
+ * @return void
+ */
+ public function isHTML( $isHtml = true ) {
+ if ( $isHtml ) {
+ $this->ContentType = 'text/html';
+ } else {
+ $this->ContentType = 'text/plain';
+ }
+ }
+
+ /**
+ * Normalize line breaks in a string.
+ * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
+ * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
+ *
+ * @param string $text
+ * @param string $breaktype What kind of line break to use, defaults to CRLF
+ *
+ * @return string
+ * @access public
+ * @static
+ */
+ public static function normalizeBreaks( $text, $breaktype = "\r\n" ) {
+ return preg_replace( '/(\r\n|\r|\n)/ms', $breaktype, $text );
+ }
+
+ /**
+ * Convert an HTML string into plain text.
+ * This is used by msgHTML().
+ * Note - older versions of this function used a bundled advanced converter
+ * which was been removed for license reasons in #232
+ * Example usage:
+ *
+ * // Use default conversion
+ * $plain = $mail->html2text($html);
+ * // Use your own custom converter
+ * $plain = $mail->html2text($html, function($html) {
+ * $converter = new MyHtml2text($html);
+ * return $converter->get_text();
+ * });
+ *
+ *
+ * @param string $html The HTML text to convert
+ * @param boolean|callable $advanced Any boolean value to use the internal converter,
+ * or provide your own callable for custom conversion.
+ *
+ * @return string
+ */
+ public function html2text( $html, $advanced = false ) {
+ if ( is_callable( $advanced ) ) {
+ return call_user_func( $advanced, $html );
+ }
+
+ return html_entity_decode(
+ trim( strip_tags( preg_replace( '/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html ) ) ),
+ ENT_QUOTES,
+ $this->CharSet
+ );
+ }
+
+ /**
+ * Set or reset instance properties.
+ * You should avoid this function - it's more verbose, less efficient, more error-prone and
+ * harder to debug than setting properties directly.
+ * Usage Example:
+ * `$mail->set('SMTPSecure', 'tls');`
+ * is the same as:
+ * `$mail->SMTPSecure = 'tls';`
+ * @access public
+ *
+ * @param string $name The property name to set
+ * @param mixed $value The value to set the property to
+ *
+ * @return boolean
+ */
+ public function set( $name, $value = '' ) {
+ if ( property_exists( $this, $name ) ) {
+ $this->$name = $value;
+
+ return true;
+ } else {
+ $this->setError( $this->lang( 'variable_set' ) . $name );
+
+ return false;
+ }
+ }
+
+ /**
+ * Set the public and private key files and password for S/MIME signing.
+ * @access public
+ *
+ * @param string $cert_filename
+ * @param string $key_filename
+ * @param string $key_pass Password for private key
+ * @param string $extracerts_filename Optional path to chain certificate
+ */
+ public function sign( $cert_filename, $key_filename, $key_pass, $extracerts_filename = '' ) {
+ $this->sign_cert_file = $cert_filename;
+ $this->sign_key_file = $key_filename;
+ $this->sign_key_pass = $key_pass;
+ $this->sign_extracerts_file = $extracerts_filename;
+ }
+
+ /**
+ * Allows for public read access to 'to' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getToAddresses() {
+ return $this->to;
+ }
+
+ /**
+ * Allows for public read access to 'cc' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getCcAddresses() {
+ return $this->cc;
+ }
+
+ /**
+ * Allows for public read access to 'bcc' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getBccAddresses() {
+ return $this->bcc;
+ }
+
+ /**
+ * Allows for public read access to 'ReplyTo' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getReplyToAddresses() {
+ return $this->ReplyTo;
+ }
+
+ /**
+ * Allows for public read access to 'all_recipients' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getAllRecipientAddresses() {
+ return $this->all_recipients;
+ }
+
+ /**
+ * Add an address to one of the recipient arrays or to the ReplyTo array.
+ * Addresses that have been added already return false, but do not throw exceptions.
+ *
+ * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
+ * @param string $address The email address to send, resp. to reply to
+ * @param string $name
+ *
+ * @throws phpmailerException
+ * @return boolean true on success, false if address already used or invalid in some way
+ * @access protected
+ */
+ protected function addAnAddress( $kind, $address, $name = '' ) {
+ if ( !in_array( $kind, array( 'to', 'cc', 'bcc', 'Reply-To' ) ) ) {
+ $error_message = $this->lang( 'Invalid recipient kind: ' ) . $kind;
+ $this->setError( $error_message );
+ $this->edebug( $error_message );
+ if ( $this->exceptions ) {
+ throw new phpmailerException( $error_message );
+ }
+
+ return false;
+ }
+ if ( !$this->validateAddress( $address ) ) {
+ $error_message = $this->lang( 'invalid_address' ) . " (addAnAddress $kind): $address";
+ $this->setError( $error_message );
+ $this->edebug( $error_message );
+ if ( $this->exceptions ) {
+ throw new phpmailerException( $error_message );
+ }
+
+ return false;
+ }
+ if ( $kind != 'Reply-To' ) {
+ if ( !array_key_exists( strtolower( $address ), $this->all_recipients ) ) {
+ array_push( $this->$kind, array( $address, $name ) );
+ $this->all_recipients[ strtolower( $address ) ] = true;
+
+ return true;
+ }
+ } else {
+ if ( !array_key_exists( strtolower( $address ), $this->ReplyTo ) ) {
+ $this->ReplyTo[ strtolower( $address ) ] = array( $address, $name );
+
+ return true;
+ }
+ }
+
+ return false;
+ }
}
/**
@@ -3884,13 +3885,13 @@ class PHPMailer {
* @package PHPMailer
*/
class phpmailerException extends Exception {
- /**
- * Prettify error message output
- * @return string
- */
- public function errorMessage() {
- $errorMsg = '' . $this->getMessage() . "
\n";
+ /**
+ * Prettify error message output
+ * @return string
+ */
+ public function errorMessage() {
+ $errorMsg = '' . $this->getMessage() . "
\n";
- return $errorMsg;
- }
+ return $errorMsg;
+ }
}
diff --git a/src/lib/mail/class.smtp.php b/src/lib/mail/class.smtp.php
index af41bf2..c0cf857 100644
--- a/src/lib/mail/class.smtp.php
+++ b/src/lib/mail/class.smtp.php
@@ -25,1190 +25,1190 @@
* @author Marcus Bointon
*/
class SMTP {
- /**
- * The PHPMailer SMTP version number.
- * @var string
- */
- const VERSION = '5.2.14';
-
- /**
- * SMTP line break constant.
- * @var string
- */
- const CRLF = "\r\n";
-
- /**
- * The SMTP port to use if one is not specified.
- * @var integer
- */
- const DEFAULT_SMTP_PORT = 25;
-
- /**
- * The maximum line length allowed by RFC 2822 section 2.1.1
- * @var integer
- */
- const MAX_LINE_LENGTH = 998;
-
- /**
- * Debug level for no output
- */
- const DEBUG_OFF = 0;
-
- /**
- * Debug level to show client -> server messages
- */
- const DEBUG_CLIENT = 1;
-
- /**
- * Debug level to show client -> server and server -> client messages
- */
- const DEBUG_SERVER = 2;
-
- /**
- * Debug level to show connection status, client -> server and server -> client messages
- */
- const DEBUG_CONNECTION = 3;
-
- /**
- * Debug level to show all messages
- */
- const DEBUG_LOWLEVEL = 4;
-
- /**
- * The PHPMailer SMTP Version number.
- * @var string
- * @deprecated Use the `VERSION` constant instead
- * @see SMTP::VERSION
- */
- public $Version = '5.2.14';
-
- /**
- * SMTP server port number.
- * @var integer
- * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead
- * @see SMTP::DEFAULT_SMTP_PORT
- */
- public $SMTP_PORT = 25;
-
- /**
- * SMTP reply line ending.
- * @var string
- * @deprecated Use the `CRLF` constant instead
- * @see SMTP::CRLF
- */
- public $CRLF = "\r\n";
-
- /**
- * Debug output level.
- * Options:
- * * self::DEBUG_OFF (`0`) No debug output, default
- * * self::DEBUG_CLIENT (`1`) Client commands
- * * self::DEBUG_SERVER (`2`) Client commands and server responses
- * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
- * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages
- * @var integer
- */
- public $do_debug = self::DEBUG_OFF;
-
- /**
- * How to handle debug output.
- * Options:
- * * `echo` Output plain-text as-is, appropriate for CLI
- * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output
- * * `error_log` Output to error log as configured in php.ini
- *
- * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
- *
- * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
- *
- * @var string|callable
- */
- public $Debugoutput = 'echo';
-
- /**
- * Whether to use VERP.
- * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
- * @link http://www.postfix.org/VERP_README.html Info on VERP
- * @var boolean
- */
- public $do_verp = false;
-
- /**
- * The timeout value for connection, in seconds.
- * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
- * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
- * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
- * @var integer
- */
- public $Timeout = 300;
-
- /**
- * How long to wait for commands to complete, in seconds.
- * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
- * @var integer
- */
- public $Timelimit = 300;
-
- /**
- * The socket for the server connection.
- * @var resource
- */
- protected $smtp_conn;
-
- /**
- * Error information, if any, for the last SMTP command.
- * @var array
- */
- protected $error = array(
- 'error' => '',
- 'detail' => '',
- 'smtp_code' => '',
- 'smtp_code_ex' => ''
- );
-
- /**
- * The reply the server sent to us for HELO.
- * If null, no HELO string has yet been received.
- * @var string|null
- */
- protected $helo_rply = null;
-
- /**
- * The set of SMTP extensions sent in reply to EHLO command.
- * Indexes of the array are extension names.
- * Value at index 'HELO' or 'EHLO' (according to command that was sent)
- * represents the server name. In case of HELO it is the only element of the array.
- * Other values can be boolean TRUE or an array containing extension options.
- * If null, no HELO/EHLO string has yet been received.
- * @var array|null
- */
- protected $server_caps = null;
-
- /**
- * The most recent reply received from the server.
- * @var string
- */
- protected $last_reply = '';
-
- /**
- * Connect to an SMTP server.
- *
- * @param string $host SMTP server IP or host name
- * @param integer $port The port number to connect to
- * @param integer $timeout How long to wait for the connection to open
- * @param array $options An array of options for stream_context_create()
- *
- * @access public
- * @return boolean
- */
- public function connect( $host, $port = null, $timeout = 30, $options = array() ) {
- static $streamok;
- //This is enabled by default since 5.0.0 but some providers disable it
- //Check this once and cache the result
- if ( is_null( $streamok ) ) {
- $streamok = function_exists( 'stream_socket_client' );
- }
- // Clear errors to avoid confusion
- $this->setError( '' );
- // Make sure we are __not__ connected
- if ( $this->connected() ) {
- // Already connected, generate error
- $this->setError( 'Already connected to a server' );
-
- return false;
- }
- if ( empty( $port ) ) {
- $port = self::DEFAULT_SMTP_PORT;
- }
- // Connect to the SMTP server
- $this->edebug(
- "Connection: opening to $host:$port, timeout=$timeout, options=" . var_export( $options, true ),
- self::DEBUG_CONNECTION
- );
- $errno = 0;
- $errstr = '';
- if ( $streamok ) {
- $socket_context = stream_context_create( $options );
- //Suppress errors; connection failures are handled at a higher level
- $this->smtp_conn = @stream_socket_client(
- $host . ":" . $port,
- $errno,
- $errstr,
- $timeout,
- STREAM_CLIENT_CONNECT,
- $socket_context
- );
- } else {
- //Fall back to fsockopen which should work in more places, but is missing some features
- $this->edebug(
- "Connection: stream_socket_client not available, falling back to fsockopen",
- self::DEBUG_CONNECTION
- );
- $this->smtp_conn = fsockopen(
- $host,
- $port,
- $errno,
- $errstr,
- $timeout
- );
- }
- // Verify we connected properly
- if ( ! is_resource( $this->smtp_conn ) ) {
- $this->setError(
- 'Failed to connect to server',
- $errno,
- $errstr
- );
- $this->edebug(
- 'SMTP ERROR: ' . $this->error['error']
- . ": $errstr ($errno)",
- self::DEBUG_CLIENT
- );
-
- return false;
- }
- $this->edebug( 'Connection: opened', self::DEBUG_CONNECTION );
- // SMTP server can take longer to respond, give longer timeout for first read
- // Windows does not have support for this timeout function
- if ( substr( PHP_OS, 0, 3 ) != 'WIN' ) {
- $max = ini_get( 'max_execution_time' );
- // Don't bother if unlimited
- if ( $max != 0 && $timeout > $max ) {
- @set_time_limit( $timeout );
- }
- stream_set_timeout( $this->smtp_conn, $timeout, 0 );
- }
- // Get any announcement
- $announce = $this->get_lines();
- $this->edebug( 'SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER );
-
- return true;
- }
-
- /**
- * Check connection state.
- * @access public
- * @return boolean True if connected.
- */
- public function connected() {
- if ( is_resource( $this->smtp_conn ) ) {
- $sock_status = stream_get_meta_data( $this->smtp_conn );
- if ( $sock_status['eof'] ) {
- // The socket is valid but we are not connected
- $this->edebug(
- 'SMTP NOTICE: EOF caught while checking if connected',
- self::DEBUG_CLIENT
- );
- $this->close();
-
- return false;
- }
-
- return true; // everything looks good
- }
-
- return false;
- }
-
- /**
- * Output debugging info via a user-selected method.
- * @see SMTP::$Debugoutput
- * @see SMTP::$do_debug
- *
- * @param string $str Debug string to output
- * @param integer $level The debug level of this message; see DEBUG_* constants
- *
- * @return void
- */
- protected function edebug( $str, $level = 0 ) {
- if ( $level > $this->do_debug ) {
- return;
- }
- //Avoid clash with built-in function names
- if ( ! in_array( $this->Debugoutput, array(
- 'error_log',
- 'html',
- 'echo'
- ) ) and is_callable( $this->Debugoutput ) ) {
- call_user_func( $this->Debugoutput, $str, $this->do_debug );
-
- return;
- }
- switch ( $this->Debugoutput ) {
- case 'error_log':
- //Don't output, just log
- error_log( $str );
- break;
- case 'html':
- //Cleans up output a bit for a better looking, HTML-safe output
- echo htmlentities(
- preg_replace( '/[\r\n]+/', '', $str ),
- ENT_QUOTES,
- 'UTF-8'
- )
- . "
\n";
- break;
- case 'echo':
- default:
- //Normalize line breaks
- $str = preg_replace( '/(\r\n|\r|\n)/ms', "\n", $str );
- echo gmdate( 'Y-m-d H:i:s' ) . "\t" . str_replace(
- "\n",
- "\n \t ",
- trim( $str )
- ) . "\n";
- }
- }
-
- /**
- * Close the socket and clean up the state of the class.
- * Don't use this function without first trying to use QUIT.
- * @see quit()
- * @access public
- * @return void
- */
- public function close() {
- $this->setError( '' );
- $this->server_caps = null;
- $this->helo_rply = null;
- if ( is_resource( $this->smtp_conn ) ) {
- // close the connection and cleanup
- fclose( $this->smtp_conn );
- $this->smtp_conn = null; //Makes for cleaner serialization
- $this->edebug( 'Connection: closed', self::DEBUG_CONNECTION );
- }
- }
-
- /**
- * Read the SMTP server's response.
- * Either before eof or socket timeout occurs on the operation.
- * With SMTP we can tell if we have more lines to read if the
- * 4th character is '-' symbol. If it is a space then we don't
- * need to read anything else.
- * @access protected
- * @return string
- */
- protected function get_lines() {
- // If the connection is bad, give up straight away
- if ( ! is_resource( $this->smtp_conn ) ) {
- return '';
- }
- $data = '';
- $endtime = 0;
- stream_set_timeout( $this->smtp_conn, $this->Timeout );
- if ( $this->Timelimit > 0 ) {
- $endtime = time() + $this->Timelimit;
- }
- while ( is_resource( $this->smtp_conn ) && ! feof( $this->smtp_conn ) ) {
- $str = @fgets( $this->smtp_conn, 515 );
- $this->edebug( "SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL );
- $this->edebug( "SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL );
- $data .= $str;
- // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen
- if ( ( isset( $str[3] ) and $str[3] == ' ' ) ) {
- break;
- }
- // Timed-out? Log and break
- $info = stream_get_meta_data( $this->smtp_conn );
- if ( $info['timed_out'] ) {
- $this->edebug(
- 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
- self::DEBUG_LOWLEVEL
- );
- break;
- }
- // Now check if reads took too long
- if ( $endtime and time() > $endtime ) {
- $this->edebug(
- 'SMTP -> get_lines(): timelimit reached (' .
- $this->Timelimit . ' sec)',
- self::DEBUG_LOWLEVEL
- );
- break;
- }
- }
-
- return $data;
- }
-
- /**
- * Initiate a TLS (encrypted) session.
- * @access public
- * @return boolean
- */
- public function startTLS() {
- if ( ! $this->sendCommand( 'STARTTLS', 'STARTTLS', 220 ) ) {
- return false;
- }
- // Begin encrypted connection
- if ( ! stream_socket_enable_crypto(
- $this->smtp_conn,
- true,
- STREAM_CRYPTO_METHOD_TLS_CLIENT
- ) ) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Send a command to an SMTP server and check its return code.
- *
- * @param string $command The command name - not sent to the server
- * @param string $commandstring The actual command to send
- * @param integer|array $expect One or more expected integer success codes
- *
- * @access protected
- * @return boolean True on success.
- */
- protected function sendCommand( $command, $commandstring, $expect ) {
- if ( ! $this->connected() ) {
- $this->setError( "Called $command without being connected" );
-
- return false;
- }
- //Reject line breaks in all commands
- if ( strpos( $commandstring, "\n" ) !== false or strpos( $commandstring, "\r" ) !== false ) {
- $this->setError( "Command '$command' contained line breaks" );
-
- return false;
- }
- $this->client_send( $commandstring . self::CRLF );
-
- $this->last_reply = $this->get_lines();
- // Fetch SMTP code and possible error code explanation
- $matches = array();
- if ( preg_match( "/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches ) ) {
- $code = $matches[1];
- $code_ex = ( count( $matches ) > 2 ? $matches[2] : null );
- // Cut off error code from each response line
- $detail = preg_replace(
- "/{$code}[ -]" . ( $code_ex ? str_replace( '.', '\\.', $code_ex ) . ' ' : '' ) . "/m",
- '',
- $this->last_reply
- );
- } else {
- // Fall back to simple parsing if regex fails
- $code = substr( $this->last_reply, 0, 3 );
- $code_ex = null;
- $detail = substr( $this->last_reply, 4 );
- }
-
- $this->edebug( 'SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER );
-
- if ( ! in_array( $code, (array) $expect ) ) {
- $this->setError(
- "$command command failed",
- $detail,
- $code,
- $code_ex
- );
- $this->edebug(
- 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
- self::DEBUG_CLIENT
- );
-
- return false;
- }
-
- $this->setError( '' );
-
- return true;
- }
-
- /**
- * Send raw data to the server.
- *
- * @param string $data The data to send
- *
- * @access public
- * @return integer|boolean The number of bytes sent to the server or false on error
- */
- public function client_send( $data ) {
- $this->edebug( "CLIENT -> SERVER: $data", self::DEBUG_CLIENT );
-
- return fwrite( $this->smtp_conn, $data );
- }
-
- /**
- * Perform SMTP authentication.
- * Must be run after hello().
- * @see hello()
- *
- * @param string $username The user name
- * @param string $password The password
- * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2)
- * @param string $realm The auth realm for NTLM
- * @param string $workstation The auth workstation for NTLM
- * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth)
- *
- * @return bool True if successfully authenticated.* @access public
- */
- public function authenticate(
- $username,
- $password,
- $authtype = null,
- $realm = '',
- $workstation = '',
- $OAuth = null
- ) {
- if ( ! $this->server_caps ) {
- $this->setError( 'Authentication is not allowed before HELO/EHLO' );
-
- return false;
- }
-
- if ( array_key_exists( 'EHLO', $this->server_caps ) ) {
- // SMTP extensions are available. Let's try to find a proper authentication method
-
- if ( ! array_key_exists( 'AUTH', $this->server_caps ) ) {
- $this->setError( 'Authentication is not allowed at this stage' );
- // 'at this stage' means that auth may be allowed after the stage changes
- // e.g. after STARTTLS
- return false;
- }
-
- self::edebug( 'Auth method requested: ' . ( $authtype ? $authtype : 'UNKNOWN' ), self::DEBUG_LOWLEVEL );
- self::edebug(
- 'Auth methods available on the server: ' . implode( ',', $this->server_caps['AUTH'] ),
- self::DEBUG_LOWLEVEL
- );
-
- if ( empty( $authtype ) ) {
- foreach ( array( 'LOGIN', 'CRAM-MD5', 'NTLM', 'PLAIN', 'XOAUTH2' ) as $method ) {
- if ( in_array( $method, $this->server_caps['AUTH'] ) ) {
- $authtype = $method;
- break;
- }
- }
- if ( empty( $authtype ) ) {
- $this->setError( 'No supported authentication methods found' );
-
- return false;
- }
- self::edebug( 'Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL );
- }
-
- if ( ! in_array( $authtype, $this->server_caps['AUTH'] ) ) {
- $this->setError( "The requested authentication method \"$authtype\" is not supported by the server" );
-
- return false;
- }
- } elseif ( empty( $authtype ) ) {
- $authtype = 'LOGIN';
- }
- switch ( $authtype ) {
- case 'PLAIN':
- // Start authentication
- if ( ! $this->sendCommand( 'AUTH', 'AUTH PLAIN', 334 ) ) {
- return false;
- }
- // Send encoded username and password
- if ( ! $this->sendCommand(
- 'User & Password',
- base64_encode( "\0" . $username . "\0" . $password ),
- 235
- )
- ) {
- return false;
- }
- break;
- case 'LOGIN':
- // Start authentication
- if ( ! $this->sendCommand( 'AUTH', 'AUTH LOGIN', 334 ) ) {
- return false;
- }
- if ( ! $this->sendCommand( "Username", base64_encode( $username ), 334 ) ) {
- return false;
- }
- if ( ! $this->sendCommand( "Password", base64_encode( $password ), 235 ) ) {
- return false;
- }
- break;
- case 'XOAUTH2':
- //If the OAuth Instance is not set. Can be a case when PHPMailer is used
- //instead of PHPMailerOAuth
- if ( is_null( $OAuth ) ) {
- return false;
- }
- $oauth = $OAuth->getOauth64();
-
- // Start authentication
- if ( ! $this->sendCommand( 'AUTH', 'AUTH XOAUTH2 ' . $oauth, 235 ) ) {
- return false;
- }
- break;
- case 'NTLM':
- /*
- * ntlm_sasl_client.php
- * Bundled with Permission
- *
- * How to telnet in windows:
- * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
- * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
- */
- require_once 'extras/ntlm_sasl_client.php';
- $temp = new stdClass;
- $ntlm_client = new ntlm_sasl_client_class;
- //Check that functions are available
- if ( ! $ntlm_client->Initialize( $temp ) ) {
- $this->setError( $temp->error );
- $this->edebug(
- 'You need to enable some modules in your php.ini file: '
- . $this->error['error'],
- self::DEBUG_CLIENT
- );
-
- return false;
- }
- //msg1
- $msg1 = $ntlm_client->TypeMsg1( $realm, $workstation ); //msg1
-
- if ( ! $this->sendCommand(
- 'AUTH NTLM',
- 'AUTH NTLM ' . base64_encode( $msg1 ),
- 334
- )
- ) {
- return false;
- }
- //Though 0 based, there is a white space after the 3 digit number
- //msg2
- $challenge = substr( $this->last_reply, 3 );
- $challenge = base64_decode( $challenge );
- $ntlm_res = $ntlm_client->NTLMResponse(
- substr( $challenge, 24, 8 ),
- $password
- );
- //msg3
- $msg3 = $ntlm_client->TypeMsg3(
- $ntlm_res,
- $username,
- $realm,
- $workstation
- );
-
- // send encoded username
- return $this->sendCommand( 'Username', base64_encode( $msg3 ), 235 );
- case 'CRAM-MD5':
- // Start authentication
- if ( ! $this->sendCommand( 'AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334 ) ) {
- return false;
- }
- // Get the challenge
- $challenge = base64_decode( substr( $this->last_reply, 4 ) );
-
- // Build the response
- $response = $username . ' ' . $this->hmac( $challenge, $password );
-
- // send encoded credentials
- return $this->sendCommand( 'Username', base64_encode( $response ), 235 );
- default:
- $this->setError( "Authentication method \"$authtype\" is not supported" );
-
- return false;
- }
-
- return true;
- }
-
- /**
- * Calculate an MD5 HMAC hash.
- * Works like hash_hmac('md5', $data, $key)
- * in case that function is not available
- *
- * @param string $data The data to hash
- * @param string $key The key to hash with
- *
- * @access protected
- * @return string
- */
- protected function hmac( $data, $key ) {
- if ( function_exists( 'hash_hmac' ) ) {
- return hash_hmac( 'md5', $data, $key );
- }
-
- // The following borrowed from
- // http://php.net/manual/en/function.mhash.php#27225
-
- // RFC 2104 HMAC implementation for php.
- // Creates an md5 HMAC.
- // Eliminates the need to install mhash to compute a HMAC
- // by Lance Rushing
-
- $bytelen = 64; // byte length for md5
- if ( strlen( $key ) > $bytelen ) {
- $key = pack( 'H*', md5( $key ) );
- }
- $key = str_pad( $key, $bytelen, chr( 0x00 ) );
- $ipad = str_pad( '', $bytelen, chr( 0x36 ) );
- $opad = str_pad( '', $bytelen, chr( 0x5c ) );
- $k_ipad = $key ^ $ipad;
- $k_opad = $key ^ $opad;
-
- return md5( $k_opad . pack( 'H*', md5( $k_ipad . $data ) ) );
- }
-
- /**
- * Send an SMTP DATA command.
- * Issues a data command and sends the msg_data to the server,
- * finializing the mail transaction. $msg_data is the message
- * that is to be send with the headers. Each header needs to be
- * on a single line followed by a with the message headers
- * and the message body being separated by and additional .
- * Implements rfc 821: DATA
- *
- * @param string $msg_data Message data to send
- *
- * @access public
- * @return boolean
- */
- public function data( $msg_data ) {
- //This will use the standard timelimit
- if ( ! $this->sendCommand( 'DATA', 'DATA', 354 ) ) {
- return false;
- }
-
- /* The server is ready to accept data!
- * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
- * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
- * smaller lines to fit within the limit.
- * We will also look for lines that start with a '.' and prepend an additional '.'.
- * NOTE: this does not count towards line-length limit.
- */
-
- // Normalize line breaks before exploding
- $lines = explode( "\n", str_replace( array( "\r\n", "\r" ), "\n", $msg_data ) );
-
- /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
- * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
- * process all lines before a blank line as headers.
- */
-
- $field = substr( $lines[0], 0, strpos( $lines[0], ':' ) );
- $in_headers = false;
- if ( ! empty( $field ) && strpos( $field, ' ' ) === false ) {
- $in_headers = true;
- }
-
- foreach ( $lines as $line ) {
- $lines_out = array();
- if ( $in_headers and $line == '' ) {
- $in_headers = false;
- }
- //Break this line up into several smaller lines if it's too long
- //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
- while ( isset( $line[ self::MAX_LINE_LENGTH ] ) ) {
- //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
- //so as to avoid breaking in the middle of a word
- $pos = strrpos( substr( $line, 0, self::MAX_LINE_LENGTH ), ' ' );
- //Deliberately matches both false and 0
- if ( ! $pos ) {
- //No nice break found, add a hard break
- $pos = self::MAX_LINE_LENGTH - 1;
- $lines_out[] = substr( $line, 0, $pos );
- $line = substr( $line, $pos );
- } else {
- //Break at the found point
- $lines_out[] = substr( $line, 0, $pos );
- //Move along by the amount we dealt with
- $line = substr( $line, $pos + 1 );
- }
- //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
- if ( $in_headers ) {
- $line = "\t" . $line;
- }
- }
- $lines_out[] = $line;
-
- //Send the lines to the server
- foreach ( $lines_out as $line_out ) {
- //RFC2821 section 4.5.2
- if ( ! empty( $line_out ) and $line_out[0] == '.' ) {
- $line_out = '.' . $line_out;
- }
- $this->client_send( $line_out . self::CRLF );
- }
- }
-
- //Message data has been sent, complete the command
- //Increase timelimit for end of DATA command
- $savetimelimit = $this->Timelimit;
- $this->Timelimit = $this->Timelimit * 2;
- $result = $this->sendCommand( 'DATA END', '.', 250 );
- //Restore timelimit
- $this->Timelimit = $savetimelimit;
-
- return $result;
- }
-
- /**
- * Send an SMTP HELO or EHLO command.
- * Used to identify the sending server to the receiving server.
- * This makes sure that client and server are in a known state.
- * Implements RFC 821: HELO
- * and RFC 2821 EHLO.
- *
- * @param string $host The host name or IP to connect to
- *
- * @access public
- * @return boolean
- */
- public function hello( $host = '' ) {
- //Try extended hello first (RFC 2821)
- return (boolean) ( $this->sendHello( 'EHLO', $host ) or $this->sendHello( 'HELO', $host ) );
- }
-
- /**
- * Send an SMTP HELO or EHLO command.
- * Low-level implementation used by hello()
- * @see hello()
- *
- * @param string $hello The HELO string
- * @param string $host The hostname to say we are
- *
- * @access protected
- * @return boolean
- */
- protected function sendHello( $hello, $host ) {
- $noerror = $this->sendCommand( $hello, $hello . ' ' . $host, 250 );
- $this->helo_rply = $this->last_reply;
- if ( $noerror ) {
- $this->parseHelloFields( $hello );
- } else {
- $this->server_caps = null;
- }
-
- return $noerror;
- }
-
- /**
- * Parse a reply to HELO/EHLO command to discover server extensions.
- * In case of HELO, the only parameter that can be discovered is a server name.
- * @access protected
- *
- * @param string $type - 'HELO' or 'EHLO'
- */
- protected function parseHelloFields( $type ) {
- $this->server_caps = array();
- $lines = explode( "\n", $this->last_reply );
-
- foreach ( $lines as $n => $s ) {
- //First 4 chars contain response code followed by - or space
- $s = trim( substr( $s, 4 ) );
- if ( empty( $s ) ) {
- continue;
- }
- $fields = explode( ' ', $s );
- if ( ! empty( $fields ) ) {
- if ( ! $n ) {
- $name = $type;
- $fields = $fields[0];
- } else {
- $name = array_shift( $fields );
- switch ( $name ) {
- case 'SIZE':
- $fields = ( $fields ? $fields[0] : 0 );
- break;
- case 'AUTH':
- if ( ! is_array( $fields ) ) {
- $fields = array();
- }
- break;
- default:
- $fields = true;
- }
- }
- $this->server_caps[ $name ] = $fields;
- }
- }
- }
-
- /**
- * Send an SMTP MAIL command.
- * Starts a mail transaction from the email address specified in
- * $from. Returns true if successful or false otherwise. If True
- * the mail transaction is started and then one or more recipient
- * commands may be called followed by a data command.
- * Implements rfc 821: MAIL FROM:
- *
- * @param string $from Source address of this message
- *
- * @access public
- * @return boolean
- */
- public function mail( $from ) {
- $useVerp = ( $this->do_verp ? ' XVERP' : '' );
-
- return $this->sendCommand(
- 'MAIL FROM',
- 'MAIL FROM:<' . $from . '>' . $useVerp,
- 250
- );
- }
-
- /**
- * Send an SMTP QUIT command.
- * Closes the socket if there is no error or the $close_on_error argument is true.
- * Implements from rfc 821: QUIT
- *
- * @param boolean $close_on_error Should the connection close if an error occurs?
- *
- * @access public
- * @return boolean
- */
- public function quit( $close_on_error = true ) {
- $noerror = $this->sendCommand( 'QUIT', 'QUIT', 221 );
- $err = $this->error; //Save any error
- if ( $noerror or $close_on_error ) {
- $this->close();
- $this->error = $err; //Restore any error from the quit command
- }
-
- return $noerror;
- }
-
- /**
- * Send an SMTP RCPT command.
- * Sets the TO argument to $toaddr.
- * Returns true if the recipient was accepted false if it was rejected.
- * Implements from rfc 821: RCPT TO:
- *
- * @param string $address The address the message is being sent to
- *
- * @access public
- * @return boolean
- */
- public function recipient( $address ) {
- return $this->sendCommand(
- 'RCPT TO',
- 'RCPT TO:<' . $address . '>',
- array( 250, 251 )
- );
- }
-
- /**
- * Send an SMTP RSET command.
- * Abort any transaction that is currently in progress.
- * Implements rfc 821: RSET
- * @access public
- * @return boolean True on success.
- */
- public function reset() {
- return $this->sendCommand( 'RSET', 'RSET', 250 );
- }
-
- /**
- * Send an SMTP SAML command.
- * Starts a mail transaction from the email address specified in $from.
- * Returns true if successful or false otherwise. If True
- * the mail transaction is started and then one or more recipient
- * commands may be called followed by a data command. This command
- * will send the message to the users terminal if they are logged
- * in and send them an email.
- * Implements rfc 821: SAML FROM:
- *
- * @param string $from The address the message is from
- *
- * @access public
- * @return boolean
- */
- public function sendAndMail( $from ) {
- return $this->sendCommand( 'SAML', "SAML FROM:$from", 250 );
- }
-
- /**
- * Send an SMTP VRFY command.
- *
- * @param string $name The name to verify
- *
- * @access public
- * @return boolean
- */
- public function verify( $name ) {
- return $this->sendCommand( 'VRFY', "VRFY $name", array( 250, 251 ) );
- }
-
- /**
- * Send an SMTP NOOP command.
- * Used to keep keep-alives alive, doesn't actually do anything
- * @access public
- * @return boolean
- */
- public function noop() {
- return $this->sendCommand( 'NOOP', 'NOOP', 250 );
- }
-
- /**
- * Send an SMTP TURN command.
- * This is an optional command for SMTP that this class does not support.
- * This method is here to make the RFC821 Definition complete for this class
- * and _may_ be implemented in future
- * Implements from rfc 821: TURN
- * @access public
- * @return boolean
- */
- public function turn() {
- $this->setError( 'The SMTP TURN command is not implemented' );
- $this->edebug( 'SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT );
-
- return false;
- }
-
- /**
- * Get the latest error.
- * @access public
- * @return array
- */
- public function getError() {
- return $this->error;
- }
-
- /**
- * Set error messages and codes.
- *
- * @param string $message The error message
- * @param string $detail Further detail on the error
- * @param string $smtp_code An associated SMTP error code
- * @param string $smtp_code_ex Extended SMTP code
- */
- protected function setError( $message, $detail = '', $smtp_code = '', $smtp_code_ex = '' ) {
- $this->error = array(
- 'error' => $message,
- 'detail' => $detail,
- 'smtp_code' => $smtp_code,
- 'smtp_code_ex' => $smtp_code_ex
- );
- }
-
- /**
- * Get SMTP extensions available on the server
- * @access public
- * @return array|null
- */
- public function getServerExtList() {
- return $this->server_caps;
- }
-
- /**
- * A multipurpose method
- * The method works in three ways, dependent on argument value and current state
- * 1. HELO/EHLO was not sent - returns null and set up $this->error
- * 2. HELO was sent
- * $name = 'HELO': returns server name
- * $name = 'EHLO': returns boolean false
- * $name = any string: returns null and set up $this->error
- * 3. EHLO was sent
- * $name = 'HELO'|'EHLO': returns server name
- * $name = any string: if extension $name exists, returns boolean True
- * or its options. Otherwise returns boolean False
- * In other words, one can use this method to detect 3 conditions:
- * - null returned: handshake was not or we don't know about ext (refer to $this->error)
- * - false returned: the requested feature exactly not exists
- * - positive value returned: the requested feature exists
- *
- * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
- *
- * @return mixed
- */
- public function getServerExt( $name ) {
- if ( ! $this->server_caps ) {
- $this->setError( 'No HELO/EHLO was sent' );
-
- return null;
- }
-
- // the tight logic knot ;)
- if ( ! array_key_exists( $name, $this->server_caps ) ) {
- if ( $name == 'HELO' ) {
- return $this->server_caps['EHLO'];
- }
- if ( $name == 'EHLO' || array_key_exists( 'EHLO', $this->server_caps ) ) {
- return false;
- }
- $this->setError( 'HELO handshake was used. Client knows nothing about server extensions' );
-
- return null;
- }
-
- return $this->server_caps[ $name ];
- }
-
- /**
- * Get the last reply from the server.
- * @access public
- * @return string
- */
- public function getLastReply() {
- return $this->last_reply;
- }
-
- /**
- * Enable or disable VERP address generation.
- *
- * @param boolean $enabled
- */
- public function setVerp( $enabled = false ) {
- $this->do_verp = $enabled;
- }
-
- /**
- * Get VERP address generation mode.
- * @return boolean
- */
- public function getVerp() {
- return $this->do_verp;
- }
-
- /**
- * Get debug output method.
- * @return string
- */
- public function getDebugOutput() {
- return $this->Debugoutput;
- }
-
- /**
- * Set debug output method.
- *
- * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it.
- */
- public function setDebugOutput( $method = 'echo' ) {
- $this->Debugoutput = $method;
- }
-
- /**
- * Set debug output level.
- *
- * @param integer $level
- */
- public function setDebugLevel( $level = 0 ) {
- $this->do_debug = $level;
- }
-
- /**
- * Get debug output level.
- * @return integer
- */
- public function getDebugLevel() {
- return $this->do_debug;
- }
-
- /**
- * Get SMTP timeout.
- * @return integer
- */
- public function getTimeout() {
- return $this->Timeout;
- }
-
- /**
- * Set SMTP timeout.
- *
- * @param integer $timeout
- */
- public function setTimeout( $timeout = 0 ) {
- $this->Timeout = $timeout;
- }
+ /**
+ * The PHPMailer SMTP version number.
+ * @var string
+ */
+ const VERSION = '5.2.14';
+
+ /**
+ * SMTP line break constant.
+ * @var string
+ */
+ const CRLF = "\r\n";
+
+ /**
+ * The SMTP port to use if one is not specified.
+ * @var integer
+ */
+ const DEFAULT_SMTP_PORT = 25;
+
+ /**
+ * The maximum line length allowed by RFC 2822 section 2.1.1
+ * @var integer
+ */
+ const MAX_LINE_LENGTH = 998;
+
+ /**
+ * Debug level for no output
+ */
+ const DEBUG_OFF = 0;
+
+ /**
+ * Debug level to show client -> server messages
+ */
+ const DEBUG_CLIENT = 1;
+
+ /**
+ * Debug level to show client -> server and server -> client messages
+ */
+ const DEBUG_SERVER = 2;
+
+ /**
+ * Debug level to show connection status, client -> server and server -> client messages
+ */
+ const DEBUG_CONNECTION = 3;
+
+ /**
+ * Debug level to show all messages
+ */
+ const DEBUG_LOWLEVEL = 4;
+
+ /**
+ * The PHPMailer SMTP Version number.
+ * @var string
+ * @deprecated Use the `VERSION` constant instead
+ * @see SMTP::VERSION
+ */
+ public $Version = '5.2.14';
+
+ /**
+ * SMTP server port number.
+ * @var integer
+ * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead
+ * @see SMTP::DEFAULT_SMTP_PORT
+ */
+ public $SMTP_PORT = 25;
+
+ /**
+ * SMTP reply line ending.
+ * @var string
+ * @deprecated Use the `CRLF` constant instead
+ * @see SMTP::CRLF
+ */
+ public $CRLF = "\r\n";
+
+ /**
+ * Debug output level.
+ * Options:
+ * * self::DEBUG_OFF (`0`) No debug output, default
+ * * self::DEBUG_CLIENT (`1`) Client commands
+ * * self::DEBUG_SERVER (`2`) Client commands and server responses
+ * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
+ * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages
+ * @var integer
+ */
+ public $do_debug = self::DEBUG_OFF;
+
+ /**
+ * How to handle debug output.
+ * Options:
+ * * `echo` Output plain-text as-is, appropriate for CLI
+ * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output
+ * * `error_log` Output to error log as configured in php.ini
+ *
+ * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
+ *
+ * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
+ *
+ * @var string|callable
+ */
+ public $Debugoutput = 'echo';
+
+ /**
+ * Whether to use VERP.
+ * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
+ * @link http://www.postfix.org/VERP_README.html Info on VERP
+ * @var boolean
+ */
+ public $do_verp = false;
+
+ /**
+ * The timeout value for connection, in seconds.
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+ * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
+ * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
+ * @var integer
+ */
+ public $Timeout = 300;
+
+ /**
+ * How long to wait for commands to complete, in seconds.
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+ * @var integer
+ */
+ public $Timelimit = 300;
+
+ /**
+ * The socket for the server connection.
+ * @var resource
+ */
+ protected $smtp_conn;
+
+ /**
+ * Error information, if any, for the last SMTP command.
+ * @var array
+ */
+ protected $error = array(
+ 'error' => '',
+ 'detail' => '',
+ 'smtp_code' => '',
+ 'smtp_code_ex' => ''
+ );
+
+ /**
+ * The reply the server sent to us for HELO.
+ * If null, no HELO string has yet been received.
+ * @var string|null
+ */
+ protected $helo_rply = null;
+
+ /**
+ * The set of SMTP extensions sent in reply to EHLO command.
+ * Indexes of the array are extension names.
+ * Value at index 'HELO' or 'EHLO' (according to command that was sent)
+ * represents the server name. In case of HELO it is the only element of the array.
+ * Other values can be boolean TRUE or an array containing extension options.
+ * If null, no HELO/EHLO string has yet been received.
+ * @var array|null
+ */
+ protected $server_caps = null;
+
+ /**
+ * The most recent reply received from the server.
+ * @var string
+ */
+ protected $last_reply = '';
+
+ /**
+ * Connect to an SMTP server.
+ *
+ * @param string $host SMTP server IP or host name
+ * @param integer $port The port number to connect to
+ * @param integer $timeout How long to wait for the connection to open
+ * @param array $options An array of options for stream_context_create()
+ *
+ * @access public
+ * @return boolean
+ */
+ public function connect( $host, $port = null, $timeout = 30, $options = array() ) {
+ static $streamok;
+ //This is enabled by default since 5.0.0 but some providers disable it
+ //Check this once and cache the result
+ if ( is_null( $streamok ) ) {
+ $streamok = function_exists( 'stream_socket_client' );
+ }
+ // Clear errors to avoid confusion
+ $this->setError( '' );
+ // Make sure we are __not__ connected
+ if ( $this->connected() ) {
+ // Already connected, generate error
+ $this->setError( 'Already connected to a server' );
+
+ return false;
+ }
+ if ( empty( $port ) ) {
+ $port = self::DEFAULT_SMTP_PORT;
+ }
+ // Connect to the SMTP server
+ $this->edebug(
+ "Connection: opening to $host:$port, timeout=$timeout, options=" . var_export( $options, true ),
+ self::DEBUG_CONNECTION
+ );
+ $errno = 0;
+ $errstr = '';
+ if ( $streamok ) {
+ $socket_context = stream_context_create( $options );
+ //Suppress errors; connection failures are handled at a higher level
+ $this->smtp_conn = @stream_socket_client(
+ $host . ":" . $port,
+ $errno,
+ $errstr,
+ $timeout,
+ STREAM_CLIENT_CONNECT,
+ $socket_context
+ );
+ } else {
+ //Fall back to fsockopen which should work in more places, but is missing some features
+ $this->edebug(
+ "Connection: stream_socket_client not available, falling back to fsockopen",
+ self::DEBUG_CONNECTION
+ );
+ $this->smtp_conn = fsockopen(
+ $host,
+ $port,
+ $errno,
+ $errstr,
+ $timeout
+ );
+ }
+ // Verify we connected properly
+ if ( !is_resource( $this->smtp_conn ) ) {
+ $this->setError(
+ 'Failed to connect to server',
+ $errno,
+ $errstr
+ );
+ $this->edebug(
+ 'SMTP ERROR: ' . $this->error[ 'error' ]
+ . ": $errstr ($errno)",
+ self::DEBUG_CLIENT
+ );
+
+ return false;
+ }
+ $this->edebug( 'Connection: opened', self::DEBUG_CONNECTION );
+ // SMTP server can take longer to respond, give longer timeout for first read
+ // Windows does not have support for this timeout function
+ if ( substr( PHP_OS, 0, 3 ) != 'WIN' ) {
+ $max = ini_get( 'max_execution_time' );
+ // Don't bother if unlimited
+ if ( $max != 0 && $timeout > $max ) {
+ @set_time_limit( $timeout );
+ }
+ stream_set_timeout( $this->smtp_conn, $timeout, 0 );
+ }
+ // Get any announcement
+ $announce = $this->get_lines();
+ $this->edebug( 'SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER );
+
+ return true;
+ }
+
+ /**
+ * Check connection state.
+ * @access public
+ * @return boolean True if connected.
+ */
+ public function connected() {
+ if ( is_resource( $this->smtp_conn ) ) {
+ $sock_status = stream_get_meta_data( $this->smtp_conn );
+ if ( $sock_status[ 'eof' ] ) {
+ // The socket is valid but we are not connected
+ $this->edebug(
+ 'SMTP NOTICE: EOF caught while checking if connected',
+ self::DEBUG_CLIENT
+ );
+ $this->close();
+
+ return false;
+ }
+
+ return true; // everything looks good
+ }
+
+ return false;
+ }
+
+ /**
+ * Output debugging info via a user-selected method.
+ * @see SMTP::$Debugoutput
+ * @see SMTP::$do_debug
+ *
+ * @param string $str Debug string to output
+ * @param integer $level The debug level of this message; see DEBUG_* constants
+ *
+ * @return void
+ */
+ protected function edebug( $str, $level = 0 ) {
+ if ( $level > $this->do_debug ) {
+ return;
+ }
+ //Avoid clash with built-in function names
+ if ( !in_array( $this->Debugoutput, array(
+ 'error_log',
+ 'html',
+ 'echo'
+ ) ) and is_callable( $this->Debugoutput ) ) {
+ call_user_func( $this->Debugoutput, $str, $this->do_debug );
+
+ return;
+ }
+ switch ( $this->Debugoutput ) {
+ case 'error_log':
+ //Don't output, just log
+ error_log( $str );
+ break;
+ case 'html':
+ //Cleans up output a bit for a better looking, HTML-safe output
+ echo htmlentities(
+ preg_replace( '/[\r\n]+/', '', $str ),
+ ENT_QUOTES,
+ 'UTF-8'
+ )
+ . "
\n";
+ break;
+ case 'echo':
+ default:
+ //Normalize line breaks
+ $str = preg_replace( '/(\r\n|\r|\n)/ms', "\n", $str );
+ echo gmdate( 'Y-m-d H:i:s' ) . "\t" . str_replace(
+ "\n",
+ "\n \t ",
+ trim( $str )
+ ) . "\n";
+ }
+ }
+
+ /**
+ * Close the socket and clean up the state of the class.
+ * Don't use this function without first trying to use QUIT.
+ * @see quit()
+ * @access public
+ * @return void
+ */
+ public function close() {
+ $this->setError( '' );
+ $this->server_caps = null;
+ $this->helo_rply = null;
+ if ( is_resource( $this->smtp_conn ) ) {
+ // close the connection and cleanup
+ fclose( $this->smtp_conn );
+ $this->smtp_conn = null; //Makes for cleaner serialization
+ $this->edebug( 'Connection: closed', self::DEBUG_CONNECTION );
+ }
+ }
+
+ /**
+ * Read the SMTP server's response.
+ * Either before eof or socket timeout occurs on the operation.
+ * With SMTP we can tell if we have more lines to read if the
+ * 4th character is '-' symbol. If it is a space then we don't
+ * need to read anything else.
+ * @access protected
+ * @return string
+ */
+ protected function get_lines() {
+ // If the connection is bad, give up straight away
+ if ( !is_resource( $this->smtp_conn ) ) {
+ return '';
+ }
+ $data = '';
+ $endtime = 0;
+ stream_set_timeout( $this->smtp_conn, $this->Timeout );
+ if ( $this->Timelimit > 0 ) {
+ $endtime = time() + $this->Timelimit;
+ }
+ while ( is_resource( $this->smtp_conn ) && !feof( $this->smtp_conn ) ) {
+ $str = @fgets( $this->smtp_conn, 515 );
+ $this->edebug( "SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL );
+ $this->edebug( "SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL );
+ $data .= $str;
+ // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen
+ if ( ( isset( $str[ 3 ] ) and $str[ 3 ] == ' ' ) ) {
+ break;
+ }
+ // Timed-out? Log and break
+ $info = stream_get_meta_data( $this->smtp_conn );
+ if ( $info[ 'timed_out' ] ) {
+ $this->edebug(
+ 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
+ self::DEBUG_LOWLEVEL
+ );
+ break;
+ }
+ // Now check if reads took too long
+ if ( $endtime and time() > $endtime ) {
+ $this->edebug(
+ 'SMTP -> get_lines(): timelimit reached (' .
+ $this->Timelimit . ' sec)',
+ self::DEBUG_LOWLEVEL
+ );
+ break;
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Initiate a TLS (encrypted) session.
+ * @access public
+ * @return boolean
+ */
+ public function startTLS() {
+ if ( !$this->sendCommand( 'STARTTLS', 'STARTTLS', 220 ) ) {
+ return false;
+ }
+ // Begin encrypted connection
+ if ( !stream_socket_enable_crypto(
+ $this->smtp_conn,
+ true,
+ STREAM_CRYPTO_METHOD_TLS_CLIENT
+ ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Send a command to an SMTP server and check its return code.
+ *
+ * @param string $command The command name - not sent to the server
+ * @param string $commandstring The actual command to send
+ * @param integer|array $expect One or more expected integer success codes
+ *
+ * @access protected
+ * @return boolean True on success.
+ */
+ protected function sendCommand( $command, $commandstring, $expect ) {
+ if ( !$this->connected() ) {
+ $this->setError( "Called $command without being connected" );
+
+ return false;
+ }
+ //Reject line breaks in all commands
+ if ( strpos( $commandstring, "\n" ) !== false or strpos( $commandstring, "\r" ) !== false ) {
+ $this->setError( "Command '$command' contained line breaks" );
+
+ return false;
+ }
+ $this->client_send( $commandstring . self::CRLF );
+
+ $this->last_reply = $this->get_lines();
+ // Fetch SMTP code and possible error code explanation
+ $matches = array();
+ if ( preg_match( "/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches ) ) {
+ $code = $matches[ 1 ];
+ $code_ex = ( count( $matches ) > 2 ? $matches[ 2 ] : null );
+ // Cut off error code from each response line
+ $detail = preg_replace(
+ "/{$code}[ -]" . ( $code_ex ? str_replace( '.', '\\.', $code_ex ) . ' ' : '' ) . "/m",
+ '',
+ $this->last_reply
+ );
+ } else {
+ // Fall back to simple parsing if regex fails
+ $code = substr( $this->last_reply, 0, 3 );
+ $code_ex = null;
+ $detail = substr( $this->last_reply, 4 );
+ }
+
+ $this->edebug( 'SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER );
+
+ if ( !in_array( $code, (array)$expect ) ) {
+ $this->setError(
+ "$command command failed",
+ $detail,
+ $code,
+ $code_ex
+ );
+ $this->edebug(
+ 'SMTP ERROR: ' . $this->error[ 'error' ] . ': ' . $this->last_reply,
+ self::DEBUG_CLIENT
+ );
+
+ return false;
+ }
+
+ $this->setError( '' );
+
+ return true;
+ }
+
+ /**
+ * Send raw data to the server.
+ *
+ * @param string $data The data to send
+ *
+ * @access public
+ * @return integer|boolean The number of bytes sent to the server or false on error
+ */
+ public function client_send( $data ) {
+ $this->edebug( "CLIENT -> SERVER: $data", self::DEBUG_CLIENT );
+
+ return fwrite( $this->smtp_conn, $data );
+ }
+
+ /**
+ * Perform SMTP authentication.
+ * Must be run after hello().
+ * @see hello()
+ *
+ * @param string $username The user name
+ * @param string $password The password
+ * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2)
+ * @param string $realm The auth realm for NTLM
+ * @param string $workstation The auth workstation for NTLM
+ * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth)
+ *
+ * @return bool True if successfully authenticated.* @access public
+ */
+ public function authenticate(
+ $username,
+ $password,
+ $authtype = null,
+ $realm = '',
+ $workstation = '',
+ $OAuth = null
+ ) {
+ if ( !$this->server_caps ) {
+ $this->setError( 'Authentication is not allowed before HELO/EHLO' );
+
+ return false;
+ }
+
+ if ( array_key_exists( 'EHLO', $this->server_caps ) ) {
+ // SMTP extensions are available. Let's try to find a proper authentication method
+
+ if ( !array_key_exists( 'AUTH', $this->server_caps ) ) {
+ $this->setError( 'Authentication is not allowed at this stage' );
+ // 'at this stage' means that auth may be allowed after the stage changes
+ // e.g. after STARTTLS
+ return false;
+ }
+
+ self::edebug( 'Auth method requested: ' . ( $authtype ? $authtype : 'UNKNOWN' ), self::DEBUG_LOWLEVEL );
+ self::edebug(
+ 'Auth methods available on the server: ' . implode( ',', $this->server_caps[ 'AUTH' ] ),
+ self::DEBUG_LOWLEVEL
+ );
+
+ if ( empty( $authtype ) ) {
+ foreach ( array( 'LOGIN', 'CRAM-MD5', 'NTLM', 'PLAIN', 'XOAUTH2' ) as $method ) {
+ if ( in_array( $method, $this->server_caps[ 'AUTH' ] ) ) {
+ $authtype = $method;
+ break;
+ }
+ }
+ if ( empty( $authtype ) ) {
+ $this->setError( 'No supported authentication methods found' );
+
+ return false;
+ }
+ self::edebug( 'Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL );
+ }
+
+ if ( !in_array( $authtype, $this->server_caps[ 'AUTH' ] ) ) {
+ $this->setError( "The requested authentication method \"$authtype\" is not supported by the server" );
+
+ return false;
+ }
+ } elseif ( empty( $authtype ) ) {
+ $authtype = 'LOGIN';
+ }
+ switch ( $authtype ) {
+ case 'PLAIN':
+ // Start authentication
+ if ( !$this->sendCommand( 'AUTH', 'AUTH PLAIN', 334 ) ) {
+ return false;
+ }
+ // Send encoded username and password
+ if ( !$this->sendCommand(
+ 'User & Password',
+ base64_encode( "\0" . $username . "\0" . $password ),
+ 235
+ )
+ ) {
+ return false;
+ }
+ break;
+ case 'LOGIN':
+ // Start authentication
+ if ( !$this->sendCommand( 'AUTH', 'AUTH LOGIN', 334 ) ) {
+ return false;
+ }
+ if ( !$this->sendCommand( "Username", base64_encode( $username ), 334 ) ) {
+ return false;
+ }
+ if ( !$this->sendCommand( "Password", base64_encode( $password ), 235 ) ) {
+ return false;
+ }
+ break;
+ case 'XOAUTH2':
+ //If the OAuth Instance is not set. Can be a case when PHPMailer is used
+ //instead of PHPMailerOAuth
+ if ( is_null( $OAuth ) ) {
+ return false;
+ }
+ $oauth = $OAuth->getOauth64();
+
+ // Start authentication
+ if ( !$this->sendCommand( 'AUTH', 'AUTH XOAUTH2 ' . $oauth, 235 ) ) {
+ return false;
+ }
+ break;
+ case 'NTLM':
+ /*
+ * ntlm_sasl_client.php
+ * Bundled with Permission
+ *
+ * How to telnet in windows:
+ * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
+ * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
+ */
+ require_once 'extras/ntlm_sasl_client.php';
+ $temp = new stdClass;
+ $ntlm_client = new ntlm_sasl_client_class;
+ //Check that functions are available
+ if ( !$ntlm_client->Initialize( $temp ) ) {
+ $this->setError( $temp->error );
+ $this->edebug(
+ 'You need to enable some modules in your php.ini file: '
+ . $this->error[ 'error' ],
+ self::DEBUG_CLIENT
+ );
+
+ return false;
+ }
+ //msg1
+ $msg1 = $ntlm_client->TypeMsg1( $realm, $workstation ); //msg1
+
+ if ( !$this->sendCommand(
+ 'AUTH NTLM',
+ 'AUTH NTLM ' . base64_encode( $msg1 ),
+ 334
+ )
+ ) {
+ return false;
+ }
+ //Though 0 based, there is a white space after the 3 digit number
+ //msg2
+ $challenge = substr( $this->last_reply, 3 );
+ $challenge = base64_decode( $challenge );
+ $ntlm_res = $ntlm_client->NTLMResponse(
+ substr( $challenge, 24, 8 ),
+ $password
+ );
+ //msg3
+ $msg3 = $ntlm_client->TypeMsg3(
+ $ntlm_res,
+ $username,
+ $realm,
+ $workstation
+ );
+
+ // send encoded username
+ return $this->sendCommand( 'Username', base64_encode( $msg3 ), 235 );
+ case 'CRAM-MD5':
+ // Start authentication
+ if ( !$this->sendCommand( 'AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334 ) ) {
+ return false;
+ }
+ // Get the challenge
+ $challenge = base64_decode( substr( $this->last_reply, 4 ) );
+
+ // Build the response
+ $response = $username . ' ' . $this->hmac( $challenge, $password );
+
+ // send encoded credentials
+ return $this->sendCommand( 'Username', base64_encode( $response ), 235 );
+ default:
+ $this->setError( "Authentication method \"$authtype\" is not supported" );
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Calculate an MD5 HMAC hash.
+ * Works like hash_hmac('md5', $data, $key)
+ * in case that function is not available
+ *
+ * @param string $data The data to hash
+ * @param string $key The key to hash with
+ *
+ * @access protected
+ * @return string
+ */
+ protected function hmac( $data, $key ) {
+ if ( function_exists( 'hash_hmac' ) ) {
+ return hash_hmac( 'md5', $data, $key );
+ }
+
+ // The following borrowed from
+ // http://php.net/manual/en/function.mhash.php#27225
+
+ // RFC 2104 HMAC implementation for php.
+ // Creates an md5 HMAC.
+ // Eliminates the need to install mhash to compute a HMAC
+ // by Lance Rushing
+
+ $bytelen = 64; // byte length for md5
+ if ( strlen( $key ) > $bytelen ) {
+ $key = pack( 'H*', md5( $key ) );
+ }
+ $key = str_pad( $key, $bytelen, chr( 0x00 ) );
+ $ipad = str_pad( '', $bytelen, chr( 0x36 ) );
+ $opad = str_pad( '', $bytelen, chr( 0x5c ) );
+ $k_ipad = $key ^ $ipad;
+ $k_opad = $key ^ $opad;
+
+ return md5( $k_opad . pack( 'H*', md5( $k_ipad . $data ) ) );
+ }
+
+ /**
+ * Send an SMTP DATA command.
+ * Issues a data command and sends the msg_data to the server,
+ * finializing the mail transaction. $msg_data is the message
+ * that is to be send with the headers. Each header needs to be
+ * on a single line followed by a with the message headers
+ * and the message body being separated by and additional .
+ * Implements rfc 821: DATA
+ *
+ * @param string $msg_data Message data to send
+ *
+ * @access public
+ * @return boolean
+ */
+ public function data( $msg_data ) {
+ //This will use the standard timelimit
+ if ( !$this->sendCommand( 'DATA', 'DATA', 354 ) ) {
+ return false;
+ }
+
+ /* The server is ready to accept data!
+ * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
+ * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
+ * smaller lines to fit within the limit.
+ * We will also look for lines that start with a '.' and prepend an additional '.'.
+ * NOTE: this does not count towards line-length limit.
+ */
+
+ // Normalize line breaks before exploding
+ $lines = explode( "\n", str_replace( array( "\r\n", "\r" ), "\n", $msg_data ) );
+
+ /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
+ * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
+ * process all lines before a blank line as headers.
+ */
+
+ $field = substr( $lines[ 0 ], 0, strpos( $lines[ 0 ], ':' ) );
+ $in_headers = false;
+ if ( !empty( $field ) && strpos( $field, ' ' ) === false ) {
+ $in_headers = true;
+ }
+
+ foreach ( $lines as $line ) {
+ $lines_out = array();
+ if ( $in_headers and $line == '' ) {
+ $in_headers = false;
+ }
+ //Break this line up into several smaller lines if it's too long
+ //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
+ while ( isset( $line[ self::MAX_LINE_LENGTH ] ) ) {
+ //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
+ //so as to avoid breaking in the middle of a word
+ $pos = strrpos( substr( $line, 0, self::MAX_LINE_LENGTH ), ' ' );
+ //Deliberately matches both false and 0
+ if ( !$pos ) {
+ //No nice break found, add a hard break
+ $pos = self::MAX_LINE_LENGTH - 1;
+ $lines_out[] = substr( $line, 0, $pos );
+ $line = substr( $line, $pos );
+ } else {
+ //Break at the found point
+ $lines_out[] = substr( $line, 0, $pos );
+ //Move along by the amount we dealt with
+ $line = substr( $line, $pos + 1 );
+ }
+ //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
+ if ( $in_headers ) {
+ $line = "\t" . $line;
+ }
+ }
+ $lines_out[] = $line;
+
+ //Send the lines to the server
+ foreach ( $lines_out as $line_out ) {
+ //RFC2821 section 4.5.2
+ if ( !empty( $line_out ) and $line_out[ 0 ] == '.' ) {
+ $line_out = '.' . $line_out;
+ }
+ $this->client_send( $line_out . self::CRLF );
+ }
+ }
+
+ //Message data has been sent, complete the command
+ //Increase timelimit for end of DATA command
+ $savetimelimit = $this->Timelimit;
+ $this->Timelimit = $this->Timelimit * 2;
+ $result = $this->sendCommand( 'DATA END', '.', 250 );
+ //Restore timelimit
+ $this->Timelimit = $savetimelimit;
+
+ return $result;
+ }
+
+ /**
+ * Send an SMTP HELO or EHLO command.
+ * Used to identify the sending server to the receiving server.
+ * This makes sure that client and server are in a known state.
+ * Implements RFC 821: HELO
+ * and RFC 2821 EHLO.
+ *
+ * @param string $host The host name or IP to connect to
+ *
+ * @access public
+ * @return boolean
+ */
+ public function hello( $host = '' ) {
+ //Try extended hello first (RFC 2821)
+ return (boolean)( $this->sendHello( 'EHLO', $host ) or $this->sendHello( 'HELO', $host ) );
+ }
+
+ /**
+ * Send an SMTP HELO or EHLO command.
+ * Low-level implementation used by hello()
+ * @see hello()
+ *
+ * @param string $hello The HELO string
+ * @param string $host The hostname to say we are
+ *
+ * @access protected
+ * @return boolean
+ */
+ protected function sendHello( $hello, $host ) {
+ $noerror = $this->sendCommand( $hello, $hello . ' ' . $host, 250 );
+ $this->helo_rply = $this->last_reply;
+ if ( $noerror ) {
+ $this->parseHelloFields( $hello );
+ } else {
+ $this->server_caps = null;
+ }
+
+ return $noerror;
+ }
+
+ /**
+ * Parse a reply to HELO/EHLO command to discover server extensions.
+ * In case of HELO, the only parameter that can be discovered is a server name.
+ * @access protected
+ *
+ * @param string $type - 'HELO' or 'EHLO'
+ */
+ protected function parseHelloFields( $type ) {
+ $this->server_caps = array();
+ $lines = explode( "\n", $this->last_reply );
+
+ foreach ( $lines as $n => $s ) {
+ //First 4 chars contain response code followed by - or space
+ $s = trim( substr( $s, 4 ) );
+ if ( empty( $s ) ) {
+ continue;
+ }
+ $fields = explode( ' ', $s );
+ if ( !empty( $fields ) ) {
+ if ( !$n ) {
+ $name = $type;
+ $fields = $fields[ 0 ];
+ } else {
+ $name = array_shift( $fields );
+ switch ( $name ) {
+ case 'SIZE':
+ $fields = ( $fields ? $fields[ 0 ] : 0 );
+ break;
+ case 'AUTH':
+ if ( !is_array( $fields ) ) {
+ $fields = array();
+ }
+ break;
+ default:
+ $fields = true;
+ }
+ }
+ $this->server_caps[ $name ] = $fields;
+ }
+ }
+ }
+
+ /**
+ * Send an SMTP MAIL command.
+ * Starts a mail transaction from the email address specified in
+ * $from. Returns true if successful or false otherwise. If True
+ * the mail transaction is started and then one or more recipient
+ * commands may be called followed by a data command.
+ * Implements rfc 821: MAIL FROM:
+ *
+ * @param string $from Source address of this message
+ *
+ * @access public
+ * @return boolean
+ */
+ public function mail( $from ) {
+ $useVerp = ( $this->do_verp ? ' XVERP' : '' );
+
+ return $this->sendCommand(
+ 'MAIL FROM',
+ 'MAIL FROM:<' . $from . '>' . $useVerp,
+ 250
+ );
+ }
+
+ /**
+ * Send an SMTP QUIT command.
+ * Closes the socket if there is no error or the $close_on_error argument is true.
+ * Implements from rfc 821: QUIT
+ *
+ * @param boolean $close_on_error Should the connection close if an error occurs?
+ *
+ * @access public
+ * @return boolean
+ */
+ public function quit( $close_on_error = true ) {
+ $noerror = $this->sendCommand( 'QUIT', 'QUIT', 221 );
+ $err = $this->error; //Save any error
+ if ( $noerror or $close_on_error ) {
+ $this->close();
+ $this->error = $err; //Restore any error from the quit command
+ }
+
+ return $noerror;
+ }
+
+ /**
+ * Send an SMTP RCPT command.
+ * Sets the TO argument to $toaddr.
+ * Returns true if the recipient was accepted false if it was rejected.
+ * Implements from rfc 821: RCPT TO:
+ *
+ * @param string $address The address the message is being sent to
+ *
+ * @access public
+ * @return boolean
+ */
+ public function recipient( $address ) {
+ return $this->sendCommand(
+ 'RCPT TO',
+ 'RCPT TO:<' . $address . '>',
+ array( 250, 251 )
+ );
+ }
+
+ /**
+ * Send an SMTP RSET command.
+ * Abort any transaction that is currently in progress.
+ * Implements rfc 821: RSET
+ * @access public
+ * @return boolean True on success.
+ */
+ public function reset() {
+ return $this->sendCommand( 'RSET', 'RSET', 250 );
+ }
+
+ /**
+ * Send an SMTP SAML command.
+ * Starts a mail transaction from the email address specified in $from.
+ * Returns true if successful or false otherwise. If True
+ * the mail transaction is started and then one or more recipient
+ * commands may be called followed by a data command. This command
+ * will send the message to the users terminal if they are logged
+ * in and send them an email.
+ * Implements rfc 821: SAML FROM:
+ *
+ * @param string $from The address the message is from
+ *
+ * @access public
+ * @return boolean
+ */
+ public function sendAndMail( $from ) {
+ return $this->sendCommand( 'SAML', "SAML FROM:$from", 250 );
+ }
+
+ /**
+ * Send an SMTP VRFY command.
+ *
+ * @param string $name The name to verify
+ *
+ * @access public
+ * @return boolean
+ */
+ public function verify( $name ) {
+ return $this->sendCommand( 'VRFY', "VRFY $name", array( 250, 251 ) );
+ }
+
+ /**
+ * Send an SMTP NOOP command.
+ * Used to keep keep-alives alive, doesn't actually do anything
+ * @access public
+ * @return boolean
+ */
+ public function noop() {
+ return $this->sendCommand( 'NOOP', 'NOOP', 250 );
+ }
+
+ /**
+ * Send an SMTP TURN command.
+ * This is an optional command for SMTP that this class does not support.
+ * This method is here to make the RFC821 Definition complete for this class
+ * and _may_ be implemented in future
+ * Implements from rfc 821: TURN
+ * @access public
+ * @return boolean
+ */
+ public function turn() {
+ $this->setError( 'The SMTP TURN command is not implemented' );
+ $this->edebug( 'SMTP NOTICE: ' . $this->error[ 'error' ], self::DEBUG_CLIENT );
+
+ return false;
+ }
+
+ /**
+ * Get the latest error.
+ * @access public
+ * @return array
+ */
+ public function getError() {
+ return $this->error;
+ }
+
+ /**
+ * Set error messages and codes.
+ *
+ * @param string $message The error message
+ * @param string $detail Further detail on the error
+ * @param string $smtp_code An associated SMTP error code
+ * @param string $smtp_code_ex Extended SMTP code
+ */
+ protected function setError( $message, $detail = '', $smtp_code = '', $smtp_code_ex = '' ) {
+ $this->error = array(
+ 'error' => $message,
+ 'detail' => $detail,
+ 'smtp_code' => $smtp_code,
+ 'smtp_code_ex' => $smtp_code_ex
+ );
+ }
+
+ /**
+ * Get SMTP extensions available on the server
+ * @access public
+ * @return array|null
+ */
+ public function getServerExtList() {
+ return $this->server_caps;
+ }
+
+ /**
+ * A multipurpose method
+ * The method works in three ways, dependent on argument value and current state
+ * 1. HELO/EHLO was not sent - returns null and set up $this->error
+ * 2. HELO was sent
+ * $name = 'HELO': returns server name
+ * $name = 'EHLO': returns boolean false
+ * $name = any string: returns null and set up $this->error
+ * 3. EHLO was sent
+ * $name = 'HELO'|'EHLO': returns server name
+ * $name = any string: if extension $name exists, returns boolean True
+ * or its options. Otherwise returns boolean False
+ * In other words, one can use this method to detect 3 conditions:
+ * - null returned: handshake was not or we don't know about ext (refer to $this->error)
+ * - false returned: the requested feature exactly not exists
+ * - positive value returned: the requested feature exists
+ *
+ * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
+ *
+ * @return mixed
+ */
+ public function getServerExt( $name ) {
+ if ( !$this->server_caps ) {
+ $this->setError( 'No HELO/EHLO was sent' );
+
+ return null;
+ }
+
+ // the tight logic knot ;)
+ if ( !array_key_exists( $name, $this->server_caps ) ) {
+ if ( $name == 'HELO' ) {
+ return $this->server_caps[ 'EHLO' ];
+ }
+ if ( $name == 'EHLO' || array_key_exists( 'EHLO', $this->server_caps ) ) {
+ return false;
+ }
+ $this->setError( 'HELO handshake was used. Client knows nothing about server extensions' );
+
+ return null;
+ }
+
+ return $this->server_caps[ $name ];
+ }
+
+ /**
+ * Get the last reply from the server.
+ * @access public
+ * @return string
+ */
+ public function getLastReply() {
+ return $this->last_reply;
+ }
+
+ /**
+ * Enable or disable VERP address generation.
+ *
+ * @param boolean $enabled
+ */
+ public function setVerp( $enabled = false ) {
+ $this->do_verp = $enabled;
+ }
+
+ /**
+ * Get VERP address generation mode.
+ * @return boolean
+ */
+ public function getVerp() {
+ return $this->do_verp;
+ }
+
+ /**
+ * Get debug output method.
+ * @return string
+ */
+ public function getDebugOutput() {
+ return $this->Debugoutput;
+ }
+
+ /**
+ * Set debug output method.
+ *
+ * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it.
+ */
+ public function setDebugOutput( $method = 'echo' ) {
+ $this->Debugoutput = $method;
+ }
+
+ /**
+ * Set debug output level.
+ *
+ * @param integer $level
+ */
+ public function setDebugLevel( $level = 0 ) {
+ $this->do_debug = $level;
+ }
+
+ /**
+ * Get debug output level.
+ * @return integer
+ */
+ public function getDebugLevel() {
+ return $this->do_debug;
+ }
+
+ /**
+ * Get SMTP timeout.
+ * @return integer
+ */
+ public function getTimeout() {
+ return $this->Timeout;
+ }
+
+ /**
+ * Set SMTP timeout.
+ *
+ * @param integer $timeout
+ */
+ public function setTimeout( $timeout = 0 ) {
+ $this->Timeout = $timeout;
+ }
}
diff --git a/src/lib/mail/mail_default.html b/src/lib/mail/mail_default.html
index 1949812..9c525bd 100644
--- a/src/lib/mail/mail_default.html
+++ b/src/lib/mail/mail_default.html
@@ -1,7 +1,7 @@
-
+
li{list-style-type:none}a{color:#039be5;text-decoration:none;-webkit-tap-highlight-color:transparent}.valign-wrapper{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.clearfix{clear:both}.z-depth-0{-webkit-box-shadow:none !important;box-shadow:none !important}.z-depth-1,nav,.card-panel,.card,.toast,.btn,.btn-large,.btn-small,.btn-floating,.dropdown-content,.collapsible,.sidenav{-webkit-box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2)}.z-depth-1-half,.btn:hover,.btn-large:hover,.btn-small:hover,.btn-floating:hover{-webkit-box-shadow:0 3px 3px 0 rgba(0,0,0,0.14),0 1px 7px 0 rgba(0,0,0,0.12),0 3px 1px -1px rgba(0,0,0,0.2);box-shadow:0 3px 3px 0 rgba(0,0,0,0.14),0 1px 7px 0 rgba(0,0,0,0.12),0 3px 1px -1px rgba(0,0,0,0.2)}.z-depth-2{-webkit-box-shadow:0 4px 5px 0 rgba(0,0,0,0.14),0 1px 10px 0 rgba(0,0,0,0.12),0 2px 4px -1px rgba(0,0,0,0.3);box-shadow:0 4px 5px 0 rgba(0,0,0,0.14),0 1px 10px 0 rgba(0,0,0,0.12),0 2px 4px -1px rgba(0,0,0,0.3)}.z-depth-3{-webkit-box-shadow:0 8px 17px 2px rgba(0,0,0,0.14),0 3px 14px 2px rgba(0,0,0,0.12),0 5px 5px -3px rgba(0,0,0,0.2);box-shadow:0 8px 17px 2px rgba(0,0,0,0.14),0 3px 14px 2px rgba(0,0,0,0.12),0 5px 5px -3px rgba(0,0,0,0.2)}.z-depth-4{-webkit-box-shadow:0 16px 24px 2px rgba(0,0,0,0.14),0 6px 30px 5px rgba(0,0,0,0.12),0 8px 10px -7px rgba(0,0,0,0.2);box-shadow:0 16px 24px 2px rgba(0,0,0,0.14),0 6px 30px 5px rgba(0,0,0,0.12),0 8px 10px -7px rgba(0,0,0,0.2)}.z-depth-5,.modal{-webkit-box-shadow:0 24px 38px 3px rgba(0,0,0,0.14),0 9px 46px 8px rgba(0,0,0,0.12),0 11px 15px -7px rgba(0,0,0,0.2);box-shadow:0 24px 38px 3px rgba(0,0,0,0.14),0 9px 46px 8px rgba(0,0,0,0.12),0 11px 15px -7px rgba(0,0,0,0.2)}.hoverable{-webkit-transition:-webkit-box-shadow .25s;transition:-webkit-box-shadow .25s;transition:box-shadow .25s;transition:box-shadow .25s, -webkit-box-shadow .25s}.hoverable:hover{-webkit-box-shadow:0 8px 17px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);box-shadow:0 8px 17px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}.divider{height:1px;overflow:hidden;background-color:#e0e0e0}blockquote{margin:20px 0;padding-left:1.5rem;border-left:5px solid #ee6e73}i{line-height:inherit}i.left{float:left;margin-right:15px}i.right{float:right;margin-left:15px}i.tiny{font-size:1rem}i.small{font-size:2rem}i.medium{font-size:4rem}i.large{font-size:6rem}img.responsive-img,video.responsive-video{max-width:100%;height:auto}.pagination li{display:inline-block;border-radius:2px;text-align:center;vertical-align:top;height:30px}.pagination li a{color:#444;display:inline-block;font-size:1.2rem;padding:0 10px;line-height:30px}.pagination li.active a{color:#fff}.pagination li.active{background-color:#ee6e73}.pagination li.disabled a{cursor:default;color:#999}.pagination li i{font-size:2rem}.pagination li.pages ul li{display:inline-block;float:none}@media only screen and (max-width: 992px){.pagination{width:100%}.pagination li.prev,.pagination li.next{width:10%}.pagination li.pages{width:80%;overflow:hidden;white-space:nowrap}}.breadcrumb{font-size:18px;color:rgba(255,255,255,0.7)}.breadcrumb i,.breadcrumb [class^="mdi-"],.breadcrumb [class*="mdi-"],.breadcrumb i.material-icons{display:inline-block;float:left;font-size:24px}.breadcrumb:before{content:'\E5CC';color:rgba(255,255,255,0.7);vertical-align:top;display:inline-block;font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:25px;margin:0 10px 0 8px;-webkit-font-smoothing:antialiased}.breadcrumb:first-child:before{display:none}.breadcrumb:last-child{color:#fff}.parallax-container{position:relative;overflow:hidden;height:500px}.parallax-container .parallax{position:absolute;top:0;left:0;right:0;bottom:0;z-index:-1}.parallax-container .parallax img{opacity:0;position:absolute;left:50%;bottom:0;min-width:100%;min-height:100%;-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);-webkit-transform:translateX(-50%);transform:translateX(-50%)}.pin-top,.pin-bottom{position:relative}.pinned{position:fixed !important}ul.staggered-list li{opacity:0}.fade-in{opacity:0;-webkit-transform-origin:0 50%;transform-origin:0 50%}@media only screen and (max-width: 600px){.hide-on-small-only,.hide-on-small-and-down{display:none !important}}@media only screen and (max-width: 992px){.hide-on-med-and-down{display:none !important}}@media only screen and (min-width: 601px){.hide-on-med-and-up{display:none !important}}@media only screen and (min-width: 600px) and (max-width: 992px){.hide-on-med-only{display:none !important}}@media only screen and (min-width: 993px){.hide-on-large-only{display:none !important}}@media only screen and (min-width: 1201px){.hide-on-extra-large-only{display:none !important}}@media only screen and (min-width: 1201px){.show-on-extra-large{display:block !important}}@media only screen and (min-width: 993px){.show-on-large{display:block !important}}@media only screen and (min-width: 600px) and (max-width: 992px){.show-on-medium{display:block !important}}@media only screen and (max-width: 600px){.show-on-small{display:block !important}}@media only screen and (min-width: 601px){.show-on-medium-and-up{display:block !important}}@media only screen and (max-width: 992px){.show-on-medium-and-down{display:block !important}}@media only screen and (max-width: 600px){.center-on-small-only{text-align:center}}.page-footer{padding-top:20px;color:#fff;background-color:#ee6e73}.page-footer .footer-copyright{overflow:hidden;min-height:50px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;padding:10px 0px;color:rgba(255,255,255,0.8);background-color:rgba(51,51,51,0.08)}table,th,td{border:none}table{width:100%;display:table;border-collapse:collapse;border-spacing:0}table.striped tr{border-bottom:none}table.striped>tbody>tr:nth-child(odd){background-color:rgba(242,242,242,0.5)}table.striped>tbody>tr>td{border-radius:0}table.highlight>tbody>tr{-webkit-transition:background-color .25s ease;transition:background-color .25s ease}table.highlight>tbody>tr:hover{background-color:rgba(242,242,242,0.5)}table.centered thead tr th,table.centered tbody tr td{text-align:center}tr{border-bottom:1px solid rgba(0,0,0,0.12)}td,th{padding:15px 5px;display:table-cell;text-align:left;vertical-align:middle;border-radius:2px}@media only screen and (max-width: 992px){table.responsive-table{width:100%;border-collapse:collapse;border-spacing:0;display:block;position:relative}table.responsive-table td:empty:before{content:'\00a0'}table.responsive-table th,table.responsive-table td{margin:0;vertical-align:top}table.responsive-table th{text-align:left}table.responsive-table thead{display:block;float:left}table.responsive-table thead tr{display:block;padding:0 10px 0 0}table.responsive-table thead tr th::before{content:"\00a0"}table.responsive-table tbody{display:block;width:auto;position:relative;overflow-x:auto;white-space:nowrap}table.responsive-table tbody tr{display:inline-block;vertical-align:top}table.responsive-table th{display:block;text-align:right}table.responsive-table td{display:block;min-height:1.25em;text-align:left}table.responsive-table tr{border-bottom:none;padding:0 10px}table.responsive-table thead{border:0;border-right:1px solid rgba(0,0,0,0.12)}}.collection{margin:.5rem 0 1rem 0;border:1px solid #e0e0e0;border-radius:2px;overflow:hidden;position:relative}.collection .collection-item{background-color:#fff;line-height:1.5rem;padding:10px 20px;margin:0;border-bottom:1px solid #e0e0e0}.collection .collection-item.avatar{min-height:84px;padding-left:72px;position:relative}.collection .collection-item.avatar:not(.circle-clipper)>.circle,.collection .collection-item.avatar :not(.circle-clipper)>.circle{position:absolute;width:42px;height:42px;overflow:hidden;left:15px;display:inline-block;vertical-align:middle}.collection .collection-item.avatar i.circle{font-size:18px;line-height:42px;color:#fff;background-color:#999;text-align:center}.collection .collection-item.avatar .title{font-size:16px}.collection .collection-item.avatar p{margin:0}.collection .collection-item.avatar .secondary-content{position:absolute;top:16px;right:16px}.collection .collection-item:last-child{border-bottom:none}.collection .collection-item.active{background-color:#26a69a;color:#eafaf9}.collection .collection-item.active .secondary-content{color:#fff}.collection a.collection-item{display:block;-webkit-transition:.25s;transition:.25s;color:#26a69a}.collection a.collection-item:not(.active):hover{background-color:#ddd}.collection.with-header .collection-header{background-color:#fff;border-bottom:1px solid #e0e0e0;padding:10px 20px}.collection.with-header .collection-item{padding-left:30px}.collection.with-header .collection-item.avatar{padding-left:72px}.secondary-content{float:right;color:#26a69a}.collapsible .collection{margin:0;border:none}.video-container{position:relative;padding-bottom:56.25%;height:0;overflow:hidden}.video-container iframe,.video-container object,.video-container embed{position:absolute;top:0;left:0;width:100%;height:100%}.progress{position:relative;height:4px;display:block;width:100%;background-color:#acece6;border-radius:2px;margin:.5rem 0 1rem 0;overflow:hidden}.progress .determinate{position:absolute;top:0;left:0;bottom:0;background-color:#26a69a;-webkit-transition:width .3s linear;transition:width .3s linear}.progress .indeterminate{background-color:#26a69a}.progress .indeterminate:before{content:'';position:absolute;background-color:inherit;top:0;left:0;bottom:0;will-change:left, right;-webkit-animation:indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;animation:indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite}.progress .indeterminate:after{content:'';position:absolute;background-color:inherit;top:0;left:0;bottom:0;will-change:left, right;-webkit-animation:indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;animation:indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;-webkit-animation-delay:1.15s;animation-delay:1.15s}@-webkit-keyframes indeterminate{0%{left:-35%;right:100%}60%{left:100%;right:-90%}100%{left:100%;right:-90%}}@keyframes indeterminate{0%{left:-35%;right:100%}60%{left:100%;right:-90%}100%{left:100%;right:-90%}}@-webkit-keyframes indeterminate-short{0%{left:-200%;right:100%}60%{left:107%;right:-8%}100%{left:107%;right:-8%}}@keyframes indeterminate-short{0%{left:-200%;right:100%}60%{left:107%;right:-8%}100%{left:107%;right:-8%}}.hide{display:none !important}.left-align{text-align:left}.right-align{text-align:right}.center,.center-align{text-align:center}.left{float:left !important}.right{float:right !important}.no-select,input[type=range],input[type=range]+.thumb{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.circle{border-radius:50%}.center-block{display:block;margin-left:auto;margin-right:auto}.truncate{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.no-padding{padding:0 !important}span.badge{min-width:3rem;padding:0 6px;margin-left:14px;text-align:center;font-size:1rem;line-height:22px;height:22px;color:#757575;float:right;-webkit-box-sizing:border-box;box-sizing:border-box}span.badge.new{font-weight:300;font-size:0.8rem;color:#fff;background-color:#26a69a;border-radius:2px}span.badge.new:after{content:" new"}span.badge[data-badge-caption]::after{content:" " attr(data-badge-caption)}nav ul a span.badge{display:inline-block;float:none;margin-left:4px;line-height:22px;height:22px;-webkit-font-smoothing:auto}.collection-item span.badge{margin-top:calc(.75rem - 11px)}.collapsible span.badge{margin-left:auto}.sidenav span.badge{margin-top:calc(24px - 11px)}table span.badge{display:inline-block;float:none;margin-left:auto}.material-icons{text-rendering:optimizeLegibility;-webkit-font-feature-settings:'liga';-moz-font-feature-settings:'liga';font-feature-settings:'liga'}.container{margin:0 auto;max-width:1280px;width:90%}@media only screen and (min-width: 601px){.container{width:85%}}@media only screen and (min-width: 993px){.container{width:70%}}.col .row{margin-left:-.75rem;margin-right:-.75rem}.section{padding-top:1rem;padding-bottom:1rem}.section.no-pad{padding:0}.section.no-pad-bot{padding-bottom:0}.section.no-pad-top{padding-top:0}.row{margin-left:auto;margin-right:auto;margin-bottom:20px}.row:after{content:"";display:table;clear:both}.row .col{float:left;-webkit-box-sizing:border-box;box-sizing:border-box;padding:0 .75rem;min-height:1px}.row .col[class*="push-"],.row .col[class*="pull-"]{position:relative}.row .col.s1{width:8.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.s2{width:16.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.s3{width:25%;margin-left:auto;left:auto;right:auto}.row .col.s4{width:33.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.s5{width:41.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.s6{width:50%;margin-left:auto;left:auto;right:auto}.row .col.s7{width:58.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.s8{width:66.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.s9{width:75%;margin-left:auto;left:auto;right:auto}.row .col.s10{width:83.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.s11{width:91.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.s12{width:100%;margin-left:auto;left:auto;right:auto}.row .col.offset-s1{margin-left:8.3333333333%}.row .col.pull-s1{right:8.3333333333%}.row .col.push-s1{left:8.3333333333%}.row .col.offset-s2{margin-left:16.6666666667%}.row .col.pull-s2{right:16.6666666667%}.row .col.push-s2{left:16.6666666667%}.row .col.offset-s3{margin-left:25%}.row .col.pull-s3{right:25%}.row .col.push-s3{left:25%}.row .col.offset-s4{margin-left:33.3333333333%}.row .col.pull-s4{right:33.3333333333%}.row .col.push-s4{left:33.3333333333%}.row .col.offset-s5{margin-left:41.6666666667%}.row .col.pull-s5{right:41.6666666667%}.row .col.push-s5{left:41.6666666667%}.row .col.offset-s6{margin-left:50%}.row .col.pull-s6{right:50%}.row .col.push-s6{left:50%}.row .col.offset-s7{margin-left:58.3333333333%}.row .col.pull-s7{right:58.3333333333%}.row .col.push-s7{left:58.3333333333%}.row .col.offset-s8{margin-left:66.6666666667%}.row .col.pull-s8{right:66.6666666667%}.row .col.push-s8{left:66.6666666667%}.row .col.offset-s9{margin-left:75%}.row .col.pull-s9{right:75%}.row .col.push-s9{left:75%}.row .col.offset-s10{margin-left:83.3333333333%}.row .col.pull-s10{right:83.3333333333%}.row .col.push-s10{left:83.3333333333%}.row .col.offset-s11{margin-left:91.6666666667%}.row .col.pull-s11{right:91.6666666667%}.row .col.push-s11{left:91.6666666667%}.row .col.offset-s12{margin-left:100%}.row .col.pull-s12{right:100%}.row .col.push-s12{left:100%}@media only screen and (min-width: 601px){.row .col.m1{width:8.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.m2{width:16.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.m3{width:25%;margin-left:auto;left:auto;right:auto}.row .col.m4{width:33.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.m5{width:41.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.m6{width:50%;margin-left:auto;left:auto;right:auto}.row .col.m7{width:58.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.m8{width:66.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.m9{width:75%;margin-left:auto;left:auto;right:auto}.row .col.m10{width:83.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.m11{width:91.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.m12{width:100%;margin-left:auto;left:auto;right:auto}.row .col.offset-m1{margin-left:8.3333333333%}.row .col.pull-m1{right:8.3333333333%}.row .col.push-m1{left:8.3333333333%}.row .col.offset-m2{margin-left:16.6666666667%}.row .col.pull-m2{right:16.6666666667%}.row .col.push-m2{left:16.6666666667%}.row .col.offset-m3{margin-left:25%}.row .col.pull-m3{right:25%}.row .col.push-m3{left:25%}.row .col.offset-m4{margin-left:33.3333333333%}.row .col.pull-m4{right:33.3333333333%}.row .col.push-m4{left:33.3333333333%}.row .col.offset-m5{margin-left:41.6666666667%}.row .col.pull-m5{right:41.6666666667%}.row .col.push-m5{left:41.6666666667%}.row .col.offset-m6{margin-left:50%}.row .col.pull-m6{right:50%}.row .col.push-m6{left:50%}.row .col.offset-m7{margin-left:58.3333333333%}.row .col.pull-m7{right:58.3333333333%}.row .col.push-m7{left:58.3333333333%}.row .col.offset-m8{margin-left:66.6666666667%}.row .col.pull-m8{right:66.6666666667%}.row .col.push-m8{left:66.6666666667%}.row .col.offset-m9{margin-left:75%}.row .col.pull-m9{right:75%}.row .col.push-m9{left:75%}.row .col.offset-m10{margin-left:83.3333333333%}.row .col.pull-m10{right:83.3333333333%}.row .col.push-m10{left:83.3333333333%}.row .col.offset-m11{margin-left:91.6666666667%}.row .col.pull-m11{right:91.6666666667%}.row .col.push-m11{left:91.6666666667%}.row .col.offset-m12{margin-left:100%}.row .col.pull-m12{right:100%}.row .col.push-m12{left:100%}}@media only screen and (min-width: 993px){.row .col.l1{width:8.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.l2{width:16.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.l3{width:25%;margin-left:auto;left:auto;right:auto}.row .col.l4{width:33.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.l5{width:41.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.l6{width:50%;margin-left:auto;left:auto;right:auto}.row .col.l7{width:58.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.l8{width:66.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.l9{width:75%;margin-left:auto;left:auto;right:auto}.row .col.l10{width:83.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.l11{width:91.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.l12{width:100%;margin-left:auto;left:auto;right:auto}.row .col.offset-l1{margin-left:8.3333333333%}.row .col.pull-l1{right:8.3333333333%}.row .col.push-l1{left:8.3333333333%}.row .col.offset-l2{margin-left:16.6666666667%}.row .col.pull-l2{right:16.6666666667%}.row .col.push-l2{left:16.6666666667%}.row .col.offset-l3{margin-left:25%}.row .col.pull-l3{right:25%}.row .col.push-l3{left:25%}.row .col.offset-l4{margin-left:33.3333333333%}.row .col.pull-l4{right:33.3333333333%}.row .col.push-l4{left:33.3333333333%}.row .col.offset-l5{margin-left:41.6666666667%}.row .col.pull-l5{right:41.6666666667%}.row .col.push-l5{left:41.6666666667%}.row .col.offset-l6{margin-left:50%}.row .col.pull-l6{right:50%}.row .col.push-l6{left:50%}.row .col.offset-l7{margin-left:58.3333333333%}.row .col.pull-l7{right:58.3333333333%}.row .col.push-l7{left:58.3333333333%}.row .col.offset-l8{margin-left:66.6666666667%}.row .col.pull-l8{right:66.6666666667%}.row .col.push-l8{left:66.6666666667%}.row .col.offset-l9{margin-left:75%}.row .col.pull-l9{right:75%}.row .col.push-l9{left:75%}.row .col.offset-l10{margin-left:83.3333333333%}.row .col.pull-l10{right:83.3333333333%}.row .col.push-l10{left:83.3333333333%}.row .col.offset-l11{margin-left:91.6666666667%}.row .col.pull-l11{right:91.6666666667%}.row .col.push-l11{left:91.6666666667%}.row .col.offset-l12{margin-left:100%}.row .col.pull-l12{right:100%}.row .col.push-l12{left:100%}}@media only screen and (min-width: 1201px){.row .col.xl1{width:8.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.xl2{width:16.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.xl3{width:25%;margin-left:auto;left:auto;right:auto}.row .col.xl4{width:33.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.xl5{width:41.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.xl6{width:50%;margin-left:auto;left:auto;right:auto}.row .col.xl7{width:58.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.xl8{width:66.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.xl9{width:75%;margin-left:auto;left:auto;right:auto}.row .col.xl10{width:83.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.xl11{width:91.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.xl12{width:100%;margin-left:auto;left:auto;right:auto}.row .col.offset-xl1{margin-left:8.3333333333%}.row .col.pull-xl1{right:8.3333333333%}.row .col.push-xl1{left:8.3333333333%}.row .col.offset-xl2{margin-left:16.6666666667%}.row .col.pull-xl2{right:16.6666666667%}.row .col.push-xl2{left:16.6666666667%}.row .col.offset-xl3{margin-left:25%}.row .col.pull-xl3{right:25%}.row .col.push-xl3{left:25%}.row .col.offset-xl4{margin-left:33.3333333333%}.row .col.pull-xl4{right:33.3333333333%}.row .col.push-xl4{left:33.3333333333%}.row .col.offset-xl5{margin-left:41.6666666667%}.row .col.pull-xl5{right:41.6666666667%}.row .col.push-xl5{left:41.6666666667%}.row .col.offset-xl6{margin-left:50%}.row .col.pull-xl6{right:50%}.row .col.push-xl6{left:50%}.row .col.offset-xl7{margin-left:58.3333333333%}.row .col.pull-xl7{right:58.3333333333%}.row .col.push-xl7{left:58.3333333333%}.row .col.offset-xl8{margin-left:66.6666666667%}.row .col.pull-xl8{right:66.6666666667%}.row .col.push-xl8{left:66.6666666667%}.row .col.offset-xl9{margin-left:75%}.row .col.pull-xl9{right:75%}.row .col.push-xl9{left:75%}.row .col.offset-xl10{margin-left:83.3333333333%}.row .col.pull-xl10{right:83.3333333333%}.row .col.push-xl10{left:83.3333333333%}.row .col.offset-xl11{margin-left:91.6666666667%}.row .col.pull-xl11{right:91.6666666667%}.row .col.push-xl11{left:91.6666666667%}.row .col.offset-xl12{margin-left:100%}.row .col.pull-xl12{right:100%}.row .col.push-xl12{left:100%}}nav{color:#fff;background-color:#ee6e73;width:100%;height:56px;line-height:56px}nav.nav-extended{height:auto}nav.nav-extended .nav-wrapper{min-height:56px;height:auto}nav.nav-extended .nav-content{position:relative;line-height:normal}nav a{color:#fff}nav i,nav [class^="mdi-"],nav [class*="mdi-"],nav i.material-icons{display:block;font-size:24px;height:56px;line-height:56px}nav .nav-wrapper{position:relative;height:100%}@media only screen and (min-width: 993px){nav a.sidenav-trigger{display:none}}nav .sidenav-trigger{float:left;position:relative;z-index:1;height:56px;margin:0 18px}nav .sidenav-trigger i{height:56px;line-height:56px}nav .brand-logo{position:absolute;color:#fff;display:inline-block;font-size:2.1rem;padding:0}nav .brand-logo.center{left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}@media only screen and (max-width: 992px){nav .brand-logo{left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}nav .brand-logo.left,nav .brand-logo.right{padding:0;-webkit-transform:none;transform:none}nav .brand-logo.left{left:0.5rem}nav .brand-logo.right{right:0.5rem;left:auto}}nav .brand-logo.right{right:0.5rem;padding:0}nav .brand-logo i,nav .brand-logo [class^="mdi-"],nav .brand-logo [class*="mdi-"],nav .brand-logo i.material-icons{float:left;margin-right:15px}nav .nav-title{display:inline-block;font-size:32px;padding:28px 0}nav ul{margin:0}nav ul li{-webkit-transition:background-color .3s;transition:background-color .3s;float:left;padding:0}nav ul li.active{background-color:rgba(0,0,0,0.1)}nav ul a{-webkit-transition:background-color .3s;transition:background-color .3s;font-size:1rem;color:#fff;display:block;padding:0 15px;cursor:pointer}nav ul a.btn,nav ul a.btn-large,nav ul a.btn-small,nav ul a.btn-large,nav ul a.btn-flat,nav ul a.btn-floating{margin-top:-2px;margin-left:15px;margin-right:15px}nav ul a.btn>.material-icons,nav ul a.btn-large>.material-icons,nav ul a.btn-small>.material-icons,nav ul a.btn-large>.material-icons,nav ul a.btn-flat>.material-icons,nav ul a.btn-floating>.material-icons{height:inherit;line-height:inherit}nav ul a:hover{background-color:rgba(0,0,0,0.1)}nav ul.left{float:left}nav form{height:100%}nav .input-field{margin:0;height:100%}nav .input-field input{height:100%;font-size:1.2rem;border:none;padding-left:2rem}nav .input-field input:focus,nav .input-field input[type=text]:valid,nav .input-field input[type=password]:valid,nav .input-field input[type=email]:valid,nav .input-field input[type=url]:valid,nav .input-field input[type=date]:valid{border:none;-webkit-box-shadow:none;box-shadow:none}nav .input-field label{top:0;left:0}nav .input-field label i{color:rgba(255,255,255,0.7);-webkit-transition:color .3s;transition:color .3s}nav .input-field label.active i{color:#fff}.navbar-fixed{position:relative;height:56px;z-index:997}.navbar-fixed nav{position:fixed}@media only screen and (min-width: 601px){nav.nav-extended .nav-wrapper{min-height:64px}nav,nav .nav-wrapper i,nav a.sidenav-trigger,nav a.sidenav-trigger i{height:64px;line-height:64px}.navbar-fixed{height:64px}}a{text-decoration:none}html{line-height:1.5;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-weight:normal;color:rgba(0,0,0,0.87)}@media only screen and (min-width: 0){html{font-size:14px}}@media only screen and (min-width: 992px){html{font-size:14.5px}}@media only screen and (min-width: 1200px){html{font-size:15px}}h1,h2,h3,h4,h5,h6{font-weight:400;line-height:1.3}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{font-weight:inherit}h1{font-size:4.2rem;line-height:110%;margin:2.8rem 0 1.68rem 0}h2{font-size:3.56rem;line-height:110%;margin:2.3733333333rem 0 1.424rem 0}h3{font-size:2.92rem;line-height:110%;margin:1.9466666667rem 0 1.168rem 0}h4{font-size:2.28rem;line-height:110%;margin:1.52rem 0 .912rem 0}h5{font-size:1.64rem;line-height:110%;margin:1.0933333333rem 0 .656rem 0}h6{font-size:1.15rem;line-height:110%;margin:.7666666667rem 0 .46rem 0}em{font-style:italic}strong{font-weight:500}small{font-size:75%}.light{font-weight:300}.thin{font-weight:200}@media only screen and (min-width: 360px){.flow-text{font-size:1.2rem}}@media only screen and (min-width: 390px){.flow-text{font-size:1.224rem}}@media only screen and (min-width: 420px){.flow-text{font-size:1.248rem}}@media only screen and (min-width: 450px){.flow-text{font-size:1.272rem}}@media only screen and (min-width: 480px){.flow-text{font-size:1.296rem}}@media only screen and (min-width: 510px){.flow-text{font-size:1.32rem}}@media only screen and (min-width: 540px){.flow-text{font-size:1.344rem}}@media only screen and (min-width: 570px){.flow-text{font-size:1.368rem}}@media only screen and (min-width: 600px){.flow-text{font-size:1.392rem}}@media only screen and (min-width: 630px){.flow-text{font-size:1.416rem}}@media only screen and (min-width: 660px){.flow-text{font-size:1.44rem}}@media only screen and (min-width: 690px){.flow-text{font-size:1.464rem}}@media only screen and (min-width: 720px){.flow-text{font-size:1.488rem}}@media only screen and (min-width: 750px){.flow-text{font-size:1.512rem}}@media only screen and (min-width: 780px){.flow-text{font-size:1.536rem}}@media only screen and (min-width: 810px){.flow-text{font-size:1.56rem}}@media only screen and (min-width: 840px){.flow-text{font-size:1.584rem}}@media only screen and (min-width: 870px){.flow-text{font-size:1.608rem}}@media only screen and (min-width: 900px){.flow-text{font-size:1.632rem}}@media only screen and (min-width: 930px){.flow-text{font-size:1.656rem}}@media only screen and (min-width: 960px){.flow-text{font-size:1.68rem}}@media only screen and (max-width: 360px){.flow-text{font-size:1.2rem}}.scale-transition{-webkit-transition:-webkit-transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;transition:-webkit-transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;transition:transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;transition:transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63), -webkit-transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important}.scale-transition.scale-out{-webkit-transform:scale(0);transform:scale(0);-webkit-transition:-webkit-transform .2s !important;transition:-webkit-transform .2s !important;transition:transform .2s !important;transition:transform .2s, -webkit-transform .2s !important}.scale-transition.scale-in{-webkit-transform:scale(1);transform:scale(1)}.card-panel{-webkit-transition:-webkit-box-shadow .25s;transition:-webkit-box-shadow .25s;transition:box-shadow .25s;transition:box-shadow .25s, -webkit-box-shadow .25s;padding:24px;margin:.5rem 0 1rem 0;border-radius:2px;background-color:#fff}.card{position:relative;margin:.5rem 0 1rem 0;background-color:#fff;-webkit-transition:-webkit-box-shadow .25s;transition:-webkit-box-shadow .25s;transition:box-shadow .25s;transition:box-shadow .25s, -webkit-box-shadow .25s;border-radius:2px}.card .card-title{font-size:24px;font-weight:300}.card .card-title.activator{cursor:pointer}.card.small,.card.medium,.card.large{position:relative}.card.small .card-image,.card.medium .card-image,.card.large .card-image{max-height:60%;overflow:hidden}.card.small .card-image+.card-content,.card.medium .card-image+.card-content,.card.large .card-image+.card-content{max-height:40%}.card.small .card-content,.card.medium .card-content,.card.large .card-content{max-height:100%;overflow:hidden}.card.small .card-action,.card.medium .card-action,.card.large .card-action{position:absolute;bottom:0;left:0;right:0}.card.small{height:300px}.card.medium{height:400px}.card.large{height:500px}.card.horizontal{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.card.horizontal.small .card-image,.card.horizontal.medium .card-image,.card.horizontal.large .card-image{height:100%;max-height:none;overflow:visible}.card.horizontal.small .card-image img,.card.horizontal.medium .card-image img,.card.horizontal.large .card-image img{height:100%}.card.horizontal .card-image{max-width:50%}.card.horizontal .card-image img{border-radius:2px 0 0 2px;max-width:100%;width:auto}.card.horizontal .card-stacked{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;position:relative}.card.horizontal .card-stacked .card-content{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.card.sticky-action .card-action{z-index:2}.card.sticky-action .card-reveal{z-index:1;padding-bottom:64px}.card .card-image{position:relative}.card .card-image img{display:block;border-radius:2px 2px 0 0;position:relative;left:0;right:0;top:0;bottom:0;width:100%}.card .card-image .card-title{color:#fff;position:absolute;bottom:0;left:0;max-width:100%;padding:24px}.card .card-content{padding:24px;border-radius:0 0 2px 2px}.card .card-content p{margin:0}.card .card-content .card-title{display:block;line-height:32px;margin-bottom:8px}.card .card-content .card-title i{line-height:32px}.card .card-action{background-color:inherit;border-top:1px solid rgba(160,160,160,0.2);position:relative;padding:16px 24px}.card .card-action:last-child{border-radius:0 0 2px 2px}.card .card-action a:not(.btn):not(.btn-large):not(.btn-small):not(.btn-large):not(.btn-floating){color:#ffab40;margin-right:24px;-webkit-transition:color .3s ease;transition:color .3s ease;text-transform:uppercase}.card .card-action a:not(.btn):not(.btn-large):not(.btn-small):not(.btn-large):not(.btn-floating):hover{color:#ffd8a6}.card .card-reveal{padding:24px;position:absolute;background-color:#fff;width:100%;overflow-y:auto;left:0;top:100%;height:100%;z-index:3;display:none}.card .card-reveal .card-title{cursor:pointer;display:block}#toast-container{display:block;position:fixed;z-index:10000}@media only screen and (max-width: 600px){#toast-container{min-width:100%;bottom:0%}}@media only screen and (min-width: 601px) and (max-width: 992px){#toast-container{left:5%;bottom:7%;max-width:90%}}@media only screen and (min-width: 993px){#toast-container{top:10%;right:7%;max-width:86%}}.toast{border-radius:2px;top:35px;width:auto;margin-top:10px;position:relative;max-width:100%;height:auto;min-height:48px;line-height:1.5em;background-color:#323232;padding:10px 25px;font-size:1.1rem;font-weight:300;color:#fff;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;cursor:default}.toast .toast-action{color:#eeff41;font-weight:500;margin-right:-25px;margin-left:3rem}.toast.rounded{border-radius:24px}@media only screen and (max-width: 600px){.toast{width:100%;border-radius:0}}.tabs{position:relative;overflow-x:auto;overflow-y:hidden;height:48px;width:100%;background-color:#fff;margin:0 auto;white-space:nowrap}.tabs.tabs-transparent{background-color:transparent}.tabs.tabs-transparent .tab a,.tabs.tabs-transparent .tab.disabled a,.tabs.tabs-transparent .tab.disabled a:hover{color:rgba(255,255,255,0.7)}.tabs.tabs-transparent .tab a:hover,.tabs.tabs-transparent .tab a.active{color:#fff}.tabs.tabs-transparent .indicator{background-color:#fff}.tabs.tabs-fixed-width{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.tabs.tabs-fixed-width .tab{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.tabs .tab{display:inline-block;text-align:center;line-height:48px;height:48px;padding:0;margin:0;text-transform:uppercase}.tabs .tab a{color:rgba(238,110,115,0.7);display:block;width:100%;height:100%;padding:0 24px;font-size:14px;text-overflow:ellipsis;overflow:hidden;-webkit-transition:color .28s ease, background-color .28s ease;transition:color .28s ease, background-color .28s ease}.tabs .tab a:focus,.tabs .tab a:focus.active{background-color:rgba(246,178,181,0.2);outline:none}.tabs .tab a:hover,.tabs .tab a.active{background-color:transparent;color:#ee6e73}.tabs .tab.disabled a,.tabs .tab.disabled a:hover{color:rgba(238,110,115,0.4);cursor:default}.tabs .indicator{position:absolute;bottom:0;height:2px;background-color:#f6b2b5;will-change:left, right}@media only screen and (max-width: 992px){.tabs{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.tabs .tab{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.tabs .tab a{padding:0 12px}}.material-tooltip{padding:10px 8px;font-size:1rem;z-index:2000;background-color:transparent;border-radius:2px;color:#fff;min-height:36px;line-height:120%;opacity:0;position:absolute;text-align:center;max-width:calc(100% - 4px);overflow:hidden;left:0;top:0;pointer-events:none;visibility:hidden;background-color:#323232}.backdrop{position:absolute;opacity:0;height:7px;width:14px;border-radius:0 0 50% 50%;background-color:#323232;z-index:-1;-webkit-transform-origin:50% 0%;transform-origin:50% 0%;visibility:hidden}.btn,.btn-large,.btn-small,.btn-flat{border:none;border-radius:2px;display:inline-block;height:36px;line-height:36px;padding:0 16px;text-transform:uppercase;vertical-align:middle;-webkit-tap-highlight-color:transparent}.btn.disabled,.disabled.btn-large,.disabled.btn-small,.btn-floating.disabled,.btn-large.disabled,.btn-small.disabled,.btn-flat.disabled,.btn:disabled,.btn-large:disabled,.btn-small:disabled,.btn-floating:disabled,.btn-large:disabled,.btn-small:disabled,.btn-flat:disabled,.btn[disabled],.btn-large[disabled],.btn-small[disabled],.btn-floating[disabled],.btn-large[disabled],.btn-small[disabled],.btn-flat[disabled]{pointer-events:none;background-color:#DFDFDF !important;-webkit-box-shadow:none;box-shadow:none;color:#9F9F9F !important;cursor:default}.btn.disabled:hover,.disabled.btn-large:hover,.disabled.btn-small:hover,.btn-floating.disabled:hover,.btn-large.disabled:hover,.btn-small.disabled:hover,.btn-flat.disabled:hover,.btn:disabled:hover,.btn-large:disabled:hover,.btn-small:disabled:hover,.btn-floating:disabled:hover,.btn-large:disabled:hover,.btn-small:disabled:hover,.btn-flat:disabled:hover,.btn[disabled]:hover,.btn-large[disabled]:hover,.btn-small[disabled]:hover,.btn-floating[disabled]:hover,.btn-large[disabled]:hover,.btn-small[disabled]:hover,.btn-flat[disabled]:hover{background-color:#DFDFDF !important;color:#9F9F9F !important}.btn,.btn-large,.btn-small,.btn-floating,.btn-large,.btn-small,.btn-flat{font-size:14px;outline:0}.btn i,.btn-large i,.btn-small i,.btn-floating i,.btn-large i,.btn-small i,.btn-flat i{font-size:1.3rem;line-height:inherit}.btn:focus,.btn-large:focus,.btn-small:focus,.btn-floating:focus{background-color:#1d7d74}.btn,.btn-large,.btn-small{text-decoration:none;color:#fff;background-color:#26a69a;text-align:center;letter-spacing:.5px;-webkit-transition:background-color .2s ease-out;transition:background-color .2s ease-out;cursor:pointer}.btn:hover,.btn-large:hover,.btn-small:hover{background-color:#2bbbad}.btn-floating{display:inline-block;color:#fff;position:relative;overflow:hidden;z-index:1;width:40px;height:40px;line-height:40px;padding:0;background-color:#26a69a;border-radius:50%;-webkit-transition:background-color .3s;transition:background-color .3s;cursor:pointer;vertical-align:middle}.btn-floating:hover{background-color:#26a69a}.btn-floating:before{border-radius:0}.btn-floating.btn-large{width:56px;height:56px;padding:0}.btn-floating.btn-large.halfway-fab{bottom:-28px}.btn-floating.btn-large i{line-height:56px}.btn-floating.btn-small{width:32.4px;height:32.4px}.btn-floating.btn-small.halfway-fab{bottom:-16.2px}.btn-floating.btn-small i{line-height:32.4px}.btn-floating.halfway-fab{position:absolute;right:24px;bottom:-20px}.btn-floating.halfway-fab.left{right:auto;left:24px}.btn-floating i{width:inherit;display:inline-block;text-align:center;color:#fff;font-size:1.6rem;line-height:40px}button.btn-floating{border:none}.fixed-action-btn{position:fixed;right:23px;bottom:23px;padding-top:15px;margin-bottom:0;z-index:997}.fixed-action-btn.active ul{visibility:visible}.fixed-action-btn.direction-left,.fixed-action-btn.direction-right{padding:0 0 0 15px}.fixed-action-btn.direction-left ul,.fixed-action-btn.direction-right ul{text-align:right;right:64px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);height:100%;left:auto;width:500px}.fixed-action-btn.direction-left ul li,.fixed-action-btn.direction-right ul li{display:inline-block;margin:7.5px 15px 0 0}.fixed-action-btn.direction-right{padding:0 15px 0 0}.fixed-action-btn.direction-right ul{text-align:left;direction:rtl;left:64px;right:auto}.fixed-action-btn.direction-right ul li{margin:7.5px 0 0 15px}.fixed-action-btn.direction-bottom{padding:0 0 15px 0}.fixed-action-btn.direction-bottom ul{top:64px;bottom:auto;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:reverse;-webkit-flex-direction:column-reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}.fixed-action-btn.direction-bottom ul li{margin:15px 0 0 0}.fixed-action-btn.toolbar{padding:0;height:56px}.fixed-action-btn.toolbar.active>a i{opacity:0}.fixed-action-btn.toolbar ul{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;top:0;bottom:0;z-index:1}.fixed-action-btn.toolbar ul li{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;display:inline-block;margin:0;height:100%;-webkit-transition:none;transition:none}.fixed-action-btn.toolbar ul li a{display:block;overflow:hidden;position:relative;width:100%;height:100%;background-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#fff;line-height:56px;z-index:1}.fixed-action-btn.toolbar ul li a i{line-height:inherit}.fixed-action-btn ul{left:0;right:0;text-align:center;position:absolute;bottom:64px;margin:0;visibility:hidden}.fixed-action-btn ul li{margin-bottom:15px}.fixed-action-btn ul a.btn-floating{opacity:0}.fixed-action-btn .fab-backdrop{position:absolute;top:0;left:0;z-index:-1;width:40px;height:40px;background-color:#26a69a;border-radius:50%;-webkit-transform:scale(0);transform:scale(0)}.btn-flat{-webkit-box-shadow:none;box-shadow:none;background-color:transparent;color:#343434;cursor:pointer;-webkit-transition:background-color .2s;transition:background-color .2s}.btn-flat:focus,.btn-flat:hover{-webkit-box-shadow:none;box-shadow:none}.btn-flat:focus{background-color:rgba(0,0,0,0.1)}.btn-flat.disabled,.btn-flat.btn-flat[disabled]{background-color:transparent !important;color:#b3b2b2 !important;cursor:default}.btn-large{height:54px;line-height:54px;font-size:15px;padding:0 28px}.btn-large i{font-size:1.6rem}.btn-small{height:32.4px;line-height:32.4px;font-size:13px}.btn-small i{font-size:1.2rem}.btn-block{display:block}.dropdown-content{background-color:#fff;margin:0;display:none;min-width:100px;overflow-y:auto;opacity:0;position:absolute;left:0;top:0;z-index:9999;-webkit-transform-origin:0 0;transform-origin:0 0}.dropdown-content:focus{outline:0}.dropdown-content li{clear:both;color:rgba(0,0,0,0.87);cursor:pointer;min-height:50px;line-height:1.5rem;width:100%;text-align:left}.dropdown-content li:hover,.dropdown-content li.active{background-color:#eee}.dropdown-content li:focus{outline:none}.dropdown-content li.divider{min-height:0;height:1px}.dropdown-content li>a,.dropdown-content li>span{font-size:16px;color:#26a69a;display:block;line-height:22px;padding:14px 16px}.dropdown-content li>span>label{top:1px;left:0;height:18px}.dropdown-content li>a>i{height:inherit;line-height:inherit;float:left;margin:0 24px 0 0;width:24px}body.keyboard-focused .dropdown-content li:focus{background-color:#dadada}.input-field.col .dropdown-content [type="checkbox"]+label{top:1px;left:0;height:18px;-webkit-transform:none;transform:none}.dropdown-trigger{cursor:pointer}/*!
+ * Waves v0.6.0
+ * http://fian.my.id/Waves
+ *
+ * Copyright 2014 Alfiana E. Sibuea and other contributors
+ * Released under the MIT license
+ * https://github.com/fians/Waves/blob/master/LICENSE
+ */.waves-effect{position:relative;cursor:pointer;display:inline-block;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;vertical-align:middle;z-index:1;-webkit-transition:.3s ease-out;transition:.3s ease-out}.waves-effect .waves-ripple{position:absolute;border-radius:50%;width:20px;height:20px;margin-top:-10px;margin-left:-10px;opacity:0;background:rgba(0,0,0,0.2);-webkit-transition:all 0.7s ease-out;transition:all 0.7s ease-out;-webkit-transition-property:opacity, -webkit-transform;transition-property:opacity, -webkit-transform;transition-property:transform, opacity;transition-property:transform, opacity, -webkit-transform;-webkit-transform:scale(0);transform:scale(0);pointer-events:none}.waves-effect.waves-light .waves-ripple{background-color:rgba(255,255,255,0.45)}.waves-effect.waves-red .waves-ripple{background-color:rgba(244,67,54,0.7)}.waves-effect.waves-yellow .waves-ripple{background-color:rgba(255,235,59,0.7)}.waves-effect.waves-orange .waves-ripple{background-color:rgba(255,152,0,0.7)}.waves-effect.waves-purple .waves-ripple{background-color:rgba(156,39,176,0.7)}.waves-effect.waves-green .waves-ripple{background-color:rgba(76,175,80,0.7)}.waves-effect.waves-teal .waves-ripple{background-color:rgba(0,150,136,0.7)}.waves-effect input[type="button"],.waves-effect input[type="reset"],.waves-effect input[type="submit"]{border:0;font-style:normal;font-size:inherit;text-transform:inherit;background:none}.waves-effect img{position:relative;z-index:-1}.waves-notransition{-webkit-transition:none !important;transition:none !important}.waves-circle{-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-mask-image:-webkit-radial-gradient(circle, white 100%, black 100%)}.waves-input-wrapper{border-radius:0.2em;vertical-align:bottom}.waves-input-wrapper .waves-button-input{position:relative;top:0;left:0;z-index:1}.waves-circle{text-align:center;width:2.5em;height:2.5em;line-height:2.5em;border-radius:50%;-webkit-mask-image:none}.waves-block{display:block}.waves-effect .waves-ripple{z-index:-1}.modal{display:none;position:fixed;left:0;right:0;background-color:#fafafa;padding:0;max-height:70%;width:55%;margin:auto;overflow-y:auto;border-radius:2px;will-change:top, opacity}.modal:focus{outline:none}@media only screen and (max-width: 992px){.modal{width:80%}}.modal h1,.modal h2,.modal h3,.modal h4{margin-top:0}.modal .modal-content{padding:24px}.modal .modal-close{cursor:pointer}.modal .modal-footer{border-radius:0 0 2px 2px;background-color:#fafafa;padding:4px 6px;height:56px;width:100%;text-align:right}.modal .modal-footer .btn,.modal .modal-footer .btn-large,.modal .modal-footer .btn-small,.modal .modal-footer .btn-flat{margin:6px 0}.modal-overlay{position:fixed;z-index:999;top:-25%;left:0;bottom:0;right:0;height:125%;width:100%;background:#000;display:none;will-change:opacity}.modal.modal-fixed-footer{padding:0;height:70%}.modal.modal-fixed-footer .modal-content{position:absolute;height:calc(100% - 56px);max-height:100%;width:100%;overflow-y:auto}.modal.modal-fixed-footer .modal-footer{border-top:1px solid rgba(0,0,0,0.1);position:absolute;bottom:0}.modal.bottom-sheet{top:auto;bottom:-100%;margin:0;width:100%;max-height:45%;border-radius:0;will-change:bottom, opacity}.collapsible{border-top:1px solid #ddd;border-right:1px solid #ddd;border-left:1px solid #ddd;margin:.5rem 0 1rem 0}.collapsible-header{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;cursor:pointer;-webkit-tap-highlight-color:transparent;line-height:1.5;padding:1rem;background-color:#fff;border-bottom:1px solid #ddd}.collapsible-header:focus{outline:0}.collapsible-header i{width:2rem;font-size:1.6rem;display:inline-block;text-align:center;margin-right:1rem}.keyboard-focused .collapsible-header:focus{background-color:#eee}.collapsible-body{display:none;border-bottom:1px solid #ddd;-webkit-box-sizing:border-box;box-sizing:border-box;padding:2rem}.sidenav .collapsible,.sidenav.fixed .collapsible{border:none;-webkit-box-shadow:none;box-shadow:none}.sidenav .collapsible li,.sidenav.fixed .collapsible li{padding:0}.sidenav .collapsible-header,.sidenav.fixed .collapsible-header{background-color:transparent;border:none;line-height:inherit;height:inherit;padding:0 16px}.sidenav .collapsible-header:hover,.sidenav.fixed .collapsible-header:hover{background-color:rgba(0,0,0,0.05)}.sidenav .collapsible-header i,.sidenav.fixed .collapsible-header i{line-height:inherit}.sidenav .collapsible-body,.sidenav.fixed .collapsible-body{border:0;background-color:#fff}.sidenav .collapsible-body li a,.sidenav.fixed .collapsible-body li a{padding:0 23.5px 0 31px}.collapsible.popout{border:none;-webkit-box-shadow:none;box-shadow:none}.collapsible.popout>li{-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);margin:0 24px;-webkit-transition:margin 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);transition:margin 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94)}.collapsible.popout>li.active{-webkit-box-shadow:0 5px 11px 0 rgba(0,0,0,0.18),0 4px 15px 0 rgba(0,0,0,0.15);box-shadow:0 5px 11px 0 rgba(0,0,0,0.18),0 4px 15px 0 rgba(0,0,0,0.15);margin:16px 0}.chip{display:inline-block;height:32px;font-size:13px;font-weight:500;color:rgba(0,0,0,0.6);line-height:32px;padding:0 12px;border-radius:16px;background-color:#e4e4e4;margin-bottom:5px;margin-right:5px}.chip:focus{outline:none;background-color:#26a69a;color:#fff}.chip>img{float:left;margin:0 8px 0 -12px;height:32px;width:32px;border-radius:50%}.chip .close{cursor:pointer;float:right;font-size:16px;line-height:32px;padding-left:8px}.chips{border:none;border-bottom:1px solid #9e9e9e;-webkit-box-shadow:none;box-shadow:none;margin:0 0 8px 0;min-height:45px;outline:none;-webkit-transition:all .3s;transition:all .3s}.chips.focus{border-bottom:1px solid #26a69a;-webkit-box-shadow:0 1px 0 0 #26a69a;box-shadow:0 1px 0 0 #26a69a}.chips:hover{cursor:text}.chips .input{background:none;border:0;color:rgba(0,0,0,0.6);display:inline-block;font-size:16px;height:3rem;line-height:32px;outline:0;margin:0;padding:0 !important;width:120px !important}.chips .input:focus{border:0 !important;-webkit-box-shadow:none !important;box-shadow:none !important}.chips .autocomplete-content{margin-top:0;margin-bottom:0}.prefix ~ .chips{margin-left:3rem;width:92%;width:calc(100% - 3rem)}.chips:empty ~ label{font-size:0.8rem;-webkit-transform:translateY(-140%);transform:translateY(-140%)}.materialboxed{display:block;cursor:-webkit-zoom-in;cursor:zoom-in;position:relative;-webkit-transition:opacity .4s;transition:opacity .4s;-webkit-backface-visibility:hidden}.materialboxed:hover:not(.active){opacity:.8}.materialboxed.active{cursor:-webkit-zoom-out;cursor:zoom-out}#materialbox-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background-color:#292929;z-index:1000;will-change:opacity}.materialbox-caption{position:fixed;display:none;color:#fff;line-height:50px;bottom:0;left:0;width:100%;text-align:center;padding:0% 15%;height:50px;z-index:1000;-webkit-font-smoothing:antialiased}select:focus{outline:1px solid #c9f3ef}button:focus{outline:none;background-color:#2ab7a9}label{font-size:.8rem;color:#9e9e9e}::-webkit-input-placeholder{color:#d1d1d1}::-moz-placeholder{color:#d1d1d1}:-ms-input-placeholder{color:#d1d1d1}::-ms-input-placeholder{color:#d1d1d1}::placeholder{color:#d1d1d1}input:not([type]),input[type=text]:not(.browser-default),input[type=password]:not(.browser-default),input[type=email]:not(.browser-default),input[type=url]:not(.browser-default),input[type=time]:not(.browser-default),input[type=date]:not(.browser-default),input[type=datetime]:not(.browser-default),input[type=datetime-local]:not(.browser-default),input[type=tel]:not(.browser-default),input[type=number]:not(.browser-default),input[type=search]:not(.browser-default),textarea.materialize-textarea{background-color:transparent;border:none;border-bottom:1px solid #9e9e9e;border-radius:0;outline:none;height:3rem;width:100%;font-size:16px;margin:0 0 8px 0;padding:0;-webkit-box-shadow:none;box-shadow:none;-webkit-box-sizing:content-box;box-sizing:content-box;-webkit-transition:border .3s, -webkit-box-shadow .3s;transition:border .3s, -webkit-box-shadow .3s;transition:box-shadow .3s, border .3s;transition:box-shadow .3s, border .3s, -webkit-box-shadow .3s}input:not([type]):disabled,input:not([type])[readonly="readonly"],input[type=text]:not(.browser-default):disabled,input[type=text]:not(.browser-default)[readonly="readonly"],input[type=password]:not(.browser-default):disabled,input[type=password]:not(.browser-default)[readonly="readonly"],input[type=email]:not(.browser-default):disabled,input[type=email]:not(.browser-default)[readonly="readonly"],input[type=url]:not(.browser-default):disabled,input[type=url]:not(.browser-default)[readonly="readonly"],input[type=time]:not(.browser-default):disabled,input[type=time]:not(.browser-default)[readonly="readonly"],input[type=date]:not(.browser-default):disabled,input[type=date]:not(.browser-default)[readonly="readonly"],input[type=datetime]:not(.browser-default):disabled,input[type=datetime]:not(.browser-default)[readonly="readonly"],input[type=datetime-local]:not(.browser-default):disabled,input[type=datetime-local]:not(.browser-default)[readonly="readonly"],input[type=tel]:not(.browser-default):disabled,input[type=tel]:not(.browser-default)[readonly="readonly"],input[type=number]:not(.browser-default):disabled,input[type=number]:not(.browser-default)[readonly="readonly"],input[type=search]:not(.browser-default):disabled,input[type=search]:not(.browser-default)[readonly="readonly"],textarea.materialize-textarea:disabled,textarea.materialize-textarea[readonly="readonly"]{color:rgba(0,0,0,0.42);border-bottom:1px dotted rgba(0,0,0,0.42)}input:not([type]):disabled+label,input:not([type])[readonly="readonly"]+label,input[type=text]:not(.browser-default):disabled+label,input[type=text]:not(.browser-default)[readonly="readonly"]+label,input[type=password]:not(.browser-default):disabled+label,input[type=password]:not(.browser-default)[readonly="readonly"]+label,input[type=email]:not(.browser-default):disabled+label,input[type=email]:not(.browser-default)[readonly="readonly"]+label,input[type=url]:not(.browser-default):disabled+label,input[type=url]:not(.browser-default)[readonly="readonly"]+label,input[type=time]:not(.browser-default):disabled+label,input[type=time]:not(.browser-default)[readonly="readonly"]+label,input[type=date]:not(.browser-default):disabled+label,input[type=date]:not(.browser-default)[readonly="readonly"]+label,input[type=datetime]:not(.browser-default):disabled+label,input[type=datetime]:not(.browser-default)[readonly="readonly"]+label,input[type=datetime-local]:not(.browser-default):disabled+label,input[type=datetime-local]:not(.browser-default)[readonly="readonly"]+label,input[type=tel]:not(.browser-default):disabled+label,input[type=tel]:not(.browser-default)[readonly="readonly"]+label,input[type=number]:not(.browser-default):disabled+label,input[type=number]:not(.browser-default)[readonly="readonly"]+label,input[type=search]:not(.browser-default):disabled+label,input[type=search]:not(.browser-default)[readonly="readonly"]+label,textarea.materialize-textarea:disabled+label,textarea.materialize-textarea[readonly="readonly"]+label{color:rgba(0,0,0,0.42)}input:not([type]):focus:not([readonly]),input[type=text]:not(.browser-default):focus:not([readonly]),input[type=password]:not(.browser-default):focus:not([readonly]),input[type=email]:not(.browser-default):focus:not([readonly]),input[type=url]:not(.browser-default):focus:not([readonly]),input[type=time]:not(.browser-default):focus:not([readonly]),input[type=date]:not(.browser-default):focus:not([readonly]),input[type=datetime]:not(.browser-default):focus:not([readonly]),input[type=datetime-local]:not(.browser-default):focus:not([readonly]),input[type=tel]:not(.browser-default):focus:not([readonly]),input[type=number]:not(.browser-default):focus:not([readonly]),input[type=search]:not(.browser-default):focus:not([readonly]),textarea.materialize-textarea:focus:not([readonly]){border-bottom:1px solid #26a69a;-webkit-box-shadow:0 1px 0 0 #26a69a;box-shadow:0 1px 0 0 #26a69a}input:not([type]):focus:not([readonly])+label,input[type=text]:not(.browser-default):focus:not([readonly])+label,input[type=password]:not(.browser-default):focus:not([readonly])+label,input[type=email]:not(.browser-default):focus:not([readonly])+label,input[type=url]:not(.browser-default):focus:not([readonly])+label,input[type=time]:not(.browser-default):focus:not([readonly])+label,input[type=date]:not(.browser-default):focus:not([readonly])+label,input[type=datetime]:not(.browser-default):focus:not([readonly])+label,input[type=datetime-local]:not(.browser-default):focus:not([readonly])+label,input[type=tel]:not(.browser-default):focus:not([readonly])+label,input[type=number]:not(.browser-default):focus:not([readonly])+label,input[type=search]:not(.browser-default):focus:not([readonly])+label,textarea.materialize-textarea:focus:not([readonly])+label{color:#26a69a}input:not([type]):focus.valid ~ label,input[type=text]:not(.browser-default):focus.valid ~ label,input[type=password]:not(.browser-default):focus.valid ~ label,input[type=email]:not(.browser-default):focus.valid ~ label,input[type=url]:not(.browser-default):focus.valid ~ label,input[type=time]:not(.browser-default):focus.valid ~ label,input[type=date]:not(.browser-default):focus.valid ~ label,input[type=datetime]:not(.browser-default):focus.valid ~ label,input[type=datetime-local]:not(.browser-default):focus.valid ~ label,input[type=tel]:not(.browser-default):focus.valid ~ label,input[type=number]:not(.browser-default):focus.valid ~ label,input[type=search]:not(.browser-default):focus.valid ~ label,textarea.materialize-textarea:focus.valid ~ label{color:#4CAF50}input:not([type]):focus.invalid ~ label,input[type=text]:not(.browser-default):focus.invalid ~ label,input[type=password]:not(.browser-default):focus.invalid ~ label,input[type=email]:not(.browser-default):focus.invalid ~ label,input[type=url]:not(.browser-default):focus.invalid ~ label,input[type=time]:not(.browser-default):focus.invalid ~ label,input[type=date]:not(.browser-default):focus.invalid ~ label,input[type=datetime]:not(.browser-default):focus.invalid ~ label,input[type=datetime-local]:not(.browser-default):focus.invalid ~ label,input[type=tel]:not(.browser-default):focus.invalid ~ label,input[type=number]:not(.browser-default):focus.invalid ~ label,input[type=search]:not(.browser-default):focus.invalid ~ label,textarea.materialize-textarea:focus.invalid ~ label{color:#F44336}input:not([type]).validate+label,input[type=text]:not(.browser-default).validate+label,input[type=password]:not(.browser-default).validate+label,input[type=email]:not(.browser-default).validate+label,input[type=url]:not(.browser-default).validate+label,input[type=time]:not(.browser-default).validate+label,input[type=date]:not(.browser-default).validate+label,input[type=datetime]:not(.browser-default).validate+label,input[type=datetime-local]:not(.browser-default).validate+label,input[type=tel]:not(.browser-default).validate+label,input[type=number]:not(.browser-default).validate+label,input[type=search]:not(.browser-default).validate+label,textarea.materialize-textarea.validate+label{width:100%}input.valid:not([type]),input.valid:not([type]):focus,input.valid[type=text]:not(.browser-default),input.valid[type=text]:not(.browser-default):focus,input.valid[type=password]:not(.browser-default),input.valid[type=password]:not(.browser-default):focus,input.valid[type=email]:not(.browser-default),input.valid[type=email]:not(.browser-default):focus,input.valid[type=url]:not(.browser-default),input.valid[type=url]:not(.browser-default):focus,input.valid[type=time]:not(.browser-default),input.valid[type=time]:not(.browser-default):focus,input.valid[type=date]:not(.browser-default),input.valid[type=date]:not(.browser-default):focus,input.valid[type=datetime]:not(.browser-default),input.valid[type=datetime]:not(.browser-default):focus,input.valid[type=datetime-local]:not(.browser-default),input.valid[type=datetime-local]:not(.browser-default):focus,input.valid[type=tel]:not(.browser-default),input.valid[type=tel]:not(.browser-default):focus,input.valid[type=number]:not(.browser-default),input.valid[type=number]:not(.browser-default):focus,input.valid[type=search]:not(.browser-default),input.valid[type=search]:not(.browser-default):focus,textarea.materialize-textarea.valid,textarea.materialize-textarea.valid:focus,.select-wrapper.valid>input.select-dropdown{border-bottom:1px solid #4CAF50;-webkit-box-shadow:0 1px 0 0 #4CAF50;box-shadow:0 1px 0 0 #4CAF50}input.invalid:not([type]),input.invalid:not([type]):focus,input.invalid[type=text]:not(.browser-default),input.invalid[type=text]:not(.browser-default):focus,input.invalid[type=password]:not(.browser-default),input.invalid[type=password]:not(.browser-default):focus,input.invalid[type=email]:not(.browser-default),input.invalid[type=email]:not(.browser-default):focus,input.invalid[type=url]:not(.browser-default),input.invalid[type=url]:not(.browser-default):focus,input.invalid[type=time]:not(.browser-default),input.invalid[type=time]:not(.browser-default):focus,input.invalid[type=date]:not(.browser-default),input.invalid[type=date]:not(.browser-default):focus,input.invalid[type=datetime]:not(.browser-default),input.invalid[type=datetime]:not(.browser-default):focus,input.invalid[type=datetime-local]:not(.browser-default),input.invalid[type=datetime-local]:not(.browser-default):focus,input.invalid[type=tel]:not(.browser-default),input.invalid[type=tel]:not(.browser-default):focus,input.invalid[type=number]:not(.browser-default),input.invalid[type=number]:not(.browser-default):focus,input.invalid[type=search]:not(.browser-default),input.invalid[type=search]:not(.browser-default):focus,textarea.materialize-textarea.invalid,textarea.materialize-textarea.invalid:focus,.select-wrapper.invalid>input.select-dropdown,.select-wrapper.invalid>input.select-dropdown:focus{border-bottom:1px solid #F44336;-webkit-box-shadow:0 1px 0 0 #F44336;box-shadow:0 1px 0 0 #F44336}input:not([type]).valid ~ .helper-text[data-success],input:not([type]):focus.valid ~ .helper-text[data-success],input:not([type]).invalid ~ .helper-text[data-error],input:not([type]):focus.invalid ~ .helper-text[data-error],input[type=text]:not(.browser-default).valid ~ .helper-text[data-success],input[type=text]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=text]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=text]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=password]:not(.browser-default).valid ~ .helper-text[data-success],input[type=password]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=password]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=password]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=email]:not(.browser-default).valid ~ .helper-text[data-success],input[type=email]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=email]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=email]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=url]:not(.browser-default).valid ~ .helper-text[data-success],input[type=url]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=url]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=url]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=time]:not(.browser-default).valid ~ .helper-text[data-success],input[type=time]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=time]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=time]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=date]:not(.browser-default).valid ~ .helper-text[data-success],input[type=date]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=date]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=date]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=datetime]:not(.browser-default).valid ~ .helper-text[data-success],input[type=datetime]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=datetime]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=datetime]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=datetime-local]:not(.browser-default).valid ~ .helper-text[data-success],input[type=datetime-local]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=datetime-local]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=datetime-local]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=tel]:not(.browser-default).valid ~ .helper-text[data-success],input[type=tel]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=tel]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=tel]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=number]:not(.browser-default).valid ~ .helper-text[data-success],input[type=number]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=number]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=number]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=search]:not(.browser-default).valid ~ .helper-text[data-success],input[type=search]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=search]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=search]:not(.browser-default):focus.invalid ~ .helper-text[data-error],textarea.materialize-textarea.valid ~ .helper-text[data-success],textarea.materialize-textarea:focus.valid ~ .helper-text[data-success],textarea.materialize-textarea.invalid ~ .helper-text[data-error],textarea.materialize-textarea:focus.invalid ~ .helper-text[data-error],.select-wrapper.valid .helper-text[data-success],.select-wrapper.invalid ~ .helper-text[data-error]{color:transparent;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none}input:not([type]).valid ~ .helper-text:after,input:not([type]):focus.valid ~ .helper-text:after,input[type=text]:not(.browser-default).valid ~ .helper-text:after,input[type=text]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=password]:not(.browser-default).valid ~ .helper-text:after,input[type=password]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=email]:not(.browser-default).valid ~ .helper-text:after,input[type=email]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=url]:not(.browser-default).valid ~ .helper-text:after,input[type=url]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=time]:not(.browser-default).valid ~ .helper-text:after,input[type=time]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=date]:not(.browser-default).valid ~ .helper-text:after,input[type=date]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=datetime]:not(.browser-default).valid ~ .helper-text:after,input[type=datetime]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=datetime-local]:not(.browser-default).valid ~ .helper-text:after,input[type=datetime-local]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=tel]:not(.browser-default).valid ~ .helper-text:after,input[type=tel]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=number]:not(.browser-default).valid ~ .helper-text:after,input[type=number]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=search]:not(.browser-default).valid ~ .helper-text:after,input[type=search]:not(.browser-default):focus.valid ~ .helper-text:after,textarea.materialize-textarea.valid ~ .helper-text:after,textarea.materialize-textarea:focus.valid ~ .helper-text:after,.select-wrapper.valid ~ .helper-text:after{content:attr(data-success);color:#4CAF50}input:not([type]).invalid ~ .helper-text:after,input:not([type]):focus.invalid ~ .helper-text:after,input[type=text]:not(.browser-default).invalid ~ .helper-text:after,input[type=text]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=password]:not(.browser-default).invalid ~ .helper-text:after,input[type=password]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=email]:not(.browser-default).invalid ~ .helper-text:after,input[type=email]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=url]:not(.browser-default).invalid ~ .helper-text:after,input[type=url]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=time]:not(.browser-default).invalid ~ .helper-text:after,input[type=time]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=date]:not(.browser-default).invalid ~ .helper-text:after,input[type=date]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=datetime]:not(.browser-default).invalid ~ .helper-text:after,input[type=datetime]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=datetime-local]:not(.browser-default).invalid ~ .helper-text:after,input[type=datetime-local]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=tel]:not(.browser-default).invalid ~ .helper-text:after,input[type=tel]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=number]:not(.browser-default).invalid ~ .helper-text:after,input[type=number]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=search]:not(.browser-default).invalid ~ .helper-text:after,input[type=search]:not(.browser-default):focus.invalid ~ .helper-text:after,textarea.materialize-textarea.invalid ~ .helper-text:after,textarea.materialize-textarea:focus.invalid ~ .helper-text:after,.select-wrapper.invalid ~ .helper-text:after{content:attr(data-error);color:#F44336}input:not([type])+label:after,input[type=text]:not(.browser-default)+label:after,input[type=password]:not(.browser-default)+label:after,input[type=email]:not(.browser-default)+label:after,input[type=url]:not(.browser-default)+label:after,input[type=time]:not(.browser-default)+label:after,input[type=date]:not(.browser-default)+label:after,input[type=datetime]:not(.browser-default)+label:after,input[type=datetime-local]:not(.browser-default)+label:after,input[type=tel]:not(.browser-default)+label:after,input[type=number]:not(.browser-default)+label:after,input[type=search]:not(.browser-default)+label:after,textarea.materialize-textarea+label:after,.select-wrapper+label:after{display:block;content:"";position:absolute;top:100%;left:0;opacity:0;-webkit-transition:.2s opacity ease-out, .2s color ease-out;transition:.2s opacity ease-out, .2s color ease-out}.input-field{position:relative;margin-top:1rem;margin-bottom:1rem}.input-field.inline{display:inline-block;vertical-align:middle;margin-left:5px}.input-field.inline input,.input-field.inline .select-dropdown{margin-bottom:1rem}.input-field.col label{left:.75rem}.input-field.col .prefix ~ label,.input-field.col .prefix ~ .validate ~ label{width:calc(100% - 3rem - 1.5rem)}.input-field>label{color:#9e9e9e;position:absolute;top:0;left:0;font-size:1rem;cursor:text;-webkit-transition:color .2s ease-out, -webkit-transform .2s ease-out;transition:color .2s ease-out, -webkit-transform .2s ease-out;transition:transform .2s ease-out, color .2s ease-out;transition:transform .2s ease-out, color .2s ease-out, -webkit-transform .2s ease-out;-webkit-transform-origin:0% 100%;transform-origin:0% 100%;text-align:initial;-webkit-transform:translateY(12px);transform:translateY(12px)}.input-field>label:not(.label-icon).active{-webkit-transform:translateY(-14px) scale(0.8);transform:translateY(-14px) scale(0.8);-webkit-transform-origin:0 0;transform-origin:0 0}.input-field>input[type]:-webkit-autofill:not(.browser-default):not([type="search"])+label,.input-field>input[type=date]:not(.browser-default)+label,.input-field>input[type=time]:not(.browser-default)+label{-webkit-transform:translateY(-14px) scale(0.8);transform:translateY(-14px) scale(0.8);-webkit-transform-origin:0 0;transform-origin:0 0}.input-field .helper-text{position:relative;min-height:18px;display:block;font-size:12px;color:rgba(0,0,0,0.54)}.input-field .helper-text::after{opacity:1;position:absolute;top:0;left:0}.input-field .prefix{position:absolute;width:3rem;font-size:2rem;-webkit-transition:color .2s;transition:color .2s;top:.5rem}.input-field .prefix.active{color:#26a69a}.input-field .prefix ~ input,.input-field .prefix ~ textarea,.input-field .prefix ~ label,.input-field .prefix ~ .validate ~ label,.input-field .prefix ~ .helper-text,.input-field .prefix ~ .autocomplete-content{margin-left:3rem;width:92%;width:calc(100% - 3rem)}.input-field .prefix ~ label{margin-left:3rem}@media only screen and (max-width: 992px){.input-field .prefix ~ input{width:86%;width:calc(100% - 3rem)}}@media only screen and (max-width: 600px){.input-field .prefix ~ input{width:80%;width:calc(100% - 3rem)}}.input-field input[type=search]{display:block;line-height:inherit;-webkit-transition:.3s background-color;transition:.3s background-color}.nav-wrapper .input-field input[type=search]{height:inherit;padding-left:4rem;width:calc(100% - 4rem);border:0;-webkit-box-shadow:none;box-shadow:none}.input-field input[type=search]:focus:not(.browser-default){background-color:#fff;border:0;-webkit-box-shadow:none;box-shadow:none;color:#444}.input-field input[type=search]:focus:not(.browser-default)+label i,.input-field input[type=search]:focus:not(.browser-default) ~ .mdi-navigation-close,.input-field input[type=search]:focus:not(.browser-default) ~ .material-icons{color:#444}.input-field input[type=search]+.label-icon{-webkit-transform:none;transform:none;left:1rem}.input-field input[type=search] ~ .mdi-navigation-close,.input-field input[type=search] ~ .material-icons{position:absolute;top:0;right:1rem;color:transparent;cursor:pointer;font-size:2rem;-webkit-transition:.3s color;transition:.3s color}textarea{width:100%;height:3rem;background-color:transparent}textarea.materialize-textarea{line-height:normal;overflow-y:hidden;padding:.8rem 0 .8rem 0;resize:none;min-height:3rem;-webkit-box-sizing:border-box;box-sizing:border-box}.hiddendiv{visibility:hidden;white-space:pre-wrap;word-wrap:break-word;overflow-wrap:break-word;padding-top:1.2rem;position:absolute;top:0;z-index:-1}.autocomplete-content li .highlight{color:#444}.autocomplete-content li img{height:40px;width:40px;margin:5px 15px}.character-counter{min-height:18px}[type="radio"]:not(:checked),[type="radio"]:checked{position:absolute;opacity:0;pointer-events:none}[type="radio"]:not(:checked)+span,[type="radio"]:checked+span{position:relative;padding-left:35px;cursor:pointer;display:inline-block;height:25px;line-height:25px;font-size:1rem;-webkit-transition:.28s ease;transition:.28s ease;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}[type="radio"]+span:before,[type="radio"]+span:after{content:'';position:absolute;left:0;top:0;margin:4px;width:16px;height:16px;z-index:0;-webkit-transition:.28s ease;transition:.28s ease}[type="radio"]:not(:checked)+span:before,[type="radio"]:not(:checked)+span:after,[type="radio"]:checked+span:before,[type="radio"]:checked+span:after,[type="radio"].with-gap:checked+span:before,[type="radio"].with-gap:checked+span:after{border-radius:50%}[type="radio"]:not(:checked)+span:before,[type="radio"]:not(:checked)+span:after{border:2px solid #5a5a5a}[type="radio"]:not(:checked)+span:after{-webkit-transform:scale(0);transform:scale(0)}[type="radio"]:checked+span:before{border:2px solid transparent}[type="radio"]:checked+span:after,[type="radio"].with-gap:checked+span:before,[type="radio"].with-gap:checked+span:after{border:2px solid #26a69a}[type="radio"]:checked+span:after,[type="radio"].with-gap:checked+span:after{background-color:#26a69a}[type="radio"]:checked+span:after{-webkit-transform:scale(1.02);transform:scale(1.02)}[type="radio"].with-gap:checked+span:after{-webkit-transform:scale(0.5);transform:scale(0.5)}[type="radio"].tabbed:focus+span:before{-webkit-box-shadow:0 0 0 10px rgba(0,0,0,0.1);box-shadow:0 0 0 10px rgba(0,0,0,0.1)}[type="radio"].with-gap:disabled:checked+span:before{border:2px solid rgba(0,0,0,0.42)}[type="radio"].with-gap:disabled:checked+span:after{border:none;background-color:rgba(0,0,0,0.42)}[type="radio"]:disabled:not(:checked)+span:before,[type="radio"]:disabled:checked+span:before{background-color:transparent;border-color:rgba(0,0,0,0.42)}[type="radio"]:disabled+span{color:rgba(0,0,0,0.42)}[type="radio"]:disabled:not(:checked)+span:before{border-color:rgba(0,0,0,0.42)}[type="radio"]:disabled:checked+span:after{background-color:rgba(0,0,0,0.42);border-color:#949494}[type="checkbox"]:not(:checked),[type="checkbox"]:checked{position:absolute;opacity:0;pointer-events:none}[type="checkbox"]+span:not(.lever){position:relative;padding-left:35px;cursor:pointer;display:inline-block;height:25px;line-height:25px;font-size:1rem;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}[type="checkbox"]+span:not(.lever):before,[type="checkbox"]:not(.filled-in)+span:not(.lever):after{content:'';position:absolute;top:0;left:0;width:18px;height:18px;z-index:0;border:2px solid #5a5a5a;border-radius:1px;margin-top:3px;-webkit-transition:.2s;transition:.2s}[type="checkbox"]:not(.filled-in)+span:not(.lever):after{border:0;-webkit-transform:scale(0);transform:scale(0)}[type="checkbox"]:not(:checked):disabled+span:not(.lever):before{border:none;background-color:rgba(0,0,0,0.42)}[type="checkbox"].tabbed:focus+span:not(.lever):after{-webkit-transform:scale(1);transform:scale(1);border:0;border-radius:50%;-webkit-box-shadow:0 0 0 10px rgba(0,0,0,0.1);box-shadow:0 0 0 10px rgba(0,0,0,0.1);background-color:rgba(0,0,0,0.1)}[type="checkbox"]:checked+span:not(.lever):before{top:-4px;left:-5px;width:12px;height:22px;border-top:2px solid transparent;border-left:2px solid transparent;border-right:2px solid #26a69a;border-bottom:2px solid #26a69a;-webkit-transform:rotate(40deg);transform:rotate(40deg);-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform-origin:100% 100%;transform-origin:100% 100%}[type="checkbox"]:checked:disabled+span:before{border-right:2px solid rgba(0,0,0,0.42);border-bottom:2px solid rgba(0,0,0,0.42)}[type="checkbox"]:indeterminate+span:not(.lever):before{top:-11px;left:-12px;width:10px;height:22px;border-top:none;border-left:none;border-right:2px solid #26a69a;border-bottom:none;-webkit-transform:rotate(90deg);transform:rotate(90deg);-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform-origin:100% 100%;transform-origin:100% 100%}[type="checkbox"]:indeterminate:disabled+span:not(.lever):before{border-right:2px solid rgba(0,0,0,0.42);background-color:transparent}[type="checkbox"].filled-in+span:not(.lever):after{border-radius:2px}[type="checkbox"].filled-in+span:not(.lever):before,[type="checkbox"].filled-in+span:not(.lever):after{content:'';left:0;position:absolute;-webkit-transition:border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s;transition:border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s;z-index:1}[type="checkbox"].filled-in:not(:checked)+span:not(.lever):before{width:0;height:0;border:3px solid transparent;left:6px;top:10px;-webkit-transform:rotateZ(37deg);transform:rotateZ(37deg);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}[type="checkbox"].filled-in:not(:checked)+span:not(.lever):after{height:20px;width:20px;background-color:transparent;border:2px solid #5a5a5a;top:0px;z-index:0}[type="checkbox"].filled-in:checked+span:not(.lever):before{top:0;left:1px;width:8px;height:13px;border-top:2px solid transparent;border-left:2px solid transparent;border-right:2px solid #fff;border-bottom:2px solid #fff;-webkit-transform:rotateZ(37deg);transform:rotateZ(37deg);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}[type="checkbox"].filled-in:checked+span:not(.lever):after{top:0;width:20px;height:20px;border:2px solid #26a69a;background-color:#26a69a;z-index:0}[type="checkbox"].filled-in.tabbed:focus+span:not(.lever):after{border-radius:2px;border-color:#5a5a5a;background-color:rgba(0,0,0,0.1)}[type="checkbox"].filled-in.tabbed:checked:focus+span:not(.lever):after{border-radius:2px;background-color:#26a69a;border-color:#26a69a}[type="checkbox"].filled-in:disabled:not(:checked)+span:not(.lever):before{background-color:transparent;border:2px solid transparent}[type="checkbox"].filled-in:disabled:not(:checked)+span:not(.lever):after{border-color:transparent;background-color:#949494}[type="checkbox"].filled-in:disabled:checked+span:not(.lever):before{background-color:transparent}[type="checkbox"].filled-in:disabled:checked+span:not(.lever):after{background-color:#949494;border-color:#949494}.switch,.switch *{-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.switch label{cursor:pointer}.switch label input[type=checkbox]{opacity:0;width:0;height:0}.switch label input[type=checkbox]:checked+.lever{background-color:#84c7c1}.switch label input[type=checkbox]:checked+.lever:before,.switch label input[type=checkbox]:checked+.lever:after{left:18px}.switch label input[type=checkbox]:checked+.lever:after{background-color:#26a69a}.switch label .lever{content:"";display:inline-block;position:relative;width:36px;height:14px;background-color:rgba(0,0,0,0.38);border-radius:15px;margin-right:10px;-webkit-transition:background 0.3s ease;transition:background 0.3s ease;vertical-align:middle;margin:0 16px}.switch label .lever:before,.switch label .lever:after{content:"";position:absolute;display:inline-block;width:20px;height:20px;border-radius:50%;left:0;top:-3px;-webkit-transition:left 0.3s ease, background .3s ease, -webkit-box-shadow 0.1s ease, -webkit-transform .1s ease;transition:left 0.3s ease, background .3s ease, -webkit-box-shadow 0.1s ease, -webkit-transform .1s ease;transition:left 0.3s ease, background .3s ease, box-shadow 0.1s ease, transform .1s ease;transition:left 0.3s ease, background .3s ease, box-shadow 0.1s ease, transform .1s ease, -webkit-box-shadow 0.1s ease, -webkit-transform .1s ease}.switch label .lever:before{background-color:rgba(38,166,154,0.15)}.switch label .lever:after{background-color:#F1F1F1;-webkit-box-shadow:0px 3px 1px -2px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 1px 5px 0px rgba(0,0,0,0.12);box-shadow:0px 3px 1px -2px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 1px 5px 0px rgba(0,0,0,0.12)}input[type=checkbox]:checked:not(:disabled) ~ .lever:active::before,input[type=checkbox]:checked:not(:disabled).tabbed:focus ~ .lever::before{-webkit-transform:scale(2.4);transform:scale(2.4);background-color:rgba(38,166,154,0.15)}input[type=checkbox]:not(:disabled) ~ .lever:active:before,input[type=checkbox]:not(:disabled).tabbed:focus ~ .lever::before{-webkit-transform:scale(2.4);transform:scale(2.4);background-color:rgba(0,0,0,0.08)}.switch input[type=checkbox][disabled]+.lever{cursor:default;background-color:rgba(0,0,0,0.12)}.switch label input[type=checkbox][disabled]+.lever:after,.switch label input[type=checkbox][disabled]:checked+.lever:after{background-color:#949494}select{display:none}select.browser-default{display:block}select{background-color:rgba(255,255,255,0.9);width:100%;padding:5px;border:1px solid #f2f2f2;border-radius:2px;height:3rem}.select-label{position:absolute}.select-wrapper{position:relative}.select-wrapper.valid+label,.select-wrapper.invalid+label{width:100%;pointer-events:none}.select-wrapper input.select-dropdown{position:relative;cursor:pointer;background-color:transparent;border:none;border-bottom:1px solid #9e9e9e;outline:none;height:3rem;line-height:3rem;width:100%;font-size:16px;margin:0 0 8px 0;padding:0;display:block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:1}.select-wrapper input.select-dropdown:focus{border-bottom:1px solid #26a69a}.select-wrapper .caret{position:absolute;right:0;top:0;bottom:0;margin:auto 0;z-index:0;fill:rgba(0,0,0,0.87)}.select-wrapper+label{position:absolute;top:-26px;font-size:.8rem}select:disabled{color:rgba(0,0,0,0.42)}.select-wrapper.disabled+label{color:rgba(0,0,0,0.42)}.select-wrapper.disabled .caret{fill:rgba(0,0,0,0.42)}.select-wrapper input.select-dropdown:disabled{color:rgba(0,0,0,0.42);cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.select-wrapper i{color:rgba(0,0,0,0.3)}.select-dropdown li.disabled,.select-dropdown li.disabled>span,.select-dropdown li.optgroup{color:rgba(0,0,0,0.3);background-color:transparent}body.keyboard-focused .select-dropdown.dropdown-content li:focus{background-color:rgba(0,0,0,0.08)}.select-dropdown.dropdown-content li:hover{background-color:rgba(0,0,0,0.08)}.select-dropdown.dropdown-content li.selected{background-color:rgba(0,0,0,0.03)}.prefix ~ .select-wrapper{margin-left:3rem;width:92%;width:calc(100% - 3rem)}.prefix ~ label{margin-left:3rem}.select-dropdown li img{height:40px;width:40px;margin:5px 15px;float:right}.select-dropdown li.optgroup{border-top:1px solid #eee}.select-dropdown li.optgroup.selected>span{color:rgba(0,0,0,0.7)}.select-dropdown li.optgroup>span{color:rgba(0,0,0,0.4)}.select-dropdown li.optgroup ~ li.optgroup-option{padding-left:1rem}.file-field{position:relative}.file-field .file-path-wrapper{overflow:hidden;padding-left:10px}.file-field input.file-path{width:100%}.file-field .btn,.file-field .btn-large,.file-field .btn-small{float:left;height:3rem;line-height:3rem}.file-field span{cursor:pointer}.file-field input[type=file]{position:absolute;top:0;right:0;left:0;bottom:0;width:100%;margin:0;padding:0;font-size:20px;cursor:pointer;opacity:0;filter:alpha(opacity=0)}.file-field input[type=file]::-webkit-file-upload-button{display:none}.range-field{position:relative}input[type=range],input[type=range]+.thumb{cursor:pointer}input[type=range]{position:relative;background-color:transparent;border:none;outline:none;width:100%;margin:15px 0;padding:0}input[type=range]:focus{outline:none}input[type=range]+.thumb{position:absolute;top:10px;left:0;border:none;height:0;width:0;border-radius:50%;background-color:#26a69a;margin-left:7px;-webkit-transform-origin:50% 50%;transform-origin:50% 50%;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}input[type=range]+.thumb .value{display:block;width:30px;text-align:center;color:#26a69a;font-size:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}input[type=range]+.thumb.active{border-radius:50% 50% 50% 0}input[type=range]+.thumb.active .value{color:#fff;margin-left:-1px;margin-top:8px;font-size:10px}input[type=range]{-webkit-appearance:none}input[type=range]::-webkit-slider-runnable-track{height:3px;background:#c2c0c2;border:none}input[type=range]::-webkit-slider-thumb{border:none;height:14px;width:14px;border-radius:50%;background:#26a69a;-webkit-transition:-webkit-box-shadow .3s;transition:-webkit-box-shadow .3s;transition:box-shadow .3s;transition:box-shadow .3s, -webkit-box-shadow .3s;-webkit-appearance:none;background-color:#26a69a;-webkit-transform-origin:50% 50%;transform-origin:50% 50%;margin:-5px 0 0 0}.keyboard-focused input[type=range]:focus:not(.active)::-webkit-slider-thumb{-webkit-box-shadow:0 0 0 10px rgba(38,166,154,0.26);box-shadow:0 0 0 10px rgba(38,166,154,0.26)}input[type=range]{border:1px solid white}input[type=range]::-moz-range-track{height:3px;background:#c2c0c2;border:none}input[type=range]::-moz-focus-inner{border:0}input[type=range]::-moz-range-thumb{border:none;height:14px;width:14px;border-radius:50%;background:#26a69a;-webkit-transition:-webkit-box-shadow .3s;transition:-webkit-box-shadow .3s;transition:box-shadow .3s;transition:box-shadow .3s, -webkit-box-shadow .3s;margin-top:-5px}input[type=range]:-moz-focusring{outline:1px solid #fff;outline-offset:-1px}.keyboard-focused input[type=range]:focus:not(.active)::-moz-range-thumb{box-shadow:0 0 0 10px rgba(38,166,154,0.26)}input[type=range]::-ms-track{height:3px;background:transparent;border-color:transparent;border-width:6px 0;color:transparent}input[type=range]::-ms-fill-lower{background:#777}input[type=range]::-ms-fill-upper{background:#ddd}input[type=range]::-ms-thumb{border:none;height:14px;width:14px;border-radius:50%;background:#26a69a;-webkit-transition:-webkit-box-shadow .3s;transition:-webkit-box-shadow .3s;transition:box-shadow .3s;transition:box-shadow .3s, -webkit-box-shadow .3s}.keyboard-focused input[type=range]:focus:not(.active)::-ms-thumb{box-shadow:0 0 0 10px rgba(38,166,154,0.26)}.table-of-contents.fixed{position:fixed}.table-of-contents li{padding:2px 0}.table-of-contents a{display:inline-block;font-weight:300;color:#757575;padding-left:16px;height:1.5rem;line-height:1.5rem;letter-spacing:.4;display:inline-block}.table-of-contents a:hover{color:#a8a8a8;padding-left:15px;border-left:1px solid #ee6e73}.table-of-contents a.active{font-weight:500;padding-left:14px;border-left:2px solid #ee6e73}.sidenav{position:fixed;width:300px;left:0;top:0;margin:0;-webkit-transform:translateX(-100%);transform:translateX(-100%);height:100%;height:calc(100% + 60px);height:-moz-calc(100%);padding-bottom:60px;background-color:#fff;z-index:999;overflow-y:auto;will-change:transform;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateX(-105%);transform:translateX(-105%)}.sidenav.right-aligned{right:0;-webkit-transform:translateX(105%);transform:translateX(105%);left:auto;-webkit-transform:translateX(100%);transform:translateX(100%)}.sidenav .collapsible{margin:0}.sidenav li{float:none;line-height:48px}.sidenav li.active{background-color:rgba(0,0,0,0.05)}.sidenav li>a{color:rgba(0,0,0,0.87);display:block;font-size:14px;font-weight:500;height:48px;line-height:48px;padding:0 32px}.sidenav li>a:hover{background-color:rgba(0,0,0,0.05)}.sidenav li>a.btn,.sidenav li>a.btn-large,.sidenav li>a.btn-small,.sidenav li>a.btn-large,.sidenav li>a.btn-flat,.sidenav li>a.btn-floating{margin:10px 15px}.sidenav li>a.btn,.sidenav li>a.btn-large,.sidenav li>a.btn-small,.sidenav li>a.btn-large,.sidenav li>a.btn-floating{color:#fff}.sidenav li>a.btn-flat{color:#343434}.sidenav li>a.btn:hover,.sidenav li>a.btn-large:hover,.sidenav li>a.btn-small:hover,.sidenav li>a.btn-large:hover{background-color:#2bbbad}.sidenav li>a.btn-floating:hover{background-color:#26a69a}.sidenav li>a>i,.sidenav li>a>[class^="mdi-"],.sidenav li>a li>a>[class*="mdi-"],.sidenav li>a>i.material-icons{float:left;height:48px;line-height:48px;margin:0 32px 0 0;width:24px;color:rgba(0,0,0,0.54)}.sidenav .divider{margin:8px 0 0 0}.sidenav .subheader{cursor:initial;pointer-events:none;color:rgba(0,0,0,0.54);font-size:14px;font-weight:500;line-height:48px}.sidenav .subheader:hover{background-color:transparent}.sidenav .user-view{position:relative;padding:32px 32px 0;margin-bottom:8px}.sidenav .user-view>a{height:auto;padding:0}.sidenav .user-view>a:hover{background-color:transparent}.sidenav .user-view .background{overflow:hidden;position:absolute;top:0;right:0;bottom:0;left:0;z-index:-1}.sidenav .user-view .circle,.sidenav .user-view .name,.sidenav .user-view .email{display:block}.sidenav .user-view .circle{height:64px;width:64px}.sidenav .user-view .name,.sidenav .user-view .email{font-size:14px;line-height:24px}.sidenav .user-view .name{margin-top:16px;font-weight:500}.sidenav .user-view .email{padding-bottom:16px;font-weight:400}.drag-target{height:100%;width:10px;position:fixed;top:0;z-index:998}.drag-target.right-aligned{right:0}.sidenav.sidenav-fixed{left:0;-webkit-transform:translateX(0);transform:translateX(0);position:fixed}.sidenav.sidenav-fixed.right-aligned{right:0;left:auto}@media only screen and (max-width: 992px){.sidenav.sidenav-fixed{-webkit-transform:translateX(-105%);transform:translateX(-105%)}.sidenav.sidenav-fixed.right-aligned{-webkit-transform:translateX(105%);transform:translateX(105%)}.sidenav>a{padding:0 16px}.sidenav .user-view{padding:16px 16px 0}}.sidenav .collapsible-body>ul:not(.collapsible)>li.active,.sidenav.sidenav-fixed .collapsible-body>ul:not(.collapsible)>li.active{background-color:#ee6e73}.sidenav .collapsible-body>ul:not(.collapsible)>li.active a,.sidenav.sidenav-fixed .collapsible-body>ul:not(.collapsible)>li.active a{color:#fff}.sidenav .collapsible-body{padding:0}.sidenav-overlay{position:fixed;top:0;left:0;right:0;opacity:0;height:120vh;background-color:rgba(0,0,0,0.5);z-index:997;display:none}.preloader-wrapper{display:inline-block;position:relative;width:50px;height:50px}.preloader-wrapper.small{width:36px;height:36px}.preloader-wrapper.big{width:64px;height:64px}.preloader-wrapper.active{-webkit-animation:container-rotate 1568ms linear infinite;animation:container-rotate 1568ms linear infinite}@-webkit-keyframes container-rotate{to{-webkit-transform:rotate(360deg)}}@keyframes container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-layer{position:absolute;width:100%;height:100%;opacity:0;border-color:#26a69a}.spinner-blue,.spinner-blue-only{border-color:#4285f4}.spinner-red,.spinner-red-only{border-color:#db4437}.spinner-yellow,.spinner-yellow-only{border-color:#f4b400}.spinner-green,.spinner-green-only{border-color:#0f9d58}.active .spinner-layer.spinner-blue{-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,blue-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,blue-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .spinner-layer.spinner-red{-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,red-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,red-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .spinner-layer.spinner-yellow{-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,yellow-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,yellow-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .spinner-layer.spinner-green{-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,green-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,green-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .spinner-layer,.active .spinner-layer.spinner-blue-only,.active .spinner-layer.spinner-red-only,.active .spinner-layer.spinner-yellow-only,.active .spinner-layer.spinner-green-only{opacity:1;-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}@-webkit-keyframes fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg)}}@keyframes fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-webkit-keyframes blue-fade-in-out{from{opacity:1}25%{opacity:1}26%{opacity:0}89%{opacity:0}90%{opacity:1}100%{opacity:1}}@keyframes blue-fade-in-out{from{opacity:1}25%{opacity:1}26%{opacity:0}89%{opacity:0}90%{opacity:1}100%{opacity:1}}@-webkit-keyframes red-fade-in-out{from{opacity:0}15%{opacity:0}25%{opacity:1}50%{opacity:1}51%{opacity:0}}@keyframes red-fade-in-out{from{opacity:0}15%{opacity:0}25%{opacity:1}50%{opacity:1}51%{opacity:0}}@-webkit-keyframes yellow-fade-in-out{from{opacity:0}40%{opacity:0}50%{opacity:1}75%{opacity:1}76%{opacity:0}}@keyframes yellow-fade-in-out{from{opacity:0}40%{opacity:0}50%{opacity:1}75%{opacity:1}76%{opacity:0}}@-webkit-keyframes green-fade-in-out{from{opacity:0}65%{opacity:0}75%{opacity:1}90%{opacity:1}100%{opacity:0}}@keyframes green-fade-in-out{from{opacity:0}65%{opacity:0}75%{opacity:1}90%{opacity:1}100%{opacity:0}}.gap-patch{position:absolute;top:0;left:45%;width:10%;height:100%;overflow:hidden;border-color:inherit}.gap-patch .circle{width:1000%;left:-450%}.circle-clipper{display:inline-block;position:relative;width:50%;height:100%;overflow:hidden;border-color:inherit}.circle-clipper .circle{width:200%;height:100%;border-width:3px;border-style:solid;border-color:inherit;border-bottom-color:transparent !important;border-radius:50%;-webkit-animation:none;animation:none;position:absolute;top:0;right:0;bottom:0}.circle-clipper.left .circle{left:0;border-right-color:transparent !important;-webkit-transform:rotate(129deg);transform:rotate(129deg)}.circle-clipper.right .circle{left:-100%;border-left-color:transparent !important;-webkit-transform:rotate(-129deg);transform:rotate(-129deg)}.active .circle-clipper.left .circle{-webkit-animation:left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .circle-clipper.right .circle{-webkit-animation:right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}@-webkit-keyframes left-spin{from{-webkit-transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg)}}@keyframes left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@-webkit-keyframes right-spin{from{-webkit-transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg)}}@keyframes right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}#spinnerContainer.cooldown{-webkit-animation:container-rotate 1568ms linear infinite,fade-out 400ms cubic-bezier(0.4, 0, 0.2, 1);animation:container-rotate 1568ms linear infinite,fade-out 400ms cubic-bezier(0.4, 0, 0.2, 1)}@-webkit-keyframes fade-out{from{opacity:1}to{opacity:0}}@keyframes fade-out{from{opacity:1}to{opacity:0}}.slider{position:relative;height:400px;width:100%}.slider.fullscreen{height:100%;width:100%;position:absolute;top:0;left:0;right:0;bottom:0}.slider.fullscreen ul.slides{height:100%}.slider.fullscreen ul.indicators{z-index:2;bottom:30px}.slider .slides{background-color:#9e9e9e;margin:0;height:400px}.slider .slides li{opacity:0;position:absolute;top:0;left:0;z-index:1;width:100%;height:inherit;overflow:hidden}.slider .slides li img{height:100%;width:100%;background-size:cover;background-position:center}.slider .slides li .caption{color:#fff;position:absolute;top:15%;left:15%;width:70%;opacity:0}.slider .slides li .caption p{color:#e0e0e0}.slider .slides li.active{z-index:2}.slider .indicators{position:absolute;text-align:center;left:0;right:0;bottom:0;margin:0}.slider .indicators .indicator-item{display:inline-block;position:relative;cursor:pointer;height:16px;width:16px;margin:0 12px;background-color:#e0e0e0;-webkit-transition:background-color .3s;transition:background-color .3s;border-radius:50%}.slider .indicators .indicator-item.active{background-color:#4CAF50}.carousel{overflow:hidden;position:relative;width:100%;height:400px;-webkit-perspective:500px;perspective:500px;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;-webkit-transform-origin:0% 50%;transform-origin:0% 50%}.carousel.carousel-slider{top:0;left:0}.carousel.carousel-slider .carousel-fixed-item{position:absolute;left:0;right:0;bottom:20px;z-index:1}.carousel.carousel-slider .carousel-fixed-item.with-indicators{bottom:68px}.carousel.carousel-slider .carousel-item{width:100%;height:100%;min-height:400px;position:absolute;top:0;left:0}.carousel.carousel-slider .carousel-item h2{font-size:24px;font-weight:500;line-height:32px}.carousel.carousel-slider .carousel-item p{font-size:15px}.carousel .carousel-item{visibility:hidden;width:200px;height:200px;position:absolute;top:0;left:0}.carousel .carousel-item>img{width:100%}.carousel .indicators{position:absolute;text-align:center;left:0;right:0;bottom:0;margin:0}.carousel .indicators .indicator-item{display:inline-block;position:relative;cursor:pointer;height:8px;width:8px;margin:24px 4px;background-color:rgba(255,255,255,0.5);-webkit-transition:background-color .3s;transition:background-color .3s;border-radius:50%}.carousel .indicators .indicator-item.active{background-color:#fff}.carousel.scrolling .carousel-item .materialboxed,.carousel .carousel-item:not(.active) .materialboxed{pointer-events:none}.tap-target-wrapper{width:800px;height:800px;position:fixed;z-index:1000;visibility:hidden;-webkit-transition:visibility 0s .3s;transition:visibility 0s .3s}.tap-target-wrapper.open{visibility:visible;-webkit-transition:visibility 0s;transition:visibility 0s}.tap-target-wrapper.open .tap-target{-webkit-transform:scale(1);transform:scale(1);opacity:.95;-webkit-transition:opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1),-webkit-transform 0.3s cubic-bezier(0.42, 0, 0.58, 1);transition:opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1),-webkit-transform 0.3s cubic-bezier(0.42, 0, 0.58, 1);transition:transform 0.3s cubic-bezier(0.42, 0, 0.58, 1),opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1);transition:transform 0.3s cubic-bezier(0.42, 0, 0.58, 1),opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1),-webkit-transform 0.3s cubic-bezier(0.42, 0, 0.58, 1)}.tap-target-wrapper.open .tap-target-wave::before{-webkit-transform:scale(1);transform:scale(1)}.tap-target-wrapper.open .tap-target-wave::after{visibility:visible;-webkit-animation:pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite;animation:pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite;-webkit-transition:opacity .3s,
visibility 0s 1s,
-webkit-transform .3s;transition:opacity .3s,
visibility 0s 1s,
-webkit-transform .3s;transition:opacity .3s,
transform .3s,
visibility 0s 1s;transition:opacity .3s,
transform .3s,
visibility 0s 1s,
-webkit-transform .3s}.tap-target{position:absolute;font-size:1rem;border-radius:50%;background-color:#ee6e73;-webkit-box-shadow:0 20px 20px 0 rgba(0,0,0,0.14),0 10px 50px 0 rgba(0,0,0,0.12),0 30px 10px -20px rgba(0,0,0,0.2);box-shadow:0 20px 20px 0 rgba(0,0,0,0.14),0 10px 50px 0 rgba(0,0,0,0.12),0 30px 10px -20px rgba(0,0,0,0.2);width:100%;height:100%;opacity:0;-webkit-transform:scale(0);transform:scale(0);-webkit-transition:opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1),-webkit-transform 0.3s cubic-bezier(0.42, 0, 0.58, 1);transition:opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1),-webkit-transform 0.3s cubic-bezier(0.42, 0, 0.58, 1);transition:transform 0.3s cubic-bezier(0.42, 0, 0.58, 1),opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1);transition:transform 0.3s cubic-bezier(0.42, 0, 0.58, 1),opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1),-webkit-transform 0.3s cubic-bezier(0.42, 0, 0.58, 1)}.tap-target-content{position:relative;display:table-cell}.tap-target-wave{position:absolute;border-radius:50%;z-index:10001}.tap-target-wave::before,.tap-target-wave::after{content:'';display:block;position:absolute;width:100%;height:100%;border-radius:50%;background-color:#ffffff}.tap-target-wave::before{-webkit-transform:scale(0);transform:scale(0);-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s, -webkit-transform .3s}.tap-target-wave::after{visibility:hidden;-webkit-transition:opacity .3s,
visibility 0s,
-webkit-transform .3s;transition:opacity .3s,
visibility 0s,
-webkit-transform .3s;transition:opacity .3s,
transform .3s,
visibility 0s;transition:opacity .3s,
transform .3s,
visibility 0s,
-webkit-transform .3s;z-index:-1}.tap-target-origin{top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);z-index:10002;position:absolute !important}.tap-target-origin:not(.btn):not(.btn-large):not(.btn-small),.tap-target-origin:not(.btn):not(.btn-large):not(.btn-small):hover{background:none}@media only screen and (max-width: 600px){.tap-target,.tap-target-wrapper{width:600px;height:600px}}.pulse{overflow:visible;position:relative}.pulse::before{content:'';display:block;position:absolute;width:100%;height:100%;top:0;left:0;background-color:inherit;border-radius:inherit;-webkit-transition:opacity .3s, -webkit-transform .3s;transition:opacity .3s, -webkit-transform .3s;transition:opacity .3s, transform .3s;transition:opacity .3s, transform .3s, -webkit-transform .3s;-webkit-animation:pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite;animation:pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite;z-index:-1}@-webkit-keyframes pulse-animation{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}50%{opacity:0;-webkit-transform:scale(1.5);transform:scale(1.5)}100%{opacity:0;-webkit-transform:scale(1.5);transform:scale(1.5)}}@keyframes pulse-animation{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}50%{opacity:0;-webkit-transform:scale(1.5);transform:scale(1.5)}100%{opacity:0;-webkit-transform:scale(1.5);transform:scale(1.5)}}.datepicker-modal{max-width:325px;min-width:300px;max-height:none}.datepicker-container.modal-content{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;padding:0}.datepicker-controls{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;width:280px;margin:0 auto}.datepicker-controls .selects-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.datepicker-controls .select-wrapper input{border-bottom:none;text-align:center;margin:0}.datepicker-controls .select-wrapper input:focus{border-bottom:none}.datepicker-controls .select-wrapper .caret{display:none}.datepicker-controls .select-year input{width:50px}.datepicker-controls .select-month input{width:70px}.month-prev,.month-next{margin-top:4px;cursor:pointer;background-color:transparent;border:none}.datepicker-date-display{-webkit-box-flex:1;-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;background-color:#26a69a;color:#fff;padding:20px 22px;font-weight:500}.datepicker-date-display .year-text{display:block;font-size:1.5rem;line-height:25px;color:rgba(255,255,255,0.7)}.datepicker-date-display .date-text{display:block;font-size:2.8rem;line-height:47px;font-weight:500}.datepicker-calendar-container{-webkit-box-flex:2.5;-webkit-flex:2.5 auto;-ms-flex:2.5 auto;flex:2.5 auto}.datepicker-table{width:280px;font-size:1rem;margin:0 auto}.datepicker-table thead{border-bottom:none}.datepicker-table th{padding:10px 5px;text-align:center}.datepicker-table tr{border:none}.datepicker-table abbr{text-decoration:none;color:#999}.datepicker-table td{border-radius:50%;padding:0}.datepicker-table td.is-today{color:#26a69a}.datepicker-table td.is-selected{background-color:#26a69a;color:#fff}.datepicker-table td.is-outside-current-month,.datepicker-table td.is-disabled{color:rgba(0,0,0,0.3);pointer-events:none}.datepicker-day-button{background-color:transparent;border:none;line-height:38px;display:block;width:100%;border-radius:50%;padding:0 5px;cursor:pointer;color:inherit}.datepicker-day-button:focus{background-color:rgba(43,161,150,0.25)}.datepicker-footer{width:280px;margin:0 auto;padding-bottom:5px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.datepicker-cancel,.datepicker-clear,.datepicker-today,.datepicker-done{color:#26a69a;padding:0 1rem}.datepicker-clear{color:#F44336}@media only screen and (min-width: 601px){.datepicker-modal{max-width:625px}.datepicker-container.modal-content{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.datepicker-date-display{-webkit-box-flex:0;-webkit-flex:0 1 270px;-ms-flex:0 1 270px;flex:0 1 270px}.datepicker-controls,.datepicker-table,.datepicker-footer{width:320px}.datepicker-day-button{line-height:44px}}.timepicker-modal{max-width:325px;max-height:none}.timepicker-container.modal-content{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;padding:0}.text-primary{color:#fff}.timepicker-digital-display{-webkit-box-flex:1;-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;background-color:#26a69a;padding:10px;font-weight:300}.timepicker-text-container{font-size:4rem;font-weight:bold;text-align:center;color:rgba(255,255,255,0.6);font-weight:400;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.timepicker-span-hours,.timepicker-span-minutes,.timepicker-span-am-pm div{cursor:pointer}.timepicker-span-hours{margin-right:3px}.timepicker-span-minutes{margin-left:3px}.timepicker-display-am-pm{font-size:1.3rem;position:absolute;right:1rem;bottom:1rem;font-weight:400}.timepicker-analog-display{-webkit-box-flex:2.5;-webkit-flex:2.5 auto;-ms-flex:2.5 auto;flex:2.5 auto}.timepicker-plate{background-color:#eee;border-radius:50%;width:270px;height:270px;overflow:visible;position:relative;margin:auto;margin-top:25px;margin-bottom:5px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.timepicker-canvas,.timepicker-dial{position:absolute;left:0;right:0;top:0;bottom:0}.timepicker-minutes{visibility:hidden}.timepicker-tick{border-radius:50%;color:rgba(0,0,0,0.87);line-height:40px;text-align:center;width:40px;height:40px;position:absolute;cursor:pointer;font-size:15px}.timepicker-tick.active,.timepicker-tick:hover{background-color:rgba(38,166,154,0.25)}.timepicker-dial{-webkit-transition:opacity 350ms, -webkit-transform 350ms;transition:opacity 350ms, -webkit-transform 350ms;transition:transform 350ms, opacity 350ms;transition:transform 350ms, opacity 350ms, -webkit-transform 350ms}.timepicker-dial-out{opacity:0}.timepicker-dial-out.timepicker-hours{-webkit-transform:scale(1.1, 1.1);transform:scale(1.1, 1.1)}.timepicker-dial-out.timepicker-minutes{-webkit-transform:scale(0.8, 0.8);transform:scale(0.8, 0.8)}.timepicker-canvas{-webkit-transition:opacity 175ms;transition:opacity 175ms}.timepicker-canvas line{stroke:#26a69a;stroke-width:4;stroke-linecap:round}.timepicker-canvas-out{opacity:0.25}.timepicker-canvas-bearing{stroke:none;fill:#26a69a}.timepicker-canvas-bg{stroke:none;fill:#26a69a}.timepicker-footer{margin:0 auto;padding:5px 1rem;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.timepicker-clear{color:#F44336}.timepicker-close{color:#26a69a}.timepicker-clear,.timepicker-close{padding:0 20px}@media only screen and (min-width: 601px){.timepicker-modal{max-width:600px}.timepicker-container.modal-content{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.timepicker-text-container{top:32%}.timepicker-display-am-pm{position:relative;right:auto;bottom:auto;text-align:center;margin-top:1.2rem}}
diff --git a/static/css/style.css b/static/css/style.css
index 12c8ed7..221e301 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -1,55 +1,37 @@
-html, body, div, span, applet, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-a, abbr, acronym, address, big, cite, code,
-del, dfn, em, img, ins, kbd, q, s, samp,
-small, strike, sub, sup, tt, var,
-b, u, i, center,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td,
-article, aside, canvas, details, embed,
-figure, figcaption, footer, header, hgroup,
-menu, nav, output, ruby, section, summary,
-time, mark, audio, video {
- margin: 0;
- padding: 0;
- border: 0;
- font-size: 100%;
- font: inherit;
- vertical-align: baseline;
+main {
+ background: #F1F1F1;
}
-/* HTML5 display-role reset for older browsers */
-article, aside, details, figcaption, figure,
-footer, header, hgroup, menu, nav, section {
- display: block;
+.nav-add {
+ padding-top: 12px;
+ height: 64px;
}
-body {
- line-height: 1;
+img.add-contacts {
+ height: 40px;
+ cursor: pointer;
}
-ol, ul {
- list-style: none;
+.row .col.s12.main-container {
+ background: #FFF;
+ padding: 10px;
+ border-radius: 5px;
}
-blockquote, q {
- quotes: none;
+#search {
+ background: white;
+ border: none;
+ border-radius: 7px;
+ height: 40px;
+ width: 200px;
+ transition: all 0.3s;
+ padding-left: 10px;
}
-blockquote:before, blockquote:after,
-q:before, q:after {
- content: '';
- content: none;
+#search:hover {
+ width: 350px;
}
-table {
- border-collapse: collapse;
- border-spacing: 0;
-}
-
-* {
- padding: 0;
- margin: 0;
- box-sizing: border-box;
-}
+#search:focus {
+ width: 500px;
+}
\ No newline at end of file
diff --git a/static/img/add-contacts.png b/static/img/add-contacts.png
new file mode 100644
index 0000000000000000000000000000000000000000..ea01cce9ab63abb4b2213fc730fb0b0cc3893e3f
GIT binary patch
literal 1274
zcmZ`%c~BEq9Dd{g8jVswQN%)9q>)1tK(3;4N+?J
V2qF!TO+t~1+NzbyzFqmJe{|-({k`A!`@Zk@-rH9Z
z84<$gS@Qq@{7`XFG>m@qG-bo@rHqAC~^P*KL@}H
zgzyCb@`L~^N&xVw1HdY);Z~F{07m(d;jzJxpj9a-0@V^2r9vqZp;U07aOhO1mP9n9
z=5I%YHQywFNd%3psQ_r_31
zU~`NulQJ!sRIifLw+CA=Lj6~$zDiEt%$uDMbZIkXKn*T|2)y&*+I?LxoX(Aohyi=B
zFPd)M{o!)iNi!C@u@WvEzG)Nmf^zVFska_HFaGC)CxP3OoMo;@_`Cn^AGz3G80)pR
ztSE`?o6K+7!!@%rduj9=)bxc0`NziJ{&V6)3`=Cmos|rf+6|WO7_2{HY~BT(_6I>Bzb;{i_2
zygPBwnUz-=X1{VZVx*dI`uN@O4>O#~jgH*VuDruwmY
z?qj%vrJ}{?oz~=IGk11!6jxbEoF`Gm9vp?iwkEu+Q8QHnl#gu$DKo4_lbMnv>ya|n
zx_hHJ*I3bS$J;uJ_Y22tMWbM+WwPmusAHVT$@@^^6rB@)3FXxVbtBMLt5IAayAIKZ1-*c`91?
z|AGYfGZYl;raQ!BqPh9fYy{-z=O0Ktm7bF#%|Z@jW@Gh_eXSu0ga$_h-3XAJ{TJR=
BN+AFM
literal 0
HcmV?d00001
diff --git a/static/img/favicon.ico b/static/img/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..c6fa4af543fd3b6b8526d72ceffac9c300a7501c
GIT binary patch
literal 4286
zcmeHKOHUJF6dhM=T)1-0(x2eQgkQiy;}4)MGfioY)>=zx<<&&xCH0jEiE*ikx^d&i
z5H-d}FtkuxBo%2YPy|#U^4NI1ciI`}D>E%EC0*b&XF8pk@1FZO_s&$3j?kyEQR2Py
z@R%eWmn7)~flf(H1U|2BbnVR1Jn|?0?+<$h9M{qf_DHhX)-x5gb;n05g}pa1Di3By
zN52Y+l8{t0XN-Y=tdC<$9v3%b@CkN@NEpMw$_hco!j5=!_Zy5>$do5zubT0*23ZGz)EYvIn+Pp!p(FbnzPHOzZcXdl
z0zcPvnCf2&tc_CjH_jlcg`+P4=g16P_tPpb_XXG7=(8R_+Z|-kEV4ooH~V6=wdXcA3q5h?`$Jh3%KjOde
zVlh913^Sl(VH3*T&w>_o{u=fl*ZutSd6A!Tz&&W%f26-!&L8IIINN7w4r;TQ_`NT4
z>a$7Lr=~xye=X*(vH$7Vd2a^ZS4+4wzJhblvT84s?EiAlp1syz^Mmq`*LY#>6n>k=
xZ!%koXPwz^qpfO=b^TSx&+i{~eg7i=>-zp#@AvP+{{QE&$B&}zpz~2ghrgt-TX6sY
literal 0
HcmV?d00001
diff --git a/static/img/logo.png b/static/img/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..e513585194604d6a2a93a637b0068b3202476749
GIT binary patch
literal 9871
zcmeHt_g7TQvv1EZ1Vxe@zWgh2!h2#N|4BnXJ)JOYvg
z6_MyDIVw3w4w7$k&RyU42fX)wxNBY3uv7Q0uCD4`^{J|UprfTqb(-lk0DuasrlJb~
z5?&$!1sObT2K?BACzQ`stUd+&3#PbzAKstxR5S4b;EdkMKSD`wEeJk5?|aSISI@)V
z*WcRP4*2`~i#WNv_}E%|+KG60JESaMW&!{Wuqs#d15$sF;+~lIhL5j}hxlqY;_OW&
zh&MFU(5UhzwAil;#>0Q#=xE0m^gWp5sxqru`?zZ>IQfy^qx^+>&ZVm2fJy9cc8$js
zVXkh!WV{Fm%~4em{qGCiglyReA7-p+(LtN3JHFc5{^Pif)|h*=@iV=n?>y31a@8Xx
z&k~c8mKDbpcgB2*&=e>DI&7j9AoD$;@EVYDz=Ieq0swqC1-v|?0uQ&)Cjud%gNMIQ
zCf|hl->(0+5}1lo!27)Td_9PpN`uLCccnD@^@>X(J4*fl6LE$1;~|E?{^iu&rZp1T
zaM59-2|kKDfADniUN{lDce5>QCG23#%1{k60!O
z^2wy-cUZ78I{
zpNA}SUKX9t`t)tBg?Tq!dh?9PvToN2`P7TK$VrSQ7*=uS9
z8YXr>jQTL}gaIHoFdE-bnxIte44!|!9@@GS(cNKC{TU8J@#lt<>y*FYzB1z|`3aBD
z*ymOp1T8xpw#iHHh5&r5(X-7XsFqC1!*EK)2n}1#gzV^cgA=M8JRYXIM5F%s?}-{c||r;W))CSISG6r>{tlRUzR^ZBlg;j|U(2563=5+-Ea{G-bJ+{GlqAmuMOm&$Fnd
z3mS!-a8TQF4tu;kLepSN3V9c=G~O70E3{kY7kdU%S&9TITGxovP(N9@N~^IBrL*K=
z)yB5#Dq`Rx1NdOp{
z*dq>np?2a13zn-MGCPnS=Vf#e3O`G}b5H&pG~qlnYVlirj7l$FSvq!7g~(}2`*!#
zlpHT|3+w*%X^Phrmuujo1|~c0#}9c)t!b!%ry>gUag8ks$o`d>;x~5m)NAUETMbsV
zylX>=R5$CFa4RN|`}Ef|Cd#yPaeewJZk2}{00?mc&npj#yzE3Y`EVw>YYkMF^@UzN
zh}zOK0mTQKmU2cUjZ!g0B^_vG6G5pnyE}6`LD6*9>R@VBb8ZW1;mAV0pUhA$DdW#
zG>=>@tIku?1B@UoAoI~9oF@1Fx}wp(kg<4W>1EEy3x2zuY{~tCa-Q^LE5%G&&?AY$
zhr^bm#qx#W^rx(pv#iIa#)KcZJEk>)ddZ>gSM<8O+Dm*K0Z0x9V)yMdc0-&PsHdjh
z$ju2K3&1y@3@
z8!A%}*%6X8KSw}LL4~1G?AU-=Dvng7
z{+s-`K=Gx_N|m1&!?jSO)&`}{8U!fSLSYiUC1M}${Pq0r`;iLI6TU8M38eWO1o`T2
z1s4AW@KwKPF^uZ2k6mUxXnYT+!c4mpUiuwvXT&S7l}01M(<{I?35vvGz2{s_Uf=Ss
zey7yzB32sLo1s<$CoFJzQe-^ZTd
zOL(|*PA*7eTx{T?2As8b{N;~`iQs@cF=Fim{kr#7WvM2-2So-#i*Nrty(^R=gT(vL
z0Xo7KNg#JLu9@w+ss?py)fP!AGXl(V0FL~X?LN18OMD)ESnkbJZyy1pZmy99-{t^FE?iZb@_22zQBu1N1T}?fC>-rwRM9L+O?T
zGjG#?kelJ`+iE?oq3BmH%Y1D<$Itq_j+qyKfKiUbB&pDD>&Tr&(9+c#1`yW9aLpKz-qL|oEkg^>RQvhrHn4_l&JvU>dh{ce}xNOd)4#Wdnx?a+RSoY59dfL
z#;Y;|^QGp^lrHY*%OEpa>`dOc^HY?DO+WK3u=AiN=nwpjp+Fx{=RzAN5uV+4ra;6@W
zrLZR+9#59$(OF;_%>jnmHP^P5eOpM~$M5%vNr{}oQ(ps>jL~Lt;|b;&)ipRmM&ymh
z8psUgGOkfel+kR3(EMI|O}}Vz0WVLzKO+K9-F)u{Y>*rY3{j3uO=_=ld>ppg5Vh_k
z!LC=gaEA|>QU51MRk`D-qMwVhd1bhLHC!|M+b`eeL$UM4V&5?$XjzBf{n_g|B9oV~
z)mxv((2Ni%aDQk1dY(-E2@(&k&zgQ$ysa;U+WY*h*uFg*^f?^6Yx}c1^3hS^xPID@
zW+{ljFI^Z@0a)w#IHi|1BGucUW
zBV%h-ZUoKUQubfkk-X@XC+Xy3LnJ;puc0`uJDEe@p7@7~5ea{8iyPDH&Fnb#cy;6AW4ZT(ucch6sQY$QkcG&PQsII_722
zz4gAw0Km)zAotQ}t194mTi%PWstHp*2kbDA#8bCgLI8!+j8r*qXWpHv;_
zsQ`z=cE4Tk8>SP}n4r}wwWG_uWDPK?Xz-fniPq8WOG`IZh;8f}cS!KxDex4gGUn5-
zmgG8*@(acN4@vM6(BjK&N1Qi^>xY(`ONgy#{ttcKJ~_SunskPMaP>GXFM}q5g^e`z
zI8E>xD)+%LWUNpJoeKhHbL)y0z@-Yx7vh6aiLmCx%ew|zKY8vX5Qtb$@uj@}5C
z4s=C>IiydCkn_6JHo44jUV4|)k(B#331GYmd>`mV&s2I=Gi{5>H6M7KliL033JNg7
z)$$0F{Ac<3S|WKDWHswJPMHIgGD$BXG6IpJJ>Ob$t0YbOn}h?~PUBSE{!%+}-t$&M
z_Y}^8`zT}vrKZCBjGVlvsFj6htSd*?*gV?$KlQp=T6tblR*-fCby2~rrf#fN~t&D}W6o<|V{mLQ5Acbhd6=@TR9ub14Bwab(l2Y3Y1{MiYZj5A{eA*-zzK1Ilt)mxg<$
zGG#mKoBbM27{|{eowoZKI*)2!f9$I+PvnNcE-%1ppPfnL*&Sh)VrPiY47AIC(10Qp
zFcAYs&2M#)+nq}NRbMPFc=5@|Igd`IzttYKI-3T#-Q0js(MOgYR8k@E8~L4*l3N{j
z-=vv5N%20xi&Hx0C%DybD%(v~!KLxxkAgjC{mW8cPay0g)kLUl0grDUKOGRDnidHP
zU>gKyeI1n3{=p;OLN!jR?n9QlC7ijfmT61vW5Y@U4am5~EkwpMS};)BM5&+d@>Y>?
zTMdB4Pg?l@WPMnWpyv+sr@>vKQkDXAd|g8RSAv~@O7g6RC~KKPa;4Nor}??(MW0Lu
zvaa%$3gtba)ED~0v_Mmd`_g|)l$R?Vh6eWwv-A=gwzFPpbr@UIkh1boWb0KVXm!f%
z?YhLfVaay_YY&H_M!E+7NJ^pr{>xhM`~$1npkIlxwNoPfjwDJ?LjT;&N*pWSDl=PT
z5_x$z@skS!o^JH=+UyNmiS63jYIT|Jt;BuWnmJXGi>Xk;!N!q-Sj)@J8jP1(X|g>I
z1`I|gSic0GL)?!UEYE38`}Ku8+17iEIN6=NAHV@%PzgP*LCsCBsl%uBVK2EVWHGe{ZIX9_N{41EI!Bke)5xGH#!kc_3@jivt^~K;qKnwEwzW=(ZV3`
z8}w@a>|%eGg&p&+Ug~+(pY-pfz8;OHZ7+*$urepN60*r@F8w93V~K%O=63(=FYZl9
zvGJ1HD3h&+AMK7xS%^FOsWFWxg4bWma)wLX99O8dt@dx;;I-#N6m}7^Bla(khPo*i
z({DRAtDYz|^9XbNYb@DP&ZgjB@Ai$~>`_&zYLwP36Wt4pZ;e-_ey}_4JSq8LDIvNm
z&yEC`Y*|93M{Y@o{J3S;s{-ToPRr5AY<&)$RFmbqe~4gCuI8_mb$qYqS@i$Iyrd(W
zM!9Xm?MMY$(Bym)0(RNA3Vm8H{uNUEE6`}o(#S7PnAg26cCg&=$fZ?yaCumwahzhR
zAGv$S>Wc6aqn*UC`l*6T-*up6ffj8mDe8c1$DJJS2SdN>msbQ#mTuah-vq3z-(Im#
zr8=}A;N&O_t!_ID1+?E3`wq>D<_lfTt-;SxM;4%I2nK17>{xRMJA4M_M`hpILMujM
zIHfBtpW_#Hl07YulOv(D;A6>WOM-X52L9%e8p&E>z~+cz`4Xrcnyo0MC;9yC)M20&
zpS@R?`}HYzXT9#toJuaQ9{^m61Y(%Feg%mnzxC{}i)Dpb2My2H_jMsB5H7zSWE9NT
z^mz;W-34>HTFr)j+Pb@fdN5jwR!DsV!>YZK?)>AuLg;OhaZWdVO7y3M_pE8C07nFv
z6VK&s9Sh+%(b}Fbr=SIUB%{alDrHR(s(BjZb3P-DP6IKL6`b4+Z@yxSW6vjTjh*qs
ze`-m@C=oqZ_I4a9CqIPuxVO8=@mjm=ACf|D8Mk#n#tgAySEH4z--b1H^~4q%fV5Wjkt3CGF6xbh*UTO1|*Rg
zntLVfZR#H&)076|htz$q+k&QhpSMd8$~=X#!1uU3z{z-FlJ-JiQJojlIpxhZTP?
zZ;SOHBi_xt82nUE0#xV`Jy-ok`g$xXAnIcyY(=4;`zDplD?qkwHnLVX}#_=Ng#gN0+;{2^r
zReD8a#si2s_DUv)JfBVPym`*T0nC1{r2B6s#F#?m&lmwDh!kM!;SvlmgEK
zQ#qpTm-Z2)!X?O6F~_8LT7!Ejv*unIq$wB82%DX}orYC;wh^FxTuV
ztGhe1yZpVlEIK|Q()n(Ki^Rj(PD}M>dD&BDX70B=@b;&X^GV|`_uZ+A%JdUMcWaaM
zGLzEp(8Glu$sCZ``hLsu)y)$SXYKn~&nLn8@rieYLDxS%d{iZp#H4t)HPD1mIsL{Q
zrVp6jocL-Up3Y#Z+4EJWw1|eG9s*0&84+-$^NqFt%(NHVy2ex_U4P5Ti+|~6B|D<@
zR7n5r>hJ7^pT=lAZx*kcD=$7z{3ifrzTGOc)sU!BUs?;(N;UjimcxZ^hpyuf`|iKj
zz2Gudm4j*`o8HNp_Do{zg96yH&=7L(^ZZc9=vdiG2(?NGqy(s@>&DybIbltvTb3_o
zZhzBcTHYDFMJIeyQW6HtR9`I9bcNdEMf$py%U(1-G{ASNeatC3v~SSKs`7J_+E_h{
zC`E+yuU?JaTAWOMs`(xQgz8bUWGC%!{bQ^!r-@GRc`@*HbgVS=vfm|-{c~3DET`dz
z<@-Rzd~dFEW?AGKDAaS;7GJu6ZVotb9sAvXsj@0g88J}zo<{PyM$*192k7I+k%~?X
zR4+|q-5KhzvBHPXXnLz~Upnl}TrEdlEWgjoJWNcgPgVhi%GzNu^`m9{?n5`7t9h5K
zRV+mq=F-PZ`a`6p{&L8=R@lNF(V!S&rAb~ahFeeMN|Re`dLJgK4875d`0)MMOG(`G
zO$=#%G`m6T{jul-|;4;vH9wp*}`OX5LfCf?q6nDRqz8YT;zws)$y4yeA!SdCoGk=O(HB
z5Z*q4x7OIU3u9%OfkA|iHPQgD10Oy4d*TyQr`@2gH**01&2S_{`>|sr*FFMJ&>>36
zTu1I2PI!@`Z=jl(xT1{_m{i=i9O@rbt?9Lh{qK>U8)L8DnriA1R++Mf1kz<5ps;0*+@@{HK&FNg;w_z
z;6{ZWLwdsGT<(9DW$wMYKN#&Qxa4(GIm*)r8Wo9+?S3CC1yi6W1)Kv5Sn3)};A$nX
zQQ;)vP{IxTMI)5x8RM{K$OVX5^#eA{1ShomVfBqX4xvmtPg*Wt~<@AUIw#jRDj&F4=lTxBh=LMD-NTtBH0
zrrohOe$IgehK-LK{I*5M=s>L7;Z1S({$Y7GI0+jD?D5tTO&upVDJ;`_?Q5G8$WS07
zC5Az@H7@Ni-2c_K12$cocPVN6F)UYA_V8nmr(t
zT#)ONq=WpaCM9fsdr=dl*;h4c<>h<8fMxe*ElAUpdkr;2VPQdRHJX`|GTRAlgu}LS
zk23fawgnG8ZAl7;-<_Q<(L&H?qt?~2;{$A?aZS(fOvYsdetm
zX*IB*8qLjGnXO7xAb4X%=%G$D*`E5seJo>7H0$q(!sVqAq(hvwEzSuZVlX3zi$OO%
zadYA;OP#eLt+{7n(;a?Tyw)YAYpCllHV7D4b?&Ahe3SL#inyYW@O}mAd-afm#FVu}
zso&@8#4{q|)6_n?PJfRLvMD5kD+8tN7}(ax|E%%HM+STa+$OQyyMFdlZv(qy**V9&
zu7{vU*cbtZW&*M4P#IylgJE78X&(eh>J5o2LxekAuS$DUyk5i%yP{E`X(3i^&jQ;|
zuvt$utweefhy!X@i7Og=B-r{ZXoJNDxWzzG1Bu~+hN`k7CR-_CGZ#wFS@1YBfxO8n
zr|hXBHa&jxNj5XLiN+S@PPb7#9W4pw9xcG=k;aD#%rC|_oUA_gR;3{VkTLsrA4NRd
z6W{3Uv?%tsV-)&3NGT@7EG1x#f$^3bmbk+mqDMSgHCeyeFnoxASh`MBtz;qDQCpzl
zlB%n?+r&wZ*SZP-vpDzsO=0c5|7_GCZ>oWN2kPUaxT%T`{Ec {
+ let tbody = $("table tbody");
+
+ for (let contact of contacts) {
+ tbody.append(`
+
+ ${(contact["first_name"] ? contact["first_name"] : (contact["firstName"] ? contact["firstName"] : ""))} |
+ ${(contact["last_name"] ? contact["last_name"] : (contact["lastName"] ? contact["lastName"] : ""))} |
+ ${(contact["surname"] ? contact["surname"] : "")} |
+ ${(contact["email"] ? contact["email"] : "")} |
+ ${(contact["address"] ? contact["address"] : "")} |
+ ${(contact["phone_number"] ? contact["phone_number"] : (contact["phoneNumber"] ? contact["phoneNumber"] : ""))} |
+ ${(contact["birthday"] ? new Date(contact["birthday"]).toLocaleDateString() : "")} |
+
+ `);
+ }
+};
+
+$(document).ready(() => {
+
+ let modal = $('.modal');
+
+ modal.modal();
+
+ let search = (query) => {
+ if (query.length >= 3) {
+ working = true;
+ $.ajax({
+ url: "/api/contact/search",
+ method: "POST",
+ data: {
+ query: query
+ },
+ success: function (data) {
+ $("table tbody").empty();
+
+ for (let i = 0; i < 4; i++) {
+ if (data && data.result && data.result[i.toString()] && data.result[i.toString()].length > 0) {
+ displayContacts(data.result[i.toString()]);
+ }
+ }
+
+ working = false;
+ },
+ error: function (e) {
+ working = false;
+ }
+ });
+ } else {
+ $("table tbody").empty();
+ displayContacts(contacts);
+ }
+ };
+
+ $(document).on("input", "#search", () => {
+ let query = $("#search").val();
+
+ if (!working) {
+ search(query);
+ } else {
+ lastQuery = query;
+ }
+ });
+
+ $(document).on("click", ".add-contacts", () => {
+ modal.open();
+ });
+
+ $("[data-tooltip]").tooltip();
+
+});
diff --git a/static/js/jquery-3.2.1.min.js b/static/js/jquery-3.2.1.min.js
deleted file mode 100644
index 644d35e..0000000
--- a/static/js/jquery-3.2.1.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */
-!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S),
-a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,""],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/
diff --git a/src/View/Site/tpl/head.php b/src/View/Site/tpl/head.php
index a7275a0..7a53de3 100644
--- a/src/View/Site/tpl/head.php
+++ b/src/View/Site/tpl/head.php
@@ -1,41 +1,66 @@
-
+