<?php
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
|
|
//
|
|
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
|
|
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
|
|
//
|
|
// PURPOSE:
|
|
// A brief OpenPGP support class for Tiki OpenPGP functionality in
|
|
// - webmail
|
|
// - ZF based mail
|
|
// - newsletters
|
|
// - admin notifications
|
|
//
|
|
//
|
|
//
|
|
// CHANGE HISTORY:
|
|
// v0.10
|
|
// 2012-11-04 hollmeer: Collected all functions into intial version openpgplib.php.
|
|
// Minimal preparation/calling portions remain in caller sources,
|
|
// as it seems so far adequate with current approch to leave
|
|
// such portions e.g. there
|
|
// NOTE: Zend Framework as is wrapped by bringing/changing
|
|
// necessary classes from
|
|
// Zend/Mail/ and
|
|
// Zend/Mail/Transport/
|
|
// into lib/openpgp/ per now. No patches needed anymore
|
|
// into ZF to enable 100% PGP/MIME encryption.
|
|
// v0.11
|
|
// 2014-11-04 hollmeer: Protected function naming to _xxxx
|
|
// v0.12
|
|
// 2014-12-01 hollmeer: Changed all OpenGPG functionality configuration to use
|
|
// preferences
|
|
//
|
|
//
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
//this script may only be included - so its better to die if called directly.
|
|
if (strpos($_SERVER["SCRIPT_NAME"], basename(__FILE__)) !== false) {
|
|
header("location: index.php");
|
|
exit;
|
|
}
|
|
|
|
class OpenPGPLib
|
|
{
|
|
//PGP/MIME HEADER CONSTANTS
|
|
const MULTIPART_PGP_ENCRYPTED = 'multipart/encrypted';
|
|
const TYPE_PGP_PROTOCOL = 'application/pgp-encrypted';
|
|
const PGP_MIME_NOTE = 'This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)';
|
|
const TYPE_PGP_CONTENT_VERSION = 'application/pgp-encrypted';
|
|
const DESCRIPTION_PGP_CONTENT_VERSION = 'PGP/MIME version identification';
|
|
const PGP_MIME_VERSION_IDENTIFICATION = 'Version: 1';
|
|
const TYPE_PGP_CONTENT_ENCRYPTED = 'application/octet-stream; name="encrypted.asc"';
|
|
const DESCRIPTION_PGP_CONTENT_ENCRYPTED = 'OpenPGP encrypted message';
|
|
const DISPOSITION_PGP_CONTENT_INLINE = 'inline; filename="encrypted.asc"';
|
|
|
|
/**
|
|
* EOL character string used by transport
|
|
* @var string
|
|
* @access public
|
|
*/
|
|
private $EOL = "\n";
|
|
|
|
|
|
/**
|
|
* Full path to gpg
|
|
* @var string
|
|
* @access protected
|
|
*/
|
|
private $_gpg_path;
|
|
|
|
/**
|
|
* Full path to keyring directory
|
|
* @var string
|
|
* @access protected
|
|
*/
|
|
private $_gpg_home;
|
|
|
|
/**
|
|
* gpg signer idfile
|
|
* @var string
|
|
* @access protected
|
|
*/
|
|
private $_gpg_sgn_id;
|
|
|
|
/**
|
|
* gpg signer passphrase
|
|
* @var string
|
|
* @access protected
|
|
*/
|
|
private $_gpg_sgn_passphrase;
|
|
|
|
/**
|
|
* gpg signer full passfile path
|
|
* @var string
|
|
* @access protected
|
|
*/
|
|
private $_gpg_sgn_passfile_path;
|
|
|
|
/**
|
|
* gpg trust
|
|
* depending on which version of GnuPG we're using there
|
|
* are two different ways to specify "always trust"
|
|
* @var string
|
|
* @access protected
|
|
*/
|
|
private $_gpg_trust;
|
|
|
|
/**
|
|
* Constructor function. Set initial defaults.
|
|
*/
|
|
public function __construct()
|
|
{
|
|
global $prefs,$tiki_p_admin;
|
|
|
|
$this->_gpg_path = $prefs['openpgp_gpg_path'];
|
|
$this->_gpg_home = $prefs['openpgp_gpg_home'];
|
|
$this->_gpg_sgn_id = $prefs['sender_email'];
|
|
if ($prefs['openpgp_gpg_signer_passphrase_store'] == 'file') {
|
|
$this->_gpg_sgn_passfile_path = $prefs['openpgp_gpg_signer_passfile'];
|
|
$this->_gpg_sgn_passphrase = '';
|
|
} else {
|
|
$this->_gpg_sgn_passfile_path = '';
|
|
$this->_gpg_sgn_passphrase = $prefs['openpgp_gpg_signer_passphrase'];
|
|
}
|
|
$this->_gpg_trust = '';
|
|
|
|
$this->setCrlf();
|
|
}
|
|
|
|
/**
|
|
* Accessor to set the CRLF style
|
|
*/
|
|
public function setCrlf($crlf = "\n")
|
|
{
|
|
if (! defined('CRLF')) {
|
|
define('CRLF', $crlf);
|
|
}
|
|
|
|
if (! defined('MAIL_MIMEPART_CRLF')) {
|
|
define('MAIL_MIMEPART_CRLF', $crlf);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gnupg version check; sets internal variable once
|
|
*
|
|
* @access protected
|
|
* @return void
|
|
*/
|
|
protected function _gpg_check_version()
|
|
{
|
|
|
|
//////////////////////////////////////////
|
|
// find which version of GnuPG we're using
|
|
//////////////////////////////////////////
|
|
|
|
///////////////////////////////
|
|
// open the GnuPG process and get the reply
|
|
// we're only concerned with the first line of output, so use "false" as last argument
|
|
$commandline = $this->_gpg_path
|
|
. ' --version';
|
|
$ret = $this->_gpg_exec_proc($commandline, null, false);
|
|
|
|
/////////////////////////////////////////////////////
|
|
// get the version (we are only concerned with the first line of output,
|
|
// which was read from gpg-process-output as single-line-read into $ret[1]
|
|
$gpg_version_output = $ret[0];
|
|
|
|
///////////////////////////////////////////////
|
|
// sanity check - see if we're working with gpg
|
|
if (preg_match('/^gpg /', $gpg_version_output) == 0) {
|
|
$error_msg = 'gpg executable is not GnuPG: "' . $this->_gpg_path . '"';
|
|
trigger_error($error_msg, E_USER_ERROR);
|
|
// if an error message directs you to the line above please
|
|
// double check that your path to gpg is really GnuPG
|
|
die();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
// pick the version number out of $gpg_encrypt_version_output
|
|
// we'll need this so we can determine the correct
|
|
// way to tell GnuPG how to "always trust"
|
|
$gpg_gpg_version = preg_replace('/^.* /', '', $gpg_version_output);
|
|
|
|
////////////////////////////////////////////////////////
|
|
// depending on which version of GnuPG we're using there
|
|
// are two different ways to specify "always trust"
|
|
if ("$gpg_gpg_version" < '1.2.3') {
|
|
$this->_gpg_trust = '--always-trust'; // the old way
|
|
} else {
|
|
$this->_gpg_trust = '--trust-model always'; // the new way
|
|
}
|
|
|
|
/////////////////////////////////////////////
|
|
// unset variables that we don't need anymore
|
|
unset(
|
|
$gpg_version_output,
|
|
$gpg_gpg_version,
|
|
$commandline
|
|
);
|
|
|
|
////////////////////////////////////////
|
|
// we're done checking the GnuPG version
|
|
////////////////////////////////////////
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Gnupg process call function
|
|
*
|
|
* @param string $gpg_proc_call
|
|
* @param string $gpg_proc_input
|
|
* @param boolean $read_multilines
|
|
* @access protected
|
|
* @return array
|
|
* 0 => process call output (STDOUT)
|
|
* 1 => warnings and notices (STDERR)
|
|
* 2 => exit status
|
|
*/
|
|
protected function _gpg_exec_proc($gpg_proc_call = '', $gpg_proc_input = null, $read_multilines = true)
|
|
{
|
|
|
|
if ($gpg_proc_call == '') {
|
|
die;
|
|
}
|
|
|
|
//////////////////////////////////////////////
|
|
// set up pipes for handling I/O to/from GnuPG
|
|
$gpg_descriptorspec = [
|
|
0 => ["pipe", "r"], // STDIN is a pipe that GnuPG will read from
|
|
1 => ["pipe", "w"], // STDOUT is a pipe that GnuPG will write to
|
|
2 => ["pipe", "w"] // STDERR is a pipe that GnuPG will write to
|
|
];
|
|
|
|
///////////////////////////////
|
|
// this opens the GnuPG process
|
|
$gpg_process = proc_open(
|
|
$gpg_proc_call,
|
|
$gpg_descriptorspec,
|
|
$gpg_pipes
|
|
);
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// this writes the "$gpg_encrypt_secret_message" to GnuPG on STDIN
|
|
if (is_resource($gpg_process)) {
|
|
if ($gpg_proc_input != null) {
|
|
fwrite($gpg_pipes[0], $gpg_proc_input);
|
|
}
|
|
fclose($gpg_pipes[0]);
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// this reads the output from GnuPG from STDOUT
|
|
$gpg_proc_output = '';
|
|
if ($read_multilines) {
|
|
while (! feof($gpg_pipes[1])) {
|
|
$gpg_proc_output .= fgets($gpg_pipes[1], 1024);
|
|
}
|
|
fclose($gpg_pipes[1]);
|
|
} else {
|
|
$gpg_proc_output = fgets($gpg_pipes[1], 1024);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// this reads warnings and notices from GnuPG from STDERR
|
|
$gpg_error_message = '';
|
|
while (! feof($gpg_pipes[2])) {
|
|
$gpg_error_message .= fgets($gpg_pipes[2], 1024);
|
|
}
|
|
fclose($gpg_pipes[2]);
|
|
|
|
/////////////////////////////////////////
|
|
// this collects the exit status of GnuPG
|
|
$gpg_exit_status = proc_close($gpg_process);
|
|
|
|
////////////////////////////////////////////
|
|
// unset variables that are no longer needed
|
|
// and can only cause trouble
|
|
unset(
|
|
$gpg_descriptorspec,
|
|
$gpg_process,
|
|
$gpg_pipes
|
|
);
|
|
|
|
////////////////////////////////////
|
|
// this returns an array containing:
|
|
// [0] encrypted output (STDOUT)
|
|
// [1] warnings and notices (STDERR)
|
|
// [2] exit status
|
|
return [$gpg_proc_output, $gpg_error_message, $gpg_exit_status];
|
|
} else {
|
|
////////////////////////////////////////////
|
|
// unset variables that are no longer needed
|
|
// and can only cause trouble
|
|
unset(
|
|
$gpg_descriptorspec,
|
|
$gpg_process,
|
|
$gpg_pipes
|
|
);
|
|
|
|
//////////////////////////////
|
|
// set output as otherwise nothing
|
|
$gpg_proc_output = '';
|
|
$gpg_error_message = 'Fatal process call error: Process call failed!';
|
|
$gpg_exit_status = 99;
|
|
return [$gpg_proc_output, $gpg_error_message, $gpg_exit_status];
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Encryption function; encrypts & signs the message
|
|
//
|
|
// usage:
|
|
// array gpg_encrypt(secret-message, recipients);
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Encryption function; encrypts & signs the message
|
|
*
|
|
* @param string $secret-message
|
|
* @param array/string $recipients
|
|
* @access public
|
|
* @return array
|
|
* 0 => encrypted message output (STDOUT)
|
|
* 1 => warnings and notices (STDERR)
|
|
* 2 => exit status
|
|
*/
|
|
public function gpg_encrypt()
|
|
{
|
|
|
|
global $prefs;
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// sanity check - make sure there are at least 2 arguments
|
|
// any extra arguments are considered to be additional key IDs
|
|
if (func_num_args() < 2) {
|
|
trigger_error("gpg_encrypt() requires at least 2 arguments", E_USER_ERROR);
|
|
// if an error message directs you to the line above please
|
|
// double check that you are providing at least 2 arguments
|
|
die();
|
|
}
|
|
|
|
////////////////////////////////
|
|
// assign arguments to variables
|
|
$gpg_args = func_get_args();
|
|
$gpg_secret_message = array_shift($gpg_args); // 1st argument - secret message; let the gpg process to deal with empty/faulty content
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// make sure that each recipient has the message encrypted to their key
|
|
// the 2nd argument, and any subsequent arguments, are key IDs
|
|
$gpg_recipient_list = '';
|
|
foreach ($gpg_args as $gpg_recipient) {
|
|
if (is_array($gpg_recipient)) {
|
|
foreach ($gpg_recipient as &$item) {
|
|
$gpg_recipient_list .= ' -r ' . $item;
|
|
}
|
|
} else {
|
|
$gpg_recipient_list .= " -r ${gpg_recipient}";
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////
|
|
// find which version of GnuPG we're using
|
|
//////////////////////////////////////////
|
|
if ($this->_gpg_trust == '') {
|
|
$this->_gpg_check_version();
|
|
}
|
|
|
|
///////////////////////////////
|
|
// open the GnuPG process and get the reply
|
|
$commandline = '';
|
|
if ($prefs['openpgp_gpg_signer_passphrase_store'] == 'file') {
|
|
// get signer-key passphrase from a file
|
|
$commandline .= $this->_gpg_path
|
|
. ' --no-random-seed-file'
|
|
. ' --homedir ' . $this->_gpg_home
|
|
. ' ' . $this->_gpg_trust
|
|
. ' --batch'
|
|
. ' --local-user ' . $this->_gpg_sgn_id
|
|
. ' --passphrase-file ' . $this->_gpg_sgn_passfile_path
|
|
. ' -sea ' . $gpg_recipient_list
|
|
. ' ';
|
|
} else {
|
|
// get signer-key passphrase from preferences
|
|
$commandline .= $this->_gpg_path
|
|
. ' --no-random-seed-file'
|
|
. ' --homedir ' . $this->_gpg_home
|
|
. ' ' . $this->_gpg_trust
|
|
. ' --batch'
|
|
. ' --local-user ' . $this->_gpg_sgn_id
|
|
. ' --passphrase ' . $this->_gpg_sgn_passphrase
|
|
. ' -sea ' . $gpg_recipient_list
|
|
. ' ';
|
|
}
|
|
$ret = $this->_gpg_exec_proc($commandline, $gpg_secret_message);
|
|
|
|
unset(
|
|
$gpg_args,
|
|
$gpg_secret_message,
|
|
$gpg_recipient_list,
|
|
$commandline
|
|
);
|
|
|
|
////////////////////////////////////
|
|
// this returns an array containing:
|
|
// [0] encrypted output (STDOUT)
|
|
// [1] warnings and notices (STDERR)
|
|
// [2] exit status
|
|
return $ret;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
//
|
|
// Get the public-key fingerprint for a key associated with the ID
|
|
//
|
|
/////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Get public-key fingerprint
|
|
*
|
|
* @param string $gpg_key_id
|
|
* @access public
|
|
* @return array
|
|
* 0 => public key gingerprint output (STDOUT)
|
|
* 1 => warnings and notices (STDERR)
|
|
* 2 => exit status
|
|
*/
|
|
public function gpg_getFingerprint($gpg_key_id = null)
|
|
{
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// sanity check - make sure there is 1 argument
|
|
if ($gpg_key_id == null) {
|
|
trigger_error("gpg_getFingerprint() requires 1 argument", E_USER_ERROR);
|
|
// if an error message directs you to the line above please
|
|
// double check that you are providing 1 argument
|
|
die();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// the argument is key ID; if array, accept only the first
|
|
$gpg_key_id_to_return = '';
|
|
if (is_array($gpg_key_id)) {
|
|
foreach ($gpg_key_id as &$item) {
|
|
$gpg_key_id_to_return .= $item;
|
|
break;
|
|
}
|
|
} else {
|
|
$gpg_key_id_to_return .= $gpg_key_id;
|
|
}
|
|
|
|
//////////////////////////////////////////
|
|
// find which version of GnuPG we're using
|
|
//////////////////////////////////////////
|
|
if ($this->_gpg_trust == '') {
|
|
$this->_gpg_check_version();
|
|
}
|
|
|
|
///////////////////////////////
|
|
// open the GnuPG process and get the reply
|
|
$commandline = $this->_gpg_path
|
|
. ' --homedir ' . $this->_gpg_home
|
|
. ' ' . $this->_gpg_trust
|
|
. ' --fingerprint'
|
|
. ' --list-sigs ' . $gpg_key_id_to_return
|
|
. ' ';
|
|
$ret = $this->_gpg_exec_proc($commandline);
|
|
|
|
unset(
|
|
$gpg_key_id_to_return,
|
|
$commandline
|
|
);
|
|
|
|
////////////////////////////////////
|
|
// this returns an array containing:
|
|
// [0] fingerprint output (STDOUT)
|
|
// [1] warnings and notices (STDERR)
|
|
// [2] exit status
|
|
return $ret;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get the public-key ascii-armor-block for a key associated with the ID
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Get public-key ascii armor block
|
|
*
|
|
* @param string $gpg_key_id
|
|
* @access public
|
|
* @return array
|
|
* 0 => public key armor output (STDOUT)
|
|
* 1 => warnings and notices (STDERR)
|
|
* 2 => exit status
|
|
*/
|
|
public function gpg_getPublicKey($gpg_key_id = null)
|
|
{
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// sanity check - make sure there is 1 argument
|
|
if ($gpg_key_id == null) {
|
|
trigger_error("gpg_getPublicKey() requires 1 argument", E_USER_ERROR);
|
|
// if an error message directs you to the line above please
|
|
// double check that you are providing 1 argument
|
|
die();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// the argument is key ID; if array, accept only the first
|
|
$gpg_key_id_to_return = '';
|
|
if (is_array($gpg_key_id)) {
|
|
foreach ($gpg_key_id as &$item) {
|
|
$gpg_key_id_to_return .= $item;
|
|
break;
|
|
}
|
|
} else {
|
|
$gpg_key_id_to_return .= $gpg_key_id;
|
|
}
|
|
|
|
//////////////////////////////////////////
|
|
// find which version of GnuPG we're using
|
|
//////////////////////////////////////////
|
|
if ($this->_gpg_trust == '') {
|
|
$this->_gpg_check_version();
|
|
}
|
|
|
|
///////////////////////////////
|
|
// open the GnuPG process and get the reply
|
|
$commandline = $this->_gpg_path
|
|
. ' --homedir ' . $this->_gpg_home
|
|
. ' ' . $this->_gpg_trust
|
|
. ' --export --armor ' . $gpg_key_id_to_return
|
|
. ' ';
|
|
$ret = $this->_gpg_exec_proc($commandline);
|
|
|
|
unset(
|
|
$gpg_key_id_to_return,
|
|
$commandline
|
|
);
|
|
|
|
////////////////////////////////////
|
|
// this returns an array containing:
|
|
// [0] public key armor output (STDOUT)
|
|
// [1] warnings and notices (STDERR)
|
|
// [2] exit status
|
|
return $ret;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// MESSAGE COMPOSING
|
|
//
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* ALPHAFIELDS 2012-01-08: added to retrieve admin email public key armor block
|
|
* Returns the contact users' email or empty
|
|
*
|
|
* @access public
|
|
* @return string (contact users' email or empty)
|
|
*/
|
|
public function get_admin_email_for_armor_block()
|
|
{
|
|
global $user, $prefs, $tikilib;
|
|
$empty = '';
|
|
return isset($prefs['sender_email']) ? $prefs['sender_email'] : $empty;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Function to get publickey ascii-armor-block and original headers into body
|
|
*
|
|
* @param string $req_priority
|
|
* @param string $req_to
|
|
* @param string $req_cc
|
|
* @access public
|
|
* @return array
|
|
* 0 => $prepend_email_body
|
|
* 1 => $user_armor
|
|
*/
|
|
public function getPublickeyArmorBlock($req_priority, $req_to, $req_cc)
|
|
{
|
|
|
|
global $user;
|
|
$userlib = TikiLib::lib('user');
|
|
|
|
// get user email for publickey armor block retrieval
|
|
$user_email = '';
|
|
if ($user != 'admin') {
|
|
$user_email = $userlib->get_user_email($user);
|
|
} else {
|
|
// NOTE: This function is in this lib-class, not in $userlib!
|
|
$user_email = $this->get_admin_email_for_armor_block();
|
|
}
|
|
$user_armor = '';
|
|
if ($user_email) {
|
|
//retrieve armor block for keyid
|
|
$gpg = $this->gpg_getPublicKey($user_email);
|
|
// $gpg is an array containing
|
|
// $gpg[0] armor output (STDOUT)
|
|
// $gpg[1] warnings and notices (STDERR)
|
|
// $gpg[2] exit status from gpg
|
|
|
|
// test gpg's exit status
|
|
if ("$gpg[2]" == '0') {
|
|
// if the gpg command returned zero
|
|
$user_armor = "\n\n--original sender public key below--\n\n" . $gpg[0];
|
|
} else {
|
|
// if the gpg command returned non-zero
|
|
$error_msg = 'OpenPGPLib: _getPublickeyArmorBlock() returned error code: ' . $gpg[2];
|
|
trigger_error($error_msg, E_USER_ERROR);
|
|
// if an error message directs you to the line above please
|
|
// double check that your gnupg-configuration, process-call commandline input, and other parameters are correct
|
|
}
|
|
// TODO: hardcoded addresses into preferences from db
|
|
$gpg = $this->gpg_getFingerprint($user_email);
|
|
// $gpg is an array containing
|
|
// $gpg[0] fingerprint (STDOUT)
|
|
// $gpg[1] warnings and notices (STDERR)
|
|
// $gpg[2] exit status from gpg
|
|
|
|
// test gpg's exit status
|
|
if ("$gpg[2]" == '0') {
|
|
// if the gpg command returned zero
|
|
$user_armor = $user_armor . "\n-----fingerprint data below-------\n\n" . $gpg[0]
|
|
. "\n----------------------------------\n\n";
|
|
} else {
|
|
// if the gpg command returned non-zero
|
|
$error_msg = 'OpenPGPLib: _getPublickeyArmorBlock() returned error code: ' . $gpg[2];
|
|
trigger_error($error_msg, E_USER_ERROR);
|
|
// if an error message directs you to the line above please
|
|
// double check that your gnupg-configuration, process-call commandline input, and other parameters are correct
|
|
}
|
|
}
|
|
// generate a message has ID to be used as a consistent message referenceID across all recipients
|
|
// and prepend it into message body
|
|
$tmpstr = chunk_split(md5(rand() . microtime()), 8, '-');
|
|
$tmpstr = substr($tmpstr, 0, strlen($tmpstr) - 1);
|
|
$prepend_email_body = "ID: "
|
|
. $tmpstr
|
|
. " (use this for message reference)\n"
|
|
. "Prio: " . $req_priority . "\n"
|
|
. "From: " . $user . " (" . $user_email . ")\n"
|
|
. "To: " . $req_to . "\n"
|
|
. "Cc: " . $req_cc . "\n\n";
|
|
|
|
return [$prepend_email_body,$user_armor];
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// WEBMAIL/htmlMimeMail GNUPG-ENCRYPTED PGP/MIME MAIL FUNCTIONS
|
|
//
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Function to encode a header if necessary
|
|
* according to RFC2047
|
|
*/
|
|
public function _encodeHeader($input, $charset = 'ISO-8859-1')
|
|
{
|
|
preg_match_all('/(\w*[\x80-\xFF]+\w*)/', $input, $matches);
|
|
|
|
foreach ($matches[1] as $value) {
|
|
$replacement = preg_replace_callback('/([\\x80-\\xFF])/', function ($match) {
|
|
return "=" . strtoupper(dechex(ord($match[1])));
|
|
}, $value);
|
|
|
|
$input = str_replace($value, '=?' . $charset . '?Q?' . $replacement . '?=', $input);
|
|
}
|
|
|
|
return $input;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Send a message to a user with gpg-armor block etc included
|
|
* A changed encryption-related version was copied/changed from lib/messu/messulib.pgp
|
|
* into lib/openpgp/openpgplib.php for prepending/appending content into
|
|
* message body
|
|
|
|
* @param string $user
|
|
* @param string $from
|
|
* @param string $to
|
|
* @param string $cc
|
|
* @param string $subject
|
|
* @param string $body
|
|
* @param string $prepend_email_body
|
|
* @param string $user_pubkeyarmor
|
|
* @param string $priority
|
|
* @param string $replyto_hash
|
|
* @param string $replyto_email
|
|
* @param string $bcc_sender
|
|
* @access public
|
|
* @return boolean true/false
|
|
*/
|
|
public function post_message_with_pgparmor_attachment(
|
|
$user,
|
|
$from,
|
|
$to,
|
|
$cc,
|
|
$subject,
|
|
$body,
|
|
$prepend_email_body,
|
|
// NOTE this
|
|
$user_pubkeyarmor,
|
|
// NOTE this
|
|
$priority,
|
|
$replyto_hash = '',
|
|
$replyto_email = '',
|
|
$bcc_sender = ''
|
|
) {
|
|
|
|
global $prefs;
|
|
$userlib = TikiLib::lib('user');
|
|
$tikilib = TikiLib::lib('tiki');
|
|
$smarty = TikiLib::lib('smarty');
|
|
|
|
$subject = strip_tags($subject);
|
|
$body = strip_tags($body, '<a><b><img><i>');
|
|
// Prevent duplicates
|
|
$hash = md5($subject . $body);
|
|
|
|
if ($tikilib->getOne("select count(*) from `messu_messages` where `user`=? and `user_from`=? and `hash`=?", [$user,$from,$hash])) {
|
|
return false;
|
|
}
|
|
|
|
$query = "insert into `messu_messages`(`user`,`user_from`,`user_to`,`user_cc`,`subject`,`body`,`date`,`isRead`,`isReplied`,`isFlagged`,`priority`,`hash`,`replyto_hash`) values(?,?,?,?,?,?,?,?,?,?,?,?,?)";
|
|
$tikilib->query($query, [$user,$from,$to,$cc,$subject,$body,(int) $tikilib->now,'n','n','n',(int) $priority,$hash,$replyto_hash]);
|
|
|
|
// Now check if the user should be notified by email
|
|
$foo = parse_url($_SERVER["REQUEST_URI"]);
|
|
$machine = $tikilib->httpPrefix(true) . $foo["path"];
|
|
$machine = str_replace('messu-compose', 'messu-mailbox', $machine);
|
|
if ($tikilib->get_user_preference($user, 'minPrio', 6) <= $priority) {
|
|
if (! isset($_SERVER["SERVER_NAME"])) {
|
|
$_SERVER["SERVER_NAME"] = $_SERVER["HTTP_HOST"];
|
|
}
|
|
$email = $userlib->get_user_email($user);
|
|
if ($email) {
|
|
include_once('lib/webmail/tikimaillib.php');
|
|
$smarty->assign('mail_site', $_SERVER["SERVER_NAME"]);
|
|
$smarty->assign('mail_machine', $machine);
|
|
$smarty->assign('mail_date', $tikilib->now);
|
|
$smarty->assign('mail_user', stripslashes($user));
|
|
$smarty->assign('mail_from', stripslashes($from));
|
|
$smarty->assign('mail_subject', stripslashes($subject));
|
|
////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// ALPHAFIELDS 2012-11-03: ADDED PGP/MIME ENCRYPTION PREPARATION //
|
|
// USING lib/openpgp/opepgplib.php //
|
|
// //
|
|
// prepend original headers into email //
|
|
$aux_body = $prepend_email_body . $body;
|
|
$body = $aux_body;
|
|
// //
|
|
////////////////////////////////////////////////////////////////////////
|
|
$smarty->assign('mail_body', stripslashes($body));
|
|
$mail = new TikiMail($user);
|
|
$lg = $tikilib->get_user_preference($user, 'language', $prefs['site_language']);
|
|
if (empty($subject)) {
|
|
$s = $smarty->fetchLang($lg, 'mail/messu_message_notification_subject.tpl');
|
|
$mail->setSubject(sprintf($s, $_SERVER["SERVER_NAME"]));
|
|
} else {
|
|
$mail->setSubject($subject);
|
|
}
|
|
$mail_data = $smarty->fetchLang($lg, 'mail/messu_message_notification.tpl');
|
|
////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// ALPHAFIELDS 2012-11-03: ADDED PGP/MIME ENCRYPTION PREPARATION //
|
|
// USING lib/openpgp/opepgplib.php //
|
|
// //
|
|
// append pgparmor block and fingerprint into email //
|
|
$mail_data .= $user_pubkeyarmor;
|
|
// //
|
|
////////////////////////////////////////////////////////////////////////
|
|
$mail->setText($mail_data);
|
|
|
|
if ($userlib->user_exists($from)) {
|
|
$from_email = $userlib->get_user_email($from);
|
|
if ($bcc_sender === 'y' && ! empty($from_email)) {
|
|
$mail->setBcc($from_email);
|
|
}
|
|
if ($replyto_email !== 'y' && $userlib->get_user_preference($from, 'email is public', 'n') == 'n') {
|
|
$from_email = ''; // empty $from_email if not to be used - saves getting it twice
|
|
}
|
|
if (! empty($from_email)) {
|
|
$mail->setReplyTo($from_email);
|
|
}
|
|
}
|
|
if (! empty($from_email)) {
|
|
$mail->setFrom($from_email);
|
|
}
|
|
|
|
if (! $mail->send([$email], 'mail')) {
|
|
return false; //TODO echo $mail->errors;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Function to insert original subject into text part body
|
|
*
|
|
* @param array $mail_headers / headers array
|
|
* @param string $text / The text body of the message
|
|
* @access public
|
|
* @return string $text / The text body of the message prepended with original subject
|
|
*/
|
|
public function prependSubjectToText($mail_headers, $text)
|
|
{
|
|
|
|
// AS THE Subject-header is hidden and contains a hash only in a pgp/mime encrypted message subject-header
|
|
// extract the original subject and prepend it into the text part
|
|
// TODO: for some reason, newsletter notifications etc
|
|
// do not set Subject yet at this point so no adjustment is generated for them;
|
|
// what is the problem and solution?
|
|
$subject = '';
|
|
if (! empty($mail_headers['Subject'])) {
|
|
$subject = $mail_headers['Subject'];
|
|
}
|
|
$ret = "******** PGP/MIME-ENCRYPTED MESSAGE ********\n"
|
|
. "Subject: " . $subject
|
|
. "\n\n"
|
|
. $text;
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* Function to insert original subject into html part body
|
|
*
|
|
* @param array $mail_headers / headers array
|
|
* @param string $html / The html body of the message
|
|
* @param string $text / The text body of the message
|
|
* @access public
|
|
* @return array
|
|
* 0 => $html / The html body of the message prepended with original subject
|
|
* 1 => $html_text / The html_text body of the message prepended with original subject
|
|
*/
|
|
public function prependSubjectToHtml($mail_headers, $html, $text)
|
|
{
|
|
|
|
// AS THE Subject-header is hidden and contains a hash only in a pgp/mime encrypted message subject-header
|
|
// extract the original subject and prepend it into the html part
|
|
// TODO: for some reason, newsletter notifications etc
|
|
// do not set Subject yet at this point so no adjustment is generated for them;
|
|
// what is the problem and solution?
|
|
$subject = '';
|
|
if (! empty($mail_headers['Subject'])) {
|
|
$subject = $mail_headers['Subject'];
|
|
}
|
|
$ret_html = "******** PGP/MIME-ENCRYPTED MESSAGE ********<br>"
|
|
. "Subject: " . $subject
|
|
. "<br>"
|
|
. $html;
|
|
|
|
$ret_html_text = "******** PGP/MIME-ENCRYPTED MESSAGE ********<br>"
|
|
. "Subject: " . $subject
|
|
. "<br>"
|
|
. $text;
|
|
return [$ret_html,$ret_html_text];
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Prepate encryption of a mail using gnupg
|
|
*
|
|
* @param array $original_mail_headers / headers array
|
|
* @param string $original_mail_body / The main body of the message after building
|
|
* @param string $mail_build_params_head_charset
|
|
* @param array/string $recipients (needs to be not-imploded string or array)
|
|
* @access public
|
|
* @return array
|
|
* 0 => string $gnupg_header
|
|
* 1 => string $gnupg_subject
|
|
* 2 => string $pgpmime_encrypted_message_body
|
|
*/
|
|
public function prepareEncryptWithMailSender($original_mail_headers, $original_mail_body, $mail_build_params_head_charset, $mail_recipients)
|
|
{
|
|
|
|
// Define gnupg/mime header variables; see constants above in this class
|
|
$gnupg_mpe = OpenPGPLib::MULTIPART_PGP_ENCRYPTED;
|
|
$gnupg_tpp = OpenPGPLib::TYPE_PGP_PROTOCOL;
|
|
$gnupg_pmn = OpenPGPLib::PGP_MIME_NOTE;
|
|
$gnupg_tpcv = OpenPGPLib::TYPE_PGP_CONTENT_VERSION;
|
|
$gnupg_dpcv = OpenPGPLib::DESCRIPTION_PGP_CONTENT_VERSION;
|
|
$gnupg_pmvi = OpenPGPLib::PGP_MIME_VERSION_IDENTIFICATION;
|
|
$gnupg_tpce = OpenPGPLib::TYPE_PGP_CONTENT_ENCRYPTED;
|
|
$gnupg_dpce = OpenPGPLib::DESCRIPTION_PGP_CONTENT_ENCRYPTED;
|
|
$gnupg_dpci = OpenPGPLib::DISPOSITION_PGP_CONTENT_INLINE;
|
|
|
|
|
|
// Define gnupg boundary
|
|
$gnupg_boundary = '------gnupg-' . md5(rand() . microtime());
|
|
|
|
// Get flat representation of headers
|
|
foreach ($original_mail_headers as $name => $value) {
|
|
// first process Content-Type and discard original and set pgp/mime-required after loop
|
|
if ($name == 'Content-Type') {
|
|
$tmpvalue = $this->_encodeHeader($value, $mail_build_params_head_charset);
|
|
// check if original message is just text/plain, so we can discard the entire
|
|
// orig_header from being prepended into encrypted part
|
|
$pos = strpos($tmpvalue, 'text/plain');
|
|
if ($pos !== false) {
|
|
// so text/plain ...set discard-flag...
|
|
$discard_orig_header = true;
|
|
} else {
|
|
// reach here if not text/plain
|
|
$orig_headers[] = $name . ': ' . $tmpvalue;
|
|
}
|
|
} else {
|
|
// Get flat representation of original headers
|
|
$orig_headers[] = $name . ': ' . $this->_encodeHeader($value, $mail_build_params_head_charset);
|
|
}
|
|
}
|
|
|
|
// save original header to be added into encrypted part
|
|
if ($discard_orig_header) {
|
|
// original body is text/plain, so no headers there
|
|
$orig_header = '';
|
|
} else {
|
|
$orig_header = implode(CRLF, $orig_headers);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// NOTE: pgpmime_header; this must be returned as header (string here) for the mail() function
|
|
$pgpmime_header = "Content-Type: {$gnupg_mpe};{$this->EOL} protocol=\"{$gnupg_tpp}\";{$this->EOL} boundary=\"{$gnupg_boundary}\"{$this->EOL}{$this->EOL}";
|
|
|
|
// Instead of original Subject, use this to hide even the subject:
|
|
// - generate a message hash ID to be used instead of orig subject;
|
|
// - show the original Subject in the encrypted message body;
|
|
$tmpstr = chunk_split(
|
|
md5(
|
|
$pgpmime_header
|
|
. rand()
|
|
. microtime()
|
|
),
|
|
8,
|
|
'-'
|
|
);
|
|
$tmpstr = substr($tmpstr, 0, strlen($tmpstr) - 1);
|
|
$replace_subject_with_msgID = '[PGP/MIME] ' . $tmpstr;
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// NOTE: pgpmime subject; this must be returned as subject for the mail() function
|
|
$pgpmime_subject = $replace_subject_with_msgID;
|
|
|
|
// gnupg pgp/mime note
|
|
$gnupg = "{$gnupg_pmn}{$this->EOL}";
|
|
|
|
// gnupg part 1 header
|
|
$gnupg .= "--{$gnupg_boundary}{$this->EOL}Content-Type: {$gnupg_tpcv}{$this->EOL}Content-Description: {$gnupg_dpcv}{$this->EOL}{$this->EOL}{$gnupg_pmvi}{$this->EOL}{$this->EOL}";
|
|
|
|
// gnupg part 2 header
|
|
$gnupg .= "--{$gnupg_boundary}{$this->EOL}Content-Type: {$gnupg_tpce}{$this->EOL}Content-Description: {$gnupg_dpce}{$this->EOL}Content-Disposition: {$gnupg_dpci}{$this->EOL}{$this->EOL}";
|
|
|
|
// gnupg encrypted/signed message body
|
|
// original header to be added into encrypted part (use here the prepared/imploded orig_header from above)
|
|
// NOTE: signer and signer passphrase are set ready in this class instantiation,
|
|
// and used in the following function directly from there
|
|
$gnupg .= $this->_encryptSignGnuPG($orig_header . "{$this->EOL}{$this->EOL}" . $original_mail_body, $mail_recipients);
|
|
// gnupg end boundary
|
|
$gnupg .= "{$this->EOL}--{$gnupg_boundary}--{$this->EOL}";
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// NOTE: gnupg into mail body; this must be returned as encrypted body for the mail() function
|
|
$pgpmime_encrypted_message_body = $gnupg;
|
|
|
|
return [
|
|
0 => $pgpmime_header,
|
|
1 => $pgpmime_subject,
|
|
2 => $pgpmime_encrypted_message_body];
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Prepare encryption of a mail using gnupg
|
|
*
|
|
* @param array $original_mail_headers / headers array
|
|
* @param string $original_mail_body / The main body of the message after building
|
|
* @param string $mail_build_params_head_charset
|
|
* @param array/string $recipients (needs to be not-imploded string or array)
|
|
* @access public
|
|
* @return array
|
|
* 0 => array $gnupg_header_array
|
|
* 1 => string $pgpmime_encrypted_message_body
|
|
*/
|
|
public function prepareEncryptWithSmtpSender($original_mail_headers, $original_mail_body, $mail_build_params_head_charset, $mail_recipients)
|
|
{
|
|
|
|
// Define gnupg/mime header variables; see constants above in this class
|
|
$gnupg_mpe = OpenPGPLib::MULTIPART_PGP_ENCRYPTED;
|
|
$gnupg_tpp = OpenPGPLib::TYPE_PGP_PROTOCOL;
|
|
$gnupg_pmn = OpenPGPLib::PGP_MIME_NOTE;
|
|
$gnupg_tpcv = OpenPGPLib::TYPE_PGP_CONTENT_VERSION;
|
|
$gnupg_dpcv = OpenPGPLib::DESCRIPTION_PGP_CONTENT_VERSION;
|
|
$gnupg_pmvi = OpenPGPLib::PGP_MIME_VERSION_IDENTIFICATION;
|
|
$gnupg_tpce = OpenPGPLib::TYPE_PGP_CONTENT_ENCRYPTED;
|
|
$gnupg_dpce = OpenPGPLib::DESCRIPTION_PGP_CONTENT_ENCRYPTED;
|
|
$gnupg_dpci = OpenPGPLib::DISPOSITION_PGP_CONTENT_INLINE;
|
|
|
|
// Define gnupg boundary
|
|
$gnupg_boundary = '------gnupg-' . md5(rand() . microtime());
|
|
|
|
// Define a boundary for orig body, if needed
|
|
$add_boundary_for_orig = '------orig--' . md5(rand() . microtime());
|
|
|
|
// set discard-flag originally false; see below foreach loop and later the origheader implode
|
|
$discard_orig_header = true;
|
|
|
|
// Get flat representation of original headers to be put with the encrypted body
|
|
foreach ($original_mail_headers as $name => $value) {
|
|
// first process Content-Type and discard original and set pgp/mime-required after loop
|
|
if ($name == 'Content-Type') {
|
|
$tmpvalue = $this->_encodeHeader($value, $mail_build_params_head_charset);
|
|
// check if original message is just text/plain, so we can discard the entire
|
|
// orig_header from being prepended into encrypted part
|
|
$pos = strpos($tmpvalue, 'text/plain');
|
|
if ($pos !== false) {
|
|
// so text/plain ...set discard-flag...
|
|
$discard_orig_header = true;
|
|
} else {
|
|
// reach here if not text/plain
|
|
$orig_headers[] = $name . ': ' . $tmpvalue;
|
|
}
|
|
} else {
|
|
// Get flat representation of original headers
|
|
$orig_headers[] = $name . ': ' . $this->_encodeHeader($value, $mail_build_params_head_charset);
|
|
}
|
|
}
|
|
|
|
// save original header to be added into encrypted part
|
|
if ($discard_orig_header) {
|
|
// original body is text/plain, so no headers there
|
|
$orig_header = '';
|
|
} else {
|
|
$orig_header = implode(CRLF, $orig_headers);
|
|
}
|
|
|
|
// gnupg pgp/mime headers; original headers from call-parameters to be added into encrypted part (see above/below)
|
|
$pgpmime_header = "Content-Type: {$gnupg_mpe};{$this->EOL} protocol=\"{$gnupg_tpp}\";{$this->EOL} boundary=\"{$gnupg_boundary}\"{$this->EOL}{$this->EOL}";
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Instead of original Subject, use this to hide even the subject:
|
|
// - generate a message hash ID to be used instead of orig subject;
|
|
// - show the original Subject in the encrypted message body;
|
|
$tmpstr = chunk_split(
|
|
md5(
|
|
$pgpmime_header
|
|
. rand()
|
|
. microtime()
|
|
),
|
|
8,
|
|
'-'
|
|
);
|
|
$tmpstr = substr($tmpstr, 0, strlen($tmpstr) - 1);
|
|
$replace_subject_with_msgID = '[PGP/MIME] ' . $tmpstr;
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// NOTE: pgpmime_header_array; this must be returned as header array (NOTE: array here) for the mail() function
|
|
// prepend the opaque Subject before other PGP/MIME headers
|
|
$pgpmime_header_array[] = "Subject: " . $replace_subject_with_msgID;
|
|
// instead of the original, set this pgp/mime-required header into $gnupg_header_array -return array
|
|
$pgpmime_header_array[] = $pgpmime_header;
|
|
|
|
// Create gnupg pgp/mime parts etc below and set/repalced to message body by caller
|
|
// NOTE: here we do not need to create directly the $gpgpmime_header, because the $gnupg_header_array is returned back
|
|
// to caller in array format (caller is htmlMimeMail)
|
|
|
|
// gnupg pgp/mime note
|
|
$gnupg = "{$gnupg_pmn}{$this->EOL}";
|
|
|
|
// gnupg part 1 header
|
|
$gnupg .= "--{$gnupg_boundary}{$this->EOL}Content-Type: {$gnupg_tpcv}{$this->EOL}Content-Description: {$gnupg_dpcv}{$this->EOL}{$this->EOL}{$gnupg_pmvi}{$this->EOL}{$this->EOL}";
|
|
|
|
// gnupg part 2 header
|
|
$gnupg .= "--{$gnupg_boundary}{$this->EOL}Content-Type: {$gnupg_tpce}{$this->EOL}Content-Description: {$gnupg_dpce}{$this->EOL}Content-Disposition: {$gnupg_dpci}{$this->EOL}{$this->EOL}";
|
|
|
|
// gnupg encrypted/signed message body
|
|
// original header to be added into encrypted part (use here the prepared/imploded orig_header from above)
|
|
// NOTE: signer and signer passphrase are set ready in this class instantiation,
|
|
// and used in the following function directly from there
|
|
$gnupg .= $this->_encryptSignGnuPG($orig_header . "{$this->EOL}{$this->EOL}" . $original_mail_body, $mail_recipients);
|
|
// gnupg end boundary
|
|
$gnupg .= "{$this->EOL}--{$gnupg_boundary}--{$this->EOL}";
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// NOTE: gnupg into mail body; this must be returned as encrypted body for the mail() function
|
|
$pgpmime_encrypted_message_body = $gnupg;
|
|
|
|
return [
|
|
0 => $pgpmime_header_array,
|
|
1 => $pgpmime_encrypted_message_body];
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// ZEND FRAMEWORK GNUPG-ENCRYPTED PGP/MIME MAIL FUNCTIONS
|
|
//
|
|
//////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////
|
|
/**
|
|
* Prepend header name to header value; copied here from Laminas/Mail/Transport/Abstract
|
|
* See usage in the array_walk in function below
|
|
* @param string $item
|
|
* @param string $key
|
|
* @param string $prefix
|
|
* @static
|
|
* @access protected
|
|
* @return void
|
|
*/
|
|
protected static function _formatHeader(&$item, $key, $prefix)
|
|
{
|
|
$item = $prefix . ': ' . $item;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
/**
|
|
* Prepate encryption of a mail using gnupg
|
|
*
|
|
* @param string $original_mail_headers
|
|
* @param string $original_mail_body
|
|
* @param array|string $recipients
|
|
* @access public
|
|
* @return array
|
|
* 0 => string $pgpmime_header
|
|
* 1 => string $pgpmime_encrypted_message_body
|
|
*/
|
|
public function prepareEncryptWithZendMail($original_mail_header, $original_mail_body, $recipients)
|
|
{
|
|
|
|
// Define gnupg/mime header variables; see constants above in this class
|
|
$gnupg_mpe = OpenPGPLib::MULTIPART_PGP_ENCRYPTED;
|
|
$gnupg_tpp = OpenPGPLib::TYPE_PGP_PROTOCOL;
|
|
$gnupg_pmn = OpenPGPLib::PGP_MIME_NOTE;
|
|
$gnupg_tpcv = OpenPGPLib::TYPE_PGP_CONTENT_VERSION;
|
|
$gnupg_dpcv = OpenPGPLib::DESCRIPTION_PGP_CONTENT_VERSION;
|
|
$gnupg_pmvi = OpenPGPLib::PGP_MIME_VERSION_IDENTIFICATION;
|
|
$gnupg_tpce = OpenPGPLib::TYPE_PGP_CONTENT_ENCRYPTED;
|
|
$gnupg_dpce = OpenPGPLib::DESCRIPTION_PGP_CONTENT_ENCRYPTED;
|
|
$gnupg_dpci = OpenPGPLib::DISPOSITION_PGP_CONTENT_INLINE;
|
|
|
|
// Define gnupg boundary
|
|
$gnupg_boundary = '------gnupg-' . md5(rand() . microtime());
|
|
|
|
//*********************
|
|
// NOTE: in smtp, $mail_recipients includes the Cc and Bcc recipients.
|
|
// so the message gets properly encrypted for them also;
|
|
// the problem is still with the Sendmail for such cases / see above...?
|
|
// What is the case in Zend Abstract, Sendmail and Smtp...?
|
|
//*********************
|
|
|
|
// Create gnupg pgp/mime parts etc below and set to this->output
|
|
|
|
// gnupg pgp/mime headers; original headers from call-parameters to be added into encrypted part (see below)
|
|
$pgpmime_header = "Content-Type: {$gnupg_mpe};{$this->EOL} protocol=\"{$gnupg_tpp}\";{$this->EOL} boundary=\"{$gnupg_boundary}\"{$this->EOL}{$this->EOL}";
|
|
|
|
// Instead of original Subject, use this to hide even the subject:
|
|
// - generate a message hash ID to be used instead of orig subject;
|
|
// - show the original Subject in the encrypted message body;
|
|
$tmpstr = chunk_split(
|
|
md5(
|
|
$pgpmime_header
|
|
. rand()
|
|
. microtime()
|
|
),
|
|
8,
|
|
'-'
|
|
);
|
|
$tmpstr = substr($tmpstr, 0, strlen($tmpstr) - 1);
|
|
$replace_subject_with_msgID = '[PGP/MIME] ' . $tmpstr;
|
|
// prepend the opaque Subject before other PGP/MIME headers
|
|
$pgpmime_header = "Subject: " . $replace_subject_with_msgID . $this->EOL . $pgpmime_header;
|
|
|
|
// gnupg pgp/mime note
|
|
$gnupg = "{$gnupg_pmn}{$this->EOL}";
|
|
|
|
// gnupg part 1 header
|
|
$gnupg .= "--{$gnupg_boundary}{$this->EOL}Content-Type: {$gnupg_tpcv}{$this->EOL}Content-Description: {$gnupg_dpcv}{$this->EOL}{$this->EOL}{$gnupg_pmvi}{$this->EOL}{$this->EOL}";
|
|
|
|
// gnupg part 2 header
|
|
$gnupg .= "--{$gnupg_boundary}{$this->EOL}Content-Type: {$gnupg_tpce}{$this->EOL}Content-Description: {$gnupg_dpce}{$this->EOL}Content-Disposition: {$gnupg_dpci}{$this->EOL}{$this->EOL}";
|
|
|
|
// gnupg encrypted/signed message body
|
|
// original header to be added into encrypted part
|
|
// NOTE: signer and signer passphrase are set ready in this class instantiation,
|
|
// and used in the following function directly from there
|
|
$gnupg .= $this->_encryptSignGnuPG($original_mail_header . "{$this->EOL}{$this->EOL}" . $original_mail_body, $recipients);
|
|
// gnupg end boundary
|
|
$gnupg .= "{$this->EOL}--{$gnupg_boundary}--{$this->EOL}";
|
|
|
|
// gnupg into mail body
|
|
$pgpmime_encrypted_message_body = $gnupg;
|
|
|
|
return [
|
|
0 => $pgpmime_header,
|
|
1 => $pgpmime_encrypted_message_body];
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
/**
|
|
* Encrypt and sign a mail using gnupg
|
|
*
|
|
* @param string $unencrypted_message
|
|
* @param array/string $recipients (needs to be not-imploded string or array)
|
|
* @access protected
|
|
* @return string ($encrypted_message)
|
|
*/
|
|
protected function _encryptSignGnuPG($unencrypted_message, $recipients)
|
|
{
|
|
|
|
$encrypted_message = '';
|
|
|
|
// encrypt $message to recipients
|
|
// sign wth signer
|
|
$gpg = $this->gpg_encrypt("${unencrypted_message}", $recipients);
|
|
|
|
// $gpg is an array containing
|
|
// $gpg[0] encrypted output (STDOUT)
|
|
// $gpg[1] warnings and notices (STDERR)
|
|
// $gpg[2] exit status from gpg
|
|
|
|
// test gpg's exit status
|
|
if ("$gpg[2]" == '0') {
|
|
// if the gpg command returned zero
|
|
$encrypted_message = $gpg[0];
|
|
} else {
|
|
// if the gpg command returned non-zero
|
|
$error_msg = 'OpenPGPLib: _encryptSignGnuPG() returned error code: ' . $gpg[2];
|
|
trigger_error($error_msg, E_USER_ERROR);
|
|
// if an error message directs you to the line above please
|
|
// double check that your gnupg-configuration, process-call commandline input, and other parameters are correct
|
|
}
|
|
|
|
return $encrypted_message;
|
|
}
|
|
}
|
|
$openpgplib = new OpenPGPLib();
|