From accb97bbe21e4501a2d29552769bea74ff154d62 Mon Sep 17 00:00:00 2001 From: Ben Klang Date: Wed, 6 Jan 2010 21:37:17 -0500 Subject: [PATCH] Hylax: Import from CVS Incubator --- hylax/compose.php | 66 ++++++ hylax/config/.cvsignore | 4 + hylax/config/.htaccess | 1 + hylax/config/conf.xml | 48 ++++ hylax/config/covers.php.dist | 27 +++ hylax/config/prefs.php.dist | 25 ++ hylax/docs/INSTALL | 35 +++ hylax/docs/TODO | 5 + hylax/folder.php | 102 ++++++++ hylax/img.php | 31 +++ hylax/index.php | 25 ++ hylax/lib/.htaccess | 1 + hylax/lib/Driver.php | 96 ++++++++ hylax/lib/Driver/hylafax.php | 255 ++++++++++++++++++++ hylax/lib/Driver/spandsp.php | 190 +++++++++++++++ hylax/lib/Hylax.php | 156 +++++++++++++ hylax/lib/Image.php | 171 ++++++++++++++ hylax/lib/SQL/Attributes.php | 308 ++++++++++++++++++++++++ hylax/lib/Storage.php | 200 ++++++++++++++++ hylax/lib/Storage/sql.php | 416 +++++++++++++++++++++++++++++++++ hylax/lib/base.php | 58 +++++ hylax/lib/version.php | 1 + hylax/print.php | 26 +++ hylax/scripts/.htaccess | 1 + hylax/scripts/.hylarc | 2 + hylax/scripts/cups/hylafax | 30 +++ hylax/scripts/cups/hylafax.ppd | 115 +++++++++ hylax/scripts/fax_create.php | 42 ++++ hylax/scripts/fax_create_recv.php | 58 +++++ hylax/scripts/fax_save_data.php | 39 ++++ hylax/scripts/fax_save_recv_data.php | 50 ++++ hylax/scripts/hylafax/faxrcvd | 41 ++++ hylax/scripts/install_cups_drivers.php | 54 +++++ hylax/scripts/spandsp/faxrcvd | 32 +++ hylax/scripts/sql/fax.mssql.sql | 29 +++ hylax/scripts/sql/fax.sql | 29 +++ hylax/send.php | 76 ++++++ hylax/summary.php | 39 ++++ hylax/templates/.htaccess | 1 + hylax/templates/common-header.inc | 29 +++ hylax/templates/compose/compose.html | 6 + hylax/templates/fax/fax.html | 21 ++ hylax/templates/folder/folder.html | 60 +++++ hylax/templates/summary/summary.html | 52 +++++ hylax/templates/view/view.html | 28 +++ hylax/themes/graphics/fax.png | Bin 0 -> 586 bytes hylax/themes/graphics/folder.png | Bin 0 -> 479 bytes hylax/themes/graphics/hylax.png | Bin 0 -> 586 bytes hylax/view.php | 51 ++++ 49 files changed, 3132 insertions(+) create mode 100644 hylax/compose.php create mode 100644 hylax/config/.cvsignore create mode 100644 hylax/config/.htaccess create mode 100644 hylax/config/conf.xml create mode 100644 hylax/config/covers.php.dist create mode 100644 hylax/config/prefs.php.dist create mode 100644 hylax/docs/INSTALL create mode 100644 hylax/docs/TODO create mode 100644 hylax/folder.php create mode 100644 hylax/img.php create mode 100644 hylax/index.php create mode 100644 hylax/lib/.htaccess create mode 100644 hylax/lib/Driver.php create mode 100644 hylax/lib/Driver/hylafax.php create mode 100644 hylax/lib/Driver/spandsp.php create mode 100644 hylax/lib/Hylax.php create mode 100644 hylax/lib/Image.php create mode 100644 hylax/lib/SQL/Attributes.php create mode 100644 hylax/lib/Storage.php create mode 100644 hylax/lib/Storage/sql.php create mode 100644 hylax/lib/base.php create mode 100644 hylax/lib/version.php create mode 100644 hylax/print.php create mode 100644 hylax/scripts/.htaccess create mode 100644 hylax/scripts/.hylarc create mode 100755 hylax/scripts/cups/hylafax create mode 100644 hylax/scripts/cups/hylafax.ppd create mode 100755 hylax/scripts/fax_create.php create mode 100755 hylax/scripts/fax_create_recv.php create mode 100755 hylax/scripts/fax_save_data.php create mode 100755 hylax/scripts/fax_save_recv_data.php create mode 100755 hylax/scripts/hylafax/faxrcvd create mode 100755 hylax/scripts/install_cups_drivers.php create mode 100644 hylax/scripts/spandsp/faxrcvd create mode 100644 hylax/scripts/sql/fax.mssql.sql create mode 100644 hylax/scripts/sql/fax.sql create mode 100644 hylax/send.php create mode 100644 hylax/summary.php create mode 100644 hylax/templates/.htaccess create mode 100644 hylax/templates/common-header.inc create mode 100644 hylax/templates/compose/compose.html create mode 100644 hylax/templates/fax/fax.html create mode 100644 hylax/templates/folder/folder.html create mode 100644 hylax/templates/summary/summary.html create mode 100644 hylax/templates/view/view.html create mode 100644 hylax/themes/graphics/fax.png create mode 100644 hylax/themes/graphics/folder.png create mode 100644 hylax/themes/graphics/hylax.png create mode 100644 hylax/view.php diff --git a/hylax/compose.php b/hylax/compose.php new file mode 100644 index 000000000..931fc2d3d --- /dev/null +++ b/hylax/compose.php @@ -0,0 +1,66 @@ + + */ + +@define('HYLAX_BASE', dirname(__FILE__)); +require_once HYLAX_BASE . '/lib/base.php'; +require_once 'Horde/Form.php'; +require_once 'Horde/Form/Renderer.php'; +require_once 'Horde/Form/Action.php'; +require_once 'Horde/Template.php'; + +/* Load Cover Page templates */ +require HYLAX_BASE . '/config/covers.php'; + +/* Get Cover Page template name */ +$covers = array(); +foreach ($_covers as $id => $cover) { + $covers[$id] = $cover['name']; +} + +$tpl = Horde_Util::getFormData('template', 'default'); +if (empty($_covers[$tpl])) { + Horde::fatal(_("The requested Cover Page does not exist."), __FILE__, __LINE__); +} + +/* Load Form Actions */ +$action = Horde_Form_Action::factory('submit'); + +/* Create Form */ +$vars = Horde_Variables::getDefaultVariables(); +$form = new Horde_Form($vars, _("Compose a new Fax"), 'compose'); +$form->setButtons(_("Send")); +$form->appendButtons(_("Preview")); + +/* Cover Page section */ +$form->addVariable(_("Cover Page"), 'fromhdr', 'header', false); +$form->addVariable(_("Template"), 'template', 'enum', true, false, null, array($covers)); +$form->addVariable(_("Fax Number"), 'faxnum', 'text', true); +$form->addVariable(_("Name"), 'name', 'text', false); +$form->addVariable(_("Company"), 'company', 'text', false); +$form->addVariable(_("Subject"), 'subject', 'text', false, false, null, array(false, 60)); +$form->addVariable(_("Comment"), 'comment', 'longtext', false, false, null, array(4, 80)); + +/* Set up template. */ +$template = new Horde_Template(); +$template->set('form', ''); +$template->set('menu', Hylax::getMenu('string')); +$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status'))); + +require HYLAX_TEMPLATES . '/common-header.inc'; +echo $template->fetch(HYLAX_TEMPLATES . '/compose/compose.html'); + +$renderer = new Horde_Form_Renderer(); +$form->renderActive($renderer, $vars, Horde::selfURL(), 'post'); + +require $registry->get('templates', 'horde') . '/common-footer.inc'; diff --git a/hylax/config/.cvsignore b/hylax/config/.cvsignore new file mode 100644 index 000000000..f6414066f --- /dev/null +++ b/hylax/config/.cvsignore @@ -0,0 +1,4 @@ +conf.php +conf.bak.php +covers.php +prefs.php diff --git a/hylax/config/.htaccess b/hylax/config/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/hylax/config/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/hylax/config/conf.xml b/hylax/config/conf.xml new file mode 100644 index 000000000..30d970293 --- /dev/null +++ b/hylax/config/conf.xml @@ -0,0 +1,48 @@ + + + + + + Fax Driver Settings + hylafax + + + /usr/sbin + + /var/spool/hylafax/bin + + + + + Zap/g1 + 0 + 60 + 20 + /var/spool/asterisk/outgoing + + + + + + + + Storage Driver Settings + sql + + + + + + + + + + + + CUPS Settings + faxuser + /usr/lib/cups/backend + /usr/share/cups/model + + + diff --git a/hylax/config/covers.php.dist b/hylax/config/covers.php.dist new file mode 100644 index 000000000..41a4d0fd2 --- /dev/null +++ b/hylax/config/covers.php.dist @@ -0,0 +1,27 @@ + _("Default Cover Page"), + 'template' => ' + + + + + + + + + + + + + + +
FromToDate
' +); diff --git a/hylax/config/prefs.php.dist b/hylax/config/prefs.php.dist new file mode 100644 index 000000000..450a4e73d --- /dev/null +++ b/hylax/config/prefs.php.dist @@ -0,0 +1,25 @@ + _("Other Options"), + 'label' => _("Display Options"), + 'desc' => _("Change display options such as how search results are sorted."), + 'members' => array('hylax_default_view') +); + +// default view +$_prefs['hylax_default_view'] = array( + 'value' => 'summary', + 'locked' => false, + 'shared' => false, + 'type' => 'enum', + 'enum' => array('summmary' => _("Summary"), + 'folder' => _("Folders"), + 'compose' => _("Compose")), + 'desc' => _("Select the view to display after login:") +); diff --git a/hylax/docs/INSTALL b/hylax/docs/INSTALL new file mode 100644 index 000000000..f7016dc34 --- /dev/null +++ b/hylax/docs/INSTALL @@ -0,0 +1,35 @@ + +Hylafax +======= + +- In the /var/spool/fax/etc/hosts.hfaxd file add something like the following line or lines: + localhost:faxuser + 127.0.0.1:faxuser + +- Add the following group to your /etc/group file: + groupadd -g 60002 faxuser + +- Make sure the apache user is part of the faxuser group: + faxuser:x:60002:apache + + +SpanDSP w/ Asterisk +=================== + +- Add the following line in your /etc/asterisk/extensions.conf + +[macro-faxreceive] +exten => s,1,SetVar(FAXFILE=/var/spool/asterisk/fax/${CALLEDFAX}/${UNIQUEID}) +exten => s,2,rxfax(${FAXFILE}.tif) + +[fax] +exten => 5551212,1,Macro(faxreceive) +exten => h,1,System(/var/www/horde/hylax/scripts/spandsp/faxrcvd "${FAXFILE}" "${CALLERIDNUM}" "${CALLEDFAX}") + +[default] + +exten => 5551212,1,SetVar(CALLEDFAX=${EXTEN}) +exten => 5551212,2,Answer +exten => 5551212,3,Goto(fax,${EXTEN},1) + + diff --git a/hylax/docs/TODO b/hylax/docs/TODO new file mode 100644 index 000000000..185b1eaa1 --- /dev/null +++ b/hylax/docs/TODO @@ -0,0 +1,5 @@ +$Horde: incubator/hylax/docs/TODO,v 1.1 2005/06/14 03:58:28 jvandal Exp $ + +- Integrate SpanDSP fax driver with Asterisk + +- Ability to create new covers and faxes diff --git a/hylax/folder.php b/hylax/folder.php new file mode 100644 index 000000000..f5f209e69 --- /dev/null +++ b/hylax/folder.php @@ -0,0 +1,102 @@ +listFaxes($folder); + +/* Set up URLs which will be used in the list. */ +$view_url = Horde::applicationUrl('view.php'); +$view_img = Horde::img('view.gif', _("View"), 'align="middle"'); + +$download_url = Horde_Util::addParameter($view_url, 'action', 'download'); +$download_img = Horde::img('download.gif', _("Download"), 'align="middle"', $GLOBALS['registry']->getImageDir('horde')); + +$edit_url = Horde::applicationUrl('edit.php'); +$edit_label = ($folder == 'pending') ? _("Edit") : _("Resend"); +$edit_img = Horde::img('edit.gif', $edit_label, 'align="middle"', $GLOBALS['registry']->getImageDir('horde')); + +$del_url = Horde::applicationUrl('delete.php'); +$del_img = Horde::img('delete-small.gif', _("Delete"), 'align="middle"', $GLOBALS['registry']->getImageDir('horde')); + +$params = array('folder' => $folder, 'path' => $path); +$warn_img = Horde::img('alerts/warning.gif', _("Warning"), 'align="middle"', $GLOBALS['registry']->getImageDir('horde')); +$send_url = Horde::applicationUrl('send.php'); + +$print_url = Horde::applicationUrl('print.php'); +$print_img = Horde::img('print.gif', _("Print"), 'align="middle"', $GLOBALS['registry']->getImageDir('horde')); + +/* Loop through list and set up items. */ +$i = 0; +foreach ($folder_list as $key => $value) { + $params['fax_id'] = $value['fax_id']; + + /* View. */ + $url = Horde_Util::addParameter($view_url, $params); + $folder_list[$key]['actions'][] = Horde::link($url, _("View")) . $view_img . ''; + + /* Download. */ + $url = Horde_Util::addParameter($download_url, $params); + $folder_list[$key]['actions'][] = Horde::link($url, _("Download")) . $download_img . ''; + + /* Delete. */ + // $url = Horde_Util::addParameter($del_url, $params); + // $folder_list[$key]['actions'][] = Horde::link($url, _("Delete")) . $del_img . ''; + + /* Print. */ + $url = Horde_Util::addParameter($print_url, $params); + $url = Horde_Util::addParameter($url, 'url', Horde::selfUrl(true)); + $folder_list[$key]['actions'][] = Horde::link($url, _("Print")) . $print_img . ''; + $folder_list[$key]['alt_count'] = $i; + $i = $i ? 0 : 1; + + /* Format date. */ + $folder_list[$key]['fax_created'] = strftime('%d/%m/%Y %H:%M', $value['fax_created']); + + if (empty($value['fax_number']) && $value['fax_type'] != 0) { + $url = Horde_Util::addParameter($send_url, 'fax_id', $value['fax_id']); + $folder_list[$key]['fax_number'] = $warn_img . ' ' . Horde::link($url, _("Insert Number")) . _("Insert Number") . ''; + } elseif (empty($value['fax_number']) && $value['fax_type'] == 0) { + $folder_list[$key]['fax_number'] = _("unknown"); + } + $folder_list[$key]['fax_status'] = $gateway->getStatus($value['job_id']); +} + +/* Set up actions. */ +$actions = array(); +foreach ($base_folders as $key => $value) { + if ($folder != $key) { + $url = Horde_Util::addParameter(Horde::applicationUrl('folder.php'), 'folder', $key); + $actions[] = Horde::link($url) . $value . ''; + } else { + $actions[] = $value; + } +} + +/* Set up template. */ +$template = &new Horde_Template(); +if ($folder == 'archive') { + $template->set('folder_name', $path); +} else { + $template->set('folder_name', $base_folders[$folder]); +} +$template->set('folder', $folder_list, true); +$template->set('actions', $actions); +$template->set('menu', Hylax::getMenu('string')); +$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status'))); + +require HYLAX_TEMPLATES . '/common-header.inc'; +echo $template->fetch(HYLAX_TEMPLATES . '/folder/folder.html'); +require $registry->get('templates', 'horde') . '/common-footer.inc'; diff --git a/hylax/img.php b/hylax/img.php new file mode 100644 index 000000000..2a8970b89 --- /dev/null +++ b/hylax/img.php @@ -0,0 +1,31 @@ +getOutput($id, "Hylax::getImage('$fax_id', '$page', '$preview');", 86400); + +header('Content-type: image/png'); +echo $image; diff --git a/hylax/index.php b/hylax/index.php new file mode 100644 index 000000000..9e3320994 --- /dev/null +++ b/hylax/index.php @@ -0,0 +1,25 @@ + + */ + +@define('HYLAX_BASE', dirname(__FILE__)); +$hylax_configured = (is_readable(HYLAX_BASE . '/config/conf.php') && + is_readable(HYLAX_BASE . '/config/prefs.php') && + is_readable(HYLAX_BASE . '/config/covers.php')); + +if (!$hylax_configured) { + require HYLAX_BASE . '/../lib/Test.php'; + Horde_Test::configFilesMissing('Hylax', HYLAX_BASE, + array('conf.php', 'prefs.php'), + array('covers.php' => 'This file defines the templates for Cover Sheets.')); +} + +require HYLAX_BASE . '/folder.php'; diff --git a/hylax/lib/.htaccess b/hylax/lib/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/hylax/lib/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/hylax/lib/Driver.php b/hylax/lib/Driver.php new file mode 100644 index 000000000..499ad3532 --- /dev/null +++ b/hylax/lib/Driver.php @@ -0,0 +1,96 @@ + + * @package Hylax + */ +class Hylax_Driver { + + var $_params; + + function Hylax_Driver($params) + { + global $conf; + + $this->_params = $params; + } + + function getFolder($folder, $path = null) + { + return $this->_getFolder($folder, $path); + } + + /** + * Attempts to return a concrete Hylax_Driver instance based on $driver. + * + * @param string $driver The type of concrete Hylax_Driver subclass to + * return. + * @param array $params A hash containing any additional configuration or + * connection parameters a subclass might need. + * + * @return Hylax_Driver The newly created concrete Hylax_Driver + * instance, or false on error. + */ + function &factory($driver = null, $params = null) + { + if (is_null($driver)) { + $driver = $GLOBALS['conf']['fax']['driver']; + } + + $driver = basename($driver); + + include_once dirname(__FILE__) . '/Driver/' . $driver . '.php'; + $class = 'Hylax_Driver_' . $driver; + if (class_exists($class)) { + $hylax = &new $class($params); + return $hylax; + } else { + Horde::fatal(PEAR::raiseError(sprintf(_("No such backend \"%s\" found"), $driver)), __FILE__, __LINE__); + } + } + + /** + * Attempts to return a reference to a concrete Hylax_Driver instance + * based on $driver. It will only create a new instance if no + * Hylax_Driver instance with the same parameters currently exists. + * + * This should be used if multiple storage sources are required. + * + * This method must be invoked as: $var = &Hylax_Driver::singleton() + * + * @param string $driver The type of concrete Hylax_Driver subclass to + * return. + * @param array $params A hash containing any additional configuration or + * connection parameters a subclass might need. + * + * @return mixed The created concrete Hylax_Driver instance, or false on + * error. + */ + function &singleton($driver = null, $params = null) + { + static $instances; + + if (is_null($driver)) { + $driver = $GLOBALS['conf']['fax']['driver']; + } + + if (!isset($instances)) { + $instances = array(); + } + $signature = serialize(array($driver, $params)); + + if (!isset($instances[$signature])) { + $instances[$signature] = &Hylax_Driver::factory($driver, $params); + } + return $instances[$signature]; + } + +} diff --git a/hylax/lib/Driver/hylafax.php b/hylax/lib/Driver/hylafax.php new file mode 100644 index 000000000..3050e55d0 --- /dev/null +++ b/hylax/lib/Driver/hylafax.php @@ -0,0 +1,255 @@ + + * @package Hylax + */ +class Hylax_Driver_hylafax extends Hylax_Driver { + + var $_states = array(); + var $_stat_cols = array(); + var $_cmd = array(); + + function Hylax_Driver_hylafax($params) + { + parent::Hylax_Driver($params); + + $this->_states = Hylax::getStates(); + $this->_stat_cols = Hylax::getStatCols(); + $this->_cmd = array('sendfax' => '/usr/bin/sendfax'); + } + + function send($number, $data, $time = null) + { + $command = sprintf('%s -n -d %s', + $this->_cmd['sendfax'], + $number); + $descriptorspec = array(0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w")); + + /* Set up the process. */ + $process = proc_open($command, $descriptorspec, $pipes); + if (is_resource($process)) { + fwrite($pipes[0], $data); + fclose($pipes[0]); + + $output = ''; + while (!feof($pipes[1])) { + $output .= fgets($pipes[1], 1024); + } + fclose($pipes[1]); + + proc_close($process); + } + + /* Regex match the job id from the output. */ + preg_match('/request id is (\d+)/', $output, $matches); + if (isset($matches[1])) { + return $matches[1]; + } + return PEAR::raiseError(sprintf(_("Could not send fax. %s"), $output)); + } + + function numFaxesIn() + { + //$inbox = $this->getInbox(); + //return count($inbox); + } + + function numFaxesOut() + { + //$outbox = $this->getOutbox(); + //return count($outbox); + } + + function getInbox() + { + return $this->_getFolder('inbox'); + } + + function _getFolder($folder, $path = null) + { + switch ($folder) { + case 'inbox': + return $this->_parseFaxStat($this->_exec('faxstat -r')); + break; + + case 'outbox': + return $this->_parseFaxStat($this->_exec('faxstat -s')); + break; + + case 'sent': + return $this->_parseFaxStat($this->_exec('faxstat -d')); + break; + + case 'archive': + //return $GLOBALS['storage']->getFolder($path); + break; + } + } + + function getJob($job_id, $folder, $path = null) + { + global $conf; + + $job = array(); + + switch ($folder) { + case 'inbox': + break; + + case 'outbox': + $filename = '/var/spool/fax/sendq/q' . $job_id; + $job = $this->_getParseSendJob($filename); + break; + + case 'sent': + $filename = '/var/spool/fax/doneq/q' . $job_id; + $job = $this->_getParseSendJob($filename); + break; + + case 'archive': + //return $GLOBALS['storage']->getFolder($path); + break; + } + + $job['thumbs'] = $this->getThumbs($job_id, 'docq/' . $job['postscript'], true); + + return $job; + } + + function getStatus($job_id) + { + static $send_q = array(); + static $done_q = array(); + if (empty($send_q)) { + exec('/usr/bin/faxstat -s', $output); + $iMax = count($output); + for ($i = 4; $i < $iMax; $i++) { + $send_q[] = $output[$i]; + } + } + if (empty($done_q)) { + exec('/usr/bin/faxstat -d', $output); + $iMax = count($output); + for ($i = 4; $i < $iMax; $i++) { + $done_q[] = $output[$i]; + } + } + + /* Check the queues. */ + foreach ($send_q as $line) { + if ((int)substr($line, 0, 4) == $job_id) { + return _("Sending"); + } + } + foreach ($done_q as $line) { + if ((int)substr($line, 0, 4) == $job_id) { + return substr($line, 51); + } + } + return ''; + } + + function _getParseSendJob($filename) + { + $job = array(); + $job_file = file_get_contents($filename); + $job_file = explode("\n", $job_file); + foreach ($job_file as $line) { + if (empty($line)) { + continue; + } + list($key, $value) = explode(':', $line, 2); + if ($key == 'postscript') { + $job[$key] = basename($value); + } else { + $job[$key] = $value; + } + } + return $job; + } + + function getThumbs($job_id, $ps) + { + if ($this->_vfs->exists(HYLAX_VFS_PATH, $job_id)) { + /* Return thumb image list. */ + $images = $this->_vfs->listFolder(HYLAX_VFS_PATH . '/' . $job_id, 'doc.png'); + if (!empty($images)) { + return array_keys($images); + } + } + $images = $this->imagesToVFS($job_id, $ps); + return array_keys($images); + } + + function imagesToVFS($job_id, $ps) + { + global $conf; + + $this->_vfs->autoCreatePath(HYLAX_VFS_PATH . '/' . $job_id); + + $ps = '/var/spool/fax/' . $ps; + $vfs_path = $conf['vfs']['params']['vfsroot'] . '/' . HYLAX_VFS_PATH; + /* Do thumbs. */ + $cmd = sprintf('convert -geometry 25%% %s %s/%s/thumb.png', $ps, $vfs_path, $job_id); + $result = $this->_exec($cmd); + /* Do full images. */ + $cmd = sprintf('convert %s %s/%s/doc.png', $ps, $vfs_path, $job_id); + $result = $this->_exec($cmd); + + /* Return thumb image list. */ + return $this->_vfs->listFolder(HYLAX_VFS_PATH . '/' . $job_id, 'doc.png'); + } + + function _exec($cmd, $input = '') + { + $spec = array(//0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('file', '/tmp/error-output.txt', 'a') + ); + $proc = proc_open($this->_params['base_path'] . $cmd, $spec, $pipes); + //fwrite($pipes[0], $input); + //@fclose($pipes[0]); + while (!feof($pipes[1])) { + $result[] = trim(fgets($pipes[1], 1024)); + } + @fclose($pipes[1]); + @fclose($pipes[2]); + proc_close($proc); + + if (empty($result[(count($result) - 1)])) { + unset($result[(count($result) - 1)]); + } + + return $result; + } + + function _parseFaxStat($result) + { + $out = array(); + $i = 0; + foreach ($result as $line) { + /* Job ID number expected as first char. */ + if (!empty($line) && is_numeric($line[0])) { + $values = explode('|', $line); + foreach ($this->_stat_cols as $j => $key) { + $out[$i][$key] = $values[$j]; + } + $out[$i]['state'] = $this->_states[$out[$i]['state']]; + } + $i++; + } + return $out; + } + +} diff --git a/hylax/lib/Driver/spandsp.php b/hylax/lib/Driver/spandsp.php new file mode 100644 index 000000000..8a12bb16e --- /dev/null +++ b/hylax/lib/Driver/spandsp.php @@ -0,0 +1,190 @@ + + * @package Hylax + */ +class Hylax_Driver_spandsp extends Hylax_Driver { + + var $_states = array(); + var $_stat_cols = array(); + var $_cmd = array(); + + function Hylax_Driver_spandsp($params) + { + parent::Hylax_Driver($params); + + $this->_states = Hylax::getStates(); + $this->_stat_cols = Hylax::getStatCols(); + } + + function send($number, $data, $time = null) + { + /* Create a temporary file. */ + $filename = sprintf("%s.fax", Horde::getTempFile('hylax')); + $fh = fopen($filename, "w"); + fwrite($fh, $data); + fclose($fh); + + return $this->createCallFile($filename); + } + + function createCallFile($filename) + { + global $conf; + + /* Create outgoing call */ + $data = sprintf("Channel: %s/%s\n", $conf['fax']['params']['channel'], $number); + $data .= sprintf("MaxRetries: %d\n", $conf['fax']['params']['maxretries']); + $data .= sprintf("RetryTime: %d\n", $conf['fax']['params']['retrytime']); + $data .= sprintf("WaitTime: %d\n", $conf['fax']['params']['waittime']); + $data .= sprintf("Application: %s\n", 'txfax'); + $data .= sprintf("Data: %s|caller\n", $filename); + + $outfile = sprintf("%s/%s.call", $conf['fax']['params']['outgoing'], md5(microtime())); + if ($fh = fopen($outfile, "w")) { + fwrite($fh, $data); + fclose($fh); + return true; + } + + return PEAR::raiseError(sprintf(_("Could not send fax. %s"), $output)); + } + + function numFaxesIn() + { + //$inbox = $this->getInbox(); + //return count($inbox); + } + + function numFaxesOut() + { + //$outbox = $this->getOutbox(); + //return count($outbox); + } + + function getInbox() + { + // return $this->_getFolder('inbox'); + } + + function _getFolder($folder, $path = null) + { + switch ($folder) { + case 'inbox': + // return $this->_parseFaxStat($this->_exec('faxstat -r')); + break; + + case 'outbox': + // return $this->_parseFaxStat($this->_exec('faxstat -s')); + break; + + case 'sent': + // return $this->_parseFaxStat($this->_exec('faxstat -d')); + break; + + case 'archive': + //return $GLOBALS['storage']->getFolder($path); + break; + } + } + + function getJob($job_id, $folder, $path = null) + { + global $conf; + + $job = array(); + + switch ($folder) { + case 'inbox': + break; + + case 'outbox': + // $filename = '/var/spool/fax/sendq/q' . $job_id; + // $job = $this->_getParseSendJob($filename); + break; + + case 'sent': + // $filename = '/var/spool/fax/doneq/q' . $job_id; + // $job = $this->_getParseSendJob($filename); + break; + + case 'archive': + //return $GLOBALS['storage']->getFolder($path); + break; + } + + // $job['thumbs'] = $this->getThumbs($job_id, 'docq/' . $job['postscript'], true); + + return $job; + } + + function getStatus($job_id) + { + return null; + } + + function getThumbs($job_id, $ps) + { + if ($this->_vfs->exists(HYLAX_VFS_PATH, $job_id)) { + /* Return thumb image list. */ + $images = $this->_vfs->listFolder(HYLAX_VFS_PATH . '/' . $job_id, 'doc.png'); + if (!empty($images)) { + return array_keys($images); + } + } + $images = $this->imagesToVFS($job_id, $ps); + return array_keys($images); + } + + function imagesToVFS($job_id, $ps) + { + global $conf; + + $this->_vfs->autoCreatePath(HYLAX_VFS_PATH . '/' . $job_id); + + $ps = '/var/spool/fax/' . $ps; + $vfs_path = $conf['vfs']['params']['vfsroot'] . '/' . HYLAX_VFS_PATH; + /* Do thumbs. */ + $cmd = sprintf('convert -geometry 25%% %s %s/%s/thumb.png', $ps, $vfs_path, $job_id); + $result = $this->_exec($cmd); + /* Do full images. */ + $cmd = sprintf('convert %s %s/%s/doc.png', $ps, $vfs_path, $job_id); + $result = $this->_exec($cmd); + + /* Return thumb image list. */ + return $this->_vfs->listFolder(HYLAX_VFS_PATH . '/' . $job_id, 'doc.png'); + } + + function _exec($cmd, $input = '') + { + $spec = array(//0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('file', '/tmp/error-output.txt', 'a') + ); + $proc = proc_open($this->_params['base_path'] . $cmd, $spec, $pipes); + //fwrite($pipes[0], $input); + //@fclose($pipes[0]); + while (!feof($pipes[1])) { + $result[] = trim(fgets($pipes[1], 1024)); + } + @fclose($pipes[1]); + @fclose($pipes[2]); + proc_close($proc); + + if (empty($result[(count($result) - 1)])) { + unset($result[(count($result) - 1)]); + } + + return $result; + } + +} diff --git a/hylax/lib/Hylax.php b/hylax/lib/Hylax.php new file mode 100644 index 000000000..1d92992fe --- /dev/null +++ b/hylax/lib/Hylax.php @@ -0,0 +1,156 @@ + + * @package Hylax + */ +class Hylax { + + function getBaseFolders() + { + return array('inbox' => _("Inbox"), + 'outbox' => _("Outbox")); + //'sent' => _("Sent"), + //'pending' => _("Pending")); + } + + function getStates() + { + return array('D' => _("Done"), + 'F' => _("Failed"), + 'S' => _("Sending"), + 'W' => _("Waiting")); + } + + function getStatCols() + { + return array('job_id', 'time', 'state', 'owner', 'number', 'pages', 'tot_pages', 'dials', 'duration', 'status'); + } + + function getVFSPath($folder) + { + return '.horde/fax/' . $folder; + } + + function getImage($fax_id, $page, $preview = false) + { + $data = $GLOBALS['hylax_storage']->getFaxData($fax_id); + + /* Get the image. */ + require_once HYLAX_BASE . '/lib/Image.php'; + $image = &new Hylax_Image(); + $image->loadData($data); + $image->getImage($page, $preview); + } + + function getPDF($fax_id) + { + $data = $GLOBALS['hylax_storage']->getFaxData($fax_id); + + /* Get the pdf. */ + require_once HYLAX_BASE . '/lib/Image.php'; + $image = &new Hylax_Image(); + $image->loadData($data); + $image->getPDF(); + } + + function printFax($fax_id) + { + $data = $GLOBALS['hylax_storage']->getFaxData($fax_id); + + $command = $GLOBALS['conf']['fax']['print']; + $descriptorspec = array(0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w")); + + /* Set up the process. */ + $process = proc_open($command, $descriptorspec, $pipes); + if (!is_resource($process)) { + return PEAR::raiseError('fail'); + } + + fwrite($pipes[0], $data); + fclose($pipes[0]); + + $output = ''; + while (!feof($pipes[1])) { + $output .= fgets($pipes[1], 1024); + } + fclose($pipes[1]); + + $stderr = ''; + while (!feof($pipes[2])) { + $stderr .= fgets($pipes[2], 1024); + } + fclose($pipes[2]); + + proc_close($process); + + if ($stderr) { + return PEAR::raiseError($stderr); + } + + return true; + } + + function getPages($fax_id, $num_pages) + { + $pages = array(); + $params = array('fax_id' => $fax_id, + 'preview' => 1); + + /* Set the params for the popup to view the full size pages. */ + Horde::addScriptFile('popup.js', 'horde'); + $popup_w = 620; + $popup_h = 860; + + for ($i = 0; $i < $num_pages; $i++) { + $params['page'] = $i; + $url = Horde_Util::addParameter('img.php', $params); + $img = Horde::img($url, sprintf(_("View page %s"), $i+1), '', $GLOBALS['registry']->get('webroot')); + + $full_url = Horde::applicationUrl(Horde_Util::addParameter('img.php', array('fax_id' => $fax_id, 'page' => $i))); + + $pages[] = Horde::link('', sprintf(_("View page %s"), $i+1), '', '', "popup('$full_url', $popup_w, $popup_h); return false;") . $img . ''; + } + return $pages; + } + + function getMenu($returnType = 'object') + { + global $registry; + + $menu = new Horde_Menu(); + + $menu->addArray(array('url' => Horde::applicationUrl('summary.php'), + 'text' => _("Summary"), + 'icon' => 'fax.png', + 'icon_path' => $registry->getImageDir())); + + $menu->addArray(array('url' => Horde::applicationUrl('folder.php'), + 'text' => _("Folders"), + 'icon' => 'folder.png', + 'icon_path' => $registry->getImageDir())); + + $menu->addArray(array('url' => Horde::applicationUrl('compose.php'), + 'text' => _("Compose"), + 'icon' => 'compose.png', + 'icon_path' => $registry->getImageDir())); + + if ($returnType == 'object') { + return $menu; + } else { + return $menu->render(); + } + } + +} diff --git a/hylax/lib/Image.php b/hylax/lib/Image.php new file mode 100644 index 000000000..13a80b42b --- /dev/null +++ b/hylax/lib/Image.php @@ -0,0 +1,171 @@ + + * @package Hylax + */ +class Hylax_Image { + + var $_data; + var $_cmd; + var $_pages = array(); + + /** + * Constructor + * + * @param array $params Any parameters needed for this image driver. + */ + function Hylax_Image() + { + $this->_cmd = array('identify' => '/usr/bin/identify', + 'convert' => '/usr/bin/convert', + 'ps2pdf' => '/usr/bin/ps2pdf14'); + } + + function loadData($data) + { + $this->_data = $data; + } + + function getDimensions() + { + $tmp_file = Horde_Util::getTempFile('fax', true, '/tmp'); + Horde::logMessage('Created temp file:' . Horde_Util::bufferOutput('var_dump', $tmp_file) . ':', __FILE__, __LINE__, PEAR_LOG_DEBUG); + $fp = fopen($tmp_file, 'w'); + fwrite($fp, $this->_data); + fclose($fp); + + /* Run a ImageMagick identify command on the file to get the details. */ + $command = sprintf('%s %s', $this->_cmd['identify'], $tmp_file); + Horde::logMessage('External command call by Hylax_Image::getDimensions(): :' . $command . ':', __FILE__, __LINE__, PEAR_LOG_DEBUG); + exec($command, $output, $retval); + + $init = strlen($tmp_file); + + /* Figure out the dimensions from the output. */ + Horde::logMessage('External command output by Hylax_Image::getDimensions(): ' . serialize($output), __FILE__, __LINE__, PEAR_LOG_DEBUG); + foreach ($output as $key => $line) { + if (substr($line, 0, $init) != $tmp_file) { + continue; + } + $info = explode(' ', $line); + $dims = explode('+', $info[2]); + list($width, $height) = explode('x', $dims[0]); + $this->_pages[$key]['width'] = $width; + $this->_pages[$key]['height'] = $height; + } + } + + function getNumPages() + { + if (empty($this->_pages)) { + $this->getDimensions(); + } + + return count($this->_pages); + } + + function getImage($page, $preview = false) + { + $tmp_file = Horde_Util::getTempFile('fax', true, '/tmp'); + $fp = fopen($tmp_file, 'wb'); + fwrite($fp, $this->_data); + fclose($fp); + + /* Set resize based on whether preview or not. */ + $resize = ($preview) ? ' -resize 140x200!' : ' -resize 595x842!'; + + $tmp_file_out = Horde_Util::getTempFile('fax_preview', true, '/tmp'); + /* Convert the page from the postscript file to PNG. */ + $command = sprintf('%s%s %s[%s] png:%s', + $this->_cmd['convert'], + $resize, + $tmp_file, + $page, + $tmp_file_out); + Horde::logMessage('Executing command: ' . $command, __FILE__, __LINE__, PEAR_LOG_DEBUG); + exec($command); + echo file_get_contents($tmp_file_out); + } + + function getPDF() + { + $tmp_file = Horde_Util::getTempFile('fax', true, '/tmp'); + $fp = fopen($tmp_file, 'wb'); + fwrite($fp, $this->_data); + fclose($fp); + + /* Convert the page from the postscript file to PDF. */ + $command = sprintf('%s %s -', $this->_cmd['ps2pdf'], $tmp_file); + Horde::logMessage('Executing command: ' . $command, __FILE__, __LINE__, PEAR_LOG_DEBUG); + passthru($command); + } + + /** + * Attempts to return a concrete Hylax_Image instance based on $driver. + * + * @param string $driver The type of concrete Hylax_Image subclass to + * return. + * @param array $params A hash containing any additional configuration or + * connection parameters a subclass might need. + * + * @return Hylax_Image The newly created concrete Hylax_Image instance, or + * false on an error. + */ + function &factory($driver, $params = array()) + { + $driver = basename($driver); + include_once dirname(__FILE__) . '/Image/' . $driver . '.php'; + $class = 'Hylax_Image_' . $driver; + if (class_exists($class)) { + $image = &new $class($params); + return $image; + } else { + Horde::fatal(PEAR::raiseError(sprintf(_("No such backend \"%s\" found"), $driver)), __FILE__, __LINE__); + } + } + + /** + * Attempts to return a reference to a concrete Hylax_Image instance based + * on $driver. + * + * It will only create a new instance if no Hylax_Image instance with the + * same parameters currently exists. + * + * This should be used if multiple image sources are required. + * + * This method must be invoked as: $var = &Hylax_Image::singleton() + * + * @param string $driver The type of concrete Hylax_Image subclass to + * return. + * @param array $params A hash containing any additional configuration or + * connection parameters a subclass might need. + * + * @return mixed The created concrete Hylax_Image instance, or false on + * error. + */ + function &singleton($driver, $params = array()) + { + static $instances; + + if (!isset($instances)) { + $instances = array(); + } + + $signature = serialize(array($driver, $params)); + if (!isset($instances[$signature])) { + $instances[$signature] = &Hylax_Image::factory($driver, $params); + } + + return $instances[$signature]; + } + +} diff --git a/hylax/lib/SQL/Attributes.php b/hylax/lib/SQL/Attributes.php new file mode 100644 index 000000000..4f49b22de --- /dev/null +++ b/hylax/lib/SQL/Attributes.php @@ -0,0 +1,308 @@ + + * @since Horde 2.2 + * @package Hylax + */ +class Hylax_SQL_Attributes { + + /** + * The PEAR::DB object to run queries with. + * + * @var DB + */ + var $_db; + + /** + * Parameters to use when generating queries: + * id_column - The primary id column to use in joins. + * primary_table - The main table name. + * attribute_table - The table that the attributes are stored in. + * + * @var array + */ + var $_params = array(); + + /** + * The number of copies of the attributes table that we need to join on in + * the current query. + * + * @var integer + */ + var $_table_count = 1; + + /** + * Constructor. + * + * @param DB $dbh A PEAR::DB object. + * @param array $params The id column, table names, etc. + */ + function Hylax_SQL_Attributes($dbh, $params) + { + $this->_db = $dbh; + $this->_params = $params; + } + + /** + * Returns all attributes for a given id or multiple ids. + * + * @param integer | array $id The id to fetch or an array of ids. + * + * @return array A hash of attributes, or a multi-level hash + * of ids => their attributes. + */ + function getAttributes($id) + { + if (is_array($id)) { + $query = sprintf('SELECT %1$s, attribute_name as name, attribute_key as "key", attribute_value as value FROM %2$s WHERE %1$s IN (%3$s)', + $this->_params['id_column'], + $this->_params['attribute_table'], + implode(', ', $id)); + + Horde::logMessage('SQL Query by Hylax_SQL_Attributes::getAttributes(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $rows = $this->_db->getAll($query, DB_FETCHMODE_ASSOC); + if (is_a($rows, 'PEAR_Error')) { + return $rows; + } + + $id_column = $this->_params['id_column']; + $data = array(); + foreach ($rows as $row) { + if (empty($data[$row[$id_column]])) { + $data[$row[$id_column]] = array(); + } + $data[$row[$id_column]][] = array('name' => $row['name'], + 'key' => $row['key'], + 'value' => $row['value']); + } + return $data; + } else { + $query = sprintf('SELECT %1$s, attribute_name as name, attribute_key as "key", attribute_value as value FROM %2$s WHERE %1$s = %3$s', + $this->_params['id_column'], + $this->_params['attribute_table'], + (int)$id); + Horde::logMessage('SQL Query by Hylax_SQL_Attributes::getAttributes(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG); + return $this->_db->getAll($query, DB_FETCHMODE_ASSOC); + } + } + + /** + * Return a set of ids based on a set of attribute criteria. + * + * @param array $criteria The array of criteria. Example: + * $criteria['OR'] = array( + * array('field' => 'name', + * 'op' => '=', + * 'test' => 'foo'), + * array('field' => 'name', + * 'op' => '=', + * 'test' => 'bar')); + * This would return all ids for which the field + * attribute_name is either 'foo' or 'bar'. + */ + function getByAttributes($criteria) + { + if (!count($criteria)) { + return array(); + } + + /* Build the query. */ + $this->_table_count = 1; + $query = ''; + foreach ($criteria as $key => $vals) { + if ($key == 'OR' || $key == 'AND') { + if (!empty($query)) { + $query .= ' ' . $key . ' '; + } + $query .= '(' . $this->_buildAttributeQuery($key, $vals) . ')'; + } + } + + /* Build the FROM/JOIN clauses. */ + $joins = array(); + $pairs = array(); + for ($i = 1; $i <= $this->_table_count; $i++) { + $joins[] = sprintf('LEFT JOIN %1$s a%2$s ON a%2$s.%3$s = m.%3$s', + $this->_params['attribute_table'], + $i, + $this->_params['id_column']); + + $pairs[] = 'AND a1.attribute_name = a' . $i . '.attribute_name'; + } + $joins = implode(' ', $joins); + $pairs = implode(' ', $pairs); + + $query = sprintf('SELECT DISTINCT a1.%s FROM %s m %s WHERE %s %s', + $this->_params['id_column'], + $this->_params['primary_table'], + $joins, + $query, + $pairs); + + Horde::logMessage('SQL Query by Hylax_SQL_Attributes::getByAttributes(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG); + + return $this->_db->getCol($query); + } + + /** + * Given a new attribute set and an id, insert each into the DB. If + * anything fails in here, rollback the transaction, return the relevant + * error and bail out. + * + * @param integer $id The id of the record for which attributes are + * being inserted. + * @param array $attributes An hash containing the attributes. + */ + function insertAttributes($id, $attributes) + { + foreach ($attributes as $attr) { + $query = 'INSERT INTO ' . $this->_params['attribute_table'] . + ' (' . $this->_params['id_column'] . ', attribute_name,' . + ' attribute_key, attribute_value) VALUES (?, ?, ?, ?)'; + $values = array((int)$id, + $attr['name'], + $attr['key'], + $attr['value']); + + Horde::logMessage('SQL Query by Hylax_SQL_Attributes::insertAttributes(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG); + + $result = $this->_db->query($query, $values); + if (is_a($result, 'PEAR_Error')) { + $this->_db->rollback(); + $this->_db->autoCommit(true); + return $result; + } + } + + /* Commit the transaction, and turn autocommit back on. */ + $result = $this->_db->commit(); + $this->_db->autoCommit(true); + } + + /** + * Given an id, delete all attributes for that id from the + * attributes table. + * + * @param integer $id The id of the record for which attributes are being + * deleted. + */ + function deleteAttributes($id) + { + /* Delete attributes. */ + $query = sprintf('DELETE FROM %s WHERE %s = %s', + $this->_params['attribute_table'], + $this->_params['id_column'], + (int)$id); + + Horde::logMessage('SQL Query by Hylax_SQL_Attributes::deleteAttributes(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_db->query($query); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + return true; + } + + /** + * Given an id, update all attributes for that id in the attributes table + * with the new attributes. + * + * @param integer $id The id of the record for which attributes are + * being deleted. + * @param array $attributes An hash containing the attributes. + */ + function updateAttributes($id, $attributes) + { + /* Delete the old attributes. */ + $result = $this->deleteAttributes($id); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + /* Insert the new attribute set. */ + $result = $this->insertAttributes($id, $attributes); + return $result; + } + + /** + * Build a piece of an attribute query. + * + * @param string $glue The glue to join the criteria (OR/AND). + * @param array $criteria The array of criteria. + * @param boolean $join Should we join on a clean attributes table? + * + * @return string An SQL fragment. + */ + function _buildAttributeQuery($glue, $criteria, $join = false) + { + require_once 'Horde/SQL.php'; + + /* Initialize the clause that we're building. */ + $clause = ''; + + /* Get the table alias to use for this set of criteria. */ + if ($join) { + $alias = $this->_getAlias(true); + } else { + $alias = $this->_getAlias(); + } + + foreach ($criteria as $key => $vals) { + if (!empty($vals['OR']) || !empty($vals['AND'])) { + if (!empty($clause)) { + $clause .= ' ' . $glue . ' '; + } + $clause .= '(' . $this->_buildAttributeQuery($glue, $vals) . ')'; + } elseif (!empty($vals['JOIN'])) { + if (!empty($clause)) { + $clause .= ' ' . $glue . ' '; + } + $clause .= $this->_buildAttributeQuery($glue, $vals['JOIN'], true); + } else { + if (isset($vals['field'])) { + if (!empty($clause)) { + $clause .= ' ' . $glue . ' '; + } + $clause .= Horde_SQL::buildClause($this->_db, $alias . '.attribute_' . $vals['field'], $vals['op'], $vals['test']); + } else { + foreach ($vals as $test) { + if (!empty($clause)) { + $clause .= ' ' . $key . ' '; + } + $clause .= Horde_SQL::buildClause($this->_db, $alias . '.attribute_' . $test['field'], $test['op'], $test['test']); + } + } + } + } + + return $clause; + } + + /** + * Get an alias to an attributes table, incrementing it if + * necessary. + * + * @param boolean $increment Increment the alias count? Defaults to false. + */ + function _getAlias($increment = false) + { + static $seen = array(); + + if ($increment && !empty($seen[$this->_table_count])) { + $this->_table_count++; + } + + $seen[$this->_table_count] = true; + return 'a' . $this->_table_count; + } + +} diff --git a/hylax/lib/Storage.php b/hylax/lib/Storage.php new file mode 100644 index 000000000..fa58e3a58 --- /dev/null +++ b/hylax/lib/Storage.php @@ -0,0 +1,200 @@ + + * @package Hylax + */ +class Hylax_Storage { + + /** + * A hash containing any parameters for the current storage driver. + * + * @var array + */ + var $_params = array(); + + /** + * @var VFS + */ + var $_vfs; + + /** + * Constructor + * + * @param array $params Any parameters needed for this storage driver. + */ + function Hylax_Storage($params) + { + global $conf; + + $this->_params = $params; + + /* Set up the VFS storage. */ + if (!isset($conf['vfs']['type'])) { + Horde::fatal(_("You must configure a VFS backend to use Hylax."), __FILE__, __LINE__); + } + $vfs_driver = $conf['vfs']['type']; + $vfs_params = Horde::getDriverConfig('vfs', $vfs_driver); + require_once 'VFS.php'; + $this->_vfs = &VFS::singleton($vfs_driver, $vfs_params); + } + + function saveFaxData($data, $type = '.ps') + { + /* Get the next ID. */ + $fax_id = $this->newFaxId(); + if (is_a($fax_id, 'PEAR_Error')) { + return $fax_id; + } + + /* Save data to VFS backend. */ + $path = Hylax::getVFSPath($fax_id); + $file = $fax_id . $type; + $saved = $this->_vfs->writeData($path, $file, $data, true); + if (is_a($saved, 'PEAR_Error')) { + Horde::logMessage('Could not save fax file to VFS: ' . $saved->getMessage(), __FILE__, __LINE__, PEAR_LOG_ERR); + return $saved; + } + return $fax_id; + } + + function createFax($info, $fix_owner = false) + { + /* In case this is just a fax creation without yet a number assigned + * create an empty number. */ + if (!isset($info['fax_number'])) { + $info['fax_number'] = ''; + } + + /* Set the folder. */ + $info['fax_folder'] = ($info['fax_type']) ? 'outbox' : 'inbox'; + + /* Set timestamp. */ + if (empty($info['fax_created'])) { + $info['fax_created'] = time(); + } + + $data = $this->getFaxData($info['fax_id']); + if (is_a($data, 'PEAR_Error')) { + Horde::logMessage('Could not get fax data: ' . $data->getMessage(), __FILE__, __LINE__, PEAR_LOG_ERR); + return $data; + } + + /* Create a fax image object. */ + require_once HYLAX_BASE . '/lib/Image.php'; + $image = &new Hylax_Image(); + $image->loadData($data); + if (empty($info['fax_pages'])) { + $info['fax_pages'] = $image->getNumPages(); + } + + /* Save to backend. */ + $result = $this->_createFax($info); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + global $conf; + foreach ($conf['fax']['notify'] as $rec) { + mail($rec, _("New fax from: ") . $info['fax_number'], '', + 'From: '. $conf['fax']['notifyfrom']); + } + return true; + } + + function getFaxData($fax_id) + { + $path = Hylax::getVFSPath($fax_id); + $file = $fax_id . '.ps'; + $data = $this->_vfs->read($path, $file); + if (is_a($data, 'PEAR_Error')) { + Horde::logMessage(sprintf("%s '%s/%s'.", $data->getMessage(), $path, $file), __FILE__, __LINE__, PEAR_LOG_ERR); + } + return $data; + } + + function listFaxes($folder) + { + return $this->_listFaxes($folder); + } + + function send($fax_id, $number) + { + global $conf, $gateway; + + $this->_setFaxNumber($fax_id, $number); + + $data = $this->getFaxData($fax_id); + + $job_id = $gateway->send($number, $data); + + $this->_setJobId($fax_id, $job_id); + } + + /** + * Attempts to return a concrete Hylax_Storage instance based on $driver. + * + * @param string $driver The type of concrete Hylax_Storage subclass to + * return. + * @param array $params A hash containing any additional configuration or + * connection parameters a subclass might need. + * + * @return Hylax_Storage The newly created concrete Hylax_Storage + * instance, or false on error. + */ + function &factory($driver, $params = array()) + { + $driver = basename($driver); + include_once dirname(__FILE__) . '/Storage/' . $driver . '.php'; + $class = 'Hylax_Storage_' . $driver; + if (class_exists($class)) { + $storage = &new $class($params); + return $storage; + } else { + Horde::fatal(PEAR::raiseError(sprintf(_("No such backend \"%s\" found"), $driver)), __FILE__, __LINE__); + } + } + + /** + * Attempts to return a reference to a concrete Hylax_Storage instance + * based on $driver. + * + * It will only create a new instance if no Hylax_Storage instance with the + * same parameters currently exists. + * + * This should be used if multiple storage sources are required. + * + * This method must be invoked as: $var = &Hylax_Storage::singleton() + * + * @param string $driver The type of concrete Hylax_Storage subclass to + * return. + * @param array $params A hash containing any additional configuration or + * connection parameters a subclass might need. + * + * @return mixed The created concrete Hylax_Storage instance, or false on + * error. + */ + function &singleton($driver, $params = array()) + { + static $instances; + + if (!isset($instances)) { + $instances = array(); + } + + $signature = serialize(array($driver, $params)); + if (!isset($instances[$signature])) { + $instances[$signature] = &Hylax_Storage::factory($driver, $params); + } + + return $instances[$signature]; + } + +} diff --git a/hylax/lib/Storage/sql.php b/hylax/lib/Storage/sql.php new file mode 100644 index 000000000..12f106ba9 --- /dev/null +++ b/hylax/lib/Storage/sql.php @@ -0,0 +1,416 @@ + + * @package Hylax + */ +class Hylax_Storage_sql extends Hylax_Storage { + + /** + * Handle for the database connection. + * + * @var DB + */ + var $_db; + + /** + * @var Hylax_SQL_Attributes + */ + var $_attributes; + + function Hylax_Storage_sql($params) + { + parent::Hylax_Storage($params); + $this->initialise(); + + /* Set up the storage attributes object in the $_attributes var. */ + $attrib_params = array('primary_table' => 'hylax_faxes', + 'attribute_table' => 'hylax_fax_attributes', + 'id_column' => 'fax_id'); + $this->_attributes = new Hylax_SQL_Attributes($this->_db, $attrib_params); + } + + function newFaxId() + { + $id = $this->_db->nextId('hylax_faxes'); + if (is_a($id, 'PEAR_Error')) { + Horde::logMessage('Could not generate new fax id. %s' . $id->getMessage(), __FILE__, __LINE__, PEAR_LOG_ERR); + } else { + Horde::logMessage('Generated new fax id: ' . $id, __FILE__, __LINE__, PEAR_LOG_DEBUG); + } + return $id; + } + + function _createFax(&$info) + { + /* Save to SQL. */ + $sql = 'INSERT INTO hylax_faxes (fax_id, fax_type, fax_user, fax_number, fax_pages, fax_created, fax_folder) VALUES (?, ?, ?, ?, ?, ?, ?)'; + $values = array($info['fax_id'], + (int)$info['fax_type'], + $info['fax_user'], + $info['fax_number'], + $info['fax_pages'], + $info['fax_created'], + $info['fax_folder']); + Horde::logMessage('SQL Query by Hylax_Storage_sql::_createFax(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_db->query($sql, $values); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + } + return $result; + } + + function _listFaxes($folder) + { + $sql = 'SELECT * FROM hylax_faxes WHERE fax_folder = ?'; + $values = array($folder); + $faxes = $this->_db->getAll($sql, $values, DB_FETCHMODE_ASSOC); + return $faxes; + } + + function getFax($fax_id) + { + $sql = 'SELECT * FROM hylax_faxes WHERE fax_id = ?'; + $values = array($fax_id); + $fax = $this->_db->getRow($sql, $values, DB_FETCHMODE_ASSOC); + if (empty($fax)) { + return PEAR::raiseError(_("No such fax found.")); + } + return $fax; + } + + function getFaxFolder($fax_id) + { + $sql = 'SELECT fax_folder FROM hylax_faxes WHERE fax_id = ?'; + $values = array($fax_id); + $fax_folder = $this->_db->getOne($sql, $values); + if (empty($fax_folder)) { + return PEAR::raiseError(_("No such fax found.")); + } + return $fax_folder; + } + + function _setFaxNumber($fax_id, $number) + { + $sql = 'UPDATE hylax_faxes SET fax_number = ? WHERE fax_id = ?'; + $values = array($number, (int)$fax_id); + return $this->_db->query($sql, $values); + } + + function _setJobId($fax_id, $job_id) + { + $sql = 'UPDATE hylax_faxes SET job_id = ? WHERE fax_id = ?'; + $values = array((int)$job_id, (int)$fax_id); + return $this->_db->query($sql, $values); + } + + function _getFolder($folder, $path = null) + { + switch ($folder) { + case 'inbox': + //return $this->_parseFaxStat($this->_exec('faxstat -r')); + break; + + case 'outbox': + return $this->_parseFaxStat($this->_exec('faxstat -s')); + break; + + case 'archive': + //return $GLOBALS['storage']->getFolder($path); + break; + } + } + + /** + * Fetches a list of available gateways. + * + * @return array An array of the available gateways. + */ + function getGateways() + { + /* Get the gateways. */ + $sql = 'SELECT * FROM swoosh_gateways'; + Horde::logMessage('SQL Query by Hylax_Storage_sql::_getGateways(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_db->getAll($sql, DB_FETCHMODE_ASSOC); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + } + + return $result; + } + + /** + * Fetches a gateway from the backend. + * + * @param int $gateway_id The gateway id to fetch. + * + * @return array An array containing the gateway settings and parameters. + */ + function &getGateway($gateway_id) + { + /* Get the gateways. */ + $sql = 'SELECT * FROM swoosh_gateways WHERE gateway_id = ?'; + $values = array((int)$gateway_id); + Horde::logMessage('SQL Query by Hylax_Storage_sql::getGateway(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $gateway = $this->_db->getRow($sql, $values, DB_FETCHMODE_ASSOC); + if (is_a($gateway, 'PEAR_Error')) { + Horde::logMessage($gateway, __FILE__, __LINE__, PEAR_LOG_ERR); + return $gateway; + } + + /* Unserialize the gateway params. */ + require_once 'Horde/Serialize.php'; + $gateway['gateway_params'] = Horde_Serialize::unserialize($gateway['gateway_params'], Horde_Serialize::UTF7_BASIC); + + /* Unserialize the gateway send params. */ + $gateway['gateway_sendparams'] = Horde_Serialize::unserialize($gateway['gateway_sendparams'], Horde_Serialize::UTF7_BASIC); + + return $gateway; + } + + /** + * Saves a gateway to the backend. + * + * @param array $info The gateway settings to be saved passed by reference + * as an array. + * + * @return mixed Gateway id on success or a PEAR error on failure. + */ + function saveGateway(&$info) + { + if (empty($info['gateway_id'])) { + /* No existing gateway id, so new gateway and get next id. */ + $info['gateway_id'] = $this->_db->nextId('swoosh_gateways'); + if (is_a($info['gateway_id'], 'PEAR_Error')) { + Horde::logMessage($info['gateway_id'], __FILE__, __LINE__, PEAR_LOG_ERR); + return $info['gateway_id']; + } + $sql = 'INSERT INTO swoosh_gateways (gateway_id, gateway_driver, gateway_name, gateway_params, gateway_sendparams) VALUES (?, ?, ?, ?, ?)'; + $values = array(); + } else { + /* Existing gateway id, so editing an existing gateway. */ + $sql_sprintf = 'UPDATE swoosh_gateways SET gateway_id = ?, gateway_driver = ?, gateway_name = ?, gateway_params = ?, gateway_sendparams = ? WHERE gateway_id = ?'; + $values = array((int)$info['gateway_id']); + } + + /* Serialize the gateway params. */ + require_once 'Horde/Serialize.php'; + if (!empty($info['gateway_params'])) { + $info['gateway_params'] = Horde_Serialize::serialize($info['gateway_params'], Horde_Serialize::UTF7_BASIC); + } else { + $info['gateway_params'] = 'NULL'; + } + + /* Serialize the gateway send params. */ + if (!empty($info['gateway_sendparams'])) { + $info['gateway_sendparams'] = Horde_Serialize::serialize($info['gateway_sendparams'], Horde_Serialize::UTF7_BASIC); + } else { + $info['gateway_sendparams'] = 'NULL'; + } + + /* Put together the sql statement. */ + array_unshift($values, + (int)$info['gateway_id'], + $info['gateway_driver'], + $info['gateway_name'], + $info['gateway_params'], + $info['gateway_sendparams']); + Horde::logMessage('SQL Query by Hylax_Storage_sql::saveGateway(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_db->query($sql, $values); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + return $result; + } + + return $info['gateway_id']; + } + + /** + * Deletes a gateway from the backend. + * + * @param int $gateway_id The gateway id of the gateway to delete. + * + * @return mixed True on success or a PEAR error on failure. + */ + function deleteGateway($gateway_id) + { + $sql = 'DELETE FROM swoosh_gateways WHERE gateway_id = ?'; + $values = array((int)$gateway_id); + Horde::logMessage('SQL Query by Hylax_Storage_sql::deleteGateway(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_db->query($sql, $values); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + } + + return $result; + } + + /** + * Saves a message to the backend. + * + * @param integer $gateway_id The id of the gateway used to send this + * message. + * @param string $message_text The text of the message. + * @param string $message_params Any send params used for this message. + * @param string $message_batch_id If batch sending is used, the batch id + * of this message. + * + * @return mixed True on success or PEAR Error on failure. + */ + function saveMessage($gateway_id, $message_text, $message_params, $message_batch_id = null) + { + $message_id = $this->_db->nextId('swoosh_messages'); + if (is_a($message_id, 'PEAR_Error')) { + Horde::logMessage($message_id, __FILE__, __LINE__, PEAR_LOG_ERR); + return $message_id; + } + + /* Serialize the message params. */ + require_once 'Horde/Serialize.php'; + $message_params = Horde_Serialize::serialize($message_params, Horde_Serialize::UTF7_BASIC); + + $sql = 'INSERT INTO swoosh_messages (message_id, user_uid, gateway_id, message_batch_id, message_text, message_params, message_submitted) VALUES (?, ?, ?, ?, ?, ?, ?)'; + $values = array((int)$message_id, + Horde_Auth::getAuth(), + (int)$gateway_id, + is_null($message_batch_id) ? 'NULL' : (int)$message_batch_id, + $message_text, + $message_params, + (int)time()); + $result = $this->_db->query($sql, $values); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + return $result; + } + + return $message_id; + } + + /** + * Saves an individual send to the backend. This will be one instance of + * a message being sent to a recipient. + * + * @param int $message_id The message id. + * @param string $remote_id The text of the message. + * @param string $recipient Any send params used for this + * @param string $error Any send params used for this + * + * @return mixed The send id on success or PEAR Error on failure. + */ + function saveSend($message_id, $remote_id, $recipient, $error) + { + $send_id = $this->_db->nextId('swoosh_sends'); + if (is_a($send_id, 'PEAR_Error')) { + Horde::logMessage($send_id, __FILE__, __LINE__, PEAR_LOG_ERR); + return $send_id; + } + $sql = 'INSERT INTO swoosh_sends (send_id, message_id, send_remote_id, send_recipient, send_status) VALUES (?, ?, ?, ?, ?)'; + $values = array($send_id, + $message_id, + (is_null($remote_id) ? 'NULL' : $remote_id), + $recipient, + (is_null($error) ? 'NULL' : $error)); + $result = $this->_db->query($sql, $values); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + return $result; + } + + return $send_id; + } + + /** + * Gets all the messages for the current user. + * + * @access private + * + * @return mixed The array of messages on success or PEAR Error on failure. + */ + function _getMessages() + { + $sql = 'SELECT * FROM swoosh_messages WHERE user_uid = ?'; + $values = array(Horde_Auth::getAuth()); + Horde::logMessage('SQL Query by Hylax_Storage_sql::_getMessages(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_db->getAssoc($sql, false, $values, DB_FETCHMODE_ASSOC); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + } + + return $result; + } + + /** + * Fetches all sends for one or more message ids. + * + * @access private + * @param int|array $message_id The message id(s). + * + * @return mixed The send id on success or PEAR Error on failure. + */ + function _getSends($message_ids) + { + if (!is_array($message_ids)) { + $message_ids = array($message_ids); + } + + $sql = 'SELECT message_id, swoosh_sends.* FROM swoosh_sends' . + ' WHERE message_id IN (' . str_repeat('?, ', count($message_ids) - 1) . '?)'; + $values = $message_ids; + Horde::logMessage('SQL Query by Hylax_Storage_sql::_getSends(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_db->getAssoc($sql, false, $values, DB_FETCHMODE_ASSOC, true); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + } + + return $result; + } + + function initialise() + { + global $registry; + + Horde::assertDriverConfig($this->_params, 'sql', + array('phptype')); + + if (!isset($this->_params['database'])) { + $this->_params['database'] = ''; + } + if (!isset($this->_params['username'])) { + $this->_params['username'] = ''; + } + if (!isset($this->_params['hostspec'])) { + $this->_params['hostspec'] = ''; + } + + /* Connect to the SQL server using the supplied parameters. */ + require_once 'DB.php'; + $this->_db = &DB::connect($this->_params, + array('persistent' => !empty($this->_params['persistent']))); + if (is_a($this->_db, 'PEAR_Error')) { + Horde::fatal($this->_db, __FILE__, __LINE__); + } + + // Set DB portability options + switch ($this->_db->phptype) { + case 'mssql': + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); + break; + default: + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); + } + + return true; + } + +} diff --git a/hylax/lib/base.php b/hylax/lib/base.php new file mode 100644 index 000000000..55962d3ca --- /dev/null +++ b/hylax/lib/base.php @@ -0,0 +1,58 @@ +pushApp('hylax', !defined('AUTH_HANDLER')); +} catch (Horde_Exception $e) { + if ($e->getCode() == 'permission_denied') { + Horde::authenticationFailureRedirect(); + } + Horde::fatal($e, __FILE__, __LINE__, false); +} + +$conf = &$GLOBALS['conf']; +@define('HYLAX_TEMPLATES', $registry->get('templates')); + +/* Notification system. */ +$notification = &Horde_Notification::singleton(); +$notification->attach('status'); + +/* Find the base file path of Hylax. */ +@define('HYLAX_BASE', dirname(__FILE__) . '/..'); + +/* Hylax base libraries. */ +require_once HYLAX_BASE . '/lib/Hylax.php'; +require_once HYLAX_BASE . '/lib/Driver.php'; +require_once HYLAX_BASE . '/lib/Storage.php'; + +/* Hylax Driver */ +$gateway = &Hylax_Driver::singleton($conf['fax']['driver'], $conf['fax']['params']); + +/* Hylax storage driver. */ +$hylax_storage = &Hylax_Storage::singleton('sql', $conf['sql']); + +/* Start compression, if requested. */ +Horde::compressOutput(); diff --git a/hylax/lib/version.php b/hylax/lib/version.php new file mode 100644 index 000000000..90df736f8 --- /dev/null +++ b/hylax/lib/version.php @@ -0,0 +1 @@ + diff --git a/hylax/print.php b/hylax/print.php new file mode 100644 index 000000000..82db9b1fd --- /dev/null +++ b/hylax/print.php @@ -0,0 +1,26 @@ +push(sprintf(_("Could not print fax ID \"%s\". %s"), $fax_id, $print->getMessage()), 'horde.error'); +} else { + $notification->push(sprintf(_("Printed fax ID \"%s\"."), $fax_id), 'horde.success'); +} + +/* Redirect back. */ +$url = Horde::applicationUrl($url, true); +header('Location: ' . $url); +exit; diff --git a/hylax/scripts/.htaccess b/hylax/scripts/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/hylax/scripts/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/hylax/scripts/.hylarc b/hylax/scripts/.hylarc new file mode 100644 index 000000000..c040f05b6 --- /dev/null +++ b/hylax/scripts/.hylarc @@ -0,0 +1,2 @@ +# This is a hylafax configuration file to override the server one. +JobFmt: "%j|%a|%o|%e|%P|%D|%z|%s" diff --git a/hylax/scripts/cups/hylafax b/hylax/scripts/cups/hylafax new file mode 100755 index 000000000..2b24be76d --- /dev/null +++ b/hylax/scripts/cups/hylafax @@ -0,0 +1,30 @@ +#!/bin/bash +# This should go into the /usr/lib/cups/backend directory to allow the fax to +# be set up as a regular printer for cups. + +SCRIPT_DIR="__SCRIPT_DIR__" + +# Nothing to see below. +DATA_SCRIPT="fax_save_data.php" +FAX_SCRIPT="fax_create.php" +FAX_USER=$2 +DATA=$6 + +# Called with no arguments, we list the provided HylaFAX backend +# (serial devices are irrelevant here). +if [ $# -eq 0 ]; then + echo "direct hylafax:/local \"Unknown\" \"Local HylaFAX server\"" + exit 0 +fi + +# Call using sudo so that VFS creates correctly owned directories for web use. +if [ -n $DATA ]; then + # The spool filename has been passed, cat the contents to the data script. + FAX_ID=`cat $DATA|sudo -u __WWW_USER__ $SCRIPT_DIR/$DATA_SCRIPT` +else + # No spool filename, use what comes through on STDIN. + FAX_ID=`sudo -u __WWW_USER__ $SCRIPT_DIR/$DATA_SCRIPT` +fi + +# Call the fax creation script to create a stub in the DB for this new data. +$SCRIPT_DIR/$FAX_SCRIPT $FAX_ID $FAX_USER diff --git a/hylax/scripts/cups/hylafax.ppd b/hylax/scripts/cups/hylafax.ppd new file mode 100644 index 000000000..73851baee --- /dev/null +++ b/hylax/scripts/cups/hylafax.ppd @@ -0,0 +1,115 @@ +*PPD-Adobe: "4.3" +*FormatVersion: "4.3" +*FileVersion: "1.0" +*LanguageVersion: English +*LanguageEncoding: ISOLatin1 +*PCFileName: "HYLAFAX.PPD" +*Manufacturer: "HylaFAX" +*Product: "HylaFAX" +*cupsFax: True +*cupsFilter: "application/vnd.cups-postscript 0 -" +*cupsVersion: 1.1 +*cupsManualCopies: True +*ModelName: "HylaFAX" +*ShortNickName: "HylaFAX" +*NickName: "HylaFAX" +*PSVersion: "(3010.000) 550" +*LanguageLevel: "3" +*ColorDevice: False +*FaxSupport: Base +*FileSystem: False +*Throughput: "1" +*TTRasterizer: Type42 +*LandscapeOrientation: Plus90 +*VariablePaperSize: False + +*OpenUI *Resolution/Fax Resolution: PickOne +*OrderDependency: 10 AnySetup *Resolution +*DefaultResolution: 204x196dpi +*Resolution 204x196dpi/204×196 dpi (high resolution): "" +*Resolution 204x98dpi/204×98 dpi (low resolution): "" +*CloseUI: *Resolution + +*OpenUI *PageSize/Media Size: PickOne +*OrderDependency: 0 AnySetup *PageSize +*DefaultPageSize: A4 +*PageSize A4/A4: "<>setpagedevice" +*PageSize Letter/US Letter: "<>setpagedevice" +*PageSize Legal/US Legal: "<>setpagedevice" +*CloseUI: *PageSize + +*DefaultPageRegion: A4 +*PageRegion A4/A4: "<>setpagedevice" +*PageRegion Letter/US Letter: "<>setpagedevice" +*PageRegion Legal/US Legal: "<>setpagedevice" + +*DefaultImageableArea: A4 + +*DefaultPaperDimension: A4 +*PaperDimension A4: "595 842" +*PaperDimension Letter: "612 792" +*PaperDimension Legal: "612 1008" + +*OpenUI *Dial/Dial Method: PickOne +*OrderDependency: 30 AnySetup *Dial +*DefaultDial: JobName +*Dial JobName/Job Name: "" +*Dial Manually/Manually: "" +*Dial Option/Option (obsolete): "" +*CloseUI: *Dial + +*DefaultFont: Courier +*Font AvantGarde-Book: Standard "(001.006S)" Standard ROM +*Font AvantGarde-BookOblique: Standard "(001.006S)" Standard ROM +*Font AvantGarde-Demi: Standard "(001.007S)" Standard ROM +*Font AvantGarde-DemiOblique: Standard "(001.007S)" Standard ROM +*Font Bookman-Demi: Standard "(001.004S)" Standard ROM +*Font Bookman-DemiItalic: Standard "(001.004S)" Standard ROM +*Font Bookman-Light: Standard "(001.004S)" Standard ROM +*Font Bookman-LightItalic: Standard "(001.004S)" Standard ROM +*Font Courier: Standard "(002.004S)" Standard ROM +*Font Courier-Bold: Standard "(002.004S)" Standard ROM +*Font Courier-BoldOblique: Standard "(002.004S)" Standard ROM +*Font Courier-Oblique: Standard "(002.004S)" Standard ROM +*Font Helvetica: Standard "(001.006S)" Standard ROM +*Font Helvetica-Bold: Standard "(001.007S)" Standard ROM +*Font Helvetica-BoldOblique: Standard "(001.007S)" Standard ROM +*Font Helvetica-Narrow: Standard "(001.006S)" Standard ROM +*Font Helvetica-Narrow-Bold: Standard "(001.007S)" Standard ROM +*Font Helvetica-Narrow-BoldOblique: Standard "(001.007S)" Standard ROM +*Font Helvetica-Narrow-Oblique: Standard "(001.006S)" Standard ROM +*Font Helvetica-Oblique: Standard "(001.006S)" Standard ROM +*Font NewCenturySchlbk-Bold: Standard "(001.009S)" Standard ROM +*Font NewCenturySchlbk-BoldItalic: Standard "(001.007S)" Standard ROM +*Font NewCenturySchlbk-Italic: Standard "(001.006S)" Standard ROM +*Font NewCenturySchlbk-Roman: Standard "(001.007S)" Standard ROM +*Font Palatino-Bold: Standard "(001.005S)" Standard ROM +*Font Palatino-BoldItalic: Standard "(001.005S)" Standard ROM +*Font Palatino-Italic: Standard "(001.005S)" Standard ROM +*Font Palatino-Roman: Standard "(001.005S)" Standard ROM +*Font Symbol: Special "(001.007S)" Special ROM +*Font Times-Bold: Standard "(001.007S)" Standard ROM +*Font Times-BoldItalic: Standard "(001.009S)" Standard ROM +*Font Times-Italic: Standard "(001.007S)" Standard ROM +*Font Times-Roman: Standard "(001.007S)" Standard ROM +*Font ZapfChancery-MediumItalic: Standard "(001.007S)" Standard ROM + +*% What follows is a pseudo-Foomatic driver to enable graphical +*% specification of the phone number. Only the phone number is listed +*% as option. It won't be used by Foomatic anyway. +*% +*% COMDATA #$VAR1 = { +*% COMDATA # 'args_byname' => { +*% COMDATA # 'phone' => { +*% COMDATA # 'name' => 'phone', +*% COMDATA # 'type' => 'string', +*% COMDATA # 'comment' => 'Target Fax Number', +*% COMDATA # 'default' => '' +*% COMDATA # } +*% COMDATA # }, +*% COMDATA # 'model' => 'HylaFaxmodem', +*% COMDATA # 'comment' => 'HylaFaxmodem driver', +*% COMDATA # 'make' => 'HylaFaxmodem', +*% COMDATA # 'color' => '0' +*% COMDATA #}; + diff --git a/hylax/scripts/fax_create.php b/hylax/scripts/fax_create.php new file mode 100755 index 000000000..a502ded3b --- /dev/null +++ b/hylax/scripts/fax_create.php @@ -0,0 +1,42 @@ +#!/usr/bin/php +init(); + +/* Create the fax information array. Set fax_type to 1 for outgoing. */ +$info = array('fax_type' => 1); + +/* Get the arguments. The third argument is the user submitting the job, used + * to differentiate jobs between users.*/ +$args = Console_Getopt::readPHPArgv(); +if (isset($args[1])) { + $info['fax_id'] = $args[1]; +} +if (isset($args[2])) { + $info['fax_user'] = $args[2]; +} +Horde::logMessage(sprintf('Creating fax ID %s for user %s.', $info['fax_id'], $info['fax_user']), __FILE__, __LINE__, PEAR_LOG_DEBUG); + +$hylax_storage->createFax($info, true); diff --git a/hylax/scripts/fax_create_recv.php b/hylax/scripts/fax_create_recv.php new file mode 100755 index 000000000..7f6dc4c01 --- /dev/null +++ b/hylax/scripts/fax_create_recv.php @@ -0,0 +1,58 @@ +#!/usr/bin/php +init(); + +/* Create the fax information array. Set fax_type to 0 for incoming. */ +$info = array('fax_type' => 0, + 'fax_user' => ''); + +/* Get the arguments. The first argument is the filename from which the job ID + * is obtained, in the format 'recvq/faxNNNNN.tif'. */ +$args = Console_Getopt::readPHPArgv(); +if (isset($args[1])) { + $info['fax_id'] = $args[1]; +} +if (isset($args[2])) { + $file = $args[2]; + $info['job_id'] = (int)substr($file, 9, -4); +} + +$fax_info = $cli->readStdin(); +$fax_info = explode("\n", $fax_info); +foreach ($fax_info as $line) { + $line = trim($line); + if (preg_match('/Pages: (\d+)/', $line, $matches)) { + $info['fax_pages'] = $matches[1]; + } elseif (preg_match('/Sender: (.+)/', $line, $matches)) { + $info['fax_number'] = $matches[1]; + } elseif (preg_match('/Received: (\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})/', $line, $d)) { + $time = mktime($d[4], $d[5], $d[6], $d[2], $d[3], $d[1]); + $info['fax_created'] = $time; + } +} + +$t = $hylax_storage->createFax($info, true); +var_dump($t); diff --git a/hylax/scripts/fax_save_data.php b/hylax/scripts/fax_save_data.php new file mode 100755 index 000000000..ba926f5c4 --- /dev/null +++ b/hylax/scripts/fax_save_data.php @@ -0,0 +1,39 @@ +#!/usr/bin/php +init(); + +/* Store the raw fax postscript data. */ +$data = $cli->readStdin(); +if (empty($data)) { + Horde::logMessage('No print data received from standard input. Exiting fax submission.', __FILE__, __LINE__, PEAR_LOG_ERR); + exit; +} + +$fax_id = $hylax_storage->saveFaxData($data); +if (is_a($fax_id, 'PEAR_Error')) { + echo '0'; +} +echo $fax_id; diff --git a/hylax/scripts/fax_save_recv_data.php b/hylax/scripts/fax_save_recv_data.php new file mode 100755 index 000000000..370d068f0 --- /dev/null +++ b/hylax/scripts/fax_save_recv_data.php @@ -0,0 +1,50 @@ +#!/usr/bin/php +init(); + +/* Get the arguments. The first argument is the filename from which the job ID + * is obtained, in the format 'recvq/faxNNNNN.tif'. */ +$args = Console_Getopt::readPHPArgv(); +if (isset($args[1])) { + $file = $args[1]; + $job_id = (int)substr($file, 9, -4); +} + +/* Store the raw fax postscript data. */ +$data = $cli->readStdin(); +if (empty($data)) { + Horde::logMessage('No print data received from standard input. Exiting fax submission.', __FILE__, __LINE__, PEAR_LOG_ERR); + exit; +} + +/* Get the file and store into VFS. */ +$fax_id = $hylax_storage->saveFaxData($data, '.ps'); +if (is_a($fax_id, 'PEAR_Error')) { + echo '0'; + exit; +} +Horde::logMessage(sprintf('Creating fax ID %s for received fax.', $fax_id), __FILE__, __LINE__, PEAR_LOG_DEBUG); +echo $fax_id; diff --git a/hylax/scripts/hylafax/faxrcvd b/hylax/scripts/hylafax/faxrcvd new file mode 100755 index 000000000..7234a2358 --- /dev/null +++ b/hylax/scripts/hylafax/faxrcvd @@ -0,0 +1,41 @@ +#! /bin/sh + +# +# faxrcvd file devID commID error-msg +# +if [ $# -lt 4 ]; then + echo "Usage: $0 file devID commID error-msg CIDNumber CIDName " + exit 1 +fi + +#test -f etc/setup.cache || { +# SPOOL=`pwd` +# cat< ${FAXFILE}.txt +echo "Pages: $PAGES" >> ${FAXFILE}.txt +echo "Received: $DATETIME" >> ${FAXFILE}.txt + +cat ${FAXFILE}.txt | sudo -u __WWW_USER__ $SCRIPT_DIR/$FAX_SCRIPT $FAX_ID $FAXFILE +rm -rf ${FAXFILE}.txt + diff --git a/hylax/scripts/sql/fax.mssql.sql b/hylax/scripts/sql/fax.mssql.sql new file mode 100644 index 000000000..478c3555d --- /dev/null +++ b/hylax/scripts/sql/fax.mssql.sql @@ -0,0 +1,29 @@ +-- $Horde: incubator/hylax/scripts/sql/fax.mssql.sql,v 1.1 2006/12/13 04:30:49 chuck Exp $ + +CREATE TABLE hylax_faxes ( + fax_id INT NOT NULL DEFAULT 0, + job_id INT DEFAULT NULL, + fax_type SMALLINT(1) NOT NULL, + fax_user VARCHAR(255) NOT NULL DEFAULT '', + fax_number VARCHAR(255) NOT NULL DEFAULT '', + fax_pages INT NOT NULL DEFAULT 0, + fax_created INT NOT NULL DEFAULT 0, + fax_status VARCHAR(255) NOT NULL DEFAULT '', + fax_folder VARCHAR(255) NOT NULL DEFAULT '', + + PRIMARY KEY (fax_id) +); + +CREATE INDEX hylax_faxes_fax_id_idx ON hylax_faxes (fax_id); + + +CREATE TABLE hylax_fax_attributes ( + fax_id VARCHAR(255) NOT NULL default '', + attribute_name VARCHAR(255) NOT NULL default '', + attribute_key VARCHAR(255) NOT NULL default '', + attribute_value VARCHAR(MAX) +); + +CREATE INDEX fax_attribute_idx ON hylax_fax_attributes (fax_id); +CREATE INDEX fax_attribute_name_idx ON hylax_fax_attributes (attribute_name); +CREATE INDEX fax_attribute_key_idx ON hylax_fax_attributes (attribute_key); diff --git a/hylax/scripts/sql/fax.sql b/hylax/scripts/sql/fax.sql new file mode 100644 index 000000000..29165efd9 --- /dev/null +++ b/hylax/scripts/sql/fax.sql @@ -0,0 +1,29 @@ +-- $Horde: incubator/hylax/scripts/sql/fax.sql,v 1.1 2006/12/13 04:30:49 chuck Exp $ + +CREATE TABLE hylax_faxes ( + fax_id INT NOT NULL DEFAULT 0, + job_id INT DEFAULT NULL, + fax_type SMALLINT(1) NOT NULL, + fax_user VARCHAR(255) NOT NULL DEFAULT '', + fax_number VARCHAR(255) NOT NULL DEFAULT '', + fax_pages INT NOT NULL DEFAULT 0, + fax_created INT NOT NULL DEFAULT 0, + fax_status VARCHAR(255) NOT NULL DEFAULT '', + fax_folder VARCHAR(255) NOT NULL DEFAULT '', + + PRIMARY KEY (fax_id) +); + +CREATE INDEX hylax_faxes_fax_id_idx ON hylax_faxes (fax_id); + + +CREATE TABLE hylax_fax_attributes ( + fax_id VARCHAR(255) NOT NULL default '', + attribute_name VARCHAR(255) NOT NULL default '', + attribute_key VARCHAR(255) NOT NULL default '', + attribute_value TEXT +); + +CREATE INDEX fax_attribute_idx ON hylax_fax_attributes (fax_id); +CREATE INDEX fax_attribute_name_idx ON hylax_fax_attributes (attribute_name); +CREATE INDEX fax_attribute_key_idx ON hylax_fax_attributes (attribute_key); diff --git a/hylax/send.php b/hylax/send.php new file mode 100644 index 000000000..8c21c005c --- /dev/null +++ b/hylax/send.php @@ -0,0 +1,76 @@ +get('fax_id'); +$url = $vars->get('url', 'folder.php'); + +$fax = $hylax_storage->getFax($fax_id); +if (is_a($fax, 'PEAR_Error')) { + $notification->push(sprintf(_("Could not open fax ID \"%s\". %s"), $fax_id, $fax->getMessage()), 'horde.error'); + $url = Horde::applicationUrl($url, true); + header('Location: ' . $url); + exit; +} elseif (!empty($fax['fax_number'])) { + $notification->push(sprintf(_("Fax ID \"%s\" already has a fax number set."), $fax_id), 'horde.error'); + $url = Horde::applicationUrl($url, true); + header('Location: ' . $url); + exit; +} + +$title = _("Send Fax"); + +/* Set up the form. */ +$form = new Horde_Form($vars, $title); +$form->setButtons(_("Send"), true); +$form->addHidden('', 'url', 'text', false); +$form->addHidden('', 'fax_id', 'int', false); +$form->addVariable(_("Fax destination"), 'fax_number', 'text', true, false, null, array('/^\d+$/')); + +if ($form->validate($vars)) { + $form->getInfo($vars, $info); + $send = $hylax_storage->send($info['fax_id'], $info['fax_number']); + if (is_a($send, 'PEAR_Error')) { + $notification->push(sprintf(_("Could not send fax ID \"%s\". %s"), $info['fax_id'], $send->getMessage()), 'horde.error'); + } else { + $notification->push(sprintf(_("Fax ID \"%s\" submitted successfully."), $info['fax_id']), 'horde.success'); + } + $url = Horde::applicationUrl($url, true); + header('Location: ' . $url); + exit; +} + +/* Get the preview pages. */ +$pages = Hylax::getPages($fax_id, $fax['fax_pages']); + +/* Render the form. */ +require_once 'Horde/Form/Renderer.php'; +$renderer = new Horde_Form_Renderer(); +$send_form = Horde_Util::bufferOutput(array($form, 'renderActive'), $renderer, $vars, 'send.php', 'post'); + +/* Set up template. */ +$template = new Horde_Template(); +$template->set('form', $send_form); +$template->set('pages', $pages); +$template->set('menu', $menu->getMenu()); +$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status'))); + +require HYLAX_TEMPLATES . '/common-header.inc'; +echo $template->fetch(HYLAX_TEMPLATES . '/fax/fax.html'); +require $registry->get('templates', 'horde') . '/common-footer.inc'; diff --git a/hylax/summary.php b/hylax/summary.php new file mode 100644 index 000000000..24a8c76fd --- /dev/null +++ b/hylax/summary.php @@ -0,0 +1,39 @@ +getInbox(); +foreach ($inbox as $item) { + $fmt_inbox[] = array('owner' => $item[2]); +} + +$fmt_outbox = array(); +//$outbox = $fax->getOutbox(); +foreach ($outbox as $item) { + $fmt_outbox[] = array(//'time' => $item + 'owner' => $item[2], + ); +} + +/* Set up actions. */ +$template = &new Horde_Template(); +$template->set('in_faxes', $gateway->numFaxesIn()); +$template->set('out_faxes', $gateway->numFaxesOut()); +$template->set('inbox', $fmt_inbox, true); +$template->set('outbox', $fmt_outbox, true); +$template->set('menu', Hylax::getMenu('string')); +$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status'))); + +require HYLAX_TEMPLATES . '/common-header.inc'; +echo $template->fetch(HYLAX_TEMPLATES . '/summary/summary.html'); +require $registry->get('templates', 'horde') . '/common-footer.inc'; diff --git a/hylax/templates/.htaccess b/hylax/templates/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/hylax/templates/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/hylax/templates/common-header.inc b/hylax/templates/common-header.inc new file mode 100644 index 000000000..dd297c6b5 --- /dev/null +++ b/hylax/templates/common-header.inc @@ -0,0 +1,29 @@ + + + + + +' : '' ?> + +get('name'); +if (!empty($title)) $page_title .= ' :: ' . $title; +if (!empty($refresh_time) && ($refresh_time > 0) && !empty($refresh_url)) { + echo "\n"; +} + +Horde::includeScriptFiles(); + +?> +<?php echo htmlspecialchars($page_title) ?> + + + + +> diff --git a/hylax/templates/compose/compose.html b/hylax/templates/compose/compose.html new file mode 100644 index 000000000..7b78b26cf --- /dev/null +++ b/hylax/templates/compose/compose.html @@ -0,0 +1,6 @@ + +
+ + diff --git a/hylax/templates/fax/fax.html b/hylax/templates/fax/fax.html new file mode 100644 index 000000000..23ae635c4 --- /dev/null +++ b/hylax/templates/fax/fax.html @@ -0,0 +1,21 @@ + +
+ + + + +
+ + + + + + +
+ Pages +
+ + +
diff --git a/hylax/templates/folder/folder.html b/hylax/templates/folder/folder.html new file mode 100644 index 000000000..7b6e5efe4 --- /dev/null +++ b/hylax/templates/folder/folder.html @@ -0,0 +1,60 @@ + +
+ + + + + + + +
+ Folder: + + | +
+
+ + + + + + + + + + + + + + + + +
+   + + Date + + Number + + Owner + + Pages + + Status +
+   + + + + + + + + + + +
+ No entries +
diff --git a/hylax/templates/summary/summary.html b/hylax/templates/summary/summary.html new file mode 100644 index 000000000..517935392 --- /dev/null +++ b/hylax/templates/summary/summary.html @@ -0,0 +1,52 @@ + +
+ + + + + + + +
+ Summary +
+
+ + + + + + +
+ New faxes + + Faxes waiting to be sent +
+ + + + + + + +
+ + +
+
+
+ + + + + + + +
+ + +
+
+
diff --git a/hylax/templates/view/view.html b/hylax/templates/view/view.html new file mode 100644 index 000000000..a341e1806 --- /dev/null +++ b/hylax/templates/view/view.html @@ -0,0 +1,28 @@ + +
+ + + + + + + +
+ Number: + + | +
+
+ + + + + + +
+ Pages +
+ +
diff --git a/hylax/themes/graphics/fax.png b/hylax/themes/graphics/fax.png new file mode 100644 index 0000000000000000000000000000000000000000..fe9c9647a7fa61bb714f899165a27881dfde4d6f GIT binary patch literal 586 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!6%)r1HuVsG}$YDu$^mSxl*w|O|J8&|PuaN8! z{-)qT)!eGD{X0EcIMQvveM$F#`?yF zy3Lz5tXsQ!$>N2VFI|wAlXY=&(9_i^E-LKr>+RPRtWf5kq9we_Q0%^w-2eaofB*jd z>({TJKY#xC@#FjV?{9#1yng-a)vK2;U*5TM=l1Q}w{G3Ke*OBDD_1UGzI^=n@vU38 zu3NWm&6+h!mMob*eR@w%Pghr$mzS4~jg6_PskF4Tu&^-D2SAf!Bcm;VEQgXHzhEGj z62Ne%dZP=_EY1Rt$YKTtZXpn6ymYtj4^VK0r;B5Vgk9Zd z^&}i_`t|MKcB2FHjk!)aD6Hr364|rd;mLZ>C$}F?oEDp-@!_noVA`*bej+(tdG3jh zoV_j=>VL_qZaN;IZZvt)tQVJf*v(gSNEbKUcz;u7v4Qwf$NG1DDvLgdUjH5%I@Q#( zg*Tr+b+KCKhO@7eO*emY61nq0I3r*7-9L>vk6Hcn%>pfMT1F?it^JxirT!nolwt$d U%PJ(>tfX-<|*E*@53LcK-SHZ2hd9&lj3MJUMpv zQ02ajGoGHC^6<)rKW`5Gyj^v2_ks)iX76vcKG0?T?cKe{hnv>TuV305F~2?F`pGrl zZcck}dHwmdp@%2f+`qiHC)=bvRO#*WyC*hvtel*9u-kgqil*y35`W&V{(7zF!qK%q z9DaA1R5Ux>pHP7_57q$vtqyX{NAr& zY*O*ay5_vyOSzZ)FBlAdH&upmB#PTz@b-`h_%w?l@BOAv!dY{#DC%^C3l!zm$*1|% zu&9{-)qT)!eGD{X0EcIMQvveM$F#`?yF zy3Lz5tXsQ!$>N2VFI|wAlXY=&(9_i^E-LKr>+RPRtWf5kq9we_Q0%^w-2eaofB*jd z>({TJKY#xC@#FjV?{9#1yng-a)vK2;U*5TM=l1Q}w{G3Ke*OBDD_1UGzI^=n@vU38 zu3NWm&6+h!mMob*eR@w%Pghr$mzS4~jg6_PskF4Tu&^-D2SAf!Bcm;VEQgXHzhEGj z62Ne%dZP=_EY1Rt$YKTtZXpn6ymYtj4^VK0r;B5Vgk9Zd z^&}i_`t|MKcB2FHjk!)aD6Hr364|rd;mLZ>C$}F?oEDp-@!_noVA`*bej+(tdG3jh zoV_j=>VL_qZaN;IZZvt)tQVJf*v(gSNEbKUcz;u7v4Qwf$NG1DDvLgdUjH5%I@Q#( zg*Tr+b+KCKhO@7eO*emY61nq0I3r*7-9L>vk6Hcn%>pfMT1F?it^JxirT!nolwt$d U%PJdownloadHeaders($filename); + Hylax::getPDF($fax_id); + exit; +} + +$fax = $hylax_storage->getFax($fax_id); +if (is_a($fax, 'PEAR_Error')) { + $notification->push(sprintf(_("Could not open fax ID \"%s\". %s"), $fax_id, $fax->getMessage()), 'horde.error'); + if (empty($url)) { + $url = Horde::applicationUrl('folder.php', true); + } + header('Location: ' . $url); + exit; +} + +$title = _("View Fax"); + +/* Get the preview pages. */ +$pages = Hylax::getPages($fax_id, $fax['fax_pages']); + +/* Set up template. */ +$template = &new Horde_Template(); +$template->set('form', ''); +$template->set('pages', $pages); +$template->set('menu', Hylax::getMenu('string')); +$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status'))); + +require HYLAX_TEMPLATES . '/common-header.inc'; +echo $template->fetch(HYLAX_TEMPLATES . '/fax/fax.html'); +require $registry->get('templates', 'horde') . '/common-footer.inc'; -- 2.11.0