Files
server/usr/share/psa-horde/mnemo/lib/Driver.php
2026-01-07 20:52:11 +01:00

542 lines
17 KiB
PHP

<?php
/**
* Mnemo_Driver:: defines an API for implementing storage backends for Mnemo.
*
* Copyright 2001-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (ASL). If you
* did not receive this file, see http://www.horde.org/licenses/apache.
*
* @author Jon Parise <jon@horde.org>
* @author Michael J. Rubinsky <mrubinsk@horde.org>
* @package Mnemo
*/
abstract class Mnemo_Driver
{
/**
* Array holding the current memo list. Each array entry is a hash
* describing a memo. The array is indexed numerically by memo ID.
*
* @var array
*/
protected $_memos = array();
/**
* String containing the current notepad name.
*
* @var string
*/
protected $_notepad = '';
/**
* Crypting processor.
*
* @var Horde_Crypt_pgp
*/
protected $_pgp;
/**
* Retrieves all of the notes of the current notepad from the backend.
*
* @thows Mnemo_Exception
*/
abstract public function retrieve();
/**
* Lists memos based on the given criteria. All memos will be
* returned by default.
*
* @return array Returns a list of the requested memos.
*/
public function listMemos()
{
return $this->_memos;
}
/**
* Update the description (short summary) of a memo.
*
* @param integer $memo_id The memo to update.
*/
public function getMemoDescription($body)
{
if (!strstr($body, "\n") && Horde_String::length($body) <= 64) {
return trim($body);
}
$lines = explode("\n", $body);
if (!is_array($lines)) {
return trim(Horde_String::substr($body, 0, 64));
}
// Move to a line with more than spaces.
$i = 0;
while (isset($lines[$i]) && !preg_match('|[^\s]|', $lines[$i])) {
$i++;
}
if (Horde_String::length($lines[$i]) <= 64) {
return trim($lines[$i]);
} else {
return trim(Horde_String::substr($lines[$i], 0, 64));
}
}
/**
* Retrieves one note from the backend.
*
* @param string $noteId The ID of the note to retrieve.
* @param string $passphrase A passphrase with which this note was
* supposed to be encrypted.
*
* @return array The array of note attributes.
* @throws Mnemo_Exception
* @throws Horde_Exception_NotFound
*/
abstract public function get($noteId, $passphrase = null);
/**
* Retrieves one note from the backend by UID.
*
* @param string $uid The UID of the note to retrieve.
* @param string $passphrase A passphrase with which this note was
* supposed to be encrypted.
*
* @return array The array of note attributes.
* @throws Mnemo_Exception
* @throws Horde_Exception_NotFound
*/
abstract public function getByUID($uid, $passphrase = null);
/**
* Adds a note to the backend storage.
*
* @param string $desc The first line of the note.
* @param string $body The whole note body.
* @param string $tags The tags of the note.
* @param string $passphrase The passphrase to encrypt the note with.
*
* @return string The ID of the new note.
* @throws Mnemo_Exception
*/
public function add($desc, $body, $tags = '', $passphrase = null)
{
$noteId = $this->_generateId();
if ($passphrase) {
$body = $this->_encrypt($body, $passphrase);
Mnemo::storePassphrase($noteId, $passphrase);
}
$uid = $this->_add($noteId, $desc, $body, $tags);
// Add tags.
$GLOBALS['injector']->getInstance('Mnemo_Tagger')
->tag($uid, $tags, $GLOBALS['registry']->getAuth(), 'note');
// Log the creation of this item in the history log.
try {
$GLOBALS['injector']->getInstance('Horde_History')
->log('mnemo:' . $this->_notepad . ':' . $uid,
array('action' => 'add'), true);
} catch (Horde_Exception $e) {
}
return $noteId;
}
/**
* Adds a note to the backend storage.
*
* @param string $noteId The ID of the new note.
* @param string $desc The first line of the note.
* @param string $body The whole note body.
* @param string $tags The tags of the note.
*
* @return string The unique ID of the new note.
* @throws Mnemo_Exception
*/
abstract protected function _add($noteId, $desc, $body, $tags);
/**
* Modifies an existing note.
*
* @param string $noteId The note to modify.
* @param string $desc The first line of the note.
* @param string $body The whole note body.
* @param string $tags The tags of the note.
* @param string $passphrase The passphrase to encrypt the note with.
*
* @throws Mnemo_Exception
*/
public function modify($noteId, $desc, $body, $tags = '',
$passphrase = null)
{
if ($passphrase) {
$body = $this->_encrypt($body, $passphrase);
Mnemo::storePassphrase($noteId, $passphrase);
}
$uid = $this->_modify($noteId, $desc, $body, $tags);
// Update tags.
$GLOBALS['injector']->getInstance('Mnemo_Tagger')
->replaceTags($uid, $tags, $GLOBALS['registry']->getAuth(), 'note');
// Log the modification of this item in the history log.
if ($uid) {
try {
$GLOBALS['injector']->getInstance('Horde_History')
->log('mnemo:' . $this->_notepad . ':' . $uid,
array('action' => 'modify'), true);
} catch (Horde_Exception $e) {
}
}
}
/**
* Modifies an existing note.
*
* @param string $noteId The note to modify.
* @param string $desc The first line of the note.
* @param string $body The whole note body.
* @param string $tags The tags of the note.
*
* @return string The note's UID.
* @throws Mnemo_Exception
*/
abstract protected function _modify($noteId, $desc, $body, $tags);
/**
* Moves a note to a new notepad.
*
* @param string $noteId The note to move.
* @param string $newNotepad The new notepad.
*
* @throws Mnemo_Exception
*/
public function move($noteId, $newNotepad)
{
$uid = $this->_move($noteId, $newNotepad);
// Log the moving of this item in the history log.
if ($uid) {
try {
$history = $GLOBALS['injector']->getInstance('Horde_History');
$history->log('mnemo:' . $this->_notepad . ':' . $uid,
array('action' => 'delete'), true);
$history->log('mnemo:' . $newNotepad . ':' . $uid,
array('action' => 'add'), true);
} catch (Horde_Exception $e) {
}
}
}
/**
* Moves a note to a new notepad.
*
* @param string $noteId The note to move.
* @param string $newNotepad The new notepad.
*
* @return string The note's UID.
* @throws Mnemo_Exception
*/
abstract protected function _move($noteId, $newNotepad);
/**
* Deletes a note permanently.
*
* @param string $noteId The note to delete.
*
* @throws Mnemo_Exception
*/
public function delete($noteId)
{
$uid = $this->_delete($noteId);
// Log the deletion of this item in the history log.
if ($uid) {
try {
$GLOBALS['injector']->getInstance('Horde_History')
->log('mnemo:' . $this->_notepad . ':' . $uid,
array('action' => 'delete'), true);
} catch (Horde_Exception $e) {
}
// Remove tags
$GLOBALS['injector']->getInstance('Mnemo_Tagger')
->replaceTags($uid, array(), $GLOBALS['registry']->getAuth(), 'note');
/* Tell content we removed the object */
$GLOBALS['injector']->getInstance('Content_Objects_Manager')
->delete(array($uid), 'note');
}
}
/**
* Deletes a note permanently.
*
* @param array $note The note to delete.
*
* @return string The note's UID.
* @throws Mnemo_Exception
*/
abstract protected function _delete($noteId);
/**
* Deletes all notes from the current notepad.
*
* @throws Mnemo_Exception
*/
public function deleteAll()
{
global $injector, $registry;
$uids = $this->_deleteAll();
// Update History.
$history = $injector->getInstance('Horde_History');
$tagger = $injector->getInstance('Mnemo_Tagger');
$manager = $injector->getInstance('Content_Objects_Manager');
try {
foreach ($uids as $uid) {
$history->log(
'mnemo:' . $this->_notepad . ':' . $uid,
array('action' => 'delete'),
true);
$tagger->replaceTags(
$uid, array(), $registry->getAuth(), 'note'
);
/* Tell content we removed the object */
$manager->delete(array($uid), 'note');
}
} catch (Horde_Exception $e) {
}
}
/**
* Deletes all notes from the current notepad.
*
* @return array An array of uids that have been removed.
* @throws Mnemo_Exception
*/
abstract protected function _deleteAll();
/**
* Loads the PGP encryption driver.
*
* @TODO: Inject *into* driver from the factory binder
*/
protected function _loadPGP()
{
if (empty($GLOBALS['conf']['gnupg']['path'])) {
throw new Mnemo_Exception(_("Encryption support has not been configured, please contact your administrator."));
}
$this->_pgp = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Crypt')->create('pgp', array(
'program' => $GLOBALS['conf']['gnupg']['path']
));
}
/**
* Encrypts a note.
*
* @param string $note The note text.
* @param string $passphrase The passphrase to encrypt the note with.
*
* @return string The encrypted text.
*/
protected function _encrypt($note, $passphrase)
{
$this->_loadPGP();
return $this->_pgp->encrypt($note, array('type' => 'message', 'symmetric' => true, 'passphrase' => $passphrase));
}
/**
* Decrypts a note.
*
* @param string $note The encrypted note text.
* @param string $passphrase The passphrase to decrypt the note with.
*
* @return string The decrypted text.
* @throws Mnemo_Exception
*/
protected function _decrypt($note, $passphrase)
{
$this->_loadPGP();
try {
return $this->_pgp->decrypt($note, array('type' => 'message', 'passphrase' => $passphrase));
} catch (Horde_Crypt_Exception $e) {
throw new Mnemo_Exception($e->getMessage(), Mnemo::ERR_DECRYPT);
}
}
/**
* Returns whether note encryption is supported.
*
* Checks if PGP support could be loaded, if it supports symmetric
* encryption, and if we have a secure connection.
*
* @return boolean Whether encryption is suppoted.
*/
public function encryptionSupported()
{
try {
$this->_loadPGP();
} catch (Mnemo_Exception $e) {
}
return (is_callable(array($this->_pgp, 'encryptedSymmetrically')) &&
Horde::isConnectionSecure());
}
/**
* Create an AS memo from this task
*
* @param array $memo A memo array.
* @param array $options
*
* @return Horde_ActiveSync_Message_Note
*/
public function toASNote($memo, $options = array())
{
$message = new Horde_ActiveSync_Message_Note(array(
'protocolversion' => $options['protocolversion']));
$message->subject = $memo['desc'];
$bp = $options['bodyprefs'];
$body = new Horde_ActiveSync_Message_AirSyncBaseBody();
// When the note is encrypted, we won't have the passphrase so the
// body will be a Mnemo_Exception.
if ($memo['body'] instanceof Mnemo_Exception) {
$memo['body'] = $memo['body']->getMessage();
}
if (isset($bp[Horde_ActiveSync::BODYPREF_TYPE_HTML])) {
$body->type = Horde_ActiveSync::BODYPREF_TYPE_HTML;
$memo['body'] = Horde_Text_Filter::filter($memo['body'], 'Text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO));
if (isset($bp[Horde_ActiveSync::BODYPREF_TYPE_HTML]['truncationsize']) &&
Horde_String::length($memo['body']) > $bp[Horde_ActiveSync::BODYPREF_TYPE_HTML]['truncationsize']) {
$body->data = Horde_String::substr($memo['body'], $bp[Horde_ActiveSync::BODYPREF_TYPE_HTML]['truncationsize']);
$body->truncated = 1;
} else {
$body->data = $memo['body'];
}
} else {
$body->type = Horde_ActiveSync::BODYPREF_TYPE_PLAIN;
if (isset($bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize']) &&
Horde_String::length($memo['body']) > $bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize']) {
$body->data = Horde_String::substr($memo['body'], 0, $bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize']);
$body->truncated = 1;
} else {
$body->data = $memo['body'];
}
}
$body->estimateddatasize = Horde_String::length($memo['body']);
$message->body = $body;
if (!empty($memo['tags'])) {
$message->categories = $memo['tags'];
}
$history = $GLOBALS['injector']->getInstance('Horde_History');
$last = $history->getActionTimeStamp('mnemo:' . $memo['memolist_id'] . ':' . $memo['uid'], 'modify');
if (empty($last)) {
$last = $history->getActionTimeStamp('mnemo:' . $memo['memolist_id'] . ':' . $memo['uid'], 'add');
}
$message->lastmodified = new Horde_Date($last);
return $message;
}
/**
* Export this memo in iCalendar format.
*
* @param array memo The memo (hash array) to export
* @param Horde_Icalendar A Horde_Icalendar object that acts as container.
*
* @return Horde_Icalendar_Vnote object for this event.
*/
public function toiCalendar($memo, $calendar)
{
global $prefs;
$vnote = Horde_Icalendar::newComponent('vnote', $calendar);
$vnote->setAttribute('UID', $memo['uid']);
$vnote->setAttribute('BODY', $memo['body']);
$vnote->setAttribute('SUMMARY', $memo['desc']);
if (!empty($memo['tags'])) {
$vnote->setAttribute('CATEGORIES', '', array(), true, $memo['tags']);
}
/* Get the note's history. */
$history = $GLOBALS['injector']->getInstance('Horde_History');
$log = $history->getHistory('mnemo:' . $memo['memolist_id'] . ':' . $memo['uid']);
if ($log) {
foreach ($log as $entry) {
switch ($entry['action']) {
case 'add':
$created = $entry['ts'];
break;
case 'modify':
$modified = $entry['ts'];
break;
}
}
}
if (!empty($created)) {
$vnote->setAttribute('DCREATED', $created);
}
if (!empty($modified)) {
$vnote->setAttribute('LAST-MODIFIED', $modified);
}
return $vnote;
}
/**
* Create a memo (hash array) from a Horde_Icalendar_Vnote object.
*
* @param Horde_Icalendar_Vnote $vnote The iCalendar data to update from.
*
* @return array Memo (hash array) created from the vNote.
*/
public function fromiCalendar(Horde_Icalendar_Vnote $vNote)
{
$memo = array();
try {
$body = $vNote->getAttribute('BODY');
} catch (Horde_Icalendar_Exception $e) {
}
if (!is_array($body)) {
$memo['body'] = $body;
} else {
$memo['body'] = '';
}
$memo['desc'] = $this->getMemoDescription($memo['body']);
try {
$cat = $vNote->getAttribute('CATEGORIES');
if (!is_array($cat)) {
$memo['tags'] = $cat;
}
} catch (Horde_Icalendar_Exception $e) {
}
return $memo;
}
/**
* Generates a local note ID.
*
* @return string A new note ID.
*/
abstract protected function _generateId();
}