Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F120823389
enigma_engine.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
17 KB
Referenced Files
None
Subscribers
None
enigma_engine.php
View Options
<?php
/*
+-------------------------------------------------------------------------+
| Engine of the Enigma Plugin |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License version 2 |
| as published by the Free Software Foundation. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License along |
| with this program; if not, write to the Free Software Foundation, Inc., |
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| |
+-------------------------------------------------------------------------+
| Author: Aleksander Machniak <alec@alec.pl> |
+-------------------------------------------------------------------------+
*/
/*
RFC2440: OpenPGP Message Format
RFC3156: MIME Security with OpenPGP
RFC3851: S/MIME
*/
class
enigma_engine
{
private
$rc
;
private
$enigma
;
private
$pgp_driver
;
private
$smime_driver
;
public
$decryptions
=
array
();
public
$signatures
=
array
();
public
$signed_parts
=
array
();
/**
* Plugin initialization.
*/
function
__construct
(
$enigma
)
{
$rcmail
=
rcmail
::
get_instance
();
$this
->
rc
=
$rcmail
;
$this
->
enigma
=
$enigma
;
}
/**
* PGP driver initialization.
*/
function
load_pgp_driver
()
{
if
(
$this
->
pgp_driver
)
return
;
$driver
=
'enigma_driver_'
.
$this
->
rc
->
config
->
get
(
'enigma_pgp_driver'
,
'gnupg'
);
$username
=
$this
->
rc
->
user
->
get_username
();
// Load driver
$this
->
pgp_driver
=
new
$driver
(
$username
);
if
(!
$this
->
pgp_driver
)
{
rcube
::
raise_error
(
array
(
'code'
=>
600
,
'type'
=>
'php'
,
'file'
=>
__FILE__
,
'line'
=>
__LINE__
,
'message'
=>
"Enigma plugin: Unable to load PGP driver: $driver"
),
true
,
true
);
}
// Initialise driver
$result
=
$this
->
pgp_driver
->
init
();
if
(
$result
instanceof
enigma_error
)
{
rcube
::
raise_error
(
array
(
'code'
=>
600
,
'type'
=>
'php'
,
'file'
=>
__FILE__
,
'line'
=>
__LINE__
,
'message'
=>
"Enigma plugin: "
.
$result
->
getMessage
()
),
true
,
true
);
}
}
/**
* S/MIME driver initialization.
*/
function
load_smime_driver
()
{
if
(
$this
->
smime_driver
)
return
;
$driver
=
'enigma_driver_'
.
$this
->
rc
->
config
->
get
(
'enigma_smime_driver'
,
'phpssl'
);
$username
=
$this
->
rc
->
user
->
get_username
();
// Load driver
$this
->
smime_driver
=
new
$driver
(
$username
);
if
(!
$this
->
smime_driver
)
{
rcube
::
raise_error
(
array
(
'code'
=>
600
,
'type'
=>
'php'
,
'file'
=>
__FILE__
,
'line'
=>
__LINE__
,
'message'
=>
"Enigma plugin: Unable to load S/MIME driver: $driver"
),
true
,
true
);
}
// Initialise driver
$result
=
$this
->
smime_driver
->
init
();
if
(
$result
instanceof
enigma_error
)
{
rcube
::
raise_error
(
array
(
'code'
=>
600
,
'type'
=>
'php'
,
'file'
=>
__FILE__
,
'line'
=>
__LINE__
,
'message'
=>
"Enigma plugin: "
.
$result
->
getMessage
()
),
true
,
true
);
}
}
/**
* Handler for plain/text message.
*
* @param array Reference to hook's parameters
*/
function
parse_plain
(&
$p
)
{
$part
=
$p
[
'structure'
];
// Get message body from IMAP server
$this
->
set_part_body
(
$part
,
$p
[
'object'
]->
uid
);
// @TODO: big message body can be a file resource
// PGP signed message
if
(
preg_match
(
'/^-----BEGIN PGP SIGNED MESSAGE-----/'
,
$part
->
body
))
{
$this
->
parse_plain_signed
(
$p
);
}
// PGP encrypted message
else
if
(
preg_match
(
'/^-----BEGIN PGP MESSAGE-----/'
,
$part
->
body
))
{
$this
->
parse_plain_encrypted
(
$p
);
}
}
/**
* Handler for multipart/signed message.
*
* @param array Reference to hook's parameters
*/
function
parse_signed
(&
$p
)
{
$struct
=
$p
[
'structure'
];
// S/MIME
if
(
$struct
->
parts
[
1
]
&&
$struct
->
parts
[
1
]->
mimetype
==
'application/pkcs7-signature'
)
{
$this
->
parse_smime_signed
(
$p
);
}
// PGP/MIME:
// The multipart/signed body MUST consist of exactly two parts.
// The first part contains the signed data in MIME canonical format,
// including a set of appropriate content headers describing the data.
// The second body MUST contain the PGP digital signature. It MUST be
// labeled with a content type of "application/pgp-signature".
else
if
(
$struct
->
parts
[
1
]
&&
$struct
->
parts
[
1
]->
mimetype
==
'application/pgp-signature'
)
{
$this
->
parse_pgp_signed
(
$p
);
}
}
/**
* Handler for multipart/encrypted message.
*
* @param array Reference to hook's parameters
*/
function
parse_encrypted
(&
$p
)
{
$struct
=
$p
[
'structure'
];
// S/MIME
if
(
$struct
->
mimetype
==
'application/pkcs7-mime'
)
{
$this
->
parse_smime_encrypted
(
$p
);
}
// PGP/MIME:
// The multipart/encrypted MUST consist of exactly two parts. The first
// MIME body part must have a content type of "application/pgp-encrypted".
// This body contains the control information.
// The second MIME body part MUST contain the actual encrypted data. It
// must be labeled with a content type of "application/octet-stream".
else
if
(
$struct
->
parts
[
0
]
&&
$struct
->
parts
[
0
]->
mimetype
==
'application/pgp-encrypted'
&&
$struct
->
parts
[
1
]
&&
$struct
->
parts
[
1
]->
mimetype
==
'application/octet-stream'
)
{
$this
->
parse_pgp_encrypted
(
$p
);
}
}
/**
* Handler for plain signed message.
* Excludes message and signature bodies and verifies signature.
*
* @param array Reference to hook's parameters
*/
private
function
parse_plain_signed
(&
$p
)
{
$this
->
load_pgp_driver
();
$part
=
$p
[
'structure'
];
// Verify signature
if
(
$this
->
rc
->
action
==
'show'
||
$this
->
rc
->
action
==
'preview'
)
{
$sig
=
$this
->
pgp_verify
(
$part
->
body
);
}
// @TODO: Handle big bodies using (temp) files
// In this way we can use fgets on string as on file handle
$fh
=
fopen
(
'php://memory'
,
'br+'
);
// @TODO: fopen/fwrite errors handling
if
(
$fh
)
{
fwrite
(
$fh
,
$part
->
body
);
rewind
(
$fh
);
}
$part
->
body
=
null
;
// Extract body (and signature?)
while
(!
feof
(
$fh
))
{
$line
=
fgets
(
$fh
,
1024
);
if
(
$part
->
body
===
null
)
$part
->
body
=
''
;
else
if
(
preg_match
(
'/^-----BEGIN PGP SIGNATURE-----/'
,
$line
))
break
;
else
$part
->
body
.=
$line
;
}
// Remove "Hash" Armor Headers
$part
->
body
=
preg_replace
(
'/^.*
\r
*
\n\r
*
\n
/'
,
''
,
$part
->
body
);
// de-Dash-Escape (RFC2440)
$part
->
body
=
preg_replace
(
'/(^|
\n
)- -/'
,
'
\\
1-'
,
$part
->
body
);
// Store signature data for display
if
(!
empty
(
$sig
))
{
$this
->
signed_parts
[
$part
->
mime_id
]
=
$part
->
mime_id
;
$this
->
signatures
[
$part
->
mime_id
]
=
$sig
;
}
fclose
(
$fh
);
}
/**
* Handler for PGP/MIME signed message.
* Verifies signature.
*
* @param array Reference to hook's parameters
*/
private
function
parse_pgp_signed
(&
$p
)
{
// Verify signature
if
(
$this
->
rc
->
action
==
'show'
||
$this
->
rc
->
action
==
'preview'
)
{
$this
->
load_pgp_driver
();
$struct
=
$p
[
'structure'
];
$msg_part
=
$struct
->
parts
[
0
];
$sig_part
=
$struct
->
parts
[
1
];
// Get bodies
$this
->
set_part_body
(
$msg_part
,
$p
[
'object'
]->
uid
);
$this
->
set_part_body
(
$sig_part
,
$p
[
'object'
]->
uid
);
// Verify
$sig
=
$this
->
pgp_verify
(
$msg_part
->
body
,
$sig_part
->
body
);
// Store signature data for display
$this
->
signatures
[
$struct
->
mime_id
]
=
$sig
;
// Message can be multipart (assign signature to each subpart)
if
(!
empty
(
$msg_part
->
parts
))
{
foreach
(
$msg_part
->
parts
as
$part
)
$this
->
signed_parts
[
$part
->
mime_id
]
=
$struct
->
mime_id
;
}
else
$this
->
signed_parts
[
$msg_part
->
mime_id
]
=
$struct
->
mime_id
;
// Remove signature file from attachments list
unset
(
$struct
->
parts
[
1
]);
}
}
/**
* Handler for S/MIME signed message.
* Verifies signature.
*
* @param array Reference to hook's parameters
*/
private
function
parse_smime_signed
(&
$p
)
{
// Verify signature
if
(
$this
->
rc
->
action
==
'show'
||
$this
->
rc
->
action
==
'preview'
)
{
$this
->
load_smime_driver
();
$struct
=
$p
[
'structure'
];
$msg_part
=
$struct
->
parts
[
0
];
// Verify
$sig
=
$this
->
smime_driver
->
verify
(
$struct
,
$p
[
'object'
]);
// Store signature data for display
$this
->
signatures
[
$struct
->
mime_id
]
=
$sig
;
// Message can be multipart (assign signature to each subpart)
if
(!
empty
(
$msg_part
->
parts
))
{
foreach
(
$msg_part
->
parts
as
$part
)
$this
->
signed_parts
[
$part
->
mime_id
]
=
$struct
->
mime_id
;
}
else
{
$this
->
signed_parts
[
$msg_part
->
mime_id
]
=
$struct
->
mime_id
;
}
// Remove signature file from attachments list
unset
(
$struct
->
parts
[
1
]);
}
}
/**
* Handler for plain encrypted message.
*
* @param array Reference to hook's parameters
*/
private
function
parse_plain_encrypted
(&
$p
)
{
$this
->
load_pgp_driver
();
$part
=
$p
[
'structure'
];
// Get body
$this
->
set_part_body
(
$part
,
$p
[
'object'
]->
uid
);
// Decrypt
$result
=
$this
->
pgp_decrypt
(
$part
->
body
);
// Store decryption status
$this
->
decryptions
[
$part
->
mime_id
]
=
$result
;
// Parse decrypted message
if
(
$result
===
true
)
{
// @TODO
}
}
/**
* Handler for PGP/MIME encrypted message.
*
* @param array Reference to hook's parameters
*/
private
function
parse_pgp_encrypted
(&
$p
)
{
$this
->
load_pgp_driver
();
$struct
=
$p
[
'structure'
];
$part
=
$struct
->
parts
[
1
];
// Get body
$this
->
set_part_body
(
$part
,
$p
[
'object'
]->
uid
);
// Decrypt
$result
=
$this
->
pgp_decrypt
(
$part
->
body
);
$this
->
decryptions
[
$part
->
mime_id
]
=
$result
;
//print_r($part);
// Parse decrypted message
if
(
$result
===
true
)
{
// @TODO
}
else
{
// Make sure decryption status message will be displayed
$part
->
type
=
'content'
;
$p
[
'object'
]->
parts
[]
=
$part
;
}
}
/**
* Handler for S/MIME encrypted message.
*
* @param array Reference to hook's parameters
*/
private
function
parse_smime_encrypted
(&
$p
)
{
// $this->load_smime_driver();
}
/**
* PGP signature verification.
*
* @param mixed Message body
* @param mixed Signature body (for MIME messages)
*
* @return mixed enigma_signature or enigma_error
*/
private
function
pgp_verify
(&
$msg_body
,
$sig_body
=
null
)
{
// @TODO: Handle big bodies using (temp) files
// @TODO: caching of verification result
$sig
=
$this
->
pgp_driver
->
verify
(
$msg_body
,
$sig_body
);
if
((
$sig
instanceof
enigma_error
)
&&
$sig
->
getCode
()
!=
enigma_error
::
E_KEYNOTFOUND
)
rcube
::
raise_error
(
array
(
'code'
=>
600
,
'type'
=>
'php'
,
'file'
=>
__FILE__
,
'line'
=>
__LINE__
,
'message'
=>
"Enigma plugin: "
.
$sig
->
getMessage
()
),
true
,
false
);
return
$sig
;
}
/**
* PGP message decryption.
*
* @param mixed Message body
*
* @return mixed True or enigma_error
*/
private
function
pgp_decrypt
(&
$msg_body
)
{
// @TODO: Handle big bodies using (temp) files
// @TODO: caching of verification result
$key
=
''
;
$pass
=
''
;
// @TODO
$result
=
$this
->
pgp_driver
->
decrypt
(
$msg_body
,
$key
,
$pass
);
if
(
$result
instanceof
enigma_error
)
{
$err_code
=
$result
->
getCode
();
if
(!
in_array
(
$err_code
,
array
(
enigma_error
::
E_KEYNOTFOUND
,
enigma_error
::
E_BADPASS
)))
rcube
::
raise_error
(
array
(
'code'
=>
600
,
'type'
=>
'php'
,
'file'
=>
__FILE__
,
'line'
=>
__LINE__
,
'message'
=>
"Enigma plugin: "
.
$result
->
getMessage
()
),
true
,
false
);
return
$result
;
}
// $msg_body = $result;
return
true
;
}
/**
* PGP keys listing.
*
* @param mixed Key ID/Name pattern
*
* @return mixed Array of keys or enigma_error
*/
function
list_keys
(
$pattern
=
''
)
{
$this
->
load_pgp_driver
();
$result
=
$this
->
pgp_driver
->
list_keys
(
$pattern
);
if
(
$result
instanceof
enigma_error
)
{
rcube
::
raise_error
(
array
(
'code'
=>
600
,
'type'
=>
'php'
,
'file'
=>
__FILE__
,
'line'
=>
__LINE__
,
'message'
=>
"Enigma plugin: "
.
$result
->
getMessage
()
),
true
,
false
);
}
return
$result
;
}
/**
* PGP key details.
*
* @param mixed Key ID
*
* @return mixed enigma_key or enigma_error
*/
function
get_key
(
$keyid
)
{
$this
->
load_pgp_driver
();
$result
=
$this
->
pgp_driver
->
get_key
(
$keyid
);
if
(
$result
instanceof
enigma_error
)
{
rcube
::
raise_error
(
array
(
'code'
=>
600
,
'type'
=>
'php'
,
'file'
=>
__FILE__
,
'line'
=>
__LINE__
,
'message'
=>
"Enigma plugin: "
.
$result
->
getMessage
()
),
true
,
false
);
}
return
$result
;
}
/**
* PGP keys/certs importing.
*
* @param mixed Import file name or content
* @param boolean True if first argument is a filename
*
* @return mixed Import status data array or enigma_error
*/
function
import_key
(
$content
,
$isfile
=
false
)
{
$this
->
load_pgp_driver
();
$result
=
$this
->
pgp_driver
->
import
(
$content
,
$isfile
);
if
(
$result
instanceof
enigma_error
)
{
rcube
::
raise_error
(
array
(
'code'
=>
600
,
'type'
=>
'php'
,
'file'
=>
__FILE__
,
'line'
=>
__LINE__
,
'message'
=>
"Enigma plugin: "
.
$result
->
getMessage
()
),
true
,
false
);
}
else
{
$result
[
'imported'
]
=
$result
[
'public_imported'
]
+
$result
[
'private_imported'
];
$result
[
'unchanged'
]
=
$result
[
'public_unchanged'
]
+
$result
[
'private_unchanged'
];
}
return
$result
;
}
/**
* Handler for keys/certs import request action
*/
function
import_file
()
{
$uid
=
rcube_utils
::
get_input_value
(
'_uid'
,
rcube_utils
::
INPUT_POST
);
$mbox
=
rcube_utils
::
get_input_value
(
'_mbox'
,
rcube_utils
::
INPUT_POST
);
$mime_id
=
rcube_utils
::
get_input_value
(
'_part'
,
rcube_utils
::
INPUT_POST
);
$storage
=
$this
->
rc
->
get_storage
();
if
(
$uid
&&
$mime_id
)
{
$storage
->
set_folder
(
$mbox
);
$part
=
$storage
->
get_message_part
(
$uid
,
$mime_id
);
}
if
(
$part
&&
is_array
(
$result
=
$this
->
import_key
(
$part
)))
{
$this
->
rc
->
output
->
show_message
(
'enigma.keysimportsuccess'
,
'confirmation'
,
array
(
'new'
=>
$result
[
'imported'
],
'old'
=>
$result
[
'unchanged'
]));
}
else
$this
->
rc
->
output
->
show_message
(
'enigma.keysimportfailed'
,
'error'
);
$this
->
rc
->
output
->
send
();
}
/**
* Checks if specified message part contains body data.
* If body is not set it will be fetched from IMAP server.
*
* @param rcube_message_part Message part object
* @param integer Message UID
*/
private
function
set_part_body
(
$part
,
$uid
)
{
// @TODO: Create such function in core
// @TODO: Handle big bodies using file handles
if
(!
isset
(
$part
->
body
))
{
$part
->
body
=
$this
->
rc
->
storage
->
get_message_part
(
$uid
,
$part
->
mime_id
,
$part
);
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Fri, Apr 24, 10:05 AM (4 d, 23 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18857299
Default Alt Text
enigma_engine.php (17 KB)
Attached To
Mode
R113 roundcubemail
Attached
Detach File
Event Timeline