public static function initListeners() { $accountController = new \GO\Email\Controller\AccountController(); $accountController->addListener('load', "GO\\Smime\\EventHandlers", "loadAccount"); $accountController->addListener('submit', "GO\\Smime\\EventHandlers", "submitAccount"); $messageController = new \GO\Email\Controller\MessageController(); $messageController->addListener('beforesend', "GO\\Smime\\EventHandlers", "beforeSend"); \GO\Email\Model\ImapMessage::model()->addListener('tooutputarray', "GO\\Smime\\EventHandlers", "toOutputArray"); $aliasController = new \GO\Email\Controller\AliasController(); $aliasController->addListener('store', "GO\\Smime\\EventHandlers", "aliasesStore"); \GO\Email\Model\Account::model()->addListener('delete', "GO\\Smime\\EventHandlers", "deleteAccount"); \GO\Base\Model\User::model()->addListener('delete', "GO\\Smime\\SmimeModule", "deleteUser"); }
protected function actionZipAllAttachments($params) { $account = Account::model()->findByPk($params['account_id']); //$imap = $account->openImapConnection($params['mailbox']); $message = \GO\Email\Model\ImapMessage::model()->findByUid($account, $params["mailbox"], $params["uid"]); $tmpFolder = \GO\Base\Fs\Folder::tempFolder(uniqid(time())); $atts = $message->getAttachments(); while ($att = array_shift($atts)) { if (empty($att->content_id) || $att->disposition == 'attachment') { $att->saveToFile($tmpFolder); } } $archiveFile = $tmpFolder->parent()->createChild(GO::t('attachments', 'email') . '.zip'); \GO\Base\Fs\Zip::create($archiveFile, $tmpFolder, $tmpFolder->ls()); \GO\Base\Util\Http::outputDownloadHeaders($archiveFile, false); readfile($archiveFile->path()); $tmpFolder->delete(); $archiveFile->delete(); }
protected function actionCopyMailTo($params) { $srcMessages = json_decode($params['srcMessages']); foreach ($srcMessages as $srcMessageInfo) { $srcAccountModel = \GO\Email\Model\Account::model()->findByPk($srcMessageInfo->accountId); $srcImapMessage = \GO\Email\Model\ImapMessage::model()->findByUid($srcAccountModel, $srcMessageInfo->mailboxPath, $srcMessageInfo->mailUid); $targetAccountModel = \GO\Email\Model\Account::model()->findByPk($params['targetAccountId']); if (!$targetAccountModel->checkPermissionLevel(\GO\Base\Model\Acl::CREATE_PERMISSION)) { throw new \GO\Base\Exception\AccessDenied(); } $targetImapConnection = $targetAccountModel->openImapConnection($params["targetMailboxPath"]); $flags = ''; if ($srcMessageInfo->seen) { $flags = '\\SEEN'; } $targetImapConnection->append_message($params['targetMailboxPath'], $srcImapMessage->getSource(), $flags); } return array('success' => true); }
protected function actionAcceptInvitation($params) { //todo calendar should be associated with mail account! //\GO::user()->id must be replaced with $account->calendar->user_id // $vevent = $this->_getVObjectFromMail($params); $account = \GO\Email\Model\Account::model()->findByPk($params['account_id']); $message = \GO\Email\Model\ImapMessage::model()->findByUid($account, $params['mailbox'], $params['uid']); $vcalendar = $message->getInvitationVcalendar(); $vevent = $vcalendar->vevent[0]; //if a recurrence-id if passed then convert it to a unix time stamp. //it is an update just for a particular occurrence. //todo check if $vevent->{'recurrence-id'} works $recurrenceDate = false; $recurrence = $vevent->select('recurrence-id'); //var_dump($recurrence);exit(); if (count($recurrence)) { $firstMatch = array_shift($recurrence); $recurrenceDate = $firstMatch->getDateTime()->format('U'); } switch ($vcalendar->method) { case 'REPLY': return $this->_handleIcalendarReply($vevent, $recurrenceDate, $account); break; case 'REQUEST': //$status = !empty($params['status']) ? $params['status'] : false; return $this->_handleIcalendarRequest($vevent, $recurrenceDate, $account); break; case 'CANCEL': return $this->_handleIcalendarCancel($vevent, $recurrenceDate, $account); break; default: throw new \Exception("Unsupported method: " . $vcalendar->method); } }
public function actionVerify($params) { $response['success'] = true; $params['email'] = strtolower($params['email']); //if file was already stored somewhere after decryption if (!empty($params['cert_id'])) { $cert = \GO\Smime\Model\PublicCertificate::model()->findByPk($params['cert_id']); $certData = $cert->cert; } else { // if (!empty($params['filepath'])) { // $srcFile = new \GO\Base\Fs\File(\GO::config()->tmpdir.$params['filepath']); if (!empty($params['account_id'])) { $account = \GO\Email\Model\Account::model()->findByPk($params['account_id']); $imapMessage = \GO\Email\Model\ImapMessage::model()->findByUid($account, $params['mailbox'], $params['uid']); $srcFile = \GO\Base\Fs\File::tempFile(); if (!$imapMessage->saveToFile($srcFile->path())) { throw new \Exception("Could not fetch message from IMAP server"); } $this->_decryptFile($srcFile, $account); } // throw new \Exception($srcFile->path()); $pubCertFile = \GO\Base\Fs\File::tempFile(); //Command line: //openssl smime -verify -in msg.txt $valid = openssl_pkcs7_verify($srcFile->path(), null, $pubCertFile->path(), $this->_getRootCertificates()); //Adding the PKCS7_NOVERIFY flag was used for testing some messages that could not be verified by openssl but did in Mozilla thunderbird. //Error msg: error:21075075:PKCS7 routines:PKCS7_verify:certificate verify error // // $valid = openssl_pkcs7_verify($srcFile->path(), PKCS7_NOVERIFY, $pubCertFile->path(), $this->_getRootCertificates()); // throw new \Exception($srcFile->path()); $srcFile->delete(); if ($valid) { if ($pubCertFile->exists()) { $certData = $pubCertFile->getContents(); $arr = openssl_x509_parse($certData); $senderEmailStr = !empty($arr['extensions']['subjectAltName']) ? $arr['extensions']['subjectAltName'] : $arr['subject']['emailAddress']; $senderEmails = explode(',', $senderEmailStr); $emails = array(); foreach ($senderEmails as $emailRaw) { $email = strtolower(\GO\Base\Util\String::get_email_from_string($emailRaw)); if ($email) { $emails[] = $email; } } $pubCertFile->delete(); $this->_savePublicCertificate($certData, $emails); } else { throw new \Exception('Certificate appears to be valid but could not get certificate from signature. SSL Error: ' . openssl_error_string()); } if (empty($certData)) { throw new \Exception('Certificate appears to be valid but could not get certificate from signature.'); } } } if (!isset($arr) && isset($certData)) { $arr = openssl_x509_parse($certData); $senderEmailStr = !empty($arr['extensions']['subjectAltName']) ? $arr['extensions']['subjectAltName'] : $arr['subject']['emailAddress']; $emails = array(); foreach ($senderEmails as $emailRaw) { $email = strtolower(\GO\Base\Util\String::get_email_from_string($emailRaw)); if ($email) { $emails[] = $email; } } } else { if (empty($emails)) { $emails = array('unknown'); } } $response['html'] = ''; $response['cls'] = ''; $response['text'] = ''; if (isset($params['account_id'])) { if (!$valid) { $response['cls'] = 'smi-invalid'; $response['text'] = \GO::t('invalidCert', 'smime'); $response['html'] .= '<h1 class="smi-invalid">' . \GO::t('invalidCert', 'smime') . '</h1>'; $response['html'] .= '<p>'; while ($msg = openssl_error_string()) { $response['html'] .= $msg . "<br />\n"; } $response['html'] .= '</p>'; } else { if (!in_array($params['email'], $emails)) { $response['cls'] = 'smi-certemailmismatch'; $response['text'] = \GO::t('certEmailMismatch', 'smime'); $response['html'] .= $response['short_html'] = '<h1 class="smi-certemailmismatch">' . \GO::t('certEmailMismatch', 'smime') . '</h1>'; } else { $response['cls'] = 'smi-valid'; $response['text'] = \GO::t('validCert', 'smime'); $response['html'] .= $response['short_html'] = '<h1 class="smi-valid">' . \GO::t('validCert', 'smime') . '</h1>'; } } } if (!isset($params['account_id']) || $valid) { $response['html'] .= '<table>'; $response['html'] .= '<tr><td width="100">' . \GO::t('name') . ':</td><td>' . $arr['name'] . '</td></tr>'; $response['html'] .= '<tr><td width="100">' . \GO::t('email', 'smime') . ':</td><td>' . implode(', ', $emails) . '</td></tr>'; $response['html'] .= '<tr><td>' . \GO::t('hash', 'smime') . ':</td><td>' . $arr['hash'] . '</td></tr>'; $response['html'] .= '<tr><td>' . \GO::t('serial_number', 'smime') . ':</td><td>' . $arr['serialNumber'] . '</td></tr>'; $response['html'] .= '<tr><td>' . \GO::t('version', 'smime') . ':</td><td>' . $arr['version'] . '</td></tr>'; $response['html'] .= '<tr><td>' . \GO::t('issuer', 'smime') . ':</td><td>'; foreach ($arr['issuer'] as $skey => $svalue) { if (is_array($svalue)) { foreach ($svalue as $sv) { $response['html'] .= $skey . ':' . $sv . '; '; } } else { $response['html'] .= $skey . ':' . $svalue . '; '; } } $response['html'] .= '</td></tr>'; $response['html'] .= '<tr><td>' . \GO::t('valid_from', 'smime') . ':</td><td>' . \GO\Base\Util\Date::get_timestamp($arr['validFrom_time_t']) . '</td></tr>'; $response['html'] .= '<tr><td>' . \GO::t('valid_to', 'smime') . ':</td><td>' . \GO\Base\Util\Date::get_timestamp($arr['validTo_time_t']) . '</td></tr>'; $response['html'] .= '</table>'; } return $response; }
public static function toOutputArray(array &$response, \GO\Email\Model\ImapMessage $imapMessage) { if ($imapMessage->content_type == 'application/x-pkcs7-mime') { $imapMessage->content_type = 'application/pkcs7-mime'; } if ($imapMessage->content_type == 'application/pkcs7-mime' && isset($imapMessage->content_type_attributes['smime-type']) && $imapMessage->content_type_attributes['smime-type'] == 'signed-data') { //signed data but not in clear text. Outlook has this option. $outfile = \GO\Base\Fs\File::tempFile(); $imapMessage->getImapConnection()->save_to_file($imapMessage->uid, $outfile->path()); $verifyOutfile = \GO\Base\Fs\File::tempFile(); // $cmd = '/usr/bin/openssl smime -verify -in ' . $outfile->path() . ' -out ' . $verifyOutfile->path(); // exec($cmd); // //PHP can't output the verified data without the signature without //suppling the extracerts option. We generated a dummy certificate for //this. openssl_pkcs7_verify($outfile->path(), null, "/dev/null", array(), GO::config()->root_path . "modules/smime/dummycert.pem", $verifyOutfile->path()); $message = \GO\Email\Model\SavedMessage::model()->createFromMimeData($verifyOutfile->getContents()); //remove temp files $outfile->delete(); $verifyOutfile->delete(); $newResponse = $message->toOutputArray(true); unset($newResponse['to']); unset($newResponse['cc']); foreach ($newResponse as $key => $value) { if (!empty($value) || $key == 'attachments') { $response[$key] = $value; } } // $response['path'] = $outfile->stripTempPath(); return; } if ($imapMessage->content_type == 'application/pkcs7-mime') { $encrypted = !isset($imapMessage->content_type_attributes['smime-type']) || $imapMessage->content_type_attributes['smime-type'] != 'signed-data'; if ($encrypted) { GO::debug("Message is encrypted"); $cert = Model\Certificate::model()->findByPk($imapMessage->account->id); if (!$cert || empty($cert->cert)) { GO::debug('SMIME: No private key at all found for this account'); $response['htmlbody'] = GO::t('noPrivateKeyForDecrypt', 'smime'); return false; } if (isset($_REQUEST['password'])) { GO::session()->values['smime']['passwords'][$imapMessage->account->id] = $_REQUEST['password']; } if (!isset(GO::session()->values['smime']['passwords'][$imapMessage->account->id])) { $response['askPassword'] = true; GO::debug("Need to ask for password"); return false; } } $attachments = $imapMessage->getAttachments(); $att = array_shift($attachments); // array ( // 'type' => 'application', // 'subtype' => 'pkcs7-mime', // 'smime-type' => 'enveloped-data', // 'name' => 'smime.p7m', // 'id' => false, // 'encoding' => 'base64', // 'size' => '2302', // 'md5' => false, // 'disposition' => false, // 'language' => false, // 'location' => false, // 'charset' => false, // 'lines' => false, // 'number' => 1, // 'extension' => 'p7m', // 'human_size' => '2,2 KB', // 'tmp_file' => false, // ) $infile = \GO\Base\Fs\File::tempFile(); $outfile = \GO\Base\Fs\File::tempFile(); //$outfilerel = $reldir . 'unencrypted.txt'; if ($encrypted) { GO::debug('Message is encrypted'); // $imapMessage->getImapConnection()->save_to_file($imapMessage->uid, $infile->path(), 'TEXT', 'base64'); // throw new \Exception($infile->path()); if (!$imapMessage->saveToFile($infile->path())) { throw new \Exception("Could not save IMAP message to file for decryption"); } $password = GO::session()->values['smime']['passwords'][$imapMessage->account->id]; openssl_pkcs12_read($cert->cert, $certs, $password); if (empty($certs)) { //password invalid $response['askPassword'] = true; GO::debug("Invalid password"); return false; } $return = openssl_pkcs7_decrypt($infile->path(), $outfile->path(), $certs['cert'], array($certs['pkey'], $password)); $infile->delete(); if (!$return || !$outfile->exists() || !$outfile->size()) { $response['htmlbody'] = GO::t('decryptionFailed', 'smime') . '<br />'; while ($str = openssl_error_string()) { $response['htmlbody'] .= '<br />' . $str; } GO::debug("Decryption failed"); return false; } else { //check if also signed $data = $outfile->getContents(); if (strpos($data, 'signed-data')) { $verifyOutfile = \GO\Base\Fs\File::tempFile(); openssl_pkcs7_verify($outfile->path(), null, "/dev/null", array(), GO::config()->root_path . "modules/smime/dummycert.pem", $verifyOutfile->path()); $outfile = $verifyOutfile; } $message = \GO\Email\Model\SavedMessage::model()->createFromMimeData($outfile->getContents()); $newResponse = $message->toOutputArray(true); unset($newResponse['to']); unset($newResponse['to_string']); unset($newResponse['cc']); foreach ($newResponse as $key => $value) { if (!empty($value) || $key == 'attachments') { $response[$key] = $value; } } $response['smime_encrypted'] = true; //$response['path']=$outfile->stripTempPath(); $outfile->delete(); } } else { GO::debug('Message is NOT encrypted'); } } }